diff -up gnome-session-2.31.6/configure.ac.max-idle gnome-session-2.31.6/configure.ac
--- gnome-session-2.31.6/configure.ac.max-idle	2010-08-04 07:35:41.000000000 -0400
+++ gnome-session-2.31.6/configure.ac	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.7.0
 
 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 @@
         </locale>
       </schema>
       <schema>
+          <key>/schemas/desktop/gnome/session/max_idle_action</key>
+          <applyto>/desktop/gnome/session/max_idle_action</applyto>
+          <owner>gnome</owner>
+          <type>string</type>
+          <default></default>
+          <locale name="C">
+             <short>The action to take after the maximum idle time</short>
+             <long>
+               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.
+             </long>
+          </locale>
+      </schema>
+      <schema>
+        <key>/schemas/desktop/gnome/session/max_idle_time</key>
+        <applyto>/desktop/gnome/session/max_idle_time</applyto>
+        <owner>gnome</owner>
+        <type>int</type>
+        <default>120</default>
+        <locale name="C">
+          <short>Amount of time a user can remain idle</short>
+          <long>
+            The number of minutes a user can remain idle before the
+            max_idle_action is automatically taken.
+          </long>
+        </locale>
+      </schema>
+      <schema>
          <key>/schemas/desktop/gnome/session/default_session</key>
          <applyto>/desktop/gnome/session/default_session</applyto>
          <owner>gnome</owner>
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 <dbus/dbus-glib-lowlevel.h>
 
 #include <upower.h>
+#include <libnotify/notify.h>
 
 #include <gtk/gtk.h> /* for logout dialog */
 #include <gconf/gconf-client.h>
@@ -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);
+                        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 <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-bindings.h>
 #include <dbus/dbus-glib-lowlevel.h>
+#include <libnotify/notify.h>
 
 #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);
 
