You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1771 lines
66 KiB
1771 lines
66 KiB
From ade647861a671eca584d25432722ac2661854748 Mon Sep 17 00:00:00 2001 |
|
From: Josselin Mouette <joss@malsain.org> |
|
Date: Mon, 21 Jun 2010 15:22:23 -0400 |
|
Subject: [PATCH 08/19] Add "Remember Currently Running Applications" button |
|
|
|
This adds back session saving that's not at logout. |
|
--- |
|
capplet/gsm-properties-dialog.c | 63 ++++++++- |
|
capplet/meson.build | 3 +- |
|
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 ++ |
|
meson.build | 1 + |
|
12 files changed, 305 insertions(+), 5 deletions(-) |
|
|
|
diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c |
|
index 33812b8b..d2be778b 100644 |
|
--- a/capplet/gsm-properties-dialog.c |
|
+++ b/capplet/gsm-properties-dialog.c |
|
@@ -5,70 +5,77 @@ |
|
* Copyright (C) 2008 Lucas Rocha. |
|
* Copyright (C) 2008 William Jon McCann <jmccann@redhat.com> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; either version 2 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
* |
|
*/ |
|
|
|
#include "config.h" |
|
|
|
#include <glib.h> |
|
#include <glib/gi18n.h> |
|
#include <gtk/gtk.h> |
|
|
|
#include "gsm-properties-dialog.h" |
|
#include "gsm-app-dialog.h" |
|
#include "gsm-util.h" |
|
#include "gsp-app.h" |
|
#include "gsp-app-manager.h" |
|
+#include <dbus/dbus-glib.h> |
|
+#include <dbus/dbus-glib-lowlevel.h> |
|
+ |
|
+#define GSM_SERVICE_DBUS "org.gnome.SessionManager" |
|
+#define GSM_PATH_DBUS "/org/gnome/SessionManager" |
|
+#define GSM_INTERFACE_DBUS "org.gnome.SessionManager" |
|
|
|
#define GSM_PROPERTIES_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogPrivate)) |
|
|
|
#define GTKBUILDER_FILE "session-properties.ui" |
|
|
|
#define CAPPLET_TREEVIEW_WIDGET_NAME "session_properties_treeview" |
|
#define CAPPLET_ADD_WIDGET_NAME "session_properties_add_button" |
|
#define CAPPLET_DELETE_WIDGET_NAME "session_properties_delete_button" |
|
#define CAPPLET_EDIT_WIDGET_NAME "session_properties_edit_button" |
|
#define CAPPLET_SAVE_WIDGET_NAME "session_properties_save_button" |
|
+#define CAPPLET_SESSION_SAVED_WIDGET_NAME "session_properties_session_saved_label" |
|
#define CAPPLET_REMEMBER_WIDGET_NAME "session_properties_remember_toggle" |
|
|
|
#define STARTUP_APP_ICON "system-run" |
|
|
|
#define SPC_SETTINGS_SCHEMA "org.gnome.SessionManager" |
|
#define SPC_SETTINGS_AUTOSAVE_KEY "auto-save-session" |
|
|
|
struct GsmPropertiesDialogPrivate |
|
{ |
|
GtkBuilder *xml; |
|
GtkListStore *list_store; |
|
GtkTreeModel *tree_filter; |
|
|
|
GtkTreeView *treeview; |
|
GtkWidget *add_button; |
|
GtkWidget *delete_button; |
|
GtkWidget *edit_button; |
|
|
|
GSettings *settings; |
|
|
|
GspAppManager *manager; |
|
}; |
|
|
|
enum { |
|
STORE_COL_VISIBLE = 0, |
|
STORE_COL_ENABLED, |
|
STORE_COL_GICON, |
|
STORE_COL_DESCRIPTION, |
|
STORE_COL_APP, |
|
STORE_COL_SEARCH, |
|
@@ -430,65 +437,119 @@ on_edit_app_clicked (GtkWidget *widget, |
|
char *exec; |
|
char *comment; |
|
|
|
edit_dialog = gsm_app_dialog_new (gsp_app_get_name (app), |
|
gsp_app_get_exec (app), |
|
gsp_app_get_comment (app)); |
|
gtk_window_set_transient_for (GTK_WINDOW (edit_dialog), |
|
GTK_WINDOW (dialog)); |
|
|
|
if (gsm_app_dialog_run (GSM_APP_DIALOG (edit_dialog), |
|
&name, &exec, &comment)) { |
|
gsp_app_update (app, name, comment, exec); |
|
g_free (name); |
|
g_free (exec); |
|
g_free (comment); |
|
} |
|
|
|
g_object_unref (app); |
|
} |
|
} |
|
|
|
static void |
|
on_row_activated (GtkTreeView *tree_view, |
|
GtkTreePath *path, |
|
GtkTreeViewColumn *column, |
|
GsmPropertiesDialog *dialog) |
|
{ |
|
on_edit_app_clicked (NULL, dialog); |
|
} |
|
|
|
+static void |
|
+session_saved_message (GsmPropertiesDialog *dialog, |
|
+ const char *msg, |
|
+ gboolean is_error) |
|
+{ |
|
+ GtkLabel *label; |
|
+ gchar *markup; |
|
+ label = GTK_LABEL (gtk_builder_get_object (dialog->priv->xml, CAPPLET_SESSION_SAVED_WIDGET_NAME)); |
|
+ if (is_error) |
|
+ markup = g_markup_printf_escaped ("<span foreground=\"red\">%s</span>", msg); |
|
+ else |
|
+ markup = g_markup_escape_text (msg, -1); |
|
+ gtk_label_set_markup (label, markup); |
|
+ g_free (markup); |
|
+} |
|
+ |
|
+static void |
|
+session_saved_cb (DBusGProxy *proxy, |
|
+ DBusGProxyCall *call_id, |
|
+ void *user_data) |
|
+{ |
|
+ gboolean res; |
|
+ GsmPropertiesDialog *dialog = user_data; |
|
+ |
|
+ res = dbus_g_proxy_end_call (proxy, call_id, NULL, G_TYPE_INVALID); |
|
+ if (res) |
|
+ session_saved_message (dialog, _("Your session has been saved."), FALSE); |
|
+ else |
|
+ session_saved_message (dialog, _("Failed to save session"), TRUE); |
|
+ |
|
+ g_object_unref (proxy); |
|
+} |
|
+ |
|
static void |
|
on_save_session_clicked (GtkWidget *widget, |
|
GsmPropertiesDialog *dialog) |
|
{ |
|
- g_debug ("Session saving is not implemented yet!"); |
|
+ DBusGConnection *conn; |
|
+ DBusGProxy *proxy; |
|
+ DBusGProxyCall *call; |
|
+ |
|
+ conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); |
|
+ if (conn == NULL) { |
|
+ session_saved_message (dialog, _("Could not connect to the session bus"), TRUE); |
|
+ return; |
|
+ } |
|
+ |
|
+ proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS); |
|
+ if (proxy == NULL) { |
|
+ session_saved_message (dialog, _("Could not connect to the session manager"), TRUE); |
|
+ return; |
|
+ } |
|
+ |
|
+ call = dbus_g_proxy_begin_call (proxy, "SaveSession", session_saved_cb, dialog, NULL, G_TYPE_INVALID); |
|
+ if (call == NULL) { |
|
+ session_saved_message (dialog, _("Failed to save session"), TRUE); |
|
+ g_object_unref (proxy); |
|
+ return; |
|
+ } |
|
} |
|
|
|
static void |
|
setup_dialog (GsmPropertiesDialog *dialog) |
|
{ |
|
GtkTreeView *treeview; |
|
GtkWidget *button; |
|
GtkTreeModel *tree_filter; |
|
GtkTreeViewColumn *column; |
|
GtkCellRenderer *renderer; |
|
GtkTreeSelection *selection; |
|
GtkTargetList *targetlist; |
|
|
|
gtk_dialog_add_buttons (GTK_DIALOG (dialog), |
|
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, |
|
NULL); |
|
|
|
dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS, |
|
G_TYPE_BOOLEAN, |
|
G_TYPE_BOOLEAN, |
|
G_TYPE_ICON, |
|
G_TYPE_STRING, |
|
G_TYPE_OBJECT, |
|
G_TYPE_STRING); |
|
tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store), |
|
NULL); |
|
g_object_unref (dialog->priv->list_store); |
|
dialog->priv->tree_filter = tree_filter; |
|
|
|
gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter), |
|
diff --git a/capplet/meson.build b/capplet/meson.build |
|
index 8dad9c80..ae6cb6b9 100644 |
|
--- a/capplet/meson.build |
|
+++ b/capplet/meson.build |
|
@@ -1,36 +1,37 @@ |
|
install_data( |
|
install_dir: session_bindir |
|
) |
|
|
|
deps = session_deps + [ |
|
glib_dep, |
|
gtk_dep, |
|
x11_dep, |
|
sm_dep, |
|
- ice_dep |
|
+ ice_dep, |
|
+ dbus_glib_dep |
|
] |
|
|
|
cflags = [ |
|
'-DLOCALE_DIR="@0@"'.format(session_localedir), |
|
'-DGTKBUILDER_DIR="@0@"'.format(session_pkgdatadir) |
|
] |
|
|
|
sources = files( |
|
'../gnome-session/gsm-util.c', |
|
'gsm-app-dialog.c', |
|
'gsm-properties-dialog.c', |
|
'gsp-app.c', |
|
'gsp-app-manager.c', |
|
'gsp-keyfile.c', |
|
'main.c' |
|
) |
|
|
|
executable( |
|
'gnome-session-properties', |
|
sources, |
|
include_directories: [ top_inc, include_directories('../gnome-session') ], |
|
dependencies: deps, |
|
c_args: cflags, |
|
install: true, |
|
install_dir: session_bindir |
|
) |
|
diff --git a/data/session-properties.ui b/data/session-properties.ui |
|
index 47a30f78..b43759ff 100644 |
|
--- a/data/session-properties.ui |
|
+++ b/data/session-properties.ui |
|
@@ -133,108 +133,120 @@ |
|
</packing> |
|
</child> |
|
<child> |
|
<object class="GtkVBox" id="vbox3"> |
|
<property name="visible">True</property> |
|
<property name="can_focus">False</property> |
|
<property name="border_width">12</property> |
|
<property name="spacing">6</property> |
|
<child> |
|
<object class="GtkCheckButton" id="session_properties_remember_toggle"> |
|
<property name="label" translatable="yes">_Automatically remember running applications when logging out</property> |
|
<property name="visible">True</property> |
|
<property name="can_focus">True</property> |
|
<property name="receives_default">False</property> |
|
<property name="use_underline">True</property> |
|
<property name="xalign">0.5</property> |
|
<property name="draw_indicator">True</property> |
|
</object> |
|
<packing> |
|
<property name="expand">False</property> |
|
<property name="fill">False</property> |
|
<property name="position">0</property> |
|
</packing> |
|
</child> |
|
<child> |
|
<object class="GtkHButtonBox" id="hbuttonbox1"> |
|
<property name="visible">True</property> |
|
<property name="can_focus">False</property> |
|
<child> |
|
<object class="GtkButton" id="session_properties_save_button"> |
|
+ <property name="visible">True</property> |
|
<property name="can_focus">True</property> |
|
<property name="receives_default">True</property> |
|
<child> |
|
<object class="GtkHBox" id="hbox2"> |
|
<property name="visible">True</property> |
|
<property name="can_focus">False</property> |
|
<property name="spacing">4</property> |
|
<child> |
|
<object class="GtkImage" id="image1"> |
|
<property name="visible">True</property> |
|
<property name="can_focus">False</property> |
|
<property name="stock">gtk-save</property> |
|
</object> |
|
<packing> |
|
<property name="expand">False</property> |
|
<property name="fill">False</property> |
|
<property name="position">0</property> |
|
</packing> |
|
</child> |
|
<child> |
|
<object class="GtkLabel" id="label7"> |
|
<property name="visible">True</property> |
|
<property name="can_focus">False</property> |
|
<property name="label" translatable="yes">_Remember Currently Running Applications</property> |
|
<property name="use_underline">True</property> |
|
</object> |
|
<packing> |
|
<property name="expand">True</property> |
|
<property name="fill">True</property> |
|
<property name="position">1</property> |
|
</packing> |
|
</child> |
|
</object> |
|
</child> |
|
</object> |
|
<packing> |
|
<property name="expand">False</property> |
|
<property name="fill">False</property> |
|
<property name="position">0</property> |
|
</packing> |
|
</child> |
|
</object> |
|
<packing> |
|
<property name="expand">False</property> |
|
<property name="fill">False</property> |
|
<property name="position">1</property> |
|
</packing> |
|
</child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="session_properties_session_saved_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="wrap">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">False</property> |
|
+ <property name="position">2</property> |
|
+ </packing> |
|
+ </child> |
|
</object> |
|
<packing> |
|
<property name="position">1</property> |
|
</packing> |
|
</child> |
|
<child type="tab"> |
|
<object class="GtkLabel" id="label5"> |
|
<property name="visible">True</property> |
|
<property name="can_focus">False</property> |
|
<property name="label" translatable="yes">Options</property> |
|
</object> |
|
<packing> |
|
<property name="position">1</property> |
|
<property name="tab_fill">False</property> |
|
</packing> |
|
</child> |
|
</object> |
|
<object class="GtkTable" id="main-table"> |
|
<property name="visible">True</property> |
|
<property name="can_focus">False</property> |
|
<property name="border_width">6</property> |
|
<property name="n_rows">3</property> |
|
<property name="n_columns">2</property> |
|
<property name="column_spacing">12</property> |
|
<property name="row_spacing">6</property> |
|
<child> |
|
<object class="GtkHBox" id="hbox3"> |
|
<property name="visible">True</property> |
|
<property name="can_focus">False</property> |
|
<property name="spacing">12</property> |
|
diff --git a/gnome-session/gsm-client.c b/gnome-session/gsm-client.c |
|
index 6828ad44..3f216b22 100644 |
|
--- a/gnome-session/gsm-client.c |
|
+++ b/gnome-session/gsm-client.c |
|
@@ -514,50 +514,60 @@ 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, |
|
GsmApp *app, |
|
GError **error) |
|
{ |
|
g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); |
|
|
|
return GSM_CLIENT_GET_CLASS (client)->impl_save (client, app, error); |
|
} |
|
|
|
void |
|
gsm_client_end_session_response (GsmClient *client, |
|
gboolean is_ok, |
|
gboolean do_last, |
|
gboolean cancel, |
|
const char *reason) |
|
{ |
|
g_signal_emit (client, signals[END_SESSION_RESPONSE], 0, |
|
is_ok, do_last, cancel, reason); |
|
} |
|
diff --git a/gnome-session/gsm-client.h b/gnome-session/gsm-client.h |
|
index 78cb15d8..19c9cd8d 100644 |
|
--- a/gnome-session/gsm-client.h |
|
+++ b/gnome-session/gsm-client.h |
|
@@ -64,91 +64,97 @@ 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, |
|
GsmApp *app, |
|
GError **error); |
|
}; |
|
|
|
typedef enum |
|
{ |
|
GSM_CLIENT_ERROR_GENERAL = 0, |
|
GSM_CLIENT_ERROR_NOT_REGISTERED, |
|
GSM_CLIENT_NUM_ERRORS |
|
} GsmClientError; |
|
|
|
#define GSM_CLIENT_ERROR gsm_client_error_quark () |
|
GQuark gsm_client_error_quark (void); |
|
|
|
GType gsm_client_get_type (void) G_GNUC_CONST; |
|
|
|
const char *gsm_client_peek_id (GsmClient *client); |
|
|
|
|
|
const char * gsm_client_peek_startup_id (GsmClient *client); |
|
const char * gsm_client_peek_app_id (GsmClient *client); |
|
guint gsm_client_peek_restart_style_hint (GsmClient *client); |
|
guint gsm_client_peek_status (GsmClient *client); |
|
|
|
|
|
char *gsm_client_get_app_name (GsmClient *client); |
|
void gsm_client_set_app_id (GsmClient *client, |
|
const char *app_id); |
|
void gsm_client_set_status (GsmClient *client, |
|
guint status); |
|
|
|
gboolean gsm_client_end_session (GsmClient *client, |
|
guint flags, |
|
GError **error); |
|
gboolean gsm_client_query_end_session (GsmClient *client, |
|
guint flags, |
|
GError **error); |
|
gboolean gsm_client_cancel_end_session (GsmClient *client, |
|
GError **error); |
|
|
|
void gsm_client_disconnected (GsmClient *client); |
|
|
|
+gboolean gsm_client_request_save (GsmClient *client, |
|
+ guint flags, |
|
+ GError **error); |
|
GKeyFile *gsm_client_save (GsmClient *client, |
|
GsmApp *app, |
|
GError **error); |
|
|
|
gboolean gsm_client_stop (GsmClient *client, |
|
GError **error); |
|
|
|
/* private */ |
|
|
|
void gsm_client_end_session_response (GsmClient *client, |
|
gboolean is_ok, |
|
gboolean do_last, |
|
gboolean cancel, |
|
const char *reason); |
|
|
|
G_END_DECLS |
|
|
|
#endif /* __GSM_CLIENT_H__ */ |
|
diff --git a/gnome-session/gsm-dbus-client.c b/gnome-session/gsm-dbus-client.c |
|
index 6e9b002b..5793f830 100644 |
|
--- a/gnome-session/gsm-dbus-client.c |
|
+++ b/gnome-session/gsm-dbus-client.c |
|
@@ -300,60 +300,73 @@ gsm_dbus_client_get_property (GObject *object, |
|
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); |
|
|
|
if (client->priv->skeleton != NULL) { |
|
g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (client->priv->skeleton), |
|
client->priv->connection); |
|
g_clear_object (&client->priv->skeleton); |
|
} |
|
|
|
g_clear_object (&client->priv->connection); |
|
|
|
if (client->priv->watch_id != 0) |
|
g_bus_unwatch_name (client->priv->watch_id); |
|
|
|
G_OBJECT_CLASS (gsm_dbus_client_parent_class)->finalize (object); |
|
} |
|
|
|
+static gboolean |
|
+dbus_client_request_save (GsmClient *client, |
|
+ guint flags, |
|
+ GError **error) |
|
+{ |
|
+ g_debug ("GsmDBusClient: sending save request to client with id %s", |
|
+ gsm_client_peek_id (client)); |
|
+ |
|
+ /* FIXME: The protocol does not support this */ |
|
+ |
|
+ return FALSE; |
|
+} |
|
+ |
|
static GKeyFile * |
|
dbus_client_save (GsmClient *client, |
|
GsmApp *app, |
|
GError **error) |
|
{ |
|
g_debug ("GsmDBusClient: saving client with id %s", |
|
gsm_client_peek_id (client)); |
|
|
|
/* FIXME: We still don't support client saving for D-Bus |
|
* session clients */ |
|
|
|
return NULL; |
|
} |
|
|
|
static gboolean |
|
dbus_client_stop (GsmClient *client, |
|
GError **error) |
|
{ |
|
GsmDBusClient *dbus_client = (GsmDBusClient *) client; |
|
gsm_exported_client_private_emit_stop (dbus_client->priv->skeleton); |
|
return TRUE; |
|
} |
|
|
|
static char * |
|
dbus_client_get_app_name (GsmClient *client) |
|
{ |
|
/* Always use app-id instead */ |
|
return NULL; |
|
} |
|
|
|
@@ -394,60 +407,61 @@ static gboolean |
|
dbus_client_end_session (GsmClient *client, |
|
GsmClientEndSessionFlag flags, |
|
GError **error) |
|
{ |
|
GsmDBusClient *dbus_client = (GsmDBusClient *) client; |
|
|
|
gsm_exported_client_private_emit_end_session (dbus_client->priv->skeleton, flags); |
|
return TRUE; |
|
} |
|
|
|
static gboolean |
|
dbus_client_cancel_end_session (GsmClient *client, |
|
GError **error) |
|
{ |
|
GsmDBusClient *dbus_client = (GsmDBusClient *) client; |
|
gsm_exported_client_private_emit_cancel_end_session (dbus_client->priv->skeleton); |
|
return TRUE; |
|
} |
|
|
|
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; |
|
|
|
+ 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 4bb81e04..ede4186a 100644 |
|
--- a/gnome-session/gsm-manager.c |
|
+++ b/gnome-session/gsm-manager.c |
|
@@ -54,60 +54,61 @@ |
|
#include "gsm-dbus-client.h" |
|
|
|
#include "gsm-autostart-app.h" |
|
|
|
#include "gsm-util.h" |
|
#include "gsm-icon-names.h" |
|
#include "gsm-system.h" |
|
#include "gsm-session-save.h" |
|
#include "gsm-shell-extensions.h" |
|
#include "gsm-fail-whale.h" |
|
|
|
#define GSM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_MANAGER, GsmManagerPrivate)) |
|
|
|
/* UUIDs for log messages */ |
|
#define GSM_MANAGER_STARTUP_SUCCEEDED_MSGID "0ce153587afa4095832d233c17a88001" |
|
#define GSM_MANAGER_UNRECOVERABLE_FAILURE_MSGID "10dd2dc188b54a5e98970f56499d1f73" |
|
|
|
#define GSM_MANAGER_DBUS_PATH "/org/gnome/SessionManager" |
|
#define GSM_MANAGER_DBUS_NAME "org.gnome.SessionManager" |
|
#define GSM_MANAGER_DBUS_IFACE "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, GParamSpec *spec, GsmManager *manager); |
|
|
|
typedef enum |
|
{ |
|
GSM_MANAGER_LOGOUT_NONE, |
|
GSM_MANAGER_LOGOUT_LOGOUT, |
|
GSM_MANAGER_LOGOUT_REBOOT, |
|
GSM_MANAGER_LOGOUT_REBOOT_INTERACT, |
|
GSM_MANAGER_LOGOUT_SHUTDOWN, |
|
@@ -1172,60 +1173,123 @@ end_session_or_show_shell_dialog (GsmManager *manager) |
|
end_phase (manager); |
|
} |
|
break; |
|
|
|
case GSM_MANAGER_LOGOUT_MODE_FORCE: |
|
end_phase (manager); |
|
break; |
|
default: |
|
g_assert_not_reached (); |
|
break; |
|
} |
|
|
|
} |
|
|
|
static void |
|
query_end_session_complete (GsmManager *manager) |
|
{ |
|
|
|
g_debug ("GsmManager: query end session complete"); |
|
|
|
/* Remove the timeout since this can be called from outside the timer |
|
* and we don't want to have it called twice */ |
|
if (manager->priv->query_timeout_id > 0) { |
|
g_source_remove (manager->priv->query_timeout_id); |
|
manager->priv->query_timeout_id = 0; |
|
} |
|
|
|
end_session_or_show_shell_dialog (manager); |
|
} |
|
|
|
+static gboolean |
|
+_client_request_save (GsmClient *client, |
|
+ ClientEndSessionData *data) |
|
+{ |
|
+ gboolean ret; |
|
+ GError *error; |
|
+ |
|
+ error = NULL; |
|
+ ret = gsm_client_request_save (client, data->flags, &error); |
|
+ if (ret) { |
|
+ g_debug ("GsmManager: adding client to query clients: %s", gsm_client_peek_id (client)); |
|
+ data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients, |
|
+ client); |
|
+ } else if (error) { |
|
+ g_debug ("GsmManager: unable to query client: %s", error->message); |
|
+ g_error_free (error); |
|
+ } |
|
+ |
|
+ return FALSE; |
|
+} |
|
+ |
|
+static gboolean |
|
+_client_request_save_helper (const char *id, |
|
+ GsmClient *client, |
|
+ ClientEndSessionData *data) |
|
+{ |
|
+ return _client_request_save (client, data); |
|
+} |
|
+ |
|
+static void |
|
+query_save_session_complete (GsmManager *manager) |
|
+{ |
|
+ GError *error = NULL; |
|
+ |
|
+ if (g_slist_length (manager->priv->next_query_clients) > 0) { |
|
+ ClientEndSessionData data; |
|
+ |
|
+ data.manager = manager; |
|
+ data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST; |
|
+ |
|
+ g_slist_foreach (manager->priv->next_query_clients, |
|
+ (GFunc)_client_request_save, |
|
+ &data); |
|
+ |
|
+ g_slist_free (manager->priv->next_query_clients); |
|
+ manager->priv->next_query_clients = NULL; |
|
+ |
|
+ return; |
|
+ } |
|
+ |
|
+ if (manager->priv->query_timeout_id > 0) { |
|
+ g_source_remove (manager->priv->query_timeout_id); |
|
+ manager->priv->query_timeout_id = 0; |
|
+ } |
|
+ |
|
+ gsm_session_save (manager->priv->clients, &error); |
|
+ |
|
+ if (error) { |
|
+ g_warning ("Error saving session: %s", error->message); |
|
+ g_error_free (error); |
|
+ } |
|
+} |
|
+ |
|
static guint32 |
|
generate_cookie (void) |
|
{ |
|
guint32 cookie; |
|
|
|
cookie = (guint32)g_random_int_range (1, G_MAXINT32); |
|
|
|
return cookie; |
|
} |
|
|
|
static guint32 |
|
_generate_unique_cookie (GsmManager *manager) |
|
{ |
|
guint32 cookie; |
|
|
|
do { |
|
cookie = generate_cookie (); |
|
} while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL); |
|
|
|
return cookie; |
|
} |
|
|
|
static gboolean |
|
_on_query_end_session_timeout (GsmManager *manager) |
|
{ |
|
GSList *l; |
|
|
|
manager->priv->query_timeout_id = 0; |
|
|
|
g_debug ("GsmManager: query end session timed out"); |
|
@@ -1252,60 +1316,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 |
|
@@ -1848,67 +1927,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, manager->priv->apps, &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); |
|
@@ -1968,85 +2066,98 @@ on_client_end_session_response (GsmClient *client, |
|
_handle_client_end_session_response (manager, |
|
client, |
|
is_ok, |
|
do_last, |
|
cancel, |
|
reason); |
|
} |
|
|
|
static void |
|
on_xsmp_client_logout_request (GsmXSMPClient *client, |
|
gboolean show_dialog, |
|
GsmManager *manager) |
|
{ |
|
GError *error; |
|
int logout_mode; |
|
|
|
if (show_dialog) { |
|
logout_mode = GSM_MANAGER_LOGOUT_MODE_NORMAL; |
|
} else { |
|
logout_mode = GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION; |
|
} |
|
|
|
error = NULL; |
|
gsm_manager_logout (manager, logout_mode, &error); |
|
if (error != NULL) { |
|
g_warning ("Unable to logout: %s", error->message); |
|
g_error_free (error); |
|
} |
|
} |
|
|
|
+static void |
|
+on_xsmp_client_save_request (GsmXSMPClient *client, |
|
+ gboolean show_dialog, |
|
+ GsmManager *manager) |
|
+{ |
|
+ g_debug ("GsmManager: save_request"); |
|
+ gsm_manager_save_session (manager, NULL); |
|
+} |
|
+ |
|
static void |
|
on_store_client_added (GsmStore *store, |
|
const char *id, |
|
GsmManager *manager) |
|
{ |
|
GsmClient *client; |
|
|
|
g_debug ("GsmManager: Client added: %s", id); |
|
|
|
client = (GsmClient *)gsm_store_lookup (store, id); |
|
|
|
/* a bit hacky */ |
|
if (GSM_IS_XSMP_CLIENT (client)) { |
|
g_signal_connect (client, |
|
"register-request", |
|
G_CALLBACK (on_xsmp_client_register_request), |
|
manager); |
|
g_signal_connect (client, |
|
"register-confirmed", |
|
G_CALLBACK (on_xsmp_client_register_confirmed), |
|
manager); |
|
g_signal_connect (client, |
|
"logout-request", |
|
G_CALLBACK (on_xsmp_client_logout_request), |
|
manager); |
|
+ g_signal_connect (client, |
|
+ "save-request", |
|
+ G_CALLBACK (on_xsmp_client_save_request), |
|
+ manager); |
|
} |
|
|
|
g_signal_connect (client, |
|
"end-session-response", |
|
G_CALLBACK (on_client_end_session_response), |
|
manager); |
|
|
|
gsm_exported_manager_emit_client_added (manager->priv->skeleton, 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); |
|
|
|
gsm_exported_manager_emit_client_removed (manager->priv->skeleton, 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); |
|
} |
|
@@ -2625,60 +2736,95 @@ gsm_manager_initialization_error (GsmExportedManager *skeleton, |
|
gboolean fatal, |
|
GsmManager *manager) |
|
{ |
|
if (manager->priv->phase != GSM_MANAGER_PHASE_INITIALIZATION) { |
|
g_dbus_method_invocation_return_error (invocation, |
|
GSM_MANAGER_ERROR, |
|
GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION, |
|
"InitializationError interface is only available during the Initialization phase"); |
|
return TRUE; |
|
} |
|
|
|
gsm_util_init_error (fatal, "%s", message); |
|
gsm_exported_manager_complete_initialization_error (skeleton, invocation); |
|
|
|
return TRUE; |
|
} |
|
|
|
static void |
|
user_logout (GsmManager *manager, |
|
GsmManagerLogoutMode mode) |
|
{ |
|
if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) { |
|
manager->priv->logout_mode = mode; |
|
end_session_or_show_shell_dialog (manager); |
|
return; |
|
} |
|
|
|
request_logout (manager, mode); |
|
} |
|
|
|
+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_logout (GsmManager *manager, |
|
guint logout_mode, |
|
GError **error) |
|
{ |
|
if (manager->priv->phase < GSM_MANAGER_PHASE_RUNNING) { |
|
g_set_error (error, |
|
GSM_MANAGER_ERROR, |
|
GSM_MANAGER_ERROR_NOT_IN_RUNNING, |
|
"Logout interface is only available after the Running phase starts"); |
|
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; |
|
} |
|
|
|
switch (logout_mode) { |
|
case GSM_MANAGER_LOGOUT_MODE_NORMAL: |
|
case GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION: |
|
case GSM_MANAGER_LOGOUT_MODE_FORCE: |
|
user_logout (manager, logout_mode); |
|
break; |
|
|
|
default: |
|
g_debug ("Unknown logout mode option"); |
|
diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h |
|
index bc440cb0..4d14aa34 100644 |
|
--- a/gnome-session/gsm-manager.h |
|
+++ b/gnome-session/gsm-manager.h |
|
@@ -96,40 +96,43 @@ 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); |
|
|
|
void _gsm_manager_set_active_session (GsmManager *manager, |
|
const char *session_name, |
|
gboolean is_fallback); |
|
|
|
void _gsm_manager_set_renderer (GsmManager *manager, |
|
const char *renderer); |
|
|
|
+gboolean gsm_manager_save_session (GsmManager *manager, |
|
+ GError **error); |
|
+ |
|
gboolean gsm_manager_logout (GsmManager *manager, |
|
guint logout_mode, |
|
GError **error); |
|
|
|
gboolean gsm_manager_set_phase (GsmManager *manager, |
|
GsmManagerPhase phase); |
|
|
|
G_END_DECLS |
|
|
|
#endif /* __GSM_MANAGER_H */ |
|
diff --git a/gnome-session/gsm-xsmp-client.c b/gnome-session/gsm-xsmp-client.c |
|
index 8a30926f..cbecd68c 100644 |
|
--- a/gnome-session/gsm-xsmp-client.c |
|
+++ b/gnome-session/gsm-xsmp-client.c |
|
@@ -39,60 +39,61 @@ |
|
#define GsmDesktopFile "_GSM_DesktopFile" |
|
|
|
#define GSM_XSMP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientPrivate)) |
|
|
|
struct GsmXSMPClientPrivate |
|
{ |
|
|
|
SmsConn conn; |
|
IceConn ice_connection; |
|
|
|
guint watch_id; |
|
|
|
char *description; |
|
GPtrArray *props; |
|
|
|
/* SaveYourself state */ |
|
int current_save_yourself; |
|
int next_save_yourself; |
|
guint next_save_yourself_allow_interact : 1; |
|
}; |
|
|
|
enum { |
|
PROP_0, |
|
PROP_ICE_CONNECTION |
|
}; |
|
|
|
enum { |
|
REGISTER_REQUEST, |
|
REGISTER_CONFIRMED, |
|
LOGOUT_REQUEST, |
|
+ SAVE_REQUEST, |
|
LAST_SIGNAL |
|
}; |
|
|
|
static guint signals[LAST_SIGNAL] = { 0 }; |
|
|
|
G_DEFINE_TYPE (GsmXSMPClient, gsm_xsmp_client, GSM_TYPE_CLIENT) |
|
|
|
static gboolean |
|
client_iochannel_watch (GIOChannel *channel, |
|
GIOCondition condition, |
|
GsmXSMPClient *client) |
|
{ |
|
gboolean keep_going; |
|
|
|
g_object_ref (client); |
|
switch (IceProcessMessages (client->priv->ice_connection, NULL, NULL)) { |
|
case IceProcessMessagesSuccess: |
|
keep_going = TRUE; |
|
break; |
|
|
|
case IceProcessMessagesIOError: |
|
g_debug ("GsmXSMPClient: IceProcessMessagesIOError on '%s'", client->priv->description); |
|
gsm_client_set_status (GSM_CLIENT (client), GSM_CLIENT_FAILED); |
|
/* Emitting "disconnected" will eventually cause |
|
* IceCloseConnection() to be called. |
|
*/ |
|
gsm_client_disconnected (GSM_CLIENT (client)); |
|
keep_going = FALSE; |
|
break; |
|
|
|
@@ -472,60 +473,84 @@ xsmp_interact (GsmClient *client) |
|
|
|
SmsInteract (xsmp->priv->conn); |
|
} |
|
|
|
static gboolean |
|
xsmp_cancel_end_session (GsmClient *client, |
|
GError **error) |
|
{ |
|
GsmXSMPClient *xsmp = (GsmXSMPClient *) client; |
|
|
|
g_debug ("GsmXSMPClient: xsmp_cancel_end_session ('%s')", xsmp->priv->description); |
|
|
|
if (xsmp->priv->conn == NULL) { |
|
g_set_error (error, |
|
GSM_CLIENT_ERROR, |
|
GSM_CLIENT_ERROR_NOT_REGISTERED, |
|
"Client is not registered"); |
|
return FALSE; |
|
} |
|
|
|
SmsShutdownCancelled (xsmp->priv->conn); |
|
|
|
/* reset the state */ |
|
xsmp->priv->current_save_yourself = -1; |
|
xsmp->priv->next_save_yourself = -1; |
|
xsmp->priv->next_save_yourself_allow_interact = FALSE; |
|
|
|
return TRUE; |
|
} |
|
|
|
+static gboolean |
|
+xsmp_request_save (GsmClient *client, |
|
+ guint flags, |
|
+ GError **error) |
|
+{ |
|
+ GsmXSMPClient *xsmp = (GsmXSMPClient *) client; |
|
+ |
|
+ g_debug ("GsmXSMPClient: xsmp_request_save ('%s')", xsmp->priv->description); |
|
+ |
|
+ if (xsmp->priv->conn == NULL) { |
|
+ g_set_error (error, |
|
+ GSM_CLIENT_ERROR, |
|
+ GSM_CLIENT_ERROR_NOT_REGISTERED, |
|
+ "Client is not registered"); |
|
+ return FALSE; |
|
+ } |
|
+ |
|
+ if (flags & GSM_CLIENT_END_SESSION_FLAG_LAST) |
|
+ xsmp_save_yourself_phase2 (client); |
|
+ else |
|
+ do_save_yourself (xsmp, SmSaveLocal, FALSE); |
|
+ |
|
+ return TRUE; |
|
+} |
|
static char * |
|
get_desktop_file_path (GsmXSMPClient *client) |
|
{ |
|
SmProp *prop; |
|
char *desktop_file_path = NULL; |
|
const char *program_name; |
|
|
|
/* XSMP clients using eggsmclient defines a special property |
|
* pointing to their respective desktop entry file */ |
|
prop = find_property (client, GsmDesktopFile, NULL); |
|
|
|
if (prop) { |
|
GFile *file = g_file_new_for_uri (prop->vals[0].value); |
|
desktop_file_path = g_file_get_path (file); |
|
g_object_unref (file); |
|
goto out; |
|
} |
|
|
|
/* If we can't get desktop file from GsmDesktopFile then we |
|
* try to find the desktop file from its program name */ |
|
prop = find_property (client, SmProgram, NULL); |
|
|
|
if (!prop) { |
|
goto out; |
|
} |
|
|
|
program_name = prop->vals[0].value; |
|
desktop_file_path = |
|
gsm_util_find_desktop_file_for_app_name (program_name, |
|
TRUE, FALSE); |
|
@@ -964,100 +989,112 @@ xsmp_get_unix_process_id (GsmClient *client) |
|
gboolean res; |
|
|
|
g_debug ("GsmXSMPClient: getting pid"); |
|
|
|
prop = find_property (GSM_XSMP_CLIENT (client), SmProcessID, NULL); |
|
|
|
if (!prop || strcmp (prop->type, SmARRAY8) != 0) { |
|
return 0; |
|
} |
|
|
|
pid = 0; |
|
res = _parse_value_as_uint ((char *)prop->vals[0].value, &pid); |
|
if (! res) { |
|
pid = 0; |
|
} |
|
|
|
return pid; |
|
} |
|
|
|
static void |
|
gsm_xsmp_client_class_init (GsmXSMPClientClass *klass) |
|
{ |
|
GObjectClass *object_class = G_OBJECT_CLASS (klass); |
|
GsmClientClass *client_class = GSM_CLIENT_CLASS (klass); |
|
|
|
object_class->finalize = gsm_xsmp_client_finalize; |
|
object_class->constructor = gsm_xsmp_client_constructor; |
|
object_class->get_property = gsm_xsmp_client_get_property; |
|
object_class->set_property = gsm_xsmp_client_set_property; |
|
|
|
+ client_class->impl_request_save = xsmp_request_save; |
|
client_class->impl_save = xsmp_save; |
|
client_class->impl_stop = xsmp_stop; |
|
client_class->impl_query_end_session = xsmp_query_end_session; |
|
client_class->impl_end_session = xsmp_end_session; |
|
client_class->impl_cancel_end_session = xsmp_cancel_end_session; |
|
client_class->impl_get_app_name = xsmp_get_app_name; |
|
client_class->impl_get_restart_style_hint = xsmp_get_restart_style_hint; |
|
client_class->impl_get_unix_process_id = xsmp_get_unix_process_id; |
|
|
|
signals[REGISTER_REQUEST] = |
|
g_signal_new ("register-request", |
|
G_OBJECT_CLASS_TYPE (object_class), |
|
G_SIGNAL_RUN_LAST, |
|
G_STRUCT_OFFSET (GsmXSMPClientClass, register_request), |
|
_boolean_handled_accumulator, |
|
NULL, |
|
NULL, |
|
G_TYPE_BOOLEAN, |
|
1, G_TYPE_POINTER); |
|
signals[REGISTER_CONFIRMED] = |
|
g_signal_new ("register-confirmed", |
|
G_OBJECT_CLASS_TYPE (object_class), |
|
G_SIGNAL_RUN_LAST, |
|
G_STRUCT_OFFSET (GsmXSMPClientClass, register_confirmed), |
|
NULL, |
|
NULL, |
|
NULL, |
|
G_TYPE_NONE, |
|
1, G_TYPE_POINTER); |
|
signals[LOGOUT_REQUEST] = |
|
g_signal_new ("logout-request", |
|
G_OBJECT_CLASS_TYPE (object_class), |
|
G_SIGNAL_RUN_LAST, |
|
G_STRUCT_OFFSET (GsmXSMPClientClass, logout_request), |
|
NULL, |
|
NULL, |
|
NULL, |
|
G_TYPE_NONE, |
|
1, G_TYPE_BOOLEAN); |
|
|
|
+ signals[SAVE_REQUEST] = |
|
+ g_signal_new ("save-request", |
|
+ G_OBJECT_CLASS_TYPE (object_class), |
|
+ G_SIGNAL_RUN_LAST, |
|
+ G_STRUCT_OFFSET (GsmXSMPClientClass, save_request), |
|
+ NULL, |
|
+ NULL, |
|
+ g_cclosure_marshal_VOID__BOOLEAN, |
|
+ G_TYPE_NONE, |
|
+ 1, G_TYPE_BOOLEAN); |
|
+ |
|
g_object_class_install_property (object_class, |
|
PROP_ICE_CONNECTION, |
|
g_param_spec_pointer ("ice-connection", |
|
"ice-connection", |
|
"ice-connection", |
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); |
|
|
|
g_type_class_add_private (klass, sizeof (GsmXSMPClientPrivate)); |
|
} |
|
|
|
GsmClient * |
|
gsm_xsmp_client_new (IceConn ice_conn) |
|
{ |
|
GsmXSMPClient *xsmp; |
|
|
|
xsmp = g_object_new (GSM_TYPE_XSMP_CLIENT, |
|
"ice-connection", ice_conn, |
|
NULL); |
|
|
|
return GSM_CLIENT (xsmp); |
|
} |
|
|
|
static Status |
|
register_client_callback (SmsConn conn, |
|
SmPointer manager_data, |
|
char *previous_id) |
|
{ |
|
GsmXSMPClient *client = manager_data; |
|
gboolean handled; |
|
char *id; |
|
diff --git a/gnome-session/gsm-xsmp-client.h b/gnome-session/gsm-xsmp-client.h |
|
index 1bb27975..6b95c51b 100644 |
|
--- a/gnome-session/gsm-xsmp-client.h |
|
+++ b/gnome-session/gsm-xsmp-client.h |
|
@@ -27,61 +27,62 @@ G_BEGIN_DECLS |
|
|
|
#define GSM_TYPE_XSMP_CLIENT (gsm_xsmp_client_get_type ()) |
|
#define GSM_XSMP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClient)) |
|
#define GSM_XSMP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass)) |
|
#define GSM_IS_XSMP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_XSMP_CLIENT)) |
|
#define GSM_IS_XSMP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_XSMP_CLIENT)) |
|
#define GSM_XSMP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass)) |
|
|
|
typedef struct _GsmXSMPClient GsmXSMPClient; |
|
typedef struct _GsmXSMPClientClass GsmXSMPClientClass; |
|
|
|
typedef struct GsmXSMPClientPrivate GsmXSMPClientPrivate; |
|
|
|
struct _GsmXSMPClient |
|
{ |
|
GsmClient parent; |
|
GsmXSMPClientPrivate *priv; |
|
}; |
|
|
|
struct _GsmXSMPClientClass |
|
{ |
|
GsmClientClass parent_class; |
|
|
|
/* signals */ |
|
gboolean (*register_request) (GsmXSMPClient *client, |
|
char **client_id); |
|
void (*register_confirmed) (GsmXSMPClient *client, |
|
const char *client_id); |
|
gboolean (*logout_request) (GsmXSMPClient *client, |
|
gboolean prompt); |
|
- |
|
+ gboolean (*save_request) (GsmXSMPClient *client, |
|
+ gboolean prompt); |
|
|
|
void (*saved_state) (GsmXSMPClient *client); |
|
|
|
void (*request_phase2) (GsmXSMPClient *client); |
|
|
|
void (*request_interaction) (GsmXSMPClient *client); |
|
void (*interaction_done) (GsmXSMPClient *client, |
|
gboolean cancel_shutdown); |
|
|
|
void (*save_yourself_done) (GsmXSMPClient *client); |
|
|
|
}; |
|
|
|
GType gsm_xsmp_client_get_type (void) G_GNUC_CONST; |
|
|
|
GsmClient *gsm_xsmp_client_new (IceConn ice_conn); |
|
|
|
void gsm_xsmp_client_connect (GsmXSMPClient *client, |
|
SmsConn conn, |
|
unsigned long *mask_ret, |
|
SmsCallbacks *callbacks_ret); |
|
|
|
void gsm_xsmp_client_save_state (GsmXSMPClient *client); |
|
void gsm_xsmp_client_save_yourself (GsmXSMPClient *client, |
|
gboolean save_state); |
|
void gsm_xsmp_client_save_yourself_phase2 (GsmXSMPClient *client); |
|
void gsm_xsmp_client_interact (GsmXSMPClient *client); |
|
void gsm_xsmp_client_shutdown_cancelled (GsmXSMPClient *client); |
|
|
|
G_END_DECLS |
|
diff --git a/gnome-session/org.gnome.SessionManager.xml b/gnome-session/org.gnome.SessionManager.xml |
|
index 580ec356..29eb0990 100644 |
|
--- a/gnome-session/org.gnome.SessionManager.xml |
|
+++ b/gnome-session/org.gnome.SessionManager.xml |
|
@@ -255,60 +255,68 @@ |
|
</arg> |
|
<arg name="handled" direction="out" type="b"> |
|
<doc:doc> |
|
<doc:summary>True if condition is handled, false otherwise</doc:summary> |
|
</doc:doc> |
|
</arg> |
|
<doc:doc> |
|
<doc:description> |
|
<doc:para>Allows the caller to determine whether the session manager is |
|
handling changes to the specified autostart condition.</doc:para> |
|
</doc:description> |
|
</doc:doc> |
|
</method> |
|
|
|
<method name="Shutdown"> |
|
<doc:doc> |
|
<doc:description> |
|
<doc:para>Request a shutdown dialog.</doc:para> |
|
</doc:description> |
|
</doc:doc> |
|
</method> |
|
|
|
<method name="Reboot"> |
|
<doc:doc> |
|
<doc:description> |
|
<doc:para>Request a reboot dialog.</doc:para> |
|
</doc:description> |
|
</doc:doc> |
|
</method> |
|
|
|
+ <method name="SaveSession"> |
|
+ <doc:doc> |
|
+ <doc:description> |
|
+ <doc:para>Request to save session</doc:para> |
|
+ </doc:description> |
|
+ </doc:doc> |
|
+ </method> |
|
+ |
|
<method name="CanShutdown"> |
|
<arg name="is_available" direction="out" type="b"> |
|
<doc:doc> |
|
<doc:summary>True if shutdown is available to the user, false otherwise</doc:summary> |
|
</doc:doc> |
|
</arg> |
|
<doc:doc> |
|
<doc:description> |
|
<doc:para>Allows the caller to determine whether or not it's okay to show |
|
a shutdown option in the UI</doc:para> |
|
</doc:description> |
|
</doc:doc> |
|
</method> |
|
|
|
<method name="Logout"> |
|
<arg name="mode" type="u" direction="in"> |
|
<doc:doc> |
|
<doc:summary>The type of logout that is being requested</doc:summary> |
|
</doc:doc> |
|
</arg> |
|
<doc:doc> |
|
<doc:description> |
|
<doc:para>Request a logout dialog</doc:para> |
|
<doc:para> |
|
Allowed values for the mode parameter are: |
|
<doc:list> |
|
<doc:item> |
|
<doc:term>0</doc:term> |
|
<doc:definition>Normal.</doc:definition> |
|
</doc:item> |
|
diff --git a/meson.build b/meson.build |
|
index 9a16d5b1..24221bb6 100644 |
|
--- a/meson.build |
|
+++ b/meson.build |
|
@@ -72,60 +72,61 @@ if enable_deprecation_flags |
|
endif |
|
|
|
compiler_flags = [] |
|
if session_debug |
|
test_cflags = [ |
|
'-Werror=format=2', |
|
'-Werror=implicit-function-declaration', |
|
'-Werror=init-self', |
|
'-Werror=missing-include-dirs', |
|
'-Werror=missing-prototypes', |
|
'-Werror=pointer-arith', |
|
'-Werror=return-type', |
|
'-Wnested-externs', |
|
'-Wstrict-prototypes' |
|
] |
|
|
|
compiler_flags += cc.get_supported_arguments(test_cflags) |
|
endif |
|
|
|
add_project_arguments(common_flags + compiler_flags, language: 'c') |
|
|
|
glib_req_version = '>= 2.46.0' |
|
|
|
gio_dep = dependency('gio-2.0', version: glib_req_version) |
|
glib_dep = dependency('glib-2.0', version: glib_req_version) |
|
gtk_dep = dependency('gtk+-3.0', version: '>= 3.18.0') |
|
xtrans_dep = dependency('xtrans') |
|
ice_dep = dependency('ice') |
|
sm_dep = dependency('sm') |
|
x11_dep = dependency('x11') |
|
+dbus_glib_dep = dependency('dbus-glib-1') |
|
|
|
session_deps = [ |
|
gio_dep, |
|
glib_dep, |
|
dependency('gnome-desktop-3.0', version: '>= 3.18.0'), |
|
dependency('json-glib-1.0', version: '>= 0.10') |
|
] |
|
|
|
session_bin_deps = session_deps + [ |
|
xtrans_dep, |
|
ice_dep, |
|
sm_dep |
|
] |
|
|
|
# Check for session selector GTK+ UI |
|
enable_session_selector = get_option('session_selector') |
|
|
|
# Check for session tracking backend |
|
session_tracking = 'null backend' |
|
|
|
enable_systemd = get_option('systemd') |
|
enable_systemd_journal = get_option('systemd_journal') |
|
enable_consolekit = get_option('consolekit') |
|
if enable_systemd or enable_consolekit |
|
session_bin_deps += dependency('gio-unix-2.0', version: glib_req_version) |
|
|
|
# Check for systemd |
|
if enable_systemd |
|
libsystemd_dep = dependency('libsystemd', version: '>= 209', required: false) |
|
session_bin_deps += libsystemd_dep |
|
-- |
|
2.17.0 |
|
|
|
|