diff options
author | Ray Strode <halfline@src.gnome.org> | 2009-01-19 22:57:40 +0000 |
---|---|---|
committer | Ray Strode <halfline@src.gnome.org> | 2009-01-19 22:57:40 +0000 |
commit | 952ee53e4a33f0164592fba45ecc70b0802daafe (patch) | |
tree | 8aea27eee55da1f171d9eaf395902ec02c144345 | |
parent | 5e5282c38afaa6ca19514448683eff81877662c0 (diff) | |
download | gnome-desktop-952ee53e4a33f0164592fba45ecc70b0802daafe.tar.gz |
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.
svn path=/trunk/; revision=5353
-rw-r--r-- | libgnome-desktop/ChangeLog | 7 | ||||
-rw-r--r-- | libgnome-desktop/Makefile.am | 1 | ||||
-rw-r--r-- | libgnome-desktop/gnome-bg-crossfade.c | 542 | ||||
-rw-r--r-- | libgnome-desktop/libgnomeui/Makefile.am | 1 | ||||
-rw-r--r-- | libgnome-desktop/libgnomeui/gnome-bg-crossfade.h | 74 |
5 files changed, 625 insertions, 0 deletions
diff --git a/libgnome-desktop/ChangeLog b/libgnome-desktop/ChangeLog index 3250aae3..7722eea9 100644 --- a/libgnome-desktop/ChangeLog +++ b/libgnome-desktop/ChangeLog @@ -1,5 +1,12 @@ 2009-01-19 Ray Strode <rstrode@redhat.com> + Add Crossfade class + + * gnome-bg-crossfade.[ch]: New files + to manage a cross fade animation (not used yet). + +2009-01-19 Ray Strode <rstrode@redhat.com> + Move part of set_pixmap_as_root to set_root_pixmap_id * gnome-bg.c (set_pixmap_as_root), diff --git a/libgnome-desktop/Makefile.am b/libgnome-desktop/Makefile.am index 8c7540c0..a30018fc 100644 --- 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 index 00000000..d57fea65 --- /dev/null +++ b/libgnome-desktop/gnome-bg-crossfade.c @@ -0,0 +1,542 @@ +/* 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 <rstrode@redhat.com> +*/ + +#include <string.h> +#include <math.h> +#include <stdarg.h> + +#include <gio/gio.h> + +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <gtk/gtk.h> + +#include <cairo.h> +#include <cairo-xlib.h> + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include <libgnomeui/gnome-bg.h> +#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); + + gnome_bg_crossfade_stop (fade); + + 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; + } +} + +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; +} + +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; +} + +/** + * 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); + + /* Reset timer in case we're called while animating + */ + fade->priv->start_time = get_current_time (); + return fade->priv->end_pixmap != NULL; +} + +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_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) +{ + if (fade->priv->timeout_id == 0) + return; + + 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 + * + * 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) +{ + GSource *source; + GMainContext *context; + + 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)); + g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN); + + source = g_timeout_source_new (1000 / 60.0); + g_source_set_callback (source, + (GSourceFunc) on_tick, + fade, + (GDestroyNotify) on_finished); + context = g_main_context_default (); + 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 index 7e11941b..578f09a6 100644 --- 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 index 00000000..b70dc89a --- /dev/null +++ b/libgnome-desktop/libgnomeui/gnome-bg-crossfade.h @@ -0,0 +1,74 @@ +/* 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 <rstrode@redhat.com> +*/ + +#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 <gdk/gdk.h> + +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); +gboolean gnome_bg_crossfade_is_started (GnomeBGCrossfade *fade); +void gnome_bg_crossfade_stop (GnomeBGCrossfade *fade); + +G_END_DECLS + +#endif |