From 1a767008113013cdefbb5b5de0bbfb77cd30b679 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 10:48:03 -0500 Subject: [PATCH 01/14] 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@ -- 1.8.3.1 From 68083665c3e7cb7d1915062820074f9b7fb42160 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 15:07:35 -0500 Subject: [PATCH 02/14] 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@ -- 1.8.3.1 From 80e68ffa8eaa7dbff7097521e92dbc9bb18e0b82 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 10:53:33 -0500 Subject: [PATCH 03/14] 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 @@ - + - - + True True 6 True + False 12 - vertical 3 True + False 0 3 3 Additional startup _programs: True session_properties_treeview False + True 0 True + False 6 True True never - automatic etched-in 210 True True + + + + True + True 0 True + False 6 start gtk-add True True True True False False 0 gtk-remove True False True True True False False 1 gtk-edit True False True True True False False 2 False False 1 + True + True 1 True + False Startup Programs False True + False 12 - vertical 6 _Automatically remember running applications when logging out True True False True + 0.5 True False False 0 True + False True True True + False 4 True + False gtk-save False False 0 True + False _Remember Currently Running Applications True + True + True 1 False False 0 False False 1 1 True + False Options 1 False True + False 6 3 2 12 6 True + False 12 True True - + + True + True 0 Browse… True True True False False 1 1 2 1 2 GTK_FILL True True - + 1 2 2 3 GTK_FILL True True - + 1 2 GTK_FILL True + False 0 Comm_ent: True label2 2 3 GTK_FILL GTK_FILL True + False 0 Co_mmand: True session_properties_command_entry 1 2 GTK_FILL GTK_FILL True + False 0 _Name: True session_properties_name_entry GTK_FILL GTK_FILL -- 1.8.3.1 From 94629d6620f5b46482f72e62cc2fb58c52e01268 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 16:14:16 -0500 Subject: [PATCH 04/14] 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 bcd4a7e..1620bd9 100644 --- a/gnome-session/gsm-manager.c +++ b/gnome-session/gsm-manager.c @@ -2099,61 +2099,60 @@ on_xsmp_client_register_request (GsmXSMPClient *client, out: g_free (*id); *id = new_id; return handled; } 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; -- 1.8.3.1 From bca5ebaa433125439d4f5dad94af13b2cb4bc13f Mon Sep 17 00:00:00 2001 From: Josselin Mouette Date: Mon, 21 Jun 2010 15:22:23 -0400 Subject: [PATCH 05/14] 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 24bf907..4cc453a 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 * * 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 #include #include #include "gsm-properties-dialog.h" #include "gsm-app-dialog.h" #include "gsm-util.h" #include "gsp-app.h" #include "gsp-app-manager.h" +#include +#include + +#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 ("%s", 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 a049b01..b71c979 100644 --- a/configure.ac +++ b/configure.ac @@ -46,60 +46,61 @@ AC_ARG_ENABLE(session-selector, AS_HELP_STRING([--enable-session-selector], 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) 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 gtk+-3.0 >= $GTK3_REQUIRED dbus-glib-1 >= $DBUS_GLIB_REQUIRED upower-glib >= $UPOWER_REQUIRED json-glib-1.0 >= $JSON_GLIB_REQUIRED gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED ) 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 @@ True False 12 6 _Automatically remember running applications when logging out True True False True 0.5 True False False 0 True False + True True True True False 4 True False gtk-save False False 0 True False _Remember Currently Running Applications True True True 1 False False 0 False False 1 + + + True + True + + + False + False + 2 + + 1 True False Options 1 False True False 6 3 2 12 6 True False 12 diff --git a/gnome-session/gsm-client.c b/gnome-session/gsm-client.c index efabde0..46b71c1 100644 --- a/gnome-session/gsm-client.c +++ b/gnome-session/gsm-client.c @@ -495,49 +495,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 aeb03c2..4a90300 100644 --- a/gnome-session/gsm-client.h +++ b/gnome-session/gsm-client.h @@ -65,105 +65,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 3f7621a..691156c 100644 --- a/gnome-session/gsm-dbus-client.c +++ b/gnome-session/gsm-dbus-client.c @@ -384,60 +384,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; } @@ -636,60 +649,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 1620bd9..e2a493f 100644 --- a/gnome-session/gsm-manager.c +++ b/gnome-session/gsm-manager.c @@ -60,60 +60,61 @@ #include "gsm-autostart-app.h" #include "gsm-util.h" #include "gdm.h" #include "gsm-logout-dialog.h" #include "gsm-icon-names.h" #include "gsm-inhibit-dialog.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_REBOOT_GDM, @@ -1338,60 +1339,123 @@ end_session_or_show_shell_dialog (GsmManager *manager) 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; } if (gsm_shell_is_running (manager->priv->shell)) { end_session_or_show_shell_dialog (manager); } else { end_session_or_show_fallback_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"); @@ -1418,60 +1482,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 @@ -2119,67 +2198,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); @@ -2240,80 +2338,93 @@ 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, "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); } @@ -3506,60 +3617,95 @@ gsm_manager_reboot (GsmManager *manager, 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, "Reboot interface is only available during the Running phase"); return FALSE; } if (_log_out_is_locked_down (manager)) { g_set_error (error, GSM_MANAGER_ERROR, GSM_MANAGER_ERROR_LOCKED_DOWN, "Logout has been locked down"); return FALSE; } shell_running = gsm_shell_is_running (manager->priv->shell); if (!shell_running) show_fallback_shutdown_dialog (manager, TRUE); else 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 f9632d7..9dbc814 100644 --- a/gnome-session/gsm-manager.h +++ b/gnome-session/gsm-manager.h @@ -134,60 +134,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, GError **error); gboolean gsm_manager_reboot (GsmManager *manager, GError **error); +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 f84fab3..a80e79b 100644 --- a/gnome-session/gsm-xsmp-client.c +++ b/gnome-session/gsm-xsmp-client.c @@ -40,60 +40,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, 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; @@ -473,60 +474,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); @@ -956,90 +981,102 @@ 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[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 b80f933..8f9ddd9 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); 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 eb69180..873e250 100644 --- a/gnome-session/org.gnome.SessionManager.xml +++ b/gnome-session/org.gnome.SessionManager.xml @@ -259,60 +259,68 @@ True if condition is handled, false otherwise Allows the caller to determine whether the session manager is handling changes to the specified autostart condition. Request a shutdown dialog. Request a reboot dialog. + + + + Request to save session + + + + True if shutdown is available to the user, false otherwise Allows the caller to determine whether or not it's okay to show a shutdown option in the UI The type of logout that is being requested Request a logout dialog Allowed values for the mode parameter are: 0 Normal. -- 1.8.3.1 From 50ed23822cd3318518f6427f19889b944f4d4cc8 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 11:22:07 -0500 Subject: [PATCH 06/14] 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 aeb575f..ceea177 100644 --- a/gnome-session/gsm-session-save.c +++ b/gnome-session/gsm-session-save.c @@ -11,61 +11,61 @@ * 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 #include #include #include #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); @@ -114,89 +114,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 = NULL; 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 b4036f6..402ac69 100644 --- a/gnome-session/gsm-util.c +++ b/gnome-session/gsm-util.c @@ -73,63 +73,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))) { -- 1.8.3.1 From ca6ffa85dfc46ba5085ba5e81c3824214abc3ac6 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 11:22:53 -0500 Subject: [PATCH 07/14] 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 ceea177..2fe0bb0 100644 --- a/gnome-session/gsm-session-save.c +++ b/gnome-session/gsm-session-save.c @@ -150,67 +150,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 = NULL; 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 402ac69..b4036f6 100644 --- a/gnome-session/gsm-util.c +++ b/gnome-session/gsm-util.c @@ -73,69 +73,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))) { -- 1.8.3.1 From ce646ac4da600b46dfc5a0605ec0c85f0a3a4f35 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 11:28:53 -0500 Subject: [PATCH 08/14] 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 4cc453a..cabe96a 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 ("%s", 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 b71c979..a61d88e 100644 --- a/configure.ac +++ b/configure.ac @@ -21,86 +21,85 @@ 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.7.90 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 gtk+-3.0 >= $GTK3_REQUIRED dbus-glib-1 >= $DBUS_GLIB_REQUIRED upower-glib >= $UPOWER_REQUIRED json-glib-1.0 >= $JSON_GLIB_REQUIRED gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED ) 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 @@ True 0.5 out True 12 True vertical 6 True other True vertical 0 True 0.0 0.5 - Please select a custom session to run + Please select a custom session to use True True 0 False True 0 True vertical 12 True 12 True True never diff --git a/tools/Makefile.am b/tools/Makefile.am index ba8a6e8..2685609 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -47,37 +47,38 @@ gnome_session_check_accelerated_helper_SOURCES = \ 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.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 dad88b4..54541d3 100644 --- a/tools/gnome-session-selector.c +++ b/tools/gnome-session-selector.c @@ -8,126 +8,133 @@ * (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. * * Written by: Matthias Clasen */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include +#include +#include + +#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\nNote: %s", info_text, _("Session names are not allowed to start with ‘.’ or contain ‘/’ characters")); } else if (user_tried_dot) { warning_text = g_strdup_printf ("%s\nNote: %s", info_text, _("Session names are not allowed to start with ‘.’")); } else if (user_tried_slash) { warning_text = g_strdup_printf ("%s\nNote: %s", 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\nNote: %s", 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"); @@ -183,517 +190,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; } -- 1.8.3.1 From da7acee836dc0c06079811f445d9054cf9f7ab73 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 11:32:52 -0500 Subject: [PATCH 09/14] 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 e2a493f..30b31f9 100644 --- a/gnome-session/gsm-manager.c +++ b/gnome-session/gsm-manager.c @@ -121,60 +121,61 @@ typedef enum GSM_MANAGER_LOGOUT_SHUTDOWN, GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT, GSM_MANAGER_LOGOUT_SHUTDOWN_GDM } 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; GtkWidget *inhibit_dialog; /* List of clients which were disconnected due to disabled condition * and shouldn't be automatically restarted */ GSList *condition_clients; 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, PROP_CLIENT_STORE, @@ -1369,90 +1370,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; @@ -3617,92 +3652,101 @@ gsm_manager_reboot (GsmManager *manager, 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, "Reboot interface is only available during the Running phase"); return FALSE; } if (_log_out_is_locked_down (manager)) { g_set_error (error, GSM_MANAGER_ERROR, GSM_MANAGER_ERROR_LOCKED_DOWN, "Logout has been locked down"); return FALSE; } shell_running = gsm_shell_is_running (manager->priv->shell); if (!shell_running) show_fallback_shutdown_dialog (manager, TRUE); else 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 9dbc814..ca29b65 100644 --- a/gnome-session/gsm-manager.h +++ b/gnome-session/gsm-manager.h @@ -135,61 +135,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, GError **error); gboolean gsm_manager_reboot (GsmManager *manager, GError **error); 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 873e250..d9fb216 100644 --- a/gnome-session/org.gnome.SessionManager.xml +++ b/gnome-session/org.gnome.SessionManager.xml @@ -260,60 +260,61 @@ True if condition is handled, false otherwise Allows the caller to determine whether the session manager is handling changes to the specified autostart condition. Request a shutdown dialog. Request a reboot dialog. + Request to save session True if shutdown is available to the user, false otherwise Allows the caller to determine whether or not it's okay to show a shutdown option in the UI The type of logout that is being requested Request a logout dialog -- 1.8.3.1 From e41b19c1d28b672f0c9d166d52b4e7028b92c451 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 15:32:04 -0500 Subject: [PATCH 10/14] 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 30b31f9..f6ccb62 100644 --- a/gnome-session/gsm-manager.c +++ b/gnome-session/gsm-manager.c @@ -1426,61 +1426,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; @@ -1687,60 +1687,77 @@ debug_app_summary (GsmManager *manager) g_debug ("GsmManager: App startup summary"); for (phase = GSM_MANAGER_PHASE_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_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; @@ -2217,61 +2234,61 @@ on_xsmp_client_register_request (GsmXSMPClient *client, return handled; } 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 ca29b65..6f94f92 100644 --- a/gnome-session/gsm-manager.h +++ b/gnome-session/gsm-manager.h @@ -100,60 +100,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 2fe0bb0..07956b7 100644 --- a/gnome-session/gsm-session-save.c +++ b/gnome-session/gsm-session-save.c @@ -1,75 +1,107 @@ /* -*- 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include +#include + #include #include #include #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; } @@ -109,112 +141,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); @@ -294,31 +328,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 10b5005..ed7fea2 100644 --- a/gnome-session/gsm-session-save.h +++ b/gnome-session/gsm-session-save.h @@ -1,35 +1,36 @@ /* 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef __GSM_SESSION_SAVE_H__ #define __GSM_SESSION_SAVE_H__ #include #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 dc6db5f..b72f2bc 100644 --- a/gnome-session/main.c +++ b/gnome-session/main.c @@ -252,60 +252,61 @@ require_dbus_session (int argc, } /* Should not be reached */ return TRUE; } static gboolean check_gl (GError **error) { int status; char *argv[] = { LIBEXECDIR "/gnome-session-check-accelerated", NULL }; 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; char *display_str; 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; 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 : ""); g_clear_error (&error); @@ -382,53 +383,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); gtk_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); gtk_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; } -- 1.8.3.1 From 3dc13d7502b8dc4a9fa0f168ade9aa776cf2d5e9 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Tue, 7 Jan 2014 21:16:23 -0500 Subject: [PATCH 11/14] 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" -- 1.8.3.1 From 25088eb65e4bf96a2b17028ae97eb48cf3c5a4dd Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 10:53:33 -0500 Subject: [PATCH 12/14] 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 @@ - + - - - - - - - - - - session-store - + + False Custom Session center 500 310 False True + False 0.5 out True + False 12 True - vertical + False 6 - True - other - + False + other - + True + False vertical - 0 True - 0.0 - 0.5 + False + 0 Please select a custom session to use - True - True + False + False 0 + + False + False + 0 + + + + + False + + + False + True + 1 + False True 0 True - vertical + False 12 True + False 12 True True never - automatic in True True + sort-model False 0 - sort-model + + + + True + True 0 True - vertical + False 6 start _New Session True True True True False False 0 _Remove Session True True True True False False 1 Rena_me Session True True True True False False 2 False + True 1 + True + True 1 + True + True 1 True + False 6 end _Continue True True True True True True False False 0 False + True 2 + + + + + + + + session-store + -- 1.8.3.1 From d6274d2a56ca17001d8530ec6fd00b918d196356 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Tue, 7 Jan 2014 21:02:02 -0500 Subject: [PATCH 13/14] 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 @@ False 2 False True 1 True True 1 True True 1 True False 6 - end + + + True + False + 6 + + + True + False + Classic Experience + + + False + True + 0 + + + + + True + True + + + False + True + 1 + + + + + False + True + 0 + + _Continue True True True True True True False False - 0 + 1 + True False True 2 session-store diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c index 54541d3..1011a52 100644 --- a/tools/gnome-session-selector.c +++ b/tools/gnome-session-selector.c @@ -17,60 +17,61 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Written by: Matthias Clasen */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #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; } @@ -126,104 +127,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\nNote: %s", 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); @@ -261,60 +284,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)) { @@ -493,60 +592,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); @@ -752,75 +853,79 @@ 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); 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; } -- 1.8.3.1 From acc1f8715b9e3a9153eeb0c2281cb156a8925c3c Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Wed, 8 Jan 2014 10:15:29 -0500 Subject: [PATCH 14/14] 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 1011a52..e18023c 100644 --- a/tools/gnome-session-selector.c +++ b/tools/gnome-session-selector.c @@ -17,61 +17,61 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Written by: Matthias Clasen */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #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; } -- 1.8.3.1