From fc01e31200b852d54aa557b1a5ce1bdfc31a64ae Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 7 Nov 2009 00:42:06 +0000 Subject: [PATCH] Add ability to perform actions after a period of idleness --- ...erform-actions-after-a-period-of-idl.patch | 618 ++++++++++++++++++ gnome-session.spec | 12 +- 2 files changed, 629 insertions(+), 1 deletion(-) create mode 100644 0001-Add-ability-to-perform-actions-after-a-period-of-idl.patch diff --git a/0001-Add-ability-to-perform-actions-after-a-period-of-idl.patch b/0001-Add-ability-to-perform-actions-after-a-period-of-idl.patch new file mode 100644 index 0000000..a39b04c --- /dev/null +++ b/0001-Add-ability-to-perform-actions-after-a-period-of-idl.patch @@ -0,0 +1,618 @@ +From 62efd61f95a5985f2dff7e109944e23354b00547 Mon Sep 17 00:00:00 2001 +From: William Jon McCann +Date: Thu, 1 Oct 2009 02:18:56 -0400 +Subject: [PATCH] Add ability to perform actions after a period of idleness + +Fixes https://bugzilla.gnome.org/show_bug.cgi?id=597030 +--- + configure.in | 2 + + data/gnome-session.schemas.in.in | 30 +++ + gnome-session/gsm-manager.c | 394 ++++++++++++++++++++++++++++++++++++-- + gnome-session/gsm-presence.c | 12 ++ + gnome-session/main.c | 3 + + 5 files changed, 428 insertions(+), 13 deletions(-) + +diff --git a/configure.in b/configure.in +index 79466c9..4c5e133 100644 +--- a/configure.in ++++ b/configure.in +@@ -42,6 +42,7 @@ LIBGNOMEUI_REQUIRED=2.2.0 + GTK_REQUIRED=2.14.0 + DBUS_GLIB_REQUIRED=0.76 + DEVKIT_POWER_REQUIRED=008 ++LIBNOTIFY_REQUIRED=0.4.3 + + dnl ==================================================================== + dnl Dependency Checks +@@ -54,6 +55,7 @@ PKG_CHECK_MODULES(GNOME_SESSION, + gtk+-2.0 >= $GTK_REQUIRED + dbus-glib-1 >= $DBUS_GLIB_REQUIRED + devkit-power-gobject >= $DEVKIT_POWER_REQUIRED ++ libnotify >= $LIBNOTIFY_REQUIRED + ) + + PKG_CHECK_MODULES(SESSION_PROPERTIES, +diff --git a/data/gnome-session.schemas.in.in b/data/gnome-session.schemas.in.in +index 2cd5e2d..f87e6c3 100644 +--- a/data/gnome-session.schemas.in.in ++++ b/data/gnome-session.schemas.in.in +@@ -61,6 +61,36 @@ + + + ++ /schemas/desktop/gnome/session/max_idle_action ++ /desktop/gnome/session/max_idle_action ++ gnome ++ string ++ ++ ++ The action to take after the maximum idle time ++ ++ The name of the action to take when the maximum allowed ++ idle time has been reached. The Delay is specified in ++ the "max_idle_time" key. Allowed values are: logout, ++ forced-logout. An empty string disables the action. ++ ++ ++ ++ ++ /schemas/desktop/gnome/session/max_idle_time ++ /desktop/gnome/session/max_idle_time ++ gnome ++ int ++ 120 ++ ++ Amount of time a user can remain idle ++ ++ The number of minutes a user can remain idle before the ++ max_idle_action is automatically taken. ++ ++ ++ ++ + /schemas/desktop/gnome/session/default_session + /desktop/gnome/session/default_session + gnome +diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c +index 49fefe6..417f353 100644 +--- a/gnome-session/gsm-manager.c ++++ b/gnome-session/gsm-manager.c +@@ -39,6 +39,7 @@ + #include + + #include ++#include + + #include /* for logout dialog */ + #include +@@ -67,6 +68,10 @@ + #define GSM_MANAGER_DBUS_PATH "/org/gnome/SessionManager" + #define GSM_MANAGER_DBUS_NAME "org.gnome.SessionManager" + ++#define GS_NAME "org.gnome.ScreenSaver" ++#define GS_PATH "/org/gnome/ScreenSaver" ++#define GS_INTERFACE "org.gnome.ScreenSaver" ++ + #define GSM_MANAGER_PHASE_TIMEOUT 10 /* seconds */ + + #define GDM_FLEXISERVER_COMMAND "gdmflexiserver" +@@ -79,6 +84,8 @@ + + #define KEY_DESKTOP_DIR "/desktop/gnome/session" + #define KEY_IDLE_DELAY KEY_DESKTOP_DIR "/idle_delay" ++#define KEY_MAX_IDLE_TIME KEY_DESKTOP_DIR "/max_idle_time" ++#define KEY_MAX_IDLE_ACTION KEY_DESKTOP_DIR "/max_idle_action" + + #define KEY_GNOME_SESSION_DIR "/apps/gnome-session/options" + #define KEY_AUTOSAVE KEY_GNOME_SESSION_DIR "/auto_save_session" +@@ -114,6 +121,11 @@ struct GsmManagerPrivate + gboolean forceful_logout; + GSList *query_clients; + guint query_timeout_id; ++ guint max_idle_timeout_id; ++ guint max_idle_warning_timeout_id; ++ guint max_idle_time_secs; ++ int max_idle_action; ++ NotifyNotification *max_idle_notification; + /* This is used for GSM_MANAGER_PHASE_END_SESSION only at the moment, + * since it uses a sublist of all running client that replied in a + * specific way */ +@@ -157,6 +169,19 @@ enum { + + static guint signals [LAST_SIGNAL] = { 0 }; + ++typedef enum { ++ ACTION_NONE = 0, ++ ACTION_LOGOUT, ++ ACTION_FORCED_LOGOUT, ++} Action; ++ ++static GConfEnumStringPair actions_enum_map [] = { ++ { ACTION_NONE, "" }, ++ { ACTION_LOGOUT, "logout" }, ++ { ACTION_FORCED_LOGOUT, "forced-logout" }, ++ { 0, NULL } ++}; ++ + static void gsm_manager_class_init (GsmManagerClass *klass); + static void gsm_manager_init (GsmManager *manager); + static void gsm_manager_finalize (GObject *object); +@@ -2147,6 +2172,19 @@ gsm_manager_dispose (GObject *object) + + g_debug ("GsmManager: disposing manager"); + ++ if (manager->priv->max_idle_notification != NULL) { ++ g_object_unref (manager->priv->max_idle_notification); ++ manager->priv->max_idle_notification = NULL; ++ } ++ if (manager->priv->max_idle_warning_timeout_id > 0) { ++ g_source_remove (manager->priv->max_idle_warning_timeout_id); ++ manager->priv->max_idle_warning_timeout_id = 0; ++ } ++ if (manager->priv->max_idle_timeout_id > 0) { ++ g_source_remove (manager->priv->max_idle_timeout_id); ++ manager->priv->max_idle_timeout_id = 0; ++ } ++ + if (manager->priv->clients != NULL) { + g_signal_handlers_disconnect_by_func (manager->priv->clients, + on_store_client_added, +@@ -2343,6 +2381,321 @@ load_idle_delay_from_gconf (GsmManager *manager) + } + + static void ++load_max_idle_from_gconf (GsmManager *manager) ++{ ++ GError *error; ++ glong value; ++ const char *str; ++ ++ error = NULL; ++ value = gconf_client_get_int (manager->priv->gconf_client, ++ KEY_MAX_IDLE_TIME, ++ &error); ++ if (error == NULL) { ++ manager->priv->max_idle_time_secs = 60 * value; ++ } else { ++ g_warning ("Error retrieving configuration key '%s': %s", ++ KEY_MAX_IDLE_TIME, ++ error->message); ++ g_error_free (error); ++ } ++ ++ error = NULL; ++ str = gconf_client_get_string (manager->priv->gconf_client, ++ KEY_MAX_IDLE_ACTION, ++ &error); ++ if (error == NULL) { ++ int action; ++ ++ if (gconf_string_to_enum (actions_enum_map, str, &action)) { ++ manager->priv->max_idle_action = action; ++ } else { ++ manager->priv->max_idle_action = ACTION_NONE; ++ } ++ } else { ++ g_warning ("Error retrieving configuration key '%s': %s", ++ KEY_MAX_IDLE_TIME, ++ error->message); ++ g_error_free (error); ++ } ++} ++ ++static void ++request_logout (GsmManager *manager, ++ gboolean forceful_logout) ++{ ++ g_debug ("GsmManager: requesting logout"); ++ ++ manager->priv->forceful_logout = forceful_logout; ++ manager->priv->logout_type = GSM_MANAGER_LOGOUT_LOGOUT; ++ ++ end_phase (manager); ++} ++ ++static gboolean ++_on_max_idle_time_timeout (GsmManager *manager) ++{ ++ manager->priv->max_idle_timeout_id = 0; ++ g_debug ("GsmManager: max idle time reached"); ++ ++ if (manager->priv->max_idle_warning_timeout_id > 0) { ++ g_source_remove (manager->priv->max_idle_warning_timeout_id); ++ manager->priv->max_idle_warning_timeout_id = 0; ++ } ++ ++ switch (manager->priv->max_idle_action) { ++ case ACTION_LOGOUT: ++ g_debug ("GsmManager: max idle action: logout"); ++ /* begin non-forceful logout */ ++ request_logout (manager, FALSE); ++ break; ++ case ACTION_FORCED_LOGOUT: ++ g_debug ("GsmManager: max idle action: force-logout"); ++ /* begin forceful logout */ ++ request_logout (manager, TRUE); ++ break; ++ case ACTION_NONE: ++ g_debug ("GsmManager: no max idle action specified"); ++ break; ++ default: ++ g_assert_not_reached (); ++ break; ++ } ++ ++ return FALSE; ++} ++ ++static void ++on_max_idle_notification_closed (NotifyNotification *notification, ++ GsmManager *manager) ++{ ++ if (manager->priv->max_idle_notification != NULL) { ++ g_object_unref (manager->priv->max_idle_notification); ++ manager->priv->max_idle_notification = NULL; ++ } ++} ++ ++/* Adapted from totem_time_to_string_text */ ++static char * ++time_to_string_text (long time) ++{ ++ char *secs, *mins, *hours, *string; ++ int sec, min, hour; ++ ++ sec = time % 60; ++ time = time - sec; ++ min = (time % (60 * 60)) / 60; ++ time = time - (min * 60); ++ hour = time / (60 * 60); ++ ++ hours = g_strdup_printf (ngettext ("%d hour", "%d hours", hour), hour); ++ ++ mins = g_strdup_printf (ngettext ("%d minute", ++ "%d minutes", min), min); ++ ++ secs = g_strdup_printf (ngettext ("%d second", ++ "%d seconds", sec), sec); ++ ++ if (hour > 0) { ++ /* hour:minutes:seconds */ ++ string = g_strdup_printf (_("%s %s %s"), hours, mins, secs); ++ } else if (min > 0) { ++ /* minutes:seconds */ ++ string = g_strdup_printf (_("%s %s"), mins, secs); ++ } else if (sec > 0) { ++ /* seconds */ ++ string = g_strdup_printf (_("%s"), secs); ++ } else { ++ /* 0 seconds */ ++ string = g_strdup (_("0 seconds")); ++ } ++ ++ g_free (hours); ++ g_free (mins); ++ g_free (secs); ++ ++ return string; ++} ++ ++static void ++update_max_idle_notification (GsmManager *manager, ++ long secs_remaining) ++{ ++ char *summary; ++ char *body; ++ char *remaining; ++ gboolean screensaver_active; ++ ++ g_object_get (manager->priv->presence, "screensaver-active", &screensaver_active, NULL); ++ ++ remaining = time_to_string_text (secs_remaining); ++ summary = NULL; ++ body = NULL; ++ ++ switch (manager->priv->max_idle_action) { ++ case ACTION_LOGOUT: ++ case ACTION_FORCED_LOGOUT: ++ summary = g_strdup_printf (_("Automatic logout in %s"), remaining); ++ body = g_strdup (_("This session is configured to automatically log out after a period of inactivity.")); ++ break; ++ default: ++ g_assert_not_reached (); ++ break; ++ } ++ ++ g_free (remaining); ++ ++ if (screensaver_active) { ++ DBusGProxy *proxy; ++ GError *error; ++ ++ proxy = dbus_g_proxy_new_for_name (manager->priv->connection, ++ GS_NAME, ++ GS_PATH, ++ GS_INTERFACE); ++ error = NULL; ++ if (! dbus_g_proxy_call (proxy, "ShowMessage", &error, ++ G_TYPE_STRING, summary, ++ G_TYPE_STRING, body, ++ G_TYPE_STRING, "", ++ G_TYPE_INVALID, ++ G_TYPE_INVALID)) { ++ g_debug ("ShowMessage failed: %s", error->message); ++ g_error_free (error); ++ } ++ ++ g_object_unref (proxy); ++ } else { ++ if (manager->priv->max_idle_notification != NULL) { ++ notify_notification_update (manager->priv->max_idle_notification, ++ summary, ++ body, ++ NULL); ++ } else { ++ manager->priv->max_idle_notification ++ = notify_notification_new (summary, body, NULL, NULL); ++ notify_notification_set_timeout (manager->priv->max_idle_notification, ++ NOTIFY_EXPIRES_NEVER); ++ ++ g_signal_connect (manager->priv->max_idle_notification, ++ "closed", ++ G_CALLBACK (on_max_idle_notification_closed), ++ manager); ++ } ++ ++ notify_notification_show (manager->priv->max_idle_notification, NULL); ++ } ++ ++ g_free (body); ++ g_free (summary); ++} ++ ++static gboolean ++_on_max_idle_warning_2_timeout (GsmManager *manager) ++{ ++ manager->priv->max_idle_warning_timeout_id = 0; ++ ++ g_debug ("Note: will perform idle action in %f seconds", ++ 0.02 * manager->priv->max_idle_time_secs); ++ update_max_idle_notification (manager, 0.02 * manager->priv->max_idle_time_secs); ++ ++ return FALSE; ++} ++ ++static gboolean ++_on_max_idle_warning_5_timeout (GsmManager *manager) ++{ ++ long warn_secs; ++ ++ warn_secs = 0.03 * manager->priv->max_idle_time_secs; ++ manager->priv->max_idle_warning_timeout_id ++ = g_timeout_add_seconds (warn_secs, ++ (GSourceFunc)_on_max_idle_warning_2_timeout, ++ manager); ++ g_debug ("Note: will perform idle action in %f seconds", ++ 0.05 * manager->priv->max_idle_time_secs); ++ update_max_idle_notification (manager, 0.05 * manager->priv->max_idle_time_secs); ++ ++ return FALSE; ++} ++ ++static gboolean ++_on_max_idle_warning_10_timeout (GsmManager *manager) ++{ ++ long warn_secs; ++ ++ warn_secs = 0.05 * manager->priv->max_idle_time_secs; ++ manager->priv->max_idle_warning_timeout_id ++ = g_timeout_add_seconds (warn_secs, ++ (GSourceFunc)_on_max_idle_warning_5_timeout, ++ manager); ++ g_debug ("Note: will perform idle action in %f seconds", ++ 0.1 * manager->priv->max_idle_time_secs); ++ update_max_idle_notification (manager, 0.1 * manager->priv->max_idle_time_secs); ++ ++ return FALSE; ++} ++ ++static gboolean ++_on_max_idle_warning_20_timeout (GsmManager *manager) ++{ ++ long warn_secs; ++ ++ warn_secs = 0.1 * manager->priv->max_idle_time_secs; ++ manager->priv->max_idle_warning_timeout_id ++ = g_timeout_add_seconds (warn_secs, ++ (GSourceFunc)_on_max_idle_warning_10_timeout, ++ manager); ++ g_debug ("Note: will perform idle action in %f seconds", ++ 0.2 * manager->priv->max_idle_time_secs); ++ ++ update_max_idle_notification (manager, 0.2 * manager->priv->max_idle_time_secs); ++ ++ return FALSE; ++} ++ ++static void ++reset_max_idle_timer (GsmManager *manager) ++{ ++ int status; ++ ++ g_object_get (manager->priv->presence, "status", &status, NULL); ++ ++ ++ g_debug ("Resetting max idle timer status=%d action=%d time=%ds", ++ status, manager->priv->max_idle_action, manager->priv->max_idle_time_secs); ++ if (manager->priv->max_idle_timeout_id > 0) { ++ g_source_remove (manager->priv->max_idle_timeout_id); ++ manager->priv->max_idle_timeout_id = 0; ++ } ++ if (manager->priv->max_idle_warning_timeout_id > 0) { ++ g_source_remove (manager->priv->max_idle_warning_timeout_id); ++ manager->priv->max_idle_warning_timeout_id = 0; ++ } ++ ++ if (status == GSM_PRESENCE_STATUS_IDLE ++ && manager->priv->max_idle_action != ACTION_NONE) { ++ long warn_secs; ++ ++ /* start counting now. probably isn't quite ++ right if we're handling a configuration ++ value change but it may not matter */ ++ ++ manager->priv->max_idle_timeout_id ++ = g_timeout_add_seconds (manager->priv->max_idle_time_secs, ++ (GSourceFunc)_on_max_idle_time_timeout, ++ manager); ++ ++ /* start warning at 80% of the way through the idle */ ++ warn_secs = 0.8 * manager->priv->max_idle_time_secs; ++ manager->priv->max_idle_warning_timeout_id ++ = g_timeout_add_seconds (warn_secs, ++ (GSourceFunc)_on_max_idle_warning_20_timeout, ++ manager); ++ } ++} ++ ++static void + on_gconf_key_changed (GConfClient *client, + guint cnxn_id, + GConfEntry *entry, +@@ -2370,6 +2723,32 @@ on_gconf_key_changed (GConfClient *client, + } else { + invalid_type_warning (key); + } ++ } else if (strcmp (key, KEY_MAX_IDLE_TIME) == 0) { ++ if (value->type == GCONF_VALUE_INT) { ++ int t; ++ ++ t = gconf_value_get_int (value); ++ ++ manager->priv->max_idle_time_secs = t * 60; ++ reset_max_idle_timer (manager); ++ } else { ++ invalid_type_warning (key); ++ } ++ } else if (strcmp (key, KEY_MAX_IDLE_ACTION) == 0) { ++ if (value->type == GCONF_VALUE_STRING) { ++ int action; ++ const char *str; ++ ++ str = gconf_value_get_string (value); ++ if (gconf_string_to_enum (actions_enum_map, str, &action)) { ++ manager->priv->max_idle_action = action; ++ } else { ++ manager->priv->max_idle_action = ACTION_NONE; ++ } ++ reset_max_idle_timer (manager); ++ } else { ++ invalid_type_warning (key); ++ } + } else if (strcmp (key, KEY_LOCK_DISABLE) == 0) { + if (value->type == GCONF_VALUE_BOOL) { + gboolean disabled; +@@ -2407,6 +2786,7 @@ on_presence_status_changed (GsmPresence *presence, + consolekit = gsm_get_consolekit (); + gsm_consolekit_set_session_idle (consolekit, + (status == GSM_PRESENCE_STATUS_IDLE)); ++ reset_max_idle_timer (manager); + } + + static void +@@ -2457,6 +2837,7 @@ gsm_manager_init (GsmManager *manager) + NULL, NULL); + + load_idle_delay_from_gconf (manager); ++ load_max_idle_from_gconf (manager); + } + + static void +@@ -2763,19 +3144,6 @@ request_hibernate (GsmManager *manager) + gtk_widget_show (manager->priv->inhibit_dialog); + } + +- +-static void +-request_logout (GsmManager *manager, +- gboolean forceful_logout) +-{ +- g_debug ("GsmManager: requesting logout"); +- +- manager->priv->forceful_logout = forceful_logout; +- manager->priv->logout_type = GSM_MANAGER_LOGOUT_LOGOUT; +- +- end_phase (manager); +-} +- + static void + request_switch_user (GsmManager *manager) + { +diff --git a/gnome-session/gsm-presence.c b/gnome-session/gsm-presence.c +index c3918de..40f123f 100644 +--- a/gnome-session/gsm-presence.c ++++ b/gnome-session/gsm-presence.c +@@ -66,6 +66,7 @@ enum { + PROP_STATUS_TEXT, + PROP_IDLE_ENABLED, + PROP_IDLE_TIMEOUT, ++ PROP_SCREENSAVER_ACTIVE, + }; + + +@@ -187,6 +188,7 @@ on_screensaver_active_changed (DBusGProxy *proxy, + presence->priv->screensaver_active = is_active; + reset_idle_watch (presence); + set_session_idle (presence, is_active); ++ g_object_notify (G_OBJECT (presence), "screensaver-active"); + } + } + +@@ -439,6 +441,9 @@ gsm_presence_get_property (GObject *object, + case PROP_IDLE_TIMEOUT: + g_value_set_uint (value, self->priv->idle_timeout); + break; ++ case PROP_SCREENSAVER_ACTIVE: ++ g_value_set_boolean (value, self->priv->screensaver_active); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -532,6 +537,13 @@ gsm_presence_class_init (GsmPresenceClass *klass) + G_MAXINT, + 300000, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ g_object_class_install_property (object_class, ++ PROP_SCREENSAVER_ACTIVE, ++ g_param_spec_boolean ("screensaver-active", ++ NULL, ++ NULL, ++ FALSE, ++ G_PARAM_READABLE)); + + dbus_g_object_type_install_info (GSM_TYPE_PRESENCE, &dbus_glib_gsm_presence_object_info); + dbus_g_error_domain_register (GSM_PRESENCE_ERROR, NULL, GSM_PRESENCE_TYPE_ERROR); +diff --git a/gnome-session/main.c b/gnome-session/main.c +index 87d46ae..90e0dce 100644 +--- a/gnome-session/main.c ++++ b/gnome-session/main.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + + #include "gdm-signal-handler.h" + #include "gdm-log.h" +@@ -467,6 +468,8 @@ main (int argc, char **argv) + exit (1); + } + ++ notify_init ("GNOME session manager"); ++ + gdm_log_init (); + gdm_log_set_debug (debug); + +-- +1.6.5.rc2 + diff --git a/gnome-session.spec b/gnome-session.spec index ce5fb1f..10f2893 100644 --- a/gnome-session.spec +++ b/gnome-session.spec @@ -10,7 +10,7 @@ Summary: GNOME session manager Name: gnome-session Version: 2.28.0 -Release: 2%{?dist} +Release: 3%{?dist} URL: http://www.gnome.org Source0: http://download.gnome.org/sources/gnome-session/2.28/%{name}-%{version}.tar.bz2 Source2: gnome.desktop @@ -63,6 +63,9 @@ BuildRequires: xmlto BuildRequires: DeviceKit-power-devel BuildRequires: gnome-common +# for patch3 +BuildRequires: libnotify-devel + Requires(pre): GConf2 >= %{gconf2_version} Requires(post): GConf2 >= %{gconf2_version} Requires(preun): GConf2 >= %{gconf2_version} @@ -74,6 +77,9 @@ Requires(preun): GConf2 >= %{gconf2_version} # https://bugzilla.gnome.org/show_bug.cgi?id=598211 Patch2: xsmp-stop.patch +# https://bugzilla.gnome.org/show_bug.cgi?id=597030 +Patch3: 0001-Add-ability-to-perform-actions-after-a-period-of-idl.patch + %description gnome-session manages a GNOME desktop or GDM login session. It starts up the other core GNOME components and handles logout and saving the session. @@ -91,6 +97,7 @@ Desktop file to add GNOME to display manager session menu. #%patch0 -p1 -b .unresponsive-timeout #%patch1 -p1 -b .show-lock %patch2 -p1 -b .xsmp-stop +%patch3 -p1 -b .max-idle echo "ACLOCAL_AMFLAGS = -I m4" >> Makefile.am @@ -178,6 +185,9 @@ fi %changelog +* Fri Nov 6 2009 Matthias Clasen - 2.28.0-3 +- Add ability to perform actions after a period of idleness + * Fri Oct 23 2009 Matthias Clasen - 2.28.0-2 - Avoid a crash on certain xsmp error conditions