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