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