1a22712
============================================================
1a22712
 Emit "transitioned" not "changed" for new frames
1a22712
1a22712
  Previously, "changed" would get emitted when switching
1a22712
backgrounds AND when switching slides in an animated
1a22712
background.  The two actions are conceptually different,
1a22712
so this commit splits the signal into two signals. This
1a22712
will allow us to add a cross fade effect when switching
1a22712
backgrounds (and not add the cross fade effect when
1a22712
switching slides that have their own transition effect)
1a22712
1a22712
diff --git a/libgnome-desktop/gnome-bg.c b/libgnome-desktop/gnome-bg.c
1a22712
--- a/libgnome-desktop/gnome-bg.c
1a22712
+++ b/libgnome-desktop/gnome-bg.c
1a22712
@@ -98,7 +98,8 @@ struct _GnomeBG
55913d1
 	GFileMonitor *		file_monitor;
55913d1
 
55913d1
 	guint                   changed_id;
1a22712
-	
55913d1
+	guint                   transitioned_id;
1a22712
+
55913d1
 	/* Cached information, only access through cache accessor functions */
55913d1
         SlideShow *		slideshow;
1a22712
 	time_t			file_mtime;
1a22712
@@ -115,6 +116,7 @@ struct _GnomeBGClass
55913d1
 
55913d1
 enum {
55913d1
 	CHANGED,
55913d1
+	TRANSITIONED,
55913d1
 	N_SIGNALS
55913d1
 };
55913d1
 
1a22712
@@ -275,6 +277,30 @@ queue_changed (GnomeBG *bg)
55913d1
 					     NULL);
55913d1
 }
55913d1
 
55913d1
+static gboolean
55913d1
+do_transitioned (GnomeBG *bg)
55913d1
+{
55913d1
+	bg->transitioned_id = 0;
55913d1
+
55913d1
+	g_signal_emit (G_OBJECT (bg), signals[TRANSITIONED], 0);
55913d1
+
55913d1
+	return FALSE;
55913d1
+}
55913d1
+
55913d1
+static void
55913d1
+queue_transitioned (GnomeBG *bg)
55913d1
+{
55913d1
+	if (bg->transitioned_id > 0) {
55913d1
+		g_source_remove (bg->transitioned_id);
55913d1
+	}
55913d1
+
55913d1
+	bg->transitioned_id = g_timeout_add_full (G_PRIORITY_LOW,
55913d1
+					     100,
55913d1
+					     (GSourceFunc)do_transitioned,
55913d1
+					     bg,
55913d1
+					     NULL);
55913d1
+}
55913d1
+
55913d1
 void
55913d1
 gnome_bg_load_from_preferences (GnomeBG     *bg,
55913d1
 				GConfClient *client)
1a22712
@@ -419,6 +445,14 @@ gnome_bg_class_init (GnomeBGClass *klass)
55913d1
 					 NULL, NULL,
55913d1
 					 g_cclosure_marshal_VOID__VOID,
55913d1
 					 G_TYPE_NONE, 0);
55913d1
+
55913d1
+	signals[TRANSITIONED] = g_signal_new ("transitioned",
55913d1
+					 G_OBJECT_CLASS_TYPE (object_class),
55913d1
+					 G_SIGNAL_RUN_LAST,
55913d1
+					 0,
55913d1
+					 NULL, NULL,
55913d1
+					 g_cclosure_marshal_VOID__VOID,
55913d1
+					 G_TYPE_NONE, 0);
55913d1
 }
55913d1
 
55913d1
 GnomeBG *
1a22712
@@ -1352,7 +1386,7 @@ on_timeout (gpointer data)
1a22712
 
1a22712
 	bg->timeout_id = 0;
1a22712
 	
1a22712
-	queue_changed (bg);
1a22712
+	queue_transitioned (bg);
1a22712
 
1a22712
 	return FALSE;
55913d1
 }
1a22712
1a22712
============================================================
1a22712
 Use gdk functions to grab server and flush client
1a22712
1a22712
  It looks a little nicer this way.
1a22712
1a22712
diff --git a/libgnome-desktop/gnome-bg.c b/libgnome-desktop/gnome-bg.c
1a22712
--- a/libgnome-desktop/gnome-bg.c
1a22712
+++ b/libgnome-desktop/gnome-bg.c
1a22712
@@ -1056,7 +1056,7 @@ gnome_bg_create_thumbnail (GnomeBG               *bg,
55913d1
 
1a22712
 
1a22712
 /* Set the root pixmap, and properties pointing to it. We
1a22712
- * do this atomically with XGrabServer to make sure that
1a22712
+ * do this atomically with a server grab to make sure that
1a22712
  * we won't leak the pixmap if somebody else it setting
1a22712
  * it at the same time. (This assumes that they follow the
1a22712
  * same conventions we do)
1a22712
@@ -1082,7 +1082,7 @@ gnome_bg_set_pixmap_as_root (GdkScreen *screen, GdkPixmap *pixmap)
1a22712
 	data_esetroot = NULL;
1a22712
 	display = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
1a22712
 	
1a22712
-	XGrabServer (display);
1a22712
+	gdk_x11_display_grab (gdk_screen_get_display (screen));
1a22712
 	
1a22712
 	result = XGetWindowProperty (
1a22712
 		display, RootWindow (display, screen_num),
1a22712
@@ -1117,10 +1117,9 @@ gnome_bg_set_pixmap_as_root (GdkScreen *screen, GdkPixmap *pixmap)
1a22712
 	XSetWindowBackgroundPixmap (display, RootWindow (display, screen_num),
1a22712
 				    pixmap_id);
1a22712
 	XClearWindow (display, RootWindow (display, screen_num));
1a22712
-	
1a22712
-	XUngrabServer (display);
1a22712
-	
1a22712
-	XFlush (display);
55913d1
+
1a22712
+	gdk_display_flush (gdk_screen_get_display (screen));
5210646
+	gdk_x11_display_ungrab (gdk_screen_get_display (screen));
55913d1
 }
55913d1
 
55913d1
 
1a22712
1a22712
============================================================
1a22712
 Move part of set_pixmap_as_root to set_root_pixmap_id
1a22712
1a22712
  The meatiest part of set_pixmap_as_root takes the passed
1a22712
in pixmap and stores it on the root window in the
1a22712
ESETROOT_PMAP_ID and _XROOTPMAP_ID properties.  That
1a22712
functionality stands on its own, and should be factored
1a22712
out so it can get reused later when adding crossfade
1a22712
transitions on background changes.
1a22712
1a22712
diff --git a/libgnome-desktop/gnome-bg.c b/libgnome-desktop/gnome-bg.c
1a22712
--- a/libgnome-desktop/gnome-bg.c
1a22712
+++ b/libgnome-desktop/gnome-bg.c
1a22712
@@ -1054,15 +1054,9 @@ gnome_bg_create_thumbnail (GnomeBG               *bg,
1a22712
 	return result;
1a22712
 }
1a22712
 
1a22712
-
55913d1
-/* Set the root pixmap, and properties pointing to it. We
1a22712
- * do this atomically with a server grab to make sure that
55913d1
- * we won't leak the pixmap if somebody else it setting
55913d1
- * it at the same time. (This assumes that they follow the
55913d1
- * same conventions we do)
55913d1
- */
55913d1
-void 
55913d1
-gnome_bg_set_pixmap_as_root (GdkScreen *screen, GdkPixmap *pixmap)
55913d1
+static void
55913d1
+gnome_bg_set_root_pixmap_id (GdkScreen *screen,
55913d1
+			     GdkPixmap *pixmap)
55913d1
 {
55913d1
 	int      result;
55913d1
 	gint     format;
1a22712
@@ -1073,24 +1067,20 @@ gnome_bg_set_pixmap_as_root (GdkScreen *screen, GdkPixmap *pixmap)
55913d1
 	Atom     type;
55913d1
 	Display *display;
55913d1
 	int      screen_num;
55913d1
-	
55913d1
-	g_return_if_fail (screen != NULL);
55913d1
-	g_return_if_fail (pixmap != NULL);
55913d1
-	
55913d1
+
55913d1
 	screen_num = gdk_screen_get_number (screen);
55913d1
-	
55913d1
 	data_esetroot = NULL;
55913d1
+
55913d1
 	display = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
55913d1
-	
1a22712
-	gdk_x11_display_grab (gdk_screen_get_display (screen));
55913d1
-	
1a22712
-	result = XGetWindowProperty (
1a22712
-		display, RootWindow (display, screen_num),
1a22712
-		gdk_x11_get_xatom_by_name ("ESETROOT_PMAP_ID"),
1a22712
-		0L, 1L, False, XA_PIXMAP,
1a22712
-		&type, &format, &nitems, &bytes_after,
1a22712
-		&data_esetroot);
55913d1
-	
55913d1
+
1a22712
+	result = XGetWindowProperty (display,
1a22712
+				     RootWindow (display, screen_num),
1a22712
+				     gdk_x11_get_xatom_by_name ("ESETROOT_PMAP_ID"),
1a22712
+				     0L, 1L, False, XA_PIXMAP,
1a22712
+				     &type, &format, &nitems,
1a22712
+				     &bytes_after,
1a22712
+				     &data_esetroot);
1a22712
+
55913d1
 	if (data_esetroot != NULL) {
55913d1
 		if (result == Success && type == XA_PIXMAP &&
55913d1
 		    format == 32 &&
1a22712
@@ -1113,9 +1103,33 @@ gnome_bg_set_pixmap_as_root (GdkScreen *screen, GdkPixmap *pixmap)
55913d1
 			 gdk_x11_get_xatom_by_name ("_XROOTPMAP_ID"), XA_PIXMAP,
55913d1
 			 32, PropModeReplace,
55913d1
 			 (guchar *) &pixmap_id, 1);
1a22712
-	
55913d1
+}
55913d1
+
55913d1
+/* Set the root pixmap, and properties pointing to it. We
1a22712
+ * do this atomically with a server grab to make sure that
55913d1
+ * we won't leak the pixmap if somebody else it setting
55913d1
+ * it at the same time. (This assumes that they follow the
55913d1
+ * same conventions we do)
55913d1
+ */
1a22712
+void
55913d1
+gnome_bg_set_pixmap_as_root (GdkScreen *screen, GdkPixmap *pixmap)
55913d1
+{
55913d1
+	Display *display;
55913d1
+	int      screen_num;
1a22712
+
55913d1
+	g_return_if_fail (screen != NULL);
55913d1
+	g_return_if_fail (pixmap != NULL);
1a22712
+
55913d1
+	screen_num = gdk_screen_get_number (screen);
1a22712
+
55913d1
+	display = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
55913d1
+
1a22712
+	gdk_x11_display_grab (gdk_screen_get_display (screen));
55913d1
+
55913d1
+	gnome_bg_set_root_pixmap_id (screen, pixmap);
55913d1
+
55913d1
 	XSetWindowBackgroundPixmap (display, RootWindow (display, screen_num),
55913d1
-				    pixmap_id);
55913d1
+				    GDK_PIXMAP_XID (pixmap));
55913d1
 	XClearWindow (display, RootWindow (display, screen_num));
55913d1
 
5210646
 	gdk_display_flush (gdk_screen_get_display (screen));
1a22712
1a22712
============================================================
1a22712
 Add Crossfade class
1a22712
1a22712
  This adds a helper class to manage doing crossfades on a window.
1a22712
It will be leveraged by gnome_bg and nautilus to do a fade
1a22712
transition when changing backgrounds on the desktop or in nautilus
1a22712
windows.
1a22712
1a22712
diff --git a/libgnome-desktop/Makefile.am b/libgnome-desktop/Makefile.am
1a22712
--- a/libgnome-desktop/Makefile.am
1a22712
+++ b/libgnome-desktop/Makefile.am
1a22712
@@ -22,6 +22,7 @@ libgnome_desktop_2_la_SOURCES = \
1a22712
 	gnome-desktop-thumbnail.c \
1a22712
 	gnome-thumbnail-pixbuf-utils.c \
1a22712
 	gnome-bg.c		\
1a22712
+	gnome-bg-crossfade.c	\
1a22712
 	display-name.c		\
1a22712
 	gnome-rr.c		\
1a22712
 	gnome-rr-config.c	\
1a22712
diff --git a/libgnome-desktop/gnome-bg-crossfade.c b/libgnome-desktop/gnome-bg-crossfade.c
1a22712
new file mode 100644
1a22712
--- /dev/null
1a22712
+++ b/libgnome-desktop/gnome-bg-crossfade.c
dd0a493
@@ -0,0 +1,540 @@
55913d1
+/* gnome-bg-crossfade.h - fade window background between two pixmaps
55913d1
+ *
55913d1
+ * Copyright (C) 2008 Red Hat, Inc.
55913d1
+ *
55913d1
+ * This program is free software; you can redistribute it and/or
55913d1
+ * modify it under the terms of the GNU Library General Public License
55913d1
+ * as published by the Free Software Foundation; either version 2 of
55913d1
+ * the License, or (at your option) any later version.
55913d1
+ *
55913d1
+ * This program is distributed in the hope that it will be useful, but
55913d1
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
55913d1
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
55913d1
+ * Library General Public License for more details.
55913d1
+ *
55913d1
+ * You should have received a copy of the GNU Library General Public
55913d1
+ * License along with this program; if not, write to the Free Software
55913d1
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
55913d1
+ * 02111-1307, USA.
55913d1
+ *
55913d1
+ * Author: Ray Strode <rstrode@redhat.com>
55913d1
+*/
55913d1
+
55913d1
+#include <string.h>
55913d1
+#include <math.h>
55913d1
+#include <stdarg.h>
55913d1
+
55913d1
+#include <gio/gio.h>
55913d1
+
55913d1
+#include <gdk/gdk.h>
55913d1
+#include <gdk/gdkx.h>
55913d1
+#include <X11/Xlib.h>
55913d1
+#include <X11/Xatom.h>
1a22712
+#include <gtk/gtk.h>
55913d1
+
55913d1
+#include <cairo.h>
55913d1
+#include <cairo-xlib.h>
55913d1
+
55913d1
+#define GNOME_DESKTOP_USE_UNSTABLE_API
55913d1
+#include <libgnomeui/gnome-bg.h>
55913d1
+#include "libgnomeui/gnome-bg-crossfade.h"
55913d1
+
55913d1
+struct _GnomeBGCrossfadePrivate
55913d1
+{
1a22712
+	GdkWindow *window;
1a22712
+	int        width;
1a22712
+	int        height;
1a22712
+	GdkPixmap *fading_pixmap;
1a22712
+	GdkPixmap *end_pixmap;
1a22712
+	gdouble    start_time;
1a22712
+	gdouble    total_duration;
1a22712
+	guint      timeout_id;
1a22712
+	guint      is_first_frame : 1;
55913d1
+};
55913d1
+
55913d1
+enum {
55913d1
+	PROP_0,
55913d1
+	PROP_WIDTH,
55913d1
+	PROP_HEIGHT,
55913d1
+};
55913d1
+
55913d1
+enum {
55913d1
+	FINISHED,
55913d1
+	NUMBER_OF_SIGNALS
55913d1
+};
55913d1
+
55913d1
+static guint signals[NUMBER_OF_SIGNALS] = { 0 };
55913d1
+
55913d1
+G_DEFINE_TYPE (GnomeBGCrossfade, gnome_bg_crossfade, G_TYPE_OBJECT)
55913d1
+#define GNOME_BG_CROSSFADE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o),\
55913d1
+			                   GNOME_TYPE_BG_CROSSFADE,\
55913d1
+			                   GnomeBGCrossfadePrivate))
55913d1
+
55913d1
+static void
55913d1
+gnome_bg_crossfade_set_property (GObject      *object,
55913d1
+				 guint         property_id,
55913d1
+				 const GValue *value,
55913d1
+				 GParamSpec   *pspec)
55913d1
+{
55913d1
+	GnomeBGCrossfade *fade;
55913d1
+
55913d1
+	g_assert (GNOME_IS_BG_CROSSFADE (object));
55913d1
+
55913d1
+	fade = GNOME_BG_CROSSFADE (object);
55913d1
+
55913d1
+	switch (property_id)
55913d1
+	{
55913d1
+	case PROP_WIDTH:
55913d1
+		fade->priv->width = g_value_get_int (value);
55913d1
+		break;
55913d1
+	case PROP_HEIGHT:
55913d1
+		fade->priv->height = g_value_get_int (value);
55913d1
+		break;
55913d1
+	default:
55913d1
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
55913d1
+		break;
55913d1
+	}
55913d1
+}
55913d1
+
55913d1
+static void
55913d1
+gnome_bg_crossfade_get_property (GObject    *object,
55913d1
+			     guint       property_id,
55913d1
+			     GValue     *value,
55913d1
+			     GParamSpec *pspec)
55913d1
+{
55913d1
+	GnomeBGCrossfade *fade;
55913d1
+
55913d1
+	g_assert (GNOME_IS_BG_CROSSFADE (object));
55913d1
+
55913d1
+	fade = GNOME_BG_CROSSFADE (object);
55913d1
+
55913d1
+	switch (property_id)
55913d1
+	{
55913d1
+	case PROP_WIDTH:
55913d1
+		g_value_set_int (value, fade->priv->width);
55913d1
+		break;
55913d1
+	case PROP_HEIGHT:
55913d1
+		g_value_set_int (value, fade->priv->height);
55913d1
+		break;
55913d1
+
55913d1
+	default:
55913d1
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
55913d1
+		break;
55913d1
+	}
55913d1
+}
55913d1
+
55913d1
+static void
55913d1
+gnome_bg_crossfade_finalize (GObject *object)
55913d1
+{
55913d1
+	GnomeBGCrossfade *fade;
55913d1
+
55913d1
+	fade = GNOME_BG_CROSSFADE (object);
55913d1
+
dd0a493
+	gnome_bg_crossfade_stop (fade);
dd0a493
+
55913d1
+	if (fade->priv->fading_pixmap != NULL) {
55913d1
+		g_object_unref (fade->priv->fading_pixmap);
55913d1
+		fade->priv->fading_pixmap = NULL;
55913d1
+	}
55913d1
+
55913d1
+	if (fade->priv->end_pixmap != NULL) {
55913d1
+		g_object_unref (fade->priv->end_pixmap);
55913d1
+		fade->priv->end_pixmap = NULL;
55913d1
+	}
55913d1
+}
55913d1
+
55913d1
+static void
55913d1
+gnome_bg_crossfade_class_init (GnomeBGCrossfadeClass *fade_class)
55913d1
+{
55913d1
+	GObjectClass *gobject_class;
55913d1
+
55913d1
+	gobject_class = G_OBJECT_CLASS (fade_class);
55913d1
+
55913d1
+	gobject_class->get_property = gnome_bg_crossfade_get_property;
55913d1
+	gobject_class->set_property = gnome_bg_crossfade_set_property;
55913d1
+	gobject_class->finalize = gnome_bg_crossfade_finalize;
55913d1
+
1a22712
+	/**
1a22712
+	 * GnomeBGCrossfade:width:
1a22712
+	 *
1a22712
+	 * When a crossfade is running, this is width of the fading
1a22712
+	 * pixmap.
1a22712
+	 */
55913d1
+	g_object_class_install_property (gobject_class,
55913d1
+					 PROP_WIDTH,
55913d1
+					 g_param_spec_int ("width",
55913d1
+						           "Window Width",
55913d1
+							    "Width of window to fade",
55913d1
+							    0, G_MAXINT, 0,
55913d1
+							    G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
55913d1
+
1a22712
+	/**
1a22712
+	 * GnomeBGCrossfade:height:
1a22712
+	 *
1a22712
+	 * When a crossfade is running, this is height of the fading
1a22712
+	 * pixmap.
1a22712
+	 */
55913d1
+	g_object_class_install_property (gobject_class,
55913d1
+					 PROP_HEIGHT,
55913d1
+					 g_param_spec_int ("height", "Window Height",
55913d1
+						           "Height of window to fade on",
55913d1
+							   0, G_MAXINT, 0,
55913d1
+							   G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1a22712
+
1a22712
+	/**
1a22712
+	 * GnomeBGCrossfade::finished:
1a22712
+	 * @fade: the #GnomeBGCrossfade that received the signal
1a22712
+	 * @window: the #GdkWindow the crossfade happend on.
1a22712
+	 *
1a22712
+	 * When a crossfade finishes, @window will have a copy
1a22712
+	 * of the end pixmap as its background, and this signal will
1a22712
+	 * get emitted.
1a22712
+	 */
55913d1
+	signals[FINISHED] = g_signal_new ("finished",
55913d1
+					  G_OBJECT_CLASS_TYPE (gobject_class),
55913d1
+					  G_SIGNAL_RUN_LAST, 0, NULL, NULL,
55913d1
+					  g_cclosure_marshal_VOID__OBJECT,
55913d1
+					  G_TYPE_NONE, 1, G_TYPE_OBJECT);
55913d1
+
55913d1
+	g_type_class_add_private (gobject_class, sizeof (GnomeBGCrossfadePrivate));
55913d1
+}
55913d1
+
55913d1
+static void
55913d1
+gnome_bg_crossfade_init (GnomeBGCrossfade *fade)
55913d1
+{
55913d1
+	fade->priv = GNOME_BG_CROSSFADE_GET_PRIVATE (fade);
1a22712
+
1a22712
+	fade->priv->fading_pixmap = NULL;
1a22712
+	fade->priv->end_pixmap = NULL;
1a22712
+	fade->priv->timeout_id = 0;
55913d1
+}
55913d1
+
1a22712
+/**
1a22712
+ * gnome_bg_crossfade_new:
1a22712
+ * @width: The width of the crossfading window
1a22712
+ * @height: The height of the crossfading window
1a22712
+ *
1a22712
+ * Creates a new object to manage crossfading a
1a22712
+ * window background between two #GdkPixmap drawables.
1a22712
+ *
1a22712
+ * Return value: the new #GnomeBGCrossfade
1a22712
+ **/
55913d1
+GnomeBGCrossfade *
55913d1
+gnome_bg_crossfade_new (int width,
55913d1
+			int height)
55913d1
+{
55913d1
+	GObject *object;
55913d1
+
55913d1
+	object = g_object_new (GNOME_TYPE_BG_CROSSFADE,
55913d1
+			       "width", width,
55913d1
+			       "height", height, NULL);
55913d1
+
55913d1
+	return (GnomeBGCrossfade *) object;
55913d1
+}
55913d1
+
55913d1
+static GdkPixmap *
55913d1
+tile_pixmap (GdkPixmap *pixmap,
55913d1
+	     int        width,
55913d1
+	     int        height)
55913d1
+{
55913d1
+	GdkPixmap *copy;
55913d1
+	cairo_t *cr;
55913d1
+
55913d1
+	copy = gdk_pixmap_new (pixmap, width, height, pixmap == NULL? 24 : -1);
55913d1
+
55913d1
+	cr = gdk_cairo_create (copy);
55913d1
+
55913d1
+	if (pixmap != NULL) {
55913d1
+		cairo_pattern_t *pattern;
55913d1
+		gdk_cairo_set_source_pixmap (cr, pixmap, 0.0, 0.0);
55913d1
+		pattern = cairo_get_source (cr);
55913d1
+		cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
55913d1
+	} else {
55913d1
+		GtkStyle *style;
55913d1
+		style = gtk_widget_get_default_style ();
55913d1
+		gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
55913d1
+	}
55913d1
+
55913d1
+	cairo_paint (cr);
55913d1
+
55913d1
+	if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
55913d1
+		g_object_unref (copy);
55913d1
+		copy = NULL;
55913d1
+	}
55913d1
+	cairo_destroy (cr);
55913d1
+
55913d1
+	return copy;
55913d1
+}
55913d1
+
1a22712
+/**
1a22712
+ * gnome_bg_crossfade_set_start_pixmap:
1a22712
+ * @fade: a #GnomeBGCrossfade
1a22712
+ * @pixmap: The #GdkPixmap to fade from
1a22712
+ *
1a22712
+ * Before initiating a crossfade with gnome_bg_crossfade_start()
1a22712
+ * a start and end pixmap have to be set.  This function sets
1a22712
+ * the pixmap shown at the beginning of the crossfade effect.
1a22712
+ *
1a22712
+ * Return value: %TRUE if successful, or %FALSE if the pixmap
1a22712
+ * could not be copied.
1a22712
+ **/
55913d1
+gboolean
55913d1
+gnome_bg_crossfade_set_start_pixmap (GnomeBGCrossfade *fade,
55913d1
+				     GdkPixmap        *pixmap)
55913d1
+{
55913d1
+	g_return_val_if_fail (GNOME_IS_BG_CROSSFADE (fade), FALSE);
55913d1
+
55913d1
+	if (fade->priv->fading_pixmap != NULL) {
55913d1
+		g_object_unref (fade->priv->fading_pixmap);
55913d1
+		fade->priv->fading_pixmap = NULL;
55913d1
+	}
55913d1
+
55913d1
+	fade->priv->fading_pixmap = tile_pixmap (pixmap,
55913d1
+						 fade->priv->width,
55913d1
+						 fade->priv->height);
55913d1
+
55913d1
+	return fade->priv->fading_pixmap != NULL;
55913d1
+}
55913d1
+
1a22712
+/**
1a22712
+ * gnome_bg_crossfade_set_end_pixmap:
1a22712
+ * @fade: a #GnomeBGCrossfade
1a22712
+ * @pixmap: The #GdkPixmap to fade to
1a22712
+ *
1a22712
+ * Before initiating a crossfade with gnome_bg_crossfade_start()
1a22712
+ * a start and end pixmap have to be set.  This function sets
1a22712
+ * the pixmap shown at the end of the crossfade effect.
1a22712
+ *
1a22712
+ * Return value: %TRUE if successful, or %FALSE if the pixmap
1a22712
+ * could not be copied.
1a22712
+ **/
55913d1
+gboolean
55913d1
+gnome_bg_crossfade_set_end_pixmap (GnomeBGCrossfade *fade,
55913d1
+				   GdkPixmap        *pixmap)
55913d1
+{
55913d1
+	g_return_val_if_fail (GNOME_IS_BG_CROSSFADE (fade), FALSE);
55913d1
+
55913d1
+	if (fade->priv->end_pixmap != NULL) {
55913d1
+		g_object_unref (fade->priv->end_pixmap);
55913d1
+		fade->priv->end_pixmap = NULL;
55913d1
+	}
55913d1
+
55913d1
+	fade->priv->end_pixmap = tile_pixmap (pixmap,
55913d1
+					      fade->priv->width,
55913d1
+					      fade->priv->height);
55913d1
+
55913d1
+	return fade->priv->end_pixmap != NULL;
55913d1
+}
55913d1
+
55913d1
+static gdouble
55913d1
+get_current_time (void)
55913d1
+{
1a22712
+	const double microseconds_per_second = (double) G_USEC_PER_SEC;
1a22712
+	double timestamp;
1a22712
+	GTimeVal now;
55913d1
+
55913d1
+	g_get_current_time (&now;;
55913d1
+
55913d1
+	timestamp = ((microseconds_per_second * now.tv_sec) + now.tv_usec) /
55913d1
+	            microseconds_per_second;
55913d1
+
55913d1
+	return timestamp;
55913d1
+}
55913d1
+
55913d1
+static gboolean
55913d1
+animations_are_disabled (GnomeBGCrossfade *fade)
55913d1
+{
55913d1
+	GtkSettings *settings;
55913d1
+	GdkScreen *screen;
55913d1
+	gboolean are_enabled;
55913d1
+
1a22712
+	g_assert (fade->priv->window != NULL);
55913d1
+
55913d1
+	screen = gdk_drawable_get_screen (fade->priv->window);
55913d1
+
55913d1
+	settings = gtk_settings_get_for_screen (screen);
55913d1
+
55913d1
+	g_object_get (settings, "gtk-enable-animations", &are_enabled, NULL);
55913d1
+
55913d1
+	return !are_enabled;
55913d1
+}
55913d1
+
55913d1
+static void
55913d1
+draw_background (GnomeBGCrossfade *fade)
55913d1
+{
55913d1
+	if (GDK_WINDOW_TYPE (fade->priv->window) == GDK_WINDOW_FOREIGN ||
55913d1
+	    GDK_WINDOW_TYPE (fade->priv->window) == GDK_WINDOW_ROOT) {
55913d1
+		GdkDisplay *display;
55913d1
+		display = gdk_drawable_get_display (fade->priv->window);
55913d1
+		gdk_window_clear (fade->priv->window);
55913d1
+		gdk_flush ();
55913d1
+	} else {
55913d1
+		gdk_window_invalidate_rect (fade->priv->window, NULL, FALSE);
55913d1
+		gdk_window_process_updates (fade->priv->window, FALSE);
55913d1
+	}
55913d1
+}
55913d1
+
55913d1
+static gboolean
55913d1
+on_tick (GnomeBGCrossfade *fade)
55913d1
+{
55913d1
+	gdouble now, percent_done;
55913d1
+	cairo_t *cr;
55913d1
+	cairo_status_t status;
55913d1
+
55913d1
+	g_return_val_if_fail (GNOME_IS_BG_CROSSFADE (fade), FALSE);
55913d1
+
55913d1
+	now = get_current_time ();
55913d1
+
55913d1
+	percent_done = (now - fade->priv->start_time) / fade->priv->total_duration;
55913d1
+	percent_done = CLAMP (percent_done, 0.0, 1.0);
55913d1
+
55913d1
+	/* If it's taking a long time to get to the first frame,
1a22712
+	 * then lengthen the duration, so the user will get to see
55913d1
+	 * the effect.
55913d1
+	 */
55913d1
+	if (fade->priv->is_first_frame && percent_done > .33) {
55913d1
+		fade->priv->is_first_frame = FALSE;
55913d1
+		fade->priv->total_duration *= 1.5;
55913d1
+		return on_tick (fade);
55913d1
+	}
55913d1
+
55913d1
+	if (fade->priv->fading_pixmap == NULL) {
55913d1
+		return FALSE;
55913d1
+	}
55913d1
+
55913d1
+	if (animations_are_disabled (fade)) {
55913d1
+		return FALSE;
55913d1
+	}
55913d1
+
55913d1
+	/* We accumulate the results in place for performance reasons.
55913d1
+	 *
55913d1
+	 * This means 1) The fade is exponential, not linear (looks good!)
55913d1
+	 * 2) The rate of fade is not independent of frame rate. Slower machines
55913d1
+	 * will get a slower fade (but never longer than .75 seconds), and
55913d1
+	 * even the fastest machines will get *some* fade because the framerate
55913d1
+	 * is capped.
55913d1
+	 */
55913d1
+	cr = gdk_cairo_create (fade->priv->fading_pixmap);
55913d1
+
55913d1
+	gdk_cairo_set_source_pixmap (cr, fade->priv->end_pixmap,
55913d1
+				     0.0, 0.0);
55913d1
+	cairo_paint_with_alpha (cr, percent_done);
55913d1
+
55913d1
+	status = cairo_status (cr);
55913d1
+	cairo_destroy (cr);
55913d1
+
55913d1
+	if (status == CAIRO_STATUS_SUCCESS) {
55913d1
+		draw_background (fade);
55913d1
+	}
55913d1
+	return percent_done <= .99;
55913d1
+}
55913d1
+
55913d1
+static void
55913d1
+on_finished (GnomeBGCrossfade *fade)
55913d1
+{
dd0a493
+	if (fade->priv->timeout_id == 0)
dd0a493
+		return;
dd0a493
+
1a22712
+	g_assert (fade->priv->end_pixmap != NULL);
55913d1
+
1a22712
+	gdk_window_set_back_pixmap (fade->priv->window,
1a22712
+				    fade->priv->end_pixmap,
1a22712
+				    FALSE);
1a22712
+	draw_background (fade);
55913d1
+
1a22712
+	g_object_unref (fade->priv->end_pixmap);
1a22712
+	fade->priv->end_pixmap = NULL;
1a22712
+
1a22712
+	g_assert (fade->priv->fading_pixmap != NULL);
1a22712
+
1a22712
+	g_object_unref (fade->priv->fading_pixmap);
1a22712
+	fade->priv->fading_pixmap = NULL;
55913d1
+
55913d1
+	fade->priv->timeout_id = 0;
55913d1
+	g_signal_emit (fade, signals[FINISHED], 0, fade->priv->window);
55913d1
+}
55913d1
+
1a22712
+/**
1a22712
+ * gnome_bg_crossfade_start:
1a22712
+ * @fade: a #GnomeBGCrossfade
1a22712
+ * @window: The #GdkWindow to draw crossfade on
1a22712
+ * @context: a #GMainContext to handle animation timeouts
1a22712
+ *           or %NULL for default context
1a22712
+ *
1a22712
+ * This function initiates a quick crossfade between two pixmaps on
1a22712
+ * the background of @window.  Before initiating the crossfade both
1a22712
+ * gnome_bg_crossfade_start() and gnome_bg_crossfade_end() need to
1a22712
+ * be called. If animations are disabled, the crossfade is skipped,
1a22712
+ * and the window background is set immediately to the end pixmap.
1a22712
+ **/
55913d1
+void
55913d1
+gnome_bg_crossfade_start (GnomeBGCrossfade *fade,
55913d1
+			  GdkWindow        *window,
55913d1
+			  GMainContext     *context)
55913d1
+{
55913d1
+	GSource *source;
55913d1
+
55913d1
+	g_return_if_fail (GNOME_IS_BG_CROSSFADE (fade));
1a22712
+	g_return_if_fail (window != NULL);
55913d1
+	g_return_if_fail (fade->priv->fading_pixmap != NULL);
55913d1
+	g_return_if_fail (fade->priv->end_pixmap != NULL);
55913d1
+	g_return_if_fail (!gnome_bg_crossfade_is_started (fade));
55913d1
+
55913d1
+	source = g_timeout_source_new (1000 / 60.0);
55913d1
+	g_source_set_callback (source,
55913d1
+			       (GSourceFunc) on_tick,
55913d1
+			       fade,
55913d1
+			       (GDestroyNotify) on_finished);
55913d1
+	fade->priv->timeout_id = g_source_attach (source, context);
55913d1
+	g_source_unref (source);
55913d1
+
55913d1
+	fade->priv->window = window;
55913d1
+	gdk_window_set_back_pixmap (fade->priv->window,
55913d1
+				    fade->priv->fading_pixmap,
55913d1
+				    FALSE);
1a22712
+	draw_background (fade);
55913d1
+
55913d1
+	fade->priv->is_first_frame = TRUE;
55913d1
+	fade->priv->total_duration = .75;
55913d1
+	fade->priv->start_time = get_current_time ();
55913d1
+}
55913d1
+
1a22712
+
1a22712
+/**
1a22712
+ * gnome_bg_crossfade_is_started:
1a22712
+ * @fade: a #GnomeBGCrossfade
1a22712
+ *
1a22712
+ * This function reveals whether or not @fade is currently
1a22712
+ * running on a window.  See gnome_bg_crossfade_start() for
1a22712
+ * information on how to initiate a crossfade.
1a22712
+ *
1a22712
+ * Return value: %TRUE if fading, or %FALSE if not fading
1a22712
+ **/
55913d1
+gboolean
55913d1
+gnome_bg_crossfade_is_started (GnomeBGCrossfade *fade)
55913d1
+{
55913d1
+	g_return_val_if_fail (GNOME_IS_BG_CROSSFADE (fade), FALSE);
55913d1
+
55913d1
+	return fade->priv->timeout_id != 0;
55913d1
+}
55913d1
+
1a22712
+/**
1a22712
+ * gnome_bg_crossfade_stop:
1a22712
+ * @fade: a #GnomeBGCrossfade
1a22712
+ *
1a22712
+ * This function stops any in progress crossfades that may be
1a22712
+ * happening.  It's harmless to call this function if @fade is
1a22712
+ * already stopped.
1a22712
+ **/
55913d1
+void
55913d1
+gnome_bg_crossfade_stop (GnomeBGCrossfade *fade)
55913d1
+{
55913d1
+	g_return_if_fail (GNOME_IS_BG_CROSSFADE (fade));
55913d1
+
1a22712
+	if (!gnome_bg_crossfade_is_started (fade))
1a22712
+		return;
1a22712
+
1a22712
+	g_assert (fade->priv->timeout_id != 0);
1a22712
+	g_source_remove (fade->priv->timeout_id);
1a22712
+	fade->priv->timeout_id = 0;
55913d1
+}
1a22712
diff --git a/libgnome-desktop/libgnomeui/Makefile.am b/libgnome-desktop/libgnomeui/Makefile.am
1a22712
--- a/libgnome-desktop/libgnomeui/Makefile.am
1a22712
+++ b/libgnome-desktop/libgnomeui/Makefile.am
1a22712
@@ -1,6 +1,7 @@
1a22712
 libgnomeui_desktopdir = $(includedir)/gnome-desktop-2.0/libgnomeui
1a22712
 libgnomeui_desktop_HEADERS =	\
1a22712
 	gnome-bg.h		\
1a22712
+	gnome-bg-crossfade.h	\
1a22712
 	gnome-desktop-thumbnail.h \
1a22712
 	gnome-rr.h		\
1a22712
 	gnome-rr-config.h	\
1a22712
diff --git a/libgnome-desktop/libgnomeui/gnome-bg-crossfade.h b/libgnome-desktop/libgnomeui/gnome-bg-crossfade.h
1a22712
new file mode 100644
1a22712
--- /dev/null
1a22712
+++ b/libgnome-desktop/libgnomeui/gnome-bg-crossfade.h
1a22712
@@ -0,0 +1,75 @@
55913d1
+/* gnome-bg-crossfade.h - fade window background between two pixmaps
55913d1
+
55913d1
+   Copyright 2008, Red Hat, Inc.
55913d1
+
55913d1
+   This file is part of the Gnome Library.
55913d1
+
55913d1
+   The Gnome Library is free software; you can redistribute it and/or
55913d1
+   modify it under the terms of the GNU Library General Public License as
55913d1
+   published by the Free Software Foundation; either version 2 of the
55913d1
+   License, or (at your option) any later version.
1a22712
+
55913d1
+   The Gnome Library is distributed in the hope that it will be useful,
55913d1
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
55913d1
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
55913d1
+   Library General Public License for more details.
1a22712
+
55913d1
+   You should have received a copy of the GNU Library General Public
55913d1
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
55913d1
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
55913d1
+   Boston, MA 02111-1307, USA.
55913d1
+
55913d1
+   Author: Ray Strode <rstrode@redhat.com>
55913d1
+*/
55913d1
+
55913d1
+#ifndef __GNOME_BG_CROSSFADE_H__
55913d1
+#define __GNOME_BG_CROSSFADE_H__
55913d1
+
55913d1
+#ifndef GNOME_DESKTOP_USE_UNSTABLE_API
55913d1
+#error    GnomeBGCrossfade is unstable API. You must define GNOME_DESKTOP_USE_UNSTABLE_API before including gnome-bg-crossfade.h
55913d1
+#endif
55913d1
+
55913d1
+#include <gdk/gdk.h>
55913d1
+
55913d1
+G_BEGIN_DECLS
55913d1
+
55913d1
+#define GNOME_TYPE_BG_CROSSFADE            (gnome_bg_crossfade_get_type ())
55913d1
+#define GNOME_BG_CROSSFADE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_BG_CROSSFADE, GnomeBGCrossfade))
55913d1
+#define GNOME_BG_CROSSFADE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GNOME_TYPE_BG_CROSSFADE, GnomeBGCrossfadeClass))
55913d1
+#define GNOME_IS_BG_CROSSFADE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_BG_CROSSFADE))
55913d1
+#define GNOME_IS_BG_CROSSFADE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GNOME_TYPE_BG_CROSSFADE))
55913d1
+#define GNOME_BG_CROSSFADE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GNOME_TYPE_BG_CROSSFADE, GnomeBGCrossfadeClass))
55913d1
+
55913d1
+typedef struct _GnomeBGCrossfadePrivate GnomeBGCrossfadePrivate;
55913d1
+typedef struct _GnomeBGCrossfade GnomeBGCrossfade;
55913d1
+typedef struct _GnomeBGCrossfadeClass GnomeBGCrossfadeClass;
55913d1
+
55913d1
+struct _GnomeBGCrossfade
55913d1
+{
55913d1
+	GObject parent_object;
55913d1
+
55913d1
+	GnomeBGCrossfadePrivate *priv;
55913d1
+};
55913d1
+
55913d1
+struct _GnomeBGCrossfadeClass
55913d1
+{
55913d1
+	GObjectClass parent_class;
55913d1
+
55913d1
+	void (* finished) (GnomeBGCrossfade *fade, GdkWindow *window);
55913d1
+};
55913d1
+
55913d1
+GType             gnome_bg_crossfade_get_type              (void);
55913d1
+GnomeBGCrossfade *gnome_bg_crossfade_new (int width, int height);
55913d1
+gboolean          gnome_bg_crossfade_set_start_pixmap (GnomeBGCrossfade *fade,
55913d1
+                                                      GdkPixmap *pixmap);
55913d1
+gboolean          gnome_bg_crossfade_set_end_pixmap (GnomeBGCrossfade *fade,
55913d1
+                                                    GdkPixmap *pixmap);
55913d1
+void              gnome_bg_crossfade_start (GnomeBGCrossfade *fade,
55913d1
+                                            GdkWindow        *window,
55913d1
+                                            GMainContext     *context);
55913d1
+gboolean          gnome_bg_crossfade_is_started (GnomeBGCrossfade *fade);
55913d1
+void              gnome_bg_crossfade_stop (GnomeBGCrossfade *fade);
55913d1
+
55913d1
+G_END_DECLS
55913d1
+
55913d1
+#endif
1a22712
1a22712
============================================================
1a22712
 Add docstring for gnome_bg_set_pixmap_as_root
1a22712
1a22712
  I'm going to be adding a parallel api that references
1a22712
this function in the docs, so we should document this
1a22712
one as well.
1a22712
1a22712
diff --git a/libgnome-desktop/gnome-bg.c b/libgnome-desktop/gnome-bg.c
1a22712
--- a/libgnome-desktop/gnome-bg.c
1a22712
+++ b/libgnome-desktop/gnome-bg.c
1a22712
@@ -1105,12 +1105,18 @@ gnome_bg_set_root_pixmap_id (GdkScreen *screen,
1a22712
 			 (guchar *) &pixmap_id, 1);
1a22712
 }
1391ff4
 
1a22712
-/* Set the root pixmap, and properties pointing to it. We
1a22712
+/**
1a22712
+ * gnome_bg_set_pixmap_as_root:
1a22712
+ * @screen: the #GdkScreen to change root background on
1a22712
+ * @pixmap: the #GdkPixmap to set root background from
1a22712
+ *
1a22712
+ * Set the root pixmap, and properties pointing to it. We
1a22712
  * do this atomically with a server grab to make sure that
1a22712
  * we won't leak the pixmap if somebody else it setting
1a22712
  * it at the same time. (This assumes that they follow the
1a22712
- * same conventions we do)
1a22712
- */
1a22712
+ * same conventions we do).  @pixmap should come from a call
1a22712
+ * to gnome_bg_create_pixmap().
1a22712
+ **/
1a22712
 void
1a22712
 gnome_bg_set_pixmap_as_root (GdkScreen *screen, GdkPixmap *pixmap)
1a22712
 {
1a22712
1a22712
============================================================
1a22712
 Add gnome_bg_set_pixmap_as_root_with_crossfade
1a22712
1a22712
  This implements a crossfade transition when switching
1a22712
backgrounds.  To actually get the fade, though, requires
1a22712
changes to gnome-settings-daemon to use this function
1a22712
instead of gnome_bg_set_pixmap_as_root.
1a22712
1a22712
We also expose gnome_bg_get_pixmap_from_root which will
1a22712
be useful for eel when it gets changed to do fading.
1a22712
1a22712
diff --git a/libgnome-desktop/gnome-bg.c b/libgnome-desktop/gnome-bg.c
1a22712
--- a/libgnome-desktop/gnome-bg.c
1a22712
+++ b/libgnome-desktop/gnome-bg.c
1a22712
@@ -38,10 +38,13 @@ Author: Soren Sandmann <sandmann@redhat.com>
1a22712
 #include <X11/Xlib.h>
1a22712
 #include <X11/Xatom.h>
1a22712
 
1a22712
+#include <cairo.h>
1a22712
+
1391ff4
 #include <gconf/gconf-client.h>
1a22712
 
1a22712
 #define GNOME_DESKTOP_USE_UNSTABLE_API
1a22712
 #include <libgnomeui/gnome-bg.h>
1391ff4
+#include <libgnomeui/gnome-bg-crossfade.h>
1a22712
 
1a22712
 #define BG_KEY_DRAW_BACKGROUND    GNOME_BG_KEY_DIR "/draw_background"
1a22712
 #define BG_KEY_PRIMARY_COLOR      GNOME_BG_KEY_DIR "/primary_color"
1a22712
@@ -1054,6 +1057,91 @@ gnome_bg_create_thumbnail (GnomeBG               *bg,
1a22712
 	return result;
1a22712
 }
1a22712
 
1a22712
+/**
1a22712
+ * gnome_bg_get_pixmap_from_root:
1a22712
+ * @screen: a #GdkScreen
1a22712
+ *
1a22712
+ * This function queries the _XROOTPMAP_ID property from
1a22712
+ * the root window associated with @screen to determine
1a22712
+ * the current root window background pixmap and returns
1a22712
+ * a copy of it. If the _XROOTPMAP_ID is not set, then
1a22712
+ * a black pixmap is returned.
1a22712
+ *
1a22712
+ * Return value: a #GdkPixmap if successful or %NULL
1a22712
+ **/
1a22712
+GdkPixmap *
1a22712
+gnome_bg_get_pixmap_from_root (GdkScreen *screen)
1a22712
+{
1a22712
+	int result;
1a22712
+	gint format;
1a22712
+	gulong nitems;
1a22712
+	gulong bytes_after;
1a22712
+	guchar *data;
1a22712
+	Atom type;
1a22712
+	Display *display;
1a22712
+	int screen_num;
1a22712
+	GdkPixmap *pixmap;
1a22712
+	GdkPixmap *source_pixmap;
1a22712
+	int width, height;
1a22712
+	cairo_t *cr;
1a22712
+	cairo_pattern_t *pattern;
1a22712
+
1a22712
+	display = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
1a22712
+	screen_num = gdk_screen_get_number (screen);
1a22712
+
1a22712
+	result = XGetWindowProperty (display,
1a22712
+				     RootWindow (display, screen_num),
1a22712
+				     gdk_x11_get_xatom_by_name ("_XROOTPMAP_ID"),
1a22712
+				     0L, 1L, False, XA_PIXMAP,
1a22712
+				     &type, &format, &nitems, &bytes_after,
1a22712
+				     &data);
1a22712
+	pixmap = NULL;
1a22712
+	source_pixmap = NULL;
1a22712
+
1a22712
+	if (result != Success || type != XA_PIXMAP ||
1a22712
+	    format != 32 || nitems != 1) {
1a22712
+		XFree (data);
1a22712
+		data = NULL;
1a22712
+	}
1a22712
+
1a22712
+	if (data != NULL) {
1a22712
+		source_pixmap = gdk_pixmap_foreign_new (*(Pixmap *) data);
1a22712
+		gdk_drawable_set_colormap (source_pixmap,
1a22712
+					   gdk_screen_get_default_colormap (screen));
1a22712
+	}
1a22712
+
1a22712
+	width = gdk_screen_get_width (screen);
1a22712
+	height = gdk_screen_get_width (screen);
1a22712
+
1a22712
+	pixmap = gdk_pixmap_new (source_pixmap != NULL? source_pixmap :
1a22712
+				 gdk_screen_get_root_window (screen),
1a22712
+				 width, height, -1);
1a22712
+
1a22712
+	cr = gdk_cairo_create (pixmap);
1a22712
+	if (source_pixmap != NULL) {
1a22712
+		gdk_cairo_set_source_pixmap (cr, source_pixmap, 0, 0);
1a22712
+		pattern = cairo_get_source (cr);
1a22712
+		cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
1a22712
+	} else {
1a22712
+		cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1a22712
+	}
1a22712
+	cairo_paint (cr);
1a22712
+
1a22712
+	if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
1a22712
+		g_object_unref (pixmap);
1a22712
+		pixmap = NULL;
1a22712
+	}
1a22712
+	cairo_destroy (cr);
1a22712
+
1a22712
+	if (source_pixmap != NULL)
1a22712
+		g_object_unref (source_pixmap);
1a22712
+
1a22712
+	if (data != NULL)
1a22712
+		XFree (data);
1a22712
+
1a22712
+	return pixmap;
1a22712
+}
1a22712
+
1a22712
 static void
1a22712
 gnome_bg_set_root_pixmap_id (GdkScreen *screen,
1a22712
 			     GdkPixmap *pixmap)
5210646
@@ -1142,6 +1230,55 @@ gnome_bg_set_pixmap_as_root (GdkScreen *screen, GdkPixmap *pixmap)
5210646
 	gdk_x11_display_ungrab (gdk_screen_get_display (screen));
1a22712
 }
1a22712
 
1a22712
+/**
1a22712
+ * gnome_bg_set_pixmap_as_root_with_crossfade:
1a22712
+ * @screen: the #GdkScreen to change root background on
1a22712
+ * @pixmap: the #GdkPixmap to set root background from
1a22712
+ * @context: a #GMainContext or %NULL
1a22712
+ *
1a22712
+ * Set the root pixmap, and properties pointing to it.
1a22712
+ * This function differs from gnome_bg_set_pixmap_as_root()
1a22712
+ * in that it adds a subtle crossfade animation from the
1a22712
+ * current root pixmap to the new one.
1a22712
+ * same conventions we do).
1a22712
+ *
1a22712
+ * Return value: a #GnomeBGCrossfade object
1a22712
+ **/
1a22712
+GnomeBGCrossfade *
1a22712
+gnome_bg_set_pixmap_as_root_with_crossfade (GdkScreen    *screen,
1a22712
+					    GdkPixmap    *pixmap,
1a22712
+					    GMainContext *context)
1a22712
+{
1a22712
+	GdkDisplay *display;
1a22712
+	GdkWindow *root_window;
1a22712
+	GdkPixmap *old_pixmap;
1a22712
+	int      width, height;
1a22712
+	GnomeBGCrossfade *fade;
1a22712
+
1a22712
+	g_return_val_if_fail (screen != NULL, NULL);
1a22712
+	g_return_val_if_fail (pixmap != NULL, NULL);
1a22712
+
1a22712
+	root_window = gdk_screen_get_root_window (screen);
1a22712
+
1a22712
+	width = gdk_screen_get_width (screen);
1a22712
+	height = gdk_screen_get_height (screen);
1a22712
+
1a22712
+	fade = gnome_bg_crossfade_new (width, height);
1a22712
+
1a22712
+	display = gdk_screen_get_display (screen);
1a22712
+	gdk_x11_display_grab (display);
1a22712
+	old_pixmap = gnome_bg_get_pixmap_from_root (screen);
1a22712
+	gnome_bg_set_root_pixmap_id (screen, pixmap);
1a22712
+	gnome_bg_crossfade_set_start_pixmap (fade, old_pixmap);
5210646
+	g_object_unref (old_pixmap);
1a22712
+	gnome_bg_crossfade_set_end_pixmap (fade, pixmap);
1a22712
+	gdk_display_flush (display);
1a22712
+	gdk_x11_display_ungrab (display);
1a22712
+
1a22712
+	gnome_bg_crossfade_start (fade, root_window, context);
1a22712
+
1a22712
+	return fade;
1a22712
+}
1a22712
 
1a22712
 /* Implementation of the pixbuf cache */
1a22712
 struct _SlideShow
1a22712
diff --git a/libgnome-desktop/libgnomeui/gnome-bg.h b/libgnome-desktop/libgnomeui/gnome-bg.h
1a22712
--- a/libgnome-desktop/libgnomeui/gnome-bg.h
1a22712
+++ b/libgnome-desktop/libgnomeui/gnome-bg.h
1a22712
@@ -32,6 +32,7 @@
1a22712
 #include <gdk/gdk.h>
1a22712
 #include <gconf/gconf-client.h>
1391ff4
 #include <libgnomeui/gnome-desktop-thumbnail.h>
1a22712
+#include <libgnomeui/gnome-bg-crossfade.h>
1391ff4
 
1391ff4
 G_BEGIN_DECLS
55913d1
 
1a22712
@@ -109,6 +110,10 @@ gboolean         gnome_bg_changes_with_size     (GnomeBG               *bg);
55913d1
 void             gnome_bg_set_pixmap_as_root    (GdkScreen             *screen,
55913d1
 						 GdkPixmap             *pixmap);
55913d1
 
55913d1
+GnomeBGCrossfade *gnome_bg_set_pixmap_as_root_with_crossfade (GdkScreen *screen,
55913d1
+                                                              GdkPixmap *pixmap,
55913d1
+                                                              GMainContext *context);
55913d1
+GdkPixmap *gnome_bg_get_pixmap_from_root (GdkScreen *screen);
55913d1
 
55913d1
 G_END_DECLS
55913d1