diff options
Diffstat (limited to 'src/gs-manager.c')
-rw-r--r-- | src/gs-manager.c | 2030 |
1 files changed, 2030 insertions, 0 deletions
diff --git a/src/gs-manager.c b/src/gs-manager.c new file mode 100644 index 0000000..3deca22 --- /dev/null +++ b/src/gs-manager.c @@ -0,0 +1,2030 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2004-2008 William Jon McCann <[email protected]> + * + * 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 <time.h> +#include <gdk/gdk.h> +#include <gdk/gdkx.h> + +#include <mateconf/mateconf-engine.h> +#include <mateconf/mateconf-client.h> + +#define MATE_DESKTOP_USE_UNSTABLE_API +#include <libmateui/mate-bg.h> + +#include "gs-prefs.h" /* for GSSaverMode */ + +#include "gs-manager.h" +#include "gs-window.h" +#include "gs-theme-manager.h" +#include "gs-job.h" +#include "gs-grab.h" +#include "gs-fade.h" +#include "gs-debug.h" + +static void gs_manager_class_init (GSManagerClass *klass); +static void gs_manager_init (GSManager *manager); +static void gs_manager_finalize (GObject *object); + +#define GS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_MANAGER, GSManagerPrivate)) + +struct GSManagerPrivate +{ + GSList *windows; + GHashTable *jobs; + + GSThemeManager *theme_manager; + MateConfClient *client; + MateBG *bg; + guint bg_notify_id; + + /* Policy */ + glong lock_timeout; + glong cycle_timeout; + glong logout_timeout; + + guint lock_enabled : 1; + guint logout_enabled : 1; + guint keyboard_enabled : 1; + guint user_switch_enabled : 1; + guint throttled : 1; + + char *logout_command; + char *keyboard_command; + + char *status_message; + + /* State */ + guint active : 1; + guint lock_active : 1; + + guint fading : 1; + guint dialog_up : 1; + + time_t activate_time; + + guint lock_timeout_id; + guint cycle_timeout_id; + + GSList *themes; + GSSaverMode saver_mode; + GSGrab *grab; + GSFade *fade; + guint unfade_idle_id; +}; + +enum +{ + ACTIVATED, + DEACTIVATED, + AUTH_REQUEST_BEGIN, + AUTH_REQUEST_END, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_LOCK_ENABLED, + PROP_LOGOUT_ENABLED, + PROP_USER_SWITCH_ENABLED, + PROP_KEYBOARD_ENABLED, + PROP_LOCK_TIMEOUT, + PROP_CYCLE_TIMEOUT, + PROP_LOGOUT_TIMEOUT, + PROP_LOGOUT_COMMAND, + PROP_KEYBOARD_COMMAND, + PROP_STATUS_MESSAGE, + PROP_ACTIVE, + PROP_THROTTLED, +}; + +#define FADE_TIMEOUT 1000 + +static guint signals [LAST_SIGNAL] = { 0, }; + +G_DEFINE_TYPE (GSManager, gs_manager, G_TYPE_OBJECT) + +static void +manager_add_job_for_window (GSManager *manager, + GSWindow *window, + GSJob *job) +{ + if (manager->priv->jobs == NULL) + { + return; + } + + g_hash_table_insert (manager->priv->jobs, window, job); +} + +static const char * +select_theme (GSManager *manager) +{ + const char *theme = NULL; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (GS_IS_MANAGER (manager), NULL); + + if (manager->priv->saver_mode == GS_MODE_BLANK_ONLY) + { + return NULL; + } + + if (manager->priv->themes) + { + int number = 0; + + if (manager->priv->saver_mode == GS_MODE_RANDOM) + { + g_random_set_seed (time (NULL)); + number = g_random_int_range (0, g_slist_length (manager->priv->themes)); + } + theme = g_slist_nth_data (manager->priv->themes, number); + } + + return theme; +} + +static GSJob * +lookup_job_for_window (GSManager *manager, + GSWindow *window) +{ + GSJob *job; + + if (manager->priv->jobs == NULL) + { + return NULL; + } + + job = g_hash_table_lookup (manager->priv->jobs, window); + + return job; +} + +static void +manager_maybe_stop_job_for_window (GSManager *manager, + GSWindow *window) +{ + GSJob *job; + + job = lookup_job_for_window (manager, window); + + if (job == NULL) + { + gs_debug ("Job not found for window"); + return; + } + + gs_job_stop (job); +} + +static void +manager_maybe_start_job_for_window (GSManager *manager, + GSWindow *window) +{ + GSJob *job; + + job = lookup_job_for_window (manager, window); + + if (job == NULL) + { + gs_debug ("Job not found for window"); + return; + } + + if (! manager->priv->dialog_up) + { + if (! manager->priv->throttled) + { + if (! gs_job_is_running (job)) + { + if (! gs_window_is_obscured (window)) + { + gs_debug ("Starting job for window"); + gs_job_start (job); + } + else + { + gs_debug ("Window is obscured deferring start of job"); + } + } + else + { + gs_debug ("Not starting job because job is running"); + } + } + else + { + gs_debug ("Not starting job because throttled"); + } + } + else + { + gs_debug ("Not starting job because dialog is up"); + } +} + +static void +manager_select_theme_for_job (GSManager *manager, + GSJob *job) +{ + const char *theme; + + theme = select_theme (manager); + + if (theme != NULL) + { + GSThemeInfo *info; + const char *command; + + command = NULL; + + info = gs_theme_manager_lookup_theme_info (manager->priv->theme_manager, theme); + if (info != NULL) + { + command = gs_theme_info_get_exec (info); + } + else + { + gs_debug ("Could not find information for theme: %s", + theme); + } + + gs_job_set_command (job, command); + + + if (info != NULL) + { + gs_theme_info_unref (info); + } + } + else + { + gs_job_set_command (job, NULL); + } +} + +static void +cycle_job (GSWindow *window, + GSJob *job, + GSManager *manager) +{ + gs_job_stop (job); + manager_select_theme_for_job (manager, job); + manager_maybe_start_job_for_window (manager, window); +} + +static void +manager_cycle_jobs (GSManager *manager) +{ + if (manager->priv->jobs != NULL) + { + g_hash_table_foreach (manager->priv->jobs, (GHFunc) cycle_job, manager); + } +} + +static void +throttle_job (GSWindow *window, + GSJob *job, + GSManager *manager) +{ + if (manager->priv->throttled) + { + gs_job_stop (job); + } + else + { + manager_maybe_start_job_for_window (manager, window); + } +} + +static void +manager_throttle_jobs (GSManager *manager) +{ + if (manager->priv->jobs != NULL) + { + g_hash_table_foreach (manager->priv->jobs, (GHFunc) throttle_job, manager); + } +} + +static void +resume_job (GSWindow *window, + GSJob *job, + GSManager *manager) +{ + if (gs_job_is_running (job)) + { + gs_job_suspend (job, FALSE); + } + else + { + manager_maybe_start_job_for_window (manager, window); + } +} + +static void +manager_resume_jobs (GSManager *manager) +{ + if (manager->priv->jobs != NULL) + { + g_hash_table_foreach (manager->priv->jobs, (GHFunc) resume_job, manager); + } +} + +static void +suspend_job (GSWindow *window, + GSJob *job, + GSManager *manager) +{ + gs_job_suspend (job, TRUE); +} + +static void +manager_suspend_jobs (GSManager *manager) +{ + if (manager->priv->jobs != NULL) + { + g_hash_table_foreach (manager->priv->jobs, (GHFunc) suspend_job, manager); + } +} + +static void +manager_stop_jobs (GSManager *manager) +{ + if (manager->priv->jobs != NULL) + { + g_hash_table_destroy (manager->priv->jobs); + + } + manager->priv->jobs = NULL; +} + +void +gs_manager_set_mode (GSManager *manager, + GSSaverMode mode) +{ + g_return_if_fail (GS_IS_MANAGER (manager)); + + manager->priv->saver_mode = mode; +} + +static void +free_themes (GSManager *manager) +{ + if (manager->priv->themes) + { + g_slist_foreach (manager->priv->themes, (GFunc)g_free, NULL); + g_slist_free (manager->priv->themes); + } +} + +void +gs_manager_set_themes (GSManager *manager, + GSList *themes) +{ + GSList *l; + + g_return_if_fail (GS_IS_MANAGER (manager)); + + free_themes (manager); + manager->priv->themes = NULL; + + for (l = themes; l; l = l->next) + { + manager->priv->themes = g_slist_append (manager->priv->themes, g_strdup (l->data)); + } +} + +void +gs_manager_set_throttled (GSManager *manager, + gboolean throttled) +{ + g_return_if_fail (GS_IS_MANAGER (manager)); + + if (manager->priv->throttled != throttled) + { + GSList *l; + + manager->priv->throttled = throttled; + + if (! manager->priv->dialog_up) + { + + manager_throttle_jobs (manager); + + for (l = manager->priv->windows; l; l = l->next) + { + gs_window_clear (l->data); + } + } + } +} + +void +gs_manager_get_lock_active (GSManager *manager, + gboolean *lock_active) +{ + if (lock_active != NULL) + { + *lock_active = FALSE; + } + + g_return_if_fail (GS_IS_MANAGER (manager)); + + *lock_active = manager->priv->lock_active; +} + +void +gs_manager_set_lock_active (GSManager *manager, + gboolean lock_active) +{ + g_return_if_fail (GS_IS_MANAGER (manager)); + + gs_debug ("Setting lock active: %d", lock_active); + + if (manager->priv->lock_active != lock_active) + { + GSList *l; + + manager->priv->lock_active = lock_active; + for (l = manager->priv->windows; l; l = l->next) + { + gs_window_set_lock_enabled (l->data, lock_active); + } + } +} + +void +gs_manager_get_lock_enabled (GSManager *manager, + gboolean *lock_enabled) +{ + if (lock_enabled != NULL) + { + *lock_enabled = FALSE; + } + + g_return_if_fail (GS_IS_MANAGER (manager)); + + *lock_enabled = manager->priv->lock_enabled; +} + +void +gs_manager_set_lock_enabled (GSManager *manager, + gboolean lock_enabled) +{ + g_return_if_fail (GS_IS_MANAGER (manager)); + + if (manager->priv->lock_enabled != lock_enabled) + { + manager->priv->lock_enabled = lock_enabled; + } +} + +void +gs_manager_set_logout_enabled (GSManager *manager, + gboolean logout_enabled) +{ + g_return_if_fail (GS_IS_MANAGER (manager)); + + if (manager->priv->logout_enabled != logout_enabled) + { + GSList *l; + + manager->priv->logout_enabled = logout_enabled; + for (l = manager->priv->windows; l; l = l->next) + { + gs_window_set_logout_enabled (l->data, logout_enabled); + } + } +} + +void +gs_manager_set_keyboard_enabled (GSManager *manager, + gboolean enabled) +{ + g_return_if_fail (GS_IS_MANAGER (manager)); + + if (manager->priv->keyboard_enabled != enabled) + { + GSList *l; + + manager->priv->keyboard_enabled = enabled; + for (l = manager->priv->windows; l; l = l->next) + { + gs_window_set_keyboard_enabled (l->data, enabled); + } + } +} + +void +gs_manager_set_user_switch_enabled (GSManager *manager, + gboolean user_switch_enabled) +{ + g_return_if_fail (GS_IS_MANAGER (manager)); + + if (manager->priv->user_switch_enabled != user_switch_enabled) + { + GSList *l; + + manager->priv->user_switch_enabled = user_switch_enabled; + for (l = manager->priv->windows; l; l = l->next) + { + gs_window_set_user_switch_enabled (l->data, user_switch_enabled); + } + } +} + +static gboolean +activate_lock_timeout (GSManager *manager) +{ + if (manager->priv->lock_enabled) + { + gs_manager_set_lock_active (manager, TRUE); + } + + manager->priv->lock_timeout_id = 0; + + return FALSE; +} + +static void +remove_lock_timer (GSManager *manager) +{ + if (manager->priv->lock_timeout_id != 0) + { + g_source_remove (manager->priv->lock_timeout_id); + manager->priv->lock_timeout_id = 0; + } +} + +static void +add_lock_timer (GSManager *manager, + glong timeout) +{ + manager->priv->lock_timeout_id = g_timeout_add (timeout, + (GSourceFunc)activate_lock_timeout, + manager); +} + +void +gs_manager_set_lock_timeout (GSManager *manager, + glong lock_timeout) +{ + g_return_if_fail (GS_IS_MANAGER (manager)); + + if (manager->priv->lock_timeout != lock_timeout) + { + + manager->priv->lock_timeout = lock_timeout; + + if (manager->priv->active + && ! manager->priv->lock_active + && (lock_timeout >= 0)) + { + + glong elapsed = (time (NULL) - manager->priv->activate_time) * 1000; + + remove_lock_timer (manager); + + if (elapsed >= lock_timeout) + { + activate_lock_timeout (manager); + } + else + { + add_lock_timer (manager, lock_timeout - elapsed); + } + } + } +} + +void +gs_manager_set_logout_timeout (GSManager *manager, + glong logout_timeout) +{ + g_return_if_fail (GS_IS_MANAGER (manager)); + + if (manager->priv->logout_timeout != logout_timeout) + { + GSList *l; + + manager->priv->logout_timeout = logout_timeout; + for (l = manager->priv->windows; l; l = l->next) + { + gs_window_set_logout_timeout (l->data, logout_timeout); + } + } +} + +void +gs_manager_set_logout_command (GSManager *manager, + const char *command) +{ + GSList *l; + + g_return_if_fail (GS_IS_MANAGER (manager)); + + g_free (manager->priv->logout_command); + + if (command) + { + manager->priv->logout_command = g_strdup (command); + } + else + { + manager->priv->logout_command = NULL; + } + + for (l = manager->priv->windows; l; l = l->next) + { + gs_window_set_logout_command (l->data, manager->priv->logout_command); + } +} + +void +gs_manager_set_keyboard_command (GSManager *manager, + const char *command) +{ + GSList *l; + + g_return_if_fail (GS_IS_MANAGER (manager)); + + g_free (manager->priv->keyboard_command); + + if (command) + { + manager->priv->keyboard_command = g_strdup (command); + } + else + { + manager->priv->keyboard_command = NULL; + } + + for (l = manager->priv->windows; l; l = l->next) + { + gs_window_set_keyboard_command (l->data, manager->priv->keyboard_command); + } +} + +void +gs_manager_set_status_message (GSManager *manager, + const char *status_message) +{ + GSList *l; + + g_return_if_fail (GS_IS_MANAGER (manager)); + + g_free (manager->priv->status_message); + + manager->priv->status_message = g_strdup (status_message); + + for (l = manager->priv->windows; l; l = l->next) + { + gs_window_set_status_message (l->data, manager->priv->status_message); + } +} + +gboolean +gs_manager_cycle (GSManager *manager) +{ + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE); + + gs_debug ("cycling jobs"); + + if (! manager->priv->active) + { + return FALSE; + } + + if (manager->priv->dialog_up) + { + return FALSE; + } + + if (manager->priv->throttled) + { + return FALSE; + } + + manager_cycle_jobs (manager); + + return TRUE; +} + +static gboolean +cycle_timeout (GSManager *manager) +{ + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE); + + if (! manager->priv->dialog_up) + { + gs_manager_cycle (manager); + } + + return TRUE; +} + +static void +remove_cycle_timer (GSManager *manager) +{ + if (manager->priv->cycle_timeout_id != 0) + { + g_source_remove (manager->priv->cycle_timeout_id); + manager->priv->cycle_timeout_id = 0; + } +} + +static void +add_cycle_timer (GSManager *manager, + glong timeout) +{ + manager->priv->cycle_timeout_id = g_timeout_add (timeout, + (GSourceFunc)cycle_timeout, + manager); +} + +void +gs_manager_set_cycle_timeout (GSManager *manager, + glong cycle_timeout) +{ + g_return_if_fail (GS_IS_MANAGER (manager)); + + if (manager->priv->cycle_timeout != cycle_timeout) + { + + manager->priv->cycle_timeout = cycle_timeout; + + if (manager->priv->active && (cycle_timeout >= 0)) + { + glong timeout; + glong elapsed = (time (NULL) - manager->priv->activate_time) * 1000; + + remove_cycle_timer (manager); + + if (elapsed >= cycle_timeout) + { + timeout = 0; + } + else + { + timeout = cycle_timeout - elapsed; + } + + add_cycle_timer (manager, timeout); + + } + } +} + +static void +gs_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSManager *self; + + self = GS_MANAGER (object); + + switch (prop_id) + { + case PROP_THROTTLED: + gs_manager_set_throttled (self, g_value_get_boolean (value)); + break; + case PROP_LOCK_ENABLED: + gs_manager_set_lock_enabled (self, g_value_get_boolean (value)); + break; + case PROP_LOCK_TIMEOUT: + gs_manager_set_lock_timeout (self, g_value_get_long (value)); + break; + case PROP_LOGOUT_ENABLED: + gs_manager_set_logout_enabled (self, g_value_get_boolean (value)); + break; + case PROP_KEYBOARD_ENABLED: + gs_manager_set_keyboard_enabled (self, g_value_get_boolean (value)); + break; + case PROP_USER_SWITCH_ENABLED: + gs_manager_set_user_switch_enabled (self, g_value_get_boolean (value)); + break; + case PROP_LOGOUT_TIMEOUT: + gs_manager_set_logout_timeout (self, g_value_get_long (value)); + break; + case PROP_LOGOUT_COMMAND: + gs_manager_set_logout_command (self, g_value_get_string (value)); + break; + case PROP_KEYBOARD_COMMAND: + gs_manager_set_keyboard_command (self, g_value_get_string (value)); + break; + case PROP_STATUS_MESSAGE: + gs_manager_set_status_message (self, g_value_get_string (value)); + break; + case PROP_CYCLE_TIMEOUT: + gs_manager_set_cycle_timeout (self, g_value_get_long (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSManager *self; + + self = GS_MANAGER (object); + + switch (prop_id) + { + case PROP_THROTTLED: + g_value_set_boolean (value, self->priv->throttled); + break; + case PROP_LOCK_ENABLED: + g_value_set_boolean (value, self->priv->lock_enabled); + break; + case PROP_LOCK_TIMEOUT: + g_value_set_long (value, self->priv->lock_timeout); + break; + case PROP_LOGOUT_ENABLED: + g_value_set_boolean (value, self->priv->logout_enabled); + break; + case PROP_KEYBOARD_ENABLED: + g_value_set_boolean (value, self->priv->keyboard_enabled); + break; + case PROP_USER_SWITCH_ENABLED: + g_value_set_boolean (value, self->priv->user_switch_enabled); + break; + case PROP_LOGOUT_TIMEOUT: + g_value_set_long (value, self->priv->logout_timeout); + break; + case PROP_LOGOUT_COMMAND: + g_value_set_string (value, self->priv->logout_command); + break; + case PROP_KEYBOARD_COMMAND: + g_value_set_string (value, self->priv->keyboard_command); + break; + case PROP_STATUS_MESSAGE: + g_value_set_string (value, self->priv->status_message); + break; + case PROP_CYCLE_TIMEOUT: + g_value_set_long (value, self->priv->cycle_timeout); + break; + case PROP_ACTIVE: + g_value_set_boolean (value, self->priv->active); + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_manager_class_init (GSManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gs_manager_finalize; + object_class->get_property = gs_manager_get_property; + object_class->set_property = gs_manager_set_property; + + signals [ACTIVATED] = + g_signal_new ("activated", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSManagerClass, activated), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals [DEACTIVATED] = + g_signal_new ("deactivated", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSManagerClass, deactivated), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals [AUTH_REQUEST_BEGIN] = + g_signal_new ("auth-request-begin", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSManagerClass, auth_request_begin), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals [AUTH_REQUEST_END] = + g_signal_new ("auth-request-end", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSManagerClass, auth_request_end), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + g_object_class_install_property (object_class, + PROP_ACTIVE, + g_param_spec_boolean ("active", + NULL, + NULL, + FALSE, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_LOCK_ENABLED, + g_param_spec_boolean ("lock-enabled", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_LOCK_TIMEOUT, + g_param_spec_long ("lock-timeout", + NULL, + NULL, + -1, + G_MAXLONG, + 0, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_LOGOUT_ENABLED, + g_param_spec_boolean ("logout-enabled", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_USER_SWITCH_ENABLED, + g_param_spec_boolean ("user-switch-enabled", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_LOGOUT_TIMEOUT, + g_param_spec_long ("logout-timeout", + NULL, + NULL, + -1, + G_MAXLONG, + 0, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_LOGOUT_COMMAND, + g_param_spec_string ("logout-command", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_CYCLE_TIMEOUT, + g_param_spec_long ("cycle-timeout", + NULL, + NULL, + 10000, + G_MAXLONG, + 300000, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_THROTTLED, + g_param_spec_boolean ("throttled", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE)); + + g_type_class_add_private (klass, sizeof (GSManagerPrivate)); +} + +static void +on_bg_changed (MateBG *bg, + GSManager *manager) +{ + gs_debug ("background changed"); +} + +static void +mateconf_changed_callback (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + GSManager *manager) +{ + mate_bg_load_from_preferences (manager->priv->bg, + manager->priv->client); +} + +static void +watch_bg_preferences (GSManager *manager) +{ + g_assert (manager->priv->bg_notify_id == 0); + + mateconf_client_add_dir (manager->priv->client, + MATE_BG_KEY_DIR, + MATECONF_CLIENT_PRELOAD_NONE, + NULL); + manager->priv->bg_notify_id = mateconf_client_notify_add (manager->priv->client, + MATE_BG_KEY_DIR, + (MateConfClientNotifyFunc)mateconf_changed_callback, + manager, + NULL, + NULL); +} + +static MateConfClient * +get_mateconf_client (void) +{ + MateConfClient *client; + GSList *addresses; + GError *error; + MateConfEngine *engine; + + client = NULL; + addresses = NULL; + + addresses = g_slist_prepend (addresses, "xml:merged:" SYSCONFDIR "/mateconf/mateconf.xml.mandatory"); + addresses = g_slist_prepend (addresses, "xml:merged:" SYSCONFDIR "/mateconf/mateconf.xml.system"); + addresses = g_slist_prepend (addresses, "xml:merged:" SYSCONFDIR "/mateconf/mateconf.xml.defaults"); + addresses = g_slist_reverse (addresses); + + error = NULL; + engine = mateconf_engine_get_for_addresses (addresses, &error); + if (engine == NULL) + { + gs_debug ("Unable to get mateconf engine for addresses: %s", error->message); + g_error_free (error); + } + else + { + client = mateconf_client_get_for_engine (engine); + } + + g_slist_free (addresses); + + return client; +} + +static void +gs_manager_init (GSManager *manager) +{ + manager->priv = GS_MANAGER_GET_PRIVATE (manager); + + manager->priv->fade = gs_fade_new (); + manager->priv->grab = gs_grab_new (); + manager->priv->theme_manager = gs_theme_manager_new (); + + manager->priv->client = get_mateconf_client (); + if (manager->priv->client != NULL) + { + manager->priv->bg = mate_bg_new (); + + g_signal_connect (manager->priv->bg, + "changed", + G_CALLBACK (on_bg_changed), + manager); + watch_bg_preferences (manager); + + mate_bg_load_from_preferences (manager->priv->bg, manager->priv->client); + } +} + +static void +remove_timers (GSManager *manager) +{ + remove_lock_timer (manager); + remove_cycle_timer (manager); +} + +static void +remove_unfade_idle (GSManager *manager) +{ + if (manager->priv->unfade_idle_id > 0) + { + g_source_remove (manager->priv->unfade_idle_id); + manager->priv->unfade_idle_id = 0; + } +} + + +static gboolean +window_deactivated_idle (GSManager *manager) +{ + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE); + + /* don't deactivate directly but only emit a signal + so that we let the parent deactivate */ + g_signal_emit (manager, signals [DEACTIVATED], 0); + + return FALSE; +} + +static void +window_deactivated_cb (GSWindow *window, + GSManager *manager) +{ + g_return_if_fail (manager != NULL); + g_return_if_fail (GS_IS_MANAGER (manager)); + + g_idle_add ((GSourceFunc)window_deactivated_idle, manager); +} + +static GSWindow * +find_window_at_pointer (GSManager *manager) +{ + GdkDisplay *display; + GdkScreen *screen; + int monitor; + int x, y; + GSWindow *window; + int screen_num; + GSList *l; + + display = gdk_display_get_default (); + gdk_display_get_pointer (display, &screen, &x, &y, NULL); + monitor = gdk_screen_get_monitor_at_point (screen, x, y); + screen_num = gdk_screen_get_number (screen); + + /* Find the gs-window that is on that screen */ + window = NULL; + for (l = manager->priv->windows; l; l = l->next) + { + GSWindow *win = GS_WINDOW (l->data); + if (gs_window_get_screen (win) == screen + && gs_window_get_monitor (win) == monitor) + { + window = win; + } + } + + if (window == NULL) + { + gs_debug ("WARNING: Could not find the GSWindow for screen %d", screen_num); + /* take the first one */ + window = manager->priv->windows->data; + } + else + { + gs_debug ("Requesting unlock for screen %d", screen_num); + } + + return window; +} + +void +gs_manager_show_message (GSManager *manager, + const char *summary, + const char *body, + const char *icon) +{ + GSWindow *window; + + g_return_if_fail (GS_IS_MANAGER (manager)); + + /* Find the GSWindow that contains the pointer */ + window = find_window_at_pointer (manager); + gs_window_show_message (window, summary, body, icon); + + gs_manager_request_unlock (manager); +} + +static gboolean +manager_maybe_grab_window (GSManager *manager, + GSWindow *window) +{ + GdkDisplay *display; + GdkScreen *screen; + int monitor; + int x, y; + gboolean grabbed; + + display = gdk_display_get_default (); + gdk_display_get_pointer (display, &screen, &x, &y, NULL); + monitor = gdk_screen_get_monitor_at_point (screen, x, y); + + gdk_flush (); + grabbed = FALSE; + if (gs_window_get_screen (window) == screen + && gs_window_get_monitor (window) == monitor) + { + gs_debug ("Moving grab to %p", window); + gs_grab_move_to_window (manager->priv->grab, + gs_window_get_gdk_window (window), + gs_window_get_screen (window), + FALSE); + grabbed = TRUE; + } + + return grabbed; +} + +static void +window_grab_broken_cb (GSWindow *window, + GdkEventGrabBroken *event, + GSManager *manager) +{ + gs_debug ("GRAB BROKEN!"); + if (event->keyboard) + { + gs_grab_keyboard_reset (manager->priv->grab); + } + else + { + gs_grab_mouse_reset (manager->priv->grab); + } +} + +static gboolean +unfade_idle (GSManager *manager) +{ + gs_debug ("resetting fade"); + gs_fade_reset (manager->priv->fade); + manager->priv->unfade_idle_id = 0; + return FALSE; +} + + +static void +add_unfade_idle (GSManager *manager) +{ + remove_unfade_idle (manager); + manager->priv->unfade_idle_id = g_timeout_add (500, (GSourceFunc)unfade_idle, manager); +} + +static gboolean +window_map_event_cb (GSWindow *window, + GdkEvent *event, + GSManager *manager) +{ + gs_debug ("Handling window map_event event"); + + manager_maybe_grab_window (manager, window); + + manager_maybe_start_job_for_window (manager, window); + + return FALSE; +} + +static void +window_map_cb (GSWindow *window, + GSManager *manager) +{ + gs_debug ("Handling window map event"); +} + +static void +window_unmap_cb (GSWindow *window, + GSManager *manager) +{ + gs_debug ("window unmapped!"); +} + +static void +apply_background_to_window (GSManager *manager, + GSWindow *window) +{ + GdkPixmap *pixmap; + GdkScreen *screen; + int width; + int height; + + if (manager->priv->bg == NULL) + { + gs_debug ("No background available"); + gs_window_set_background_pixmap (window, NULL); + } + + screen = gs_window_get_screen (window); + width = gdk_screen_get_width (screen); + height = gdk_screen_get_height (screen); + gs_debug ("Creating pixmap background w:%d h:%d", width, height); + pixmap = mate_bg_create_pixmap (manager->priv->bg, + gs_window_get_gdk_window (window), + width, + height, + FALSE); + gs_window_set_background_pixmap (window, pixmap); + g_object_unref (pixmap); +} + +static void +manager_show_window (GSManager *manager, + GSWindow *window) +{ + GSJob *job; + + apply_background_to_window (manager, window); + + job = gs_job_new_for_widget (gs_window_get_drawing_area (window)); + + manager_select_theme_for_job (manager, job); + manager_add_job_for_window (manager, window, job); + + manager->priv->activate_time = time (NULL); + + if (manager->priv->lock_timeout >= 0) + { + remove_lock_timer (manager); + add_lock_timer (manager, manager->priv->lock_timeout); + } + + if (manager->priv->cycle_timeout >= 10000) + { + remove_cycle_timer (manager); + add_cycle_timer (manager, manager->priv->cycle_timeout); + } + + add_unfade_idle (manager); + + /* FIXME: only emit signal once */ + g_signal_emit (manager, signals [ACTIVATED], 0); +} + +static void +window_show_cb (GSWindow *window, + GSManager *manager) +{ + + g_return_if_fail (manager != NULL); + g_return_if_fail (GS_IS_MANAGER (manager)); + g_return_if_fail (window != NULL); + g_return_if_fail (GS_IS_WINDOW (window)); + + gs_debug ("Handling window show"); + manager_show_window (manager, window); +} + +static void +maybe_set_window_throttle (GSManager *manager, + GSWindow *window, + gboolean throttled) +{ + if (throttled) + { + manager_maybe_stop_job_for_window (manager, window); + } + else + { + manager_maybe_start_job_for_window (manager, window); + } +} + +static void +window_obscured_cb (GSWindow *window, + GParamSpec *pspec, + GSManager *manager) +{ + gboolean obscured; + + obscured = gs_window_is_obscured (window); + gs_debug ("Handling window obscured: %s", obscured ? "obscured" : "unobscured"); + + maybe_set_window_throttle (manager, window, obscured); + + if (! obscured) + { + gs_manager_request_unlock (manager); + } +} + +static void +handle_window_dialog_up (GSManager *manager, + GSWindow *window) +{ + GSList *l; + + g_return_if_fail (manager != NULL); + g_return_if_fail (GS_IS_MANAGER (manager)); + + gs_debug ("Handling dialog up"); + + g_signal_emit (manager, signals [AUTH_REQUEST_BEGIN], 0); + + manager->priv->dialog_up = TRUE; + /* Make all other windows insensitive so we don't get events */ + for (l = manager->priv->windows; l; l = l->next) + { + if (l->data != window) + { + gtk_widget_set_sensitive (GTK_WIDGET (l->data), FALSE); + } + } + + /* Move keyboard and mouse grabs so dialog can be used */ + gs_grab_move_to_window (manager->priv->grab, + gs_window_get_gdk_window (window), + gs_window_get_screen (window), + FALSE); + + /* Release the pointer grab while dialog is up so that + the dialog can be used. We'll regrab it when the dialog goes down. */ + gs_grab_release_mouse (manager->priv->grab); + + if (! manager->priv->throttled) + { + gs_debug ("Suspending jobs"); + + manager_suspend_jobs (manager); + } +} + +static void +handle_window_dialog_down (GSManager *manager, + GSWindow *window) +{ + GSList *l; + + g_return_if_fail (manager != NULL); + g_return_if_fail (GS_IS_MANAGER (manager)); + + gs_debug ("Handling dialog down"); + + /* Regrab the mouse */ + gs_grab_move_to_window (manager->priv->grab, + gs_window_get_gdk_window (window), + gs_window_get_screen (window), + FALSE); + + /* Make all windows sensitive so we get events */ + for (l = manager->priv->windows; l; l = l->next) + { + gtk_widget_set_sensitive (GTK_WIDGET (l->data), TRUE); + } + + manager->priv->dialog_up = FALSE; + + if (! manager->priv->throttled) + { + manager_resume_jobs (manager); + } + + g_signal_emit (manager, signals [AUTH_REQUEST_END], 0); +} + +static void +window_dialog_up_changed_cb (GSWindow *window, + GParamSpec *pspec, + GSManager *manager) +{ + gboolean up; + + up = gs_window_is_dialog_up (window); + gs_debug ("Handling window dialog up changed: %s", up ? "up" : "down"); + if (up) + { + handle_window_dialog_up (manager, window); + } + else + { + handle_window_dialog_down (manager, window); + } +} + +static gboolean +window_activity_cb (GSWindow *window, + GSManager *manager) +{ + gboolean handled; + + handled = gs_manager_request_unlock (manager); + + return handled; +} + +static void +disconnect_window_signals (GSManager *manager, + GSWindow *window) +{ + g_signal_handlers_disconnect_by_func (window, window_deactivated_cb, manager); + g_signal_handlers_disconnect_by_func (window, window_activity_cb, manager); + g_signal_handlers_disconnect_by_func (window, window_show_cb, manager); + g_signal_handlers_disconnect_by_func (window, window_map_cb, manager); + g_signal_handlers_disconnect_by_func (window, window_map_event_cb, manager); + g_signal_handlers_disconnect_by_func (window, window_obscured_cb, manager); + g_signal_handlers_disconnect_by_func (window, window_dialog_up_changed_cb, manager); + g_signal_handlers_disconnect_by_func (window, window_unmap_cb, manager); + g_signal_handlers_disconnect_by_func (window, window_grab_broken_cb, manager); +} + +static void +window_destroyed_cb (GtkWindow *window, + GSManager *manager) +{ + disconnect_window_signals (manager, GS_WINDOW (window)); +} + +static void +connect_window_signals (GSManager *manager, + GSWindow *window) +{ + g_signal_connect_object (window, "destroy", + G_CALLBACK (window_destroyed_cb), manager, 0); + g_signal_connect_object (window, "activity", + G_CALLBACK (window_activity_cb), manager, 0); + g_signal_connect_object (window, "deactivated", + G_CALLBACK (window_deactivated_cb), manager, 0); + g_signal_connect_object (window, "show", + G_CALLBACK (window_show_cb), manager, G_CONNECT_AFTER); + g_signal_connect_object (window, "map", + G_CALLBACK (window_map_cb), manager, G_CONNECT_AFTER); + g_signal_connect_object (window, "map_event", + G_CALLBACK (window_map_event_cb), manager, G_CONNECT_AFTER); + g_signal_connect_object (window, "notify::obscured", + G_CALLBACK (window_obscured_cb), manager, G_CONNECT_AFTER); + g_signal_connect_object (window, "notify::dialog-up", + G_CALLBACK (window_dialog_up_changed_cb), manager, 0); + g_signal_connect_object (window, "unmap", + G_CALLBACK (window_unmap_cb), manager, G_CONNECT_AFTER); + g_signal_connect_object (window, "grab_broken_event", + G_CALLBACK (window_grab_broken_cb), manager, G_CONNECT_AFTER); +} + +static void +gs_manager_create_window_for_monitor (GSManager *manager, + GdkScreen *screen, + int monitor) +{ + GSWindow *window; + GdkRectangle rect; + + gdk_screen_get_monitor_geometry (screen, monitor, &rect); + + gs_debug ("Creating window for monitor %d [%d,%d] (%dx%d)", + monitor, rect.x, rect.y, rect.width, rect.height); + + window = gs_window_new (screen, monitor, manager->priv->lock_active); + + gs_window_set_user_switch_enabled (window, manager->priv->user_switch_enabled); + gs_window_set_logout_enabled (window, manager->priv->logout_enabled); + gs_window_set_logout_timeout (window, manager->priv->logout_timeout); + gs_window_set_logout_command (window, manager->priv->logout_command); + gs_window_set_keyboard_enabled (window, manager->priv->keyboard_enabled); + gs_window_set_keyboard_command (window, manager->priv->keyboard_command); + gs_window_set_status_message (window, manager->priv->status_message); + + connect_window_signals (manager, window); + + manager->priv->windows = g_slist_append (manager->priv->windows, window); + + if (manager->priv->active && !manager->priv->fading) + { + gtk_widget_show (GTK_WIDGET (window)); + } +} + +static void +on_screen_monitors_changed (GdkScreen *screen, + GSManager *manager) +{ + GSList *l; + int n_monitors; + int n_windows; + int i; + + n_monitors = gdk_screen_get_n_monitors (screen); + n_windows = g_slist_length (manager->priv->windows); + + gs_debug ("Monitors changed for screen %d: num=%d", + gdk_screen_get_number (screen), + n_monitors); + + if (n_monitors > n_windows) + { + + /* Tear down unlock dialog in case we want to move it + * to a new monitor + */ + l = manager->priv->windows; + while (l != NULL) + { + gs_window_cancel_unlock_request (GS_WINDOW (l->data)); + l = l->next; + } + + /* add more windows */ + for (i = n_windows; i < n_monitors; i++) + { + gs_manager_create_window_for_monitor (manager, screen, i); + } + + /* And put unlock dialog up where ever it's supposed to be + */ + gs_manager_request_unlock (manager); + } + else + { + + gdk_x11_grab_server (); + + /* remove the extra windows */ + l = manager->priv->windows; + while (l != NULL) + { + GdkScreen *this_screen; + int this_monitor; + GSList *next = l->next; + + this_screen = gs_window_get_screen (GS_WINDOW (l->data)); + this_monitor = gs_window_get_monitor (GS_WINDOW (l->data)); + if (this_screen == screen && this_monitor >= n_monitors) + { + manager_maybe_stop_job_for_window (manager, GS_WINDOW (l->data)); + g_hash_table_remove (manager->priv->jobs, l->data); + gs_window_destroy (GS_WINDOW (l->data)); + manager->priv->windows = g_slist_delete_link (manager->priv->windows, l); + } + l = next; + } + + /* make sure there is a lock dialog on a connected monitor, + * and that the keyboard is still properly grabbed after all + * the windows above got destroyed*/ + if (n_windows > n_monitors) + { + gs_manager_request_unlock (manager); + } + + gdk_flush (); + gdk_x11_ungrab_server (); + } +} + +static void +gs_manager_destroy_windows (GSManager *manager) +{ + GdkDisplay *display; + GSList *l; + int n_screens; + int i; + + g_return_if_fail (manager != NULL); + g_return_if_fail (GS_IS_MANAGER (manager)); + + if (manager->priv->windows == NULL) + { + return; + } + + display = gdk_display_get_default (); + + n_screens = gdk_display_get_n_screens (display); + + for (i = 0; i < n_screens; i++) + { + g_signal_handlers_disconnect_by_func (gdk_display_get_screen (display, i), + on_screen_monitors_changed, + manager); + } + + for (l = manager->priv->windows; l; l = l->next) + { + gs_window_destroy (l->data); + } + g_slist_free (manager->priv->windows); + manager->priv->windows = NULL; +} + +static void +gs_manager_finalize (GObject *object) +{ + GSManager *manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (GS_IS_MANAGER (object)); + + manager = GS_MANAGER (object); + + g_return_if_fail (manager->priv != NULL); + + if (manager->priv->bg_notify_id != 0) + { + mateconf_client_remove_dir (manager->priv->client, + MATE_BG_KEY_DIR, + NULL); + mateconf_client_notify_remove (manager->priv->client, + manager->priv->bg_notify_id); + manager->priv->bg_notify_id = 0; + } + if (manager->priv->bg != NULL) + { + g_object_unref (manager->priv->bg); + } + if (manager->priv->client != NULL) + { + g_object_unref (manager->priv->client); + } + + free_themes (manager); + g_free (manager->priv->logout_command); + g_free (manager->priv->keyboard_command); + g_free (manager->priv->status_message); + + remove_unfade_idle (manager); + remove_timers (manager); + + gs_grab_release (manager->priv->grab); + + manager_stop_jobs (manager); + + gs_manager_destroy_windows (manager); + + manager->priv->active = FALSE; + manager->priv->activate_time = 0; + manager->priv->lock_enabled = FALSE; + + g_object_unref (manager->priv->fade); + g_object_unref (manager->priv->grab); + g_object_unref (manager->priv->theme_manager); + + G_OBJECT_CLASS (gs_manager_parent_class)->finalize (object); +} + +static void +gs_manager_create_windows_for_screen (GSManager *manager, + GdkScreen *screen) +{ + int n_monitors; + int i; + + g_return_if_fail (manager != NULL); + g_return_if_fail (GS_IS_MANAGER (manager)); + g_return_if_fail (GDK_IS_SCREEN (screen)); + + g_object_ref (manager); + g_object_ref (screen); + + n_monitors = gdk_screen_get_n_monitors (screen); + + gs_debug ("Creating %d windows for screen %d", n_monitors, gdk_screen_get_number (screen)); + + for (i = 0; i < n_monitors; i++) + { + gs_manager_create_window_for_monitor (manager, screen, i); + } + + g_object_unref (screen); + g_object_unref (manager); +} + +static void +gs_manager_create_windows (GSManager *manager) +{ + GdkDisplay *display; + int n_screens; + int i; + + g_return_if_fail (manager != NULL); + g_return_if_fail (GS_IS_MANAGER (manager)); + + g_assert (manager->priv->windows == NULL); + + display = gdk_display_get_default (); + n_screens = gdk_display_get_n_screens (display); + + for (i = 0; i < n_screens; i++) + { + g_signal_connect (gdk_display_get_screen (display, i), + "monitors-changed", + G_CALLBACK (on_screen_monitors_changed), + manager); + + gs_manager_create_windows_for_screen (manager, gdk_display_get_screen (display, i)); + } +} + +GSManager * +gs_manager_new (void) +{ + GObject *manager; + + manager = g_object_new (GS_TYPE_MANAGER, NULL); + + return GS_MANAGER (manager); +} + +static void +show_windows (GSList *windows) +{ + GSList *l; + + for (l = windows; l; l = l->next) + { + gtk_widget_show (GTK_WIDGET (l->data)); + } +} + +static void +remove_job (GSJob *job) +{ + if (job == NULL) + { + return; + } + + gs_job_stop (job); + g_object_unref (job); +} + +static void +fade_done_cb (GSFade *fade, + GSManager *manager) +{ + gs_debug ("fade completed, showing windows"); + show_windows (manager->priv->windows); + manager->priv->fading = FALSE; +} + +static gboolean +gs_manager_activate (GSManager *manager) +{ + gboolean do_fade; + gboolean res; + + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE); + + if (manager->priv->active) + { + gs_debug ("Trying to activate manager when already active"); + return FALSE; + } + + res = gs_grab_grab_root (manager->priv->grab, FALSE); + if (! res) + { + return FALSE; + } + + if (manager->priv->windows == NULL) + { + gs_manager_create_windows (GS_MANAGER (manager)); + } + + manager->priv->jobs = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify)remove_job); + + manager->priv->active = TRUE; + + /* fade to black and show windows */ + do_fade = TRUE; + if (do_fade) + { + manager->priv->fading = TRUE; + gs_debug ("fading out"); + gs_fade_async (manager->priv->fade, + FADE_TIMEOUT, + (GSFadeDoneFunc)fade_done_cb, + manager); + + while (manager->priv->fading) + { + gtk_main_iteration (); + } + } + else + { + show_windows (manager->priv->windows); + } + + return TRUE; +} + +static gboolean +gs_manager_deactivate (GSManager *manager) +{ + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE); + + if (! manager->priv->active) + { + gs_debug ("Trying to deactivate a screensaver that is not active"); + return FALSE; + } + + remove_unfade_idle (manager); + gs_fade_reset (manager->priv->fade); + remove_timers (manager); + + gs_grab_release (manager->priv->grab); + + manager_stop_jobs (manager); + + gs_manager_destroy_windows (manager); + + /* reset state */ + manager->priv->active = FALSE; + manager->priv->activate_time = 0; + manager->priv->lock_active = FALSE; + manager->priv->dialog_up = FALSE; + manager->priv->fading = FALSE; + + return TRUE; +} + +gboolean +gs_manager_set_active (GSManager *manager, + gboolean active) +{ + gboolean res; + + if (active) + { + res = gs_manager_activate (manager); + } + else + { + res = gs_manager_deactivate (manager); + } + + return res; +} + +gboolean +gs_manager_get_active (GSManager *manager) +{ + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE); + + return manager->priv->active; +} + +gboolean +gs_manager_request_unlock (GSManager *manager) +{ + GSWindow *window; + + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE); + + if (! manager->priv->active) + { + gs_debug ("Request unlock but manager is not active"); + return FALSE; + } + + if (manager->priv->dialog_up) + { + gs_debug ("Request unlock but dialog is already up"); + return FALSE; + } + + if (manager->priv->fading) + { + gs_debug ("Request unlock so finishing fade"); + gs_fade_finish (manager->priv->fade); + } + + if (manager->priv->windows == NULL) + { + gs_debug ("We don't have any windows!"); + return FALSE; + } + + /* Find the GSWindow that contains the pointer */ + window = find_window_at_pointer (manager); + gs_window_request_unlock (window); + + return TRUE; +} + +void +gs_manager_cancel_unlock_request (GSManager *manager) +{ + GSList *l; + for (l = manager->priv->windows; l; l = l->next) + { + gs_window_cancel_unlock_request (l->data); + } +} |