Blob Blame History Raw
From 8d967c46fe8debcd3eedbb0bbf43a6545429b243 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 8 May 2015 16:24:59 -0400
Subject: [PATCH 01/17] Revert "Remove gnome-session-properties"

This reverts commit ea285af9962313ee2675fff27d3a852bb61e936a.
---
 Makefile.am                                 |    1 +
 capplet/Makefile.am                         |   31 +
 capplet/gsm-app-dialog.c                    |  540 +++++++++++++
 capplet/gsm-app-dialog.h                    |   66 ++
 capplet/gsm-properties-dialog.c             |  774 +++++++++++++++++++
 capplet/gsm-properties-dialog.h             |   57 ++
 capplet/gsp-app-manager.c                   |  593 ++++++++++++++
 capplet/gsp-app-manager.h                   |   81 ++
 capplet/gsp-app.c                           | 1102 +++++++++++++++++++++++++++
 capplet/gsp-app.h                           |  108 +++
 capplet/gsp-keyfile.c                       |  201 +++++
 capplet/gsp-keyfile.h                       |   65 ++
 capplet/main.c                              |  108 +++
 configure.ac                                |    2 +
 data/Makefile.am                            |    6 +
 data/gnome-session-properties.desktop.in.in |   15 +
 doc/man/Makefile.am                         |    1 +
 doc/man/gnome-session-properties.1          |   24 +
 po/POTFILES.in                              |    5 +
 19 files changed, 3780 insertions(+)
 create mode 100644 capplet/Makefile.am
 create mode 100644 capplet/gsm-app-dialog.c
 create mode 100644 capplet/gsm-app-dialog.h
 create mode 100644 capplet/gsm-properties-dialog.c
 create mode 100644 capplet/gsm-properties-dialog.h
 create mode 100644 capplet/gsp-app-manager.c
 create mode 100644 capplet/gsp-app-manager.h
 create mode 100644 capplet/gsp-app.c
 create mode 100644 capplet/gsp-app.h
 create mode 100644 capplet/gsp-keyfile.c
 create mode 100644 capplet/gsp-keyfile.h
 create mode 100644 capplet/main.c
 create mode 100644 data/gnome-session-properties.desktop.in.in
 create mode 100644 doc/man/gnome-session-properties.1

diff --git a/Makefile.am b/Makefile.am
index 6560a4e..6b6a47b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,32 +1,33 @@
 SUBDIRS =			\
 	gnome-session		\
+	capplet			\
 	tools			\
 	data			\
 	doc			\
 	po
 
 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
 
 EXTRA_DIST =		\
 	HACKING		\
 	MAINTAINERS
 
 MAINTAINERCLEANFILES = \
 	$(srcdir)/INSTALL \
 	$(srcdir)/aclocal.m4 \
 	$(srcdir)/config.guess \
 	$(srcdir)/config.h.in \
 	$(srcdir)/config.sub \
 	$(srcdir)/depcomp \
 	$(srcdir)/install-sh \
 	$(srcdir)/ltmain.sh \
 	$(srcdir)/missing \
 	$(srcdir)/mkinstalldirs \
 	$(srcdir)/configure \
 	$(srcdir)/m4/intltool.m4 \
 	`find "$(srcdir)" -type f -name Makefile.in -print`
 
 CHANGELOG_GIT_RANGE = GNOME_SESSION_2_26_1..
 dist-hook:
 	$(AM_V_GEN)if test -d "$(srcdir)/.git"; then \
 	  ( echo '# Generated by Makefile. Do not edit.'; echo; \
diff --git a/capplet/Makefile.am b/capplet/Makefile.am
new file mode 100644
index 0000000..c2e563c
--- /dev/null
+++ b/capplet/Makefile.am
@@ -0,0 +1,31 @@
+bin_PROGRAMS = gnome-session-properties
+
+AM_CPPFLAGS =						\
+	$(SESSION_PROPERTIES_CFLAGS)			\
+	$(GCONF_CFLAGS)					\
+	-I$(top_srcdir)/gnome-session			\
+	-DLOCALE_DIR=\""$(datadir)/locale"\"		\
+	-DGTKBUILDER_DIR=\""$(pkgdatadir)"\"		\
+	$(DISABLE_DEPRECATED_CFLAGS)
+
+AM_CFLAGS = $(WARN_CFLAGS)
+
+gnome_session_properties_SOURCES =			\
+	main.c						\
+	gsm-properties-dialog.h				\
+	gsm-properties-dialog.c				\
+	gsm-app-dialog.h				\
+	gsm-app-dialog.c				\
+	gsp-app.h					\
+	gsp-app.c					\
+	gsp-app-manager.h				\
+	gsp-app-manager.c				\
+	gsp-keyfile.h					\
+	gsp-keyfile.c
+
+gnome_session_properties_LDADD =			\
+	$(SESSION_PROPERTIES_LIBS)			\
+	$(top_builddir)/gnome-session/libgsmutil.la 	\
+	$(GCONF_LIBS)
+
+-include $(top_srcdir)/git.mk
diff --git a/capplet/gsm-app-dialog.c b/capplet/gsm-app-dialog.c
new file mode 100644
index 0000000..e7369dd
--- /dev/null
+++ b/capplet/gsm-app-dialog.c
@@ -0,0 +1,540 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
+ *
+ * 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.
+ *
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gsm-util.h"
+
+#include "gsm-app-dialog.h"
+
+#define GTKBUILDER_FILE "session-properties.ui"
+
+#define CAPPLET_NAME_ENTRY_WIDGET_NAME    "session_properties_name_entry"
+#define CAPPLET_COMMAND_ENTRY_WIDGET_NAME "session_properties_command_entry"
+#define CAPPLET_COMMENT_ENTRY_WIDGET_NAME "session_properties_comment_entry"
+#define CAPPLET_BROWSE_WIDGET_NAME        "session_properties_browse_button"
+
+
+#define GSM_APP_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_APP_DIALOG, GsmAppDialogPrivate))
+
+struct GsmAppDialogPrivate
+{
+        GtkWidget *name_entry;
+        GtkWidget *command_entry;
+        GtkWidget *comment_entry;
+        GtkWidget *browse_button;
+        char      *name;
+        char      *command;
+        char      *comment;
+};
+
+static void     gsm_app_dialog_class_init  (GsmAppDialogClass *klass);
+static void     gsm_app_dialog_init        (GsmAppDialog      *app_dialog);
+static void     gsm_app_dialog_finalize    (GObject           *object);
+
+enum {
+        PROP_0,
+        PROP_NAME,
+        PROP_COMMAND,
+        PROP_COMMENT
+};
+
+G_DEFINE_TYPE (GsmAppDialog, gsm_app_dialog, GTK_TYPE_DIALOG)
+
+static char *
+make_exec_uri (const char *exec)
+{
+        GString    *str;
+        const char *c;
+
+        if (exec == NULL) {
+                return g_strdup ("");
+        }
+
+        if (strchr (exec, ' ') == NULL) {
+                return g_strdup (exec);
+        }
+
+        str = g_string_new_len (NULL, strlen (exec));
+
+        str = g_string_append_c (str, '"');
+        for (c = exec; *c != '\0'; c++) {
+                /* FIXME: GKeyFile will add an additional backslach so we'll
+                 * end up with toto\\" instead of toto\"
+                 * We could use g_key_file_set_value(), but then we don't
+                 * benefit from the other escaping that glib is doing...
+                 */
+                if (*c == '"') {
+                        str = g_string_append (str, "\\\"");
+                } else {
+                        str = g_string_append_c (str, *c);
+                }
+        }
+        str = g_string_append_c (str, '"');
+
+        return g_string_free (str, FALSE);
+}
+
+static void
+on_browse_button_clicked (GtkWidget    *widget,
+                          GsmAppDialog *dialog)
+{
+        GtkWidget *chooser;
+        int        response;
+
+        chooser = gtk_file_chooser_dialog_new ("",
+                                               GTK_WINDOW (dialog),
+                                               GTK_FILE_CHOOSER_ACTION_OPEN,
+                                               GTK_STOCK_CANCEL,
+                                               GTK_RESPONSE_CANCEL,
+                                               GTK_STOCK_OPEN,
+                                               GTK_RESPONSE_ACCEPT,
+                                               NULL);
+
+        gtk_window_set_transient_for (GTK_WINDOW (chooser),
+                                      GTK_WINDOW (dialog));
+
+        gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE);
+
+        gtk_window_set_title (GTK_WINDOW (chooser), _("Select Command"));
+
+        gtk_widget_show (chooser);
+
+        response = gtk_dialog_run (GTK_DIALOG (chooser));
+
+        if (response == GTK_RESPONSE_ACCEPT) {
+                char *text;
+                char *uri;
+
+                text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
+
+                uri = make_exec_uri (text);
+
+                g_free (text);
+
+                gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), uri);
+
+                g_free (uri);
+        }
+
+        gtk_widget_destroy (chooser);
+}
+
+static void
+on_entry_activate (GtkEntry     *entry,
+                   GsmAppDialog *dialog)
+{
+        gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+}
+
+static void
+setup_dialog (GsmAppDialog *dialog)
+{
+        GtkWidget  *content_area;
+        GtkWidget  *widget;
+        GtkBuilder *xml;
+        GError     *error;
+
+        xml = gtk_builder_new ();
+        gtk_builder_set_translation_domain (xml, GETTEXT_PACKAGE);
+
+        error = NULL;
+        if (!gtk_builder_add_from_file (xml,
+                                        GTKBUILDER_DIR "/" GTKBUILDER_FILE,
+                                        &error)) {
+                if (error) {
+                        g_warning ("Could not load capplet UI file: %s",
+                                   error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("Could not load capplet UI file.");
+                }
+        }
+
+        content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+        widget = GTK_WIDGET (gtk_builder_get_object (xml, "main-table"));
+        gtk_container_add (GTK_CONTAINER (content_area), widget);
+
+        gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+        gtk_window_set_icon_name (GTK_WINDOW (dialog), "session-properties");
+
+        g_object_set (dialog,
+                      "allow-shrink", FALSE,
+                      "allow-grow", FALSE,
+                      NULL);
+
+        gtk_dialog_add_button (GTK_DIALOG (dialog),
+                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+        if (dialog->priv->name == NULL
+            && dialog->priv->command == NULL
+            && dialog->priv->comment == NULL) {
+                gtk_window_set_title (GTK_WINDOW (dialog), _("Add Startup Program"));
+                gtk_dialog_add_button (GTK_DIALOG (dialog),
+                                       GTK_STOCK_ADD, GTK_RESPONSE_OK);
+        } else {
+                gtk_window_set_title (GTK_WINDOW (dialog), _("Edit Startup Program"));
+                gtk_dialog_add_button (GTK_DIALOG (dialog),
+                                       GTK_STOCK_SAVE, GTK_RESPONSE_OK);
+        }
+
+        dialog->priv->name_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_NAME_ENTRY_WIDGET_NAME));
+        g_signal_connect (dialog->priv->name_entry,
+                          "activate",
+                          G_CALLBACK (on_entry_activate),
+                          dialog);
+        if (dialog->priv->name != NULL) {
+                gtk_entry_set_text (GTK_ENTRY (dialog->priv->name_entry), dialog->priv->name);
+        }
+
+        dialog->priv->browse_button = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_BROWSE_WIDGET_NAME));
+        g_signal_connect (dialog->priv->browse_button,
+                          "clicked",
+                          G_CALLBACK (on_browse_button_clicked),
+                          dialog);
+
+        dialog->priv->command_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_COMMAND_ENTRY_WIDGET_NAME));
+        g_signal_connect (dialog->priv->command_entry,
+                          "activate",
+                          G_CALLBACK (on_entry_activate),
+                          dialog);
+        if (dialog->priv->command != NULL) {
+                gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), dialog->priv->command);
+        }
+
+        dialog->priv->comment_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_COMMENT_ENTRY_WIDGET_NAME));
+        g_signal_connect (dialog->priv->comment_entry,
+                          "activate",
+                          G_CALLBACK (on_entry_activate),
+                          dialog);
+        if (dialog->priv->comment != NULL) {
+                gtk_entry_set_text (GTK_ENTRY (dialog->priv->comment_entry), dialog->priv->comment);
+        }
+
+        if (xml != NULL) {
+                g_object_unref (xml);
+        }
+}
+
+static GObject *
+gsm_app_dialog_constructor (GType                  type,
+                            guint                  n_construct_app,
+                            GObjectConstructParam *construct_app)
+{
+        GsmAppDialog *dialog;
+
+        dialog = GSM_APP_DIALOG (G_OBJECT_CLASS (gsm_app_dialog_parent_class)->constructor (type,
+                                                                                                                  n_construct_app,
+                                                                                                                  construct_app));
+
+        setup_dialog (dialog);
+
+        gtk_widget_show_all (GTK_WIDGET (dialog));
+
+        return G_OBJECT (dialog);
+}
+
+static void
+gsm_app_dialog_dispose (GObject *object)
+{
+        GsmAppDialog *dialog;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GSM_IS_APP_DIALOG (object));
+
+        dialog = GSM_APP_DIALOG (object);
+
+        g_free (dialog->priv->name);
+        dialog->priv->name = NULL;
+        g_free (dialog->priv->command);
+        dialog->priv->command = NULL;
+        g_free (dialog->priv->comment);
+        dialog->priv->comment = NULL;
+
+        G_OBJECT_CLASS (gsm_app_dialog_parent_class)->dispose (object);
+}
+
+static void
+gsm_app_dialog_set_name (GsmAppDialog *dialog,
+                         const char   *name)
+{
+        g_return_if_fail (GSM_IS_APP_DIALOG (dialog));
+
+        g_free (dialog->priv->name);
+
+        dialog->priv->name = g_strdup (name);
+        g_object_notify (G_OBJECT (dialog), "name");
+}
+
+static void
+gsm_app_dialog_set_command (GsmAppDialog *dialog,
+                            const char   *name)
+{
+        g_return_if_fail (GSM_IS_APP_DIALOG (dialog));
+
+        g_free (dialog->priv->command);
+
+        dialog->priv->command = g_strdup (name);
+        g_object_notify (G_OBJECT (dialog), "command");
+}
+
+static void
+gsm_app_dialog_set_comment (GsmAppDialog *dialog,
+                            const char   *name)
+{
+        g_return_if_fail (GSM_IS_APP_DIALOG (dialog));
+
+        g_free (dialog->priv->comment);
+
+        dialog->priv->comment = g_strdup (name);
+        g_object_notify (G_OBJECT (dialog), "comment");
+}
+
+const char *
+gsm_app_dialog_get_name (GsmAppDialog *dialog)
+{
+        g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL);
+        return gtk_entry_get_text (GTK_ENTRY (dialog->priv->name_entry));
+}
+
+const char *
+gsm_app_dialog_get_command (GsmAppDialog *dialog)
+{
+        g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL);
+        return gtk_entry_get_text (GTK_ENTRY (dialog->priv->command_entry));
+}
+
+const char *
+gsm_app_dialog_get_comment (GsmAppDialog *dialog)
+{
+        g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL);
+        return gtk_entry_get_text (GTK_ENTRY (dialog->priv->comment_entry));
+}
+
+static void
+gsm_app_dialog_set_property (GObject        *object,
+                             guint           prop_id,
+                             const GValue   *value,
+                             GParamSpec     *pspec)
+{
+        GsmAppDialog *dialog = GSM_APP_DIALOG (object);
+
+        switch (prop_id) {
+        case PROP_NAME:
+                gsm_app_dialog_set_name (dialog, g_value_get_string (value));
+                break;
+        case PROP_COMMAND:
+                gsm_app_dialog_set_command (dialog, g_value_get_string (value));
+                break;
+        case PROP_COMMENT:
+                gsm_app_dialog_set_comment (dialog, g_value_get_string (value));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gsm_app_dialog_get_property (GObject        *object,
+                             guint           prop_id,
+                             GValue         *value,
+                             GParamSpec     *pspec)
+{
+        GsmAppDialog *dialog = GSM_APP_DIALOG (object);
+
+        switch (prop_id) {
+        case PROP_NAME:
+                g_value_set_string (value, dialog->priv->name);
+                break;
+        case PROP_COMMAND:
+                g_value_set_string (value, dialog->priv->command);
+                break;
+        case PROP_COMMENT:
+                g_value_set_string (value, dialog->priv->comment);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gsm_app_dialog_class_init (GsmAppDialogClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->get_property = gsm_app_dialog_get_property;
+        object_class->set_property = gsm_app_dialog_set_property;
+        object_class->constructor = gsm_app_dialog_constructor;
+        object_class->dispose = gsm_app_dialog_dispose;
+        object_class->finalize = gsm_app_dialog_finalize;
+
+        g_object_class_install_property (object_class,
+                                         PROP_NAME,
+                                         g_param_spec_string ("name",
+                                                              "name",
+                                                              "name",
+                                                              NULL,
+                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+        g_object_class_install_property (object_class,
+                                         PROP_COMMAND,
+                                         g_param_spec_string ("command",
+                                                              "command",
+                                                              "command",
+                                                              NULL,
+                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+        g_object_class_install_property (object_class,
+                                         PROP_COMMENT,
+                                         g_param_spec_string ("comment",
+                                                              "comment",
+                                                              "comment",
+                                                              NULL,
+                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+        g_type_class_add_private (klass, sizeof (GsmAppDialogPrivate));
+}
+
+static void
+gsm_app_dialog_init (GsmAppDialog *dialog)
+{
+
+        dialog->priv = GSM_APP_DIALOG_GET_PRIVATE (dialog);
+}
+
+static void
+gsm_app_dialog_finalize (GObject *object)
+{
+        GsmAppDialog *dialog;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GSM_IS_APP_DIALOG (object));
+
+        dialog = GSM_APP_DIALOG (object);
+
+        g_return_if_fail (dialog->priv != NULL);
+
+        G_OBJECT_CLASS (gsm_app_dialog_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gsm_app_dialog_new (const char *name,
+                    const char *command,
+                    const char *comment)
+{
+        GObject *object;
+
+        object = g_object_new (GSM_TYPE_APP_DIALOG,
+                               "name", name,
+                               "command", command,
+                               "comment", comment,
+                               NULL);
+
+        return GTK_WIDGET (object);
+}
+
+gboolean
+gsm_app_dialog_run (GsmAppDialog  *dialog,
+                    char         **name_p,
+                    char         **command_p,
+                    char         **comment_p)
+{
+        gboolean retval;
+
+        retval = FALSE;
+
+        while (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
+                const char *name;
+                const char *exec;
+                const char *comment;
+                const char *error_msg;
+                GError     *error;
+                char      **argv;
+                int         argc;
+
+                name = gsm_app_dialog_get_name (GSM_APP_DIALOG (dialog));
+                exec = gsm_app_dialog_get_command (GSM_APP_DIALOG (dialog));
+                comment = gsm_app_dialog_get_comment (GSM_APP_DIALOG (dialog));
+
+                error = NULL;
+                error_msg = NULL;
+
+                if (gsm_util_text_is_blank (exec)) {
+                        error_msg = _("The startup command cannot be empty");
+                } else {
+                        if (!g_shell_parse_argv (exec, &argc, &argv, &error)) {
+                                if (error != NULL) {
+                                        error_msg = error->message;
+                                } else {
+                                        error_msg = _("The startup command is not valid");
+                                }
+                        }
+                }
+
+                if (error_msg != NULL) {
+                        GtkWidget *msgbox;
+
+                        msgbox = gtk_message_dialog_new (GTK_WINDOW (dialog),
+                                                         GTK_DIALOG_MODAL,
+                                                         GTK_MESSAGE_ERROR,
+                                                         GTK_BUTTONS_CLOSE,
+                                                         "%s", error_msg);
+
+                        if (error != NULL) {
+                                g_error_free (error);
+                        }
+
+                        gtk_dialog_run (GTK_DIALOG (msgbox));
+
+                        gtk_widget_destroy (msgbox);
+
+                        continue;
+                }
+
+                if (gsm_util_text_is_blank (name)) {
+                        name = argv[0];
+                }
+
+                if (name_p) {
+                        *name_p = g_strdup (name);
+                }
+
+                g_strfreev (argv);
+
+                if (command_p) {
+                        *command_p = g_strdup (exec);
+                }
+
+                if (comment_p) {
+                        *comment_p = g_strdup (comment);
+                }
+
+                retval = TRUE;
+                break;
+        }
+
+        gtk_widget_destroy (GTK_WIDGET (dialog));
+
+        return retval;
+}
diff --git a/capplet/gsm-app-dialog.h b/capplet/gsm-app-dialog.h
new file mode 100644
index 0000000..ced0628
--- /dev/null
+++ b/capplet/gsm-app-dialog.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __GSM_APP_DIALOG_H
+#define __GSM_APP_DIALOG_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GSM_TYPE_APP_DIALOG         (gsm_app_dialog_get_type ())
+#define GSM_APP_DIALOG(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_APP_DIALOG, GsmAppDialog))
+#define GSM_APP_DIALOG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_APP_DIALOG, GsmAppDialogClass))
+#define GSM_IS_APP_DIALOG(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_APP_DIALOG))
+#define GSM_IS_APP_DIALOG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_APP_DIALOG))
+#define GSM_APP_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_APP_DIALOG, GsmAppDialogClass))
+
+typedef struct GsmAppDialogPrivate GsmAppDialogPrivate;
+
+typedef struct
+{
+        GtkDialog            parent;
+        GsmAppDialogPrivate *priv;
+} GsmAppDialog;
+
+typedef struct
+{
+        GtkDialogClass   parent_class;
+} GsmAppDialogClass;
+
+GType                  gsm_app_dialog_get_type           (void);
+
+GtkWidget            * gsm_app_dialog_new                (const char   *name,
+                                                          const char   *command,
+                                                          const char   *comment);
+
+gboolean               gsm_app_dialog_run               (GsmAppDialog  *dialog,
+                                                         char         **name_p,
+                                                         char         **command_p,
+                                                         char         **comment_p);
+
+const char *           gsm_app_dialog_get_name           (GsmAppDialog *dialog);
+const char *           gsm_app_dialog_get_command        (GsmAppDialog *dialog);
+const char *           gsm_app_dialog_get_comment        (GsmAppDialog *dialog);
+
+G_END_DECLS
+
+#endif /* __GSM_APP_DIALOG_H */
diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c
new file mode 100644
index 0000000..33812b8
--- /dev/null
+++ b/capplet/gsm-properties-dialog.c
@@ -0,0 +1,774 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2007 Vincent Untz.
+ * Copyright (C) 2008 Lucas Rocha.
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
+ *
+ * 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.
+ *
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gsm-properties-dialog.h"
+#include "gsm-app-dialog.h"
+#include "gsm-util.h"
+#include "gsp-app.h"
+#include "gsp-app-manager.h"
+
+#define GSM_PROPERTIES_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogPrivate))
+
+#define GTKBUILDER_FILE "session-properties.ui"
+
+#define CAPPLET_TREEVIEW_WIDGET_NAME      "session_properties_treeview"
+#define CAPPLET_ADD_WIDGET_NAME           "session_properties_add_button"
+#define CAPPLET_DELETE_WIDGET_NAME        "session_properties_delete_button"
+#define CAPPLET_EDIT_WIDGET_NAME          "session_properties_edit_button"
+#define CAPPLET_SAVE_WIDGET_NAME          "session_properties_save_button"
+#define CAPPLET_REMEMBER_WIDGET_NAME      "session_properties_remember_toggle"
+
+#define STARTUP_APP_ICON     "system-run"
+
+#define SPC_SETTINGS_SCHEMA          "org.gnome.SessionManager"
+#define SPC_SETTINGS_AUTOSAVE_KEY    "auto-save-session"
+
+struct GsmPropertiesDialogPrivate
+{
+        GtkBuilder        *xml;
+        GtkListStore      *list_store;
+        GtkTreeModel      *tree_filter;
+
+        GtkTreeView       *treeview;
+        GtkWidget         *add_button;
+        GtkWidget         *delete_button;
+        GtkWidget         *edit_button;
+
+        GSettings         *settings;
+
+        GspAppManager     *manager;
+};
+
+enum {
+        STORE_COL_VISIBLE = 0,
+        STORE_COL_ENABLED,
+        STORE_COL_GICON,
+        STORE_COL_DESCRIPTION,
+        STORE_COL_APP,
+        STORE_COL_SEARCH,
+        NUMBER_OF_COLUMNS
+};
+
+static void     gsm_properties_dialog_class_init  (GsmPropertiesDialogClass *klass);
+static void     gsm_properties_dialog_init        (GsmPropertiesDialog      *properties_dialog);
+static void     gsm_properties_dialog_finalize    (GObject                  *object);
+
+G_DEFINE_TYPE (GsmPropertiesDialog, gsm_properties_dialog, GTK_TYPE_DIALOG)
+
+static gboolean
+find_by_app (GtkTreeModel *model,
+             GtkTreeIter  *iter,
+             GspApp       *app)
+{
+        GspApp *iter_app = NULL;
+
+        if (!gtk_tree_model_get_iter_first (model, iter)) {
+                return FALSE;
+        }
+
+        do {
+                gtk_tree_model_get (model, iter,
+                                    STORE_COL_APP, &iter_app,
+                                    -1);
+
+                if (iter_app == app) {
+                        g_object_unref (iter_app);
+                        return TRUE;
+                }
+        } while (gtk_tree_model_iter_next (model, iter));
+
+        return FALSE;
+}
+
+static void
+_fill_iter_from_app (GtkListStore *list_store,
+                     GtkTreeIter  *iter,
+                     GspApp       *app)
+{
+        gboolean    hidden;
+        gboolean    display;
+        gboolean    enabled;
+        gboolean    shown;
+        GIcon      *icon;
+        const char *description;
+        const char *app_name;
+
+        hidden      = gsp_app_get_hidden (app);
+        display     = gsp_app_get_display (app);
+        enabled     = gsp_app_get_enabled (app);
+        shown       = gsp_app_get_shown (app);
+        icon        = gsp_app_get_icon (app);
+        description = gsp_app_get_description (app);
+        app_name    = gsp_app_get_name (app);
+
+        if (G_IS_THEMED_ICON (icon)) {
+                GtkIconTheme       *theme;
+                const char * const *icon_names;
+
+                theme = gtk_icon_theme_get_default ();
+                icon_names = g_themed_icon_get_names (G_THEMED_ICON (icon));
+                if (icon_names[0] == NULL ||
+                    !gtk_icon_theme_has_icon (theme, icon_names[0])) {
+                        g_object_unref (icon);
+                        icon = NULL;
+                }
+        } else if (G_IS_FILE_ICON (icon)) {
+                GFile *iconfile;
+
+                iconfile = g_file_icon_get_file (G_FILE_ICON (icon));
+                if (!g_file_query_exists (iconfile, NULL)) {
+                        g_object_unref (icon);
+                        icon = NULL;
+                }
+        }
+
+        if (icon == NULL) {
+                icon = g_themed_icon_new (STARTUP_APP_ICON);
+        }
+
+        gtk_list_store_set (list_store, iter,
+                            STORE_COL_VISIBLE, !hidden && shown && display,
+                            STORE_COL_ENABLED, enabled,
+                            STORE_COL_GICON, icon,
+                            STORE_COL_DESCRIPTION, description,
+                            STORE_COL_APP, app,
+                            STORE_COL_SEARCH, app_name,
+                            -1);
+        g_object_unref (icon);
+}
+
+static void
+_app_changed (GsmPropertiesDialog *dialog,
+              GspApp              *app)
+{
+        GtkTreeIter iter;
+
+        if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store),
+                          &iter, app)) {
+                return;
+        }
+
+        _fill_iter_from_app (dialog->priv->list_store, &iter, app);
+}
+
+static void
+append_app (GsmPropertiesDialog *dialog,
+            GspApp              *app)
+{
+        GtkTreeIter   iter;
+
+        gtk_list_store_append (dialog->priv->list_store, &iter);
+        _fill_iter_from_app (dialog->priv->list_store, &iter, app);
+
+        g_signal_connect_swapped (app, "changed",
+                                  G_CALLBACK (_app_changed), dialog);
+}
+
+static void
+_app_added (GsmPropertiesDialog *dialog,
+            GspApp              *app,
+            GspAppManager       *manager)
+{
+        append_app (dialog, app);
+}
+
+static void
+_app_removed (GsmPropertiesDialog *dialog,
+              GspApp              *app,
+              GspAppManager       *manager)
+{
+        GtkTreeIter iter;
+
+        if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store),
+                          &iter, app)) {
+                return;
+        }
+
+        g_signal_handlers_disconnect_by_func (app,
+                                              _app_changed,
+                                              dialog);
+        gtk_list_store_remove (dialog->priv->list_store, &iter);
+}
+
+static void
+populate_model (GsmPropertiesDialog *dialog)
+{
+        GSList *apps;
+        GSList *l;
+
+        apps = gsp_app_manager_get_apps (dialog->priv->manager);
+        for (l = apps; l != NULL; l = l->next) {
+                append_app (dialog, GSP_APP (l->data));
+        }
+        g_slist_free (apps);
+}
+
+static void
+on_selection_changed (GtkTreeSelection    *selection,
+                      GsmPropertiesDialog *dialog)
+{
+        gboolean sel;
+
+        sel = gtk_tree_selection_get_selected (selection, NULL, NULL);
+
+        gtk_widget_set_sensitive (dialog->priv->edit_button, sel);
+        gtk_widget_set_sensitive (dialog->priv->delete_button, sel);
+}
+
+static void
+on_startup_enabled_toggled (GtkCellRendererToggle *cell_renderer,
+                            char                  *path,
+                            GsmPropertiesDialog   *dialog)
+{
+        GtkTreeIter iter;
+        GspApp     *app;
+        gboolean    active;
+
+        if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dialog->priv->tree_filter),
+                                                  &iter, path)) {
+                return;
+        }
+
+        app = NULL;
+        gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
+                            &iter,
+                            STORE_COL_APP, &app,
+                            -1);
+
+        active = gtk_cell_renderer_toggle_get_active (cell_renderer);
+        active = !active;
+
+        if (app) {
+                gsp_app_set_enabled (app, active);
+                g_object_unref (app);
+        }
+}
+
+static void
+on_drag_data_received (GtkWidget           *widget,
+                       GdkDragContext      *drag_context,
+                       gint                 x,
+                       gint                 y,
+                       GtkSelectionData    *data,
+                       guint                info,
+                       guint                time,
+                       GsmPropertiesDialog *dialog)
+{
+        gboolean dnd_success;
+
+        dnd_success = FALSE;
+
+        if (data != NULL) {
+                char **filenames;
+                int    i;
+
+                filenames = gtk_selection_data_get_uris (data);
+
+                for (i = 0; filenames[i] && filenames[i][0]; i++) {
+                        /* Return success if at least one file succeeded */
+                        gboolean file_success;
+                        file_success = gsp_app_copy_desktop_file (filenames[i]);
+                        dnd_success = dnd_success || file_success;
+                }
+
+                g_strfreev (filenames);
+        }
+
+        gtk_drag_finish (drag_context, dnd_success, FALSE, time);
+        g_signal_stop_emission_by_name (widget, "drag_data_received");
+}
+
+static void
+on_drag_begin (GtkWidget           *widget,
+               GdkDragContext      *context,
+               GsmPropertiesDialog *dialog)
+{
+        GtkTreePath *path;
+        GtkTreeIter  iter;
+        GspApp      *app;
+
+        gtk_tree_view_get_cursor (GTK_TREE_VIEW (widget), &path, NULL);
+        gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->priv->tree_filter),
+                                 &iter, path);
+        gtk_tree_path_free (path);
+
+        gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
+                            &iter,
+                            STORE_COL_APP, &app,
+                            -1);
+
+        if (app) {
+                g_object_set_data_full (G_OBJECT (context), "gsp-app",
+                                        g_object_ref (app), g_object_unref);
+                g_object_unref (app);
+        }
+
+}
+
+static void
+on_drag_data_get (GtkWidget           *widget,
+                  GdkDragContext      *context,
+                  GtkSelectionData    *selection_data,
+                  guint                info,
+                  guint                time,
+                  GsmPropertiesDialog *dialog)
+{
+        GspApp *app;
+
+        app = g_object_get_data (G_OBJECT (context), "gsp-app");
+        if (app) {
+                const char *uris[2];
+                char       *uri;
+
+                uri = g_filename_to_uri (gsp_app_get_path (app), NULL, NULL);
+
+                uris[0] = uri;
+                uris[1] = NULL;
+                gtk_selection_data_set_uris (selection_data, (char **) uris);
+
+                g_free (uri);
+        }
+}
+
+static void
+on_add_app_clicked (GtkWidget           *widget,
+                    GsmPropertiesDialog *dialog)
+{
+        GtkWidget  *add_dialog;
+        char       *name;
+        char       *exec;
+        char       *comment;
+
+        add_dialog = gsm_app_dialog_new (NULL, NULL, NULL);
+        gtk_window_set_transient_for (GTK_WINDOW (add_dialog),
+                                      GTK_WINDOW (dialog));
+
+        if (gsm_app_dialog_run (GSM_APP_DIALOG (add_dialog),
+                                &name, &exec, &comment)) {
+                gsp_app_create (name, comment, exec);
+                g_free (name);
+                g_free (exec);
+                g_free (comment);
+        }
+}
+
+static void
+on_delete_app_clicked (GtkWidget           *widget,
+                       GsmPropertiesDialog *dialog)
+{
+        GtkTreeSelection *selection;
+        GtkTreeIter       iter;
+        GspApp           *app;
+
+        selection = gtk_tree_view_get_selection (dialog->priv->treeview);
+
+        if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+                return;
+        }
+
+        app = NULL;
+        gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
+                            &iter,
+                            STORE_COL_APP, &app,
+                            -1);
+
+        if (app) {
+                gsp_app_delete (app);
+                g_object_unref (app);
+        }
+}
+
+static void
+on_edit_app_clicked (GtkWidget           *widget,
+                     GsmPropertiesDialog *dialog)
+{
+        GtkTreeSelection *selection;
+        GtkTreeIter       iter;
+        GspApp           *app;
+
+        selection = gtk_tree_view_get_selection (dialog->priv->treeview);
+
+        if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+                return;
+        }
+
+        app = NULL;
+        gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
+                            &iter,
+                            STORE_COL_APP, &app,
+                            -1);
+
+        if (app) {
+                GtkWidget  *edit_dialog;
+                char       *name;
+                char       *exec;
+                char       *comment;
+
+                edit_dialog = gsm_app_dialog_new (gsp_app_get_name (app),
+                                                  gsp_app_get_exec (app),
+                                                  gsp_app_get_comment (app));
+                gtk_window_set_transient_for (GTK_WINDOW (edit_dialog),
+                                              GTK_WINDOW (dialog));
+
+                if (gsm_app_dialog_run (GSM_APP_DIALOG (edit_dialog),
+                                        &name, &exec, &comment)) {
+                        gsp_app_update (app, name, comment, exec);
+                        g_free (name);
+                        g_free (exec);
+                        g_free (comment);
+                }
+
+                g_object_unref (app);
+        }
+}
+
+static void
+on_row_activated (GtkTreeView         *tree_view,
+                  GtkTreePath         *path,
+                  GtkTreeViewColumn   *column,
+                  GsmPropertiesDialog *dialog)
+{
+        on_edit_app_clicked (NULL, dialog);
+}
+
+static void
+on_save_session_clicked (GtkWidget           *widget,
+                         GsmPropertiesDialog *dialog)
+{
+        g_debug ("Session saving is not implemented yet!");
+}
+
+static void
+setup_dialog (GsmPropertiesDialog *dialog)
+{
+        GtkTreeView       *treeview;
+        GtkWidget         *button;
+        GtkTreeModel      *tree_filter;
+        GtkTreeViewColumn *column;
+        GtkCellRenderer   *renderer;
+        GtkTreeSelection  *selection;
+        GtkTargetList     *targetlist;
+
+        gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+                                GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+                                NULL);
+
+        dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
+                                                       G_TYPE_BOOLEAN,
+                                                       G_TYPE_BOOLEAN,
+                                                       G_TYPE_ICON,
+                                                       G_TYPE_STRING,
+                                                       G_TYPE_OBJECT,
+                                                       G_TYPE_STRING);
+        tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store),
+                                                 NULL);
+        g_object_unref (dialog->priv->list_store);
+        dialog->priv->tree_filter = tree_filter;
+
+        gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter),
+                                                  STORE_COL_VISIBLE);
+
+        treeview = GTK_TREE_VIEW (gtk_builder_get_object (dialog->priv->xml,
+                                                          CAPPLET_TREEVIEW_WIDGET_NAME));
+        dialog->priv->treeview = treeview;
+
+        gtk_tree_view_set_model (treeview, tree_filter);
+        g_object_unref (tree_filter);
+
+        gtk_tree_view_set_headers_visible (treeview, FALSE);
+        g_signal_connect (treeview,
+                          "row-activated",
+                          G_CALLBACK (on_row_activated),
+                          dialog);
+
+        selection = gtk_tree_view_get_selection (treeview);
+        gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+        g_signal_connect (selection,
+                          "changed",
+                          G_CALLBACK (on_selection_changed),
+                          dialog);
+
+        /* CHECKBOX COLUMN */
+        renderer = gtk_cell_renderer_toggle_new ();
+        column = gtk_tree_view_column_new_with_attributes (_("Enabled"),
+                                                           renderer,
+                                                           "active", STORE_COL_ENABLED,
+                                                           NULL);
+        gtk_tree_view_append_column (treeview, column);
+        g_signal_connect (renderer,
+                          "toggled",
+                          G_CALLBACK (on_startup_enabled_toggled),
+                          dialog);
+
+        /* ICON COLUMN */
+        renderer = gtk_cell_renderer_pixbuf_new ();
+        column = gtk_tree_view_column_new_with_attributes (_("Icon"),
+                                                           renderer,
+                                                           "gicon", STORE_COL_GICON,
+                                                           "sensitive", STORE_COL_ENABLED,
+                                                           NULL);
+        g_object_set (renderer,
+                      "stock-size", GSM_PROPERTIES_ICON_SIZE,
+                      NULL);
+        gtk_tree_view_append_column (treeview, column);
+
+        /* NAME COLUMN */
+        renderer = gtk_cell_renderer_text_new ();
+        column = gtk_tree_view_column_new_with_attributes (_("Program"),
+                                                           renderer,
+                                                           "markup", STORE_COL_DESCRIPTION,
+                                                           "sensitive", STORE_COL_ENABLED,
+                                                           NULL);
+        g_object_set (renderer,
+                      "ellipsize", PANGO_ELLIPSIZE_END,
+                      NULL);
+        gtk_tree_view_append_column (treeview, column);
+
+
+        gtk_tree_view_column_set_sort_column_id (column, STORE_COL_DESCRIPTION);
+        gtk_tree_view_set_search_column (treeview, STORE_COL_SEARCH);
+        gtk_tree_view_set_rules_hint (treeview, TRUE);
+
+        gtk_tree_view_enable_model_drag_source (treeview,
+                                                GDK_BUTTON1_MASK|GDK_BUTTON2_MASK,
+                                                NULL, 0,
+                                                GDK_ACTION_COPY);
+        gtk_drag_source_add_uri_targets (GTK_WIDGET (treeview));
+
+        gtk_drag_dest_set (GTK_WIDGET (treeview),
+                           GTK_DEST_DEFAULT_ALL,
+                           NULL, 0,
+                           GDK_ACTION_COPY);
+
+        gtk_drag_dest_add_uri_targets (GTK_WIDGET (treeview));
+        /* we don't want to accept drags coming from this widget */
+        targetlist = gtk_drag_dest_get_target_list (GTK_WIDGET (treeview));
+        if (targetlist != NULL) {
+                GtkTargetEntry *targets;
+                gint n_targets;
+                gint i;
+
+                targets = gtk_target_table_new_from_list (targetlist, &n_targets);
+                for (i = 0; i < n_targets; i++)
+                        targets[i].flags = GTK_TARGET_OTHER_WIDGET;
+
+                targetlist = gtk_target_list_new (targets, n_targets);
+                gtk_drag_dest_set_target_list (GTK_WIDGET (treeview), targetlist);
+                gtk_target_list_unref (targetlist);
+
+                gtk_target_table_free (targets, n_targets);
+        }
+
+        g_signal_connect (treeview, "drag_begin",
+                          G_CALLBACK (on_drag_begin),
+                          dialog);
+        g_signal_connect (treeview, "drag_data_get",
+                          G_CALLBACK (on_drag_data_get),
+                          dialog);
+        g_signal_connect (treeview, "drag_data_received",
+                          G_CALLBACK (on_drag_data_received),
+                          dialog);
+
+        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->priv->list_store),
+                                              STORE_COL_DESCRIPTION,
+                                              GTK_SORT_ASCENDING);
+
+
+        button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+                                                     CAPPLET_ADD_WIDGET_NAME));
+        dialog->priv->add_button = button;
+        g_signal_connect (button,
+                          "clicked",
+                          G_CALLBACK (on_add_app_clicked),
+                          dialog);
+
+        button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+                                                     CAPPLET_DELETE_WIDGET_NAME));
+        dialog->priv->delete_button = button;
+        g_signal_connect (button,
+                          "clicked",
+                          G_CALLBACK (on_delete_app_clicked),
+                          dialog);
+
+        button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+                                                     CAPPLET_EDIT_WIDGET_NAME));
+        dialog->priv->edit_button = button;
+        g_signal_connect (button,
+                          "clicked",
+                          G_CALLBACK (on_edit_app_clicked),
+                          dialog);
+
+        button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+                                                     CAPPLET_REMEMBER_WIDGET_NAME));
+        g_settings_bind (dialog->priv->settings, SPC_SETTINGS_AUTOSAVE_KEY,
+                         button, "active", G_SETTINGS_BIND_DEFAULT);
+
+        button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+                                                     CAPPLET_SAVE_WIDGET_NAME));
+        g_signal_connect (button,
+                          "clicked",
+                          G_CALLBACK (on_save_session_clicked),
+                          dialog);
+
+        dialog->priv->manager = gsp_app_manager_get ();
+        gsp_app_manager_fill (dialog->priv->manager);
+        g_signal_connect_swapped (dialog->priv->manager, "added",
+                                  G_CALLBACK (_app_added), dialog);
+        g_signal_connect_swapped (dialog->priv->manager, "removed",
+                                  G_CALLBACK (_app_removed), dialog);
+
+        populate_model (dialog);
+}
+
+static GObject *
+gsm_properties_dialog_constructor (GType                  type,
+                                guint                  n_construct_properties,
+                                GObjectConstructParam *construct_properties)
+{
+        GsmPropertiesDialog *dialog;
+
+        dialog = GSM_PROPERTIES_DIALOG (G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->constructor (type,
+                                                                                                                  n_construct_properties,
+                                                                                                                  construct_properties));
+
+        setup_dialog (dialog);
+
+        gtk_widget_show (GTK_WIDGET (dialog));
+
+        return G_OBJECT (dialog);
+}
+
+static void
+gsm_properties_dialog_dispose (GObject *object)
+{
+        GsmPropertiesDialog *dialog;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object));
+
+        dialog = GSM_PROPERTIES_DIALOG (object);
+
+        if (dialog->priv->xml != NULL) {
+                g_object_unref (dialog->priv->xml);
+                dialog->priv->xml = NULL;
+        }
+
+        if (dialog->priv->settings != NULL) {
+                g_object_unref (dialog->priv->settings);
+                dialog->priv->settings = NULL;
+        }
+
+        G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->dispose (object);
+
+        /* it's important to do this after chaining to the parent dispose
+         * method because we want to make sure the treeview has been disposed
+         * and removed all its references to GspApp objects */
+        if (dialog->priv->manager != NULL) {
+                g_object_unref (dialog->priv->manager);
+                dialog->priv->manager = NULL;
+        }
+}
+
+static void
+gsm_properties_dialog_class_init (GsmPropertiesDialogClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->constructor = gsm_properties_dialog_constructor;
+        object_class->dispose = gsm_properties_dialog_dispose;
+        object_class->finalize = gsm_properties_dialog_finalize;
+
+        g_type_class_add_private (klass, sizeof (GsmPropertiesDialogPrivate));
+}
+
+static void
+gsm_properties_dialog_init (GsmPropertiesDialog *dialog)
+{
+        GtkWidget   *content_area;
+        GtkWidget   *widget;
+        GError      *error;
+
+        dialog->priv = GSM_PROPERTIES_DIALOG_GET_PRIVATE (dialog);
+
+        dialog->priv->settings = g_settings_new (SPC_SETTINGS_SCHEMA);
+
+        dialog->priv->xml = gtk_builder_new ();
+        gtk_builder_set_translation_domain (dialog->priv->xml, GETTEXT_PACKAGE);
+
+        error = NULL;
+        if (!gtk_builder_add_from_file (dialog->priv->xml,
+                                        GTKBUILDER_DIR "/" GTKBUILDER_FILE,
+                                        &error)) {
+                if (error) {
+                        g_warning ("Could not load capplet UI file: %s",
+                                   error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("Could not load capplet UI file.");
+                }
+        }
+
+        content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+        widget = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+                                                     "main-notebook"));
+        gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
+
+        gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 450);
+        gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+        gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+        gtk_box_set_spacing (GTK_BOX (content_area), 2);
+        gtk_window_set_icon_name (GTK_WINDOW (dialog), "session-properties");
+        gtk_window_set_title (GTK_WINDOW (dialog), _("Startup Applications Preferences"));
+}
+
+static void
+gsm_properties_dialog_finalize (GObject *object)
+{
+        GsmPropertiesDialog *dialog;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object));
+
+        dialog = GSM_PROPERTIES_DIALOG (object);
+
+        g_return_if_fail (dialog->priv != NULL);
+
+        G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gsm_properties_dialog_new (void)
+{
+        GObject *object;
+
+        object = g_object_new (GSM_TYPE_PROPERTIES_DIALOG,
+                               NULL);
+
+        return GTK_WIDGET (object);
+}
diff --git a/capplet/gsm-properties-dialog.h b/capplet/gsm-properties-dialog.h
new file mode 100644
index 0000000..df4915e
--- /dev/null
+++ b/capplet/gsm-properties-dialog.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __GSM_PROPERTIES_DIALOG_H
+#define __GSM_PROPERTIES_DIALOG_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GSM_TYPE_PROPERTIES_DIALOG         (gsm_properties_dialog_get_type ())
+#define GSM_PROPERTIES_DIALOG(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialog))
+#define GSM_PROPERTIES_DIALOG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogClass))
+#define GSM_IS_PROPERTIES_DIALOG(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_PROPERTIES_DIALOG))
+#define GSM_IS_PROPERTIES_DIALOG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_PROPERTIES_DIALOG))
+#define GSM_PROPERTIES_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogClass))
+
+typedef struct GsmPropertiesDialogPrivate GsmPropertiesDialogPrivate;
+
+typedef struct
+{
+        GtkDialog                   parent;
+        GsmPropertiesDialogPrivate *priv;
+} GsmPropertiesDialog;
+
+typedef struct
+{
+        GtkDialogClass   parent_class;
+} GsmPropertiesDialogClass;
+
+GType                  gsm_properties_dialog_get_type           (void);
+
+GtkWidget            * gsm_properties_dialog_new                (void);
+
+#define GSM_PROPERTIES_ICON_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR
+
+G_END_DECLS
+
+#endif /* __GSM_PROPERTIES_DIALOG_H */
diff --git a/capplet/gsp-app-manager.c b/capplet/gsp-app-manager.c
new file mode 100644
index 0000000..bcd6d40
--- /dev/null
+++ b/capplet/gsp-app-manager.c
@@ -0,0 +1,593 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2007, 2009 Vincent Untz.
+ * Copyright (C) 2008 Lucas Rocha.
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
+ *
+ * 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.
+ *
+ */
+
+#include <string.h>
+
+#include "gsm-util.h"
+#include "gsp-app.h"
+
+#include "gsp-app-manager.h"
+
+static GspAppManager *manager = NULL;
+
+typedef struct {
+        char         *dir;
+        int           index;
+        GFileMonitor *monitor;
+} GspXdgDir;
+
+struct _GspAppManagerPrivate {
+        GSList *apps;
+        GSList *dirs;
+};
+
+#define GSP_APP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSP_TYPE_APP_MANAGER, GspAppManagerPrivate))
+
+
+enum {
+        ADDED,
+        REMOVED,
+        LAST_SIGNAL
+};
+
+static guint gsp_app_manager_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE (GspAppManager, gsp_app_manager, G_TYPE_OBJECT)
+
+static void     gsp_app_manager_dispose      (GObject       *object);
+static void     gsp_app_manager_finalize     (GObject       *object);
+static void     _gsp_app_manager_app_unref   (GspApp        *app,
+                                              GspAppManager *manager);
+static void     _gsp_app_manager_app_removed (GspAppManager *manager,
+                                              GspApp        *app);
+
+static GspXdgDir *
+_gsp_xdg_dir_new (const char *dir,
+                  int         index)
+{
+        GspXdgDir *xdgdir;
+
+        xdgdir = g_slice_new (GspXdgDir);
+
+        xdgdir->dir = g_strdup (dir);
+        xdgdir->index = index;
+        xdgdir->monitor = NULL;
+
+        return xdgdir;
+}
+
+static void
+_gsp_xdg_dir_free (GspXdgDir *xdgdir)
+{
+        if (xdgdir->dir) {
+                g_free (xdgdir->dir);
+                xdgdir->dir = NULL;
+        }
+
+        if (xdgdir->monitor) {
+                g_file_monitor_cancel (xdgdir->monitor);
+                g_object_unref (xdgdir->monitor);
+                xdgdir->monitor = NULL;
+        }
+
+        g_slice_free (GspXdgDir, xdgdir);
+}
+
+static void
+gsp_app_manager_class_init (GspAppManagerClass *class)
+{
+        GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+        gobject_class->dispose  = gsp_app_manager_dispose;
+        gobject_class->finalize = gsp_app_manager_finalize;
+
+        gsp_app_manager_signals[ADDED] =
+                g_signal_new ("added",
+                              G_TYPE_FROM_CLASS (gobject_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GspAppManagerClass,
+                                               added),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+        gsp_app_manager_signals[REMOVED] =
+                g_signal_new ("removed",
+                              G_TYPE_FROM_CLASS (gobject_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GspAppManagerClass,
+                                               removed),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+        g_type_class_add_private (class, sizeof (GspAppManagerPrivate));
+}
+
+static void
+gsp_app_manager_init (GspAppManager *manager)
+{
+        manager->priv = GSP_APP_MANAGER_GET_PRIVATE (manager);
+
+        memset (manager->priv, 0, sizeof (GspAppManagerPrivate));
+}
+
+static void
+gsp_app_manager_dispose (GObject *object)
+{
+        GspAppManager *manager;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GSP_IS_APP_MANAGER (object));
+
+        manager = GSP_APP_MANAGER (object);
+
+        /* we unref GspApp objects in dispose since they might need to
+         * reference us during their dispose/finalize */
+        g_slist_foreach (manager->priv->apps,
+                         (GFunc) _gsp_app_manager_app_unref, manager);
+        g_slist_free (manager->priv->apps);
+        manager->priv->apps = NULL;
+
+        G_OBJECT_CLASS (gsp_app_manager_parent_class)->dispose (object);
+}
+
+static void
+gsp_app_manager_finalize (GObject *object)
+{
+        GspAppManager *manager;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GSP_IS_APP_MANAGER (object));
+
+        manager = GSP_APP_MANAGER (object);
+
+        g_slist_foreach (manager->priv->dirs,
+                         (GFunc) _gsp_xdg_dir_free, NULL);
+        g_slist_free (manager->priv->dirs);
+        manager->priv->dirs = NULL;
+
+        G_OBJECT_CLASS (gsp_app_manager_parent_class)->finalize (object);
+
+        manager = NULL;
+}
+
+static void
+_gsp_app_manager_emit_added (GspAppManager *manager,
+                             GspApp        *app)
+{
+        g_signal_emit (G_OBJECT (manager), gsp_app_manager_signals[ADDED],
+                       0, app);
+}
+
+static void
+_gsp_app_manager_emit_removed (GspAppManager *manager,
+                               GspApp        *app)
+{
+        g_signal_emit (G_OBJECT (manager), gsp_app_manager_signals[REMOVED],
+                       0, app);
+}
+
+/*
+ * Directories
+ */
+
+static int
+gsp_app_manager_get_dir_index (GspAppManager *manager,
+                               const char    *dir)
+{
+        GSList    *l;
+        GspXdgDir *xdgdir;
+
+        g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), -1);
+        g_return_val_if_fail (dir != NULL, -1);
+
+        for (l = manager->priv->dirs; l != NULL; l = l->next) {
+                xdgdir = l->data;
+                if (strcmp (dir, xdgdir->dir) == 0) {
+                        return xdgdir->index;
+                }
+        }
+
+        return -1;
+}
+
+const char *
+gsp_app_manager_get_dir (GspAppManager *manager,
+                         unsigned int   index)
+{
+        GSList    *l;
+        GspXdgDir *xdgdir;
+
+        g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL);
+
+        for (l = manager->priv->dirs; l != NULL; l = l->next) {
+                xdgdir = l->data;
+                if (index == xdgdir->index) {
+                        return xdgdir->dir;
+                }
+        }
+
+        return NULL;
+}
+
+static int
+_gsp_app_manager_find_dir_with_basename (GspAppManager *manager,
+                                         const char    *basename,
+                                         int            minimum_index)
+{
+        GSList    *l;
+        GspXdgDir *xdgdir;
+        char      *path;
+        GKeyFile  *keyfile;
+        int        result = -1;
+
+        path = NULL;
+        keyfile = g_key_file_new ();
+
+        for (l = manager->priv->dirs; l != NULL; l = l->next) {
+                xdgdir = l->data;
+
+                if (xdgdir->index <= minimum_index) {
+                        continue;
+                }
+
+                g_free (path);
+                path = g_build_filename (xdgdir->dir, basename, NULL);
+                if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
+                        continue;
+                }
+
+                if (!g_key_file_load_from_file (keyfile, path,
+                                                G_KEY_FILE_NONE, NULL)) {
+                        continue;
+                }
+
+                /* the file exists and is readable */
+                if (result == -1) {
+                        result = xdgdir->index;
+                } else {
+                        result = MIN (result, xdgdir->index);
+                }
+        }
+
+        g_key_file_free (keyfile);
+        g_free (path);
+
+        return result;
+}
+
+static void
+_gsp_app_manager_handle_delete (GspAppManager *manager,
+                                GspApp        *app,
+                                const char    *basename,
+                                int            index)
+{
+        unsigned int position;
+        unsigned int system_position;
+
+        position = gsp_app_get_xdg_position (app);
+        system_position = gsp_app_get_xdg_system_position (app);
+
+        if (system_position < index) {
+                /* it got deleted, but we don't even care about it */
+                return;
+        }
+
+        if (index < position) {
+                /* it got deleted, but in a position earlier than the current
+                 * one. This happens when the user file was changed and became
+                 * identical to the system file; in this case, the user file is
+                 * simply removed. */
+                 g_assert (index == 0);
+                 return;
+        }
+
+        if (position == index &&
+            (system_position == index || system_position == G_MAXUINT)) {
+                /* the file used by the user was deleted, and there's no other
+                 * file in system directories. So it really got deleted. */
+                _gsp_app_manager_app_removed (manager, app);
+                return;
+        }
+
+        if (system_position == index) {
+                /* then we know that position != index; we just hae to tell
+                 * GspApp if there's still a system directory containing this
+                 * basename */
+                int new_system;
+
+                new_system = _gsp_app_manager_find_dir_with_basename (manager,
+                                                                      basename,
+                                                                      index);
+                if (new_system < 0) {
+                        gsp_app_set_xdg_system_position (app, G_MAXUINT);
+                } else {
+                        gsp_app_set_xdg_system_position (app, new_system);
+                }
+
+                return;
+        }
+
+        if (position == index) {
+                /* then we know that system_position != G_MAXUINT; we need to
+                 * tell GspApp to change position to system_position */
+                const char *dir;
+
+                dir = gsp_app_manager_get_dir (manager, system_position);
+                if (dir) {
+                        char *path;
+
+                        path = g_build_filename (dir, basename, NULL);
+                        gsp_app_reload_at (app, path,
+                                           (unsigned int) system_position);
+                        g_free (path);
+                } else {
+                        _gsp_app_manager_app_removed (manager, app);
+                }
+
+                return;
+        }
+
+        g_assert_not_reached ();
+}
+
+static gboolean
+gsp_app_manager_xdg_dir_monitor (GFileMonitor      *monitor,
+                                 GFile             *child,
+                                 GFile             *other_file,
+                                 GFileMonitorEvent  flags,
+                                 gpointer           data)
+{
+        GspAppManager *manager;
+        GspApp        *old_app;
+        GspApp        *app;
+        GFile         *parent;
+        char          *basename;
+        char          *dir;
+        char          *path;
+        int            index;
+
+        manager = GSP_APP_MANAGER (data);
+
+        basename = g_file_get_basename (child);
+        if (!g_str_has_suffix (basename, ".desktop")) {
+                /* not a desktop file, we can ignore */
+                g_free (basename);
+                return TRUE;
+        }
+        old_app = gsp_app_manager_find_app_with_basename (manager, basename);
+
+        parent = g_file_get_parent (child);
+        dir = g_file_get_path (parent);
+        g_object_unref (parent);
+
+        index = gsp_app_manager_get_dir_index (manager, dir);
+        if (index < 0) {
+                /* not a directory we know; should never happen, though */
+                g_free (dir);
+                return TRUE;
+        }
+
+        path = g_file_get_path (child);
+
+        switch (flags) {
+        case G_FILE_MONITOR_EVENT_CHANGED:
+        case G_FILE_MONITOR_EVENT_CREATED:
+                /* we just do as if it was a new file: GspApp is clever enough
+                 * to do the right thing */
+                app = gsp_app_new (path, (unsigned int) index);
+
+                /* we didn't have this app before, so add it */
+                if (old_app == NULL && app != NULL) {
+                        gsp_app_manager_add (manager, app);
+                        g_object_unref (app);
+                }
+                /* else: it was just updated, GspApp took care of
+                 * sending the event */
+                break;
+        case G_FILE_MONITOR_EVENT_DELETED:
+                if (!old_app) {
+                        /* it got deleted, but we don't know about it, so
+                         * nothing to do */
+                        break;
+                }
+
+                _gsp_app_manager_handle_delete (manager, old_app,
+                                                basename, index);
+                break;
+        default:
+                break;
+        }
+
+        g_free (path);
+        g_free (dir);
+        g_free (basename);
+
+        return TRUE;
+}
+
+/*
+ * Initialization
+ */
+
+static void
+_gsp_app_manager_fill_from_dir (GspAppManager *manager,
+                                GspXdgDir     *xdgdir)
+{
+        GFile      *file;
+        GDir       *dir;
+        const char *name;
+
+        file = g_file_new_for_path (xdgdir->dir);
+        xdgdir->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
+                                                    NULL, NULL);
+        g_object_unref (file);
+
+        if (xdgdir->monitor) {
+                g_signal_connect (xdgdir->monitor, "changed",
+                                  G_CALLBACK (gsp_app_manager_xdg_dir_monitor),
+                                  manager);
+        }
+
+        dir = g_dir_open (xdgdir->dir, 0, NULL);
+        if (!dir) {
+                return;
+        }
+
+        while ((name = g_dir_read_name (dir))) {
+                GspApp *app;
+                char   *desktop_file_path;
+
+                if (!g_str_has_suffix (name, ".desktop")) {
+                        continue;
+                }
+
+                desktop_file_path = g_build_filename (xdgdir->dir, name, NULL);
+                app = gsp_app_new (desktop_file_path, xdgdir->index);
+
+                if (app != NULL) {
+                        gsp_app_manager_add (manager, app);
+                        g_object_unref (app);
+                }
+
+                g_free (desktop_file_path);
+        }
+
+        g_dir_close (dir);
+}
+
+void
+gsp_app_manager_fill (GspAppManager *manager)
+{
+        char **autostart_dirs;
+        int    i;
+
+        if (manager->priv->apps != NULL)
+                return;
+
+        autostart_dirs = gsm_util_get_autostart_dirs ();
+        /* we always assume that the first directory is the user one */
+        g_assert (g_str_has_prefix (autostart_dirs[0],
+                                    g_get_user_config_dir ()));
+
+        for (i = 0; autostart_dirs[i] != NULL; i++) {
+                GspXdgDir *xdgdir;
+
+                if (gsp_app_manager_get_dir_index (manager,
+                                                   autostart_dirs[i]) >= 0) {
+                        continue;
+                }
+
+                xdgdir = _gsp_xdg_dir_new (autostart_dirs[i], i);
+                manager->priv->dirs = g_slist_prepend (manager->priv->dirs,
+                                                       xdgdir);
+
+                _gsp_app_manager_fill_from_dir (manager, xdgdir);
+        }
+
+        g_strfreev (autostart_dirs);
+}
+
+/*
+ * App handling
+ */
+
+static void
+_gsp_app_manager_app_unref (GspApp        *app,
+                            GspAppManager *manager)
+{
+        g_signal_handlers_disconnect_by_func (app,
+                                              _gsp_app_manager_app_removed,
+                                              manager);
+        g_object_unref (app);
+}
+
+static void
+_gsp_app_manager_app_removed (GspAppManager *manager,
+                              GspApp        *app)
+{
+        _gsp_app_manager_emit_removed (manager, app);
+        manager->priv->apps = g_slist_remove (manager->priv->apps, app);
+        _gsp_app_manager_app_unref (app, manager);
+}
+
+void
+gsp_app_manager_add (GspAppManager *manager,
+                     GspApp        *app)
+{
+        g_return_if_fail (GSP_IS_APP_MANAGER (manager));
+        g_return_if_fail (GSP_IS_APP (app));
+
+        manager->priv->apps = g_slist_prepend (manager->priv->apps,
+                                               g_object_ref (app));
+        g_signal_connect_swapped (app, "removed",
+                                  G_CALLBACK (_gsp_app_manager_app_removed),
+                                  manager);
+        _gsp_app_manager_emit_added (manager, app);
+}
+
+GspApp *
+gsp_app_manager_find_app_with_basename (GspAppManager *manager,
+                                        const char    *basename)
+{
+        GSList *l;
+        GspApp *app;
+
+        g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL);
+        g_return_val_if_fail (basename != NULL, NULL);
+
+        for (l = manager->priv->apps; l != NULL; l = l->next) {
+                app = GSP_APP (l->data);
+                if (strcmp (basename, gsp_app_get_basename (app)) == 0)
+                        return app;
+        }
+
+        return NULL;
+}
+
+/*
+ * Singleton
+ */
+
+GspAppManager *
+gsp_app_manager_get (void)
+{
+        if (manager == NULL) {
+                manager = g_object_new (GSP_TYPE_APP_MANAGER, NULL);
+                return manager;
+        } else {
+                return g_object_ref (manager);
+        }
+}
+
+GSList *
+gsp_app_manager_get_apps (GspAppManager *manager)
+{
+        g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL);
+
+        return g_slist_copy (manager->priv->apps);
+}
diff --git a/capplet/gsp-app-manager.h b/capplet/gsp-app-manager.h
new file mode 100644
index 0000000..777f8d6
--- /dev/null
+++ b/capplet/gsp-app-manager.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2007, 2009 Vincent Untz.
+ * Copyright (C) 2008 Lucas Rocha.
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __GSP_APP_MANAGER_H
+#define __GSP_APP_MANAGER_H
+
+#include <glib-object.h>
+
+#include <gsp-app.h>
+
+G_BEGIN_DECLS
+
+#define GSP_TYPE_APP_MANAGER            (gsp_app_manager_get_type ())
+#define GSP_APP_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSP_TYPE_APP_MANAGER, GspAppManager))
+#define GSP_APP_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GSP_TYPE_APP_MANAGER, GspAppManagerClass))
+#define GSP_IS_APP_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSP_TYPE_APP_MANAGER))
+#define GSP_IS_APP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSP_TYPE_APP_MANAGER))
+#define GSP_APP_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GSP_TYPE_APP_MANAGER, GspAppManagerClass))
+
+typedef struct _GspAppManager        GspAppManager;
+typedef struct _GspAppManagerClass   GspAppManagerClass;
+
+typedef struct _GspAppManagerPrivate GspAppManagerPrivate;
+
+struct _GspAppManagerClass
+{
+        GObjectClass parent_class;
+
+        void (* added)   (GspAppManager *manager,
+                          GspApp        *app);
+        void (* removed) (GspAppManager *manager,
+                          GspApp        *app);
+};
+
+struct _GspAppManager
+{
+        GObject parent_instance;
+
+        GspAppManagerPrivate *priv;
+};
+
+GType           gsp_app_manager_get_type               (void);
+
+GspAppManager  *gsp_app_manager_get                    (void);
+
+void            gsp_app_manager_fill                   (GspAppManager *manager);
+
+GSList         *gsp_app_manager_get_apps               (GspAppManager *manager);
+
+GspApp         *gsp_app_manager_find_app_with_basename (GspAppManager *manager,
+                                                        const char    *basename);
+
+const char     *gsp_app_manager_get_dir                (GspAppManager *manager,
+                                                        unsigned int   index);
+
+void            gsp_app_manager_add                    (GspAppManager *manager,
+                                                        GspApp        *app);
+
+G_END_DECLS
+
+#endif /* __GSP_APP_MANAGER_H */
diff --git a/capplet/gsp-app.c b/capplet/gsp-app.c
new file mode 100644
index 0000000..c92b8da
--- /dev/null
+++ b/capplet/gsp-app.c
@@ -0,0 +1,1102 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2007, 2009 Vincent Untz.
+ * Copyright (C) 2008 Lucas Rocha.
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
+ *
+ * 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <sys/stat.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include "gsm-app-dialog.h"
+#include "gsm-properties-dialog.h"
+#include "gsm-util.h"
+#include "gsp-app-manager.h"
+#include "gsp-keyfile.h"
+
+#include "gsp-app.h"
+
+#define GSP_APP_SAVE_DELAY 2
+
+#define GSP_ASP_SAVE_MASK_HIDDEN     0x0001
+#define GSP_ASP_SAVE_MASK_ENABLED    0x0002
+#define GSP_ASP_SAVE_MASK_NAME       0x0004
+#define GSP_ASP_SAVE_MASK_EXEC       0x0008
+#define GSP_ASP_SAVE_MASK_COMMENT    0x0010
+#define GSP_ASP_SAVE_MASK_NO_DISPLAY 0x0020
+#define GSP_ASP_SAVE_MASK_ALL        0xffff
+
+struct _GspAppPrivate {
+        char         *basename;
+        char         *path;
+
+        gboolean      hidden;
+        gboolean      no_display;
+        gboolean      enabled;
+        gboolean      shown;
+
+        char         *name;
+        char         *exec;
+        char         *comment;
+        char         *icon;
+
+        GIcon        *gicon;
+        char         *description;
+
+        /* position of the directory in the XDG environment variable */
+        unsigned int  xdg_position;
+        /* position of the first system directory in the XDG env var containing
+         * this autostart app too (G_MAXUINT means none) */
+        unsigned int  xdg_system_position;
+
+        unsigned int  save_timeout;
+        /* mask of what has changed */
+        unsigned int  save_mask;
+        /* path that contains the original file that needs to be saved */
+        char         *old_system_path;
+        /* after writing to file, we skip the next file monitor event of type
+         * CHANGED */
+        gboolean      skip_next_monitor_event;
+};
+
+#define GSP_APP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSP_TYPE_APP, GspAppPrivate))
+
+
+enum {
+        CHANGED,
+        REMOVED,
+        LAST_SIGNAL
+};
+
+static guint gsp_app_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE (GspApp, gsp_app, G_TYPE_OBJECT)
+
+static void     gsp_app_dispose  (GObject *object);
+static void     gsp_app_finalize (GObject *object);
+static gboolean _gsp_app_save    (gpointer data);
+
+
+static gboolean
+_gsp_str_equal (const char *a,
+                const char *b)
+{
+        if (g_strcmp0 (a, b) == 0) {
+                return TRUE;
+        }
+
+        if (a && !b && a[0] == '\0') {
+                return TRUE;
+        }
+
+        if (b && !a && b[0] == '\0') {
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+
+static void
+gsp_app_class_init (GspAppClass *class)
+{
+        GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+        gobject_class->dispose  = gsp_app_dispose;
+        gobject_class->finalize = gsp_app_finalize;
+
+        gsp_app_signals[CHANGED] =
+                g_signal_new ("changed",
+                              G_TYPE_FROM_CLASS (gobject_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GspAppClass,
+                                               changed),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        gsp_app_signals[REMOVED] =
+                g_signal_new ("removed",
+                              G_TYPE_FROM_CLASS (gobject_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GspAppClass,
+                                               removed),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        g_type_class_add_private (class, sizeof (GspAppPrivate));
+}
+
+static void
+gsp_app_init (GspApp *app)
+{
+        app->priv = GSP_APP_GET_PRIVATE (app);
+
+        memset (app->priv, 0, sizeof (GspAppPrivate));
+        app->priv->xdg_position        = G_MAXUINT;
+        app->priv->xdg_system_position = G_MAXUINT;
+}
+
+static void
+_gsp_app_free_reusable_data (GspApp *app)
+{
+        if (app->priv->path) {
+                g_free (app->priv->path);
+                app->priv->path = NULL;
+        }
+
+        if (app->priv->name) {
+                g_free (app->priv->name);
+                app->priv->name = NULL;
+        }
+
+        if (app->priv->exec) {
+                g_free (app->priv->exec);
+                app->priv->exec = NULL;
+        }
+
+        if (app->priv->comment) {
+                g_free (app->priv->comment);
+                app->priv->comment = NULL;
+        }
+
+        if (app->priv->icon) {
+                g_free (app->priv->icon);
+                app->priv->icon = NULL;
+        }
+
+        if (app->priv->gicon) {
+                g_object_unref (app->priv->gicon);
+                app->priv->gicon = NULL;
+        }
+
+        if (app->priv->description) {
+                g_free (app->priv->description);
+                app->priv->description = NULL;
+        }
+
+        if (app->priv->old_system_path) {
+                g_free (app->priv->old_system_path);
+                app->priv->old_system_path = NULL;
+        }
+}
+
+static void
+gsp_app_dispose (GObject *object)
+{
+        GspApp *app;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GSP_IS_APP (object));
+
+        app = GSP_APP (object);
+
+        /* we save in dispose since we might need to reference GspAppManager */
+        if (app->priv->save_timeout) {
+                g_source_remove (app->priv->save_timeout);
+                app->priv->save_timeout = 0;
+
+                /* save now */
+                _gsp_app_save (app);
+        }
+
+        G_OBJECT_CLASS (gsp_app_parent_class)->dispose (object);
+}
+
+static void
+gsp_app_finalize (GObject *object)
+{
+        GspApp *app;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GSP_IS_APP (object));
+
+        app = GSP_APP (object);
+
+        if (app->priv->basename) {
+                g_free (app->priv->basename);
+                app->priv->basename = NULL;
+        }
+
+        _gsp_app_free_reusable_data (app);
+
+        G_OBJECT_CLASS (gsp_app_parent_class)->finalize (object);
+}
+
+static void
+_gsp_app_emit_changed (GspApp *app)
+{
+        g_signal_emit (G_OBJECT (app), gsp_app_signals[CHANGED], 0);
+}
+
+static void
+_gsp_app_emit_removed (GspApp *app)
+{
+        g_signal_emit (G_OBJECT (app), gsp_app_signals[REMOVED], 0);
+}
+
+static void
+_gsp_app_update_description (GspApp *app)
+{
+        const char *primary;
+        const char *secondary;
+
+        if (!gsm_util_text_is_blank (app->priv->name)) {
+                primary = app->priv->name;
+        } else if (!gsm_util_text_is_blank (app->priv->exec)) {
+                primary = app->priv->exec;
+        } else {
+                primary = _("No name");
+        }
+
+        if (!gsm_util_text_is_blank (app->priv->comment)) {
+                secondary = app->priv->comment;
+        } else {
+                secondary = _("No description");
+        }
+
+        g_free (app->priv->description);
+        app->priv->description = g_markup_printf_escaped ("<b>%s</b>\n%s",
+                                                          primary,
+                                                          secondary);
+}
+
+/*
+ * Saving
+ */
+
+static void
+_gsp_ensure_user_autostart_dir (void)
+{
+        char *dir;
+
+        dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL);
+        g_mkdir_with_parents (dir, S_IRWXU);
+
+        g_free (dir);
+}
+
+static gboolean
+_gsp_app_user_equal_system (GspApp  *app,
+                            char   **system_path)
+{
+        GspAppManager *manager;
+        const char    *system_dir;
+        char          *path;
+        char          *str;
+        GKeyFile      *keyfile;
+
+        manager = gsp_app_manager_get ();
+        system_dir = gsp_app_manager_get_dir (manager,
+                                              app->priv->xdg_system_position);
+        g_object_unref (manager);
+        if (!system_dir) {
+                return FALSE;
+        }
+
+        path = g_build_filename (system_dir, app->priv->basename, NULL);
+
+        keyfile = g_key_file_new ();
+        if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
+                g_free (path);
+                g_key_file_free (keyfile);
+                return FALSE;
+        }
+
+        if (gsp_key_file_get_boolean (keyfile,
+                                      G_KEY_FILE_DESKTOP_KEY_HIDDEN,
+                                      FALSE) != app->priv->hidden ||
+            gsp_key_file_get_boolean (keyfile,
+                                      GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
+                                      TRUE) != app->priv->enabled ||
+            gsp_key_file_get_shown (keyfile,
+                                    gsm_util_get_current_desktop ()) != app->priv->shown) {
+                g_free (path);
+                g_key_file_free (keyfile);
+                return FALSE;
+        }
+
+        if (gsp_key_file_get_boolean (keyfile,
+                                      G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
+                                      FALSE) != app->priv->no_display) {
+                g_free (path);
+                g_key_file_free (keyfile);
+                return FALSE;
+        }
+
+        str = gsp_key_file_get_locale_string (keyfile,
+                                              G_KEY_FILE_DESKTOP_KEY_NAME);
+        if (!_gsp_str_equal (str, app->priv->name)) {
+                g_free (str);
+                g_free (path);
+                g_key_file_free (keyfile);
+                return FALSE;
+        }
+        g_free (str);
+
+        str = gsp_key_file_get_locale_string (keyfile,
+                                              G_KEY_FILE_DESKTOP_KEY_COMMENT);
+        if (!_gsp_str_equal (str, app->priv->comment)) {
+                g_free (str);
+                g_free (path);
+                g_key_file_free (keyfile);
+                return FALSE;
+        }
+        g_free (str);
+
+        str = gsp_key_file_get_string (keyfile,
+                                       G_KEY_FILE_DESKTOP_KEY_EXEC);
+        if (!_gsp_str_equal (str, app->priv->exec)) {
+                g_free (str);
+                g_free (path);
+                g_key_file_free (keyfile);
+                return FALSE;
+        }
+        g_free (str);
+
+        str = gsp_key_file_get_locale_string (keyfile,
+                                              G_KEY_FILE_DESKTOP_KEY_ICON);
+        if (!_gsp_str_equal (str, app->priv->icon)) {
+                g_free (str);
+                g_free (path);
+                g_key_file_free (keyfile);
+                return FALSE;
+        }
+        g_free (str);
+
+        g_key_file_free (keyfile);
+
+        *system_path = path;
+
+        return TRUE;
+}
+
+static inline void
+_gsp_app_save_done_success (GspApp *app)
+{
+        app->priv->save_mask = 0;
+
+        if (app->priv->old_system_path) {
+                g_free (app->priv->old_system_path);
+                app->priv->old_system_path = NULL;
+        }
+}
+
+static gboolean
+_gsp_app_save (gpointer data)
+{
+        GspApp   *app;
+        char     *use_path;
+        GKeyFile *keyfile;
+        GError   *error;
+
+        app = GSP_APP (data);
+
+        /* first check if removing the data from the user dir and using the
+         * data from the system dir is enough -- this helps us keep clean the
+         * user config dir by removing unneeded files */
+        if (_gsp_app_user_equal_system (app, &use_path)) {
+                if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) {
+                        g_remove (app->priv->path);
+                }
+
+                g_free (app->priv->path);
+                app->priv->path = use_path;
+
+                app->priv->xdg_position = app->priv->xdg_system_position;
+
+                _gsp_app_save_done_success (app);
+                return FALSE;
+        }
+
+        if (app->priv->old_system_path)
+                use_path = app->priv->old_system_path;
+        else
+                use_path = app->priv->path;
+
+        keyfile = g_key_file_new ();
+
+        error = NULL;
+        g_key_file_load_from_file (keyfile, use_path,
+                                   G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS,
+                                   &error);
+
+        if (error) {
+                g_error_free (error);
+                gsp_key_file_populate (keyfile);
+        }
+
+        if (app->priv->save_mask & GSP_ASP_SAVE_MASK_HIDDEN) {
+                gsp_key_file_set_boolean (keyfile,
+                                          G_KEY_FILE_DESKTOP_KEY_HIDDEN,
+                                          app->priv->hidden);
+        }
+
+        if (app->priv->save_mask & GSP_ASP_SAVE_MASK_NO_DISPLAY) {
+                gsp_key_file_set_boolean (keyfile,
+                                          G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
+                                          app->priv->no_display);
+        }
+
+        if (app->priv->save_mask & GSP_ASP_SAVE_MASK_ENABLED) {
+                gsp_key_file_set_boolean (keyfile,
+                                          GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
+                                          app->priv->enabled);
+        }
+
+        if (app->priv->save_mask & GSP_ASP_SAVE_MASK_NAME) {
+                gsp_key_file_set_locale_string (keyfile,
+                                                G_KEY_FILE_DESKTOP_KEY_NAME,
+                                                app->priv->name);
+                gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_NAME);
+        }
+
+        if (app->priv->save_mask & GSP_ASP_SAVE_MASK_COMMENT) {
+                gsp_key_file_set_locale_string (keyfile,
+                                                G_KEY_FILE_DESKTOP_KEY_COMMENT,
+                                                app->priv->comment);
+                gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_COMMENT);
+        }
+
+        if (app->priv->save_mask & GSP_ASP_SAVE_MASK_EXEC) {
+                gsp_key_file_set_string (keyfile,
+                                         G_KEY_FILE_DESKTOP_KEY_EXEC,
+                                         app->priv->exec);
+        }
+
+        _gsp_ensure_user_autostart_dir ();
+        if (gsp_key_file_to_file (keyfile, app->priv->path, NULL)) {
+                app->priv->skip_next_monitor_event = TRUE;
+                _gsp_app_save_done_success (app);
+        } else {
+                g_warning ("Could not save %s file", app->priv->path);
+        }
+
+        g_key_file_free (keyfile);
+
+        app->priv->save_timeout = 0;
+        return FALSE;
+}
+
+static void
+_gsp_app_queue_save (GspApp *app)
+{
+        if (app->priv->save_timeout) {
+                g_source_remove (app->priv->save_timeout);
+                app->priv->save_timeout = 0;
+        }
+
+        /* if the file was not in the user directory, then we'll create a copy
+         * there */
+        if (app->priv->xdg_position != 0) {
+                app->priv->xdg_position = 0;
+
+                if (app->priv->old_system_path == NULL) {
+                        app->priv->old_system_path = app->priv->path;
+                        /* if old_system_path was not NULL, then it means we
+                         * tried to save and we failed; in that case, we want
+                         * to try again and use the old file as a basis again */
+                }
+
+                app->priv->path = g_build_filename (g_get_user_config_dir (),
+                                                    "autostart",
+                                                    app->priv->basename, NULL);
+        }
+
+        app->priv->save_timeout = g_timeout_add_seconds (GSP_APP_SAVE_DELAY,
+                                                         _gsp_app_save,
+                                                         app);
+}
+
+/*
+ * Accessors
+ */
+
+const char *
+gsp_app_get_basename (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+        return app->priv->basename;
+}
+
+const char *
+gsp_app_get_path (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+        return app->priv->path;
+}
+
+gboolean
+gsp_app_get_hidden (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), FALSE);
+
+        return app->priv->hidden;
+}
+
+gboolean
+gsp_app_get_display (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), FALSE);
+
+        return !app->priv->no_display;
+}
+
+gboolean
+gsp_app_get_enabled (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), FALSE);
+
+        return app->priv->enabled;
+}
+
+void
+gsp_app_set_enabled (GspApp   *app,
+                     gboolean  enabled)
+{
+        g_return_if_fail (GSP_IS_APP (app));
+
+        if (enabled == app->priv->enabled) {
+                return;
+        }
+
+        app->priv->enabled = enabled;
+        app->priv->save_mask |= GSP_ASP_SAVE_MASK_ENABLED;
+
+        _gsp_app_queue_save (app);
+        _gsp_app_emit_changed (app);
+}
+
+gboolean
+gsp_app_get_shown (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), FALSE);
+
+        return app->priv->shown;
+}
+
+const char *
+gsp_app_get_name (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+        return app->priv->name;
+}
+
+const char *
+gsp_app_get_exec (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+        return app->priv->exec;
+}
+
+const char *
+gsp_app_get_comment (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+        return app->priv->comment;
+}
+
+GIcon *
+gsp_app_get_icon (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+        if (app->priv->gicon) {
+                return g_object_ref (app->priv->gicon);
+        } else {
+                return NULL;
+        }
+}
+
+unsigned int
+gsp_app_get_xdg_position (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT);
+
+        return app->priv->xdg_position;
+}
+
+unsigned int
+gsp_app_get_xdg_system_position (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT);
+
+        return app->priv->xdg_system_position;
+}
+
+void
+gsp_app_set_xdg_system_position (GspApp       *app,
+                                 unsigned int  position)
+{
+        g_return_if_fail (GSP_IS_APP (app));
+
+        app->priv->xdg_system_position = position;
+}
+
+const char *
+gsp_app_get_description (GspApp *app)
+{
+        g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+        return app->priv->description;
+}
+
+/*
+ * High-level edition
+ */
+
+void
+gsp_app_update (GspApp     *app,
+                const char *name,
+                const char *comment,
+                const char *exec)
+{
+        gboolean    changed;
+
+        g_return_if_fail (GSP_IS_APP (app));
+
+        changed = FALSE;
+
+        if (!_gsp_str_equal (name, app->priv->name)) {
+                changed = TRUE;
+                g_free (app->priv->name);
+                app->priv->name = g_strdup (name);
+                app->priv->save_mask |= GSP_ASP_SAVE_MASK_NAME;
+        }
+
+        if (!_gsp_str_equal (comment, app->priv->comment)) {
+                changed = TRUE;
+                g_free (app->priv->comment);
+                app->priv->comment = g_strdup (comment);
+                app->priv->save_mask |= GSP_ASP_SAVE_MASK_COMMENT;
+        }
+
+        if (changed) {
+                _gsp_app_update_description (app);
+        }
+
+        if (!_gsp_str_equal (exec, app->priv->exec)) {
+                changed = TRUE;
+                g_free (app->priv->exec);
+                app->priv->exec = g_strdup (exec);
+                app->priv->save_mask |= GSP_ASP_SAVE_MASK_EXEC;
+        }
+
+        if (changed) {
+                _gsp_app_queue_save (app);
+                _gsp_app_emit_changed (app);
+        }
+}
+
+void
+gsp_app_delete (GspApp *app)
+{
+        g_return_if_fail (GSP_IS_APP (app));
+
+        if (app->priv->xdg_position == 0 &&
+            app->priv->xdg_system_position == G_MAXUINT) {
+                /* exists in user directory only */
+                if (app->priv->save_timeout) {
+                        g_source_remove (app->priv->save_timeout);
+                        app->priv->save_timeout = 0;
+                }
+
+                if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) {
+                        g_remove (app->priv->path);
+                }
+
+                /* for extra safety */
+                app->priv->hidden = TRUE;
+                app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
+
+                _gsp_app_emit_removed (app);
+        } else {
+                /* also exists in system directory, so we have to keep a file
+                 * in the user directory */
+                app->priv->hidden = TRUE;
+                app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
+
+                _gsp_app_queue_save (app);
+                _gsp_app_emit_changed (app);
+        }
+}
+
+/*
+ * New autostart app
+ */
+
+void
+gsp_app_reload_at (GspApp       *app,
+                   const char   *path,
+                   unsigned int  xdg_position)
+{
+        g_return_if_fail (GSP_IS_APP (app));
+
+        app->priv->xdg_position = G_MAXUINT;
+        gsp_app_new (path, xdg_position);
+}
+
+GspApp *
+gsp_app_new (const char   *path,
+             unsigned int  xdg_position)
+{
+        GspAppManager *manager;
+        GspApp        *app;
+        GKeyFile      *keyfile;
+        char          *basename;
+        gboolean       new;
+
+        basename = g_path_get_basename (path);
+
+        manager = gsp_app_manager_get ();
+        app = gsp_app_manager_find_app_with_basename (manager, basename);
+        g_object_unref (manager);
+
+        new = (app == NULL);
+
+        if (!new) {
+                if (app->priv->xdg_position == xdg_position) {
+                        if (app->priv->skip_next_monitor_event) {
+                                app->priv->skip_next_monitor_event = FALSE;
+                                return NULL;
+                        }
+                        /* else: the file got changed but not by us, we'll
+                         * update our data from disk */
+                }
+
+                if (app->priv->xdg_position < xdg_position ||
+                    app->priv->save_timeout != 0) {
+                        /* we don't really care about this file, since we
+                         * already have something with a higher priority, or
+                         * we're going to write something in the user config
+                         * anyway.
+                         * Note: xdg_position >= 1 so it's a system dir */
+                        app->priv->xdg_system_position = MIN (xdg_position,
+                                                              app->priv->xdg_system_position);
+                        return NULL;
+                }
+        }
+
+        keyfile = g_key_file_new ();
+        if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
+                g_key_file_free (keyfile);
+                g_free (basename);
+                return NULL;
+        }
+
+        if (new) {
+                app = g_object_new (GSP_TYPE_APP, NULL);
+                app->priv->basename = basename;
+        } else {
+                g_free (basename);
+                _gsp_app_free_reusable_data (app);
+        }
+
+        app->priv->path = g_strdup (path);
+
+        app->priv->hidden = gsp_key_file_get_boolean (keyfile,
+                                                      G_KEY_FILE_DESKTOP_KEY_HIDDEN,
+                                                      FALSE);
+        app->priv->no_display = gsp_key_file_get_boolean (keyfile,
+                                                          G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
+                                                          FALSE);
+        app->priv->enabled = gsp_key_file_get_boolean (keyfile,
+                                                       GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
+                                                       TRUE);
+        app->priv->shown = gsp_key_file_get_shown (keyfile,
+                                                   gsm_util_get_current_desktop ());
+
+        app->priv->name = gsp_key_file_get_locale_string (keyfile,
+                                                          G_KEY_FILE_DESKTOP_KEY_NAME);
+        app->priv->exec = gsp_key_file_get_string (keyfile,
+                                                   G_KEY_FILE_DESKTOP_KEY_EXEC);
+        app->priv->comment = gsp_key_file_get_locale_string (keyfile,
+                                                             G_KEY_FILE_DESKTOP_KEY_COMMENT);
+
+        if (gsm_util_text_is_blank (app->priv->name)) {
+                g_free (app->priv->name);
+                app->priv->name = g_strdup (app->priv->exec);
+        }
+
+        app->priv->icon = gsp_key_file_get_locale_string (keyfile,
+                                                          G_KEY_FILE_DESKTOP_KEY_ICON);
+
+        if (app->priv->icon) {
+                /* look at icon and see if it's a themed icon or not */
+                if (g_path_is_absolute (app->priv->icon)) {
+                        GFile *iconfile;
+
+                        iconfile = g_file_new_for_path (app->priv->icon);
+                        app->priv->gicon = g_file_icon_new (iconfile);
+                        g_object_unref (iconfile);
+                } else {
+                        app->priv->gicon = g_themed_icon_new (app->priv->icon);
+                }
+        } else {
+                app->priv->gicon = NULL;
+        }
+
+        g_key_file_free (keyfile);
+
+        _gsp_app_update_description (app);
+
+        if (xdg_position > 0) {
+                g_assert (xdg_position <= app->priv->xdg_system_position);
+                app->priv->xdg_system_position = xdg_position;
+        }
+        /* else we keep the old value (which is G_MAXUINT if it wasn't set) */
+        app->priv->xdg_position = xdg_position;
+
+        g_assert (!new || app->priv->save_timeout == 0);
+        app->priv->save_timeout = 0;
+        app->priv->old_system_path = NULL;
+        app->priv->skip_next_monitor_event = FALSE;
+
+        if (!new) {
+                _gsp_app_emit_changed (app);
+        }
+
+        return app;
+}
+
+static char *
+_gsp_find_free_basename (const char *suggested_basename)
+{
+        GspAppManager *manager;
+        char          *base_path;
+        char          *filename;
+        char          *basename;
+        int            i;
+
+        if (g_str_has_suffix (suggested_basename, ".desktop")) {
+                char *basename_no_ext;
+
+                basename_no_ext = g_strndup (suggested_basename,
+                                             strlen (suggested_basename) - strlen (".desktop"));
+                base_path = g_build_filename (g_get_user_config_dir (),
+                                              "autostart",
+                                              basename_no_ext, NULL);
+                g_free (basename_no_ext);
+        } else {
+                base_path = g_build_filename (g_get_user_config_dir (),
+                                              "autostart",
+                                              suggested_basename, NULL);
+        }
+
+        filename = g_strdup_printf ("%s.desktop", base_path);
+        basename = g_path_get_basename (filename);
+
+        manager = gsp_app_manager_get ();
+
+        i = 1;
+#define _GSP_FIND_MAX_TRY 10000
+        while (gsp_app_manager_find_app_with_basename (manager,
+                                                       basename) != NULL &&
+               g_file_test (filename, G_FILE_TEST_EXISTS) &&
+               i < _GSP_FIND_MAX_TRY) {
+                g_free (filename);
+                g_free (basename);
+
+                filename = g_strdup_printf ("%s-%d.desktop", base_path, i);
+                basename = g_path_get_basename (filename);
+
+                i++;
+        }
+
+        g_object_unref (manager);
+
+        g_free (base_path);
+        g_free (filename);
+
+        if (i == _GSP_FIND_MAX_TRY) {
+                g_free (basename);
+                return NULL;
+        }
+
+        return basename;
+}
+
+void
+gsp_app_create (const char *name,
+                const char *comment,
+                const char *exec)
+{
+        GspAppManager  *manager;
+        GspApp         *app;
+        char           *basename;
+        char          **argv;
+        int             argc;
+
+        g_return_if_fail (!gsm_util_text_is_blank (exec));
+
+        if (!g_shell_parse_argv (exec, &argc, &argv, NULL)) {
+                return;
+        }
+
+        basename = _gsp_find_free_basename (argv[0]);
+        g_strfreev (argv);
+        if (basename == NULL) {
+                return;
+        }
+
+        app = g_object_new (GSP_TYPE_APP, NULL);
+
+        app->priv->basename = basename;
+        app->priv->path = g_build_filename (g_get_user_config_dir (),
+                                            "autostart",
+                                            app->priv->basename, NULL);
+
+        app->priv->hidden = FALSE;
+        app->priv->no_display = FALSE;
+        app->priv->enabled = TRUE;
+        app->priv->shown = TRUE;
+
+        if (!gsm_util_text_is_blank (name)) {
+                app->priv->name = g_strdup (name);
+        } else {
+                app->priv->name = g_strdup (exec);
+        }
+        app->priv->exec = g_strdup (exec);
+        app->priv->comment = g_strdup (comment);
+        app->priv->icon = NULL;
+
+        app->priv->gicon = NULL;
+        _gsp_app_update_description (app);
+
+        /* by definition */
+        app->priv->xdg_position = 0;
+        app->priv->xdg_system_position = G_MAXUINT;
+
+        app->priv->save_timeout = 0;
+        app->priv->save_mask |= GSP_ASP_SAVE_MASK_ALL;
+        app->priv->old_system_path = NULL;
+        app->priv->skip_next_monitor_event = FALSE;
+
+        _gsp_app_queue_save (app);
+
+        manager = gsp_app_manager_get ();
+        gsp_app_manager_add (manager, app);
+        g_object_unref (app);
+        g_object_unref (manager);
+}
+
+gboolean
+gsp_app_copy_desktop_file (const char *uri)
+{
+        GspAppManager *manager;
+        GspApp        *app;
+        GFile         *src_file;
+        char          *src_basename;
+        char          *dst_basename;
+        char          *dst_path;
+        GFile         *dst_file;
+        gboolean       changed;
+
+        g_return_val_if_fail (uri != NULL, FALSE);
+
+        src_file = g_file_new_for_uri (uri);
+        src_basename = g_file_get_basename (src_file);
+
+        if (src_basename == NULL) {
+                g_object_unref (src_file);
+                return FALSE;
+        }
+
+        dst_basename = _gsp_find_free_basename (src_basename);
+        g_free (src_basename);
+
+        if (dst_basename == NULL) {
+                g_object_unref (src_file);
+                return FALSE;
+        }
+
+        dst_path = g_build_filename (g_get_user_config_dir (),
+                                     "autostart",
+                                     dst_basename, NULL);
+        g_free (dst_basename);
+
+        dst_file = g_file_new_for_path (dst_path);
+
+        _gsp_ensure_user_autostart_dir ();
+        if (!g_file_copy (src_file, dst_file, G_FILE_COPY_NONE,
+                          NULL, NULL, NULL, NULL)) {
+                g_object_unref (src_file);
+                g_object_unref (dst_file);
+                g_free (dst_path);
+                return FALSE;
+        }
+
+        g_object_unref (src_file);
+        g_object_unref (dst_file);
+
+        app = gsp_app_new (dst_path, 0);
+        if (!app) {
+                g_remove (dst_path);
+                g_free (dst_path);
+                return FALSE;
+        }
+
+        g_free (dst_path);
+
+        changed = FALSE;
+        if (app->priv->hidden) {
+                changed = TRUE;
+                app->priv->hidden = FALSE;
+                app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
+        }
+
+        if (app->priv->no_display) {
+                changed = TRUE;
+                app->priv->no_display = FALSE;
+                app->priv->save_mask |= GSP_ASP_SAVE_MASK_NO_DISPLAY;
+        }
+
+        if (!app->priv->enabled) {
+                changed = TRUE;
+                app->priv->enabled = TRUE;
+                app->priv->save_mask |= GSP_ASP_SAVE_MASK_ENABLED;
+        }
+
+        if (changed) {
+                _gsp_app_queue_save (app);
+        }
+
+        manager = gsp_app_manager_get ();
+        gsp_app_manager_add (manager, app);
+        g_object_unref (app);
+        g_object_unref (manager);
+
+        return TRUE;
+}
diff --git a/capplet/gsp-app.h b/capplet/gsp-app.h
new file mode 100644
index 0000000..a199795
--- /dev/null
+++ b/capplet/gsp-app.h
@@ -0,0 +1,108 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2007, 2009 Vincent Untz.
+ * Copyright (C) 2008 Lucas Rocha.
+ * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __GSP_APP_H
+#define __GSP_APP_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GSP_TYPE_APP            (gsp_app_get_type ())
+#define GSP_APP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSP_TYPE_APP, GspApp))
+#define GSP_APP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GSP_TYPE_APP, GspAppClass))
+#define GSP_IS_APP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSP_TYPE_APP))
+#define GSP_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSP_TYPE_APP))
+#define GSP_APP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GSP_TYPE_APP, GspAppClass))
+
+typedef struct _GspApp        GspApp;
+typedef struct _GspAppClass   GspAppClass;
+
+typedef struct _GspAppPrivate GspAppPrivate;
+
+struct _GspAppClass
+{
+        GObjectClass parent_class;
+
+        void (* changed) (GspApp *app);
+        void (* removed) (GspApp *app);
+};
+
+struct _GspApp
+{
+        GObject parent_instance;
+
+        GspAppPrivate *priv;
+};
+
+GType            gsp_app_get_type          (void);
+
+void             gsp_app_create            (const char   *name,
+                                            const char   *comment,
+                                            const char   *exec);
+void             gsp_app_update            (GspApp       *app,
+                                            const char   *name,
+                                            const char   *comment,
+                                            const char   *exec);
+
+gboolean         gsp_app_copy_desktop_file (const char   *uri);
+
+void             gsp_app_delete            (GspApp       *app);
+
+const char      *gsp_app_get_basename      (GspApp       *app);
+const char      *gsp_app_get_path          (GspApp       *app);
+
+gboolean         gsp_app_get_hidden        (GspApp       *app);
+gboolean         gsp_app_get_display       (GspApp       *app);
+
+gboolean         gsp_app_get_enabled       (GspApp       *app);
+void             gsp_app_set_enabled       (GspApp       *app,
+                                            gboolean      enabled);
+
+gboolean         gsp_app_get_shown         (GspApp       *app);
+
+const char      *gsp_app_get_name          (GspApp       *app);
+const char      *gsp_app_get_exec          (GspApp       *app);
+const char      *gsp_app_get_comment       (GspApp       *app);
+
+const char      *gsp_app_get_description   (GspApp       *app);
+GIcon           *gsp_app_get_icon          (GspApp       *app);
+
+/* private interface for GspAppManager only */
+
+GspApp          *gsp_app_new                      (const char   *path,
+                                                   unsigned int  xdg_position);
+
+void             gsp_app_reload_at                (GspApp       *app,
+                                                   const char   *path,
+                                                   unsigned int  xdg_position);
+
+unsigned int     gsp_app_get_xdg_position         (GspApp       *app);
+unsigned int     gsp_app_get_xdg_system_position  (GspApp       *app);
+void             gsp_app_set_xdg_system_position  (GspApp       *app,
+                                                   unsigned int  position);
+
+G_END_DECLS
+
+#endif /* __GSP_APP_H */
diff --git a/capplet/gsp-keyfile.c b/capplet/gsp-keyfile.c
new file mode 100644
index 0000000..de9fac0
--- /dev/null
+++ b/capplet/gsp-keyfile.c
@@ -0,0 +1,201 @@
+/*
+ * gsp-keyfile.c: GKeyFile extensions
+ *
+ * Copyright (C) 2008, 2009 Novell, Inc.
+ *
+ * Based on code from panel-keyfile.c (from gnome-panel)
+ *
+ * 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:
+ *        Vincent Untz <vuntz@gnome.org>
+ */
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "gsp-keyfile.h"
+
+void
+gsp_key_file_populate (GKeyFile *keyfile)
+{
+        gsp_key_file_set_string (keyfile,
+                                 G_KEY_FILE_DESKTOP_KEY_TYPE,
+                                 "Application");
+
+        gsp_key_file_set_string (keyfile,
+                                 G_KEY_FILE_DESKTOP_KEY_EXEC,
+                                 "/bin/false");
+}
+
+//FIXME: kill this when bug #309224 is fixed
+gboolean
+gsp_key_file_to_file (GKeyFile     *keyfile,
+                      const gchar  *path,
+                      GError      **error)
+{
+        GError  *write_error;
+        gchar   *data;
+        gsize    length;
+        gboolean res;
+
+        g_return_val_if_fail (keyfile != NULL, FALSE);
+        g_return_val_if_fail (path != NULL, FALSE);
+
+        write_error = NULL;
+        data = g_key_file_to_data (keyfile, &length, &write_error);
+
+        if (write_error) {
+                g_propagate_error (error, write_error);
+                return FALSE;
+        }
+
+        res = g_file_set_contents (path, data, length, &write_error);
+        g_free (data);
+
+        if (write_error) {
+                g_propagate_error (error, write_error);
+                return FALSE;
+        }
+
+        return res;
+}
+
+gboolean
+gsp_key_file_get_boolean (GKeyFile    *keyfile,
+                          const gchar *key,
+                          gboolean     default_value)
+{
+        GError   *error;
+        gboolean  retval;
+
+        error = NULL;
+        retval = g_key_file_get_boolean (keyfile, G_KEY_FILE_DESKTOP_GROUP,
+                                         key, &error);
+        if (error != NULL) {
+                retval = default_value;
+                g_error_free (error);
+        }
+
+        return retval;
+}
+
+gboolean
+gsp_key_file_get_shown (GKeyFile   *keyfile,
+                        const char *current_desktop)
+{
+        char     **only_show_in, **not_show_in;
+        gboolean   found;
+        int        i;
+
+        if (!current_desktop)
+                return TRUE;
+
+        only_show_in = g_key_file_get_string_list (keyfile, G_KEY_FILE_DESKTOP_GROUP,
+                                                   G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN,
+                                                   NULL, NULL);
+
+        if (only_show_in) {
+                found = FALSE;
+                for (i = 0; only_show_in[i] != NULL; i++) {
+                        if (g_strcmp0 (current_desktop, only_show_in[i]) == 0) {
+                                found = TRUE;
+                                break;
+                        }
+                }
+
+                g_strfreev (only_show_in);
+
+                if (!found)
+                        return FALSE;
+        }
+
+        not_show_in = g_key_file_get_string_list (keyfile, G_KEY_FILE_DESKTOP_GROUP,
+                                                  G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN,
+                                                  NULL, NULL);
+
+        if (not_show_in) {
+                found = FALSE;
+                for (i = 0; not_show_in[i] != NULL; i++) {
+                        if (g_strcmp0 (current_desktop, not_show_in[i]) == 0) {
+                                found = TRUE;
+                                break;
+                        }
+                }
+
+                g_strfreev (not_show_in);
+
+                if (found)
+                        return FALSE;
+        }
+
+        return TRUE;
+}
+
+void
+gsp_key_file_set_locale_string (GKeyFile    *keyfile,
+                                const gchar *key,
+                                const gchar *value)
+{
+        const char         *locale;
+        const char * const *langs_pointer;
+        int                 i;
+
+        if (value == NULL) {
+                value = "";
+        }
+
+        locale = NULL;
+        langs_pointer = g_get_language_names ();
+        for (i = 0; langs_pointer[i] != NULL; i++) {
+                /* find first without encoding  */
+                if (strchr (langs_pointer[i], '.') == NULL) {
+                        locale = langs_pointer[i];
+                        break;
+                }
+        }
+
+        if (locale != NULL) {
+                g_key_file_set_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP,
+                                              key, locale, value);
+        } else {
+                g_key_file_set_string (keyfile, G_KEY_FILE_DESKTOP_GROUP,
+                                       key, value);
+        }
+}
+
+void
+gsp_key_file_ensure_C_key (GKeyFile   *keyfile,
+                           const char *key)
+{
+        char *C_value;
+        char *buffer;
+
+        /* Make sure we set the "C" locale strings to the terms we set here.
+         * This is so that if the user logs into another locale they get their
+         * own description there rather then empty. It is not the C locale
+         * however, but the user created this entry herself so it's OK */
+        C_value = gsp_key_file_get_string (keyfile, key);
+        if (C_value == NULL || C_value [0] == '\0') {
+                buffer = gsp_key_file_get_locale_string (keyfile, key);
+                if (buffer) {
+                        gsp_key_file_set_string (keyfile, key, buffer);
+                        g_free (buffer);
+                }
+        }
+        g_free (C_value);
+}
diff --git a/capplet/gsp-keyfile.h b/capplet/gsp-keyfile.h
new file mode 100644
index 0000000..d94f667
--- /dev/null
+++ b/capplet/gsp-keyfile.h
@@ -0,0 +1,65 @@
+/*
+ * gsp-keyfile.h: GKeyFile extensions
+ *
+ * Copyright (C) 2008, 2009 Novell, Inc.
+ *
+ * Based on code from panel-keyfile.h (from gnome-panel)
+ *
+ * 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:
+ *        Vincent Untz <vuntz@gnome.org>
+ */
+
+#ifndef GSP_KEYFILE_H
+#define GSP_KEYFILE_H
+
+#include "glib.h"
+
+G_BEGIN_DECLS
+
+#define GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED "X-GNOME-Autostart-enabled"
+
+void      gsp_key_file_populate        (GKeyFile *keyfile);
+
+gboolean  gsp_key_file_to_file         (GKeyFile       *keyfile,
+                                        const gchar    *path,
+                                        GError        **error);
+
+gboolean gsp_key_file_get_boolean      (GKeyFile       *keyfile,
+                                        const gchar    *key,
+                                        gboolean        default_value);
+gboolean gsp_key_file_get_shown        (GKeyFile       *keyfile,
+                                        const char     *current_desktop);
+#define gsp_key_file_get_string(key_file, key) \
+         g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL)
+#define gsp_key_file_get_locale_string(key_file, key) \
+         g_key_file_get_locale_string(key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL)
+
+#define gsp_key_file_set_boolean(key_file, key, value) \
+         g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, key, value)
+#define gsp_key_file_set_string(key_file, key, value) \
+         g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, key, value)
+void    gsp_key_file_set_locale_string (GKeyFile    *keyfile,
+                                        const gchar *key,
+                                        const gchar *value);
+
+void gsp_key_file_ensure_C_key         (GKeyFile   *keyfile,
+                                        const char *key);
+
+G_END_DECLS
+
+#endif /* GSP_KEYFILE_H */
diff --git a/capplet/main.c b/capplet/main.c
new file mode 100644
index 0000000..3c7177b
--- /dev/null
+++ b/capplet/main.c
@@ -0,0 +1,108 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ * main.c
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2008 Lucas Rocha.
+ *
+ * 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
+ * Lesser 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.
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gsm-properties-dialog.h"
+
+static gboolean    show_version     = FALSE;
+
+static GOptionEntry options[] = {
+        { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL },
+        { NULL, 0, 0, 0, NULL, NULL, NULL }
+};
+
+static void
+dialog_response (GsmPropertiesDialog *dialog,
+                 guint                response_id,
+                 gpointer             data)
+{
+        GdkScreen *screen;
+        GError    *error;
+
+        if (response_id == GTK_RESPONSE_HELP) {
+                screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
+
+                error = NULL;
+                gtk_show_uri (screen, "ghelp:user-guide?gosstartsession-2",
+                              gtk_get_current_event_time (), &error);
+
+                if (error != NULL) {
+                        GtkWidget *d;
+                        d = gtk_message_dialog_new (GTK_WINDOW (dialog),
+                                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                    GTK_MESSAGE_ERROR,
+                                                    GTK_BUTTONS_CLOSE,
+                                                    "%s",
+                                                    _("Could not display help document"));
+                        gtk_message_dialog_format_secondary_text (
+                                                GTK_MESSAGE_DIALOG (d),
+                                                "%s", error->message);
+                        g_error_free (error);
+
+                        gtk_dialog_run (GTK_DIALOG (d));
+                        gtk_widget_destroy (d);
+                }
+        } else {
+                gtk_widget_destroy (GTK_WIDGET (dialog));
+                gtk_main_quit ();
+        }
+}
+
+int
+main (int argc, char *argv[])
+{
+        GError    *error;
+        GtkWidget *dialog;
+
+        bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
+        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+        textdomain (GETTEXT_PACKAGE);
+
+        error = NULL;
+        if (! gtk_init_with_args (&argc, &argv, " - GNOME Session Properties", options, GETTEXT_PACKAGE, &error)) {
+                g_warning ("Unable to start: %s", error->message);
+                g_error_free (error);
+                return 1;
+        }
+
+        if (show_version) {
+                g_print ("%s %s\n", argv [0], VERSION);
+                return 0;
+        }
+
+        dialog = gsm_properties_dialog_new ();
+        g_signal_connect (dialog,
+                          "response",
+                          G_CALLBACK (dialog_response),
+                          NULL);
+        gtk_widget_show (dialog);
+
+        gtk_main ();
+
+        return 0;
+}
diff --git a/configure.ac b/configure.ac
index 6485d57..b67c0e6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -301,65 +301,67 @@ if test $enable_ipv6 = yes; then
     ]])],
     [have_ipv6=yes],
     [have_ipv6=no]
   )
   AC_MSG_RESULT($have_ipv6)
 
   dnl =================================================================
   dnl Now we would check for specific function like getaddrinfo.
   dnl =================================================================
   have_getaddrinfo=no
   if test $have_ipv6=yes; then
     AC_CHECK_FUNC(getaddrinfo, have_getaddrinfo=yes)
     if test $have_getaddrinfo != yes; then
       # getaddrinfo is not in the default libraries.  See if it's in some other.
       for lib in bsd socket inet; do
         AC_CHECK_LIB($lib, getaddrinfo, [LIBS="$LIBS -l$lib";have_getaddrinfo=yes; break])
       done
     fi
     if test $have_getaddrinfo=yes; then
       AC_DEFINE(ENABLE_IPV6, 1, [Define if IPV6 is supported])
       have_full_ipv6=yes
     fi
   fi
 fi
 dnl ==============================================================================
 dnl End of IPv6 checks
 dnl ==============================================================================
 
 AC_CONFIG_FILES([
 Makefile
+capplet/Makefile
 doc/Makefile
 doc/dbus/Makefile
 doc/dbus/gnome-session.xml
 doc/man/Makefile
 data/Makefile
+data/gnome-session-properties.desktop.in
 data/org.gnome.SessionManager.gschema.xml
 data/icons/Makefile
 data/icons/16x16/Makefile
 data/icons/22x22/Makefile
 data/icons/24x24/Makefile
 data/icons/32x32/Makefile
 data/icons/48x48/Makefile
 data/icons/scalable/Makefile
 gnome-session/Makefile
 tools/Makefile
 po/Makefile.in
 ])
 AC_OUTPUT
 
 dnl ---------------------------------------------------------------------------
 dnl - Show summary
 dnl ---------------------------------------------------------------------------
 
 echo "
               gnome-session $VERSION
               `echo gnome-session $VERSION | sed "s/./=/g"`
 
         prefix:                   ${prefix}
         exec_prefix:              ${exec_prefix}
         libdir:                   ${libdir}
         bindir:                   ${bindir}
         sbindir:                  ${sbindir}
         sysconfdir:               ${sysconfdir}
         localstatedir:            ${localstatedir}
         datadir:                  ${datadir}
diff --git a/data/Makefile.am b/data/Makefile.am
index d1d8d25..524cc6e 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,68 +1,74 @@
 SUBDIRS = icons
 
 uidir = $(pkgdatadir)
 ui_DATA = \
 	session-properties.ui
 
 if BUILD_SESSION_SELECTOR
 ui_DATA += session-selector.ui
 endif
 
 hwcompatdir = $(pkgdatadir)
 hwcompat_DATA = hardware-compatibility
 
 xsessiondir = $(datadir)/xsessions
 xsession_in_files = gnome.desktop.in
 
 if BUILD_SESSION_SELECTOR
 xsession_in_files += gnome-custom-session.desktop.in
 endif
 
 xsession_DATA = $(xsession_in_files:.desktop.in=.desktop)
 
 wayland_sessiondir = $(datadir)/wayland-sessions
 wayland_session_in_files = gnome-wayland.desktop.in
 wayland_session_DATA = $(wayland_session_in_files:.desktop.in=.desktop)
 
+desktopdir = $(datadir)/applications
+desktop_in_files = gnome-session-properties.desktop.in
+desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
+
 sessiondir = $(datadir)/gnome-session/sessions
 session_in_in_files = gnome.session.desktop.in.in gnome-dummy.session.desktop.in.in gnome-wayland.session.desktop.in.in
 session_in_files = $(session_in_in_files:.session.desktop.in.in=.session.desktop.in)
 session_DATA = $(session_in_files:.session.desktop.in=.session)
 
 %.session.desktop.in: %.session.desktop.in.in Makefile
 	$(AM_V_GEN)sed \
 		-e "s|\@LIBEXECDIR\@|$(libexecdir)|" \
 		$< > $@
 
 %.session: %.session.desktop.in   $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@
 
 @INTLTOOL_DESKTOP_RULE@
 @INTLTOOL_XML_NOMERGE_RULE@
 
 gsettings_SCHEMAS = org.gnome.SessionManager.gschema.xml
 @GSETTINGS_RULES@
 
 migrationdir = $(datadir)/GConf/gsettings
 dist_migration_DATA = gnome-session.convert
 
 EXTRA_DIST =					\
 	$(xsession_in_files)			\
 	$(session_in_in_files)			\
 	$(wayland_session_in_files)		\
 	$(gsettings_SCHEMAS:.xml=.xml.in)	\
 	session-selector.ui			\
 	gnome-custom-session.desktop.in		\
 	$(ui_DATA)				\
 	$(hwcompat_DATA)
 
 CLEANFILES =					\
 	$(gsettings_SCHEMAS)			\
 	$(xsession_DATA)			\
 	$(wayland_session_DATA)			\
+	$(desktop_DATA)				\
 	$(session_DATA)
 
 DISTCLEANFILES =				\
 	$(gsettings_SCHEMAS)			\
+	$(desktop_in_files)			\
 	$(wayland_session_in_files)
 
 -include $(top_srcdir)/git.mk
diff --git a/data/gnome-session-properties.desktop.in.in b/data/gnome-session-properties.desktop.in.in
new file mode 100644
index 0000000..3dc7b03
--- /dev/null
+++ b/data/gnome-session-properties.desktop.in.in
@@ -0,0 +1,15 @@
+[Desktop Entry]
+_Name=Startup Applications
+_Comment=Choose what applications to start when you log in
+Exec=gnome-session-properties
+Icon=session-properties
+Terminal=false
+Type=Application
+StartupNotify=true
+Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings;
+OnlyShowIn=GNOME;Unity;
+NoDisplay=true
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-session
+X-GNOME-Bugzilla-Component=gnome-session-properties
+X-GNOME-Bugzilla-Version=@VERSION@
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 02e5c62..2879423 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -1,29 +1,30 @@
 XSLTPROC_FLAGS = \
         --nonet \
         --stringparam man.output.quietly 1 \
         --stringparam funcsynopsis.style ansi \
         --stringparam man.th.extra1.suppress 1 \
         --stringparam man.authors.section.enabled 0 \
         --stringparam man.copyright.section.enabled 0
 
 .xml.1:
 	$(AM_V_GEN) $(XSLTPROC) $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
 
 man_MANS =				\
 	gnome-session.1			\
+	gnome-session-properties.1	\
 	gnome-session-quit.1		\
 	gnome-session-inhibit.1
 
 if BUILD_SESSION_SELECTOR
 man_MANS += gnome-session-selector.1
 endif
 
 EXTRA_DIST =				\
 	gnome-session-inhibit.xml	\
 	gnome-session-selector.xml	\
 	$(man_MANS)
 
 CLEANFILES =				\
 	gnome-session-inhibit.1
 
 -include $(top_srcdir)/git.mk
diff --git a/doc/man/gnome-session-properties.1 b/doc/man/gnome-session-properties.1
new file mode 100644
index 0000000..c7ef1af
--- /dev/null
+++ b/doc/man/gnome-session-properties.1
@@ -0,0 +1,24 @@
+.\"
+.\" gnome-session-properties manual page.
+.\" (C) 2009-2010 Vincent Untz (vuntz@gnome.org)
+.\"
+.TH GNOME-SESSION-PROPERTIES 1 "GNOME"
+.SH NAME
+gnome-session-properties \- Configure applications to start on login
+.SH SYNOPSIS
+.B gnome-session-properties
+.SH DESCRIPTION
+.PP
+The \fIgnome-session-properties\fP program enables the users to
+configure what applications should be started on login, in addition to
+the default startup applications configured on the system.
+.PP
+It also proposes an interface to save a snapshot of the currently
+running applications so that they can automatically be restored to
+their current state on your next GNOME session.
+.SH BUGS
+If you find bugs in the \fIgnome-session-properties\fP program, please report
+these on https://bugzilla.gnome.org.
+.SH SEE ALSO
+.BR gnome-session(1)
+.BR gnome-session-quit(1)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5a887ec..9221412 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,20 +1,25 @@
 # List of source files containing translatable strings.
 # Please keep this file sorted alphabetically.
+capplet/gsm-app-dialog.c
+capplet/gsm-properties-dialog.c
+capplet/gsp-app.c
+capplet/main.c
 data/gnome-custom-session.desktop.in
 data/gnome.desktop.in
 data/gnome-dummy.session.desktop.in.in
 data/gnome.session.desktop.in.in
+data/gnome-session-properties.desktop.in.in
 data/gnome-wayland.desktop.in
 data/gnome-wayland.session.desktop.in.in
 [type: gettext/glade]data/session-selector.ui
 [type: gettext/glade]data/session-properties.ui
 gnome-session/gsm-fail-whale-dialog.c
 gnome-session/gsm-manager.c
 gnome-session/gsm-process-helper.c
 gnome-session/gsm-util.c
 gnome-session/gsm-xsmp-client.c
 gnome-session/gsm-xsmp-server.c
 gnome-session/main.c
 tools/gnome-session-inhibit.c
 tools/gnome-session-selector.c
 tools/gnome-session-quit.c
-- 
2.3.7


From 36694037511ac9d61270c7194118cb3fbb438ab7 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 8 May 2015 16:27:15 -0400
Subject: [PATCH 02/17] Revert "Rename the desktop file to
 gnome-session-properties.desktop"

This reverts commit ac9fd0dc97a17674cb082f80df0b1fcc45bc92bf.
---
 configure.ac                                |  2 +-
 data/Makefile.am                            |  2 +-
 data/gnome-session-properties.desktop.in.in | 15 ---------------
 data/session-properties.desktop.in.in       | 15 +++++++++++++++
 po/POTFILES.in                              |  1 +
 po/POTFILES.skip                            |  3 ++-
 6 files changed, 20 insertions(+), 18 deletions(-)
 delete mode 100644 data/gnome-session-properties.desktop.in.in
 create mode 100644 data/session-properties.desktop.in.in

diff --git a/configure.ac b/configure.ac
index b67c0e6..0990e70 100644
--- a/configure.ac
+++ b/configure.ac
@@ -307,61 +307,61 @@ if test $enable_ipv6 = yes; then
   dnl =================================================================
   dnl Now we would check for specific function like getaddrinfo.
   dnl =================================================================
   have_getaddrinfo=no
   if test $have_ipv6=yes; then
     AC_CHECK_FUNC(getaddrinfo, have_getaddrinfo=yes)
     if test $have_getaddrinfo != yes; then
       # getaddrinfo is not in the default libraries.  See if it's in some other.
       for lib in bsd socket inet; do
         AC_CHECK_LIB($lib, getaddrinfo, [LIBS="$LIBS -l$lib";have_getaddrinfo=yes; break])
       done
     fi
     if test $have_getaddrinfo=yes; then
       AC_DEFINE(ENABLE_IPV6, 1, [Define if IPV6 is supported])
       have_full_ipv6=yes
     fi
   fi
 fi
 dnl ==============================================================================
 dnl End of IPv6 checks
 dnl ==============================================================================
 
 AC_CONFIG_FILES([
 Makefile
 capplet/Makefile
 doc/Makefile
 doc/dbus/Makefile
 doc/dbus/gnome-session.xml
 doc/man/Makefile
 data/Makefile
-data/gnome-session-properties.desktop.in
+data/session-properties.desktop.in
 data/org.gnome.SessionManager.gschema.xml
 data/icons/Makefile
 data/icons/16x16/Makefile
 data/icons/22x22/Makefile
 data/icons/24x24/Makefile
 data/icons/32x32/Makefile
 data/icons/48x48/Makefile
 data/icons/scalable/Makefile
 gnome-session/Makefile
 tools/Makefile
 po/Makefile.in
 ])
 AC_OUTPUT
 
 dnl ---------------------------------------------------------------------------
 dnl - Show summary
 dnl ---------------------------------------------------------------------------
 
 echo "
               gnome-session $VERSION
               `echo gnome-session $VERSION | sed "s/./=/g"`
 
         prefix:                   ${prefix}
         exec_prefix:              ${exec_prefix}
         libdir:                   ${libdir}
         bindir:                   ${bindir}
         sbindir:                  ${sbindir}
         sysconfdir:               ${sysconfdir}
         localstatedir:            ${localstatedir}
         datadir:                  ${datadir}
diff --git a/data/Makefile.am b/data/Makefile.am
index 524cc6e..8834fcb 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,58 +1,58 @@
 SUBDIRS = icons
 
 uidir = $(pkgdatadir)
 ui_DATA = \
 	session-properties.ui
 
 if BUILD_SESSION_SELECTOR
 ui_DATA += session-selector.ui
 endif
 
 hwcompatdir = $(pkgdatadir)
 hwcompat_DATA = hardware-compatibility
 
 xsessiondir = $(datadir)/xsessions
 xsession_in_files = gnome.desktop.in
 
 if BUILD_SESSION_SELECTOR
 xsession_in_files += gnome-custom-session.desktop.in
 endif
 
 xsession_DATA = $(xsession_in_files:.desktop.in=.desktop)
 
 wayland_sessiondir = $(datadir)/wayland-sessions
 wayland_session_in_files = gnome-wayland.desktop.in
 wayland_session_DATA = $(wayland_session_in_files:.desktop.in=.desktop)
 
 desktopdir = $(datadir)/applications
-desktop_in_files = gnome-session-properties.desktop.in
+desktop_in_files = session-properties.desktop.in
 desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
 
 sessiondir = $(datadir)/gnome-session/sessions
 session_in_in_files = gnome.session.desktop.in.in gnome-dummy.session.desktop.in.in gnome-wayland.session.desktop.in.in
 session_in_files = $(session_in_in_files:.session.desktop.in.in=.session.desktop.in)
 session_DATA = $(session_in_files:.session.desktop.in=.session)
 
 %.session.desktop.in: %.session.desktop.in.in Makefile
 	$(AM_V_GEN)sed \
 		-e "s|\@LIBEXECDIR\@|$(libexecdir)|" \
 		$< > $@
 
 %.session: %.session.desktop.in   $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@
 
 @INTLTOOL_DESKTOP_RULE@
 @INTLTOOL_XML_NOMERGE_RULE@
 
 gsettings_SCHEMAS = org.gnome.SessionManager.gschema.xml
 @GSETTINGS_RULES@
 
 migrationdir = $(datadir)/GConf/gsettings
 dist_migration_DATA = gnome-session.convert
 
 EXTRA_DIST =					\
 	$(xsession_in_files)			\
 	$(session_in_in_files)			\
 	$(wayland_session_in_files)		\
 	$(gsettings_SCHEMAS:.xml=.xml.in)	\
 	session-selector.ui			\
 	gnome-custom-session.desktop.in		\
diff --git a/data/gnome-session-properties.desktop.in.in b/data/gnome-session-properties.desktop.in.in
deleted file mode 100644
index 3dc7b03..0000000
--- a/data/gnome-session-properties.desktop.in.in
+++ /dev/null
@@ -1,15 +0,0 @@
-[Desktop Entry]
-_Name=Startup Applications
-_Comment=Choose what applications to start when you log in
-Exec=gnome-session-properties
-Icon=session-properties
-Terminal=false
-Type=Application
-StartupNotify=true
-Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings;
-OnlyShowIn=GNOME;Unity;
-NoDisplay=true
-X-GNOME-Bugzilla-Bugzilla=GNOME
-X-GNOME-Bugzilla-Product=gnome-session
-X-GNOME-Bugzilla-Component=gnome-session-properties
-X-GNOME-Bugzilla-Version=@VERSION@
diff --git a/data/session-properties.desktop.in.in b/data/session-properties.desktop.in.in
new file mode 100644
index 0000000..3dc7b03
--- /dev/null
+++ b/data/session-properties.desktop.in.in
@@ -0,0 +1,15 @@
+[Desktop Entry]
+_Name=Startup Applications
+_Comment=Choose what applications to start when you log in
+Exec=gnome-session-properties
+Icon=session-properties
+Terminal=false
+Type=Application
+StartupNotify=true
+Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings;
+OnlyShowIn=GNOME;Unity;
+NoDisplay=true
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-session
+X-GNOME-Bugzilla-Component=gnome-session-properties
+X-GNOME-Bugzilla-Version=@VERSION@
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9221412..b18fcab 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,25 +1,26 @@
 # List of source files containing translatable strings.
 # Please keep this file sorted alphabetically.
 capplet/gsm-app-dialog.c
 capplet/gsm-properties-dialog.c
 capplet/gsp-app.c
 capplet/main.c
 data/gnome-custom-session.desktop.in
 data/gnome.desktop.in
 data/gnome-dummy.session.desktop.in.in
 data/gnome.session.desktop.in.in
 data/gnome-session-properties.desktop.in.in
 data/gnome-wayland.desktop.in
 data/gnome-wayland.session.desktop.in.in
 [type: gettext/glade]data/session-selector.ui
+data/session-properties.desktop.in.in
 [type: gettext/glade]data/session-properties.ui
 gnome-session/gsm-fail-whale-dialog.c
 gnome-session/gsm-manager.c
 gnome-session/gsm-process-helper.c
 gnome-session/gsm-util.c
 gnome-session/gsm-xsmp-client.c
 gnome-session/gsm-xsmp-server.c
 gnome-session/main.c
 tools/gnome-session-inhibit.c
 tools/gnome-session-selector.c
 tools/gnome-session-quit.c
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index c5d4ac9..6f57edc 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -1,9 +1,10 @@
 # List of source files containing translatable strings that should not be
 # translated.
 # Please keep this file sorted alphabetically.
 data/gnome-dummy.session.desktop.in
 data/gnome.session.desktop.in
-data/gnome-session-properties.desktop.in
+data/session-properties.desktop.in.
 data/gnome-session.schemas.in
 data/gnome-wayland.session.desktop.in
 data/gnome-wm.desktop.in
+data/session-properties.desktop.in
-- 
2.3.7


From 2ed0c1357a3fc729b2f5a86662fb15609c1d81a5 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 13 May 2015 10:56:09 -0400
Subject: [PATCH 03/17] stop using gsm_util_get_current_desktop

It no longer exists.
---
 capplet/gsp-app.c | 111 +++++++++++++++++++++++++++++-------------------------
 1 file changed, 60 insertions(+), 51 deletions(-)

diff --git a/capplet/gsp-app.c b/capplet/gsp-app.c
index c92b8da..123ab21 100644
--- a/capplet/gsp-app.c
+++ b/capplet/gsp-app.c
@@ -3,60 +3,61 @@
  * Copyright (C) 1999 Free Software Foundation, Inc.
  * Copyright (C) 2007, 2009 Vincent Untz.
  * Copyright (C) 2008 Lucas Rocha.
  * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
  *
  * 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.
  *
  */
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
 #include <string.h>
 #include <sys/stat.h>
 
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
+#include <gio/gdesktopappinfo.h>
 
 #include "gsm-app-dialog.h"
 #include "gsm-properties-dialog.h"
 #include "gsm-util.h"
 #include "gsp-app-manager.h"
 #include "gsp-keyfile.h"
 
 #include "gsp-app.h"
 
 #define GSP_APP_SAVE_DELAY 2
 
 #define GSP_ASP_SAVE_MASK_HIDDEN     0x0001
 #define GSP_ASP_SAVE_MASK_ENABLED    0x0002
 #define GSP_ASP_SAVE_MASK_NAME       0x0004
 #define GSP_ASP_SAVE_MASK_EXEC       0x0008
 #define GSP_ASP_SAVE_MASK_COMMENT    0x0010
 #define GSP_ASP_SAVE_MASK_NO_DISPLAY 0x0020
 #define GSP_ASP_SAVE_MASK_ALL        0xffff
 
 struct _GspAppPrivate {
         char         *basename;
         char         *path;
 
         gboolean      hidden;
         gboolean      no_display;
         gboolean      enabled;
         gboolean      shown;
 
         char         *name;
         char         *exec;
@@ -281,148 +282,145 @@ _gsp_app_update_description (GspApp *app)
         } else {
                 secondary = _("No description");
         }
 
         g_free (app->priv->description);
         app->priv->description = g_markup_printf_escaped ("<b>%s</b>\n%s",
                                                           primary,
                                                           secondary);
 }
 
 /*
  * Saving
  */
 
 static void
 _gsp_ensure_user_autostart_dir (void)
 {
         char *dir;
 
         dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL);
         g_mkdir_with_parents (dir, S_IRWXU);
 
         g_free (dir);
 }
 
 static gboolean
 _gsp_app_user_equal_system (GspApp  *app,
                             char   **system_path)
 {
         GspAppManager *manager;
-        const char    *system_dir;
-        char          *path;
-        char          *str;
-        GKeyFile      *keyfile;
+        gboolean       return_value = FALSE;
+        const char    *system_dir = NULL;
+        char          *path = NULL;
+        char          *str = NULL;
+        GKeyFile      *keyfile = NULL;
+        GDesktopAppInfo *app_info = NULL;
 
         manager = gsp_app_manager_get ();
         system_dir = gsp_app_manager_get_dir (manager,
                                               app->priv->xdg_system_position);
         g_object_unref (manager);
         if (!system_dir) {
-                return FALSE;
+                goto out;
         }
 
         path = g_build_filename (system_dir, app->priv->basename, NULL);
 
         keyfile = g_key_file_new ();
         if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
-                g_free (path);
-                g_key_file_free (keyfile);
-                return FALSE;
+                goto out;
         }
 
-        if (gsp_key_file_get_boolean (keyfile,
-                                      G_KEY_FILE_DESKTOP_KEY_HIDDEN,
-                                      FALSE) != app->priv->hidden ||
-            gsp_key_file_get_boolean (keyfile,
-                                      GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
-                                      TRUE) != app->priv->enabled ||
-            gsp_key_file_get_shown (keyfile,
-                                    gsm_util_get_current_desktop ()) != app->priv->shown) {
-                g_free (path);
-                g_key_file_free (keyfile);
-                return FALSE;
+        app_info = g_desktop_app_info_new_from_keyfile (keyfile);
+
+        if (!app_info) {
+                goto out;
         }
 
-        if (gsp_key_file_get_boolean (keyfile,
-                                      G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
-                                      FALSE) != app->priv->no_display) {
-                g_free (path);
-                g_key_file_free (keyfile);
-                return FALSE;
+        if (g_desktop_app_info_get_is_hidden (app_info)) {
+                goto out;
+        }
+
+        if (g_desktop_app_info_has_key (app_info,
+                                        GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED) &&
+            !g_desktop_app_info_get_boolean (app_info,
+                                             GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED)) {
+                goto out;
+        }
+
+        if (!g_desktop_app_info_get_show_in (app_info, NULL)) {
+                goto out;
+        }
+
+        if (g_desktop_app_info_get_nodisplay (app_info)) {
+                goto out;
         }
 
         str = gsp_key_file_get_locale_string (keyfile,
                                               G_KEY_FILE_DESKTOP_KEY_NAME);
         if (!_gsp_str_equal (str, app->priv->name)) {
-                g_free (str);
-                g_free (path);
-                g_key_file_free (keyfile);
-                return FALSE;
+                goto out;
         }
-        g_free (str);
+        g_clear_pointer (&str, g_free);
 
         str = gsp_key_file_get_locale_string (keyfile,
                                               G_KEY_FILE_DESKTOP_KEY_COMMENT);
         if (!_gsp_str_equal (str, app->priv->comment)) {
-                g_free (str);
-                g_free (path);
-                g_key_file_free (keyfile);
-                return FALSE;
+                goto out;
         }
-        g_free (str);
+        g_clear_pointer (&str, g_free);
 
         str = gsp_key_file_get_string (keyfile,
                                        G_KEY_FILE_DESKTOP_KEY_EXEC);
         if (!_gsp_str_equal (str, app->priv->exec)) {
-                g_free (str);
-                g_free (path);
-                g_key_file_free (keyfile);
-                return FALSE;
+                goto out;
         }
-        g_free (str);
+        g_clear_pointer (&str, g_free);
 
         str = gsp_key_file_get_locale_string (keyfile,
                                               G_KEY_FILE_DESKTOP_KEY_ICON);
         if (!_gsp_str_equal (str, app->priv->icon)) {
-                g_free (str);
-                g_free (path);
-                g_key_file_free (keyfile);
-                return FALSE;
+                goto out;
         }
-        g_free (str);
-
-        g_key_file_free (keyfile);
+        g_clear_pointer (&str, g_free);
 
         *system_path = path;
-
-        return TRUE;
+        path = NULL;
+        return_value = TRUE;
+out:
+        g_clear_pointer (&path, g_free);
+        g_clear_pointer (&str, g_free);
+        g_clear_object (&app_info);
+        g_clear_pointer (&keyfile, g_key_file_free);
+
+        return return_value;
 }
 
 static inline void
 _gsp_app_save_done_success (GspApp *app)
 {
         app->priv->save_mask = 0;
 
         if (app->priv->old_system_path) {
                 g_free (app->priv->old_system_path);
                 app->priv->old_system_path = NULL;
         }
 }
 
 static gboolean
 _gsp_app_save (gpointer data)
 {
         GspApp   *app;
         char     *use_path;
         GKeyFile *keyfile;
         GError   *error;
 
         app = GSP_APP (data);
 
         /* first check if removing the data from the user dir and using the
          * data from the system dir is enough -- this helps us keep clean the
          * user config dir by removing unneeded files */
         if (_gsp_app_user_equal_system (app, &use_path)) {
                 if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) {
                         g_remove (app->priv->path);
                 }
@@ -748,153 +746,164 @@ gsp_app_delete (GspApp *app)
                 app->priv->hidden = TRUE;
                 app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
 
                 _gsp_app_queue_save (app);
                 _gsp_app_emit_changed (app);
         }
 }
 
 /*
  * New autostart app
  */
 
 void
 gsp_app_reload_at (GspApp       *app,
                    const char   *path,
                    unsigned int  xdg_position)
 {
         g_return_if_fail (GSP_IS_APP (app));
 
         app->priv->xdg_position = G_MAXUINT;
         gsp_app_new (path, xdg_position);
 }
 
 GspApp *
 gsp_app_new (const char   *path,
              unsigned int  xdg_position)
 {
         GspAppManager *manager;
         GspApp        *app;
         GKeyFile      *keyfile;
+        GDesktopAppInfo *app_info;
         char          *basename;
         gboolean       new;
 
         basename = g_path_get_basename (path);
 
         manager = gsp_app_manager_get ();
         app = gsp_app_manager_find_app_with_basename (manager, basename);
         g_object_unref (manager);
 
         new = (app == NULL);
 
         if (!new) {
                 if (app->priv->xdg_position == xdg_position) {
                         if (app->priv->skip_next_monitor_event) {
                                 app->priv->skip_next_monitor_event = FALSE;
                                 return NULL;
                         }
                         /* else: the file got changed but not by us, we'll
                          * update our data from disk */
                 }
 
                 if (app->priv->xdg_position < xdg_position ||
                     app->priv->save_timeout != 0) {
                         /* we don't really care about this file, since we
                          * already have something with a higher priority, or
                          * we're going to write something in the user config
                          * anyway.
                          * Note: xdg_position >= 1 so it's a system dir */
                         app->priv->xdg_system_position = MIN (xdg_position,
                                                               app->priv->xdg_system_position);
                         return NULL;
                 }
         }
 
         keyfile = g_key_file_new ();
         if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
                 g_key_file_free (keyfile);
                 g_free (basename);
                 return NULL;
         }
 
+        app_info = g_desktop_app_info_new_from_keyfile (keyfile);
+
+        if (!app_info) {
+                g_object_unref (app_info);
+                g_key_file_free (keyfile);
+                g_free (basename);
+                return NULL;
+        }
+
         if (new) {
                 app = g_object_new (GSP_TYPE_APP, NULL);
                 app->priv->basename = basename;
         } else {
                 g_free (basename);
                 _gsp_app_free_reusable_data (app);
         }
 
+
         app->priv->path = g_strdup (path);
 
         app->priv->hidden = gsp_key_file_get_boolean (keyfile,
                                                       G_KEY_FILE_DESKTOP_KEY_HIDDEN,
                                                       FALSE);
         app->priv->no_display = gsp_key_file_get_boolean (keyfile,
                                                           G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
                                                           FALSE);
         app->priv->enabled = gsp_key_file_get_boolean (keyfile,
                                                        GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
                                                        TRUE);
-        app->priv->shown = gsp_key_file_get_shown (keyfile,
-                                                   gsm_util_get_current_desktop ());
+        app->priv->shown = g_desktop_app_info_get_show_in (app_info, NULL);
 
         app->priv->name = gsp_key_file_get_locale_string (keyfile,
                                                           G_KEY_FILE_DESKTOP_KEY_NAME);
         app->priv->exec = gsp_key_file_get_string (keyfile,
                                                    G_KEY_FILE_DESKTOP_KEY_EXEC);
         app->priv->comment = gsp_key_file_get_locale_string (keyfile,
                                                              G_KEY_FILE_DESKTOP_KEY_COMMENT);
 
         if (gsm_util_text_is_blank (app->priv->name)) {
                 g_free (app->priv->name);
                 app->priv->name = g_strdup (app->priv->exec);
         }
 
         app->priv->icon = gsp_key_file_get_locale_string (keyfile,
                                                           G_KEY_FILE_DESKTOP_KEY_ICON);
 
         if (app->priv->icon) {
                 /* look at icon and see if it's a themed icon or not */
                 if (g_path_is_absolute (app->priv->icon)) {
                         GFile *iconfile;
 
                         iconfile = g_file_new_for_path (app->priv->icon);
                         app->priv->gicon = g_file_icon_new (iconfile);
                         g_object_unref (iconfile);
                 } else {
                         app->priv->gicon = g_themed_icon_new (app->priv->icon);
                 }
         } else {
                 app->priv->gicon = NULL;
         }
 
+        g_object_unref (app_info);
         g_key_file_free (keyfile);
 
         _gsp_app_update_description (app);
 
         if (xdg_position > 0) {
                 g_assert (xdg_position <= app->priv->xdg_system_position);
                 app->priv->xdg_system_position = xdg_position;
         }
         /* else we keep the old value (which is G_MAXUINT if it wasn't set) */
         app->priv->xdg_position = xdg_position;
 
         g_assert (!new || app->priv->save_timeout == 0);
         app->priv->save_timeout = 0;
         app->priv->old_system_path = NULL;
         app->priv->skip_next_monitor_event = FALSE;
 
         if (!new) {
                 _gsp_app_emit_changed (app);
         }
 
         return app;
 }
 
 static char *
 _gsp_find_free_basename (const char *suggested_basename)
 {
         GspAppManager *manager;
         char          *base_path;
         char          *filename;
         char          *basename;
-- 
2.3.7


From 6b24a33c89913eec63a1547d5e372833e5c25a30 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Dec 2013 10:48:03 -0500
Subject: [PATCH 04/17] session-properties: show it in the menus again

It provides functionality for adjusting the session which
customers rely on, so make it show up in the menus again.
---
 data/session-properties.desktop.in.in | 1 -
 1 file changed, 1 deletion(-)

diff --git a/data/session-properties.desktop.in.in b/data/session-properties.desktop.in.in
index 3dc7b03..1a506d9 100644
--- a/data/session-properties.desktop.in.in
+++ b/data/session-properties.desktop.in.in
@@ -1,15 +1,14 @@
 [Desktop Entry]
 _Name=Startup Applications
 _Comment=Choose what applications to start when you log in
 Exec=gnome-session-properties
 Icon=session-properties
 Terminal=false
 Type=Application
 StartupNotify=true
 Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings;
 OnlyShowIn=GNOME;Unity;
-NoDisplay=true
 X-GNOME-Bugzilla-Bugzilla=GNOME
 X-GNOME-Bugzilla-Product=gnome-session
 X-GNOME-Bugzilla-Component=gnome-session-properties
 X-GNOME-Bugzilla-Version=@VERSION@
-- 
2.3.7


From 9fe478de117315c714d63a73b6c143b50a57912e Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Dec 2013 15:07:35 -0500
Subject: [PATCH 05/17] session-properties: get out of Other

Put it in the menus next to Settings and Software
---
 data/session-properties.desktop.in.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/data/session-properties.desktop.in.in b/data/session-properties.desktop.in.in
index 1a506d9..e615d5c 100644
--- a/data/session-properties.desktop.in.in
+++ b/data/session-properties.desktop.in.in
@@ -1,14 +1,14 @@
 [Desktop Entry]
 _Name=Startup Applications
 _Comment=Choose what applications to start when you log in
 Exec=gnome-session-properties
 Icon=session-properties
 Terminal=false
 Type=Application
 StartupNotify=true
-Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings;
+Categories=GNOME;GTK;System;
 OnlyShowIn=GNOME;Unity;
 X-GNOME-Bugzilla-Bugzilla=GNOME
 X-GNOME-Bugzilla-Product=gnome-session
 X-GNOME-Bugzilla-Component=gnome-session-properties
 X-GNOME-Bugzilla-Version=@VERSION@
-- 
2.3.7


From 0e2261200550ebe0a0e455b550b227d12c47ac50 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Dec 2013 10:53:33 -0500
Subject: [PATCH 06/17] session-properties: refresh from recent glade

The ui file is rather old.  This commit just opens it up in a recent
glade and resaves it, so we have a fresh starting point to make
changes.
---
 data/session-properties.ui | 43 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/data/session-properties.ui b/data/session-properties.ui
index 1f0cb9a..47a30f7 100644
--- a/data/session-properties.ui
+++ b/data/session-properties.ui
@@ -1,323 +1,348 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="2.16"/>
-  <!-- interface-naming-policy toplevel-contextual -->
+  <!-- interface-requires gtk+ 3.0 -->
   <object class="GtkNotebook" id="main-notebook">
     <property name="visible">True</property>
     <property name="can_focus">True</property>
     <property name="border_width">6</property>
     <child>
       <object class="GtkVBox" id="vbox1">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="border_width">12</property>
-        <property name="orientation">vertical</property>
         <property name="spacing">3</property>
         <child>
           <object class="GtkLabel" id="label6">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <property name="xalign">0</property>
             <property name="xpad">3</property>
             <property name="ypad">3</property>
             <property name="label" translatable="yes">Additional startup _programs:</property>
             <property name="use_underline">True</property>
             <property name="mnemonic_widget">session_properties_treeview</property>
           </object>
           <packing>
             <property name="expand">False</property>
+            <property name="fill">True</property>
             <property name="position">0</property>
           </packing>
         </child>
         <child>
           <object class="GtkHBox" id="hbox1">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <property name="spacing">6</property>
             <child>
               <object class="GtkScrolledWindow" id="scrolledwindow1">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="hscrollbar_policy">never</property>
-                <property name="vscrollbar_policy">automatic</property>
                 <property name="shadow_type">etched-in</property>
                 <child>
                   <object class="GtkTreeView" id="session_properties_treeview">
                     <property name="height_request">210</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
+                    <child internal-child="selection">
+                      <object class="GtkTreeSelection" id="treeview-selection1"/>
+                    </child>
                   </object>
                 </child>
               </object>
               <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
                 <property name="position">0</property>
               </packing>
             </child>
             <child>
               <object class="GtkVButtonBox" id="vbuttonbox1">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="spacing">6</property>
                 <property name="layout_style">start</property>
                 <child>
                   <object class="GtkButton" id="session_properties_add_button">
                     <property name="label">gtk-add</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="receives_default">True</property>
                     <property name="use_stock">True</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">False</property>
                     <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkButton" id="session_properties_delete_button">
                     <property name="label">gtk-remove</property>
                     <property name="visible">True</property>
                     <property name="sensitive">False</property>
                     <property name="can_focus">True</property>
                     <property name="receives_default">True</property>
                     <property name="use_stock">True</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">False</property>
                     <property name="position">1</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkButton" id="session_properties_edit_button">
                     <property name="label">gtk-edit</property>
                     <property name="visible">True</property>
                     <property name="sensitive">False</property>
                     <property name="can_focus">True</property>
                     <property name="receives_default">True</property>
                     <property name="use_stock">True</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">False</property>
                     <property name="position">2</property>
                   </packing>
                 </child>
               </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
                 <property name="position">1</property>
               </packing>
             </child>
           </object>
           <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
             <property name="position">1</property>
           </packing>
         </child>
       </object>
     </child>
     <child type="tab">
       <object class="GtkLabel" id="label4">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="label" translatable="yes">Startup Programs</property>
       </object>
       <packing>
         <property name="tab_fill">False</property>
       </packing>
     </child>
     <child>
       <object class="GtkVBox" id="vbox3">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="border_width">12</property>
-        <property name="orientation">vertical</property>
         <property name="spacing">6</property>
         <child>
           <object class="GtkCheckButton" id="session_properties_remember_toggle">
             <property name="label" translatable="yes">_Automatically remember running applications when logging out</property>
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <property name="receives_default">False</property>
             <property name="use_underline">True</property>
+            <property name="xalign">0.5</property>
             <property name="draw_indicator">True</property>
           </object>
           <packing>
             <property name="expand">False</property>
             <property name="fill">False</property>
             <property name="position">0</property>
           </packing>
         </child>
         <child>
           <object class="GtkHButtonBox" id="hbuttonbox1">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <child>
               <object class="GtkButton" id="session_properties_save_button">
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
                 <child>
                   <object class="GtkHBox" id="hbox2">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                     <property name="spacing">4</property>
                     <child>
                       <object class="GtkImage" id="image1">
                         <property name="visible">True</property>
+                        <property name="can_focus">False</property>
                         <property name="stock">gtk-save</property>
                       </object>
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
                         <property name="position">0</property>
                       </packing>
                     </child>
                     <child>
                       <object class="GtkLabel" id="label7">
                         <property name="visible">True</property>
+                        <property name="can_focus">False</property>
                         <property name="label" translatable="yes">_Remember Currently Running Applications</property>
                         <property name="use_underline">True</property>
                       </object>
                       <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
                         <property name="position">1</property>
                       </packing>
                     </child>
                   </object>
                 </child>
               </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
                 <property name="position">0</property>
               </packing>
             </child>
           </object>
           <packing>
             <property name="expand">False</property>
             <property name="fill">False</property>
             <property name="position">1</property>
           </packing>
         </child>
       </object>
       <packing>
         <property name="position">1</property>
       </packing>
     </child>
     <child type="tab">
       <object class="GtkLabel" id="label5">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="label" translatable="yes">Options</property>
       </object>
       <packing>
         <property name="position">1</property>
         <property name="tab_fill">False</property>
       </packing>
     </child>
   </object>
   <object class="GtkTable" id="main-table">
     <property name="visible">True</property>
+    <property name="can_focus">False</property>
     <property name="border_width">6</property>
     <property name="n_rows">3</property>
     <property name="n_columns">2</property>
     <property name="column_spacing">12</property>
     <property name="row_spacing">6</property>
     <child>
       <object class="GtkHBox" id="hbox3">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="spacing">12</property>
         <child>
           <object class="GtkEntry" id="session_properties_command_entry">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="invisible_char">&#x25CF;</property>
+            <property name="invisible_char">ā—</property>
           </object>
           <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
             <property name="position">0</property>
           </packing>
         </child>
         <child>
           <object class="GtkButton" id="session_properties_browse_button">
             <property name="label" translatable="yes">Browseā€¦</property>
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <property name="receives_default">True</property>
           </object>
           <packing>
             <property name="expand">False</property>
             <property name="fill">False</property>
             <property name="position">1</property>
           </packing>
         </child>
       </object>
       <packing>
         <property name="left_attach">1</property>
         <property name="right_attach">2</property>
         <property name="top_attach">1</property>
         <property name="bottom_attach">2</property>
         <property name="y_options">GTK_FILL</property>
       </packing>
     </child>
     <child>
       <object class="GtkEntry" id="session_properties_comment_entry">
         <property name="visible">True</property>
         <property name="can_focus">True</property>
-        <property name="invisible_char">&#x25CF;</property>
+        <property name="invisible_char">ā—</property>
       </object>
       <packing>
         <property name="left_attach">1</property>
         <property name="right_attach">2</property>
         <property name="top_attach">2</property>
         <property name="bottom_attach">3</property>
         <property name="y_options">GTK_FILL</property>
       </packing>
     </child>
     <child>
       <object class="GtkEntry" id="session_properties_name_entry">
         <property name="visible">True</property>
         <property name="can_focus">True</property>
-        <property name="invisible_char">&#x25CF;</property>
+        <property name="invisible_char">ā—</property>
       </object>
       <packing>
         <property name="left_attach">1</property>
         <property name="right_attach">2</property>
         <property name="y_options">GTK_FILL</property>
       </packing>
     </child>
     <child>
       <object class="GtkLabel" id="label3">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="xalign">0</property>
         <property name="label" translatable="yes">Comm_ent:</property>
         <property name="use_underline">True</property>
         <property name="mnemonic_widget">label2</property>
       </object>
       <packing>
         <property name="top_attach">2</property>
         <property name="bottom_attach">3</property>
         <property name="x_options">GTK_FILL</property>
         <property name="y_options">GTK_FILL</property>
       </packing>
     </child>
     <child>
       <object class="GtkLabel" id="label2">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="xalign">0</property>
         <property name="label" translatable="yes">Co_mmand:</property>
         <property name="use_underline">True</property>
         <property name="mnemonic_widget">session_properties_command_entry</property>
       </object>
       <packing>
         <property name="top_attach">1</property>
         <property name="bottom_attach">2</property>
         <property name="x_options">GTK_FILL</property>
         <property name="y_options">GTK_FILL</property>
       </packing>
     </child>
     <child>
       <object class="GtkLabel" id="label1">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="xalign">0</property>
         <property name="label" translatable="yes">_Name:</property>
         <property name="use_underline">True</property>
         <property name="mnemonic_widget">session_properties_name_entry</property>
       </object>
       <packing>
         <property name="x_options">GTK_FILL</property>
         <property name="y_options">GTK_FILL</property>
       </packing>
     </child>
   </object>
 </interface>
-- 
2.3.7


From 70e758b03d9b4a018c196cf58d4147709ae1eb5e Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Dec 2013 16:14:16 -0500
Subject: [PATCH 07/17] manager: Don't clear saved session if autosaving is
 disabled

Now that we support on-demand saving again, we need to make sure
we don't wipe that away at log out.
---
 gnome-session/gsm-manager.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c
index 596db92..c32bbe4 100644
--- a/gnome-session/gsm-manager.c
+++ b/gnome-session/gsm-manager.c
@@ -1952,61 +1952,60 @@ on_xsmp_client_register_confirmed (GsmXSMPClient *client,
         app = find_app_for_startup_id (manager, id);
 
         if (app != NULL) {
                 gsm_app_registered (app);
         }
 }
 
 static gboolean
 auto_save_is_enabled (GsmManager *manager)
 {
         return g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE_ONE_SHOT)
             || g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE);
 }
 
 static void
 maybe_save_session (GsmManager *manager)
 {
         GError *error;
 
         if (gsm_system_is_login_session (manager->priv->system))
                 return;
 
         /* We only allow session saving when session is running or when
          * logging out */
         if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING &&
             manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) {
                 return;
         }
 
         if (!auto_save_is_enabled (manager)) {
-                gsm_session_save_clear ();
                 return;
         }
 
         error = NULL;
         gsm_session_save (manager->priv->clients, &error);
 
         if (error) {
                 g_warning ("Error saving session: %s", error->message);
                 g_error_free (error);
         }
 }
 
 static void
 _handle_client_end_session_response (GsmManager *manager,
                                      GsmClient  *client,
                                      gboolean    is_ok,
                                      gboolean    do_last,
                                      gboolean    cancel,
                                      const char *reason)
 {
         /* just ignore if received outside of shutdown */
         if (manager->priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) {
                 return;
         }
 
         g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :"");
 
         if (cancel) {
                 cancel_end_session (manager);
                 return;
-- 
2.3.7


From 4d41334693ccf1617f242326d1c5a9e367a54169 Mon Sep 17 00:00:00 2001
From: Josselin Mouette <joss@malsain.org>
Date: Mon, 21 Jun 2010 15:22:23 -0400
Subject: [PATCH 08/17] Add "Remember Currently Running Applications" button

This adds back session saving that's not at logout.
---
 capplet/gsm-properties-dialog.c            |  63 +++++++++++-
 configure.ac                               |   1 +
 data/session-properties.ui                 |  12 +++
 gnome-session/gsm-client.c                 |  10 ++
 gnome-session/gsm-client.h                 |   6 ++
 gnome-session/gsm-dbus-client.c            |  14 +++
 gnome-session/gsm-manager.c                | 150 ++++++++++++++++++++++++++++-
 gnome-session/gsm-manager.h                |   3 +
 gnome-session/gsm-xsmp-client.c            |  37 +++++++
 gnome-session/gsm-xsmp-client.h            |   3 +-
 gnome-session/org.gnome.SessionManager.xml |   8 ++
 11 files changed, 303 insertions(+), 4 deletions(-)

diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c
index 33812b8..d2be778 100644
--- a/capplet/gsm-properties-dialog.c
+++ b/capplet/gsm-properties-dialog.c
@@ -5,70 +5,77 @@
  * Copyright (C) 2008 Lucas Rocha.
  * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
  *
  * 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.
  *
  */
 
 #include "config.h"
 
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
 #include "gsm-properties-dialog.h"
 #include "gsm-app-dialog.h"
 #include "gsm-util.h"
 #include "gsp-app.h"
 #include "gsp-app-manager.h"
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#define GSM_SERVICE_DBUS   "org.gnome.SessionManager"
+#define GSM_PATH_DBUS      "/org/gnome/SessionManager"
+#define GSM_INTERFACE_DBUS "org.gnome.SessionManager"
 
 #define GSM_PROPERTIES_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogPrivate))
 
 #define GTKBUILDER_FILE "session-properties.ui"
 
 #define CAPPLET_TREEVIEW_WIDGET_NAME      "session_properties_treeview"
 #define CAPPLET_ADD_WIDGET_NAME           "session_properties_add_button"
 #define CAPPLET_DELETE_WIDGET_NAME        "session_properties_delete_button"
 #define CAPPLET_EDIT_WIDGET_NAME          "session_properties_edit_button"
 #define CAPPLET_SAVE_WIDGET_NAME          "session_properties_save_button"
+#define CAPPLET_SESSION_SAVED_WIDGET_NAME "session_properties_session_saved_label"
 #define CAPPLET_REMEMBER_WIDGET_NAME      "session_properties_remember_toggle"
 
 #define STARTUP_APP_ICON     "system-run"
 
 #define SPC_SETTINGS_SCHEMA          "org.gnome.SessionManager"
 #define SPC_SETTINGS_AUTOSAVE_KEY    "auto-save-session"
 
 struct GsmPropertiesDialogPrivate
 {
         GtkBuilder        *xml;
         GtkListStore      *list_store;
         GtkTreeModel      *tree_filter;
 
         GtkTreeView       *treeview;
         GtkWidget         *add_button;
         GtkWidget         *delete_button;
         GtkWidget         *edit_button;
 
         GSettings         *settings;
 
         GspAppManager     *manager;
 };
 
 enum {
         STORE_COL_VISIBLE = 0,
         STORE_COL_ENABLED,
         STORE_COL_GICON,
         STORE_COL_DESCRIPTION,
         STORE_COL_APP,
         STORE_COL_SEARCH,
@@ -431,64 +438,118 @@ on_edit_app_clicked (GtkWidget           *widget,
                 char       *comment;
 
                 edit_dialog = gsm_app_dialog_new (gsp_app_get_name (app),
                                                   gsp_app_get_exec (app),
                                                   gsp_app_get_comment (app));
                 gtk_window_set_transient_for (GTK_WINDOW (edit_dialog),
                                               GTK_WINDOW (dialog));
 
                 if (gsm_app_dialog_run (GSM_APP_DIALOG (edit_dialog),
                                         &name, &exec, &comment)) {
                         gsp_app_update (app, name, comment, exec);
                         g_free (name);
                         g_free (exec);
                         g_free (comment);
                 }
 
                 g_object_unref (app);
         }
 }
 
 static void
 on_row_activated (GtkTreeView         *tree_view,
                   GtkTreePath         *path,
                   GtkTreeViewColumn   *column,
                   GsmPropertiesDialog *dialog)
 {
         on_edit_app_clicked (NULL, dialog);
 }
 
 static void
+session_saved_message (GsmPropertiesDialog *dialog,
+                       const char *msg,
+                       gboolean is_error)
+{
+        GtkLabel *label;
+        gchar *markup;
+        label = GTK_LABEL (gtk_builder_get_object (dialog->priv->xml, CAPPLET_SESSION_SAVED_WIDGET_NAME));
+        if (is_error)
+                markup = g_markup_printf_escaped ("<span foreground=\"red\">%s</span>", msg);
+        else
+                markup = g_markup_escape_text (msg, -1);
+        gtk_label_set_markup (label, markup);
+        g_free (markup);
+}
+
+static void
+session_saved_cb (DBusGProxy *proxy,
+                  DBusGProxyCall *call_id,
+                  void *user_data)
+{
+        gboolean res;
+        GsmPropertiesDialog *dialog = user_data;
+
+        res = dbus_g_proxy_end_call (proxy, call_id, NULL, G_TYPE_INVALID);
+        if (res)
+                session_saved_message (dialog, _("Your session has been saved."), FALSE);
+        else
+                session_saved_message (dialog, _("Failed to save session"), TRUE);
+
+        g_object_unref (proxy);
+}
+
+static void
 on_save_session_clicked (GtkWidget           *widget,
                          GsmPropertiesDialog *dialog)
 {
-        g_debug ("Session saving is not implemented yet!");
+        DBusGConnection *conn;
+        DBusGProxy *proxy;
+        DBusGProxyCall *call;
+
+        conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+        if (conn == NULL) {
+                session_saved_message (dialog, _("Could not connect to the session bus"), TRUE);
+                return;
+        }
+
+        proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS);
+        if (proxy == NULL) {
+                session_saved_message (dialog, _("Could not connect to the session manager"), TRUE);
+                return;
+        }
+
+        call = dbus_g_proxy_begin_call (proxy, "SaveSession", session_saved_cb, dialog, NULL, G_TYPE_INVALID);
+        if (call == NULL) {
+                session_saved_message (dialog, _("Failed to save session"), TRUE);
+                g_object_unref (proxy);
+                return;
+        }
 }
 
 static void
 setup_dialog (GsmPropertiesDialog *dialog)
 {
         GtkTreeView       *treeview;
         GtkWidget         *button;
         GtkTreeModel      *tree_filter;
         GtkTreeViewColumn *column;
         GtkCellRenderer   *renderer;
         GtkTreeSelection  *selection;
         GtkTargetList     *targetlist;
 
         gtk_dialog_add_buttons (GTK_DIALOG (dialog),
                                 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
                                 NULL);
 
         dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
                                                        G_TYPE_BOOLEAN,
                                                        G_TYPE_BOOLEAN,
                                                        G_TYPE_ICON,
                                                        G_TYPE_STRING,
                                                        G_TYPE_OBJECT,
                                                        G_TYPE_STRING);
         tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store),
                                                  NULL);
         g_object_unref (dialog->priv->list_store);
         dialog->priv->tree_filter = tree_filter;
 
         gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter),
diff --git a/configure.ac b/configure.ac
index 0990e70..505292c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -52,60 +52,61 @@ if test "$enable_session_selector" = yes; then
 fi
 
 dnl ====================================================================
 dnl Dependency Checks
 dnl ====================================================================
 
 dnl Standard vertical stacks
 PKG_CHECK_MODULES(GIO, gio-2.0)
 PKG_CHECK_MODULES(GIOUNIX, gio-unix-2.0 >= $GLIB_REQUIRED)
 PKG_CHECK_MODULES(GTK3, gtk+-3.0 >= $GTK3_REQUIRED)
 
 PKG_CHECK_MODULES(GNOME_SESSION,
         glib-2.0 >= $GLIB_REQUIRED
         gio-2.0 >= $GLIB_REQUIRED
         dbus-glib-1 >= $DBUS_GLIB_REQUIRED
         json-glib-1.0 >= $JSON_GLIB_REQUIRED
         gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED
 )
 
 dnl We can only support old upower
 dnl https://bugzilla.gnome.org/show_bug.cgi?id=710383
 PKG_CHECK_MODULES(UPOWER, upower-glib < 0.99.0, have_old_upower=yes, have_old_upower=no)
 AS_IF([test x$have_old_upower = xyes], [
   AC_DEFINE([HAVE_OLD_UPOWER], [1], [Define if we have an older upower])
 ])
 AM_CONDITIONAL(HAVE_OLD_UPOWER, test x$have_old_upower = xyes)
 
 PKG_CHECK_MODULES(SESSION_PROPERTIES,
         glib-2.0 >= $GLIB_REQUIRED
         gtk+-3.0 >= $GTK3_REQUIRED
+        dbus-glib-1 >= $DBUS_GLIB_REQUIRED
 )
 
 PKG_CHECK_MODULES(X11, x11)
 PKG_CHECK_MODULES(SM, sm)
 PKG_CHECK_MODULES(ICE, ice)
 PKG_CHECK_MODULES(XEXT, xext xau)
 
 PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1 >= $DBUS_GLIB_REQUIRED)
 
 PKG_CHECK_MODULES(EGG_SMCLIENT, gtk+-3.0)
 
 PKG_CHECK_MODULES(GL_TEST, xcomposite gl glib-2.0)
 
 dnl ====================================================================
 dnl Check for gconf
 dnl ====================================================================
 AC_ARG_ENABLE([gconf],
               AS_HELP_STRING([--enable-gconf], [Support gconf-based autostart]),
               [enable_gconf=$enableval],
               [enable_gconf=auto])
 
 PKG_CHECK_MODULES(GCONF, gconf-2.0, [have_gconf=yes], [have_gconf=no])
 
 if test x$enable_gconf = xauto ; then
         enable_gconf=$have_gconf
 elif test x$enable_gconf = xyes -a x$have_gconf = xno ; then
         AC_MSG_ERROR([GConf support explicitly required, but gconf not found])
 fi
 
 if test x$enable_gconf = xyes ; then
diff --git a/data/session-properties.ui b/data/session-properties.ui
index 47a30f7..b43759f 100644
--- a/data/session-properties.ui
+++ b/data/session-properties.ui
@@ -133,108 +133,120 @@
       </packing>
     </child>
     <child>
       <object class="GtkVBox" id="vbox3">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="border_width">12</property>
         <property name="spacing">6</property>
         <child>
           <object class="GtkCheckButton" id="session_properties_remember_toggle">
             <property name="label" translatable="yes">_Automatically remember running applications when logging out</property>
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <property name="receives_default">False</property>
             <property name="use_underline">True</property>
             <property name="xalign">0.5</property>
             <property name="draw_indicator">True</property>
           </object>
           <packing>
             <property name="expand">False</property>
             <property name="fill">False</property>
             <property name="position">0</property>
           </packing>
         </child>
         <child>
           <object class="GtkHButtonBox" id="hbuttonbox1">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <child>
               <object class="GtkButton" id="session_properties_save_button">
+                <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
                 <child>
                   <object class="GtkHBox" id="hbox2">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="spacing">4</property>
                     <child>
                       <object class="GtkImage" id="image1">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="stock">gtk-save</property>
                       </object>
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
                         <property name="position">0</property>
                       </packing>
                     </child>
                     <child>
                       <object class="GtkLabel" id="label7">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="label" translatable="yes">_Remember Currently Running Applications</property>
                         <property name="use_underline">True</property>
                       </object>
                       <packing>
                         <property name="expand">True</property>
                         <property name="fill">True</property>
                         <property name="position">1</property>
                       </packing>
                     </child>
                   </object>
                 </child>
               </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
                 <property name="position">0</property>
               </packing>
             </child>
           </object>
           <packing>
             <property name="expand">False</property>
             <property name="fill">False</property>
             <property name="position">1</property>
           </packing>
         </child>
+        <child>
+          <object class="GtkLabel" id="session_properties_session_saved_label">
+            <property name="visible">True</property>
+            <property name="wrap">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
       </object>
       <packing>
         <property name="position">1</property>
       </packing>
     </child>
     <child type="tab">
       <object class="GtkLabel" id="label5">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="label" translatable="yes">Options</property>
       </object>
       <packing>
         <property name="position">1</property>
         <property name="tab_fill">False</property>
       </packing>
     </child>
   </object>
   <object class="GtkTable" id="main-table">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
     <property name="border_width">6</property>
     <property name="n_rows">3</property>
     <property name="n_columns">2</property>
     <property name="column_spacing">12</property>
     <property name="row_spacing">6</property>
     <child>
       <object class="GtkHBox" id="hbox3">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="spacing">12</property>
diff --git a/gnome-session/gsm-client.c b/gnome-session/gsm-client.c
index 543d7ef..23f0f56 100644
--- a/gnome-session/gsm-client.c
+++ b/gnome-session/gsm-client.c
@@ -493,49 +493,59 @@ gsm_client_query_end_session (GsmClient                *client,
         g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
 
         return GSM_CLIENT_GET_CLASS (client)->impl_query_end_session (client, flags, error);
 }
 
 gboolean
 gsm_client_end_session (GsmClient                *client,
                         GsmClientEndSessionFlag   flags,
                         GError                  **error)
 {
         g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
 
         return GSM_CLIENT_GET_CLASS (client)->impl_end_session (client, flags, error);
 }
 
 gboolean
 gsm_client_stop (GsmClient *client,
                  GError   **error)
 {
         g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
 
         return GSM_CLIENT_GET_CLASS (client)->impl_stop (client, error);
 }
 
 void
 gsm_client_disconnected (GsmClient *client)
 {
         g_signal_emit (client, signals[DISCONNECTED], 0);
 }
 
+gboolean
+gsm_client_request_save (GsmClient *client,
+                         guint      flags,
+                         GError   **error)
+{
+        g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
+
+        return GSM_CLIENT_GET_CLASS (client)->impl_request_save (client, flags, error);
+}
+
 GKeyFile *
 gsm_client_save (GsmClient *client,
                  GError   **error)
 {
         g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
 
         return GSM_CLIENT_GET_CLASS (client)->impl_save (client, error);
 }
 
 void
 gsm_client_end_session_response (GsmClient  *client,
                                  gboolean    is_ok,
                                  gboolean    do_last,
                                  gboolean    cancel,
                                  const char *reason)
 {
         g_signal_emit (client, signals[END_SESSION_RESPONSE], 0,
                        is_ok, do_last, cancel, reason);
 }
diff --git a/gnome-session/gsm-client.h b/gnome-session/gsm-client.h
index 4ef5dc9..46c913a 100644
--- a/gnome-session/gsm-client.h
+++ b/gnome-session/gsm-client.h
@@ -63,105 +63,111 @@ struct _GsmClient
         GObject           parent;
         GsmClientPrivate *priv;
 };
 
 struct _GsmClientClass
 {
         GObjectClass parent_class;
 
         /* signals */
         void         (*disconnected)               (GsmClient  *client);
         void         (*end_session_response)       (GsmClient  *client,
                                                     gboolean    ok,
                                                     gboolean    do_last,
                                                     gboolean    cancel,
                                                     const char *reason);
 
         /* virtual methods */
         char *                (*impl_get_app_name)           (GsmClient *client);
         GsmClientRestartStyle (*impl_get_restart_style_hint) (GsmClient *client);
         guint                 (*impl_get_unix_process_id)    (GsmClient *client);
         gboolean              (*impl_query_end_session)      (GsmClient *client,
                                                               GsmClientEndSessionFlag flags,
                                                               GError   **error);
         gboolean              (*impl_end_session)            (GsmClient *client,
                                                               GsmClientEndSessionFlag flags,
                                                               GError   **error);
         gboolean              (*impl_cancel_end_session)     (GsmClient *client,
                                                               GError   **error);
         gboolean              (*impl_stop)                   (GsmClient *client,
                                                               GError   **error);
+        gboolean              (*impl_request_save)           (GsmClient *client,
+                                                              guint      flags,
+                                                              GError   **error);
         GKeyFile *            (*impl_save)                   (GsmClient *client,
                                                               GError   **error);
 };
 
 typedef enum
 {
         GSM_CLIENT_ERROR_GENERAL = 0,
         GSM_CLIENT_ERROR_NOT_REGISTERED,
         GSM_CLIENT_NUM_ERRORS
 } GsmClientError;
 
 #define GSM_CLIENT_ERROR gsm_client_error_quark ()
 #define GSM_CLIENT_TYPE_ERROR (gsm_client_error_get_type ())
 
 GType                 gsm_client_error_get_type             (void);
 GQuark                gsm_client_error_quark                (void);
 
 GType                 gsm_client_get_type                   (void) G_GNUC_CONST;
 
 const char           *gsm_client_peek_id                    (GsmClient  *client);
 
 
 const char *          gsm_client_peek_startup_id            (GsmClient  *client);
 const char *          gsm_client_peek_app_id                (GsmClient  *client);
 guint                 gsm_client_peek_restart_style_hint    (GsmClient  *client);
 guint                 gsm_client_peek_status                (GsmClient  *client);
 
 
 char                 *gsm_client_get_app_name               (GsmClient  *client);
 void                  gsm_client_set_app_id                 (GsmClient  *client,
                                                              const char *app_id);
 void                  gsm_client_set_status                 (GsmClient  *client,
                                                              guint       status);
 
 gboolean              gsm_client_end_session                (GsmClient  *client,
                                                              guint       flags,
                                                              GError    **error);
 gboolean              gsm_client_query_end_session          (GsmClient  *client,
                                                              guint       flags,
                                                              GError    **error);
 gboolean              gsm_client_cancel_end_session         (GsmClient  *client,
                                                              GError    **error);
 
 void                  gsm_client_disconnected               (GsmClient  *client);
 
+gboolean              gsm_client_request_save               (GsmClient  *client,
+                                                             guint       flags,
+                                                             GError    **error);
 GKeyFile             *gsm_client_save                       (GsmClient  *client,
                                                              GError    **error);
 /* exported to bus */
 gboolean              gsm_client_stop                       (GsmClient  *client,
                                                              GError    **error);
 gboolean              gsm_client_get_startup_id             (GsmClient  *client,
                                                              char      **startup_id,
                                                              GError    **error);
 gboolean              gsm_client_get_app_id                 (GsmClient  *client,
                                                              char      **app_id,
                                                              GError    **error);
 gboolean              gsm_client_get_restart_style_hint     (GsmClient  *client,
                                                              guint      *hint,
                                                              GError    **error);
 gboolean              gsm_client_get_status                 (GsmClient  *client,
                                                              guint      *status,
                                                              GError    **error);
 gboolean              gsm_client_get_unix_process_id        (GsmClient  *client,
                                                              guint      *pid,
                                                              GError    **error);
 
 /* private */
 
 void                  gsm_client_end_session_response       (GsmClient  *client,
                                                              gboolean    is_ok,
                                                              gboolean    do_last,
                                                              gboolean    cancel,
                                                              const char *reason);
 
 G_END_DECLS
diff --git a/gnome-session/gsm-dbus-client.c b/gnome-session/gsm-dbus-client.c
index fdd43d2..16c3df0 100644
--- a/gnome-session/gsm-dbus-client.c
+++ b/gnome-session/gsm-dbus-client.c
@@ -388,60 +388,73 @@ gsm_dbus_client_set_property (GObject       *object,
 static void
 gsm_dbus_client_get_property (GObject    *object,
                               guint       prop_id,
                               GValue     *value,
                               GParamSpec *pspec)
 {
         GsmDBusClient *self;
 
         self = GSM_DBUS_CLIENT (object);
 
         switch (prop_id) {
         case PROP_BUS_NAME:
                 g_value_set_string (value, self->priv->bus_name);
                 break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
         }
 }
 
 static void
 gsm_dbus_client_finalize (GObject *object)
 {
         GsmDBusClient *client = (GsmDBusClient *) object;
 
         g_free (client->priv->bus_name);
 
         G_OBJECT_CLASS (gsm_dbus_client_parent_class)->finalize (object);
 }
 
+static gboolean
+dbus_client_request_save (GsmClient *client,
+                          guint      flags,
+                          GError   **error)
+{
+        g_debug ("GsmDBusClient: sending save request to client with id %s",
+                 gsm_client_peek_id (client));
+
+        /* FIXME: The protocol does not support this */
+
+        return FALSE;
+}
+
 static GKeyFile *
 dbus_client_save (GsmClient *client,
                   GError   **error)
 {
         g_debug ("GsmDBusClient: saving client with id %s",
                  gsm_client_peek_id (client));
 
         /* FIXME: We still don't support client saving for D-Bus
          * session clients */
 
         return NULL;
 }
 
 static gboolean
 dbus_client_stop (GsmClient *client,
                   GError   **error)
 {
         GsmDBusClient  *dbus_client = (GsmDBusClient *) client;
         DBusMessage    *message;
         gboolean        ret;
 
         ret = FALSE;
 
         /* unicast the signal to only the registered bus name */
         message = dbus_message_new_signal (gsm_client_peek_id (client),
                                            SM_DBUS_CLIENT_PRIVATE_INTERFACE,
                                            "Stop");
         if (message == NULL) {
                 goto out;
         }
@@ -640,60 +653,61 @@ dbus_client_cancel_end_session (GsmClient *client,
         return ret;
 }
 
 static void
 gsm_dbus_client_dispose (GObject *object)
 {
         GsmDBusClient *client;
 
         g_return_if_fail (object != NULL);
         g_return_if_fail (GSM_IS_DBUS_CLIENT (object));
 
         client = GSM_DBUS_CLIENT (object);
 
         dbus_connection_remove_filter (client->priv->connection, client_dbus_filter_function, client);
 
         G_OBJECT_CLASS (gsm_dbus_client_parent_class)->dispose (object);
 }
 
 static void
 gsm_dbus_client_class_init (GsmDBusClientClass *klass)
 {
         GObjectClass   *object_class = G_OBJECT_CLASS (klass);
         GsmClientClass *client_class = GSM_CLIENT_CLASS (klass);
 
         object_class->finalize             = gsm_dbus_client_finalize;
         object_class->constructor          = gsm_dbus_client_constructor;
         object_class->get_property         = gsm_dbus_client_get_property;
         object_class->set_property         = gsm_dbus_client_set_property;
         object_class->dispose              = gsm_dbus_client_dispose;
 
+        client_class->impl_request_save           = dbus_client_request_save;
         client_class->impl_save                   = dbus_client_save;
         client_class->impl_stop                   = dbus_client_stop;
         client_class->impl_query_end_session      = dbus_client_query_end_session;
         client_class->impl_end_session            = dbus_client_end_session;
         client_class->impl_cancel_end_session     = dbus_client_cancel_end_session;
         client_class->impl_get_app_name           = dbus_client_get_app_name;
         client_class->impl_get_restart_style_hint = dbus_client_get_restart_style_hint;
         client_class->impl_get_unix_process_id    = dbus_client_get_unix_process_id;
 
         g_object_class_install_property (object_class,
                                          PROP_BUS_NAME,
                                          g_param_spec_string ("bus-name",
                                                               "bus-name",
                                                               "bus-name",
                                                               NULL,
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
         g_type_class_add_private (klass, sizeof (GsmDBusClientPrivate));
 }
 
 GsmClient *
 gsm_dbus_client_new (const char *startup_id,
                      const char *bus_name)
 {
         GsmDBusClient *client;
 
         client = g_object_new (GSM_TYPE_DBUS_CLIENT,
                                "startup-id", startup_id,
                                "bus-name", bus_name,
                                NULL);
diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c
index c32bbe4..67e0c82 100644
--- a/gnome-session/gsm-manager.c
+++ b/gnome-session/gsm-manager.c
@@ -55,60 +55,61 @@
 #include "gsm-xsmp-client.h"
 #include "gsm-dbus-client.h"
 
 #include "gsm-autostart-app.h"
 
 #include "gsm-util.h"
 #include "gsm-icon-names.h"
 #include "gsm-system.h"
 #include "gsm-session-save.h"
 #include "gsm-shell-extensions.h"
 #include "gsm-fail-whale.h"
 
 #define GSM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_MANAGER, GsmManagerPrivate))
 
 /* UUIDs for log messages */
 #define GSM_MANAGER_STARTUP_SUCCEEDED_MSGID     "0ce153587afa4095832d233c17a88001"
 #define GSM_MANAGER_UNRECOVERABLE_FAILURE_MSGID "10dd2dc188b54a5e98970f56499d1f73"
 
 #define GSM_MANAGER_DBUS_PATH "/org/gnome/SessionManager"
 #define GSM_MANAGER_DBUS_NAME "org.gnome.SessionManager"
 
 /* Probably about the longest amount of time someone could reasonably
  * want to wait, at least for something happening more than once.
  * We can get deployed on very slow media though like CDROM devices,
  * often with complex stacking/compressing filesystems on top, which
  * is not a recipie for speed.   Particularly now that we throw up
  * a fail whale if required components don't show up quickly enough,
  * let's make this fairly long.
  */
 #define GSM_MANAGER_PHASE_TIMEOUT 90 /* seconds */
+#define GSM_MANAGER_SAVE_SESSION_TIMEOUT 2
 
 #define GDM_FLEXISERVER_COMMAND "gdmflexiserver"
 #define GDM_FLEXISERVER_ARGS    "--startnew Standard"
 
 #define SESSION_SCHEMA            "org.gnome.desktop.session"
 #define KEY_IDLE_DELAY            "idle-delay"
 #define KEY_SESSION_NAME          "session-name"
 
 #define GSM_MANAGER_SCHEMA        "org.gnome.SessionManager"
 #define KEY_AUTOSAVE              "auto-save-session"
 #define KEY_AUTOSAVE_ONE_SHOT     "auto-save-session-one-shot"
 #define KEY_LOGOUT_PROMPT         "logout-prompt"
 #define KEY_SHOW_FALLBACK_WARNING "show-fallback-warning"
 
 #define SCREENSAVER_SCHEMA        "org.gnome.desktop.screensaver"
 #define KEY_SLEEP_LOCK            "lock-enabled"
 
 #define LOCKDOWN_SCHEMA           "org.gnome.desktop.lockdown"
 #define KEY_DISABLE_LOG_OUT       "disable-log-out"
 #define KEY_DISABLE_USER_SWITCHING "disable-user-switching"
 
 static void app_registered (GsmApp     *app, GsmManager *manager);
 
 typedef enum
 {
         GSM_MANAGER_LOGOUT_NONE,
         GSM_MANAGER_LOGOUT_LOGOUT,
         GSM_MANAGER_LOGOUT_REBOOT,
         GSM_MANAGER_LOGOUT_REBOOT_INTERACT,
         GSM_MANAGER_LOGOUT_SHUTDOWN,
@@ -1177,60 +1178,123 @@ end_session_or_show_shell_dialog (GsmManager *manager)
                         end_phase (manager);
                 }
                 break;
 
         case GSM_MANAGER_LOGOUT_MODE_FORCE:
                 end_phase (manager);
                 break;
         default:
                 g_assert_not_reached ();
                 break;
         }
 
 }
 
 static void
 query_end_session_complete (GsmManager *manager)
 {
 
         g_debug ("GsmManager: query end session complete");
 
         /* Remove the timeout since this can be called from outside the timer
          * and we don't want to have it called twice */
         if (manager->priv->query_timeout_id > 0) {
                 g_source_remove (manager->priv->query_timeout_id);
                 manager->priv->query_timeout_id = 0;
         }
 
         end_session_or_show_shell_dialog (manager);
 }
 
+static gboolean
+_client_request_save (GsmClient            *client,
+                      ClientEndSessionData *data)
+{
+        gboolean ret;
+        GError  *error;
+
+        error = NULL;
+        ret = gsm_client_request_save (client, data->flags, &error);
+        if (ret) {
+                g_debug ("GsmManager: adding client to query clients: %s", gsm_client_peek_id (client));
+                data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients,
+                                                                      client);
+        } else if (error) {
+                g_debug ("GsmManager: unable to query client: %s", error->message);
+                g_error_free (error);
+        }
+
+        return FALSE;
+}
+
+static gboolean
+_client_request_save_helper (const char           *id,
+                             GsmClient            *client,
+                             ClientEndSessionData *data)
+{
+        return _client_request_save (client, data);
+}
+
+static void
+query_save_session_complete (GsmManager *manager)
+{
+        GError *error = NULL;
+
+        if (g_slist_length (manager->priv->next_query_clients) > 0) {
+                ClientEndSessionData data;
+
+                data.manager = manager;
+                data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST;
+
+                g_slist_foreach (manager->priv->next_query_clients,
+                                 (GFunc)_client_request_save,
+                                 &data);
+
+                g_slist_free (manager->priv->next_query_clients);
+                manager->priv->next_query_clients = NULL;
+
+                return;
+        }
+
+        if (manager->priv->query_timeout_id > 0) {
+                g_source_remove (manager->priv->query_timeout_id);
+                manager->priv->query_timeout_id = 0;
+        }
+
+        gsm_session_save (manager->priv->clients, &error);
+
+        if (error) {
+                g_warning ("Error saving session: %s", error->message);
+                g_error_free (error);
+        }
+}
+
 static guint32
 generate_cookie (void)
 {
         guint32 cookie;
 
         cookie = (guint32)g_random_int_range (1, G_MAXINT32);
 
         return cookie;
 }
 
 static guint32
 _generate_unique_cookie (GsmManager *manager)
 {
         guint32 cookie;
 
         do {
                 cookie = generate_cookie ();
         } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL);
 
         return cookie;
 }
 
 static gboolean
 _on_query_end_session_timeout (GsmManager *manager)
 {
         GSList *l;
 
         manager->priv->query_timeout_id = 0;
 
         g_debug ("GsmManager: query end session timed out");
@@ -1257,60 +1321,75 @@ _on_query_end_session_timeout (GsmManager *manager)
                         bus_name = NULL;
                 }
 
                 app_id = g_strdup (gsm_client_peek_app_id (l->data));
                 if (IS_STRING_EMPTY (app_id)) {
                         /* XSMP clients don't give us an app id unless we start them */
                         g_free (app_id);
                         app_id = gsm_client_get_app_name (l->data);
                 }
 
                 cookie = _generate_unique_cookie (manager);
                 inhibitor = gsm_inhibitor_new_for_client (gsm_client_peek_id (l->data),
                                                           app_id,
                                                           GSM_INHIBITOR_FLAG_LOGOUT,
                                                           _("Not responding"),
                                                           bus_name,
                                                           cookie);
                 g_free (app_id);
                 gsm_store_add (manager->priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor));
                 g_object_unref (inhibitor);
         }
 
         g_slist_free (manager->priv->query_clients);
         manager->priv->query_clients = NULL;
 
         query_end_session_complete (manager);
 
         return FALSE;
 }
 
+static gboolean
+_on_query_save_session_timeout (GsmManager *manager)
+{
+        manager->priv->query_timeout_id = 0;
+
+        g_debug ("GsmManager: query to save session timed out");
+
+        g_slist_free (manager->priv->query_clients);
+        manager->priv->query_clients = NULL;
+
+        query_save_session_complete (manager);
+
+        return FALSE;
+}
+
 static void
 do_phase_query_end_session (GsmManager *manager)
 {
         ClientEndSessionData data;
 
         data.manager = manager;
         data.flags = 0;
 
         if (manager->priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) {
                 data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL;
         }
         /* We only query if an app is ready to log out, so we don't use
          * GSM_CLIENT_END_SESSION_FLAG_SAVE here.
          */
 
         debug_clients (manager);
         g_debug ("GsmManager: sending query-end-session to clients (logout mode: %s)",
                  manager->priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_NORMAL? "normal" :
                  manager->priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE? "forceful":
                  "no confirmation");
         gsm_store_foreach (manager->priv->clients,
                            (GsmStoreFunc)_client_query_end_session,
                            &data);
 
         /* This phase doesn't time out unless logout is forced. Typically, this
          * separate timer is only used to show UI. */
         manager->priv->query_timeout_id = g_timeout_add_seconds (1, (GSourceFunc)_on_query_end_session_timeout, manager);
 }
 
 static void
@@ -1972,67 +2051,86 @@ maybe_save_session (GsmManager *manager)
                 return;
 
         /* We only allow session saving when session is running or when
          * logging out */
         if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING &&
             manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) {
                 return;
         }
 
         if (!auto_save_is_enabled (manager)) {
                 return;
         }
 
         error = NULL;
         gsm_session_save (manager->priv->clients, &error);
 
         if (error) {
                 g_warning ("Error saving session: %s", error->message);
                 g_error_free (error);
         }
 }
 
 static void
 _handle_client_end_session_response (GsmManager *manager,
                                      GsmClient  *client,
                                      gboolean    is_ok,
                                      gboolean    do_last,
                                      gboolean    cancel,
                                      const char *reason)
 {
-        /* just ignore if received outside of shutdown */
-        if (manager->priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+        /* just ignore if we are not yet running */
+        if (manager->priv->phase < GSM_MANAGER_PHASE_RUNNING) {
                 return;
         }
 
         g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :"");
 
+        if (manager->priv->phase == GSM_MANAGER_PHASE_RUNNING) {
+                /* Ignore responses when no requests were sent */
+                if (manager->priv->query_clients == NULL) {
+                        return;
+                }
+
+                manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client);
+
+                if (do_last) {
+                        manager->priv->next_query_clients = g_slist_prepend (manager->priv->next_query_clients,
+                                                                             client);
+                }
+
+                if (manager->priv->query_clients == NULL) {
+                        query_save_session_complete (manager);
+                }
+                return;
+        }
+
         if (cancel) {
                 cancel_end_session (manager);
                 return;
         }
 
         manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client);
 
         if (! is_ok && manager->priv->logout_mode != GSM_MANAGER_LOGOUT_MODE_FORCE) {
                 guint         cookie;
                 GsmInhibitor *inhibitor;
                 char         *app_id;
                 const char   *bus_name;
 
                 /* FIXME: do we support updating the reason? */
 
                 /* Create JIT inhibit */
                 if (GSM_IS_DBUS_CLIENT (client)) {
                         bus_name = gsm_dbus_client_get_bus_name (GSM_DBUS_CLIENT (client));
                 } else {
                         bus_name = NULL;
                 }
 
                 app_id = g_strdup (gsm_client_peek_app_id (client));
                 if (IS_STRING_EMPTY (app_id)) {
                         /* XSMP clients don't give us an app id unless we start them */
                         g_free (app_id);
                         app_id = gsm_client_get_app_name (client);
                 }
 
                 cookie = _generate_unique_cookie (manager);
@@ -2093,84 +2191,97 @@ on_client_end_session_response (GsmClient  *client,
                                              client,
                                              is_ok,
                                              do_last,
                                              cancel,
                                              reason);
 }
 
 static void
 on_xsmp_client_logout_request (GsmXSMPClient *client,
                                gboolean       show_dialog,
                                GsmManager    *manager)
 {
         GError *error;
         int     logout_mode;
 
         if (show_dialog) {
                 logout_mode = GSM_MANAGER_LOGOUT_MODE_NORMAL;
         } else {
                 logout_mode = GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION;
         }
 
         error = NULL;
         gsm_manager_logout (manager, logout_mode, &error);
         if (error != NULL) {
                 g_warning ("Unable to logout: %s", error->message);
                 g_error_free (error);
         }
 }
 
 static void
+on_xsmp_client_save_request (GsmXSMPClient *client,
+                             gboolean       show_dialog,
+                             GsmManager    *manager)
+{
+        g_debug ("GsmManager: save_request");
+        gsm_manager_save_session (manager, NULL);
+}
+
+static void
 on_store_client_added (GsmStore   *store,
                        const char *id,
                        GsmManager *manager)
 {
         GsmClient *client;
 
         g_debug ("GsmManager: Client added: %s", id);
 
         client = (GsmClient *)gsm_store_lookup (store, id);
 
         /* a bit hacky */
         if (GSM_IS_XSMP_CLIENT (client)) {
                 g_signal_connect (client,
                                   "register-request",
                                   G_CALLBACK (on_xsmp_client_register_request),
                                   manager);
                 g_signal_connect (client,
                                   "register-confirmed",
                                   G_CALLBACK (on_xsmp_client_register_confirmed),
                                   manager);
                 g_signal_connect (client,
                                   "logout-request",
                                   G_CALLBACK (on_xsmp_client_logout_request),
                                   manager);
+		g_signal_connect (client,
+				  "save-request",
+				  G_CALLBACK (on_xsmp_client_save_request),
+				  manager);
         }
 
         g_signal_connect (client,
                           "end-session-response",
                           G_CALLBACK (on_client_end_session_response),
                           manager);
 
         g_signal_emit (manager, signals [CLIENT_ADDED], 0, id);
         /* FIXME: disconnect signal handler */
 }
 
 static void
 on_store_client_removed (GsmStore   *store,
                          const char *id,
                          GsmManager *manager)
 {
         g_debug ("GsmManager: Client removed: %s", id);
 
         g_signal_emit (manager, signals [CLIENT_REMOVED], 0, id);
 }
 
 static void
 gsm_manager_set_client_store (GsmManager *manager,
                               GsmStore   *store)
 {
         g_return_if_fail (GSM_IS_MANAGER (manager));
 
         if (store != NULL) {
                 g_object_ref (store);
         }
@@ -3127,60 +3238,95 @@ gsm_manager_reboot (GsmManager            *manager,
         if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) {
                 error = g_error_new (GSM_MANAGER_ERROR,
                                      GSM_MANAGER_ERROR_NOT_IN_RUNNING,
                                      "Reboot interface is only available during the Running phase");
                 dbus_g_method_return_error (context, error);
                 g_error_free (error);
                 return FALSE;
         }
 
         if (_log_out_is_locked_down (manager)) {
                 error = g_error_new (GSM_MANAGER_ERROR,
                                      GSM_MANAGER_ERROR_LOCKED_DOWN,
                                      "Logout has been locked down");
                 dbus_g_method_return_error (context, error);
                 g_error_free (error);
                 return FALSE;
         }
 
         task = g_task_new (manager, manager->priv->end_session_cancellable, (GAsyncReadyCallback) complete_end_session_task, context);
 
         manager->priv->pending_end_session_tasks = g_slist_prepend (manager->priv->pending_end_session_tasks,
                                                                     task);
 
         request_reboot (manager);
 
         return TRUE;
 }
 
 
 gboolean
+gsm_manager_save_session (GsmManager *manager,
+                          GError     **error)
+{
+        ClientEndSessionData data;
+
+        g_debug ("GsmManager: SaveSession called");
+
+        g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+        if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) {
+                g_set_error (error,
+                             GSM_MANAGER_ERROR,
+                             GSM_MANAGER_ERROR_NOT_IN_RUNNING,
+                             "SaveSession interface is only available during the Running phase");
+                return FALSE;
+        }
+
+        data.manager = manager;
+        data.flags = 0;
+        gsm_store_foreach (manager->priv->clients,
+                           (GsmStoreFunc)_client_request_save_helper,
+                           &data);
+
+        if (manager->priv->query_clients) {
+                manager->priv->query_timeout_id = g_timeout_add_seconds (GSM_MANAGER_SAVE_SESSION_TIMEOUT,
+                                                                         (GSourceFunc)_on_query_save_session_timeout,
+                                                                         manager);
+                return TRUE;
+        } else {
+                g_debug ("GsmManager: Nothing to save");
+                return FALSE;
+        }
+}
+
+gboolean
 gsm_manager_can_shutdown (GsmManager *manager,
                           gboolean   *shutdown_available,
                           GError    **error)
 {
         g_debug ("GsmManager: CanShutdown called");
 
         g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
 
         *shutdown_available = !_log_out_is_locked_down (manager) &&
                               (gsm_system_can_stop (manager->priv->system)
                                || gsm_system_can_restart (manager->priv->system)
                                || gsm_system_can_suspend (manager->priv->system)
                                || gsm_system_can_hibernate (manager->priv->system));
 
         return TRUE;
 }
 
 gboolean
 gsm_manager_logout (GsmManager *manager,
                     guint       logout_mode,
                     GError    **error)
 {
         g_debug ("GsmManager: Logout called");
 
         g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
 
         if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) {
                 g_set_error (error,
                              GSM_MANAGER_ERROR,
                              GSM_MANAGER_ERROR_NOT_IN_RUNNING,
diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h
index a0030b9..bc16654 100644
--- a/gnome-session/gsm-manager.h
+++ b/gnome-session/gsm-manager.h
@@ -137,60 +137,63 @@ void                _gsm_manager_set_active_session            (GsmManager     *
 
 /* exported methods */
 
 gboolean            gsm_manager_register_client                (GsmManager            *manager,
                                                                 const char            *app_id,
                                                                 const char            *client_startup_id,
                                                                 DBusGMethodInvocation *context);
 gboolean            gsm_manager_unregister_client              (GsmManager            *manager,
                                                                 const char            *session_client_id,
                                                                 DBusGMethodInvocation *context);
 
 gboolean            gsm_manager_inhibit                        (GsmManager            *manager,
                                                                 const char            *app_id,
                                                                 guint                  toplevel_xid,
                                                                 const char            *reason,
                                                                 guint                  flags,
                                                                 DBusGMethodInvocation *context);
 gboolean            gsm_manager_uninhibit                      (GsmManager            *manager,
                                                                 guint                  inhibit_cookie,
                                                                 DBusGMethodInvocation *context);
 gboolean            gsm_manager_is_inhibited                   (GsmManager            *manager,
                                                                 guint                  flags,
                                                                 gboolean              *is_inhibited,
                                                                 GError                *error);
 
 gboolean            gsm_manager_shutdown                       (GsmManager     *manager,
                                                                 DBusGMethodInvocation *context);
 gboolean            gsm_manager_reboot                         (GsmManager     *manager,
                                                                 DBusGMethodInvocation *context);
 
+gboolean            gsm_manager_save_session                   (GsmManager     *manager,
+                                                                GError        **error);
+
 gboolean            gsm_manager_can_shutdown                   (GsmManager     *manager,
                                                                 gboolean       *shutdown_available,
                                                                 GError        **error);
 gboolean            gsm_manager_logout                         (GsmManager     *manager,
                                                                 guint           logout_mode,
                                                                 GError        **error);
 
 gboolean            gsm_manager_setenv                         (GsmManager     *manager,
                                                                 const char     *variable,
                                                                 const char     *value,
                                                                 GError        **error);
 gboolean            gsm_manager_get_locale                     (GsmManager     *manager,
                                                                 int             category,
                                                                 const char    **value,
                                                                 GError        **error);
 gboolean            gsm_manager_initialization_error           (GsmManager     *manager,
                                                                 const char     *message,
                                                                 gboolean        fatal,
                                                                 GError        **error);
 
 gboolean            gsm_manager_get_clients                    (GsmManager     *manager,
                                                                 GPtrArray     **clients,
                                                                 GError        **error);
 gboolean            gsm_manager_get_inhibitors                 (GsmManager     *manager,
                                                                 GPtrArray     **inhibitors,
                                                                 GError        **error);
 gboolean            gsm_manager_is_autostart_condition_handled (GsmManager     *manager,
                                                                 const char     *condition,
                                                                 gboolean       *handled,
                                                                 GError        **error);
diff --git a/gnome-session/gsm-xsmp-client.c b/gnome-session/gsm-xsmp-client.c
index c256acb..1cfb47f 100644
--- a/gnome-session/gsm-xsmp-client.c
+++ b/gnome-session/gsm-xsmp-client.c
@@ -39,60 +39,61 @@
 #define GsmDesktopFile "_GSM_DesktopFile"
 
 #define GSM_XSMP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientPrivate))
 
 struct GsmXSMPClientPrivate
 {
 
         SmsConn    conn;
         IceConn    ice_connection;
 
         guint      watch_id;
 
         char      *description;
         GPtrArray *props;
 
         /* SaveYourself state */
         int        current_save_yourself;
         int        next_save_yourself;
         guint      next_save_yourself_allow_interact : 1;
 };
 
 enum {
         PROP_0,
         PROP_ICE_CONNECTION
 };
 
 enum {
         REGISTER_REQUEST,
         REGISTER_CONFIRMED,
         LOGOUT_REQUEST,
+	SAVE_REQUEST,
         LAST_SIGNAL
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE (GsmXSMPClient, gsm_xsmp_client, GSM_TYPE_CLIENT)
 
 static gboolean
 client_iochannel_watch (GIOChannel    *channel,
                         GIOCondition   condition,
                         GsmXSMPClient *client)
 {
         gboolean keep_going;
 
         g_object_ref (client);
         switch (IceProcessMessages (client->priv->ice_connection, NULL, NULL)) {
         case IceProcessMessagesSuccess:
                 keep_going = TRUE;
                 break;
 
         case IceProcessMessagesIOError:
                 g_debug ("GsmXSMPClient: IceProcessMessagesIOError on '%s'", client->priv->description);
                 gsm_client_set_status (GSM_CLIENT (client), GSM_CLIENT_FAILED);
                 /* Emitting "disconnected" will eventually cause
                  * IceCloseConnection() to be called.
                  */
                 gsm_client_disconnected (GSM_CLIENT (client));
                 keep_going = FALSE;
                 break;
 
@@ -472,60 +473,84 @@ xsmp_interact (GsmClient *client)
 
         SmsInteract (xsmp->priv->conn);
 }
 
 static gboolean
 xsmp_cancel_end_session (GsmClient *client,
                          GError   **error)
 {
         GsmXSMPClient *xsmp = (GsmXSMPClient *) client;
 
         g_debug ("GsmXSMPClient: xsmp_cancel_end_session ('%s')", xsmp->priv->description);
 
         if (xsmp->priv->conn == NULL) {
                 g_set_error (error,
                              GSM_CLIENT_ERROR,
                              GSM_CLIENT_ERROR_NOT_REGISTERED,
                              "Client is not registered");
                 return FALSE;
         }
 
         SmsShutdownCancelled (xsmp->priv->conn);
 
         /* reset the state */
         xsmp->priv->current_save_yourself = -1;
         xsmp->priv->next_save_yourself = -1;
         xsmp->priv->next_save_yourself_allow_interact = FALSE;
 
         return TRUE;
 }
 
+static gboolean
+xsmp_request_save (GsmClient *client,
+                   guint      flags,
+                   GError   **error)
+{
+        GsmXSMPClient *xsmp = (GsmXSMPClient *) client;
+
+        g_debug ("GsmXSMPClient: xsmp_request_save ('%s')", xsmp->priv->description);
+
+        if (xsmp->priv->conn == NULL) {
+                g_set_error (error,
+                             GSM_CLIENT_ERROR,
+                             GSM_CLIENT_ERROR_NOT_REGISTERED,
+                             "Client is not registered");
+                return FALSE;
+        }
+
+        if (flags & GSM_CLIENT_END_SESSION_FLAG_LAST)
+                xsmp_save_yourself_phase2 (client);
+        else
+                do_save_yourself (xsmp, SmSaveLocal, FALSE);
+
+        return TRUE;
+}
 static char *
 get_desktop_file_path (GsmXSMPClient *client)
 {
         SmProp     *prop;
         char       *desktop_file_path = NULL;
         const char *program_name;
 
         /* XSMP clients using eggsmclient defines a special property
          * pointing to their respective desktop entry file */
         prop = find_property (client, GsmDesktopFile, NULL);
 
         if (prop) {
                 GFile *file = g_file_new_for_uri (prop->vals[0].value);
                 desktop_file_path = g_file_get_path (file);
                 g_object_unref (file);
                 goto out;
         }
 
         /* If we can't get desktop file from GsmDesktopFile then we
          * try to find the desktop file from its program name */
         prop = find_property (client, SmProgram, NULL);
 
         if (!prop) {
                 goto out;
         }
 
         program_name = prop->vals[0].value;
         desktop_file_path =
                 gsm_util_find_desktop_file_for_app_name (program_name,
                                                          TRUE, FALSE);
@@ -955,100 +980,112 @@ xsmp_get_unix_process_id (GsmClient *client)
         gboolean res;
 
         g_debug ("GsmXSMPClient: getting pid");
 
         prop = find_property (GSM_XSMP_CLIENT (client), SmProcessID, NULL);
 
         if (!prop || strcmp (prop->type, SmARRAY8) != 0) {
                 return 0;
         }
 
         pid = 0;
         res = _parse_value_as_uint ((char *)prop->vals[0].value, &pid);
         if (! res) {
                 pid = 0;
         }
 
         return pid;
 }
 
 static void
 gsm_xsmp_client_class_init (GsmXSMPClientClass *klass)
 {
         GObjectClass   *object_class = G_OBJECT_CLASS (klass);
         GsmClientClass *client_class = GSM_CLIENT_CLASS (klass);
 
         object_class->finalize             = gsm_xsmp_client_finalize;
         object_class->constructor          = gsm_xsmp_client_constructor;
         object_class->get_property         = gsm_xsmp_client_get_property;
         object_class->set_property         = gsm_xsmp_client_set_property;
 
+        client_class->impl_request_save           = xsmp_request_save;
         client_class->impl_save                   = xsmp_save;
         client_class->impl_stop                   = xsmp_stop;
         client_class->impl_query_end_session      = xsmp_query_end_session;
         client_class->impl_end_session            = xsmp_end_session;
         client_class->impl_cancel_end_session     = xsmp_cancel_end_session;
         client_class->impl_get_app_name           = xsmp_get_app_name;
         client_class->impl_get_restart_style_hint = xsmp_get_restart_style_hint;
         client_class->impl_get_unix_process_id    = xsmp_get_unix_process_id;
 
         signals[REGISTER_REQUEST] =
                 g_signal_new ("register-request",
                               G_OBJECT_CLASS_TYPE (object_class),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GsmXSMPClientClass, register_request),
                               _boolean_handled_accumulator,
                               NULL,
                               NULL,
                               G_TYPE_BOOLEAN,
                               1, G_TYPE_POINTER);
         signals[REGISTER_CONFIRMED] =
                 g_signal_new ("register-confirmed",
                               G_OBJECT_CLASS_TYPE (object_class),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GsmXSMPClientClass, register_confirmed),
                               NULL,
                               NULL,
                               NULL,
                               G_TYPE_NONE,
                               1, G_TYPE_POINTER);
         signals[LOGOUT_REQUEST] =
                 g_signal_new ("logout-request",
                               G_OBJECT_CLASS_TYPE (object_class),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GsmXSMPClientClass, logout_request),
                               NULL,
                               NULL,
                               NULL,
                               G_TYPE_NONE,
                               1, G_TYPE_BOOLEAN);
 
+	signals[SAVE_REQUEST] =
+		g_signal_new ("save-request",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GsmXSMPClientClass, save_request),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__BOOLEAN,
+                              G_TYPE_NONE,
+                              1, G_TYPE_BOOLEAN);
+
         g_object_class_install_property (object_class,
                                          PROP_ICE_CONNECTION,
                                          g_param_spec_pointer ("ice-connection",
                                                                "ice-connection",
                                                                "ice-connection",
                                                                G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
         g_type_class_add_private (klass, sizeof (GsmXSMPClientPrivate));
 }
 
 GsmClient *
 gsm_xsmp_client_new (IceConn ice_conn)
 {
         GsmXSMPClient *xsmp;
 
         xsmp = g_object_new (GSM_TYPE_XSMP_CLIENT,
                              "ice-connection", ice_conn,
                              NULL);
 
         return GSM_CLIENT (xsmp);
 }
 
 static Status
 register_client_callback (SmsConn    conn,
                           SmPointer  manager_data,
                           char      *previous_id)
 {
         GsmXSMPClient *client = manager_data;
         gboolean       handled;
         char          *id;
diff --git a/gnome-session/gsm-xsmp-client.h b/gnome-session/gsm-xsmp-client.h
index 1bb2797..6b95c51 100644
--- a/gnome-session/gsm-xsmp-client.h
+++ b/gnome-session/gsm-xsmp-client.h
@@ -27,61 +27,62 @@ G_BEGIN_DECLS
 
 #define GSM_TYPE_XSMP_CLIENT            (gsm_xsmp_client_get_type ())
 #define GSM_XSMP_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClient))
 #define GSM_XSMP_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass))
 #define GSM_IS_XSMP_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_XSMP_CLIENT))
 #define GSM_IS_XSMP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_XSMP_CLIENT))
 #define GSM_XSMP_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass))
 
 typedef struct _GsmXSMPClient        GsmXSMPClient;
 typedef struct _GsmXSMPClientClass   GsmXSMPClientClass;
 
 typedef struct GsmXSMPClientPrivate  GsmXSMPClientPrivate;
 
 struct _GsmXSMPClient
 {
         GsmClient             parent;
         GsmXSMPClientPrivate *priv;
 };
 
 struct _GsmXSMPClientClass
 {
         GsmClientClass parent_class;
 
         /* signals */
         gboolean (*register_request)     (GsmXSMPClient  *client,
                                           char          **client_id);
         void     (*register_confirmed)   (GsmXSMPClient  *client,
                                           const char     *client_id);
         gboolean (*logout_request)       (GsmXSMPClient  *client,
                                           gboolean        prompt);
-
+        gboolean (*save_request)         (GsmXSMPClient  *client,
+                                          gboolean        prompt);
 
         void     (*saved_state)          (GsmXSMPClient  *client);
 
         void     (*request_phase2)       (GsmXSMPClient  *client);
 
         void     (*request_interaction)  (GsmXSMPClient  *client);
         void     (*interaction_done)     (GsmXSMPClient  *client,
                                           gboolean        cancel_shutdown);
 
         void     (*save_yourself_done)   (GsmXSMPClient  *client);
 
 };
 
 GType       gsm_xsmp_client_get_type             (void) G_GNUC_CONST;
 
 GsmClient  *gsm_xsmp_client_new                  (IceConn         ice_conn);
 
 void        gsm_xsmp_client_connect              (GsmXSMPClient  *client,
                                                   SmsConn         conn,
                                                   unsigned long  *mask_ret,
                                                   SmsCallbacks   *callbacks_ret);
 
 void        gsm_xsmp_client_save_state           (GsmXSMPClient  *client);
 void        gsm_xsmp_client_save_yourself        (GsmXSMPClient  *client,
                                                   gboolean        save_state);
 void        gsm_xsmp_client_save_yourself_phase2 (GsmXSMPClient  *client);
 void        gsm_xsmp_client_interact             (GsmXSMPClient  *client);
 void        gsm_xsmp_client_shutdown_cancelled   (GsmXSMPClient  *client);
 
 G_END_DECLS
diff --git a/gnome-session/org.gnome.SessionManager.xml b/gnome-session/org.gnome.SessionManager.xml
index 8c31694..eacac4b 100644
--- a/gnome-session/org.gnome.SessionManager.xml
+++ b/gnome-session/org.gnome.SessionManager.xml
@@ -261,60 +261,68 @@
         <doc:doc>
           <doc:summary>True if condition is handled, false otherwise</doc:summary>
         </doc:doc>
       </arg>
       <doc:doc>
         <doc:description>
           <doc:para>Allows the caller to determine whether the session manager is
           handling changes to the specified autostart condition.</doc:para>
         </doc:description>
       </doc:doc>
     </method>
 
     <method name="Shutdown">
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       <doc:doc>
         <doc:description>
           <doc:para>Request a shutdown dialog.</doc:para>
         </doc:description>
       </doc:doc>
     </method>
 
     <method name="Reboot">
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       <doc:doc>
         <doc:description>
           <doc:para>Request a reboot dialog.</doc:para>
         </doc:description>
       </doc:doc>
     </method>
 
+    <method name="SaveSession">
+      <doc:doc>
+	<doc:description>
+	  <doc:para>Request to save session</doc:para>
+	</doc:description>
+      </doc:doc>
+    </method>
+
     <method name="CanShutdown">
       <arg name="is_available" direction="out" type="b">
         <doc:doc>
           <doc:summary>True if shutdown is available to the user, false otherwise</doc:summary>
         </doc:doc>
       </arg>
       <doc:doc>
         <doc:description>
           <doc:para>Allows the caller to determine whether or not it's okay to show
           a shutdown option in the UI</doc:para>
         </doc:description>
       </doc:doc>
     </method>
 
     <method name="Logout">
       <arg name="mode" type="u" direction="in">
         <doc:doc>
           <doc:summary>The type of logout that is being requested</doc:summary>
         </doc:doc>
       </arg>
       <doc:doc>
         <doc:description>
           <doc:para>Request a logout dialog</doc:para>
           <doc:para>
             Allowed values for the mode parameter are:
             <doc:list>
               <doc:item>
                 <doc:term>0</doc:term>
                 <doc:definition>Normal.</doc:definition>
               </doc:item>
-- 
2.3.7


From 7b1c1782ef895a536f95d83e0d6596dd076ee32f Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Dec 2013 11:22:07 -0500
Subject: [PATCH 09/17] Revert "Allow saved-session to be a symlink"

This reverts commit b733c2ee519b65c3c4eab0d0e93056412f995f3f.
---
 gnome-session/gsm-session-save.c | 32 ++++++++++++++++++++++++++++----
 gnome-session/gsm-util.c         |  6 ++++++
 2 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/gnome-session/gsm-session-save.c b/gnome-session/gsm-session-save.c
index 025eeea..131a58b 100644
--- a/gnome-session/gsm-session-save.c
+++ b/gnome-session/gsm-session-save.c
@@ -9,61 +9,61 @@
  *
  * 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
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 
 #include <glib.h>
 #include <glib/gstdio.h>
 #include <gio/gio.h>
 
 #include "gsm-util.h"
 #include "gsm-autostart-app.h"
 #include "gsm-client.h"
 
 #include "gsm-session-save.h"
 
 #define GSM_MANAGER_SCHEMA        "org.gnome.SessionManager"
 #define KEY_AUTOSAVE_ONE_SHOT     "auto-save-session-one-shot"
 
 
 static gboolean gsm_session_clear_saved_session (const char *directory,
                                                  GHashTable *discard_hash);
 
 typedef struct {
-        const char  *dir;
+        char        *dir;
         GHashTable  *discard_hash;
         GError     **error;
 } SessionSaveData;
 
 static gboolean
 save_one_client (char            *id,
                  GObject         *object,
                  SessionSaveData *data)
 {
         GsmClient  *client;
         GKeyFile   *keyfile;
         const char *app_id;
         char       *path = NULL;
         char       *filename = NULL;
         char       *contents = NULL;
         gsize       length = 0;
         char       *discard_exec;
         GError     *local_error;
 
         client = GSM_CLIENT (object);
 
         local_error = NULL;
 
         keyfile = gsm_client_save (client, &local_error);
 
         if (keyfile == NULL || local_error) {
                 goto out;
         }
 
         contents = g_key_file_to_data (keyfile, &length, &local_error);
@@ -112,89 +112,113 @@ save_one_client (char            *id,
         }
 
         g_debug ("GsmSessionSave: saved client %s to %s", id, filename);
 
 out:
         if (keyfile != NULL) {
                 g_key_file_free (keyfile);
         }
 
         g_free (contents);
         g_free (filename);
         g_free (path);
 
         /* in case of any error, stop saving session */
         if (local_error) {
                 g_propagate_error (data->error, local_error);
                 g_error_free (local_error);
 
                 return TRUE;
         }
 
         return FALSE;
 }
 
 void
 gsm_session_save (GsmStore  *client_store,
                   GError   **error)
 {
         GSettings       *settings;
         const char      *save_dir;
+        char            *tmp_dir;
         SessionSaveData  data;
 
         g_debug ("GsmSessionSave: Saving session");
 
         /* Clear one shot key autosave in the event its set (so that it's actually
          * one shot only)
          */
         settings = g_settings_new (GSM_MANAGER_SCHEMA);
         g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE);
         g_object_unref (settings);
 
         save_dir = gsm_util_get_saved_session_dir ();
         if (save_dir == NULL) {
                 g_warning ("GsmSessionSave: cannot create saved session directory");
                 return;
         }
 
-        data.dir = save_dir;
+        tmp_dir = gsm_util_get_empty_tmp_session_dir ();
+        if (tmp_dir == NULL) {
+                g_warning ("GsmSessionSave: cannot create new saved session directory");
+                return;
+        }
+
+        /* save the session in a temp directory, and remember the discard
+         * commands */
+        data.dir = tmp_dir;
         data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                    g_free, NULL);
-        /* remove old saved session */
-        gsm_session_clear_saved_session (save_dir, data.discard_hash);
         data.error = error;
 
         gsm_store_foreach (client_store,
                            (GsmStoreFunc) save_one_client,
                            &data);
 
+        if (!*error) {
+                /* remove the old saved session */
+                gsm_session_clear_saved_session (save_dir, data.discard_hash);
+
+                /* rename the temp session dir */
+                if (g_file_test (save_dir, G_FILE_TEST_IS_DIR))
+                        g_rmdir (save_dir);
+                g_rename (tmp_dir, save_dir);
+        } else {
+                g_warning ("GsmSessionSave: error saving session: %s", (*error)->message);
+                /* FIXME: we should create a hash table filled with the discard
+                 * commands that are in desktop files from save_dir. */
+                gsm_session_clear_saved_session (tmp_dir, NULL);
+                g_rmdir (tmp_dir);
+        }
+
         g_hash_table_destroy (data.discard_hash);
+        g_free (tmp_dir);
 }
 
 static gboolean
 gsm_session_clear_one_client (const char *filename,
                               GHashTable *discard_hash)
 {
         gboolean  result = TRUE;
         GKeyFile *key_file;
         char     *discard_exec = NULL;
 
         g_debug ("GsmSessionSave: removing '%s' from saved session", filename);
 
         key_file = g_key_file_new ();
         if (g_key_file_load_from_file (key_file, filename,
                                        G_KEY_FILE_NONE, NULL)) {
                 char **argv;
                 int    argc;
 
                 discard_exec = g_key_file_get_string (key_file,
                                                       G_KEY_FILE_DESKTOP_GROUP,
                                                       GSM_AUTOSTART_APP_DISCARD_KEY,
                                                       NULL);
                 if (!discard_exec)
                         goto out;
 
                 if (discard_hash && g_hash_table_lookup (discard_hash, discard_exec))
                         goto out;
 
                 if (!g_shell_parse_argv (discard_exec, &argc, &argv, NULL))
                         goto out;
diff --git a/gnome-session/gsm-util.c b/gnome-session/gsm-util.c
index ff6484c..bf24830 100644
--- a/gnome-session/gsm-util.c
+++ b/gnome-session/gsm-util.c
@@ -71,63 +71,69 @@ gsm_util_find_desktop_file_for_app_name (const char *name,
                 g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path);
         }
 
         /* look for gnome vendor prefix */
         if (app_path == NULL) {
                 g_free (desktop_file);
                 desktop_file = g_strdup_printf ("gnome-%s.desktop", name);
 
                 g_key_file_load_from_dirs (key_file,
                                            desktop_file,
                                            (const char **) app_dirs,
                                            &app_path,
                                            G_KEY_FILE_NONE,
                                            NULL);
                 if (app_path != NULL) {
                         g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path);
                 }
         }
 
         g_free (desktop_file);
         g_key_file_free (key_file);
 
         g_strfreev (app_dirs);
 
         return app_path;
 }
 
 static gboolean
 ensure_dir_exists (const char *dir)
 {
+        if (g_file_test (dir, G_FILE_TEST_IS_DIR))
+                return TRUE;
+
         if (g_mkdir_with_parents (dir, 0755) == 0)
                 return TRUE;
 
+        if (errno == EEXIST)
+                return g_file_test (dir, G_FILE_TEST_IS_DIR);
+
         g_warning ("GsmSessionSave: Failed to create directory %s: %s", dir, strerror (errno));
 
         return FALSE;
 }
 
 gchar *
 gsm_util_get_empty_tmp_session_dir (void)
 {
         char *tmp;
         gboolean exists;
 
         tmp = g_build_filename (g_get_user_config_dir (),
                                 "gnome-session",
                                 "saved-session.new",
                                 NULL);
 
         exists = ensure_dir_exists (tmp);
 
         if (G_UNLIKELY (!exists)) {
                 g_warning ("GsmSessionSave: could not create directory for saved session: %s", tmp);
                 g_free (tmp);
                 return NULL;
         } else {
                 /* make sure it's empty */
                 GDir       *dir;
                 const char *filename;
 
                 dir = g_dir_open (tmp, 0, NULL);
                 if (dir) {
                         while ((filename = g_dir_read_name (dir))) {
-- 
2.3.7


From efdb8db827399dadd3b8638b201e7da81f6e9592 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Dec 2013 11:22:53 -0500
Subject: [PATCH 10/17] Allow saved-session directory to be a symlink

This gives us the option of adding a rudimentary session
chooser later.
---
 gnome-session/gsm-session-save.c | 36 ++++++++++++++++++++++++++++++------
 gnome-session/gsm-util.c         |  6 ------
 2 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/gnome-session/gsm-session-save.c b/gnome-session/gsm-session-save.c
index 131a58b..ea23be5 100644
--- a/gnome-session/gsm-session-save.c
+++ b/gnome-session/gsm-session-save.c
@@ -148,67 +148,91 @@ gsm_session_save (GsmStore  *client_store,
          * one shot only)
          */
         settings = g_settings_new (GSM_MANAGER_SCHEMA);
         g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE);
         g_object_unref (settings);
 
         save_dir = gsm_util_get_saved_session_dir ();
         if (save_dir == NULL) {
                 g_warning ("GsmSessionSave: cannot create saved session directory");
                 return;
         }
 
         tmp_dir = gsm_util_get_empty_tmp_session_dir ();
         if (tmp_dir == NULL) {
                 g_warning ("GsmSessionSave: cannot create new saved session directory");
                 return;
         }
 
         /* save the session in a temp directory, and remember the discard
          * commands */
         data.dir = tmp_dir;
         data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                    g_free, NULL);
         data.error = error;
 
         gsm_store_foreach (client_store,
                            (GsmStoreFunc) save_one_client,
                            &data);
 
         if (!*error) {
-                /* remove the old saved session */
-                gsm_session_clear_saved_session (save_dir, data.discard_hash);
+                char *session_dir;
 
-                /* rename the temp session dir */
-                if (g_file_test (save_dir, G_FILE_TEST_IS_DIR))
-                        g_rmdir (save_dir);
-                g_rename (tmp_dir, save_dir);
+                if (g_file_test (save_dir, G_FILE_TEST_IS_SYMLINK))
+                        session_dir = g_file_read_link (save_dir, error);
+                else
+                        session_dir = g_strdup (save_dir);
+
+                if (session_dir != NULL) {
+
+                        char *absolute_session_dir;
+
+                        if (g_path_is_absolute (session_dir)) {
+                                absolute_session_dir = g_strdup (session_dir);
+                        } else {
+                                char *parent_dir;
+
+                                parent_dir = g_path_get_dirname (save_dir);
+                                absolute_session_dir = g_build_filename (parent_dir, session_dir, NULL);
+                                g_free (parent_dir);
+                        }
+                        g_free (session_dir);
+
+                        /* remove the old saved session */
+                        gsm_session_clear_saved_session (absolute_session_dir, data.discard_hash);
+
+                        if (g_file_test (absolute_session_dir, G_FILE_TEST_IS_DIR))
+                                g_rmdir (absolute_session_dir);
+                        g_rename (tmp_dir, absolute_session_dir);
+
+                        g_free (absolute_session_dir);
+                }
         } else {
                 g_warning ("GsmSessionSave: error saving session: %s", (*error)->message);
                 /* FIXME: we should create a hash table filled with the discard
                  * commands that are in desktop files from save_dir. */
                 gsm_session_clear_saved_session (tmp_dir, NULL);
                 g_rmdir (tmp_dir);
         }
 
         g_hash_table_destroy (data.discard_hash);
         g_free (tmp_dir);
 }
 
 static gboolean
 gsm_session_clear_one_client (const char *filename,
                               GHashTable *discard_hash)
 {
         gboolean  result = TRUE;
         GKeyFile *key_file;
         char     *discard_exec = NULL;
 
         g_debug ("GsmSessionSave: removing '%s' from saved session", filename);
 
         key_file = g_key_file_new ();
         if (g_key_file_load_from_file (key_file, filename,
                                        G_KEY_FILE_NONE, NULL)) {
                 char **argv;
                 int    argc;
 
                 discard_exec = g_key_file_get_string (key_file,
                                                       G_KEY_FILE_DESKTOP_GROUP,
diff --git a/gnome-session/gsm-util.c b/gnome-session/gsm-util.c
index bf24830..ff6484c 100644
--- a/gnome-session/gsm-util.c
+++ b/gnome-session/gsm-util.c
@@ -71,69 +71,63 @@ gsm_util_find_desktop_file_for_app_name (const char *name,
                 g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path);
         }
 
         /* look for gnome vendor prefix */
         if (app_path == NULL) {
                 g_free (desktop_file);
                 desktop_file = g_strdup_printf ("gnome-%s.desktop", name);
 
                 g_key_file_load_from_dirs (key_file,
                                            desktop_file,
                                            (const char **) app_dirs,
                                            &app_path,
                                            G_KEY_FILE_NONE,
                                            NULL);
                 if (app_path != NULL) {
                         g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path);
                 }
         }
 
         g_free (desktop_file);
         g_key_file_free (key_file);
 
         g_strfreev (app_dirs);
 
         return app_path;
 }
 
 static gboolean
 ensure_dir_exists (const char *dir)
 {
-        if (g_file_test (dir, G_FILE_TEST_IS_DIR))
-                return TRUE;
-
         if (g_mkdir_with_parents (dir, 0755) == 0)
                 return TRUE;
 
-        if (errno == EEXIST)
-                return g_file_test (dir, G_FILE_TEST_IS_DIR);
-
         g_warning ("GsmSessionSave: Failed to create directory %s: %s", dir, strerror (errno));
 
         return FALSE;
 }
 
 gchar *
 gsm_util_get_empty_tmp_session_dir (void)
 {
         char *tmp;
         gboolean exists;
 
         tmp = g_build_filename (g_get_user_config_dir (),
                                 "gnome-session",
                                 "saved-session.new",
                                 NULL);
 
         exists = ensure_dir_exists (tmp);
 
         if (G_UNLIKELY (!exists)) {
                 g_warning ("GsmSessionSave: could not create directory for saved session: %s", tmp);
                 g_free (tmp);
                 return NULL;
         } else {
                 /* make sure it's empty */
                 GDir       *dir;
                 const char *filename;
 
                 dir = g_dir_open (tmp, 0, NULL);
                 if (dir) {
                         while ((filename = g_dir_read_name (dir))) {
-- 
2.3.7


From fdc43e0cc51bf0d16bb9173bd3a23cf27c1ed2b3 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Dec 2013 11:28:53 -0500
Subject: [PATCH 11/17] Tie session selector to properties dialog

---
 capplet/gsm-properties-dialog.c |  30 +++++-
 configure.ac                    |   3 +-
 data/session-selector.ui        |   2 +-
 tools/Makefile.am               |   1 +
 tools/gnome-session-selector.c  | 211 ++++++++++++++++++++++++++++++++--------
 5 files changed, 200 insertions(+), 47 deletions(-)

diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c
index d2be778..51fa510 100644
--- a/capplet/gsm-properties-dialog.c
+++ b/capplet/gsm-properties-dialog.c
@@ -471,88 +471,114 @@ session_saved_message (GsmPropertiesDialog *dialog,
 {
         GtkLabel *label;
         gchar *markup;
         label = GTK_LABEL (gtk_builder_get_object (dialog->priv->xml, CAPPLET_SESSION_SAVED_WIDGET_NAME));
         if (is_error)
                 markup = g_markup_printf_escaped ("<span foreground=\"red\">%s</span>", msg);
         else
                 markup = g_markup_escape_text (msg, -1);
         gtk_label_set_markup (label, markup);
         g_free (markup);
 }
 
 static void
 session_saved_cb (DBusGProxy *proxy,
                   DBusGProxyCall *call_id,
                   void *user_data)
 {
         gboolean res;
         GsmPropertiesDialog *dialog = user_data;
 
         res = dbus_g_proxy_end_call (proxy, call_id, NULL, G_TYPE_INVALID);
         if (res)
                 session_saved_message (dialog, _("Your session has been saved."), FALSE);
         else
                 session_saved_message (dialog, _("Failed to save session"), TRUE);
 
         g_object_unref (proxy);
 }
 
 static void
-on_save_session_clicked (GtkWidget           *widget,
-                         GsmPropertiesDialog *dialog)
+save_session_directly (GsmPropertiesDialog *dialog)
 {
         DBusGConnection *conn;
         DBusGProxy *proxy;
         DBusGProxyCall *call;
 
         conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
         if (conn == NULL) {
                 session_saved_message (dialog, _("Could not connect to the session bus"), TRUE);
                 return;
         }
 
         proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS);
         if (proxy == NULL) {
                 session_saved_message (dialog, _("Could not connect to the session manager"), TRUE);
                 return;
         }
 
         call = dbus_g_proxy_begin_call (proxy, "SaveSession", session_saved_cb, dialog, NULL, G_TYPE_INVALID);
         if (call == NULL) {
                 session_saved_message (dialog, _("Failed to save session"), TRUE);
                 g_object_unref (proxy);
                 return;
         }
 }
 
 static void
+save_session_from_selector (GsmPropertiesDialog *dialog,
+                            const char          *program_path)
+{
+        char *command_line = g_strdup_printf ("%s --action save", program_path);
+
+        g_spawn_command_line_sync (command_line, NULL, NULL, NULL, NULL);
+
+        g_free (command_line);
+}
+
+static void
+on_save_session_clicked (GtkWidget           *widget,
+                         GsmPropertiesDialog *dialog)
+{
+        char *program_path;
+
+        program_path = g_find_program_in_path ("gnome-session-selector");
+
+        if (program_path != NULL) {
+                save_session_from_selector (dialog, program_path);
+                g_free (program_path);
+        } else {
+                save_session_directly (dialog);
+        }
+}
+
+static void
 setup_dialog (GsmPropertiesDialog *dialog)
 {
         GtkTreeView       *treeview;
         GtkWidget         *button;
         GtkTreeModel      *tree_filter;
         GtkTreeViewColumn *column;
         GtkCellRenderer   *renderer;
         GtkTreeSelection  *selection;
         GtkTargetList     *targetlist;
 
         gtk_dialog_add_buttons (GTK_DIALOG (dialog),
                                 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
                                 NULL);
 
         dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
                                                        G_TYPE_BOOLEAN,
                                                        G_TYPE_BOOLEAN,
                                                        G_TYPE_ICON,
                                                        G_TYPE_STRING,
                                                        G_TYPE_OBJECT,
                                                        G_TYPE_STRING);
         tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store),
                                                  NULL);
         g_object_unref (dialog->priv->list_store);
         dialog->priv->tree_filter = tree_filter;
 
         gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter),
                                                   STORE_COL_VISIBLE);
 
         treeview = GTK_TREE_VIEW (gtk_builder_get_object (dialog->priv->xml,
diff --git a/configure.ac b/configure.ac
index 505292c..9b97c15 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,92 +21,91 @@ LT_PREREQ([2.2.6])
 LT_INIT([dlopen disable-static])
 
 GNOME_MAINTAINER_MODE_DEFINES
 GNOME_COMPILE_WARNINGS([maximum])
 
 AC_ARG_ENABLE(deprecation_flags,
               [AS_HELP_STRING([--enable-deprecation-flags],
                               [use *_DISABLE_DEPRECATED flags @<:@default=no@:>@])],,
               [enable_deprecation_flags=no])
 
 if test "x$enable_deprecation_flags" = "xyes"; then
    DISABLE_DEPRECATED_CFLAGS=$DISABLE_DEPRECATED
    AC_SUBST([DISABLE_DEPRECATED_CFLAGS])
 fi
 
 GLIB_REQUIRED=2.35.0
 GTK3_REQUIRED=2.90.7
 DBUS_GLIB_REQUIRED=0.76
 UPOWER_REQUIRED=0.9.0
 JSON_GLIB_REQUIRED=0.10
 GNOME_DESKTOP_REQUIRED=3.9.91
 
 AC_ARG_ENABLE(session-selector, AS_HELP_STRING([--enable-session-selector],
                                                [enable building a custom session selector dialog]),
                                                 enable_session_selector=$enableval,enable_session_selector=no)
 
 AM_CONDITIONAL(BUILD_SESSION_SELECTOR,
                [test "$enable_session_selector" = yes])
 
 if test "$enable_session_selector" = yes; then
-        PKG_CHECK_MODULES(SESSION_SELECTOR, gtk+-3.0 gio-2.0)
+        PKG_CHECK_MODULES(SESSION_SELECTOR, gtk+-3.0 gio-2.0 dbus-glib-1 >= $DBUS_GLIB_REQUIRED)
 fi
 
 dnl ====================================================================
 dnl Dependency Checks
 dnl ====================================================================
 
 dnl Standard vertical stacks
 PKG_CHECK_MODULES(GIO, gio-2.0)
 PKG_CHECK_MODULES(GIOUNIX, gio-unix-2.0 >= $GLIB_REQUIRED)
 PKG_CHECK_MODULES(GTK3, gtk+-3.0 >= $GTK3_REQUIRED)
 
 PKG_CHECK_MODULES(GNOME_SESSION,
         glib-2.0 >= $GLIB_REQUIRED
         gio-2.0 >= $GLIB_REQUIRED
         dbus-glib-1 >= $DBUS_GLIB_REQUIRED
         json-glib-1.0 >= $JSON_GLIB_REQUIRED
         gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED
 )
 
 dnl We can only support old upower
 dnl https://bugzilla.gnome.org/show_bug.cgi?id=710383
 PKG_CHECK_MODULES(UPOWER, upower-glib < 0.99.0, have_old_upower=yes, have_old_upower=no)
 AS_IF([test x$have_old_upower = xyes], [
   AC_DEFINE([HAVE_OLD_UPOWER], [1], [Define if we have an older upower])
 ])
 AM_CONDITIONAL(HAVE_OLD_UPOWER, test x$have_old_upower = xyes)
 
 PKG_CHECK_MODULES(SESSION_PROPERTIES,
         glib-2.0 >= $GLIB_REQUIRED
         gtk+-3.0 >= $GTK3_REQUIRED
-        dbus-glib-1 >= $DBUS_GLIB_REQUIRED
 )
 
 PKG_CHECK_MODULES(X11, x11)
 PKG_CHECK_MODULES(SM, sm)
 PKG_CHECK_MODULES(ICE, ice)
 PKG_CHECK_MODULES(XEXT, xext xau)
 
 PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1 >= $DBUS_GLIB_REQUIRED)
 
 PKG_CHECK_MODULES(EGG_SMCLIENT, gtk+-3.0)
 
 PKG_CHECK_MODULES(GL_TEST, xcomposite gl glib-2.0)
 
 dnl ====================================================================
 dnl Check for gconf
 dnl ====================================================================
 AC_ARG_ENABLE([gconf],
               AS_HELP_STRING([--enable-gconf], [Support gconf-based autostart]),
               [enable_gconf=$enableval],
               [enable_gconf=auto])
 
 PKG_CHECK_MODULES(GCONF, gconf-2.0, [have_gconf=yes], [have_gconf=no])
 
 if test x$enable_gconf = xauto ; then
         enable_gconf=$have_gconf
 elif test x$enable_gconf = xyes -a x$have_gconf = xno ; then
         AC_MSG_ERROR([GConf support explicitly required, but gconf not found])
 fi
 
 if test x$enable_gconf = xyes ; then
diff --git a/data/session-selector.ui b/data/session-selector.ui
index 1c55712..1534a74 100644
--- a/data/session-selector.ui
+++ b/data/session-selector.ui
@@ -20,61 +20,61 @@
     <child>
       <object class="GtkFrame" id="frame1">
         <property name="visible">True</property>
         <property name="label_xalign">0.5</property>
         <property name="shadow_type">out</property>
         <child>
           <object class="GtkAlignment" id="alignment3">
             <property name="visible">True</property>
             <property name="border_width">12</property>
             <child>
               <object class="GtkVBox" id="vbox3">
                 <property name="visible">True</property>
                 <property name="orientation">vertical</property>
                 <property name="spacing">6</property>
 
                 <child>
                   <object class="GtkInfoBar" id="info-bar">
                     <property name="visible">True</property>
                     <property name="message-type">other</property>
 
                     <child internal-child="content_area">
                       <object class="GtkHBox" id="info-bar-content_area">
                         <property name="visible">True</property>
                         <property name="orientation">vertical</property>
                         <property name="spacing">0</property>
                         <child>
                           <object class="GtkLabel" id="info-label">
                             <property name="visible">True</property>
                             <property name="xalign">0.0</property>
                             <property name="yalign">0.5</property>
-                            <property name="label" translatable="yes">Please select a custom session to run</property>
+                            <property name="label" translatable="yes">Please select a custom session to use</property>
                           </object>
                           <packing>
                             <property name="expand">True</property>
                             <property name="fill">True</property>
                             <property name="position">0</property>
                           </packing>
                         </child>
                       </object>
                     </child>
                   </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
                     <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkVBox" id="vbox4">
                     <property name="visible">True</property>
                     <property name="orientation">vertical</property>
                     <property name="spacing">12</property>
                     <child>
                       <object class="GtkHBox" id="hbox3">
                         <property name="visible">True</property>
                         <property name="spacing">12</property>
                         <child>
                           <object class="GtkScrolledWindow" id="scrolledwindow2">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="hscrollbar_policy">never</property>
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 57b82c7..715affe 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -49,37 +49,38 @@ gnome_session_check_accelerated_helper_CPPFLAGS =	\
 	-DPKGDATADIR=\""$(pkgdatadir)"\"		\
 	$(GL_TEST_CFLAGS)
 
 gnome_session_check_accelerated_helper_LDADD = 		\
 	$(GL_TEST_LIBS)				\
 	$(X11_LIBS)
 
 gnome_session_check_accelerated_SOURCES =       	\
 	gnome-session-check-accelerated-common.h	\
 	gnome-session-check-accelerated.c
 
 gnome_session_check_accelerated_CPPFLAGS =      \
 	-DLIBEXECDIR=\""$(libexecdir)"\"	\
 	$(AM_CPPFLAGS)			        \
 	$(GTK3_CFLAGS)
 
 gnome_session_check_accelerated_LDADD =         \
 	$(GTK3_LIBS)				\
 	$(X11_LIBS)
 
 if BUILD_SESSION_SELECTOR
 gnome_session_selector_CPPFLAGS =		\
 	$(AM_CPPFLAGS)				\
 	$(GNOME_SESSION_CFLAGS)			\
 	$(DBUS_GLIB_CFLAGS)			\
 	-DGTKBUILDER_DIR=\""$(pkgdatadir)"\"	\
 	-DLOCALE_DIR=\""$(datadir)/locale"\"	\
 	$(DISABLE_DEPRECATED_CFLAGS)
 
 gnome_session_selector_LDADD = 			\
+	$(DBUS_GLIB_CFLAGS)			\
 	$(SESSION_SELECTOR_LIBS)
 
 gnome_session_selector_SOURCES = 		\
 	gnome-session-selector.c
 endif
 
 -include $(top_srcdir)/git.mk
diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c
index d7584ff..8f37eca 100644
--- a/tools/gnome-session-selector.c
+++ b/tools/gnome-session-selector.c
@@ -7,126 +7,133 @@
  * 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, see <http://www.gnu.org/licenses/>.
  *
  * Written by: Matthias Clasen <mclasen@redhat.com>
  */
 
 #include "config.h"
 
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include <glib.h>
 #include <gtk/gtk.h>
 #include <gio/gio.h>
 
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#define GSM_SERVICE_DBUS   "org.gnome.SessionManager"
+#define GSM_PATH_DBUS      "/org/gnome/SessionManager"
+#define GSM_INTERFACE_DBUS "org.gnome.SessionManager"
+
 #define GSM_MANAGER_SCHEMA        "org.gnome.SessionManager"
 #define KEY_AUTOSAVE_ONE_SHOT     "auto-save-session-one-shot"
 
 static GtkBuilder *builder;
 static GtkWidget *session_list;
 static GtkListStore *store;
 static GtkTreeModelSort *sort_model;
+static char *info_text;
 
 static void select_session (const char *name);
+static gboolean make_session_current (const char *name);
 
 static char *
 get_session_path (const char *name)
 {
         return g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL);
 }
 
 static char *
 find_new_session_name (void)
 {
         char *name;
         char *path;
         int i;
 
         for (i = 1; i < 20; i++) {
                 name = g_strdup_printf (_("Session %d"), i);
                 path = get_session_path (name);
                 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
                         g_free (path);
                         return name;
                 }
                 g_free (path);
                 g_free (name);
         }
 
         return NULL;
 }
 
 static gboolean
 is_valid_session_name (const char *name)
 {
         GtkTreeIter iter;
         char *n;
-        const char *info_text;
         char *warning_text;
         gboolean user_tried_dot;
         gboolean user_tried_slash;
         GtkWidget *info_bar;
         GtkWidget *label;
 
         if (name[0] == 0) {
                 return FALSE;
         }
 
         if (name[0] == '.') {
             user_tried_dot = TRUE;
         } else {
             user_tried_dot = FALSE;
         }
 
         if (strchr (name, '/') != NULL) {
             user_tried_slash = TRUE;
         } else {
             user_tried_slash = FALSE;
         }
 
-        info_text = _("Please select a custom session to run");
         warning_text = NULL;
         if (user_tried_dot && user_tried_slash) {
             warning_text = g_strdup_printf ("%s\n<small><b>Note:</b> <i>%s</i></small>",
                                             info_text,
                                             _("Session names are not allowed to start with ā€˜.ā€™ or contain ā€˜/ā€™ characters"));
         } else if (user_tried_dot) {
             warning_text = g_strdup_printf ("%s\n<small><b>Note:</b> <i>%s</i></small>",
                                             info_text,
                                             _("Session names are not allowed to start with ā€˜.ā€™"));
         } else if (user_tried_slash) {
             warning_text = g_strdup_printf ("%s\n<small><b>Note:</b> <i>%s</i></small>",
                                             info_text,
                                             _("Session names are not allowed to contain ā€˜/ā€™ characters"));
         }
 
         gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
         do {
                 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &n, -1);
                 if (strcmp (n, name) == 0) {
                         char *message;
                         message = g_strdup_printf (_("A session named ā€˜%sā€™ already exists"), name);
                         warning_text = g_strdup_printf ("%s\n<small><b>Note:</b> <i>%s</i></small>", info_text, message);
                         g_free (message);
                         g_free (n);
                         break;
                 }
                 g_free (n);
         } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
 
         info_bar = (GtkWidget *) gtk_builder_get_object (builder, "info-bar");
@@ -182,517 +189,637 @@ populate_session_list (GtkWidget *session_list)
         default_name = NULL;
         if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) {
                 default_name = g_path_get_basename (last_session);
         }
 
         while ((name = g_dir_read_name (dir)) != NULL) {
                 if (strcmp (name, "saved-session") == 0)
                         continue;
 
                 gtk_list_store_insert_with_values (store, &iter, 100, 0, name, -1);
 
                 if (g_strcmp0 (default_name, name) == 0) {
                         GtkTreeSelection *selection;
                         GtkTreeIter child_iter;
 
                         gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &child_iter, &iter);
                         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list));
                         gtk_tree_selection_select_iter (selection, &child_iter);
                 }
         }
 
         g_free (default_name);
         g_dir_close (dir);
 
  out:
         g_free (saved_session);
         g_free (path);
 }
 
 static char *
+get_last_session (void)
+{
+        char *saved_session;
+        char last_session[PATH_MAX] = "";
+        char *name = NULL;
+
+        saved_session = get_session_path ("saved-session");
+
+        if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) {
+                name = g_path_get_basename (last_session);
+        }
+
+        g_free (saved_session);
+
+        return name;
+}
+
+static char *
 get_selected_session (void)
 {
         GtkTreeSelection *selection;
         GtkTreeModel *model;
         GtkTreeIter iter;
         gchar *name;
 
         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list));
         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
                 gtk_tree_model_get (model, &iter, 0, &name, -1);
                 return name;
         }
 
         return NULL;
 }
 
 static void
 remove_session (const char *name)
 {
         char *path1, *path2;
         char *n, *path;
         const char *d;
         GDir *dir;
         GError *error;
 
         path1 = get_session_path ("saved-session");
         path2 = get_session_path (name);
 
         error = NULL;
         n = g_file_read_link (path1, &error);
         if (n == NULL) {
                 g_warning ("Failed to read link: %s", error->message);
                 g_error_free (error);
         }
         else if (strcmp (n, name) == 0) {
                 unlink (path1);
         }
         g_free (n);
 
         dir = g_dir_open (path2, 0, NULL);
         while ((d = g_dir_read_name (dir)) != NULL) {
                 path = g_build_filename (path2, d, NULL);
                 unlink (path);
                 g_free (path);
         }
         g_dir_close (dir);
 
         remove (path2);
 
         g_free (path1);
         g_free (path2);
 }
 
+static gboolean
+make_session_current (const char *name)
+{
+        char *path1;
+        gboolean ret = TRUE;
+
+        path1 = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session", NULL);
+
+        unlink (path1);
+        if (symlink (name, path1) < 0) {
+                g_warning ("Failed to make session '%s' current", name);
+                ret = FALSE;
+        }
+
+        g_free (path1);
+
+        return ret;
+}
+
 static void
 on_remove_session_clicked (GtkButton *button,
                            gpointer   data)
 {
         GtkTreeSelection *selection;
         GtkTreeModel *model;
         GtkTreeIter iter;
         char *name;
 
         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list));
         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
                 GtkTreeIter child_iter;
                 gtk_tree_model_get (model, &iter, 0, &name, -1);
                 remove_session (name);
                 g_free (name);
 
                 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model), &child_iter, &iter);
                 gtk_list_store_remove (GTK_LIST_STORE (store), &child_iter);
 
                 if (!gtk_tree_selection_get_selected (selection, NULL, NULL)) {
                         gtk_tree_model_get_iter_first (model, &iter);
                         gtk_tree_model_get (model, &iter, 0, &name, -1);
                         select_session (name);
+                        make_session_current (name);
                         g_free (name);
                 }
         }
 }
 
 static void
 begin_rename (void)
 {
         GtkTreePath *path;
         GtkTreeViewColumn *column;
         GList *cells;
 
         gtk_widget_grab_focus (session_list);
 
         gtk_tree_view_get_cursor (GTK_TREE_VIEW (session_list),
                                   &path, &column);
 
         cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
 
         if (cells != NULL) {
             GtkCellRenderer *cell;
 
             cell = (GtkCellRenderer *) cells->data;
             g_list_free (cells);
 
             g_object_set (cell, "editable", TRUE, NULL);
             gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (session_list), path,
                                               column, cell, TRUE);
         }
         gtk_tree_path_free (path);
 }
 
 static void
 on_rename_session_clicked (GtkButton *button,
                            gpointer   data)
 {
     begin_rename ();
 }
 
 static void
 on_continue_clicked (GtkButton *button,
                      gpointer    data)
 {
         char *name;
 
         name = get_selected_session ();
         g_free (name);
 
         gtk_main_quit ();
 }
 
 static void
 create_session (const char *name)
 {
         char *path;
         GtkTreeIter iter;
 
         path = get_session_path (name);
 
-        if (mkdir (path, 0755) < 0) {
+        if (g_mkdir_with_parents (path, 0755) < 0) {
                 g_warning ("Failed to create directory %s", path);
         }
         else {
                 char *marker;
 
                 gtk_list_store_insert_with_values (store, &iter, 100, 0, name, -1);
 
                 marker = g_build_filename (path, ".new-session", NULL);
                 creat (marker, 0600);
                 g_free (marker);
         }
 
         g_free (path);
 }
 
 static gboolean
 rename_session (const char *old_name,
                 const char *new_name)
 {
         char *old_path, *new_path;
         int result;
 
         if (g_strcmp0 (old_name, new_name) == 0) {
                 return TRUE;
         }
 
         if (!is_valid_session_name (new_name)) {
                return FALSE;
         }
 
         old_path = get_session_path (old_name);
         new_path = get_session_path (new_name);
 
         result = g_rename (old_path, new_path);
 
         if (result < 0) {
                 g_warning ("Failed to rename session from '%s' to '%s': %m", old_name, new_name);
+        } else {
+                char *last_session;
+                last_session = get_last_session ();
+                if (g_strcmp0 (old_name, last_session) == 0) {
+                        make_session_current (new_name);
+                }
+                g_free (last_session);
         }
 
         g_free (old_path);
         g_free (new_path);
-
         return result == 0;
 }
 
 static gboolean
-make_session_current (const char *name)
-{
-        char *path1;
-        gboolean ret = TRUE;
-
-        path1 = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session", NULL);
-
-        unlink (path1);
-        if (symlink (name, path1) < 0) {
-                g_warning ("Failed to make session '%s' current", name);
-                ret = FALSE;
-        }
-
-        g_free (path1);
-
-        return ret;
-}
-
-static gboolean
 create_and_select_session (const char *name)
 {
         gchar *path;
 
         if (name[0] == 0 || name[0] == '.' || strchr (name, '/')) {
                 g_warning ("Invalid session name");
                 return FALSE;
         }
 
         path = g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL);
         if (!g_file_test (path, G_FILE_TEST_IS_DIR)) {
-                if (mkdir (path, 0755) < 0) {
+                if (g_mkdir_with_parents (path, 0755) < 0) {
                         g_warning ("Failed to create directory %s", path);
                         g_free (path);
                         return FALSE;
                 }
         }
 
         g_free (path);
 
         return make_session_current (name);
 }
 
 static void
 select_session (const char *name)
 {
         GtkTreeIter iter;
         char *n;
 
-        make_session_current (name);
-
         /* now select it in the list */
         gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sort_model), &iter);
         do {
                 gtk_tree_model_get (GTK_TREE_MODEL (sort_model), &iter, 0, &n, -1);
                 if (strcmp (n, name) == 0) {
                         GtkTreePath *path;
 
                         path = gtk_tree_model_get_path (GTK_TREE_MODEL (sort_model), &iter);
                         gtk_tree_view_set_cursor (GTK_TREE_VIEW (session_list), path, NULL, FALSE);
                         gtk_tree_path_free (path);
                         g_free (n);
                         break;
                 }
                 g_free (n);
         } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (sort_model), &iter));
 }
 
 static void
-on_new_session_clicked (GtkButton *button,
-                        gpointer   data)
+create_session_and_begin_rename (void)
 {
         gchar *name;
 
         name = find_new_session_name ();
         create_session (name);
         select_session (name);
 
         begin_rename ();
 }
 
 static void
+on_new_session_clicked (GtkButton *button,
+                        gpointer   data)
+{
+	create_session_and_begin_rename ();
+}
+
+static void
 on_selection_changed (GtkTreeSelection *selection,
                       gpointer          data)
 {
         char *name;
 
         name = get_selected_session ();
 
         if (name == NULL) {
                 return;
         }
 
-        make_session_current (name);
-
         g_free (name);
 }
 
 static void
 update_remove_button (void)
 {
         GtkWidget *button;
 
         button = (GtkWidget *)gtk_builder_get_object (builder, "remove-session");
         if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) > 1) {
                 gtk_widget_set_sensitive (button, TRUE);
         } else {
                 gtk_widget_set_sensitive (button, FALSE);
         }
 }
 
 static void
 on_row_edited (GtkCellRendererText *cell,
                const char          *path_string,
                const char          *new_name,
                gpointer             data)
 {
         GtkTreePath *path;
         GtkTreeIter  sort_iter, items_iter;
         char        *old_name;
         gboolean     was_renamed;
 
         path = gtk_tree_path_new_from_string (path_string);
         gtk_tree_model_get_iter (GTK_TREE_MODEL (sort_model), &sort_iter, path);
 
         gtk_tree_model_get (GTK_TREE_MODEL (sort_model), &sort_iter, 0, &old_name, -1);
 
         was_renamed = rename_session (old_name, new_name);
 
         if (was_renamed) {
                 gtk_tree_model_sort_convert_iter_to_child_iter (sort_model, &items_iter, &sort_iter);
 
                 gtk_list_store_set (store, &items_iter, 0, g_strdup (new_name), -1);
                 g_free (old_name);
-                make_session_current (new_name);
         } else {
                 begin_rename ();
         }
 
         gtk_tree_path_free (path);
 
         g_object_set (cell, "editable", FALSE, NULL);
 }
 
 static void
 on_row_deleted (GtkTreeModel *model,
                 GtkTreePath  *path,
                 gpointer      data)
 {
         update_remove_button ();
 }
 
 static void
 on_row_inserted (GtkTreeModel *model,
                  GtkTreePath  *path,
                  GtkTreeIter  *iter,
                  gpointer      data)
 {
         update_remove_button ();
 }
 
 static void
 on_row_activated (GtkTreeView       *tree_view,
                   GtkTreePath       *path,
                   GtkTreeViewColumn *column,
                   gpointer           data)
 {
-        char *name;
-
-        name = get_selected_session ();
-        g_free (name);
-
         gtk_main_quit ();
 }
 
 static void
 auto_save_next_session (void)
 {
         GSettings *settings;
 
         settings = g_settings_new (GSM_MANAGER_SCHEMA);
         g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, TRUE);
         g_object_unref (settings);
 }
 
 static void
 auto_save_next_session_if_needed (void)
 {
         char *marker;
 
         marker = g_build_filename (g_get_user_config_dir (),
                                    "gnome-session", "saved-session",
                                    ".new-session", NULL);
 
         if (g_file_test (marker, G_FILE_TEST_EXISTS)) {
                 auto_save_next_session ();
                 unlink (marker);
         }
         g_free (marker);
 }
 
+static void
+save_session (void)
+{
+        DBusGConnection *conn;
+        DBusGProxy *proxy;
+        GError *error;
+
+        conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+        if (conn == NULL) {
+                g_warning ("Could not connect to the session bus");
+                return;
+        }
+
+        proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS);
+        if (proxy == NULL) {
+                g_warning ("Could not connect to the session manager");
+                return;
+        }
+
+        error = NULL;
+        if (!dbus_g_proxy_call (proxy, "SaveSession", &error, G_TYPE_INVALID, G_TYPE_INVALID)) {
+                g_warning ("Failed to save session: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        g_object_unref (proxy);
+}
+
 static int
 compare_sessions (GtkTreeModel *model,
                   GtkTreeIter  *a,
                   GtkTreeIter  *b,
                   gpointer      data)
 {
     char *name_a, *name_b;
     int result;
 
     gtk_tree_model_get (model, a, 0, &name_a, -1);
     gtk_tree_model_get (model, b, 0, &name_b, -1);
 
     result = g_utf8_collate (name_a, name_b);
 
     g_free (name_a);
     g_free (name_b);
 
     return result;
 }
 
 static void
 on_map (GtkWidget *widget,
         gpointer   data)
 {
         gdk_window_focus (gtk_widget_get_window (widget), GDK_CURRENT_TIME);
 }
 
 int
 main (int argc, char *argv[])
 {
         GtkWidget *window;
         GtkWidget *widget;
+        GtkWidget *label;
         GtkCellRenderer *cell;
         GtkTreeViewColumn *column;
         GtkTreeSelection *selection;
         GError *error;
+        char *selected_session;
+
+        static char *action = NULL;
+        static char **remaining_args = NULL;
+        static GOptionEntry entries[] = {
+                {"action", '\0', 0, G_OPTION_ARG_STRING, &action, N_("What to do with session selection (save|load|print)"), NULL},
+{ G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &remaining_args, N_("[session-name]"), NULL}
+        };
+
+        if (action == NULL) {
+            if (getenv ("SESSION_MANAGER") != NULL)
+                action = "print";
+            else
+                action = "load";
+        }
 
-        if (getenv ("SESSION_MANAGER") != NULL)
+        if (getenv ("SESSION_MANAGER") != NULL && strcmp (action, "load") == 0) {
+            g_warning ("Cannot load new session when session currently loaded");
             return 1;
+        }
+
+        if (getenv ("SESSION_MANAGER") == NULL && strcmp (action, "save") == 0) {
+            g_warning ("Can only save session when session loaded");
+            return 1;
+        }
+
+        if (strcmp (action, "load") != 0 && strcmp (action, "save") != 0 && strcmp (action, "print") != 0) {
+            g_warning ("'%s' is not a supported action.  Supported actions are load, save, and print.\n", action);
+            return 1;
+        }
 
-        gtk_init (&argc, &argv);
-        if (argc > 1) {
-                g_print ("create and select session\n");
-                if (!create_and_select_session (argv[1]))
+        error = NULL;
+        gtk_init_with_args (&argc, &argv,
+                            NULL, entries, GETTEXT_PACKAGE, &error);
+
+        if (remaining_args != NULL) {
+                if (g_strv_length (remaining_args) > 1) {
+                        g_warning ("gnome-session-selector takes at most one session argument");
+                        return 1;
+                }
+
+                if (!create_and_select_session (remaining_args[0]))
                         return 1;
                 else
                         return 0;
         }
 
         builder = gtk_builder_new ();
         gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE);
 
         error = NULL;
         if (!gtk_builder_add_from_file (builder, GTKBUILDER_DIR "/" "session-selector.ui",  &error)) {
                 g_warning ("Could not load file 'session-selector.ui': %s", error->message);
                 exit (1);
         }
 
         window = (GtkWidget *) gtk_builder_get_object (builder, "main-window");
 
         store = (GtkListStore *) gtk_builder_get_object (builder, "session-store");
         sort_model = (GtkTreeModelSort *) gtk_builder_get_object (builder, "sort-model");
 
         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model),
                                          0, compare_sessions, NULL, NULL);
         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model),
                                               0, GTK_SORT_ASCENDING);
         g_signal_connect (store, "row-deleted", G_CALLBACK (on_row_deleted), NULL);
         g_signal_connect (store, "row-inserted", G_CALLBACK (on_row_inserted), NULL);
         session_list = (GtkWidget *) gtk_builder_get_object (builder, "session-list");
 
         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list));
         gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
 
         populate_session_list (session_list);
 
         cell = gtk_cell_renderer_text_new ();
         g_signal_connect (cell, "edited", G_CALLBACK (on_row_edited), NULL);
 
         column = gtk_tree_view_column_new_with_attributes ("", cell, "text", 0, NULL);
         gtk_tree_view_append_column (GTK_TREE_VIEW (session_list), GTK_TREE_VIEW_COLUMN (column));
 
         g_signal_connect (session_list, "row-activated", G_CALLBACK (on_row_activated), NULL);
 
         g_signal_connect (selection, "changed",
                           G_CALLBACK (on_selection_changed), NULL);
 
         widget = (GtkWidget *) gtk_builder_get_object (builder, "new-session");
         g_signal_connect (widget, "clicked", G_CALLBACK (on_new_session_clicked), NULL);
         widget = (GtkWidget *) gtk_builder_get_object (builder, "remove-session");
         g_signal_connect (widget, "clicked", G_CALLBACK (on_remove_session_clicked), NULL);
         widget = (GtkWidget *) gtk_builder_get_object (builder, "rename-session");
         g_signal_connect (widget, "clicked", G_CALLBACK (on_rename_session_clicked), NULL);
         widget = (GtkWidget *) gtk_builder_get_object (builder, "continue-button");
         g_signal_connect (widget, "clicked", G_CALLBACK (on_continue_clicked), NULL);
 
         g_signal_connect (window, "map", G_CALLBACK (on_map), NULL);
         gtk_widget_show (window);
 
+        if (g_strcmp0 (action, "load") == 0) {
+            info_text = _("Please select a custom session to run");
+        } else if (g_strcmp0 (action, "print") == 0) {
+            info_text = _("Please select a session to use");
+        } else if (g_strcmp0 (action, "save") == 0) {
+            info_text = _("Please select a session to save to");
+        }
+
+        label = (GtkWidget*) gtk_builder_get_object (builder, "info-label");
+        gtk_label_set_markup (GTK_LABEL (label), info_text);
+
+        selected_session = get_selected_session ();
+
+        if (selected_session == NULL) {
+		create_session_and_begin_rename ();
+	} else {
+		g_free (selected_session);
+        }
+
         gtk_main ();
 
-        auto_save_next_session_if_needed ();
+        selected_session = get_selected_session ();
+
+        if (g_strcmp0 (action, "load") == 0) {
+                make_session_current (selected_session);
+                auto_save_next_session_if_needed ();
+        } else if (g_strcmp0 (action, "save") == 0) {
+                char *last_session;
+
+                last_session = get_last_session ();
+                make_session_current (selected_session);
+                save_session ();
+                if (last_session != NULL)
+                    make_session_current (last_session);
+        } else if (g_strcmp0 (action, "print") == 0) {
+                g_print ("%s\n", selected_session);
+        }
+        g_free (selected_session);
 
         return 0;
 }
-- 
2.3.7


From ebf36d525604940d33f23732ddeb6c8ab306a7b0 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Dec 2013 11:32:52 -0500
Subject: [PATCH 12/17] make save-session stall until it finishes

---
 gnome-session/gsm-manager.c                | 58 ++++++++++++++++++++++++++----
 gnome-session/gsm-manager.h                |  2 +-
 gnome-session/org.gnome.SessionManager.xml |  1 +
 3 files changed, 53 insertions(+), 8 deletions(-)

diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c
index 67e0c82..2af3358 100644
--- a/gnome-session/gsm-manager.c
+++ b/gnome-session/gsm-manager.c
@@ -114,60 +114,61 @@ typedef enum
         GSM_MANAGER_LOGOUT_REBOOT_INTERACT,
         GSM_MANAGER_LOGOUT_SHUTDOWN,
         GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT,
 } GsmManagerLogoutType;
 
 struct GsmManagerPrivate
 {
         gboolean                failsafe;
         GsmStore               *clients;
         GsmStore               *inhibitors;
         GsmInhibitorFlag        inhibited_actions;
         GsmStore               *apps;
         GsmPresence            *presence;
         GsmXsmpServer          *xsmp_server;
 
         char                   *session_name;
         gboolean                is_fallback_session : 1;
 
         /* Current status */
         GsmManagerPhase         phase;
         guint                   phase_timeout_id;
         GSList                 *required_apps;
         GSList                 *pending_apps;
         GsmManagerLogoutMode    logout_mode;
         GSList                 *query_clients;
         guint                   query_timeout_id;
         /* 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 */
         GSList                 *next_query_clients;
+        GSList                 *pending_save_invocations;
         /* This is the action that will be done just before we exit */
         GsmManagerLogoutType    logout_type;
 
         /* List of clients which were disconnected due to disabled condition
          * and shouldn't be automatically restarted */
         GSList                 *condition_clients;
 
         GSList                 *pending_end_session_tasks;
         GCancellable           *end_session_cancellable;
 
         GSettings              *settings;
         GSettings              *session_settings;
         GSettings              *screensaver_settings;
         GSettings              *lockdown_settings;
 
         GsmSystem              *system;
         DBusGProxy             *bus_proxy;
         DBusGConnection        *connection;
         gboolean                dbus_disconnected : 1;
 
         GsmShell               *shell;
         guint                   shell_end_session_dialog_canceled_id;
         guint                   shell_end_session_dialog_open_failed_id;
         guint                   shell_end_session_dialog_confirmed_logout_id;
         guint                   shell_end_session_dialog_confirmed_shutdown_id;
         guint                   shell_end_session_dialog_confirmed_reboot_id;
 };
 
 enum {
         PROP_0,
@@ -1208,90 +1209,124 @@ query_end_session_complete (GsmManager *manager)
 static gboolean
 _client_request_save (GsmClient            *client,
                       ClientEndSessionData *data)
 {
         gboolean ret;
         GError  *error;
 
         error = NULL;
         ret = gsm_client_request_save (client, data->flags, &error);
         if (ret) {
                 g_debug ("GsmManager: adding client to query clients: %s", gsm_client_peek_id (client));
                 data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients,
                                                                       client);
         } else if (error) {
                 g_debug ("GsmManager: unable to query client: %s", error->message);
                 g_error_free (error);
         }
 
         return FALSE;
 }
 
 static gboolean
 _client_request_save_helper (const char           *id,
                              GsmClient            *client,
                              ClientEndSessionData *data)
 {
         return _client_request_save (client, data);
 }
 
 static void
+fail_pending_save_invocations (GsmManager *manager,
+                               GError     *error)
+{
+        GSList *l;
+
+        for (l = manager->priv->pending_save_invocations; l != NULL; l = l->next) {
+                DBusGMethodInvocation *context = l->data;
+
+                dbus_g_method_return_error (context, error);
+        }
+
+        g_slist_free (manager->priv->pending_save_invocations);
+        manager->priv->pending_save_invocations = NULL;
+}
+
+static void
+finish_pending_save_invocations (GsmManager *manager)
+{
+        GSList *l;
+
+        for (l = manager->priv->pending_save_invocations; l != NULL; l = l->next) {
+                DBusGMethodInvocation *context = l->data;
+
+                dbus_g_method_return (context);
+        }
+
+        g_slist_free (manager->priv->pending_save_invocations);
+        manager->priv->pending_save_invocations = NULL;
+}
+
+static void
 query_save_session_complete (GsmManager *manager)
 {
         GError *error = NULL;
 
         if (g_slist_length (manager->priv->next_query_clients) > 0) {
                 ClientEndSessionData data;
 
                 data.manager = manager;
                 data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST;
 
                 g_slist_foreach (manager->priv->next_query_clients,
                                  (GFunc)_client_request_save,
                                  &data);
 
                 g_slist_free (manager->priv->next_query_clients);
                 manager->priv->next_query_clients = NULL;
 
                 return;
         }
 
         if (manager->priv->query_timeout_id > 0) {
                 g_source_remove (manager->priv->query_timeout_id);
                 manager->priv->query_timeout_id = 0;
         }
 
         gsm_session_save (manager->priv->clients, &error);
 
         if (error) {
                 g_warning ("Error saving session: %s", error->message);
+                fail_pending_save_invocations (manager, error);
                 g_error_free (error);
+        } else {
+                finish_pending_save_invocations (manager);
         }
 }
 
 static guint32
 generate_cookie (void)
 {
         guint32 cookie;
 
         cookie = (guint32)g_random_int_range (1, G_MAXINT32);
 
         return cookie;
 }
 
 static guint32
 _generate_unique_cookie (GsmManager *manager)
 {
         guint32 cookie;
 
         do {
                 cookie = generate_cookie ();
         } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL);
 
         return cookie;
 }
 
 static gboolean
 _on_query_end_session_timeout (GsmManager *manager)
 {
         GSList *l;
 
@@ -3238,92 +3273,101 @@ gsm_manager_reboot (GsmManager            *manager,
         if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) {
                 error = g_error_new (GSM_MANAGER_ERROR,
                                      GSM_MANAGER_ERROR_NOT_IN_RUNNING,
                                      "Reboot interface is only available during the Running phase");
                 dbus_g_method_return_error (context, error);
                 g_error_free (error);
                 return FALSE;
         }
 
         if (_log_out_is_locked_down (manager)) {
                 error = g_error_new (GSM_MANAGER_ERROR,
                                      GSM_MANAGER_ERROR_LOCKED_DOWN,
                                      "Logout has been locked down");
                 dbus_g_method_return_error (context, error);
                 g_error_free (error);
                 return FALSE;
         }
 
         task = g_task_new (manager, manager->priv->end_session_cancellable, (GAsyncReadyCallback) complete_end_session_task, context);
 
         manager->priv->pending_end_session_tasks = g_slist_prepend (manager->priv->pending_end_session_tasks,
                                                                     task);
 
         request_reboot (manager);
 
         return TRUE;
 }
 
 
 gboolean
-gsm_manager_save_session (GsmManager *manager,
-                          GError     **error)
+gsm_manager_save_session (GsmManager            *manager,
+                          DBusGMethodInvocation *context)
 {
         ClientEndSessionData data;
+        GError *error;
 
         g_debug ("GsmManager: SaveSession called");
 
         g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
 
         if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) {
-                g_set_error (error,
-                             GSM_MANAGER_ERROR,
-                             GSM_MANAGER_ERROR_NOT_IN_RUNNING,
-                             "SaveSession interface is only available during the Running phase");
+                error = g_error_new (GSM_MANAGER_ERROR,
+                                     GSM_MANAGER_ERROR_NOT_IN_RUNNING,
+                                     "SaveSession interface is only available during the Running phase");
+                dbus_g_method_return_error (context, error);
+                g_error_free (error);
                 return FALSE;
         }
 
         data.manager = manager;
         data.flags = 0;
         gsm_store_foreach (manager->priv->clients,
                            (GsmStoreFunc)_client_request_save_helper,
                            &data);
 
         if (manager->priv->query_clients) {
                 manager->priv->query_timeout_id = g_timeout_add_seconds (GSM_MANAGER_SAVE_SESSION_TIMEOUT,
                                                                          (GSourceFunc)_on_query_save_session_timeout,
                                                                          manager);
+
+                manager->priv->pending_save_invocations = g_slist_prepend (manager->priv->pending_save_invocations,
+                                                                           context);
+
                 return TRUE;
         } else {
                 g_debug ("GsmManager: Nothing to save");
-                return FALSE;
+                dbus_g_method_return (context);
+                return TRUE;
         }
+
+        return TRUE;
 }
 
 gboolean
 gsm_manager_can_shutdown (GsmManager *manager,
                           gboolean   *shutdown_available,
                           GError    **error)
 {
         g_debug ("GsmManager: CanShutdown called");
 
         g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
 
         *shutdown_available = !_log_out_is_locked_down (manager) &&
                               (gsm_system_can_stop (manager->priv->system)
                                || gsm_system_can_restart (manager->priv->system)
                                || gsm_system_can_suspend (manager->priv->system)
                                || gsm_system_can_hibernate (manager->priv->system));
 
         return TRUE;
 }
 
 gboolean
 gsm_manager_logout (GsmManager *manager,
                     guint       logout_mode,
                     GError    **error)
 {
         g_debug ("GsmManager: Logout called");
 
         g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
 
         if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) {
diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h
index bc16654..c365596 100644
--- a/gnome-session/gsm-manager.h
+++ b/gnome-session/gsm-manager.h
@@ -138,61 +138,61 @@ void                _gsm_manager_set_active_session            (GsmManager     *
 /* exported methods */
 
 gboolean            gsm_manager_register_client                (GsmManager            *manager,
                                                                 const char            *app_id,
                                                                 const char            *client_startup_id,
                                                                 DBusGMethodInvocation *context);
 gboolean            gsm_manager_unregister_client              (GsmManager            *manager,
                                                                 const char            *session_client_id,
                                                                 DBusGMethodInvocation *context);
 
 gboolean            gsm_manager_inhibit                        (GsmManager            *manager,
                                                                 const char            *app_id,
                                                                 guint                  toplevel_xid,
                                                                 const char            *reason,
                                                                 guint                  flags,
                                                                 DBusGMethodInvocation *context);
 gboolean            gsm_manager_uninhibit                      (GsmManager            *manager,
                                                                 guint                  inhibit_cookie,
                                                                 DBusGMethodInvocation *context);
 gboolean            gsm_manager_is_inhibited                   (GsmManager            *manager,
                                                                 guint                  flags,
                                                                 gboolean              *is_inhibited,
                                                                 GError                *error);
 
 gboolean            gsm_manager_shutdown                       (GsmManager     *manager,
                                                                 DBusGMethodInvocation *context);
 gboolean            gsm_manager_reboot                         (GsmManager     *manager,
                                                                 DBusGMethodInvocation *context);
 
 gboolean            gsm_manager_save_session                   (GsmManager     *manager,
-                                                                GError        **error);
+								DBusGMethodInvocation *context);
 
 gboolean            gsm_manager_can_shutdown                   (GsmManager     *manager,
                                                                 gboolean       *shutdown_available,
                                                                 GError        **error);
 gboolean            gsm_manager_logout                         (GsmManager     *manager,
                                                                 guint           logout_mode,
                                                                 GError        **error);
 
 gboolean            gsm_manager_setenv                         (GsmManager     *manager,
                                                                 const char     *variable,
                                                                 const char     *value,
                                                                 GError        **error);
 gboolean            gsm_manager_get_locale                     (GsmManager     *manager,
                                                                 int             category,
                                                                 const char    **value,
                                                                 GError        **error);
 gboolean            gsm_manager_initialization_error           (GsmManager     *manager,
                                                                 const char     *message,
                                                                 gboolean        fatal,
                                                                 GError        **error);
 
 gboolean            gsm_manager_get_clients                    (GsmManager     *manager,
                                                                 GPtrArray     **clients,
                                                                 GError        **error);
 gboolean            gsm_manager_get_inhibitors                 (GsmManager     *manager,
                                                                 GPtrArray     **inhibitors,
                                                                 GError        **error);
 gboolean            gsm_manager_is_autostart_condition_handled (GsmManager     *manager,
                                                                 const char     *condition,
                                                                 gboolean       *handled,
diff --git a/gnome-session/org.gnome.SessionManager.xml b/gnome-session/org.gnome.SessionManager.xml
index eacac4b..81d3da1 100644
--- a/gnome-session/org.gnome.SessionManager.xml
+++ b/gnome-session/org.gnome.SessionManager.xml
@@ -262,60 +262,61 @@
           <doc:summary>True if condition is handled, false otherwise</doc:summary>
         </doc:doc>
       </arg>
       <doc:doc>
         <doc:description>
           <doc:para>Allows the caller to determine whether the session manager is
           handling changes to the specified autostart condition.</doc:para>
         </doc:description>
       </doc:doc>
     </method>
 
     <method name="Shutdown">
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       <doc:doc>
         <doc:description>
           <doc:para>Request a shutdown dialog.</doc:para>
         </doc:description>
       </doc:doc>
     </method>
 
     <method name="Reboot">
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       <doc:doc>
         <doc:description>
           <doc:para>Request a reboot dialog.</doc:para>
         </doc:description>
       </doc:doc>
     </method>
 
     <method name="SaveSession">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       <doc:doc>
 	<doc:description>
 	  <doc:para>Request to save session</doc:para>
 	</doc:description>
       </doc:doc>
     </method>
 
     <method name="CanShutdown">
       <arg name="is_available" direction="out" type="b">
         <doc:doc>
           <doc:summary>True if shutdown is available to the user, false otherwise</doc:summary>
         </doc:doc>
       </arg>
       <doc:doc>
         <doc:description>
           <doc:para>Allows the caller to determine whether or not it's okay to show
           a shutdown option in the UI</doc:para>
         </doc:description>
       </doc:doc>
     </method>
 
     <method name="Logout">
       <arg name="mode" type="u" direction="in">
         <doc:doc>
           <doc:summary>The type of logout that is being requested</doc:summary>
         </doc:doc>
       </arg>
       <doc:doc>
         <doc:description>
           <doc:para>Request a logout dialog</doc:para>
-- 
2.3.7


From b7ed1a7fd45a0dac66353f8ec5b8f30a5f0207a8 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Dec 2013 15:32:04 -0500
Subject: [PATCH 13/17] manager: save session type in session dir

If a user saved their session when in classic mode, make sure we
record that information so subsequent calls to gnome-session will
restore classic mode.
---
 gnome-session/gsm-manager.c      | 21 ++++++++++++++++++--
 gnome-session/gsm-manager.h      |  1 +
 gnome-session/gsm-session-save.c | 41 +++++++++++++++++++++++++++++++++++++---
 gnome-session/gsm-session-save.h |  5 +++--
 gnome-session/main.c             | 14 +++++++++++---
 5 files changed, 72 insertions(+), 10 deletions(-)

diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c
index 2af3358..8a8943d 100644
--- a/gnome-session/gsm-manager.c
+++ b/gnome-session/gsm-manager.c
@@ -1265,61 +1265,61 @@ finish_pending_save_invocations (GsmManager *manager)
         g_slist_free (manager->priv->pending_save_invocations);
         manager->priv->pending_save_invocations = NULL;
 }
 
 static void
 query_save_session_complete (GsmManager *manager)
 {
         GError *error = NULL;
 
         if (g_slist_length (manager->priv->next_query_clients) > 0) {
                 ClientEndSessionData data;
 
                 data.manager = manager;
                 data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST;
 
                 g_slist_foreach (manager->priv->next_query_clients,
                                  (GFunc)_client_request_save,
                                  &data);
 
                 g_slist_free (manager->priv->next_query_clients);
                 manager->priv->next_query_clients = NULL;
 
                 return;
         }
 
         if (manager->priv->query_timeout_id > 0) {
                 g_source_remove (manager->priv->query_timeout_id);
                 manager->priv->query_timeout_id = 0;
         }
 
-        gsm_session_save (manager->priv->clients, &error);
+        gsm_session_save (manager->priv->clients, manager->priv->session_name, &error);
 
         if (error) {
                 g_warning ("Error saving session: %s", error->message);
                 fail_pending_save_invocations (manager, error);
                 g_error_free (error);
         } else {
                 finish_pending_save_invocations (manager);
         }
 }
 
 static guint32
 generate_cookie (void)
 {
         guint32 cookie;
 
         cookie = (guint32)g_random_int_range (1, G_MAXINT32);
 
         return cookie;
 }
 
 static guint32
 _generate_unique_cookie (GsmManager *manager)
 {
         guint32 cookie;
 
         do {
                 cookie = generate_cookie ();
         } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL);
 
         return cookie;
@@ -1532,60 +1532,77 @@ debug_app_summary (GsmManager *manager)
 
         g_debug ("GsmManager: App startup summary");
         for (phase = GSM_MANAGER_PHASE_EARLY_INITIALIZATION; phase < GSM_MANAGER_PHASE_RUNNING; phase++) {
                 g_debug ("GsmManager: Phase %s", phase_num_to_name (phase));
                 gsm_store_foreach (manager->priv->apps,
                                    (GsmStoreFunc)_debug_app_for_phase,
                                    GUINT_TO_POINTER (phase));
         }
 }
 
 void
 gsm_manager_start (GsmManager *manager)
 {
         g_debug ("GsmManager: GSM starting to manage");
 
         g_return_if_fail (GSM_IS_MANAGER (manager));
 
         gsm_xsmp_server_start (manager->priv->xsmp_server);
         gsm_manager_set_phase (manager, GSM_MANAGER_PHASE_EARLY_INITIALIZATION);
         debug_app_summary (manager);
         start_phase (manager);
 }
 
 const char *
 _gsm_manager_get_default_session (GsmManager     *manager)
 {
         return g_settings_get_string (manager->priv->session_settings,
                                       KEY_SESSION_NAME);
 }
 
+char *
+_gsm_manager_get_saved_session (GsmManager     *manager)
+{
+        char *file;
+        char *type;
+        gboolean loaded;
+
+        file = g_build_filename (gsm_util_get_saved_session_dir (), "type", NULL);
+        loaded = g_file_get_contents (file, &type, NULL, NULL);
+        g_free (file);
+
+        if (!loaded)
+                return NULL;
+
+        return type;
+}
+
 void
 _gsm_manager_set_active_session (GsmManager     *manager,
                                  const char     *session_name,
                                  gboolean        is_fallback)
 {
         g_free (manager->priv->session_name);
         manager->priv->session_name = g_strdup (session_name);
         manager->priv->is_fallback_session = is_fallback;
 }
 
 static gboolean
 _app_has_app_id (const char   *id,
                  GsmApp       *app,
                  const char   *app_id_a)
 {
         const char *app_id_b;
 
         app_id_b = gsm_app_peek_app_id (app);
         return (app_id_b != NULL && strcmp (app_id_a, app_id_b) == 0);
 }
 
 static GsmApp *
 find_app_for_app_id (GsmManager *manager,
                      const char *app_id)
 {
         GsmApp *app;
         app = (GsmApp *)gsm_store_find (manager->priv->apps,
                                         (GsmStoreFunc)_app_has_app_id,
                                         (char *)app_id);
         return app;
@@ -2070,61 +2087,61 @@ on_xsmp_client_register_confirmed (GsmXSMPClient *client,
         }
 }
 
 static gboolean
 auto_save_is_enabled (GsmManager *manager)
 {
         return g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE_ONE_SHOT)
             || g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE);
 }
 
 static void
 maybe_save_session (GsmManager *manager)
 {
         GError *error;
 
         if (gsm_system_is_login_session (manager->priv->system))
                 return;
 
         /* We only allow session saving when session is running or when
          * logging out */
         if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING &&
             manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) {
                 return;
         }
 
         if (!auto_save_is_enabled (manager)) {
                 return;
         }
 
         error = NULL;
-        gsm_session_save (manager->priv->clients, &error);
+        gsm_session_save (manager->priv->clients, manager->priv->session_name, &error);
 
         if (error) {
                 g_warning ("Error saving session: %s", error->message);
                 g_error_free (error);
         }
 }
 
 static void
 _handle_client_end_session_response (GsmManager *manager,
                                      GsmClient  *client,
                                      gboolean    is_ok,
                                      gboolean    do_last,
                                      gboolean    cancel,
                                      const char *reason)
 {
         /* just ignore if we are not yet running */
         if (manager->priv->phase < GSM_MANAGER_PHASE_RUNNING) {
                 return;
         }
 
         g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :"");
 
         if (manager->priv->phase == GSM_MANAGER_PHASE_RUNNING) {
                 /* Ignore responses when no requests were sent */
                 if (manager->priv->query_clients == NULL) {
                         return;
                 }
 
                 manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client);
 
diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h
index c365596..d10879b 100644
--- a/gnome-session/gsm-manager.h
+++ b/gnome-session/gsm-manager.h
@@ -103,60 +103,61 @@ typedef enum
 } GsmManagerError;
 
 #define GSM_MANAGER_ERROR gsm_manager_error_quark ()
 
 GType               gsm_manager_error_get_type                 (void);
 #define GSM_MANAGER_TYPE_ERROR (gsm_manager_error_get_type ())
 
 GQuark              gsm_manager_error_quark                    (void);
 GType               gsm_manager_get_type                       (void);
 
 GsmManager *        gsm_manager_new                            (GsmStore       *client_store,
                                                                 gboolean        failsafe);
 GsmManager *        gsm_manager_get                            (void);
 
 gboolean            gsm_manager_get_failsafe                   (GsmManager     *manager);
 
 gboolean            gsm_manager_add_autostart_app              (GsmManager     *manager,
                                                                 const char     *path,
                                                                 const char     *provides);
 gboolean            gsm_manager_add_required_app               (GsmManager     *manager,
                                                                 const char     *path,
                                                                 const char     *provides);
 gboolean            gsm_manager_add_autostart_apps_from_dir    (GsmManager     *manager,
                                                                 const char     *path);
 gboolean            gsm_manager_add_legacy_session_apps        (GsmManager     *manager,
                                                                 const char     *path);
 
 void                gsm_manager_start                          (GsmManager     *manager);
 
 const char *        _gsm_manager_get_default_session           (GsmManager     *manager);
+char *              _gsm_manager_get_saved_session             (GsmManager     *manager);
 
 void                _gsm_manager_set_active_session            (GsmManager     *manager,
                                                                 const char     *session_name,
                                                                 gboolean        is_fallback);
 
 /* exported methods */
 
 gboolean            gsm_manager_register_client                (GsmManager            *manager,
                                                                 const char            *app_id,
                                                                 const char            *client_startup_id,
                                                                 DBusGMethodInvocation *context);
 gboolean            gsm_manager_unregister_client              (GsmManager            *manager,
                                                                 const char            *session_client_id,
                                                                 DBusGMethodInvocation *context);
 
 gboolean            gsm_manager_inhibit                        (GsmManager            *manager,
                                                                 const char            *app_id,
                                                                 guint                  toplevel_xid,
                                                                 const char            *reason,
                                                                 guint                  flags,
                                                                 DBusGMethodInvocation *context);
 gboolean            gsm_manager_uninhibit                      (GsmManager            *manager,
                                                                 guint                  inhibit_cookie,
                                                                 DBusGMethodInvocation *context);
 gboolean            gsm_manager_is_inhibited                   (GsmManager            *manager,
                                                                 guint                  flags,
                                                                 gboolean              *is_inhibited,
                                                                 GError                *error);
 
 gboolean            gsm_manager_shutdown                       (GsmManager     *manager,
diff --git a/gnome-session/gsm-session-save.c b/gnome-session/gsm-session-save.c
index ea23be5..06eeeb1 100644
--- a/gnome-session/gsm-session-save.c
+++ b/gnome-session/gsm-session-save.c
@@ -1,73 +1,105 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
  * gsm-session-save.c
  * Copyright (C) 2008 Lucas Rocha.
  *
  * 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
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 
+#include <string.h>
+
 #include <glib.h>
 #include <glib/gstdio.h>
 #include <gio/gio.h>
 
 #include "gsm-util.h"
 #include "gsm-autostart-app.h"
 #include "gsm-client.h"
 
 #include "gsm-session-save.h"
 
 #define GSM_MANAGER_SCHEMA        "org.gnome.SessionManager"
 #define KEY_AUTOSAVE_ONE_SHOT     "auto-save-session-one-shot"
 
 
 static gboolean gsm_session_clear_saved_session (const char *directory,
                                                  GHashTable *discard_hash);
 
 typedef struct {
         char        *dir;
         GHashTable  *discard_hash;
         GError     **error;
 } SessionSaveData;
 
+static void
+clear_session_type (const char *save_dir)
+{
+        char *file;
+
+        file = g_build_filename (save_dir, "type", NULL);
+
+        g_unlink (file);
+
+        g_free (file);
+}
+
+static void
+set_session_type (const char *save_dir,
+                  const char *type)
+{
+        char *file;
+        GError *error;
+
+        file = g_build_filename (save_dir, "type", NULL);
+
+        error = NULL;
+        g_file_set_contents (file, type, strlen (type), &error);
+        if (error != NULL)
+                g_warning ("couldn't save session type to %s: %s",
+                           type, error->message);
+
+        g_free (file);
+}
+
 static gboolean
 save_one_client (char            *id,
                  GObject         *object,
                  SessionSaveData *data)
 {
         GsmClient  *client;
         GKeyFile   *keyfile;
         const char *app_id;
         char       *path = NULL;
         char       *filename = NULL;
         char       *contents = NULL;
         gsize       length = 0;
         char       *discard_exec;
         GError     *local_error;
 
         client = GSM_CLIENT (object);
 
         local_error = NULL;
 
         keyfile = gsm_client_save (client, &local_error);
 
         if (keyfile == NULL || local_error) {
                 goto out;
         }
 
         contents = g_key_file_to_data (keyfile, &length, &local_error);
 
         if (local_error) {
                 goto out;
         }
@@ -107,112 +139,114 @@ save_one_client (char            *id,
                                               GSM_AUTOSTART_APP_DISCARD_KEY,
                                               NULL);
         if (discard_exec) {
                 g_hash_table_insert (data->discard_hash,
                                      discard_exec, discard_exec);
         }
 
         g_debug ("GsmSessionSave: saved client %s to %s", id, filename);
 
 out:
         if (keyfile != NULL) {
                 g_key_file_free (keyfile);
         }
 
         g_free (contents);
         g_free (filename);
         g_free (path);
 
         /* in case of any error, stop saving session */
         if (local_error) {
                 g_propagate_error (data->error, local_error);
                 g_error_free (local_error);
 
                 return TRUE;
         }
 
         return FALSE;
 }
 
 void
-gsm_session_save (GsmStore  *client_store,
-                  GError   **error)
+gsm_session_save (GsmStore    *client_store,
+                  const char  *type,
+                  GError     **error)
 {
         GSettings       *settings;
         const char      *save_dir;
         char            *tmp_dir;
         SessionSaveData  data;
 
         g_debug ("GsmSessionSave: Saving session");
 
         /* Clear one shot key autosave in the event its set (so that it's actually
          * one shot only)
          */
         settings = g_settings_new (GSM_MANAGER_SCHEMA);
         g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE);
         g_object_unref (settings);
 
         save_dir = gsm_util_get_saved_session_dir ();
         if (save_dir == NULL) {
                 g_warning ("GsmSessionSave: cannot create saved session directory");
                 return;
         }
 
         tmp_dir = gsm_util_get_empty_tmp_session_dir ();
         if (tmp_dir == NULL) {
                 g_warning ("GsmSessionSave: cannot create new saved session directory");
                 return;
         }
 
         /* save the session in a temp directory, and remember the discard
          * commands */
         data.dir = tmp_dir;
         data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                    g_free, NULL);
         data.error = error;
 
         gsm_store_foreach (client_store,
                            (GsmStoreFunc) save_one_client,
                            &data);
 
         if (!*error) {
                 char *session_dir;
 
                 if (g_file_test (save_dir, G_FILE_TEST_IS_SYMLINK))
                         session_dir = g_file_read_link (save_dir, error);
                 else
                         session_dir = g_strdup (save_dir);
 
                 if (session_dir != NULL) {
-
                         char *absolute_session_dir;
 
+                        set_session_type (tmp_dir, type);
+
                         if (g_path_is_absolute (session_dir)) {
                                 absolute_session_dir = g_strdup (session_dir);
                         } else {
                                 char *parent_dir;
 
                                 parent_dir = g_path_get_dirname (save_dir);
                                 absolute_session_dir = g_build_filename (parent_dir, session_dir, NULL);
                                 g_free (parent_dir);
                         }
                         g_free (session_dir);
 
                         /* remove the old saved session */
                         gsm_session_clear_saved_session (absolute_session_dir, data.discard_hash);
 
                         if (g_file_test (absolute_session_dir, G_FILE_TEST_IS_DIR))
                                 g_rmdir (absolute_session_dir);
                         g_rename (tmp_dir, absolute_session_dir);
 
                         g_free (absolute_session_dir);
                 }
         } else {
                 g_warning ("GsmSessionSave: error saving session: %s", (*error)->message);
                 /* FIXME: we should create a hash table filled with the discard
                  * commands that are in desktop files from save_dir. */
                 gsm_session_clear_saved_session (tmp_dir, NULL);
                 g_rmdir (tmp_dir);
         }
 
         g_hash_table_destroy (data.discard_hash);
         g_free (tmp_dir);
@@ -292,31 +326,32 @@ gsm_session_clear_saved_session (const char *directory,
 
         while ((filename = g_dir_read_name (dir))) {
                 char *path = g_build_filename (directory,
                                                filename, NULL);
 
                 result = gsm_session_clear_one_client (path, discard_hash)
                          && result;
 
                 g_free (path);
         }
 
         g_dir_close (dir);
 
         return result;
 }
 
 void
 gsm_session_save_clear (void)
 {
         const char *save_dir;
 
         g_debug ("GsmSessionSave: Clearing saved session");
 
         save_dir = gsm_util_get_saved_session_dir ();
         if (save_dir == NULL) {
                 g_warning ("GsmSessionSave: cannot create saved session directory");
                 return;
         }
 
 	gsm_session_clear_saved_session (save_dir, NULL);
+        clear_session_type (save_dir);
 }
diff --git a/gnome-session/gsm-session-save.h b/gnome-session/gsm-session-save.h
index e623260..c91b561 100644
--- a/gnome-session/gsm-session-save.h
+++ b/gnome-session/gsm-session-save.h
@@ -1,33 +1,34 @@
 /* gsm-session-save.h
  * Copyright (C) 2008 Lucas Rocha.
  *
  * 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
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #ifndef __GSM_SESSION_SAVE_H__
 #define __GSM_SESSION_SAVE_H__
 
 #include <glib.h>
 
 #include "gsm-store.h"
 
 G_BEGIN_DECLS
 
-void      gsm_session_save                 (GsmStore  *client_store,
-                                            GError   **error);
+void      gsm_session_save                 (GsmStore    *client_store,
+                                            const char  *type,
+                                            GError     **error);
 void      gsm_session_save_clear           (void);
 
 G_END_DECLS
 
 #endif /* __GSM_SESSION_SAVE_H__ */
diff --git a/gnome-session/main.c b/gnome-session/main.c
index fce5e86..52d2aaa 100644
--- a/gnome-session/main.c
+++ b/gnome-session/main.c
@@ -270,60 +270,61 @@ require_dbus_session (int      argc,
 }
 
 static gboolean
 check_gl (GError **error)
 {
         int status;
         char *argv[] = { LIBEXECDIR "/gnome-session-check-accelerated", NULL };
 
         if (getenv ("DISPLAY") == NULL) {
                 /* Not connected to X11, someone else will take care of checking GL */
                 return TRUE;
         }
 
         if (!g_spawn_sync (NULL, (char **) argv, NULL, 0, NULL, NULL, NULL, NULL,
                            &status, error)) {
                 return FALSE;
         }
 
         return g_spawn_check_exit_status (status, error);
 }
 
 int
 main (int argc, char **argv)
 {
         GError           *error = NULL;
         GsmManager       *manager;
         GsmStore         *client_store;
         static char     **override_autostart_dirs = NULL;
         static char      *opt_session_name = NULL;
         const char       *session_name;
+        char             *saved_session_name = NULL;
         gboolean          gl_failed = FALSE;
         GOptionContext   *options;
         static GOptionEntry entries[] = {
                 { "autostart", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &override_autostart_dirs, N_("Override standard autostart directories"), N_("AUTOSTART_DIR") },
                 { "session", 0, 0, G_OPTION_ARG_STRING, &opt_session_name, N_("Session to use"), N_("SESSION_NAME") },
                 { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable debugging code"), NULL },
                 { "failsafe", 'f', 0, G_OPTION_ARG_NONE, &failsafe, N_("Do not load user-specified applications"), NULL },
                 { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL },
                 /* Translators: the 'fail whale' is the black dialog we show when something goes seriously wrong */
                 { "whale", 0, 0, G_OPTION_ARG_NONE, &please_fail, N_("Show the fail whale dialog for testing"), NULL },
                 { NULL, 0, 0, 0, NULL, NULL, NULL }
         };
 
         /* Make sure that we have a session bus */
         if (!require_dbus_session (argc, argv, &error)) {
                 gsm_util_init_error (TRUE, "%s", error->message);
         }
 
         /* Check GL, if it doesn't work out then force software fallback */
         if (!check_gl (&error)) {
                 gl_failed = TRUE;
 
                 g_debug ("hardware acceleration check failed: %s",
                          error? error->message : "");
                 g_clear_error (&error);
                 if (g_getenv ("LIBGL_ALWAYS_SOFTWARE") == NULL) {
                         g_setenv ("LIBGL_ALWAYS_SOFTWARE", "1", TRUE);
                         if (!check_gl (&error)) {
                                 g_warning ("software acceleration check failed: %s",
                                            error? error->message : "");
@@ -402,53 +403,60 @@ main (int argc, char **argv)
         gsm_util_setenv ("XDG_MENU_PREFIX", "gnome-");
 
         client_store = gsm_store_new ();
 
         /* Talk to logind before acquiring a name, since it does synchronous
          * calls at initialization time that invoke a main loop and if we
          * already owned a name, then we would service too early during
          * that main loop.
          */
         g_object_unref (gsm_get_system ());
 
         if (!acquire_name ()) {
                 gsm_fail_whale_dialog_we_failed (TRUE, TRUE, NULL);
                 gsm_main ();
                 exit (1);
         }
 
         manager = gsm_manager_new (client_store, failsafe);
 
         g_signal_connect_object (bus_proxy,
                                  "destroy",
                                  G_CALLBACK (shutdown_cb),
                                  manager,
                                  G_CONNECT_SWAPPED);
 
         g_unix_signal_add (SIGTERM, term_or_int_signal_cb, manager);
         g_unix_signal_add (SIGINT, term_or_int_signal_cb, manager);
         g_unix_signal_add (SIGUSR1, sigusr1_cb, manager);
         g_unix_signal_add (SIGUSR2, sigusr2_cb, manager);
 
-        if (IS_STRING_EMPTY (opt_session_name))
-                session_name = _gsm_manager_get_default_session (manager);
-        else
+        if (IS_STRING_EMPTY (opt_session_name)) {
+                saved_session_name = _gsm_manager_get_saved_session (manager);
+
+                if (IS_STRING_EMPTY (saved_session_name))
+                        session_name = _gsm_manager_get_default_session (manager);
+                else
+                        session_name = saved_session_name;
+        } else {
                 session_name = opt_session_name;
+        }
 
         gsm_util_set_autostart_dirs (override_autostart_dirs);
 
         if (!gsm_session_fill (manager, session_name)) {
                 gsm_fail_whale_dialog_we_failed (FALSE, TRUE, NULL);
         }
 
         gsm_manager_start (manager);
 
         gsm_main ();
 
         g_clear_object (&manager);
         g_clear_object (&client_store);
         g_clear_object (&bus_proxy);
+        g_free (saved_session_name);
 
         gdm_log_shutdown ();
 
         return 0;
 }
-- 
2.3.7


From c9c7daa61e77f86a4b3cd53ae55af3ad93e8b406 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 7 Jan 2014 21:16:23 -0500
Subject: [PATCH 14/17] session-selector: restore saved session mode

When using the custom session selector, we need to know
whether to use classic mode or not.

This commit makes us use whatever mode was saved with the
session.
---
 tools/gnome-session-custom-session | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/tools/gnome-session-custom-session b/tools/gnome-session-custom-session
index 07fdb0c..358aee0 100644
--- a/tools/gnome-session-custom-session
+++ b/tools/gnome-session-custom-session
@@ -1,4 +1,19 @@
 #! /bin/sh
 
 gnome-session-selector
-exec gnome-session
+
+type_file="${XDG_CONFIG_HOME:-$HOME/.config}/gnome-session/saved-session/type"
+
+session_type=""
+if [ -e "$type_file" ]; then
+        read session_type < "$type_file"
+fi
+
+session_type_argument=""
+[ -n "$session_type" ] && session_type_argument="--session=$session_type"
+
+if [ "$session_type" = "gnome-classic" ]; then
+        export GNOME_SHELL_SESSION_MODE="classic"
+fi
+
+exec gnome-session "$session_type_argument"
-- 
2.3.7


From ded037a059a781042d61e3bc7ef1ced0c9ee706f Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Dec 2013 10:53:33 -0500
Subject: [PATCH 15/17] session-selector: refresh from recent glade

The ui file is rather old.  This commit just opens it up in a recent
glade and resaves it, so we have a fresh starting point to make
changes.
---
 data/session-selector.ui | 80 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 54 insertions(+), 26 deletions(-)

diff --git a/data/session-selector.ui b/data/session-selector.ui
index 1534a74..4d1e300 100644
--- a/data/session-selector.ui
+++ b/data/session-selector.ui
@@ -1,195 +1,223 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="2.16"/>
-  <!-- interface-naming-policy project-wide -->
-  <object class="GtkListStore" id="session-store">
-    <columns>
-      <!-- column-name name -->
-      <column type="gchararray"/>
-    </columns>
-  </object>
-  <object class="GtkTreeModelSort" id="sort-model">
-    <property name="model">session-store</property>
-  </object>
+  <!-- interface-requires gtk+ 3.0 -->
   <object class="GtkWindow" id="main-window">
+    <property name="can_focus">False</property>
     <property name="title" translatable="yes">Custom Session</property>
     <property name="window_position">center</property>
     <property name="default_width">500</property>
     <property name="default_height">310</property>
     <property name="decorated">False</property>
     <child>
       <object class="GtkFrame" id="frame1">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="label_xalign">0.5</property>
         <property name="shadow_type">out</property>
         <child>
           <object class="GtkAlignment" id="alignment3">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <property name="border_width">12</property>
             <child>
               <object class="GtkVBox" id="vbox3">
                 <property name="visible">True</property>
-                <property name="orientation">vertical</property>
+                <property name="can_focus">False</property>
                 <property name="spacing">6</property>
-
                 <child>
                   <object class="GtkInfoBar" id="info-bar">
                     <property name="visible">True</property>
-                    <property name="message-type">other</property>
-
+                    <property name="can_focus">False</property>
+                    <property name="message_type">other</property>
                     <child internal-child="content_area">
-                      <object class="GtkHBox" id="info-bar-content_area">
+                      <object class="GtkBox" id="info-bar-content_area">
                         <property name="visible">True</property>
+                        <property name="can_focus">False</property>
                         <property name="orientation">vertical</property>
-                        <property name="spacing">0</property>
                         <child>
                           <object class="GtkLabel" id="info-label">
                             <property name="visible">True</property>
-                            <property name="xalign">0.0</property>
-                            <property name="yalign">0.5</property>
+                            <property name="can_focus">False</property>
+                            <property name="xalign">0</property>
                             <property name="label" translatable="yes">Please select a custom session to use</property>
                           </object>
                           <packing>
-                            <property name="expand">True</property>
-                            <property name="fill">True</property>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
                             <property name="position">0</property>
                           </packing>
                         </child>
                       </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child internal-child="action_area">
+                      <object class="GtkButtonBox" id="infobar-action_area1">
+                        <property name="can_focus">False</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
                     </child>
                   </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
                     <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkVBox" id="vbox4">
                     <property name="visible">True</property>
-                    <property name="orientation">vertical</property>
+                    <property name="can_focus">False</property>
                     <property name="spacing">12</property>
                     <child>
                       <object class="GtkHBox" id="hbox3">
                         <property name="visible">True</property>
+                        <property name="can_focus">False</property>
                         <property name="spacing">12</property>
                         <child>
                           <object class="GtkScrolledWindow" id="scrolledwindow2">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="hscrollbar_policy">never</property>
-                            <property name="vscrollbar_policy">automatic</property>
                             <property name="shadow_type">in</property>
                             <child>
                               <object class="GtkTreeView" id="session-list">
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
+                                <property name="model">sort-model</property>
                                 <property name="headers_visible">False</property>
                                 <property name="search_column">0</property>
-                                <property name="model">sort-model</property>
+                                <child internal-child="selection">
+                                  <object class="GtkTreeSelection" id="treeview-selection1"/>
+                                </child>
                               </object>
                             </child>
                           </object>
                           <packing>
+                            <property name="expand">True</property>
+                            <property name="fill">True</property>
                             <property name="position">0</property>
                           </packing>
                         </child>
                         <child>
                           <object class="GtkVButtonBox" id="vbuttonbox2">
                             <property name="visible">True</property>
-                            <property name="orientation">vertical</property>
+                            <property name="can_focus">False</property>
                             <property name="spacing">6</property>
                             <property name="layout_style">start</property>
                             <child>
                               <object class="GtkButton" id="new-session">
                                 <property name="label" translatable="yes">_New Session</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">True</property>
                                 <property name="use_underline">True</property>
                               </object>
                               <packing>
                                 <property name="expand">False</property>
                                 <property name="fill">False</property>
                                 <property name="position">0</property>
                               </packing>
                             </child>
                             <child>
                               <object class="GtkButton" id="remove-session">
                                 <property name="label" translatable="yes">_Remove Session</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">True</property>
                                 <property name="use_underline">True</property>
                               </object>
                               <packing>
                                 <property name="expand">False</property>
                                 <property name="fill">False</property>
                                 <property name="position">1</property>
                               </packing>
                             </child>
                             <child>
                               <object class="GtkButton" id="rename-session">
                                 <property name="label" translatable="yes">Rena_me Session</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">True</property>
                                 <property name="use_underline">True</property>
                               </object>
                               <packing>
                                 <property name="expand">False</property>
                                 <property name="fill">False</property>
                                 <property name="position">2</property>
                               </packing>
                             </child>
                           </object>
                           <packing>
                             <property name="expand">False</property>
+                            <property name="fill">True</property>
                             <property name="position">1</property>
                           </packing>
                         </child>
                       </object>
                       <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
                         <property name="position">1</property>
                       </packing>
                     </child>
                   </object>
                   <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
                     <property name="position">1</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkHButtonBox" id="hbuttonbox2">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                     <property name="spacing">6</property>
                     <property name="layout_style">end</property>
                     <child>
                       <object class="GtkButton" id="continue-button">
                         <property name="label" translatable="yes">_Continue</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_default">True</property>
                         <property name="has_default">True</property>
                         <property name="receives_default">True</property>
                         <property name="use_underline">True</property>
                       </object>
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
                         <property name="position">0</property>
                       </packing>
                     </child>
                   </object>
                   <packing>
                     <property name="expand">False</property>
+                    <property name="fill">True</property>
                     <property name="position">2</property>
                   </packing>
                 </child>
               </object>
             </child>
           </object>
         </child>
       </object>
     </child>
   </object>
+  <object class="GtkListStore" id="session-store">
+    <columns>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkTreeModelSort" id="sort-model">
+    <property name="model">session-store</property>
+  </object>
 </interface>
-- 
2.3.7


From 34d666e847c142545c3de244df08705f6a19b933 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 7 Jan 2014 21:02:02 -0500
Subject: [PATCH 16/17] session-selector: add toggle for classic/normal
 selection

Since we offer both classic mode and regular mode when
not using the session selector, we should also offer it
when using the session selector.
---
 data/session-selector.ui       |  39 ++++++++++++++-
 tools/gnome-session-selector.c | 105 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 142 insertions(+), 2 deletions(-)

diff --git a/data/session-selector.ui b/data/session-selector.ui
index 4d1e300..beab73a 100644
--- a/data/session-selector.ui
+++ b/data/session-selector.ui
@@ -153,71 +153,106 @@
                                 <property name="fill">False</property>
                                 <property name="position">2</property>
                               </packing>
                             </child>
                           </object>
                           <packing>
                             <property name="expand">False</property>
                             <property name="fill">True</property>
                             <property name="position">1</property>
                           </packing>
                         </child>
                       </object>
                       <packing>
                         <property name="expand">True</property>
                         <property name="fill">True</property>
                         <property name="position">1</property>
                       </packing>
                     </child>
                   </object>
                   <packing>
                     <property name="expand">True</property>
                     <property name="fill">True</property>
                     <property name="position">1</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkHButtonBox" id="hbuttonbox2">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="spacing">6</property>
-                    <property name="layout_style">end</property>
+                    <child>
+                      <object class="GtkBox" id="box1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="classic-mode-label">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="yes">Classic Experience</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSwitch" id="classic-mode-switch">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
                     <child>
                       <object class="GtkButton" id="continue-button">
                         <property name="label" translatable="yes">_Continue</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_default">True</property>
                         <property name="has_default">True</property>
                         <property name="receives_default">True</property>
                         <property name="use_underline">True</property>
                       </object>
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
-                        <property name="position">0</property>
+                        <property name="position">1</property>
+                        <property name="non_homogeneous">True</property>
                       </packing>
                     </child>
                   </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
                     <property name="position">2</property>
                   </packing>
                 </child>
               </object>
             </child>
           </object>
         </child>
       </object>
     </child>
   </object>
   <object class="GtkListStore" id="session-store">
     <columns>
       <!-- column-name name -->
       <column type="gchararray"/>
     </columns>
   </object>
   <object class="GtkTreeModelSort" id="sort-model">
     <property name="model">session-store</property>
   </object>
 </interface>
diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c
index 8f37eca..dbe3bda 100644
--- a/tools/gnome-session-selector.c
+++ b/tools/gnome-session-selector.c
@@ -16,60 +16,61 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  *
  * Written by: Matthias Clasen <mclasen@redhat.com>
  */
 
 #include "config.h"
 
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include <glib.h>
 #include <gtk/gtk.h>
 #include <gio/gio.h>
 
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
 
 #define GSM_SERVICE_DBUS   "org.gnome.SessionManager"
 #define GSM_PATH_DBUS      "/org/gnome/SessionManager"
 #define GSM_INTERFACE_DBUS "org.gnome.SessionManager"
 
 #define GSM_MANAGER_SCHEMA        "org.gnome.SessionManager"
 #define KEY_AUTOSAVE_ONE_SHOT     "auto-save-session-one-shot"
+#define DEFAULT_SESSION_NAME      "gnome"
 
 static GtkBuilder *builder;
 static GtkWidget *session_list;
 static GtkListStore *store;
 static GtkTreeModelSort *sort_model;
 static char *info_text;
 
 static void select_session (const char *name);
 static gboolean make_session_current (const char *name);
 
 static char *
 get_session_path (const char *name)
 {
         return g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL);
 }
 
 static char *
 find_new_session_name (void)
 {
         char *name;
         char *path;
         int i;
 
         for (i = 1; i < 20; i++) {
                 name = g_strdup_printf (_("Session %d"), i);
                 path = get_session_path (name);
                 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
                         g_free (path);
                         return name;
                 }
@@ -125,104 +126,126 @@ is_valid_session_name (const char *name)
         gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
         do {
                 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &n, -1);
                 if (strcmp (n, name) == 0) {
                         char *message;
                         message = g_strdup_printf (_("A session named ā€˜%sā€™ already exists"), name);
                         warning_text = g_strdup_printf ("%s\n<small><b>Note:</b> <i>%s</i></small>", info_text, message);
                         g_free (message);
                         g_free (n);
                         break;
                 }
                 g_free (n);
         } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
 
         info_bar = (GtkWidget *) gtk_builder_get_object (builder, "info-bar");
         label = (GtkWidget*) gtk_builder_get_object (builder, "info-label");
 
         if (warning_text != NULL) {
             gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
             gtk_label_set_markup (GTK_LABEL (label), warning_text);
             g_free (warning_text);
             return FALSE;
         }
 
         gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_OTHER);
         gtk_label_set_markup (GTK_LABEL (label), info_text);
 
         return TRUE;
 }
 
+static char *
+get_session_type_from_file (const char *name)
+{
+        char *file;
+        char *type;
+        gboolean loaded;
+
+        file = g_build_filename (g_get_user_config_dir (), "gnome-session", name, "type", NULL);
+        loaded = g_file_get_contents (file, &type, NULL, NULL);
+        g_free (file);
+
+        if (!loaded)
+                return g_strdup (DEFAULT_SESSION_NAME);
+
+        return type;
+}
+
 static void
 populate_session_list (GtkWidget *session_list)
 {
         GtkTreeIter iter;
         char *path;
         const char *name;
         GDir *dir;
         GError *error;
         char *saved_session;
         char *default_session;
         char *default_name;
         char last_session[PATH_MAX] = "";
 
         saved_session = get_session_path ("saved-session");
 
         if (!g_file_test (saved_session, G_FILE_TEST_IS_SYMLINK)) {
                 default_name = find_new_session_name ();
                 default_session = get_session_path (default_name);
                 rename (saved_session, default_session);
                 if (symlink (default_name, saved_session) < 0)
                         g_warning ("Failed to convert saved-session to symlink");
                 g_free (default_name);
                 g_free (default_session);
         }
 
         path = g_build_filename (g_get_user_config_dir (), "gnome-session", NULL);
         error = NULL;
         dir = g_dir_open (path, 0, &error);
         if (dir == NULL) {
                 g_warning ("Failed to open %s: %s", path, error->message);
                 g_error_free (error);
                 goto out;
         }
 
         default_name = NULL;
         if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) {
                 default_name = g_path_get_basename (last_session);
         }
 
         while ((name = g_dir_read_name (dir)) != NULL) {
+                char *session_type;
+
                 if (strcmp (name, "saved-session") == 0)
                         continue;
 
+                session_type = get_session_type_from_file (name);
+
                 gtk_list_store_insert_with_values (store, &iter, 100, 0, name, -1);
+                g_free (session_type);
 
                 if (g_strcmp0 (default_name, name) == 0) {
                         GtkTreeSelection *selection;
                         GtkTreeIter child_iter;
 
                         gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &child_iter, &iter);
                         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list));
                         gtk_tree_selection_select_iter (selection, &child_iter);
                 }
         }
 
         g_free (default_name);
         g_dir_close (dir);
 
  out:
         g_free (saved_session);
         g_free (path);
 }
 
 static char *
 get_last_session (void)
 {
         char *saved_session;
         char last_session[PATH_MAX] = "";
         char *name = NULL;
 
         saved_session = get_session_path ("saved-session");
 
         if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) {
                 name = g_path_get_basename (last_session);
@@ -260,60 +283,136 @@ remove_session (const char *name)
         GError *error;
 
         path1 = get_session_path ("saved-session");
         path2 = get_session_path (name);
 
         error = NULL;
         n = g_file_read_link (path1, &error);
         if (n == NULL) {
                 g_warning ("Failed to read link: %s", error->message);
                 g_error_free (error);
         }
         else if (strcmp (n, name) == 0) {
                 unlink (path1);
         }
         g_free (n);
 
         dir = g_dir_open (path2, 0, NULL);
         while ((d = g_dir_read_name (dir)) != NULL) {
                 path = g_build_filename (path2, d, NULL);
                 unlink (path);
                 g_free (path);
         }
         g_dir_close (dir);
 
         remove (path2);
 
         g_free (path1);
         g_free (path2);
 }
 
+static const char *
+get_session_type_from_switch (void)
+{
+        GtkWidget *mode_switch;
+        gboolean is_classic_mode;
+
+        mode_switch = (GtkWidget *)gtk_builder_get_object (builder, "classic-mode-switch");
+
+        is_classic_mode = gtk_switch_get_active (GTK_SWITCH (mode_switch));
+
+        if (is_classic_mode) {
+                return "gnome-classic";
+        } else {
+                return "gnome";
+        }
+}
+
+static void
+set_mode_switch_from_session_type_file (const char *name)
+{
+        GtkWidget *mode_switch;
+        gboolean is_classic_mode = FALSE;
+        char *type;
+
+        mode_switch = (GtkWidget *)gtk_builder_get_object (builder, "classic-mode-switch");
+
+        type = get_session_type_from_file (name);
+        is_classic_mode = strcmp (type, "gnome-classic") == 0;
+        g_free (type);
+
+        gtk_switch_set_active (GTK_SWITCH (mode_switch), is_classic_mode);
+}
+
+static void
+save_session_type (const char *save_dir,
+                   const char *type)
+{
+        char *file;
+        GError *error;
+
+        file = g_build_filename (save_dir, "type", NULL);
+
+        error = NULL;
+        g_file_set_contents (file, type, strlen (type), &error);
+        if (error != NULL)
+                g_warning ("couldn't save session type to %s: %s",
+                           type, error->message);
+
+        g_free (file);
+}
+
+static void
+save_session_type_from_switch (void)
+{
+        char *name, *path;
+        const char *session_type;
+
+        name = get_selected_session ();
+
+        if (name == NULL) {
+                return;
+        }
+
+        path = get_session_path (name);
+        g_free (name);
+
+        session_type = get_session_type_from_switch ();
+        save_session_type (path, session_type);
+}
+
+static void
+on_mode_switched (GtkSwitch *mode_switch)
+{
+        save_session_type_from_switch ();
+}
+
 static gboolean
 make_session_current (const char *name)
 {
         char *path1;
         gboolean ret = TRUE;
 
         path1 = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session", NULL);
 
         unlink (path1);
         if (symlink (name, path1) < 0) {
                 g_warning ("Failed to make session '%s' current", name);
                 ret = FALSE;
         }
 
         g_free (path1);
 
         return ret;
 }
 
 static void
 on_remove_session_clicked (GtkButton *button,
                            gpointer   data)
 {
         GtkTreeSelection *selection;
         GtkTreeModel *model;
         GtkTreeIter iter;
         char *name;
 
         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list));
         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
@@ -492,60 +591,62 @@ static void
 create_session_and_begin_rename (void)
 {
         gchar *name;
 
         name = find_new_session_name ();
         create_session (name);
         select_session (name);
 
         begin_rename ();
 }
 
 static void
 on_new_session_clicked (GtkButton *button,
                         gpointer   data)
 {
 	create_session_and_begin_rename ();
 }
 
 static void
 on_selection_changed (GtkTreeSelection *selection,
                       gpointer          data)
 {
         char *name;
 
         name = get_selected_session ();
 
         if (name == NULL) {
                 return;
         }
 
+        set_mode_switch_from_session_type_file (name);
+
         g_free (name);
 }
 
 static void
 update_remove_button (void)
 {
         GtkWidget *button;
 
         button = (GtkWidget *)gtk_builder_get_object (builder, "remove-session");
         if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) > 1) {
                 gtk_widget_set_sensitive (button, TRUE);
         } else {
                 gtk_widget_set_sensitive (button, FALSE);
         }
 }
 
 static void
 on_row_edited (GtkCellRendererText *cell,
                const char          *path_string,
                const char          *new_name,
                gpointer             data)
 {
         GtkTreePath *path;
         GtkTreeIter  sort_iter, items_iter;
         char        *old_name;
         gboolean     was_renamed;
 
         path = gtk_tree_path_new_from_string (path_string);
         gtk_tree_model_get_iter (GTK_TREE_MODEL (sort_model), &sort_iter, path);
 
@@ -751,75 +852,80 @@ main (int argc, char *argv[])
         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model),
                                               0, GTK_SORT_ASCENDING);
         g_signal_connect (store, "row-deleted", G_CALLBACK (on_row_deleted), NULL);
         g_signal_connect (store, "row-inserted", G_CALLBACK (on_row_inserted), NULL);
         session_list = (GtkWidget *) gtk_builder_get_object (builder, "session-list");
 
         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list));
         gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
 
         populate_session_list (session_list);
 
         cell = gtk_cell_renderer_text_new ();
         g_signal_connect (cell, "edited", G_CALLBACK (on_row_edited), NULL);
 
         column = gtk_tree_view_column_new_with_attributes ("", cell, "text", 0, NULL);
         gtk_tree_view_append_column (GTK_TREE_VIEW (session_list), GTK_TREE_VIEW_COLUMN (column));
 
         g_signal_connect (session_list, "row-activated", G_CALLBACK (on_row_activated), NULL);
 
         g_signal_connect (selection, "changed",
                           G_CALLBACK (on_selection_changed), NULL);
 
         widget = (GtkWidget *) gtk_builder_get_object (builder, "new-session");
         g_signal_connect (widget, "clicked", G_CALLBACK (on_new_session_clicked), NULL);
         widget = (GtkWidget *) gtk_builder_get_object (builder, "remove-session");
         g_signal_connect (widget, "clicked", G_CALLBACK (on_remove_session_clicked), NULL);
         widget = (GtkWidget *) gtk_builder_get_object (builder, "rename-session");
         g_signal_connect (widget, "clicked", G_CALLBACK (on_rename_session_clicked), NULL);
         widget = (GtkWidget *) gtk_builder_get_object (builder, "continue-button");
         g_signal_connect (widget, "clicked", G_CALLBACK (on_continue_clicked), NULL);
+        widget = (GtkWidget *) gtk_builder_get_object (builder, "classic-mode-switch");
+        g_signal_connect (widget, "notify::active", G_CALLBACK (on_mode_switched), NULL);
 
         g_signal_connect (window, "map", G_CALLBACK (on_map), NULL);
         gtk_widget_show (window);
 
         if (g_strcmp0 (action, "load") == 0) {
             info_text = _("Please select a custom session to run");
         } else if (g_strcmp0 (action, "print") == 0) {
             info_text = _("Please select a session to use");
         } else if (g_strcmp0 (action, "save") == 0) {
             info_text = _("Please select a session to save to");
         }
 
         label = (GtkWidget*) gtk_builder_get_object (builder, "info-label");
         gtk_label_set_markup (GTK_LABEL (label), info_text);
 
         selected_session = get_selected_session ();
 
         if (selected_session == NULL) {
 		create_session_and_begin_rename ();
 	} else {
+                set_mode_switch_from_session_type_file (selected_session);
 		g_free (selected_session);
         }
 
         gtk_main ();
 
         selected_session = get_selected_session ();
 
         if (g_strcmp0 (action, "load") == 0) {
                 make_session_current (selected_session);
+                save_session_type_from_switch ();
                 auto_save_next_session_if_needed ();
         } else if (g_strcmp0 (action, "save") == 0) {
                 char *last_session;
 
                 last_session = get_last_session ();
                 make_session_current (selected_session);
                 save_session ();
+                save_session_type_from_switch ();
                 if (last_session != NULL)
                     make_session_current (last_session);
         } else if (g_strcmp0 (action, "print") == 0) {
                 g_print ("%s\n", selected_session);
         }
         g_free (selected_session);
 
         return 0;
 }
-- 
2.3.7


From c056d368938c75b1d6227e84f3daba40e8d39d71 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 8 Jan 2014 10:15:29 -0500
Subject: [PATCH 17/17] session-selector: use classic mode by default

---
 tools/gnome-session-selector.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c
index dbe3bda..3e0b32e 100644
--- a/tools/gnome-session-selector.c
+++ b/tools/gnome-session-selector.c
@@ -16,61 +16,61 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  *
  * Written by: Matthias Clasen <mclasen@redhat.com>
  */
 
 #include "config.h"
 
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include <glib.h>
 #include <gtk/gtk.h>
 #include <gio/gio.h>
 
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
 
 #define GSM_SERVICE_DBUS   "org.gnome.SessionManager"
 #define GSM_PATH_DBUS      "/org/gnome/SessionManager"
 #define GSM_INTERFACE_DBUS "org.gnome.SessionManager"
 
 #define GSM_MANAGER_SCHEMA        "org.gnome.SessionManager"
 #define KEY_AUTOSAVE_ONE_SHOT     "auto-save-session-one-shot"
-#define DEFAULT_SESSION_NAME      "gnome"
+#define DEFAULT_SESSION_NAME      "gnome-classic"
 
 static GtkBuilder *builder;
 static GtkWidget *session_list;
 static GtkListStore *store;
 static GtkTreeModelSort *sort_model;
 static char *info_text;
 
 static void select_session (const char *name);
 static gboolean make_session_current (const char *name);
 
 static char *
 get_session_path (const char *name)
 {
         return g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL);
 }
 
 static char *
 find_new_session_name (void)
 {
         char *name;
         char *path;
         int i;
 
         for (i = 1; i < 20; i++) {
                 name = g_strdup_printf (_("Session %d"), i);
                 path = get_session_path (name);
                 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
                         g_free (path);
                         return name;
                 }
-- 
2.3.7