summaryrefslogtreecommitdiff
path: root/src/gs-fade.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gs-fade.c')
-rw-r--r--src/gs-fade.c987
1 files changed, 987 insertions, 0 deletions
diff --git a/src/gs-fade.c b/src/gs-fade.c
new file mode 100644
index 0000000..ba04b67
--- /dev/null
+++ b/src/gs-fade.c
@@ -0,0 +1,987 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2009 William Jon McCann <[email protected]>
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include "gs-fade.h"
+#include "gs-debug.h"
+
+#define MATE_DESKTOP_USE_UNSTABLE_API
+
+#include "libmateui/mate-rr.h"
+
+/* XFree86 4.x+ Gamma fading */
+
+
+#ifdef HAVE_XF86VMODE_GAMMA
+
+#include <X11/extensions/xf86vmode.h>
+
+#define XF86_MIN_GAMMA 0.1
+
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+static void gs_fade_class_init (GSFadeClass *klass);
+static void gs_fade_init (GSFade *fade);
+static void gs_fade_finalize (GObject *object);
+
+#define GS_FADE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_FADE, GSFadePrivate))
+
+struct GSGammaInfo
+{
+ int size;
+ unsigned short *r;
+ unsigned short *g;
+ unsigned short *b;
+};
+
+struct GSFadeScreenPrivate
+{
+ int fade_type;
+ int num_ramps;
+ /* one per crtc in randr mode */
+ struct GSGammaInfo *info;
+ /* one per screen in theory */
+ MateRRScreen *rrscreen;
+#ifdef HAVE_XF86VMODE_GAMMA
+ /* one per screen also */
+ XF86VidModeGamma vmg;
+#endif /* HAVE_XF86VMODE_GAMMA */
+ gboolean (*fade_setup) (GSFade *fade,
+ int screen);
+ gboolean (*fade_set_alpha_gamma) (GSFade *fade,
+ int screen,
+ gdouble alpha);
+ void (*fade_finish) (GSFade *fade,
+ int screen);
+};
+
+struct GSFadePrivate
+{
+ guint enabled : 1;
+ guint active : 1;
+
+ guint timeout;
+
+ guint step;
+ guint num_steps;
+ guint timer_id;
+
+ gdouble alpha_per_iter;
+ gdouble current_alpha;
+
+ int num_screens;
+
+ struct GSFadeScreenPrivate *screen_priv;
+};
+
+enum
+{
+ FADED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ FADE_TYPE_NONE,
+ FADE_TYPE_GAMMA_NUMBER,
+ FADE_TYPE_GAMMA_RAMP,
+ FADE_TYPE_XRANDR,
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSFade, gs_fade, G_TYPE_OBJECT)
+
+static gpointer fade_object = NULL;
+
+#ifdef HAVE_XF86VMODE_GAMMA
+
+/* This is needed because the VidMode extension doesn't work
+ on remote displays -- but if the remote display has the extension
+ at all, XF86VidModeQueryExtension returns true, and then
+ XF86VidModeQueryVersion dies with an X error.
+*/
+
+static gboolean error_handler_hit = FALSE;
+
+static int
+ignore_all_errors_ehandler (Display *dpy,
+ XErrorEvent *error)
+{
+ error_handler_hit = TRUE;
+
+ return 0;
+}
+
+static Bool
+safe_XF86VidModeQueryVersion (Display *dpy,
+ int *majP,
+ int *minP)
+{
+ Bool result;
+ XErrorHandler old_handler;
+
+ XSync (dpy, False);
+ error_handler_hit = FALSE;
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+ result = XF86VidModeQueryVersion (dpy, majP, minP);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XSync (dpy, False);
+
+ return (error_handler_hit
+ ? False
+ : result);
+}
+
+static gboolean
+xf86_whack_gamma (int screen,
+ struct GSFadeScreenPrivate *screen_priv,
+ float ratio)
+{
+ Bool status;
+ struct GSGammaInfo *gamma_info;
+
+ gamma_info = screen_priv->info;
+
+ if (!gamma_info)
+ return FALSE;
+
+ if (ratio < 0)
+ {
+ ratio = 0;
+ }
+ if (ratio > 1)
+ {
+ ratio = 1;
+ }
+
+ if (gamma_info->size == 0)
+ {
+ /* we only have a gamma number, not a ramp. */
+
+ XF86VidModeGamma g2;
+
+ g2.red = screen_priv->vmg.red * ratio;
+ g2.green = screen_priv->vmg.green * ratio;
+ g2.blue = screen_priv->vmg.blue * ratio;
+
+ if (g2.red < XF86_MIN_GAMMA)
+ {
+ g2.red = XF86_MIN_GAMMA;
+ }
+ if (g2.green < XF86_MIN_GAMMA)
+ {
+ g2.green = XF86_MIN_GAMMA;
+ }
+ if (g2.blue < XF86_MIN_GAMMA)
+ {
+ g2.blue = XF86_MIN_GAMMA;
+ }
+
+ status = XF86VidModeSetGamma (GDK_DISPLAY (), screen, &g2);
+ }
+ else
+ {
+
+# ifdef HAVE_XF86VMODE_GAMMA_RAMP
+ unsigned short *r, *g, *b;
+ int i;
+
+ r = g_new0 (unsigned short, gamma_info->size);
+ g = g_new0 (unsigned short, gamma_info->size);
+ b = g_new0 (unsigned short, gamma_info->size);
+
+ for (i = 0; i < gamma_info->size; i++)
+ {
+ r[i] = gamma_info->r[i] * ratio;
+ g[i] = gamma_info->g[i] * ratio;
+ b[i] = gamma_info->b[i] * ratio;
+ }
+
+ status = XF86VidModeSetGammaRamp (GDK_DISPLAY (), screen, gamma_info->size, r, g, b);
+
+ g_free (r);
+ g_free (g);
+ g_free (b);
+
+# else /* !HAVE_XF86VMODE_GAMMA_RAMP */
+ abort ();
+# endif /* !HAVE_XF86VMODE_GAMMA_RAMP */
+ }
+
+ gdk_flush ();
+
+ return status;
+}
+
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+/* VidModeExtension version 2.0 or better is needed to do gamma.
+ 2.0 added gamma values; 2.1 added gamma ramps.
+*/
+# define XF86_VIDMODE_GAMMA_MIN_MAJOR 2
+# define XF86_VIDMODE_GAMMA_MIN_MINOR 0
+# define XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR 2
+# define XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR 1
+
+
+gboolean
+gs_fade_get_enabled (GSFade *fade)
+{
+ g_return_val_if_fail (GS_IS_FADE (fade), FALSE);
+
+ return fade->priv->enabled;
+}
+
+void
+gs_fade_set_enabled (GSFade *fade,
+ gboolean enabled)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ if (fade->priv->enabled != enabled)
+ {
+ fade->priv->enabled = enabled;
+ }
+}
+
+#ifdef HAVE_XF86VMODE_GAMMA
+static gboolean
+gamma_fade_setup (GSFade *fade, int screen_idx)
+{
+ gboolean res;
+ struct GSFadeScreenPrivate *screen_priv;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (screen_priv->info)
+ return TRUE;
+
+# ifndef HAVE_XF86VMODE_GAMMA_RAMP
+ if (FADE_TYPE_GAMMA_RAMP == screen_priv->fade_type)
+ {
+ /* server is newer than client! */
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ }
+# endif
+
+# ifdef HAVE_XF86VMODE_GAMMA_RAMP
+
+ screen_priv->info = g_new0(struct GSGammaInfo, 1);
+ screen_priv->num_ramps = 1;
+
+ if (FADE_TYPE_GAMMA_RAMP == screen_priv->fade_type)
+ {
+ /* have ramps */
+
+ res = XF86VidModeGetGammaRampSize (GDK_DISPLAY (), screen_idx, &screen_priv->info->size);
+ if (!res || screen_priv->info->size <= 0)
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ goto test_number;
+ }
+
+ screen_priv->info->r = g_new0 (unsigned short, screen_priv->info->size);
+ screen_priv->info->g = g_new0 (unsigned short, screen_priv->info->size);
+ screen_priv->info->b = g_new0 (unsigned short, screen_priv->info->size);
+
+ if (! (screen_priv->info->r && screen_priv->info->g && screen_priv->info->b))
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ goto test_number;
+ }
+
+ res = XF86VidModeGetGammaRamp (GDK_DISPLAY (),
+ screen_idx,
+ screen_priv->info->size,
+ screen_priv->info->r,
+ screen_priv->info->g,
+ screen_priv->info->b);
+ if (! res)
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ goto test_number;
+ }
+ gs_debug ("Initialized gamma ramp fade");
+ }
+# endif /* HAVE_XF86VMODE_GAMMA_RAMP */
+
+test_number:
+ if (FADE_TYPE_GAMMA_NUMBER == screen_priv->fade_type)
+ {
+ /* only have gamma parameter, not ramps. */
+
+ res = XF86VidModeGetGamma (GDK_DISPLAY (), screen_idx, &screen_priv->vmg);
+ if (! res)
+ {
+ screen_priv->fade_type = FADE_TYPE_NONE;
+ goto test_none;
+ }
+ gs_debug ("Initialized gamma fade for screen %d: %f %f %f",
+ screen_idx,
+ screen_priv->vmg.red,
+ screen_priv->vmg.green,
+ screen_priv->vmg.blue);
+ }
+
+test_none:
+ if (FADE_TYPE_NONE == screen_priv->fade_type)
+ {
+ goto FAIL;
+ }
+
+ return TRUE;
+FAIL:
+
+ return FALSE;
+}
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+static void
+screen_fade_finish (GSFade *fade, int screen_idx)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ int i;
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (!screen_priv->info)
+ return;
+
+ for (i = 0; i < screen_priv->num_ramps; i++)
+ {
+ if (screen_priv->info[i].r)
+ g_free (screen_priv->info[i].r);
+ if (screen_priv->info[i].g)
+ g_free (screen_priv->info[i].g);
+ if (screen_priv->info[i].b)
+ g_free (screen_priv->info[i].b);
+ }
+
+ g_free (screen_priv->info);
+ screen_priv->info = NULL;
+ screen_priv->num_ramps = 0;
+}
+
+#ifdef HAVE_XF86VMODE_GAMMA
+static gboolean
+gamma_fade_set_alpha_gamma (GSFade *fade,
+ int screen_idx,
+ gdouble alpha)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ gboolean res;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+ res = xf86_whack_gamma (screen_idx, screen_priv, alpha);
+
+ return TRUE;
+}
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+static void
+check_gamma_extension (GSFade *fade, int screen_idx)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+#ifdef HAVE_XF86VMODE_GAMMA
+ int event;
+ int error;
+ int major;
+ int minor;
+ gboolean res;
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+#ifdef HAVE_XF86VMODE_GAMMA
+ res = XF86VidModeQueryExtension (GDK_DISPLAY (), &event, &error);
+ if (! res)
+ goto fade_none;
+
+ res = safe_XF86VidModeQueryVersion (GDK_DISPLAY (), &major, &minor);
+ if (! res)
+ goto fade_none;
+
+ if (major < XF86_VIDMODE_GAMMA_MIN_MAJOR ||
+ (major == XF86_VIDMODE_GAMMA_MIN_MAJOR &&
+ minor < XF86_VIDMODE_GAMMA_MIN_MINOR))
+ goto fade_none;
+
+ screen_priv->fade_setup = gamma_fade_setup;
+ screen_priv->fade_finish = screen_fade_finish;
+ screen_priv->fade_set_alpha_gamma = gamma_fade_set_alpha_gamma;
+
+ if (major < XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR ||
+ (major == XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR &&
+ minor < XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR))
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ return;
+ }
+
+ /* Copacetic */
+ screen_priv->fade_type = FADE_TYPE_GAMMA_RAMP;
+ return;
+fade_none:
+#endif
+ screen_priv->fade_type = FADE_TYPE_NONE;
+}
+
+/* Xrandr support */
+
+static gboolean xrandr_fade_setup (GSFade *fade, int screen_idx)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ MateRRCrtc *crtc;
+ MateRRCrtc **crtcs;
+ int crtc_count = 0;
+ struct GSGammaInfo *info;
+ gboolean res;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (screen_priv->info)
+ return TRUE;
+
+ /* refresh the screen info */
+ mate_rr_screen_refresh (screen_priv->rrscreen, NULL);
+
+ crtcs = mate_rr_screen_list_crtcs (screen_priv->rrscreen);
+ while (*crtcs)
+ {
+ crtc_count++;
+ crtcs++;
+ };
+
+ screen_priv->info = g_new0 (struct GSGammaInfo, crtc_count);
+ screen_priv->num_ramps = crtc_count;
+
+ crtc_count = 0;
+ crtcs = mate_rr_screen_list_crtcs (screen_priv->rrscreen);
+ while (*crtcs)
+ {
+ crtc = *crtcs;
+
+ info = &screen_priv->info[crtc_count];
+
+ /* if no mode ignore crtc */
+ if (!mate_rr_crtc_get_current_mode (crtc))
+ {
+ info->size = 0;
+ info->r = NULL;
+ info->g = NULL;
+ info->b = NULL;
+ }
+ else
+ {
+ res = mate_rr_crtc_get_gamma (crtc, &info->size,
+ &info->r, &info->g,
+ &info->b);
+ if (res == FALSE)
+ goto fail;
+ }
+
+ crtcs++;
+ crtc_count++;
+ }
+ return TRUE;
+fail:
+ return FALSE;
+}
+
+static void xrandr_crtc_whack_gamma (MateRRCrtc *crtc,
+ struct GSGammaInfo *gamma_info,
+ float ratio)
+{
+ unsigned short *r, *g, *b;
+ int i;
+
+ if (gamma_info->size == 0)
+ return;
+
+ if (ratio < 0)
+ {
+ ratio = 0;
+ }
+ if (ratio > 1)
+ {
+ ratio = 1;
+ }
+
+ r = g_new0 (unsigned short, gamma_info->size);
+ g = g_new0 (unsigned short, gamma_info->size);
+ b = g_new0 (unsigned short, gamma_info->size);
+
+ for (i = 0; i < gamma_info->size; i++)
+ {
+ r[i] = gamma_info->r[i] * ratio;
+ g[i] = gamma_info->g[i] * ratio;
+ b[i] = gamma_info->b[i] * ratio;
+ }
+
+ mate_rr_crtc_set_gamma (crtc, gamma_info->size,
+ r, g, b);
+ g_free (r);
+ g_free (g);
+ g_free (b);
+}
+
+static gboolean xrandr_fade_set_alpha_gamma (GSFade *fade,
+ int screen_idx,
+ gdouble alpha)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ struct GSGammaInfo *info;
+ MateRRCrtc **crtcs;
+ int i;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (!screen_priv->info)
+ return FALSE;
+
+ crtcs = mate_rr_screen_list_crtcs (screen_priv->rrscreen);
+ i = 0;
+
+ while (*crtcs)
+ {
+ info = &screen_priv->info[i];
+ xrandr_crtc_whack_gamma (*crtcs, info, alpha);
+ i++;
+ crtcs++;
+ }
+ return TRUE;
+}
+
+static void
+check_randr_extension (GSFade *fade, int screen_idx)
+{
+ GdkDisplay *display = gdk_display_get_default ();
+ GdkScreen *screen = gdk_display_get_screen (display, screen_idx);
+ struct GSFadeScreenPrivate *screen_priv;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ screen_priv->rrscreen = mate_rr_screen_new (screen,
+ NULL,
+ NULL,
+ NULL);
+ if (!screen_priv->rrscreen)
+ {
+ screen_priv->fade_type = FADE_TYPE_NONE;
+ return;
+ }
+
+ screen_priv->fade_type = FADE_TYPE_XRANDR;
+ screen_priv->fade_setup = xrandr_fade_setup;
+ screen_priv->fade_finish = screen_fade_finish;
+ screen_priv->fade_set_alpha_gamma = xrandr_fade_set_alpha_gamma;
+}
+
+static gboolean
+gs_fade_set_alpha (GSFade *fade,
+ gdouble alpha)
+{
+ gboolean ret = FALSE;
+ int i;
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ switch (fade->priv->screen_priv[i].fade_type)
+ {
+ case FADE_TYPE_GAMMA_RAMP:
+ case FADE_TYPE_GAMMA_NUMBER:
+ case FADE_TYPE_XRANDR:
+ ret = fade->priv->screen_priv[i].fade_set_alpha_gamma (fade, i, alpha);
+ break;
+ case FADE_TYPE_NONE:
+ ret = FALSE;
+ break;
+ default:
+ g_warning ("Unknown fade type");
+ ret = FALSE;
+ break;
+ }
+ }
+ return ret;
+}
+
+static gboolean
+gs_fade_out_iter (GSFade *fade)
+{
+ gboolean ret;
+
+ if (fade->priv->current_alpha < 0.01)
+ {
+ return FALSE;
+ }
+
+ fade->priv->current_alpha -= fade->priv->alpha_per_iter;
+
+ ret = gs_fade_set_alpha (fade, fade->priv->current_alpha);
+
+ return ret;
+}
+
+static gboolean
+gs_fade_stop (GSFade *fade)
+{
+ if (fade->priv->timer_id > 0)
+ {
+ g_source_remove (fade->priv->timer_id);
+ fade->priv->timer_id = 0;
+ }
+
+ fade->priv->step = 0;
+ fade->priv->active = FALSE;
+
+ return TRUE;
+}
+
+void
+gs_fade_finish (GSFade *fade)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ if (! fade->priv->active)
+ {
+ return;
+ }
+
+ gs_fade_stop (fade);
+
+ g_signal_emit (fade, signals [FADED], 0);
+
+ fade->priv->active = FALSE;
+}
+
+static gboolean
+fade_out_timer (GSFade *fade)
+{
+ gboolean res;
+
+ res = gs_fade_out_iter (fade);
+
+ /* if failed then fade is complete */
+ if (! res)
+ {
+ gs_fade_finish (fade);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_fade_get_active (GSFade *fade)
+{
+ g_return_val_if_fail (GS_IS_FADE (fade), FALSE);
+
+ return fade->priv->active;
+}
+
+static void
+gs_fade_set_timeout (GSFade *fade,
+ guint timeout)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ fade->priv->timeout = timeout;
+}
+
+static void
+gs_fade_start (GSFade *fade,
+ guint timeout)
+{
+ guint steps_per_sec = 30;
+ guint msecs_per_step;
+ struct GSFadeScreenPrivate *screen_priv;
+ gboolean active_fade, res;
+ int i;
+
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ screen_priv = &fade->priv->screen_priv[i];
+ if (screen_priv->fade_type != FADE_TYPE_NONE)
+ {
+ res = screen_priv->fade_setup (fade, i);
+ if (res == FALSE)
+ return;
+ }
+ }
+
+ if (fade->priv->timer_id > 0)
+ {
+ gs_fade_stop (fade);
+ }
+
+ fade->priv->active = TRUE;
+
+ gs_fade_set_timeout (fade, timeout);
+
+ active_fade = FALSE;
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ screen_priv = &fade->priv->screen_priv[i];
+ if (screen_priv->fade_type != FADE_TYPE_NONE)
+ active_fade = TRUE;
+ }
+ if (active_fade)
+ {
+ guint num_steps;
+
+ num_steps = (fade->priv->timeout / 1000) * steps_per_sec;
+ msecs_per_step = 1000 / steps_per_sec;
+ fade->priv->alpha_per_iter = 1.0 / (gdouble)num_steps;
+
+ fade->priv->timer_id = g_timeout_add (msecs_per_step, (GSourceFunc)fade_out_timer, fade);
+ }
+ else
+ {
+ gs_fade_finish (fade);
+ }
+}
+
+typedef struct
+{
+ GSFadeDoneFunc done_cb;
+ gpointer data;
+} FadedCallbackData;
+
+static void
+gs_fade_async_callback (GSFade *fade,
+ FadedCallbackData *cdata)
+{
+ g_signal_handlers_disconnect_by_func (fade,
+ gs_fade_async_callback,
+ cdata);
+
+ if (cdata->done_cb)
+ {
+ cdata->done_cb (fade, cdata->data);
+ }
+
+ g_free (cdata);
+}
+
+void
+gs_fade_async (GSFade *fade,
+ guint timeout,
+ GSFadeDoneFunc func,
+ gpointer data)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ /* if fade is active then pause it */
+ if (fade->priv->active)
+ {
+ gs_fade_stop (fade);
+ }
+
+ if (func)
+ {
+ FadedCallbackData *cb_data;
+
+ cb_data = g_new0 (FadedCallbackData, 1);
+ cb_data->done_cb = func;
+ cb_data->data = data;
+
+ g_signal_connect (fade, "faded",
+ G_CALLBACK (gs_fade_async_callback),
+ cb_data);
+ }
+
+ gs_fade_start (fade, timeout);
+}
+
+static void
+gs_fade_sync_callback (GSFade *fade,
+ int *flag)
+{
+ *flag = TRUE;
+ g_signal_handlers_disconnect_by_func (fade,
+ gs_fade_sync_callback,
+ flag);
+}
+
+void
+gs_fade_sync (GSFade *fade,
+ guint timeout)
+{
+ int flag = FALSE;
+
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ /* if fade is active then pause it */
+ if (fade->priv->active)
+ {
+ gs_fade_stop (fade);
+ }
+
+ g_signal_connect (fade, "faded",
+ G_CALLBACK (gs_fade_sync_callback),
+ &flag);
+
+ gs_fade_start (fade, timeout);
+
+ while (! flag)
+ {
+ gtk_main_iteration ();
+ }
+}
+
+void
+gs_fade_reset (GSFade *fade)
+{
+ int i;
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ gs_debug ("Resetting fade");
+
+ if (fade->priv->active)
+ {
+ gs_fade_stop (fade);
+ }
+
+ fade->priv->current_alpha = 1.0;
+
+ gs_fade_set_alpha (fade, fade->priv->current_alpha);
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ fade->priv->screen_priv[i].fade_finish (fade, i);
+}
+
+static void
+gs_fade_class_init (GSFadeClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_fade_finalize;
+
+ signals [FADED] =
+ g_signal_new ("faded",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSFadeClass, faded),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0, G_TYPE_NONE);
+
+ g_type_class_add_private (klass, sizeof (GSFadePrivate));
+}
+
+static void
+gs_fade_init (GSFade *fade)
+{
+ GdkDisplay *display;
+ int i;
+
+ fade->priv = GS_FADE_GET_PRIVATE (fade);
+
+ fade->priv->timeout = 1000;
+ fade->priv->current_alpha = 1.0;
+
+ display = gdk_display_get_default ();
+ fade->priv->num_screens = gdk_display_get_n_screens (display);
+
+ fade->priv->screen_priv = g_new0 (struct GSFadeScreenPrivate, fade->priv->num_screens);
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ check_randr_extension (fade, i);
+ if (!fade->priv->screen_priv[i].fade_type)
+ check_gamma_extension (fade, i);
+ gs_debug ("Fade type: %d", fade->priv->screen_priv[i].fade_type);
+ }
+}
+
+static void
+gs_fade_finalize (GObject *object)
+{
+ GSFade *fade;
+ int i;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_FADE (object));
+
+ fade = GS_FADE (object);
+
+ g_return_if_fail (fade->priv != NULL);
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ fade->priv->screen_priv[i].fade_finish(fade, i);
+
+ if (fade->priv->screen_priv)
+ {
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ if (!fade->priv->screen_priv[i].rrscreen)
+ continue;
+ mate_rr_screen_destroy (fade->priv->screen_priv[i].rrscreen);
+ }
+
+ g_free (fade->priv->screen_priv);
+ fade->priv->screen_priv = NULL;
+ }
+
+ G_OBJECT_CLASS (gs_fade_parent_class)->finalize (object);
+}
+
+GSFade *
+gs_fade_new (void)
+{
+ if (fade_object)
+ {
+ g_object_ref (fade_object);
+ }
+ else
+ {
+ fade_object = g_object_new (GS_TYPE_FADE, NULL);
+ g_object_add_weak_pointer (fade_object,
+ (gpointer *) &fade_object);
+ }
+
+ return GS_FADE (fade_object);
+}