diff -up gnome-session-2.31.6/configure.in.max-idle gnome-session-2.31.6/configure.in --- gnome-session-2.31.6/configure.in.max-idle 2010-08-04 07:35:41.000000000 -0400 +++ gnome-session-2.31.6/configure.in 2010-08-06 20:18:07.157048001 -0400 @@ -50,6 +50,7 @@ AC_MSG_RESULT([$with_gtk]) GLIB_REQUIRED=2.16.0 DBUS_GLIB_REQUIRED=0.76 UPOWER_REQUIRED=0.9.0 +LIBNOTIFY_REQUIRED=0.4.3 case "$with_gtk" in 2.0) GTK_API_VERSION=2.0 @@ -71,6 +72,7 @@ PKG_CHECK_MODULES(GNOME_SESSION, gtk+-$GTK_API_VERSION >= $GTK_REQUIRED dbus-glib-1 >= $DBUS_GLIB_REQUIRED upower-glib >= $UPOWER_REQUIRED + libnotify >= $LIBNOTIFY_REQUIRED ) PKG_CHECK_MODULES(SESSION_PROPERTIES, diff -up gnome-session-2.31.6/data/gnome-session.schemas.in.in.max-idle gnome-session-2.31.6/data/gnome-session.schemas.in.in --- gnome-session-2.31.6/data/gnome-session.schemas.in.in.max-idle 2010-08-04 07:22:30.000000000 -0400 +++ gnome-session-2.31.6/data/gnome-session.schemas.in.in 2010-08-06 20:06:05.274048002 -0400 @@ -39,6 +39,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 -up gnome-session-2.31.6/gnome-session/gsm-manager.c.max-idle gnome-session-2.31.6/gnome-session/gsm-manager.c --- gnome-session-2.31.6/gnome-session/gsm-manager.c.max-idle 2010-07-15 08:53:08.000000000 -0400 +++ gnome-session-2.31.6/gnome-session/gsm-manager.c 2010-08-06 20:06:05.278048002 -0400 @@ -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); @@ -2141,6 +2166,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, @@ -2337,6 +2375,321 @@ load_idle_delay_from_gconf (GsmManager * } 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, @@ -2364,6 +2717,32 @@ on_gconf_key_changed (GConfClient *clien } 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; @@ -2401,6 +2780,7 @@ on_presence_status_changed (GsmPresence consolekit = gsm_get_consolekit (); gsm_consolekit_set_session_idle (consolekit, (status == GSM_PRESENCE_STATUS_IDLE)); + reset_max_idle_timer (manager); } static void @@ -2451,6 +2831,7 @@ gsm_manager_init (GsmManager *manager) NULL, NULL); load_idle_delay_from_gconf (manager); + load_max_idle_from_gconf (manager); } static void @@ -2757,19 +3138,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 -up gnome-session-2.31.6/gnome-session/gsm-presence.c.max-idle gnome-session-2.31.6/gnome-session/gsm-presence.c --- gnome-session-2.31.6/gnome-session/gsm-presence.c.max-idle 2010-02-09 08:22:01.000000000 -0500 +++ gnome-session-2.31.6/gnome-session/gsm-presence.c 2010-08-06 20:06:05.281048002 -0400 @@ -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 (DBusGProx 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 *o 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 (GsmPresenceClas 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 -up gnome-session-2.31.6/gnome-session/main.c.max-idle gnome-session-2.31.6/gnome-session/main.c --- gnome-session-2.31.6/gnome-session/main.c.max-idle 2010-03-29 19:46:51.000000000 -0400 +++ gnome-session-2.31.6/gnome-session/main.c 2010-08-06 20:06:05.282048002 -0400 @@ -36,6 +36,7 @@ #include #include #include +#include #include "gdm-signal-handler.h" #include "gdm-log.h" @@ -471,6 +472,8 @@ main (int argc, char **argv) exit (1); } + notify_init ("GNOME session manager"); + gdm_log_init (); gdm_log_set_debug (debug);