ae4b759
--- gnome-desktop-2.19.90/configure.in.gnome-bg	2007-08-13 20:04:02.000000000 -0400
a599c27
+++ gnome-desktop-2.19.90/configure.in	2007-08-28 14:01:15.000000000 -0400
0c4bf27
@@ -51,10 +51,10 @@
ae4b759
 AC_SUBST(GNOME_DISTRIBUTOR)
ae4b759
 AC_SUBST(GNOME_DATE)
ae4b759
 
ae4b759
-GNOME_COMMON_INIT
ae4b759
-GNOME_DEBUG_CHECK
ae4b759
-GNOME_COMPILE_WARNINGS([maximum])
ae4b759
-GNOME_MAINTAINER_MODE_DEFINES
ae4b759
+#GNOME_COMMON_INIT
ae4b759
+#GNOME_DEBUG_CHECK
ae4b759
+#GNOME_COMPILE_WARNINGS([maximum])
ae4b759
+#GNOME_MAINTAINER_MODE_DEFINES
ae4b759
 
ae4b759
 # As a special favour for vuntz, support --disable-deprecations
ae4b759
 
0c4bf27
--- /dev/null	2007-08-15 18:04:26.337218222 -0400
a599c27
+++ gnome-desktop-2.19.90/libgnome-desktop/gnome-bg.c	2007-08-29 16:17:12.000000000 -0400
a599c27
@@ -0,0 +1,1824 @@
ae4b759
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
ae4b759
+   
ae4b759
+gnomebg.c: Object for the desktop background.
ae4b759
+
ae4b759
+Copyright (C) 2000 Eazel, Inc.
ae4b759
+Copyright (C) 2007 Red Hat, Inc.
ae4b759
+
ae4b759
+This program is free software; you can redistribute it and/or
ae4b759
+modify it under the terms of the GNU Library General Public License as
ae4b759
+published by the Free Software Foundation; either version 2 of the
ae4b759
+License, or (at your option) any later version.
ae4b759
+
ae4b759
+This program is distributed in the hope that it will be useful,
ae4b759
+but WITHOUT ANY WARRANTY; without even the implied warranty of
ae4b759
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
ae4b759
+Library General Public License for more details.
ae4b759
+
ae4b759
+You should have received a copy of the GNU Library General Public
ae4b759
+License along with this program; if not, write to the
ae4b759
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
ae4b759
+Boston, MA 02111-1307, USA.
ae4b759
+
ae4b759
+Derived from eel-background.c and eel-gdk-pixbuf-extensions.c by
ae4b759
+Darin Adler <darin@eazel.com> and Ramiro Estrugo <ramiro@eazel.com>
ae4b759
+
ae4b759
+Author: Soren Sandmann <sandmann@redhat.com>
ae4b759
+
ae4b759
+*/
ae4b759
+
ae4b759
+#include <string.h>
ae4b759
+#include <libgnomeui/gnome-bg.h>
ae4b759
+#include <gdk/gdkx.h>
ae4b759
+#include <libgnomevfs/gnome-vfs-ops.h>
ae4b759
+#include <libgnomeui/libgnomeui.h>
ae4b759
+#include <math.h>
ae4b759
+#include <X11/Xlib.h>
ae4b759
+#include <X11/Xatom.h>
ae4b759
+#include <gdk/gdkx.h>
ae4b759
+#include <libgnomevfs/gnome-vfs-utils.h>
ae4b759
+#include <stdarg.h>
ae4b759
+
ae4b759
+typedef struct _SlideShow SlideShow;
ae4b759
+typedef struct _Slide Slide;
ae4b759
+
b979214
+struct _Slide
b979214
+{
b979214
+	double   duration;		/* in seconds */
b979214
+	gboolean fixed;	
b979214
+	
b979214
+	char *file1;
b979214
+	char *file2;		/* NULL if fixed is TRUE */
b979214
+};
b979214
+
ae4b759
+/* This is the size of the GdkRGB dither matrix, in order to avoid
ae4b759
+ * bad dithering when tiling the gradient
ae4b759
+ */
ae4b759
+#define GRADIENT_PIXMAP_TILE_SIZE 128
ae4b759
+
a599c27
+typedef struct FileCacheEntry FileCacheEntry;
a599c27
+#define CACHE_SIZE 4
a599c27
+
ae4b759
+/*
ae4b759
+ *   Implementation of the GnomeBG class
ae4b759
+ */
ae4b759
+struct _GnomeBG
ae4b759
+{
ae4b759
+	GObject			parent_instance;
ae4b759
+	
ae4b759
+	char *			uri;
ae4b759
+	GnomeBGPlacement	placement;
ae4b759
+	GnomeBGColorType	color_type;
ae4b759
+	GdkColor		c1;
ae4b759
+	GdkColor		c2;
ae4b759
+	
ae4b759
+	/* Cached information, only access through cache accessor functions */
ae4b759
+        SlideShow *		slideshow;
ae4b759
+	time_t			uri_mtime;
ae4b759
+	GdkPixbuf *		pixbuf_cache;
ae4b759
+	int			timeout_id;
a599c27
+
a599c27
+	GList *		        file_cache;
ae4b759
+};
ae4b759
+
ae4b759
+struct _GnomeBGClass
ae4b759
+{
ae4b759
+	GObjectClass parent_class;
ae4b759
+};
ae4b759
+
ae4b759
+enum {
ae4b759
+	CHANGED,
ae4b759
+	N_SIGNALS
ae4b759
+};
ae4b759
+
ae4b759
+static guint signals[N_SIGNALS] = { 0 };
ae4b759
+
ae4b759
+G_DEFINE_TYPE (GnomeBG, gnome_bg, G_TYPE_OBJECT);
ae4b759
+
ae4b759
+static GdkPixmap *make_root_pixmap     (GdkScreen  *screen,
ae4b759
+					gint        width,
ae4b759
+					gint        height);
ae4b759
+
ae4b759
+/* Pixbuf utils */
ae4b759
+static guint32    pixbuf_average_value (GdkPixbuf  *pixbuf);
ae4b759
+static GdkPixbuf *pixbuf_scale_to_fit  (GdkPixbuf  *src,
ae4b759
+					int         max_width,
ae4b759
+					int         max_height);
ae4b759
+static GdkPixbuf *pixbuf_scale_to_min  (GdkPixbuf  *src,
ae4b759
+					int         min_width,
ae4b759
+					int         min_height);
ae4b759
+static void       pixbuf_draw_gradient (GdkPixbuf  *pixbuf,
ae4b759
+					gboolean    horizontal,
ae4b759
+					GdkColor   *c1,
ae4b759
+					GdkColor   *c2);
ae4b759
+static void       pixbuf_tile          (GdkPixbuf  *src,
ae4b759
+					GdkPixbuf  *dest);
ae4b759
+static void       pixbuf_blend         (GdkPixbuf  *src,
ae4b759
+					GdkPixbuf  *dest,
ae4b759
+					int         src_x,
ae4b759
+					int         src_y,
ae4b759
+					int         width,
ae4b759
+					int         height,
ae4b759
+					int         dest_x,
ae4b759
+					int         dest_y,
ae4b759
+					double      alpha);
ae4b759
+
ae4b759
+/* Thumbnail utilities */
a599c27
+static GdkPixbuf *create_thumbnail_for_uri (GnomeThumbnailFactory *factory,
a599c27
+					    const char            *uri);
ae4b759
+static gboolean   get_thumb_annotations (GdkPixbuf             *thumb,
ae4b759
+					 int                   *orig_width,
ae4b759
+					 int                   *orig_height);
ae4b759
+
ae4b759
+/* Cache */
ae4b759
+static GdkPixbuf *get_pixbuf           (GnomeBG               *bg);
ae4b759
+static void       clear_cache          (GnomeBG               *bg);
ae4b759
+static gboolean   is_different         (GnomeBG               *bg,
ae4b759
+					const char            *uri);
ae4b759
+static time_t     get_mtime            (const char            *uri);
ae4b759
+static GdkPixbuf *create_img_thumbnail (GnomeBG               *bg,
ae4b759
+					GnomeThumbnailFactory *factory,
ae4b759
+					GdkScreen             *screen,
ae4b759
+					int                    dest_width,
ae4b759
+					int                    dest_height);
b979214
+static SlideShow * get_as_slideshow    (GnomeBG               *bg, 
b979214
+					const char 	      *uri);
b979214
+static Slide *     get_current_slide   (SlideShow 	      *show,
b979214
+		   			double    	      *alpha);
ae4b759
+
ae4b759
+static void
ae4b759
+gnome_bg_init (GnomeBG *bg)
ae4b759
+{
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+gnome_bg_finalize (GObject *object)
ae4b759
+{
ae4b759
+	GnomeBG *bg = GNOME_BG (object);
ae4b759
+	
ae4b759
+	clear_cache (bg);
ae4b759
+	
ae4b759
+	G_OBJECT_CLASS (gnome_bg_parent_class)->finalize (object);
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+gnome_bg_class_init (GnomeBGClass *class)
ae4b759
+{
ae4b759
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
ae4b759
+	
ae4b759
+	object_class->finalize = gnome_bg_finalize;
ae4b759
+	
ae4b759
+	signals[CHANGED] = g_signal_new ("changed",
ae4b759
+					 G_OBJECT_CLASS_TYPE (object_class),
ae4b759
+					 G_SIGNAL_RUN_LAST,
ae4b759
+					 0,
ae4b759
+					 NULL, NULL,
ae4b759
+					 g_cclosure_marshal_VOID__VOID,
ae4b759
+					 G_TYPE_NONE, 0);
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+emit_changed (GnomeBG *bg)
ae4b759
+{
ae4b759
+	g_signal_emit (G_OBJECT (bg), signals[CHANGED], 0);
ae4b759
+}
ae4b759
+
ae4b759
+GnomeBG *
ae4b759
+gnome_bg_new (void)
ae4b759
+{
ae4b759
+	return g_object_new (GNOME_TYPE_BG, NULL);
ae4b759
+}
ae4b759
+
ae4b759
+static gboolean
ae4b759
+colors_equal (const GdkColor *c1, const GdkColor *c2)
ae4b759
+{
ae4b759
+	return  c1->red   == c2->red	&&
ae4b759
+		c1->green == c2->green  &&
ae4b759
+		c1->blue  == c2->blue;
ae4b759
+}
ae4b759
+
ae4b759
+void
ae4b759
+gnome_bg_set_color (GnomeBG *bg,
ae4b759
+		    GnomeBGColorType type,
ae4b759
+		    GdkColor *c1,
ae4b759
+		    GdkColor *c2)
ae4b759
+{
ae4b759
+	g_return_if_fail (bg != NULL);
ae4b759
+	
ae4b759
+	if (bg->color_type != type			||
ae4b759
+	    !colors_equal (&bg->c1, c1)			||
ae4b759
+	    (c2 && !colors_equal (&bg->c2, c2))) {
ae4b759
+		
ae4b759
+		bg->color_type = type;
ae4b759
+		bg->c1 = *c1;	
ae4b759
+		if (c2) {
ae4b759
+			bg->c2 = *c2;
ae4b759
+		}
ae4b759
+		
ae4b759
+		emit_changed (bg);
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+void
ae4b759
+gnome_bg_set_placement (GnomeBG          *bg,
ae4b759
+			GnomeBGPlacement  placement)
ae4b759
+{
ae4b759
+	g_return_if_fail (bg != NULL);
ae4b759
+	
ae4b759
+	if (bg->placement != placement) {
ae4b759
+		bg->placement = placement;
ae4b759
+		
ae4b759
+		emit_changed (bg);
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+void
ae4b759
+gnome_bg_set_uri (GnomeBG     *bg,
ae4b759
+		  const char  *uri)
ae4b759
+{
ae4b759
+	g_return_if_fail (bg != NULL);
ae4b759
+	
ae4b759
+	if (is_different (bg, uri)) {
ae4b759
+		char *tmp = g_strdup (uri);
ae4b759
+		
ae4b759
+		/* FIXME: maybe check that it is local */
ae4b759
+		
ae4b759
+		g_free (bg->uri);
ae4b759
+		
ae4b759
+		bg->uri = tmp;
ae4b759
+		
ae4b759
+		clear_cache (bg);
ae4b759
+		
ae4b759
+		emit_changed (bg);
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+draw_color (GnomeBG *bg, GdkPixbuf *dest)
ae4b759
+{
ae4b759
+	guint32 pixel;
ae4b759
+	
ae4b759
+	switch (bg->color_type)
ae4b759
+	{
ae4b759
+	case GNOME_BG_COLOR_SOLID:
ae4b759
+		pixel = ((bg->c1.red >> 8) << 24)      |
ae4b759
+			((bg->c1.green >> 8) << 16)    |
ae4b759
+			((bg->c1.blue >> 8) << 8)      |
ae4b759
+			(0xff);
ae4b759
+		
ae4b759
+		gdk_pixbuf_fill (dest, pixel);
ae4b759
+		break;
ae4b759
+		
ae4b759
+	case GNOME_BG_COLOR_H_GRADIENT:
ae4b759
+		pixbuf_draw_gradient (dest, TRUE, &(bg->c1), &(bg->c2));
ae4b759
+		break;
ae4b759
+		
ae4b759
+	case GNOME_BG_COLOR_V_GRADIENT:
ae4b759
+		pixbuf_draw_gradient (dest, FALSE, &(bg->c1), &(bg->c2));
ae4b759
+		break;
ae4b759
+		
ae4b759
+	default:
ae4b759
+		break;
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+static GdkPixbuf *
ae4b759
+get_scaled_pixbuf (GnomeBGPlacement placement,
ae4b759
+		   GdkPixbuf *pixbuf,
ae4b759
+		   int width, int height,
ae4b759
+		   int *x, int *y, int *w, int *h)
ae4b759
+{
ae4b759
+	GdkPixbuf *new;
ae4b759
+	
ae4b759
+#if 0
ae4b759
+	g_print ("original_width: %d %d\n",
ae4b759
+		 gdk_pixbuf_get_width (pixbuf),
ae4b759
+		 gdk_pixbuf_get_height (pixbuf));
ae4b759
+#endif
ae4b759
+	
ae4b759
+	switch (placement) {
ae4b759
+	case GNOME_BG_PLACEMENT_ZOOMED:
ae4b759
+		new = pixbuf_scale_to_min (pixbuf, width, height);
ae4b759
+		break;
ae4b759
+		
ae4b759
+	case GNOME_BG_PLACEMENT_CENTERED:
ae4b759
+		new = g_object_ref (pixbuf);
ae4b759
+		break;
ae4b759
+		
ae4b759
+	case GNOME_BG_PLACEMENT_FILL_SCREEN:
ae4b759
+		new = gdk_pixbuf_scale_simple (pixbuf, width, height,
ae4b759
+					       GDK_INTERP_BILINEAR);
ae4b759
+		break;
ae4b759
+		
ae4b759
+	case GNOME_BG_PLACEMENT_SCALED:
ae4b759
+		new = pixbuf_scale_to_fit (pixbuf, width, height);
ae4b759
+		break;
ae4b759
+		
ae4b759
+	case GNOME_BG_PLACEMENT_TILED:
ae4b759
+	default:
ae4b759
+		new = g_object_ref (pixbuf);
ae4b759
+		break;
ae4b759
+	}
ae4b759
+	
ae4b759
+	*w = gdk_pixbuf_get_width (new);
ae4b759
+	*h = gdk_pixbuf_get_height (new);
ae4b759
+	*x = (width - *w) / 2;
ae4b759
+	*y = (height - *h) / 2;
ae4b759
+	
ae4b759
+	return new;
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+draw_image (GnomeBGPlacement  placement,
ae4b759
+	    GdkPixbuf        *pixbuf,
ae4b759
+	    GdkPixbuf        *dest)
ae4b759
+{
ae4b759
+	int dest_width = gdk_pixbuf_get_width (dest);
ae4b759
+	int dest_height = gdk_pixbuf_get_height (dest);
ae4b759
+	int x, y, w, h;
ae4b759
+	GdkPixbuf *scaled;
ae4b759
+	
ae4b759
+	if (!pixbuf)
ae4b759
+		return;
ae4b759
+	
ae4b759
+	scaled = get_scaled_pixbuf (
ae4b759
+		placement, pixbuf, dest_width, dest_height, &x, &y, &w, &h);
ae4b759
+	
ae4b759
+	switch (placement) {
ae4b759
+	case GNOME_BG_PLACEMENT_TILED:
ae4b759
+		pixbuf_tile (scaled, dest);
ae4b759
+		break;
ae4b759
+	case GNOME_BG_PLACEMENT_ZOOMED:
ae4b759
+	case GNOME_BG_PLACEMENT_CENTERED:
ae4b759
+	case GNOME_BG_PLACEMENT_FILL_SCREEN:
ae4b759
+	case GNOME_BG_PLACEMENT_SCALED:
ae4b759
+		pixbuf_blend (scaled, dest, 0, 0, w, h, x, y, 1.0);
ae4b759
+		break;
ae4b759
+	default:
ae4b759
+		g_assert_not_reached ();
ae4b759
+		break;
ae4b759
+	}
ae4b759
+	
ae4b759
+	g_object_unref (scaled);
ae4b759
+}
ae4b759
+
ae4b759
+void
ae4b759
+gnome_bg_draw (GnomeBG *bg, GdkPixbuf *dest)
ae4b759
+{
ae4b759
+	if (!bg)
ae4b759
+		return;
ae4b759
+	
ae4b759
+	draw_color (bg, dest);
ae4b759
+	
ae4b759
+	draw_image (bg->placement, get_pixbuf (bg), dest);
ae4b759
+}
ae4b759
+
ae4b759
+gboolean
ae4b759
+gnome_bg_changes_with_size (GnomeBG *bg)
ae4b759
+{
ae4b759
+	g_return_val_if_fail (bg != NULL, FALSE);
ae4b759
+	
ae4b759
+	if (bg->color_type != GNOME_BG_COLOR_SOLID) {
ae4b759
+		if (!get_pixbuf (bg))
ae4b759
+			return TRUE;
ae4b759
+		if (gdk_pixbuf_get_has_alpha (get_pixbuf (bg)))
ae4b759
+			return TRUE;
ae4b759
+		if (bg->placement == GNOME_BG_PLACEMENT_CENTERED)
ae4b759
+			return TRUE;
ae4b759
+		return FALSE;
ae4b759
+	}
ae4b759
+	else if (bg->placement == GNOME_BG_PLACEMENT_TILED) {
ae4b759
+		return FALSE;
ae4b759
+	}
ae4b759
+	else {
ae4b759
+		return TRUE;
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+gnome_bg_get_pixmap_size (GnomeBG  *bg,
ae4b759
+			  int       width,
ae4b759
+			  int       height,
ae4b759
+			  int      *pixmap_width,
ae4b759
+			  int      *pixmap_height)
ae4b759
+{
ae4b759
+	int dummy;
ae4b759
+	int pb_width, pb_height;
ae4b759
+	
ae4b759
+	if (!pixmap_width)
ae4b759
+		pixmap_width = &dummy;
ae4b759
+	if (!pixmap_height)
ae4b759
+		pixmap_height = &dummy;
ae4b759
+	
ae4b759
+	*pixmap_width = width;
ae4b759
+	*pixmap_height = height;
ae4b759
+	
ae4b759
+	if (!get_pixbuf (bg)) {
ae4b759
+		switch (bg->color_type) {
ae4b759
+		case GNOME_BG_COLOR_SOLID:
ae4b759
+			*pixmap_width = 1;
ae4b759
+			*pixmap_height = 1;
ae4b759
+			break;
ae4b759
+			
ae4b759
+		case GNOME_BG_COLOR_H_GRADIENT:
ae4b759
+			*pixmap_width = width;
ae4b759
+			*pixmap_height = GRADIENT_PIXMAP_TILE_SIZE;
ae4b759
+			break;
ae4b759
+			
ae4b759
+		case GNOME_BG_COLOR_V_GRADIENT:
ae4b759
+			*pixmap_width = GRADIENT_PIXMAP_TILE_SIZE;
ae4b759
+			*pixmap_height = height;
ae4b759
+			break;
ae4b759
+		}
ae4b759
+		
ae4b759
+		return;
ae4b759
+	}
ae4b759
+	
ae4b759
+	pb_width = gdk_pixbuf_get_width (get_pixbuf (bg));
ae4b759
+	pb_height = gdk_pixbuf_get_height (get_pixbuf (bg));
ae4b759
+	
ae4b759
+	if (bg->placement == GNOME_BG_PLACEMENT_TILED) {
ae4b759
+		if (gdk_pixbuf_get_has_alpha (get_pixbuf (bg)) &&
ae4b759
+		    bg->color_type != GNOME_BG_COLOR_SOLID) {
ae4b759
+			if (bg->color_type == GNOME_BG_COLOR_H_GRADIENT) {
ae4b759
+				/* FIXME: Should this be
ae4b759
+				 * MAX (GRADIENT_TILE_SIZE, pb_height)?
ae4b759
+				 */
ae4b759
+				*pixmap_height = pb_height; 
ae4b759
+				*pixmap_width = width;
ae4b759
+			}
ae4b759
+			else {
ae4b759
+				/* FIXME: Should this be
ae4b759
+				 * MAX (GRAIDENT_TILE_SIZE, pb_width? */
ae4b759
+				*pixmap_width = pb_width;
ae4b759
+				*pixmap_height = height;
ae4b759
+			}
ae4b759
+		}
ae4b759
+		else {
ae4b759
+			*pixmap_width = pb_width;
ae4b759
+			*pixmap_height = pb_height;
ae4b759
+		}
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+/**
ae4b759
+ * gnome_bg_get_pixmap:
ae4b759
+ * @bg: GnomeBG 
ae4b759
+ * @window: 
ae4b759
+ * @width: 
ae4b759
+ * @height:
ae4b759
+ *
ae4b759
+ * Create a pixmap that can be set as background for @window. If @root is TRUE,
ae4b759
+ * the pixmap created will be created by a temporary X server connection so
ae4b759
+ * that if someone calls XKillClient on it, it won't affect the application who
ae4b759
+ * created it.
ae4b759
+ *
ae4b759
+ * Since: 2.20
ae4b759
+ **/
ae4b759
+GdkPixmap *
ae4b759
+gnome_bg_create_pixmap (GnomeBG	    *bg,
ae4b759
+			GdkWindow   *window,
ae4b759
+			int	     width,
ae4b759
+			int	     height,
ae4b759
+			gboolean     root)
ae4b759
+{
ae4b759
+	int pm_width, pm_height;
ae4b759
+	GdkPixmap *pixmap;
ae4b759
+	
ae4b759
+	g_return_val_if_fail (bg != NULL, NULL);
ae4b759
+	g_return_val_if_fail (window != NULL, NULL);
ae4b759
+	
ae4b759
+	gnome_bg_get_pixmap_size (bg, width, height, &pm_width, &pm_height);
ae4b759
+	
ae4b759
+	if (root) {
ae4b759
+		pixmap = make_root_pixmap (gdk_drawable_get_screen (window),
ae4b759
+					   pm_width, pm_height);
ae4b759
+	}
ae4b759
+	else {
ae4b759
+		pixmap = gdk_pixmap_new (window, pm_width, pm_height, -1);
ae4b759
+	}
ae4b759
+	
ae4b759
+	if (!get_pixbuf (bg) && bg->color_type == GNOME_BG_COLOR_SOLID) {
ae4b759
+		GdkGC *gc = gdk_gc_new (pixmap);
ae4b759
+		gdk_gc_set_rgb_fg_color (gc, &(bg->c1));
ae4b759
+		
ae4b759
+		gdk_draw_point (pixmap, gc, 0, 0);
ae4b759
+		
ae4b759
+		g_object_unref (gc);
ae4b759
+	}
ae4b759
+	else {
ae4b759
+		GdkPixbuf *pixbuf;
ae4b759
+		
ae4b759
+		pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
ae4b759
+					 width, height);
ae4b759
+		gnome_bg_draw (bg, pixbuf);
ae4b759
+		gdk_draw_pixbuf (pixmap, NULL, pixbuf,
ae4b759
+				 0, 0,
ae4b759
+				 0, 0, width, height,
ae4b759
+				 GDK_RGB_DITHER_MAX, 0, 0);
ae4b759
+		g_object_unref (pixbuf);
ae4b759
+	}
ae4b759
+	
ae4b759
+	return pixmap;
ae4b759
+}
ae4b759
+
ae4b759
+
ae4b759
+/* determine if a background is darker or lighter than average, to help
ae4b759
+ * clients know what colors to draw on top with
ae4b759
+ */
ae4b759
+gboolean
ae4b759
+gnome_bg_is_dark (GnomeBG *bg)
ae4b759
+{
ae4b759
+	GdkColor color;
ae4b759
+	int intensity;
ae4b759
+	
ae4b759
+	g_return_val_if_fail (bg != NULL, FALSE);
ae4b759
+	
ae4b759
+	if (bg->color_type == GNOME_BG_COLOR_SOLID) {
ae4b759
+		color = bg->c1;
ae4b759
+	} else {
ae4b759
+		color.red = (bg->c1.red + bg->c2.red) / 2;
ae4b759
+		color.green = (bg->c1.green + bg->c2.green) / 2;
ae4b759
+		color.blue = (bg->c1.blue + bg->c2.blue) / 2;
ae4b759
+	}
ae4b759
+	
ae4b759
+	if (get_pixbuf (bg)) {
ae4b759
+		guint32 argb = pixbuf_average_value (get_pixbuf (bg));
ae4b759
+		guchar a = (argb >> 24) & 0xff;
ae4b759
+		guchar r = (argb >> 16) & 0xff;
ae4b759
+		guchar g = (argb >>  8) & 0xff;
ae4b759
+		guchar b = (argb >>  0) & 0xff;
ae4b759
+		
ae4b759
+		color.red = (color.red * (0xFF - a) + r * 0x101 * a) / 0xFF;
ae4b759
+		color.green = (color.green * (0xFF - a) + g * 0x101 * a) / 0xFF;
ae4b759
+		color.blue = (color.blue * (0xFF - a) + b * 0x101 * a) / 0xFF;
ae4b759
+	}
ae4b759
+	
ae4b759
+	intensity = (color.red * 77 +
ae4b759
+		     color.green * 150 +
ae4b759
+		     color.blue * 28) >> 16;
ae4b759
+	
ae4b759
+	return intensity < 160; /* biased slightly to be dark */
ae4b759
+}
ae4b759
+
ae4b759
+void
ae4b759
+gnome_bg_free (GnomeBG *bg)
ae4b759
+{
ae4b759
+	g_return_if_fail (bg != NULL);
ae4b759
+	
ae4b759
+	g_object_unref (G_OBJECT (bg));
ae4b759
+}
ae4b759
+
ae4b759
+/* 
ae4b759
+ * Create a persistent pixmap. We create a separate display
ae4b759
+ * and set the closedown mode on it to RetainPermanent.
ae4b759
+ */
ae4b759
+static GdkPixmap *
ae4b759
+make_root_pixmap (GdkScreen *screen, gint width, gint height)
ae4b759
+{
ae4b759
+	Display *display;
ae4b759
+        const char *display_name;
ae4b759
+	Pixmap result;
ae4b759
+	GdkPixmap *gdk_pixmap;
ae4b759
+	int screen_num;
ae4b759
+	
ae4b759
+	screen_num = gdk_screen_get_number (screen);
ae4b759
+	
ae4b759
+	gdk_flush ();
ae4b759
+	
ae4b759
+	display_name = gdk_display_get_name (gdk_screen_get_display (screen));
ae4b759
+	display = XOpenDisplay (display_name);
ae4b759
+	
ae4b759
+        if (display == NULL) {
ae4b759
+                g_warning ("Unable to open display '%s' when setting "
ae4b759
+			   "background pixmap\n",
ae4b759
+                           (display_name) ? display_name : "NULL");
ae4b759
+                return NULL;
ae4b759
+        }
ae4b759
+	
ae4b759
+	/* Desktop background pixmap should be created from 
ae4b759
+	 * dummy X client since most applications will try to
ae4b759
+	 * kill it with XKillClient later when changing pixmap
ae4b759
+	 */
ae4b759
+	
ae4b759
+	XSetCloseDownMode (display, RetainPermanent);
ae4b759
+	
ae4b759
+	result = XCreatePixmap (display,
ae4b759
+				RootWindow (display, screen_num),
ae4b759
+				width, height,
ae4b759
+				DefaultDepth (display, screen_num));
ae4b759
+	
ae4b759
+	XCloseDisplay (display);
ae4b759
+	
ae4b759
+	gdk_pixmap = gdk_pixmap_foreign_new (result);
ae4b759
+	gdk_drawable_set_colormap (
ae4b759
+		GDK_DRAWABLE (gdk_pixmap),
ae4b759
+		gdk_drawable_get_colormap (gdk_screen_get_root_window (screen)));
ae4b759
+	
ae4b759
+	return gdk_pixmap;
ae4b759
+}
ae4b759
+
ae4b759
+static gboolean
ae4b759
+get_original_size (const char *uri,
ae4b759
+		   int        *orig_width,
ae4b759
+		   int        *orig_height)
ae4b759
+{
b979214
+	char *filename;
b979214
+	gboolean result;
b979214
+
b979214
+	if (g_str_has_prefix (uri, "file:"))
b979214
+		filename = gnome_vfs_get_local_path_from_uri (uri);
b979214
+	else 
0c4bf27
+		filename = g_strdup (uri);
ae4b759
+	
0c4bf27
+        if (gdk_pixbuf_get_file_info (filename, orig_width, orig_height))
0c4bf27
+		result = TRUE;
0c4bf27
+	else
0c4bf27
+		result = FALSE;
ae4b759
+	
0c4bf27
+	g_free (filename);
b979214
+
b979214
+	return result;
ae4b759
+}
ae4b759
+
ae4b759
+gboolean
ae4b759
+gnome_bg_get_image_size (GnomeBG	       *bg,
ae4b759
+			 GnomeThumbnailFactory *factory,
ae4b759
+			 int		       *width,
ae4b759
+			 int		       *height)
ae4b759
+{
ae4b759
+	GdkPixbuf *thumb;
ae4b759
+	gboolean result = FALSE;
b979214
+	const gchar *uri;
ae4b759
+	
ae4b759
+	g_return_val_if_fail (bg != NULL, FALSE);
ae4b759
+	g_return_val_if_fail (factory != NULL, FALSE);
ae4b759
+	
ae4b759
+	if (!bg->uri)
ae4b759
+		return FALSE;
ae4b759
+	
b979214
+	uri = bg->uri;
a599c27
+	thumb = create_thumbnail_for_uri (factory, uri);
ae4b759
+	
b979214
+	if (!thumb) {
b979214
+		SlideShow *show = get_as_slideshow (bg, bg->uri);
b979214
+		if (show) {
b979214
+			double alpha;
b979214
+			Slide *slide = get_current_slide (show, &alpha);
b979214
+			uri = slide->file1;
a599c27
+			thumb = create_thumbnail_for_uri (factory, uri);
b979214
+		}
b979214
+	}
b979214
+
ae4b759
+	if (thumb) {
ae4b759
+		if (get_thumb_annotations (thumb, width, height))
ae4b759
+			result = TRUE;
ae4b759
+		
ae4b759
+		g_object_unref (thumb);
ae4b759
+	}
b979214
+
b979214
+	if (!result) {
b979214
+		if (get_original_size (uri, width, height))
b979214
+			result = TRUE;
b979214
+	}
b979214
+
ae4b759
+	return result;
ae4b759
+}
ae4b759
+
ae4b759
+static double
ae4b759
+fit_factor (int from_width, int from_height,
ae4b759
+	    int to_width,   int to_height)
ae4b759
+{
ae4b759
+	return MIN (to_width  / (double) from_width, to_height / (double) from_height);
ae4b759
+}
ae4b759
+
ae4b759
+GdkPixbuf *
ae4b759
+gnome_bg_create_thumbnail (GnomeBG               *bg,
ae4b759
+			   GnomeThumbnailFactory *factory,
ae4b759
+			   GdkScreen             *screen,
ae4b759
+			   int                    dest_width,
ae4b759
+			   int                    dest_height)
ae4b759
+{
ae4b759
+	GdkPixbuf *result;
ae4b759
+	GdkPixbuf *thumb;
ae4b759
+	
ae4b759
+	g_return_val_if_fail (bg != NULL, NULL);
ae4b759
+	
ae4b759
+	result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, dest_width, dest_height);
ae4b759
+	
ae4b759
+	draw_color (bg, result);
ae4b759
+	
ae4b759
+	thumb = create_img_thumbnail (bg, factory, screen, dest_width, dest_height);
ae4b759
+	
ae4b759
+	if (thumb) {
ae4b759
+		draw_image (bg->placement, thumb, result);
ae4b759
+		g_object_unref (thumb);
ae4b759
+	}
ae4b759
+	
ae4b759
+	return result;
ae4b759
+}
ae4b759
+
ae4b759
+
ae4b759
+/* Set the root pixmap, and properties pointing to it. We
ae4b759
+ * do this atomically with XGrabServer to make sure that
ae4b759
+ * we won't leak the pixmap if somebody else it setting
ae4b759
+ * it at the same time. (This assumes that they follow the
ae4b759
+ * same conventions we do)
ae4b759
+ */
ae4b759
+void 
ae4b759
+gnome_bg_set_pixmap_as_root (GdkScreen *screen, GdkPixmap *pixmap)
ae4b759
+{
ae4b759
+	int      result;
ae4b759
+	gint     format;
ae4b759
+	gulong   nitems;
ae4b759
+	gulong   bytes_after;
ae4b759
+	guchar  *data_esetroot;
ae4b759
+	Pixmap   pixmap_id;
ae4b759
+	Atom     type;
ae4b759
+	Display *display;
ae4b759
+	int      screen_num;
ae4b759
+	
ae4b759
+	g_return_if_fail (screen != NULL);
ae4b759
+	g_return_if_fail (pixmap != NULL);
ae4b759
+	
ae4b759
+	screen_num = gdk_screen_get_number (screen);
ae4b759
+	
ae4b759
+	data_esetroot = NULL;
ae4b759
+	display = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
ae4b759
+	
ae4b759
+	XGrabServer (display);
ae4b759
+	
ae4b759
+	result = XGetWindowProperty (
ae4b759
+		display, RootWindow (display, screen_num),
ae4b759
+		gdk_x11_get_xatom_by_name ("ESETROOT_PMAP_ID"),
ae4b759
+		0L, 1L, False, XA_PIXMAP,
ae4b759
+		&type, &format, &nitems, &bytes_after,
ae4b759
+		&data_esetroot);
ae4b759
+	
ae4b759
+	if (data_esetroot != NULL) {
ae4b759
+		if (result == Success && type == XA_PIXMAP &&
ae4b759
+		    format == 32 &&
ae4b759
+		    nitems == 1) {
ae4b759
+			gdk_error_trap_push ();
ae4b759
+			XKillClient (display, *(Pixmap *)data_esetroot);
ae4b759
+			gdk_flush ();
ae4b759
+			gdk_error_trap_pop ();
ae4b759
+		}
ae4b759
+		XFree (data_esetroot);
ae4b759
+	}
ae4b759
+	
ae4b759
+	pixmap_id = GDK_WINDOW_XWINDOW (pixmap);
ae4b759
+	
ae4b759
+	XChangeProperty (display, RootWindow (display, screen_num),
ae4b759
+			 gdk_x11_get_xatom_by_name ("ESETROOT_PMAP_ID"),
ae4b759
+			 XA_PIXMAP, 32, PropModeReplace,
ae4b759
+			 (guchar *) &pixmap_id, 1);
ae4b759
+	XChangeProperty (display, RootWindow (display, screen_num),
ae4b759
+			 gdk_x11_get_xatom_by_name ("_XROOTPMAP_ID"), XA_PIXMAP,
ae4b759
+			 32, PropModeReplace,
ae4b759
+			 (guchar *) &pixmap_id, 1);
ae4b759
+	
ae4b759
+	XSetWindowBackgroundPixmap (display, RootWindow (display, screen_num),
ae4b759
+				    pixmap_id);
ae4b759
+	XClearWindow (display, RootWindow (display, screen_num));
ae4b759
+	
ae4b759
+	XUngrabServer (display);
ae4b759
+	
ae4b759
+	XFlush (display);
ae4b759
+}
ae4b759
+
ae4b759
+
ae4b759
+/* Implementation of the pixbuf cache */
ae4b759
+struct _SlideShow
ae4b759
+{
0c4bf27
+	double start_time;
ae4b759
+	double total_duration;
ae4b759
+
ae4b759
+	GQueue *slides;
ae4b759
+	
ae4b759
+	/* used during parsing */
0c4bf27
+	struct tm start_tm;
ae4b759
+	GQueue *stack;
ae4b759
+};
ae4b759
+
ae4b759
+static SlideShow *read_slideshow_file (const char *filename,
ae4b759
+				       GError     **err);
ae4b759
+static void       slideshow_free      (SlideShow  *show);
ae4b759
+
ae4b759
+static double
ae4b759
+now (void)
ae4b759
+{
ae4b759
+	GTimeVal tv;
ae4b759
+	time_t t;
ae4b759
+
ae4b759
+	g_get_current_time (&tv;;
ae4b759
+
ae4b759
+	time (&t);
ae4b759
+	
ae4b759
+	return (double)tv.tv_sec + (tv.tv_usec / 1000000.0);
ae4b759
+}
ae4b759
+
ae4b759
+static Slide *
ae4b759
+get_current_slide (SlideShow *show,
ae4b759
+		   double    *alpha)
ae4b759
+{
ae4b759
+	double delta = fmod (now() - show->start_time, show->total_duration);
ae4b759
+	GList *list;
ae4b759
+	double elapsed;
ae4b759
+	int i;
ae4b759
+
ae4b759
+	elapsed = 0;
ae4b759
+	i = 0;
ae4b759
+	for (list = show->slides->head; list != NULL; list = list->next) {
ae4b759
+		Slide *slide = list->data;
ae4b759
+
ae4b759
+		if (elapsed + slide->duration > delta) {
ae4b759
+			*alpha = (delta - elapsed) / (double)slide->duration;
ae4b759
+			return slide;
ae4b759
+		}
ae4b759
+
ae4b759
+		i++;
ae4b759
+		elapsed += slide->duration;
ae4b759
+	}
ae4b759
+
ae4b759
+	return NULL;
ae4b759
+}
ae4b759
+
ae4b759
+static GdkPixbuf *
ae4b759
+blend (GdkPixbuf *p1,
ae4b759
+       GdkPixbuf *p2,
ae4b759
+       double alpha)
ae4b759
+{
ae4b759
+	GdkPixbuf *result = gdk_pixbuf_copy (p1);
b979214
+	GdkPixbuf *tmp;
b979214
+
b979214
+	if (gdk_pixbuf_get_width (p2) != gdk_pixbuf_get_width (p1) ||
b979214
+            gdk_pixbuf_get_height (p2) != gdk_pixbuf_get_height (p1))
b979214
+          tmp = gdk_pixbuf_scale_simple (p2, 
b979214
+                                         gdk_pixbuf_get_width (p1),
b979214
+                                         gdk_pixbuf_get_height (p1),
b979214
+                                         GDK_INTERP_BILINEAR);
b979214
+        else
b979214
+          tmp = g_object_ref (p2);
b979214
+
b979214
+	pixbuf_blend (tmp, result, 0, 0, -1, -1, 0, 0, alpha);
b979214
+        
b979214
+        g_object_unref (tmp);	
ae4b759
+
ae4b759
+	return result;
ae4b759
+}
ae4b759
+
ae4b759
+typedef	enum {
ae4b759
+	PIXBUF,
ae4b759
+	SLIDESHOW,
ae4b759
+	THUMBNAIL
ae4b759
+} FileType;
ae4b759
+
ae4b759
+struct FileCacheEntry
ae4b759
+{
ae4b759
+	FileType type;
ae4b759
+	char *uri;
ae4b759
+	union {
ae4b759
+		GdkPixbuf *pixbuf;
ae4b759
+		SlideShow *slideshow;
ae4b759
+		GdkPixbuf *thumbnail;
ae4b759
+	} u;
ae4b759
+};
ae4b759
+
a599c27
+static void
a599c27
+file_cache_entry_delete (FileCacheEntry *ent)
a599c27
+{
a599c27
+	g_free (ent->uri);
a599c27
+	
a599c27
+	switch (ent->type) {
a599c27
+	case PIXBUF:
a599c27
+		g_object_unref (ent->u.pixbuf);
a599c27
+		break;
a599c27
+	case SLIDESHOW:
a599c27
+		slideshow_free (ent->u.slideshow);
a599c27
+		break;
a599c27
+	case THUMBNAIL:
a599c27
+		g_object_unref (ent->u.thumbnail);
a599c27
+		break;
a599c27
+	}
a599c27
+
a599c27
+	g_free (ent);
a599c27
+}
a599c27
+
ae4b759
+static const FileCacheEntry *
ae4b759
+file_cache_lookup (GnomeBG *bg, FileType type, const char *uri)
ae4b759
+{
ae4b759
+	GList *list;
ae4b759
+
ae4b759
+	for (list = bg->file_cache; list != NULL; list = list->next) {
ae4b759
+		FileCacheEntry *ent = list->data;
ae4b759
+
a599c27
+		if (ent && ent->type == type && strcmp (ent->uri, uri) == 0)
ae4b759
+			return ent;
ae4b759
+	}
ae4b759
+
ae4b759
+	return NULL;
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+file_cache_add_pixbuf (GnomeBG *bg,
ae4b759
+		       const char *uri,
ae4b759
+		       GdkPixbuf *pixbuf)
ae4b759
+{
a599c27
+	FileCacheEntry *ent;
a599c27
+
ae4b759
+	g_assert (!file_cache_lookup (bg, PIXBUF, uri));
a599c27
+	
a599c27
+	while (g_list_length (bg->file_cache) > CACHE_SIZE) {
a599c27
+		GList *last_link = g_list_last (bg->file_cache);
a599c27
+		FileCacheEntry *ent = last_link->data;
ae4b759
+
a599c27
+		file_cache_entry_delete (ent);
a599c27
+
a599c27
+		bg->file_cache = g_list_delete_link (bg->file_cache, last_link);
a599c27
+	}
a599c27
+
a599c27
+	ent = g_new0 (FileCacheEntry, 1);
a599c27
+	
ae4b759
+	ent->type = PIXBUF;
ae4b759
+	ent->uri = g_strdup (uri);
ae4b759
+	ent->u.pixbuf = pixbuf;
ae4b759
+
ae4b759
+	bg->file_cache = g_list_prepend (bg->file_cache, ent);
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+file_cache_add_thumbnail (GnomeBG *bg,
ae4b759
+			  const char *uri,
ae4b759
+			  GdkPixbuf *pixbuf)
ae4b759
+{
ae4b759
+	FileCacheEntry *ent = g_new0 (FileCacheEntry, 1);
ae4b759
+	
ae4b759
+	g_assert (!file_cache_lookup (bg, THUMBNAIL, uri));
ae4b759
+
ae4b759
+	ent->type = THUMBNAIL;
ae4b759
+	ent->uri = g_strdup (uri);
ae4b759
+	ent->u.thumbnail = pixbuf;
ae4b759
+
ae4b759
+	bg->file_cache = g_list_prepend (bg->file_cache, ent);
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+file_cache_add_slide_show (GnomeBG *bg,
ae4b759
+			   const char *uri,
ae4b759
+			   SlideShow *show)
ae4b759
+{
ae4b759
+	FileCacheEntry *ent = g_new0 (FileCacheEntry, 1);
ae4b759
+
ae4b759
+	g_assert (!file_cache_lookup (bg, SLIDESHOW, uri));
ae4b759
+
ae4b759
+	ent->type = SLIDESHOW;
ae4b759
+	ent->uri = g_strdup (uri);
ae4b759
+	ent->u.slideshow = show;
ae4b759
+
ae4b759
+	bg->file_cache = g_list_prepend (bg->file_cache, ent);
ae4b759
+}
ae4b759
+
ae4b759
+static GdkPixbuf *
ae4b759
+get_as_pixbuf (GnomeBG *bg, const char *uri)
ae4b759
+{
ae4b759
+	const FileCacheEntry *ent;
ae4b759
+	if ((ent = file_cache_lookup (bg, PIXBUF, uri))) {
ae4b759
+		return ent->u.pixbuf;
ae4b759
+	}
ae4b759
+	else {
ae4b759
+		GdkPixbuf *pixbuf = gnome_gdk_pixbuf_new_from_uri (uri);
ae4b759
+
ae4b759
+		if (pixbuf)
ae4b759
+			file_cache_add_pixbuf (bg, uri, pixbuf);
ae4b759
+
ae4b759
+		return pixbuf;
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+static SlideShow *
ae4b759
+get_as_slideshow (GnomeBG *bg, const char *uri)
ae4b759
+{
ae4b759
+	const FileCacheEntry *ent;
ae4b759
+	if ((ent = file_cache_lookup (bg, SLIDESHOW, uri))) {
ae4b759
+		return ent->u.slideshow;
ae4b759
+	}
ae4b759
+	else {
ae4b759
+		SlideShow *show = read_slideshow_file (uri, NULL);
ae4b759
+
ae4b759
+		if (show)
ae4b759
+			file_cache_add_slide_show (bg, uri, show);
ae4b759
+
ae4b759
+		return show;
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+static GdkPixbuf *
ae4b759
+get_as_thumbnail (GnomeBG *bg, GnomeThumbnailFactory *factory, const char *uri)
ae4b759
+{
ae4b759
+	const FileCacheEntry *ent;
ae4b759
+	if ((ent = file_cache_lookup (bg, THUMBNAIL, uri))) {
ae4b759
+		return ent->u.thumbnail;
ae4b759
+	}
ae4b759
+	else {
a599c27
+		GdkPixbuf *thumb = create_thumbnail_for_uri (factory, uri);
ae4b759
+
ae4b759
+		if (thumb)
ae4b759
+			file_cache_add_thumbnail (bg, uri, thumb);
ae4b759
+
ae4b759
+		return thumb;
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+static gboolean
ae4b759
+on_timeout (gpointer data)
ae4b759
+{
ae4b759
+	GnomeBG *bg = data;
ae4b759
+
ae4b759
+	if (bg->pixbuf_cache) {
ae4b759
+		g_object_unref (bg->pixbuf_cache);
ae4b759
+		bg->pixbuf_cache = NULL;
ae4b759
+	}
ae4b759
+
ae4b759
+	bg->timeout_id = 0;
ae4b759
+	
ae4b759
+	emit_changed (bg);
ae4b759
+
ae4b759
+	return FALSE;
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+ensure_timeout (GnomeBG *bg,
ae4b759
+		Slide   *slide)
ae4b759
+{
ae4b759
+	if (!bg->timeout_id) {
ae4b759
+		double timeout;
ae4b759
+		
ae4b759
+		if (slide->fixed) {
ae4b759
+			timeout = slide->duration;
ae4b759
+		}
ae4b759
+		else {
ae4b759
+			/* Maybe the number of steps should be configurable? */
ae4b759
+			timeout = slide->duration / 255.0;
ae4b759
+		}
ae4b759
+
ae4b759
+		bg->timeout_id = g_timeout_add_full (
ae4b759
+			G_PRIORITY_LOW,
ae4b759
+			timeout * 1000, on_timeout, bg, NULL);
ae4b759
+
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+static time_t
ae4b759
+get_mtime (const char *uri)
ae4b759
+{
ae4b759
+        time_t mtime;
ae4b759
+        GnomeVFSFileInfo *info;
ae4b759
+	
ae4b759
+	mtime = (time_t)-1;
ae4b759
+	
ae4b759
+	if (uri) {
ae4b759
+		GnomeVFSResult vfs_res;
ae4b759
+		
ae4b759
+		info = gnome_vfs_file_info_new ();
ae4b759
+		
ae4b759
+		vfs_res = gnome_vfs_get_file_info (
ae4b759
+			uri, info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
ae4b759
+		
ae4b759
+		if (vfs_res == GNOME_VFS_OK)
ae4b759
+			mtime = info->mtime;
ae4b759
+		
ae4b759
+		gnome_vfs_file_info_unref (info);
ae4b759
+	}
ae4b759
+	
ae4b759
+	return mtime;
ae4b759
+}
ae4b759
+
ae4b759
+static GdkPixbuf *
ae4b759
+scale_thumbnail (GnomeBGPlacement placement,
ae4b759
+		 const char *uri,
ae4b759
+		 GdkPixbuf *thumb,
ae4b759
+		 GdkScreen *screen,
ae4b759
+		 int	    dest_width,
ae4b759
+		 int	    dest_height)
ae4b759
+{
ae4b759
+	int o_width;
ae4b759
+	int o_height;
ae4b759
+	
ae4b759
+	if (placement != GNOME_BG_PLACEMENT_TILED &&
ae4b759
+	    placement != GNOME_BG_PLACEMENT_CENTERED) {
ae4b759
+		
ae4b759
+		/* In this case, the pixbuf will be scaled to fit the screen anyway,
ae4b759
+		 * so just return the pixbuf here
ae4b759
+		 */
ae4b759
+		return g_object_ref (thumb);
ae4b759
+	}
ae4b759
+	
ae4b759
+	/* FIXME: in case of tiled, we should probably scale the pixbuf to
ae4b759
+	 * be maybe dest_width/4 pixels tall. While strictly speaking incorrect,
ae4b759
+	 * it might give a better idea of what the background would actually look
ae4b759
+	 * like.
ae4b759
+	 */
ae4b759
+	if (get_thumb_annotations (thumb, &o_width, &o_height)		||
ae4b759
+	    (uri && get_original_size (uri, &o_width, &o_height))) {
ae4b759
+		
ae4b759
+		int scr_height = gdk_screen_get_height (screen);
ae4b759
+		int scr_width = gdk_screen_get_width (screen);
ae4b759
+		int thumb_width = gdk_pixbuf_get_width (thumb);
ae4b759
+		int thumb_height = gdk_pixbuf_get_height (thumb);
ae4b759
+		double screen_to_dest = fit_factor (scr_width, scr_height,
ae4b759
+						    dest_width, dest_height);
ae4b759
+		double thumb_to_orig  = fit_factor (thumb_width, thumb_height,
ae4b759
+						    o_width, o_height);
ae4b759
+		double f = thumb_to_orig * screen_to_dest;
ae4b759
+		int new_width, new_height;
ae4b759
+		
ae4b759
+		new_width = floor (thumb_width * f + 0.5);
ae4b759
+		new_height = floor (thumb_height * f + 0.5);
ae4b759
+		
ae4b759
+		thumb = gdk_pixbuf_scale_simple (thumb, new_width, new_height,
ae4b759
+						 GDK_INTERP_BILINEAR);
ae4b759
+	}
ae4b759
+	else
ae4b759
+		g_object_ref (thumb);
ae4b759
+	
ae4b759
+	return thumb;
ae4b759
+}
ae4b759
+
ae4b759
+static GdkPixbuf *
ae4b759
+create_img_thumbnail (GnomeBG               *bg,
ae4b759
+		      GnomeThumbnailFactory *factory,
ae4b759
+		      GdkScreen             *screen,
ae4b759
+		      int                    dest_width,
ae4b759
+		      int                    dest_height)
ae4b759
+{
ae4b759
+	if (bg->uri) {
ae4b759
+		GdkPixbuf *thumb = get_as_thumbnail (bg, factory, bg->uri);
ae4b759
+
ae4b759
+		if (thumb) {
ae4b759
+			return scale_thumbnail (
ae4b759
+				bg->placement, bg->uri,
ae4b759
+				thumb, screen, dest_width, dest_height);
ae4b759
+		}
ae4b759
+		else {
ae4b759
+			SlideShow *show = get_as_slideshow (bg, bg->uri);
a599c27
+			
ae4b759
+			if (show) {
ae4b759
+				double alpha;
ae4b759
+				Slide *slide = get_current_slide (show, &alpha);
ae4b759
+
ae4b759
+				if (slide->fixed) {
ae4b759
+					GdkPixbuf *tmp;
ae4b759
+					
ae4b759
+					tmp = get_as_thumbnail (bg, factory, slide->file1);
ae4b759
+
ae4b759
+					thumb = scale_thumbnail (
ae4b759
+						bg->placement, bg->uri,
ae4b759
+						tmp, screen, dest_width, dest_height);
ae4b759
+				}
ae4b759
+				else {
ae4b759
+					GdkPixbuf *p1 = get_as_thumbnail (bg, factory, slide->file1);
ae4b759
+					GdkPixbuf *p2 = get_as_thumbnail (bg, factory, slide->file2);
ae4b759
+
ae4b759
+					if (p1 && p2) {
ae4b759
+						GdkPixbuf *thumb1, *thumb2;
ae4b759
+
ae4b759
+						thumb1 = scale_thumbnail (
ae4b759
+							bg->placement, bg->uri,
ae4b759
+							p1, screen, dest_width, dest_height);
ae4b759
+						thumb2 = scale_thumbnail (
ae4b759
+							bg->placement, bg->uri,
ae4b759
+							p2, screen, dest_width, dest_height);
ae4b759
+
ae4b759
+						thumb = blend (thumb1, thumb2, alpha);
ae4b759
+
ae4b759
+						g_object_unref (thumb1);
ae4b759
+						g_object_unref (thumb2);
ae4b759
+					}
ae4b759
+				}
ae4b759
+
ae4b759
+				ensure_timeout (bg, slide);
ae4b759
+			}
ae4b759
+		}
ae4b759
+
ae4b759
+		return thumb;
ae4b759
+	}
ae4b759
+
ae4b759
+	return NULL;
ae4b759
+}
ae4b759
+
ae4b759
+static GdkPixbuf *
ae4b759
+get_pixbuf (GnomeBG *bg)
ae4b759
+{
ae4b759
+	/* FIXME: this ref=TRUE/FALSE stuff is crazy */
ae4b759
+	
ae4b759
+	gboolean ref = FALSE;
ae4b759
+	
ae4b759
+	if (!bg->pixbuf_cache && bg->uri) {
ae4b759
+		ref = TRUE;
ae4b759
+		bg->uri_mtime = get_mtime (bg->uri);
ae4b759
+		
ae4b759
+		bg->pixbuf_cache = get_as_pixbuf (bg, bg->uri);
ae4b759
+		if (!bg->pixbuf_cache) {
ae4b759
+			SlideShow *show = get_as_slideshow (bg, bg->uri);
ae4b759
+
ae4b759
+			if (show) {
ae4b759
+				double alpha;
ae4b759
+				Slide *slide = get_current_slide (show, &alpha);
ae4b759
+
ae4b759
+				if (slide->fixed) {
ae4b759
+					bg->pixbuf_cache = get_as_pixbuf (bg, slide->file1);
ae4b759
+				}
ae4b759
+				else {
ae4b759
+					GdkPixbuf *p1 = get_as_pixbuf (bg, slide->file1);
ae4b759
+					GdkPixbuf *p2 = get_as_pixbuf (bg, slide->file2);
ae4b759
+
ae4b759
+					if (p1 && p2) {
ae4b759
+						bg->pixbuf_cache = blend (p1, p2, alpha);
ae4b759
+						ref = FALSE;
ae4b759
+					}
ae4b759
+				}
ae4b759
+
ae4b759
+				ensure_timeout (bg, slide);
ae4b759
+			}
ae4b759
+		}
ae4b759
+	}
ae4b759
+
ae4b759
+	if (bg->pixbuf_cache && ref)
ae4b759
+		g_object_ref (bg->pixbuf_cache);
ae4b759
+	
ae4b759
+	return bg->pixbuf_cache;
ae4b759
+}
ae4b759
+
ae4b759
+static gboolean
ae4b759
+is_different (GnomeBG    *bg,
ae4b759
+	      const char *uri)
ae4b759
+{
ae4b759
+	if (!uri && bg->uri) {
ae4b759
+		return TRUE;
ae4b759
+	}
ae4b759
+	else if (uri && !bg->uri) {
ae4b759
+		return TRUE;
ae4b759
+	}
ae4b759
+	else if (!uri && !bg->uri) {
ae4b759
+		return FALSE;
ae4b759
+	}
ae4b759
+	else {
ae4b759
+		time_t mtime = get_mtime (uri);
ae4b759
+		
ae4b759
+		if (mtime != bg->uri_mtime)
ae4b759
+			return TRUE;
ae4b759
+		
ae4b759
+		if (strcmp (uri, bg->uri) != 0)
ae4b759
+			return TRUE;
ae4b759
+		
ae4b759
+		return FALSE;
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+clear_cache (GnomeBG *bg)
ae4b759
+{
ae4b759
+	if (bg->pixbuf_cache) {
ae4b759
+		g_object_unref (bg->pixbuf_cache);
ae4b759
+		
ae4b759
+		bg->pixbuf_cache = NULL;
ae4b759
+	}
ae4b759
+
ae4b759
+	if (bg->timeout_id) {
ae4b759
+		g_source_remove (bg->timeout_id);
ae4b759
+
ae4b759
+		bg->timeout_id = 0;
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+/* Pixbuf utilities */
ae4b759
+static guint32
ae4b759
+pixbuf_average_value (GdkPixbuf *pixbuf)
ae4b759
+{
ae4b759
+	guint64 a_total, r_total, g_total, b_total;
ae4b759
+	guint row, column;
ae4b759
+	int row_stride;
ae4b759
+	const guchar *pixels, *p;
ae4b759
+	int r, g, b, a;
ae4b759
+	guint64 dividend;
ae4b759
+	guint width, height;
ae4b759
+	
ae4b759
+	width = gdk_pixbuf_get_width (pixbuf);
ae4b759
+	height = gdk_pixbuf_get_height (pixbuf);
ae4b759
+	row_stride = gdk_pixbuf_get_rowstride (pixbuf);
ae4b759
+	pixels = gdk_pixbuf_get_pixels (pixbuf);
ae4b759
+	
ae4b759
+	/* iterate through the pixbuf, counting up each component */
ae4b759
+	a_total = 0;
ae4b759
+	r_total = 0;
ae4b759
+	g_total = 0;
ae4b759
+	b_total = 0;
ae4b759
+	
ae4b759
+	if (gdk_pixbuf_get_has_alpha (pixbuf)) {
ae4b759
+		for (row = 0; row < height; row++) {
ae4b759
+			p = pixels + (row * row_stride);
ae4b759
+			for (column = 0; column < width; column++) {
ae4b759
+				r = *p++;
ae4b759
+				g = *p++;
ae4b759
+				b = *p++;
ae4b759
+				a = *p++;
ae4b759
+				
ae4b759
+				a_total += a;
ae4b759
+				r_total += r * a;
ae4b759
+				g_total += g * a;
ae4b759
+				b_total += b * a;
ae4b759
+			}
ae4b759
+		}
ae4b759
+		dividend = height * width * 0xFF;
ae4b759
+		a_total *= 0xFF;
ae4b759
+	} else {
ae4b759
+		for (row = 0; row < height; row++) {
ae4b759
+			p = pixels + (row * row_stride);
ae4b759
+			for (column = 0; column < width; column++) {
ae4b759
+				r = *p++;
ae4b759
+				g = *p++;
ae4b759
+				b = *p++;
ae4b759
+				
ae4b759
+				r_total += r;
ae4b759
+				g_total += g;
ae4b759
+				b_total += b;
ae4b759
+			}
ae4b759
+		}
ae4b759
+		dividend = height * width;
ae4b759
+		a_total = dividend * 0xFF;
ae4b759
+	}
ae4b759
+	
ae4b759
+	return ((a_total + dividend / 2) / dividend) << 24
ae4b759
+		| ((r_total + dividend / 2) / dividend) << 16
ae4b759
+		| ((g_total + dividend / 2) / dividend) << 8
ae4b759
+		| ((b_total + dividend / 2) / dividend);
ae4b759
+}
ae4b759
+
ae4b759
+static GdkPixbuf *
ae4b759
+pixbuf_scale_to_fit (GdkPixbuf *src, int max_width, int max_height)
ae4b759
+{
ae4b759
+	double factor;
ae4b759
+	int src_width, src_height;
ae4b759
+	int new_width, new_height;
ae4b759
+	
ae4b759
+	src_width = gdk_pixbuf_get_width (src);
ae4b759
+	src_height = gdk_pixbuf_get_height (src);
ae4b759
+	
ae4b759
+	factor = MIN (max_width  / (double) src_width, max_height / (double) src_height);
ae4b759
+	
ae4b759
+	new_width  = floor (src_width * factor + 0.5);
ae4b759
+	new_height = floor (src_height * factor + 0.5);
ae4b759
+	
ae4b759
+	return gdk_pixbuf_scale_simple (src, new_width, new_height, GDK_INTERP_BILINEAR);	
ae4b759
+}
ae4b759
+
ae4b759
+static GdkPixbuf *
ae4b759
+pixbuf_scale_to_min (GdkPixbuf *src, int min_width, int min_height)
ae4b759
+{
ae4b759
+	double factor;
ae4b759
+	int src_width, src_height;
ae4b759
+	int new_width, new_height;
ae4b759
+	
ae4b759
+	src_width = gdk_pixbuf_get_width (src);
ae4b759
+	src_height = gdk_pixbuf_get_height (src);
ae4b759
+	
ae4b759
+	factor = MAX (min_width / (double) src_width, min_height / (double) src_height);
ae4b759
+	
ae4b759
+	new_width = floor (src_width * factor + 0.5);
ae4b759
+	new_height = floor (src_height * factor + 0.5);
ae4b759
+	
ae4b759
+	return gdk_pixbuf_scale_simple (src, new_width, new_height, GDK_INTERP_BILINEAR);
ae4b759
+}
ae4b759
+
ae4b759
+static guchar *
ae4b759
+create_gradient (const GdkColor *c1,
ae4b759
+		 const GdkColor *c2,
ae4b759
+		 int	          n_pixels)
ae4b759
+{
ae4b759
+	guchar *result = g_malloc (n_pixels * 3);
ae4b759
+	int i;
ae4b759
+	
ae4b759
+	for (i = 0; i < n_pixels; ++i) {
ae4b759
+		double ratio = (i + 0.5) / n_pixels;
ae4b759
+		
ae4b759
+		result[3 * i + 0] = ((guint16) (c1->red * (1 - ratio) + c2->red * ratio)) >> 8;
ae4b759
+		result[3 * i + 1] = ((guint16) (c1->green * (1 - ratio) + c2->green * ratio)) >> 8;
ae4b759
+		result[3 * i + 2] = ((guint16) (c1->blue * (1 - ratio) + c2->blue * ratio)) >> 8;
ae4b759
+	}
ae4b759
+	
ae4b759
+	return result;
ae4b759
+}	
ae4b759
+
ae4b759
+static void
ae4b759
+pixbuf_draw_gradient (GdkPixbuf *pixbuf,
ae4b759
+		      gboolean   horizontal,
ae4b759
+		      GdkColor  *c1,
ae4b759
+		      GdkColor  *c2)
ae4b759
+{
ae4b759
+	int width  = gdk_pixbuf_get_width (pixbuf);
ae4b759
+	int height = gdk_pixbuf_get_height (pixbuf);
ae4b759
+	int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
ae4b759
+	guchar *dst = gdk_pixbuf_get_pixels (pixbuf);
ae4b759
+	guchar *dst_limit = dst + height * rowstride;
ae4b759
+	
ae4b759
+	if (horizontal) {
ae4b759
+		guchar *gradient = create_gradient (c1, c2, width);
ae4b759
+		int copy_bytes_per_row = width * 3;
ae4b759
+		
ae4b759
+		while (dst < dst_limit) {
ae4b759
+			memcpy (dst, gradient, copy_bytes_per_row);
ae4b759
+			dst += rowstride;
ae4b759
+		}
ae4b759
+		g_free (gradient);
ae4b759
+	} else {
ae4b759
+		guchar *gb, *gradient;
ae4b759
+		
ae4b759
+		gb = gradient = create_gradient (c1, c2, height);
ae4b759
+		while (dst < dst_limit) {
ae4b759
+			int i;
ae4b759
+			guchar *d = dst;
ae4b759
+			guchar r = *gb++;
ae4b759
+			guchar g = *gb++;
ae4b759
+			guchar b = *gb++;
ae4b759
+			for (i = 0; i < width; i++) {
ae4b759
+				*d++ = r;
ae4b759
+				*d++ = g;
ae4b759
+				*d++ = b;
ae4b759
+			}
ae4b759
+			dst += rowstride;
ae4b759
+		}
ae4b759
+		g_free (gradient);
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+pixbuf_blend (GdkPixbuf *src,
ae4b759
+	      GdkPixbuf *dest,
ae4b759
+	      int	 src_x,
ae4b759
+	      int	 src_y,
ae4b759
+	      int	 width,
ae4b759
+	      int        height,
ae4b759
+	      int	 dest_x,
ae4b759
+	      int	 dest_y,
ae4b759
+	      double	 alpha)
ae4b759
+{
ae4b759
+	int dest_width = gdk_pixbuf_get_width (dest);
ae4b759
+	int dest_height = gdk_pixbuf_get_height (dest);
ae4b759
+	int offset_x = dest_x - src_x;
ae4b759
+	int offset_y = dest_y - src_y;
ae4b759
+
ae4b759
+	if (width < 0)
ae4b759
+		width = gdk_pixbuf_get_width (src);
ae4b759
+
ae4b759
+	if (height < 0)
ae4b759
+		height = gdk_pixbuf_get_height (src);
ae4b759
+	
ae4b759
+	if (dest_x < 0) {
ae4b759
+		offset_x -= dest_x;
ae4b759
+		dest_x = 0;
ae4b759
+	}
ae4b759
+	
ae4b759
+	if (dest_y < 0) {
ae4b759
+		offset_y -= dest_y;
ae4b759
+		dest_y = 0;
ae4b759
+	}
ae4b759
+	
ae4b759
+	if (dest_x + width > dest_width) {
ae4b759
+		width = dest_width - dest_x;
ae4b759
+	}
ae4b759
+	
ae4b759
+	if (dest_y + height > dest_height) {
ae4b759
+		height = dest_height - dest_y;
ae4b759
+	}
ae4b759
+
ae4b759
+	gdk_pixbuf_composite (src, dest,
ae4b759
+			      dest_x, dest_y,
ae4b759
+			      width, height,
ae4b759
+			      offset_x, offset_y,
ae4b759
+			      1, 1, GDK_INTERP_NEAREST,
ae4b759
+			      alpha * 0xFF + 0.5);
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+pixbuf_tile (GdkPixbuf *src, GdkPixbuf *dest)
ae4b759
+{
ae4b759
+	int x, y;
ae4b759
+	int tile_width, tile_height;
ae4b759
+	int dest_width = gdk_pixbuf_get_width (dest);
ae4b759
+	int dest_height = gdk_pixbuf_get_height (dest);
ae4b759
+	
ae4b759
+	tile_width = gdk_pixbuf_get_width (src);
ae4b759
+	tile_height = gdk_pixbuf_get_height (src);
ae4b759
+	
ae4b759
+	for (y = 0; y < dest_height; y += tile_height) {
ae4b759
+		for (x = 0; x < dest_width; x += tile_width) {
ae4b759
+			pixbuf_blend (src, dest, 0, 0,
ae4b759
+				      tile_width, tile_height, x, y, 1.0);
ae4b759
+		}
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+
ae4b759
+/* Parser for fading background */
ae4b759
+static void
ae4b759
+handle_start_element (GMarkupParseContext *context,
ae4b759
+		      const gchar         *name,
ae4b759
+		      const gchar        **attr_names,
ae4b759
+		      const gchar        **attr_values,
ae4b759
+		      gpointer             user_data,
ae4b759
+		      GError             **err)
ae4b759
+{
ae4b759
+	SlideShow *parser = user_data;
ae4b759
+	
ae4b759
+	if (strcmp (name, "static") == 0 || strcmp (name, "transition") == 0) {
ae4b759
+		Slide *slide = g_new0 (Slide, 1);
ae4b759
+		
ae4b759
+		if (strcmp (name, "static") == 0)
ae4b759
+			slide->fixed = TRUE;
ae4b759
+		
ae4b759
+		g_queue_push_tail (parser->slides, slide);
ae4b759
+	}
ae4b759
+	
ae4b759
+	g_queue_push_tail (parser->stack, g_strdup (name));
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+handle_end_element (GMarkupParseContext *context,
ae4b759
+		    const gchar         *name,
ae4b759
+		    gpointer             user_data,
ae4b759
+		    GError             **err)
ae4b759
+{
ae4b759
+	SlideShow *parser = user_data;
ae4b759
+	
ae4b759
+	g_free (g_queue_pop_tail (parser->stack));
ae4b759
+}
ae4b759
+
ae4b759
+static gboolean
ae4b759
+stack_is (SlideShow *parser,
ae4b759
+	  const char *s1,
ae4b759
+	  ...)
ae4b759
+{
ae4b759
+	GList *stack = NULL;
ae4b759
+	const char *s;
ae4b759
+	GList *l1, *l2;
ae4b759
+	va_list args;
ae4b759
+	
ae4b759
+	stack = g_list_prepend (stack, (gpointer)s1);
ae4b759
+	
ae4b759
+	va_start (args, s1);
ae4b759
+	
ae4b759
+	s = va_arg (args, const char *);
ae4b759
+	while (s) {
ae4b759
+		stack = g_list_prepend (stack, (gpointer)s);
ae4b759
+		s = va_arg (args, const char *);
ae4b759
+	}
ae4b759
+	
ae4b759
+	l1 = stack;
ae4b759
+	l2 = parser->stack->head;
ae4b759
+	
ae4b759
+	while (l1 && l2) {
ae4b759
+		if (strcmp (l1->data, l2->data) != 0)
ae4b759
+			return FALSE;
ae4b759
+		
ae4b759
+		l1 = l1->next;
ae4b759
+		l2 = l2->next;
ae4b759
+	}
ae4b759
+
ae4b759
+	g_list_free (stack);
ae4b759
+
ae4b759
+	return (!l1 && !l2);
ae4b759
+}
ae4b759
+
0c4bf27
+static int
0c4bf27
+parse_int (const char *text)
0c4bf27
+{
0c4bf27
+	return strtol (text, NULL, 0);
0c4bf27
+}
0c4bf27
+
ae4b759
+static void
ae4b759
+handle_text (GMarkupParseContext *context,
ae4b759
+	     const gchar         *text,
ae4b759
+	     gsize                text_len,
ae4b759
+	     gpointer             user_data,
ae4b759
+	     GError             **err)
ae4b759
+{
ae4b759
+	SlideShow *parser = user_data;
ae4b759
+	Slide *slide = parser->slides->tail? parser->slides->tail->data : NULL;
0c4bf27
+
0c4bf27
+	if (stack_is (parser, "year", "starttime", "background", NULL)) {
0c4bf27
+		parser->start_tm.tm_year = parse_int (text) - 1900;
0c4bf27
+	}
0c4bf27
+	else if (stack_is (parser, "month", "starttime", "background", NULL)) {
0c4bf27
+		parser->start_tm.tm_mon = parse_int (text) - 1;
0c4bf27
+	}
0c4bf27
+	else if (stack_is (parser, "day", "starttime", "background", NULL)) {
0c4bf27
+		parser->start_tm.tm_mday = parse_int (text);
0c4bf27
+	}
0c4bf27
+	else if (stack_is (parser, "hour", "starttime", "background", NULL)) {
0c4bf27
+		parser->start_tm.tm_hour = parse_int (text) - 1;
0c4bf27
+	}
0c4bf27
+	else if (stack_is (parser, "minute", "starttime", "background", NULL)) {
0c4bf27
+		parser->start_tm.tm_min = parse_int (text);
0c4bf27
+	}
0c4bf27
+	else if (stack_is (parser, "second", "starttime", "background", NULL)) {
0c4bf27
+		parser->start_tm.tm_sec = parse_int (text);
ae4b759
+	}
ae4b759
+	else if (stack_is (parser, "duration", "static", "background", NULL) ||
ae4b759
+		 stack_is (parser, "duration", "transition", "background", NULL)) {
ae4b759
+		slide->duration = g_strtod (text, NULL);
ae4b759
+		parser->total_duration += slide->duration;
ae4b759
+	}
ae4b759
+	else if (stack_is (parser, "file", "static", "background", NULL) ||
ae4b759
+		 stack_is (parser, "from", "transition", "background", NULL)) {
ae4b759
+		slide->file1 = g_strdup (text);
ae4b759
+	}
ae4b759
+	else if (stack_is (parser, "to", "transition", "background", NULL)) {
ae4b759
+		slide->file2 = g_strdup (text);
ae4b759
+	}
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+slideshow_free (SlideShow *show)
ae4b759
+{
ae4b759
+	GList *list;
ae4b759
+	
ae4b759
+	for (list = show->slides->head; list != NULL; list = list->next) {
ae4b759
+		Slide *slide = list->data;
ae4b759
+		
ae4b759
+		g_free (slide->file1);
ae4b759
+		g_free (slide->file2);
ae4b759
+		g_free (slide);
ae4b759
+	}
ae4b759
+	
ae4b759
+	g_queue_free (show->slides);
ae4b759
+	
ae4b759
+	for (list = show->stack->head; list != NULL; list = list->next) {
ae4b759
+		gchar *str = list->data;
ae4b759
+		
ae4b759
+		g_free (str);
ae4b759
+	}
ae4b759
+	
ae4b759
+	g_queue_free (show->stack);
ae4b759
+	
ae4b759
+	g_free (show);
ae4b759
+}
ae4b759
+
ae4b759
+static void
ae4b759
+dump_bg (SlideShow *show)
ae4b759
+{
ae4b759
+#if 0
ae4b759
+	GList *list;
ae4b759
+	
ae4b759
+	for (list = show->slides->head; list != NULL; list = list->next)
ae4b759
+	{
ae4b759
+		Slide *slide = list->data;
ae4b759
+		
ae4b759
+		g_print ("\nSlide: %s\n", slide->fixed? "fixed" : "transition");
ae4b759
+		g_print ("duration: %d\n", slide->duration);
ae4b759
+		g_print ("file1: %p %s\n", slide, slide->file1);
ae4b759
+		g_print ("file2: %s\n", slide->file2);
ae4b759
+	}
ae4b759
+#endif
ae4b759
+}
ae4b759
+
0c4bf27
+static void
0c4bf27
+threadsafe_localtime (time_t time, struct tm *tm)
0c4bf27
+{
0c4bf27
+	struct tm *res;
0c4bf27
+	
0c4bf27
+	G_LOCK_DEFINE_STATIC (localtime_mutex);
0c4bf27
+
0c4bf27
+	G_LOCK (localtime_mutex);
0c4bf27
+
0c4bf27
+	res = localtime (&time);
0c4bf27
+	if (tm) {
0c4bf27
+		*tm = *res;
0c4bf27
+	}
0c4bf27
+	
0c4bf27
+	G_UNLOCK (localtime_mutex);
0c4bf27
+}
0c4bf27
+
ae4b759
+static SlideShow *
ae4b759
+read_slideshow_file (const char *uri,
ae4b759
+		     GError     **err)
ae4b759
+{
ae4b759
+	GMarkupParser parser = {
ae4b759
+		handle_start_element,
ae4b759
+		handle_end_element,
ae4b759
+		handle_text,
ae4b759
+		NULL, /* passthrough */
ae4b759
+		NULL, /* error */
ae4b759
+	};
ae4b759
+	
ae4b759
+	char *contents = NULL;
ae4b759
+	gssize len;
ae4b759
+	SlideShow *show = NULL;
ae4b759
+	GMarkupParseContext *context = NULL;
0c4bf27
+	time_t t;
ae4b759
+
ae4b759
+	if (!uri || (gnome_vfs_read_entire_file (uri, &len, &contents) != GNOME_VFS_OK))
ae4b759
+		return NULL;
ae4b759
+	
ae4b759
+	show = g_new0 (SlideShow, 1);
0c4bf27
+	threadsafe_localtime ((time_t)0, &show->start_tm);
ae4b759
+	show->stack = g_queue_new ();
ae4b759
+	show->slides = g_queue_new ();
ae4b759
+	
ae4b759
+	context = g_markup_parse_context_new (&parser, 0, show, NULL);
ae4b759
+	
ae4b759
+	if (!g_markup_parse_context_parse (context, contents, len, err)) {
ae4b759
+		slideshow_free (show);
ae4b759
+		show = NULL;
ae4b759
+	}
ae4b759
+	
ae4b759
+	if (!g_markup_parse_context_end_parse (context, err)) {
ae4b759
+		slideshow_free (show);
ae4b759
+		show = NULL;
ae4b759
+	}
ae4b759
+	
ae4b759
+	g_markup_parse_context_free (context);
0c4bf27
+
0c4bf27
+	t = mktime (&show->start_tm);
0c4bf27
+
0c4bf27
+	g_print ("start time: %s\n", asctime (&show->start_tm));
ae4b759
+	
0c4bf27
+	show->start_time = (double)t;
0c4bf27
+		
ae4b759
+	dump_bg (show);
ae4b759
+
ae4b759
+	g_free (contents);
ae4b759
+	
ae4b759
+	return show;
ae4b759
+}
ae4b759
+
ae4b759
+/* Thumbnail utilities */
ae4b759
+static GdkPixbuf *
a599c27
+create_thumbnail_for_uri (GnomeThumbnailFactory *factory,
a599c27
+			  const char            *uri)
ae4b759
+{
ae4b759
+	char *filename;
ae4b759
+	time_t mtime;
ae4b759
+	GdkPixbuf *pixbuf, *orig;
ae4b759
+	
ae4b759
+	mtime = get_mtime (uri);
ae4b759
+	
ae4b759
+	if (mtime == (time_t)-1)
ae4b759
+		return NULL;
ae4b759
+	
ae4b759
+	filename = gnome_thumbnail_factory_lookup (factory, uri, mtime);
ae4b759
+	
ae4b759
+	if (filename) {
ae4b759
+		pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
ae4b759
+		
ae4b759
+		return pixbuf;
ae4b759
+	}
ae4b759
+	
ae4b759
+	orig = gnome_gdk_pixbuf_new_from_uri (uri);
ae4b759
+	if (orig) {
ae4b759
+		int orig_width = gdk_pixbuf_get_width (orig);
ae4b759
+		int orig_height = gdk_pixbuf_get_height (orig);
ae4b759
+		
ae4b759
+		pixbuf = pixbuf_scale_to_fit (orig, 128, 128);
ae4b759
+		
ae4b759
+		g_object_set_data_full (G_OBJECT (pixbuf), "gnome-thumbnail-height",
ae4b759
+					g_strdup_printf ("%d", orig_height), g_free);
ae4b759
+		g_object_set_data_full (G_OBJECT (pixbuf), "gnome-thumbnail-width",
ae4b759
+					g_strdup_printf ("%d", orig_width), g_free);
ae4b759
+		
ae4b759
+		g_object_unref (orig);
ae4b759
+		
ae4b759
+		gnome_thumbnail_factory_save_thumbnail (factory, pixbuf, uri, mtime);
ae4b759
+		
ae4b759
+		return pixbuf;
ae4b759
+	}
ae4b759
+	
ae4b759
+	gnome_thumbnail_factory_create_failed_thumbnail (factory, uri, mtime);
ae4b759
+	return NULL;
ae4b759
+}
ae4b759
+
ae4b759
+static gboolean
ae4b759
+get_thumb_annotations (GdkPixbuf *thumb,
ae4b759
+		       int	 *orig_width,
ae4b759
+		       int	 *orig_height)
ae4b759
+{
ae4b759
+	char *end;
ae4b759
+	const char *wstr, *hstr;
ae4b759
+	
ae4b759
+	wstr = gdk_pixbuf_get_option (thumb, "tEXt::Thumb::Image::Width");
ae4b759
+	hstr = gdk_pixbuf_get_option (thumb, "tEXt::Thumb::Image::Height");
ae4b759
+	
ae4b759
+	if (hstr && wstr) {
ae4b759
+		*orig_width = strtol (wstr, &end, 10);
ae4b759
+		if (*end != 0)
ae4b759
+			return FALSE;
ae4b759
+		
ae4b759
+		*orig_height = strtol (hstr, &end, 10);
ae4b759
+		if (*end != 0)
ae4b759
+			return FALSE;
ae4b759
+		
ae4b759
+		return TRUE;
ae4b759
+	}
ae4b759
+	
ae4b759
+	return FALSE;
ae4b759
+}
ae4b759
--- gnome-desktop-2.19.90/libgnome-desktop/Makefile.am.gnome-bg	2007-08-13 20:04:00.000000000 -0400
a599c27
+++ gnome-desktop-2.19.90/libgnome-desktop/Makefile.am	2007-08-28 14:01:15.000000000 -0400
0c4bf27
@@ -18,7 +18,8 @@
ae4b759
 libgnome_desktop_2_la_SOURCES = \
ae4b759
 	gnome-desktop-item.c	\
ae4b759
 	gnome-ditem-edit.c	\
ae4b759
-	gnome-hint.c
ae4b759
+	gnome-hint.c		\
ae4b759
+	gnome-bg.c
ae4b759
 
ae4b759
 libgnome_desktop_2_la_LIBADD = \
ae4b759
 	$(GNOME_DESKTOP_LIBS)
0c4bf27
--- gnome-desktop-2.19.90/libgnome-desktop/libgnomeui/Makefile.am.gnome-bg	2007-08-13 20:04:00.000000000 -0400
a599c27
+++ gnome-desktop-2.19.90/libgnome-desktop/libgnomeui/Makefile.am	2007-08-28 14:01:15.000000000 -0400
0c4bf27
@@ -1,4 +1,5 @@
0c4bf27
 libgnomeui_desktopdir = $(includedir)/gnome-desktop-2.0/libgnomeui
0c4bf27
 libgnomeui_desktop_HEADERS = \
0c4bf27
 	gnome-ditem-edit.h \
0c4bf27
-	gnome-hint.h
0c4bf27
+	gnome-hint.h       \
0c4bf27
+	gnome-bg.h
0c4bf27
--- /dev/null	2007-08-15 18:04:26.337218222 -0400
a599c27
+++ gnome-desktop-2.19.90/libgnome-desktop/libgnomeui/gnome-bg.h	2007-08-28 14:01:15.000000000 -0400
0c4bf27
@@ -0,0 +1,99 @@
0c4bf27
+/* gnome-bg.h - 
0c4bf27
+
0c4bf27
+   Copyright 2007, Red Hat, Inc.
0c4bf27
+
0c4bf27
+   This file is part of the Gnome Library.
0c4bf27
+
0c4bf27
+   The Gnome Library is free software; you can redistribute it and/or
0c4bf27
+   modify it under the terms of the GNU Library General Public License as
0c4bf27
+   published by the Free Software Foundation; either version 2 of the
0c4bf27
+   License, or (at your option) any later version.
0c4bf27
+   
0c4bf27
+   The Gnome Library is distributed in the hope that it will be useful,
0c4bf27
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
0c4bf27
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0c4bf27
+   Library General Public License for more details.
0c4bf27
+   
0c4bf27
+   You should have received a copy of the GNU Library General Public
0c4bf27
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
0c4bf27
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
0c4bf27
+   Boston, MA 02111-1307, USA.
0c4bf27
+
0c4bf27
+   Author: Soren Sandmann <sandmann@redhat.com>
0c4bf27
+*/
0c4bf27
+
0c4bf27
+#ifndef __GNOME_BG_H__
0c4bf27
+#define __GNOME_BG_H__
0c4bf27
+
0c4bf27
+#ifdef __cplusplus
0c4bf27
+extern "C" {
0c4bf27
+#endif
0c4bf27
+
0c4bf27
+#include <libgnomeui/libgnomeui.h>
0c4bf27
+#include <gdk/gdk.h>
0c4bf27
+
0c4bf27
+#define GNOME_TYPE_BG            (gnome_bg_get_type ())
0c4bf27
+#define GNOME_BG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_BG, GnomeBG))
0c4bf27
+#define GNOME_BG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GNOME_TYPE_BG, GnomeBGClass))
0c4bf27
+#define GNOME_IS_BG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_BG))
0c4bf27
+#define GNOME_IS_BG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GNOME_TYPE_BG))
0c4bf27
+#define GNOME_BG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GNOME_TYPE_BG, GnomeBGClass))
0c4bf27
+
0c4bf27
+typedef struct _GnomeBG GnomeBG;
0c4bf27
+typedef struct _GnomeBGClass GnomeBGClass;
0c4bf27
+
0c4bf27
+typedef enum {
0c4bf27
+	GNOME_BG_COLOR_SOLID,
0c4bf27
+	GNOME_BG_COLOR_H_GRADIENT,
0c4bf27
+	GNOME_BG_COLOR_V_GRADIENT
0c4bf27
+} GnomeBGColorType;
0c4bf27
+
0c4bf27
+typedef enum {
0c4bf27
+	GNOME_BG_PLACEMENT_TILED,
0c4bf27
+	GNOME_BG_PLACEMENT_ZOOMED,
0c4bf27
+	GNOME_BG_PLACEMENT_CENTERED,
0c4bf27
+	GNOME_BG_PLACEMENT_SCALED,
0c4bf27
+	GNOME_BG_PLACEMENT_FILL_SCREEN
0c4bf27
+} GnomeBGPlacement;
0c4bf27
+
0c4bf27
+GType      gnome_bg_get_type           (void);
0c4bf27
+GnomeBG *  gnome_bg_new                (void);
0c4bf27
+void       gnome_bg_free               (GnomeBG               *img);
0c4bf27
+void       gnome_bg_set_placement      (GnomeBG               *img,
0c4bf27
+					GnomeBGPlacement       placement);
0c4bf27
+void       gnome_bg_set_color          (GnomeBG               *img,
0c4bf27
+					GnomeBGColorType       type,
0c4bf27
+					GdkColor              *c1,
0c4bf27
+					GdkColor              *c2);
0c4bf27
+void       gnome_bg_set_uri            (GnomeBG               *img,
0c4bf27
+					const char            *uri);
0c4bf27
+void       gnome_bg_draw               (GnomeBG               *img,
0c4bf27
+					GdkPixbuf             *dest);
0c4bf27
+GdkPixmap *gnome_bg_create_pixmap      (GnomeBG               *img,
0c4bf27
+					GdkWindow             *window,
0c4bf27
+					int                    width,
0c4bf27
+					int                    height,
0c4bf27
+					gboolean               root);
0c4bf27
+gboolean   gnome_bg_get_image_size     (GnomeBG	               *bg,
0c4bf27
+					GnomeThumbnailFactory *factory,
0c4bf27
+					int		       *width,
0c4bf27
+					int		       *height);
0c4bf27
+GdkPixbuf *gnome_bg_create_thumbnail   (GnomeBG               *bg,
0c4bf27
+					GnomeThumbnailFactory *factory,
0c4bf27
+					GdkScreen             *screen,
0c4bf27
+					int		       dest_width,
0c4bf27
+					int		       dest_height);
0c4bf27
+gboolean   gnome_bg_is_dark            (GnomeBG               *img);
0c4bf27
+gboolean   gnome_bg_changes_with_size  (GnomeBG               *img);
0c4bf27
+
0c4bf27
+
0c4bf27
+/* Set a pixmap as root - not a GnomeBG method */
0c4bf27
+void       gnome_bg_set_pixmap_as_root (GdkScreen             *screen,
0c4bf27
+					GdkPixmap             *pixmap);
0c4bf27
+
0c4bf27
+
0c4bf27
+#ifdef __cplusplus
0c4bf27
+}
0c4bf27
+#endif
0c4bf27
+
0c4bf27
+#endif