Blob Blame History Raw
From 5e8c5967d65f61a58241c6429eb79650870fa7d0 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 27 Nov 2017 15:40:54 -0500
Subject: [PATCH 1/2] save: make sure app state is written into desktop file

There are a number of important bits of app state written into
an applications desktop file that needs to be restored when
the session is saved.  For instance, the phase in which the client
should get started.

That state is currently stored on the GsmApp object, which is
inaccessible, from the client save function.

This commit adds the neccesary plumbing to route the GsmApp object
associated with a client to the client save function, and also
adds code to allow the app object to augment the client keyfile at
save time.

https://bugzilla.gnome.org/show_bug.cgi?id=790913

https://bugzilla.redhat.com/show_bug.cgi?id=1529175
---
 gnome-session/gsm-app.c           |  9 +++++
 gnome-session/gsm-app.h           |  8 +++++
 gnome-session/gsm-autostart-app.c | 70 +++++++++++++++++++++++++++++++++++++++
 gnome-session/gsm-client.c        |  3 +-
 gnome-session/gsm-client.h        |  3 ++
 gnome-session/gsm-dbus-client.c   |  1 +
 gnome-session/gsm-manager.c       |  4 +--
 gnome-session/gsm-session-save.c  | 41 +++++++++++++++++------
 gnome-session/gsm-session-save.h  |  1 +
 gnome-session/gsm-xsmp-client.c   |  9 +++++
 10 files changed, 135 insertions(+), 14 deletions(-)

diff --git a/gnome-session/gsm-app.c b/gnome-session/gsm-app.c
index 845e067a..d1ef89a8 100644
--- a/gnome-session/gsm-app.c
+++ b/gnome-session/gsm-app.c
@@ -531,30 +531,39 @@ gsm_app_exited (GsmApp *app,
 }
 
 void
 gsm_app_died (GsmApp *app,
               int     signal)
 {
         g_return_if_fail (GSM_IS_APP (app));
 
         g_signal_emit (app, signals[DIED], 0, signal);
 }
 
 gboolean
 gsm_app_get_registered (GsmApp *app)
 {
         g_return_val_if_fail (GSM_IS_APP (app), FALSE);
 
         return app->priv->registered;
 }
 
 void
 gsm_app_set_registered (GsmApp   *app,
                         gboolean  registered)
 {
         g_return_if_fail (GSM_IS_APP (app));
 
         if (app->priv->registered != registered) {
                 app->priv->registered = registered;
                 g_object_notify (G_OBJECT (app), "registered");
         }
 }
+
+gboolean
+gsm_app_save_to_keyfile (GsmApp    *app,
+                         GKeyFile  *keyfile,
+                         GError   **error)
+{
+        g_debug ("Saving app: %s", app->priv->id);
+        return GSM_APP_GET_CLASS (app)->impl_save_to_keyfile (app, keyfile, error);
+}
diff --git a/gnome-session/gsm-app.h b/gnome-session/gsm-app.h
index 14a9f94b..f38b3be4 100644
--- a/gnome-session/gsm-app.h
+++ b/gnome-session/gsm-app.h
@@ -47,80 +47,88 @@ struct _GsmApp
 };
 
 struct _GsmAppClass
 {
         GObjectClass parent_class;
 
         /* signals */
         void        (*exited)       (GsmApp *app,
                                      guchar  exit_code);
         void        (*died)         (GsmApp *app,
                                      int     signal);
 
         /* virtual methods */
         gboolean    (*impl_start)                     (GsmApp     *app,
                                                        GError    **error);
         gboolean    (*impl_restart)                   (GsmApp     *app,
                                                        GError    **error);
         gboolean    (*impl_stop)                      (GsmApp     *app,
                                                        GError    **error);
         gboolean    (*impl_provides)                  (GsmApp     *app,
                                                        const char *service);
         char **     (*impl_get_provides)              (GsmApp     *app);
         gboolean    (*impl_has_autostart_condition)   (GsmApp     *app,
                                                        const char *service);
         gboolean    (*impl_is_running)                (GsmApp     *app);
 
         gboolean    (*impl_get_autorestart)           (GsmApp     *app);
         const char *(*impl_get_app_id)                (GsmApp     *app);
         gboolean    (*impl_is_disabled)               (GsmApp     *app);
         gboolean    (*impl_is_conditionally_disabled) (GsmApp     *app);
+
+        gboolean    (*impl_save_to_keyfile)           (GsmApp     *app,
+                                                       GKeyFile   *keyfile,
+                                                       GError    **error);
 };
 
 typedef enum
 {
         GSM_APP_ERROR_GENERAL = 0,
         GSM_APP_ERROR_RESTART_LIMIT,
         GSM_APP_ERROR_START,
         GSM_APP_ERROR_STOP,
         GSM_APP_NUM_ERRORS
 } GsmAppError;
 
 #define GSM_APP_ERROR gsm_app_error_quark ()
 
 GQuark           gsm_app_error_quark                    (void);
 GType            gsm_app_get_type                       (void) G_GNUC_CONST;
 
 gboolean         gsm_app_peek_autorestart               (GsmApp     *app);
 
 const char      *gsm_app_peek_id                        (GsmApp     *app);
 const char      *gsm_app_peek_app_id                    (GsmApp     *app);
 const char      *gsm_app_peek_startup_id                (GsmApp     *app);
 GsmManagerPhase  gsm_app_peek_phase                     (GsmApp     *app);
 gboolean         gsm_app_peek_is_disabled               (GsmApp     *app);
 gboolean         gsm_app_peek_is_conditionally_disabled (GsmApp     *app);
 
 gboolean         gsm_app_start                          (GsmApp     *app,
                                                          GError    **error);
 gboolean         gsm_app_restart                        (GsmApp     *app,
                                                          GError    **error);
 gboolean         gsm_app_stop                           (GsmApp     *app,
                                                          GError    **error);
 gboolean         gsm_app_is_running                     (GsmApp     *app);
 
 void             gsm_app_exited                         (GsmApp     *app,
                                                          guchar      exit_code);
 void             gsm_app_died                           (GsmApp     *app,
                                                          int         signal);
 
 gboolean         gsm_app_provides                       (GsmApp     *app,
                                                          const char *service);
 char           **gsm_app_get_provides                   (GsmApp     *app);
 gboolean         gsm_app_has_autostart_condition        (GsmApp     *app,
                                                          const char *condition);
 gboolean         gsm_app_get_registered                 (GsmApp     *app);
 void             gsm_app_set_registered                 (GsmApp     *app,
                                                          gboolean  registered);
 
+gboolean         gsm_app_save_to_keyfile                (GsmApp    *app,
+                                                         GKeyFile  *keyfile,
+                                                         GError   **error);
+
 G_END_DECLS
 
 #endif /* __GSM_APP_H__ */
diff --git a/gnome-session/gsm-autostart-app.c b/gnome-session/gsm-autostart-app.c
index 870b1516..9eb1db5b 100644
--- a/gnome-session/gsm-autostart-app.c
+++ b/gnome-session/gsm-autostart-app.c
@@ -1400,86 +1400,156 @@ gsm_autostart_app_get_autorestart (GsmApp *app)
 static const char *
 gsm_autostart_app_get_app_id (GsmApp *app)
 {
         if (GSM_AUTOSTART_APP (app)->priv->app_info == NULL) {
                 return NULL;
         }
 
         return g_app_info_get_id (G_APP_INFO (GSM_AUTOSTART_APP (app)->priv->app_info));
 }
 
 static gboolean
 gsm_autostart_app_initable_init (GInitable *initable,
                                  GCancellable *cancellable,
                                  GError  **error)
 {
         GsmAutostartApp *app = GSM_AUTOSTART_APP (initable);
 
         g_assert (app->priv->desktop_filename != NULL);
         app->priv->app_info = g_desktop_app_info_new_from_filename (app->priv->desktop_filename);
         if (app->priv->app_info == NULL) {
                 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                              "Could not parse desktop file %s or it references a not found TryExec binary", app->priv->desktop_id);
                 return FALSE;
         }
 
         load_desktop_file (app);
 
         return TRUE;
 }
 
+static gboolean
+gsm_autostart_app_save_to_keyfile (GsmApp    *base_app,
+                                   GKeyFile  *keyfile,
+                                   GError   **error)
+{
+        GsmAutostartApp *app = GSM_AUTOSTART_APP (base_app);
+        char   **provides = NULL;
+        char    *dbus_name;
+        char    *phase;
+        gboolean res;
+
+        provides = gsm_app_get_provides (base_app);
+        if (provides != NULL) {
+                g_key_file_set_string_list (keyfile,
+                                            G_KEY_FILE_DESKTOP_GROUP,
+                                            GSM_AUTOSTART_APP_PROVIDES_KEY,
+                                            (const char * const *)
+                                            provides,
+                                            g_strv_length (provides));
+                g_strfreev (provides);
+        }
+
+        phase = g_desktop_app_info_get_string (app->priv->app_info,
+                                                   GSM_AUTOSTART_APP_PHASE_KEY);
+        if (phase != NULL) {
+                g_key_file_set_string (keyfile,
+                                       G_KEY_FILE_DESKTOP_GROUP,
+                                       GSM_AUTOSTART_APP_PHASE_KEY,
+                                       phase);
+                g_free (phase);
+        }
+
+        dbus_name = g_desktop_app_info_get_string (app->priv->app_info,
+                                                   GSM_AUTOSTART_APP_DBUS_NAME_KEY);
+        if (dbus_name != NULL) {
+                g_key_file_set_string (keyfile,
+                                       G_KEY_FILE_DESKTOP_GROUP,
+                                       GSM_AUTOSTART_APP_DBUS_NAME_KEY,
+                                       dbus_name);
+                g_free (dbus_name);
+        }
+
+        res = g_desktop_app_info_has_key (app->priv->app_info,
+                                          GSM_AUTOSTART_APP_AUTORESTART_KEY);
+        if (res) {
+                g_key_file_set_boolean (keyfile,
+                                        G_KEY_FILE_DESKTOP_GROUP,
+                                        GSM_AUTOSTART_APP_AUTORESTART_KEY,
+                                        g_desktop_app_info_get_boolean (app->priv->app_info,
+                                                                        GSM_AUTOSTART_APP_AUTORESTART_KEY));
+        }
+
+        res = g_desktop_app_info_has_key (app->priv->app_info,
+                                          GSM_AUTOSTART_APP_AUTORESTART_KEY);
+        if (res) {
+                char *autostart_condition;
+
+                autostart_condition = g_desktop_app_info_get_string (app->priv->app_info, "AutostartCondition");
+
+                g_key_file_set_string (keyfile,
+                                       G_KEY_FILE_DESKTOP_GROUP,
+                                       "AutostartCondition",
+                                       autostart_condition);
+                g_free (autostart_condition);
+        }
+
+        return TRUE;
+}
+
 static void
 gsm_autostart_app_initable_iface_init (GInitableIface  *iface)
 {
         iface->init = gsm_autostart_app_initable_init;
 }
 
 static void
 gsm_autostart_app_class_init (GsmAutostartAppClass *klass)
 {
         GObjectClass *object_class = G_OBJECT_CLASS (klass);
         GsmAppClass  *app_class = GSM_APP_CLASS (klass);
 
         object_class->set_property = gsm_autostart_app_set_property;
         object_class->get_property = gsm_autostart_app_get_property;
         object_class->dispose = gsm_autostart_app_dispose;
 
         app_class->impl_is_disabled = is_disabled;
         app_class->impl_is_conditionally_disabled = is_conditionally_disabled;
         app_class->impl_is_running = is_running;
         app_class->impl_start = gsm_autostart_app_start;
         app_class->impl_restart = gsm_autostart_app_restart;
         app_class->impl_stop = gsm_autostart_app_stop;
         app_class->impl_provides = gsm_autostart_app_provides;
         app_class->impl_get_provides = gsm_autostart_app_get_provides;
         app_class->impl_has_autostart_condition = gsm_autostart_app_has_autostart_condition;
         app_class->impl_get_app_id = gsm_autostart_app_get_app_id;
         app_class->impl_get_autorestart = gsm_autostart_app_get_autorestart;
+        app_class->impl_save_to_keyfile = gsm_autostart_app_save_to_keyfile;
 
         g_object_class_install_property (object_class,
                                          PROP_DESKTOP_FILENAME,
                                          g_param_spec_string ("desktop-filename",
                                                               "Desktop filename",
                                                               "Freedesktop .desktop file",
                                                               NULL,
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
         signals[CONDITION_CHANGED] =
                 g_signal_new ("condition-changed",
                               G_OBJECT_CLASS_TYPE (object_class),
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GsmAutostartAppClass, condition_changed),
                               NULL, NULL, NULL,
                               G_TYPE_NONE,
                               1,
                               G_TYPE_BOOLEAN);
 
         g_type_class_add_private (object_class, sizeof (GsmAutostartAppPrivate));
 }
 
 GsmApp *
 gsm_autostart_app_new (const char *desktop_file,
                        GError    **error)
 {
         return (GsmApp*) g_initable_new (GSM_TYPE_AUTOSTART_APP, NULL, error,
                                          "desktop-filename", desktop_file,
                                          NULL);
 }
diff --git a/gnome-session/gsm-client.c b/gnome-session/gsm-client.c
index 7b78d9e1..3f216b22 100644
--- a/gnome-session/gsm-client.c
+++ b/gnome-session/gsm-client.c
@@ -526,47 +526,48 @@ gsm_client_end_session (GsmClient                *client,
         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,
+                 GsmApp    *app,
                  GError   **error)
 {
         g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
 
-        return GSM_CLIENT_GET_CLASS (client)->impl_save (client, error);
+        return GSM_CLIENT_GET_CLASS (client)->impl_save (client, app, 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 f79896b3..19c9cd8d 100644
--- a/gnome-session/gsm-client.h
+++ b/gnome-session/gsm-client.h
@@ -6,60 +6,61 @@
  * 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_CLIENT_H__
 #define __GSM_CLIENT_H__
 
 #include <glib.h>
 #include <glib-object.h>
 #include <sys/types.h>
 
 G_BEGIN_DECLS
 
 #define GSM_TYPE_CLIENT            (gsm_client_get_type ())
 #define GSM_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_CLIENT, GsmClient))
 #define GSM_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_CLIENT, GsmClientClass))
 #define GSM_IS_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_CLIENT))
 #define GSM_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_CLIENT))
 #define GSM_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_CLIENT, GsmClientClass))
 
+typedef struct _GsmApp           GsmApp;
 typedef struct _GsmClient        GsmClient;
 typedef struct _GsmClientClass   GsmClientClass;
 
 typedef struct GsmClientPrivate GsmClientPrivate;
 
 typedef enum {
         GSM_CLIENT_UNREGISTERED = 0,
         GSM_CLIENT_REGISTERED,
         GSM_CLIENT_FINISHED,
         GSM_CLIENT_FAILED
 } GsmClientStatus;
 
 typedef enum {
         GSM_CLIENT_RESTART_NEVER = 0,
         GSM_CLIENT_RESTART_IF_RUNNING,
         GSM_CLIENT_RESTART_ANYWAY,
         GSM_CLIENT_RESTART_IMMEDIATELY
 } GsmClientRestartStyle;
 
 typedef enum {
         GSM_CLIENT_END_SESSION_FLAG_FORCEFUL = 1 << 0,
         GSM_CLIENT_END_SESSION_FLAG_SAVE     = 1 << 1,
         GSM_CLIENT_END_SESSION_FLAG_LAST     = 1 << 2
 } GsmClientEndSessionFlag;
 
 struct _GsmClient
 {
         GObject           parent;
         GsmClientPrivate *priv;
 };
@@ -67,91 +68,93 @@ struct _GsmClient
 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,
+                                                              GsmApp    *app,
                                                               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 ()
 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,
+                                                             GsmApp     *app,
                                                              GError    **error);
 
 gboolean              gsm_client_stop                       (GsmClient  *client,
                                                              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
 
 #endif /* __GSM_CLIENT_H__ */
diff --git a/gnome-session/gsm-dbus-client.c b/gnome-session/gsm-dbus-client.c
index 050ea18f..5793f830 100644
--- a/gnome-session/gsm-dbus-client.c
+++ b/gnome-session/gsm-dbus-client.c
@@ -315,60 +315,61 @@ gsm_dbus_client_finalize (GObject *object)
 
         if (client->priv->skeleton != NULL) {
                 g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (client->priv->skeleton),
                                                                     client->priv->connection);
                 g_clear_object (&client->priv->skeleton);
         }
 
         g_clear_object (&client->priv->connection);
 
         if (client->priv->watch_id != 0)
                 g_bus_unwatch_name (client->priv->watch_id);
 
         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,
+                  GsmApp    *app,
                   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;
         gsm_exported_client_private_emit_stop (dbus_client->priv->skeleton);
         return TRUE;
 }
 
 static char *
 dbus_client_get_app_name (GsmClient *client)
 {
         /* Always use app-id instead */
         return NULL;
 }
 
 static GsmClientRestartStyle
 dbus_client_get_restart_style_hint (GsmClient *client)
 {
diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c
index 29c3054d..e7f0d7f8 100644
--- a/gnome-session/gsm-manager.c
+++ b/gnome-session/gsm-manager.c
@@ -1260,61 +1260,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, manager->priv->session_name, &error);
+        gsm_session_save (manager->priv->clients, manager->priv->apps, 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;
@@ -1963,61 +1963,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, manager->priv->session_name, &error);
+        gsm_session_save (manager->priv->clients, manager->priv->apps, 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-session-save.c b/gnome-session/gsm-session-save.c
index 78b64197..35ffaae0 100644
--- a/gnome-session/gsm-session-save.c
+++ b/gnome-session/gsm-session-save.c
@@ -1,234 +1,253 @@
 /* -*- 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-app.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;
+        GsmStore    *app_store;
         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
+_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 g_strcmp0 (app_id_a, app_id_b) == 0;
+}
+
 static gboolean
 save_one_client (char            *id,
                  GObject         *object,
                  SessionSaveData *data)
 {
         GsmClient  *client;
         GKeyFile   *keyfile;
+        GsmApp     *app = NULL;
         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);
+        app_id = gsm_client_peek_app_id (client);
+        if (!IS_STRING_EMPTY (app_id)) {
+                if (g_str_has_suffix (app_id, ".desktop"))
+                        filename = g_strdup (app_id);
+                else
+                        filename = g_strdup_printf ("%s.desktop", app_id);
+
+                path = g_build_filename (data->dir, filename, NULL);
+
+                app = (GsmApp *)gsm_store_find (data->app_store,
+                                                (GsmStoreFunc)_app_has_app_id,
+                                                (char *)app_id);
+        }
+        keyfile = gsm_client_save (client, app, &local_error);
 
         if (keyfile == NULL || local_error) {
                 goto out;
         }
 
         contents = g_key_file_to_data (keyfile, &length, &local_error);
 
         if (local_error) {
                 goto out;
         }
 
-        app_id = gsm_client_peek_app_id (client);
-        if (!IS_STRING_EMPTY (app_id)) {
-                if (g_str_has_suffix (app_id, ".desktop"))
-                        filename = g_strdup (app_id);
-                else
-                        filename = g_strdup_printf ("%s.desktop", app_id);
-
-                path = g_build_filename (data->dir, filename, NULL);
-        }
-
         if (!path || g_file_test (path, G_FILE_TEST_EXISTS)) {
                 if (filename)
                         g_free (filename);
                 if (path)
                         g_free (path);
 
                 filename = g_strdup_printf ("%s.desktop",
                                             gsm_client_peek_startup_id (client));
                 path = g_build_filename (data->dir, filename, NULL);
         }
 
         g_file_set_contents (path,
                              contents,
                              length,
                              &local_error);
 
         if (local_error) {
                 goto out;
         }
 
         discard_exec = g_key_file_get_string (keyfile,
                                               G_KEY_FILE_DESKTOP_GROUP,
                                               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,
+                  GsmStore    *app_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;
+        data.app_store = app_store;
 
         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 */
diff --git a/gnome-session/gsm-session-save.h b/gnome-session/gsm-session-save.h
index c91b5615..b32673c4 100644
--- a/gnome-session/gsm-session-save.h
+++ b/gnome-session/gsm-session-save.h
@@ -1,34 +1,35 @@
 /* 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,
+                                            GsmStore    *app_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/gsm-xsmp-client.c b/gnome-session/gsm-xsmp-client.c
index 2846d9b3..cbecd68c 100644
--- a/gnome-session/gsm-xsmp-client.c
+++ b/gnome-session/gsm-xsmp-client.c
@@ -612,118 +612,127 @@ set_desktop_file_keys_from_client (GsmClient *client,
 
         g_free (comment);
 }
 
 static GKeyFile *
 create_client_key_file (GsmClient   *client,
                         const char  *desktop_file_path,
                         GError     **error) {
         GKeyFile *keyfile;
 
         keyfile = g_key_file_new ();
 
         if (desktop_file_path != NULL) {
                 g_key_file_load_from_file (keyfile,
                                            desktop_file_path,
                                            G_KEY_FILE_KEEP_COMMENTS |
                                            G_KEY_FILE_KEEP_TRANSLATIONS,
                                            error);
         } else {
                 set_desktop_file_keys_from_client (client, keyfile);
         }
 
         return keyfile;
 }
 
 static GsmClientRestartStyle
 xsmp_get_restart_style_hint (GsmClient *client);
 
 static GKeyFile *
 xsmp_save (GsmClient *client,
+           GsmApp    *app,
            GError   **error)
 {
         GsmClientRestartStyle restart_style;
 
         GKeyFile *keyfile = NULL;
         char     *desktop_file_path = NULL;
         char     *exec_program = NULL;
         char     *exec_discard = NULL;
         char     *startup_id = NULL;
         GError   *local_error;
 
         g_debug ("GsmXSMPClient: saving client with id %s",
                  gsm_client_peek_id (client));
 
         local_error = NULL;
 
         restart_style = xsmp_get_restart_style_hint (client);
         if (restart_style == GSM_CLIENT_RESTART_NEVER) {
                 goto out;
         }
 
         exec_program = xsmp_get_restart_command (client);
         if (!exec_program) {
                 goto out;
         }
 
         desktop_file_path = get_desktop_file_path (GSM_XSMP_CLIENT (client));
 
         /* this can accept desktop_file_path == NULL */
         keyfile = create_client_key_file (client,
                                           desktop_file_path,
                                           &local_error);
 
         if (local_error) {
                 goto out;
         }
 
         g_object_get (client,
                       "startup-id", &startup_id,
                       NULL);
 
         g_key_file_set_string (keyfile,
                                G_KEY_FILE_DESKTOP_GROUP,
                                GSM_AUTOSTART_APP_STARTUP_ID_KEY,
                                startup_id);
 
         g_key_file_set_string (keyfile,
                                G_KEY_FILE_DESKTOP_GROUP,
                                G_KEY_FILE_DESKTOP_KEY_EXEC,
                                exec_program);
 
         exec_discard = xsmp_get_discard_command (client);
         if (exec_discard)
                 g_key_file_set_string (keyfile,
                                        G_KEY_FILE_DESKTOP_GROUP,
                                        GSM_AUTOSTART_APP_DISCARD_KEY,
                                        exec_discard);
 
+        if (app != NULL) {
+                gsm_app_save_to_keyfile (app, keyfile, &local_error);
+
+                if (local_error) {
+                        goto out;
+                }
+        }
+
 out:
         g_free (desktop_file_path);
         g_free (exec_program);
         g_free (exec_discard);
         g_free (startup_id);
 
         if (local_error != NULL) {
                 g_propagate_error (error, local_error);
                 g_key_file_free (keyfile);
 
                 return NULL;
         }
 
         return keyfile;
 }
 
 static gboolean
 xsmp_stop (GsmClient *client,
            GError   **error)
 {
         GsmXSMPClient *xsmp = (GsmXSMPClient *) client;
 
         g_debug ("GsmXSMPClient: xsmp_stop ('%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;
-- 
2.14.3