[Xfce4-commits] <libxfce4ui:new-sm-client> add first cut of a session management client

Brian J. Tarricone brian at tarricone.org
Tue Sep 15 05:22:02 CEST 2009


Updating branch refs/heads/new-sm-client
         to 6cb7545b7a1b15af2771e6c6be0a763110ccf070 (commit)
       from fe8612ecbb27283245f2b2b20e748996fc7a1cb0 (commit)

commit 6cb7545b7a1b15af2771e6c6be0a763110ccf070
Author: Brian J. Tarricone <brian at tarricone.org>
Date:   Mon Sep 14 20:18:10 2009 -0700

    add first cut of a session management client
    
    API is not frozen, and it's not well tested.  it does compile, and it
    does successfully connect to the session manager on startup, but that's
    about all i can say about it at this point.

 libxfce4ui/Makefile.am        |   51 +-
 libxfce4ui/libxfce4ui.h       |    2 +
 libxfce4ui/libxfce4ui.symbols |   43 +
 libxfce4ui/xfce-sm-client.c   | 2250 +++++++++++++++++++++++++++++++++++++++++
 libxfce4ui/xfce-sm-client.h   |  184 ++++
 5 files changed, 2528 insertions(+), 2 deletions(-)

diff --git a/libxfce4ui/Makefile.am b/libxfce4ui/Makefile.am
index 6f2ff89..c48cb75 100644
--- a/libxfce4ui/Makefile.am
+++ b/libxfce4ui/Makefile.am
@@ -13,18 +13,27 @@ INCLUDES = \
 
 lib_LTLIBRARIES = libxfce4ui-1.la
 
+libxfce4ui_enum_headers = \
+	xfce-sm-client.h
+
 libxfce4ui_headers = \
 	libxfce4ui.h \
 	libxfce4ui-config.h \
+	libxfce4ui-enum-types.h \
 	xfce-dialogs.h \
 	xfce-gdk-extensions.h \
 	xfce-gtk-extensions.h \
 	xfce-spawn.h \
-	xfce-titled-dialog.h
+	xfce-titled-dialog.h \
+	$(libxfce4ui_enum_headers)
 
 libxfce4ui_built_sources = \
 	libxfce4ui-alias.h \
-	libxfce4ui-aliasdef.c
+	libxfce4ui-aliasdef.c \
+	libxfce4ui-enum-types.c \
+	libxfce4ui-enum-types.h \
+	libxfce4ui-marshal.c \
+	libxfce4ui-marshal.h
 
 libxfce4ui_includedir = \
 	$(includedir)/xfce4/libxfce4ui-$(LIBXFCE4UI_VERSION_API)/libxfce4ui
@@ -42,6 +51,7 @@ libxfce4ui_1_la_SOURCES = \
 	xfce-gtk-extensions.c \
 	xfce-heading.c \
 	xfce-heading.h \
+	xfce-sm-client.c \
 	xfce-spawn.c \
 	xfce-titled-dialog.c
 
@@ -97,6 +107,43 @@ libxfce4ui-alias.h: make-libxfce4ui-alias.pl libxfce4ui.symbols
 
 libxfce4ui-aliasdef.c: make-libxfce4ui-alias.pl libxfce4ui.symbols
 	$(PERL) $(srcdir)/make-libxfce4ui-alias.pl -def < $(srcdir)/libxfce4ui.symbols > libxfce4ui-aliasdef.c
+
+libxfce4ui-marshal.h: stamp-libxfce4ui-marshal.h
+	@true
+stamp-libxfce4ui-marshal.h: libxfce4ui-marshal.list Makefile
+	glib-genmarshal --prefix=_libxfce4ui_marshal --header $(srcdir)/libxfce4ui-marshal.list >xgen-lmh
+	cmp -s xgen-lmh libxfce4-marshal.h || cp xgen-lmh libxfce4ui-marshal.h
+	rm -f xgen-lmh
+	echo timestamp >$(@F)
+
+libxfce4ui-marshal.c: libxfce4ui-marshal.list Makefile
+	glib-genmarshal --prefix=_libxfce4ui_marshal --body $(srcdir)/libxfce4ui-marshal.list >xgen-lmc
+	cmp -s xgen-lmc libxfce4-marshal.c || cp xgen-lmc libxfce4ui-marshal.c
+	rm -f xgen-lmc
+
+libxfce4ui-enum-types.h: stamp-libxfce4ui-enum-types.h
+	@true
+stamp-libxfce4ui-enum-types.h: $(libxfce4ui_enum_headers) Makefile
+	( cd $(srcdir) && glib-mkenums \
+		--fhead "#ifndef __LIBXFCE4UI_ENUM_TYPES_H__\n#define __LIBXFCE4UI_ENUM_TYPES_H__\n\nG_BEGIN_DECLS\n\n" \
+		--fprod "/* enumerations from \"@filename@\" */\n\n" \
+		--vhead "GType @enum_name at _get_type(void) G_GNUC_CONST;\n#define XFCE_TYPE_ at ENUMSHORT@ (@enum_name at _get_type())\n\n" \
+		--ftail "G_END_DECLS\n\n#endif /* !__LIBXFCE4UI_ENUM_TYPES_H__ */" \
+		$(libxfce4ui_enum_headers) ) >xgen-leth
+	cmp -s xgen-leth libxfce4ui-enum-types.h || cp xgen-leth libxfce4ui-enum-types.h
+	rm -f xgen-leth
+	echo timestamp > $(@F)
+libxfce4ui-enum-types.c: $(libxfce4ui_enum_headers) Makefile
+	( cd $(srcdir) && glib-mkenums \
+		--fhead "#include <libxfce4ui/libxfce4ui.h>\n#include <libxfce4ui/libxfce4ui-alias.h>\n\n" \
+		--fprod "/* enumerations from \"@filename@\" */\n\n" \
+		--vhead "GType\n at enum_name@_get_type(void)\n{\n    static GType type = 0;\n\n    if(!type) {\n        static const G at Type@Value values[] = {"\
+		--vprod "            { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+		--vtail "            { 0, NULL, NULL }\n\t};\n\ttype = g_ at type@_register_static(\"@EnumName@\", values);\n    }\n\n    return type;\n}\n\n" \
+		--ftail "\n#define __LIBXFCE4UI_ENUM_TYPES_C__\n#include \"libxfce4ui-aliasdef.c\"\n" \
+		$(libxfce4ui_enum_headers) ) > xgen-letc
+	cp xgen-letc libxfce4ui-enum-types.c
+	rm -f xgen-letc
 endif
 
 # required for gtk-doc
diff --git a/libxfce4ui/libxfce4ui.h b/libxfce4ui/libxfce4ui.h
index abd859e..f494bc9 100644
--- a/libxfce4ui/libxfce4ui.h
+++ b/libxfce4ui/libxfce4ui.h
@@ -28,7 +28,9 @@
 #include <libxfce4ui/xfce-gdk-extensions.h>
 #include <libxfce4ui/xfce-gtk-extensions.h>
 #include <libxfce4ui/xfce-spawn.h>
+#include <libxfce4ui/xfce-sm-client.h>
 #include <libxfce4ui/xfce-titled-dialog.h>
+#include <libxfce4ui/libxfce4ui-enum-types.h>
 
 #undef LIBXFCE4UI_INSIDE_LIBXFCE4UI_H
 
diff --git a/libxfce4ui/libxfce4ui.symbols b/libxfce4ui/libxfce4ui.symbols
index 0f79e52..5f9fd3d 100644
--- a/libxfce4ui/libxfce4ui.symbols
+++ b/libxfce4ui/libxfce4ui.symbols
@@ -46,6 +46,16 @@ libxfce4ui_check_version
 #endif
 #endif
 
+/* libxfce4ui-enum-types functions */
+#if IN_HEADER(__LIBXFCE4UI_ENUM_TYPES_H__)
+#if IN_SOURCE(__LIBXFCE4UI_ENUM_TYPES_C__)
+xfce_sm_client_restart_style_get_type
+xfce_sm_client_state_get_type
+xfce_sm_client_priority_get_type
+xfce_sm_client_shutdown_hint_get_type
+#endif
+#endif
+
 /* xfce-dialogs functions */
 #if IN_HEADER(__XFCE_DIALOGS_H__)
 #if IN_SOURCE(__XFCE_DIALOGS_C__)
@@ -83,6 +93,39 @@ xfce_spawn_command_line_on_screen
 #endif
 #endif
 
+/* xfce-sm-client functions */
+#if IN_HEADER(__XFCE_SM_CLIENT_H__)
+#if IN_SOURCE(__XFCE_SM_CLIENT_C__)
+xfce_sm_client_get_type
+xfce_sm_client_get_option_group
+xfce_sm_client_get
+xfce_sm_client_get_with_argv
+xfce_sm_client_get_full
+xfce_sm_client_connect
+xfce_sm_client_disconnect
+xfce_sm_client_request_shutdown
+xfce_sm_client_get_state
+xfce_sm_client_get_client_id
+xfce_sm_client_get_state_file
+xfce_sm_client_set_restart_style
+xfce_sm_client_get_restart_style
+xfce_sm_client_set_priority
+xfce_sm_client_get_priority
+xfce_sm_client_set_program
+xfce_sm_client_get_program
+xfce_sm_client_set_clone_command
+xfce_sm_client_get_clone_command
+xfce_sm_client_set_resign_command
+xfce_sm_client_get_resign_command
+xfce_sm_client_set_restart_command
+xfce_sm_client_get_restart_command
+xfce_sm_client_set_discard_command
+xfce_sm_client_get_discard_command
+xfce_sm_client_set_shutdown_command
+xfce_sm_client_get_shutdown_command
+#endif
+#endif
+
 /* xfce-titled-dialog functions */
 #if IN_HEADER(__XFCE_TITLED_DIALOG_H__)
 #if IN_SOURCE(__XFCE_TITLED_DIALOG_C__)
diff --git a/libxfce4ui/xfce-sm-client.c b/libxfce4ui/xfce-sm-client.c
new file mode 100644
index 0000000..edb2b70
--- /dev/null
+++ b/libxfce4ui/xfce-sm-client.c
@@ -0,0 +1,2250 @@
+/*  xfce4
+ *  Copyright (C) 1999 Olivier Fourdan (fourdan at xfce.org)
+ *  Copyright (C) 2009 Brian Tarricone <brian at tarricone.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_LIBSM
+#include <X11/ICE/ICElib.h>
+#include <X11/SM/SMlib.h>
+#endif
+
+#include <gdk/gdkwindow.h>
+#include <gtk/gtk.h>
+
+#include <libxfce4util/libxfce4util.h>
+
+#include "xfce-sm-client.h"
+#include "libxfce4ui-marshal.h"
+#include "libxfce4ui-enum-types.h"
+
+#define SM_ID_ARG "--sm-client-id"
+#define DPY_ARG "--display"
+
+#define SM_ARG_APPEND 1
+#define SM_ARG_REMOVE 2
+
+#define xfce_safe_strcmp(s1, s2)  ( (!s1 && !s2) \
+                                    ? 0 : (!s1 \
+                                           ? -1 : (!s2 \
+                                                   ? 1 : strcmp(s1, s2))) )
+
+struct _XfceSMClientPrivate
+{
+#ifdef HAVE_LIBSM
+    SmcConn session_connection;
+#endif
+
+    XfceSMClientState state;
+    XfceSMClientRestartStyle restart_style;
+
+    gchar priority;
+
+    gchar *client_id;
+
+    gchar *current_directory;
+    gchar *program;
+    gchar **clone_command;
+    gchar **resign_command;
+    gchar **restart_command;
+    gchar **discard_command;
+    gchar **shutdown_command;
+
+    guint32 resumed:1,
+            needs_save_state:1,
+            shutdown_cancelled:1;
+
+    guint argc;
+    gchar **argv;
+
+    gchar *state_file;
+};
+
+typedef struct
+{
+    guint argc;
+    gchar **argv;
+    gchar *client_id;
+    gboolean sm_disable;
+} XfceSMClientStartupOptions;
+
+enum
+{
+    SIG_SAVE_STATE = 0,
+    SIG_SAVE_STATE_EXTENDED,
+    SIG_QUIT_REQUESTED,
+    SIG_QUIT,
+    SIG_QUIT_CANCELLED,
+    SIG_STATE_CHANGED,
+    N_SIGS
+};
+
+enum
+{
+    PROP0 = 0,
+    PROP_SESSION_CONNECTION,
+    PROP_STATE,
+    PROP_RESUMED,
+    PROP_RESTART_STYLE,
+    PROP_PRIORITY,
+    PROP_CLIENT_ID,
+    PROP_CURRENT_DIRECTORY,
+    PROP_PROGRAM,
+    PROP_CLONE_COMMAND,
+    PROP_RESIGN_COMMAND,
+    PROP_RESTART_COMMAND,
+    PROP_DISCARD_COMMAND,
+    PROP_SHUTDOWN_COMMAND,
+    PROP_ARGC,
+    PROP_ARGV,
+};
+
+static void xfce_sm_client_get_property(GObject *obj,
+                                        guint property_id,
+                                        GValue *value,
+                                        GParamSpec *pspec);
+static void xfce_sm_client_set_property(GObject *obj,
+                                        guint property_id,
+                                        const GValue *value,
+                                        GParamSpec *pspec);
+static GObject *xfce_sm_client_constructor(GType type,
+                                           guint n_construct_params,
+                                           GObjectConstructParam *construct_params);
+static void xfce_sm_client_finalize(GObject *obj);
+
+static void xfce_sm_client_set_client_id(XfceSMClient *sm_client,
+                                         const gchar *client_id);
+static void xfce_sm_client_parse_argv(XfceSMClient *sm_client);
+
+
+static guint signals[N_SIGS] = { 0, };
+static XfceSMClientStartupOptions startup_options = { 0, NULL, NULL, FALSE };
+static XfceSMClient *sm_client_singleton = NULL;
+
+
+G_DEFINE_TYPE(XfceSMClient, xfce_sm_client, G_TYPE_OBJECT)
+
+
+static void
+xfce_sm_client_class_init(XfceSMClientClass *klass)
+{
+    GObjectClass *gobject_class = (GObjectClass *)klass;
+
+    g_type_class_add_private(klass, sizeof(XfceSMClientPrivate));
+
+    gobject_class->get_property = xfce_sm_client_get_property;
+    gobject_class->set_property = xfce_sm_client_set_property;
+    gobject_class->constructor = xfce_sm_client_constructor;
+    gobject_class->finalize = xfce_sm_client_finalize;
+
+    /**
+     * XfceSMClient::save-state:
+     * @sm_client: An #XfceSMClient
+     *
+     * Signals the client that it should save a copy of its current state
+     * such that it could be restarted later in exactly the same state as
+     * it is at the time of signal emission.
+     *
+     * If the state is simple enough to be encoded in the application's
+     * command line, xfce_sm_client_set_restart_command() can be used
+     * to set that command line.  For more complex state data,
+     * xfce_sm_client_get_state_file() should be used.
+     *
+     * The application should attempt to save its state as quickly as
+     * possible, and MUST NOT interact with the user as a part of saving
+     * state.
+     **/
+    signals[SIG_SAVE_STATE] = g_signal_new("save-state",
+                                           G_TYPE_FROM_CLASS(klass),
+                                           G_SIGNAL_RUN_LAST,
+                                           G_STRUCT_OFFSET(XfceSMClientClass,
+                                                           save_state_extended),
+                                           NULL, NULL,
+                                           g_cclosure_marshal_VOID__BOOLEAN,
+                                           G_TYPE_NONE, 1,
+                                           G_TYPE_BOOLEAN);
+
+    /**
+     * XfceSMClient::save-state-extended:
+     * @sm_client: An #XfceSMClient
+     *
+     * Allows the application to save extra state information after all
+     * other applications in the session have had a chance to save their
+     * state.  This is mainly used by the window manager to save window
+     * positions.  Most applications should not need to connect to this
+     * signal.
+     **/
+    signals[SIG_SAVE_STATE_EXTENDED] = g_signal_new("save-state-extended",
+                                                    G_TYPE_FROM_CLASS(klass),
+                                                    G_SIGNAL_RUN_LAST,
+                                                    G_STRUCT_OFFSET(XfceSMClientClass,
+                                                                    save_state_extended),
+                                                    NULL, NULL,
+                                                    g_cclosure_marshal_VOID__VOID,
+                                                    G_TYPE_NONE, 0);
+
+    /**
+     * XfceSMClient::quit-requested:
+     * @sm_client: An #XfceSMClient
+     *
+     * Signals the client that the session manager will soon want the
+     * application to quit, perhaps as a part of ending the session
+     * (but this should not be assumed).  The application can take
+     * this opportunity to prompt the user to save any unsaved work
+     * to disk.
+     *
+     * This signal also expects a return value from the handler.  If the
+     * application wishes to cancel the quit request (perhaps because the
+     * user selected "Cancel" in prompts to save unsaved work), it should
+     * return %TRUE from the handler.  If the application is satisfied
+     * with possibly needing to quit soon, the handler should return %FALSE.
+     **/
+    signals[SIG_QUIT_REQUESTED] = g_signal_new("quit-requested",
+                                               G_TYPE_FROM_CLASS(klass),
+                                               G_SIGNAL_RUN_LAST,
+                                               G_STRUCT_OFFSET(XfceSMClientClass,
+                                                               quit_requested),
+                                               g_signal_accumulator_true_handled,
+                                               NULL,
+                                               _libxfce4ui_marshal_BOOLEAN__VOID,
+                                               G_TYPE_BOOLEAN, 0);
+
+    /**
+     * XfceSMClient::quit:
+     * @sm_client: An #XfceSMClient
+     *
+     * Emitted when the application is required to quit.  This is not
+     * optional: if the client does not quit a short time after receiving
+     * this signal, it will likely be terminated in some other way.  While
+     * not required, the applicatiokn will usually receive quit-requested
+     * before receiving quit.  If the application does not connect to this
+     * signal, #XfceSMClient will call exit(3) with an exit code of zero
+     * on behalf of the application.
+     **/
+    signals[SIG_QUIT] = g_signal_new("quit",
+                                     G_TYPE_FROM_CLASS(klass),
+                                     G_SIGNAL_RUN_LAST,
+                                     G_STRUCT_OFFSET(XfceSMClientClass,
+                                                     quit),
+                                     NULL, NULL,
+                                     g_cclosure_marshal_VOID__VOID,
+                                     G_TYPE_NONE, 0);
+
+    /**
+     * XfceSMClient::quit-cancelled:
+     * @sm_client: An #XfceSMClient
+     *
+     * Informs the application that it will not need to quit.  In most cases,
+     * quit-cancelled will be emitted a short time after quit-requested.
+     **/
+    signals[SIG_QUIT_CANCELLED] = g_signal_new("quit-cancelled",
+                                               G_TYPE_FROM_CLASS(klass),
+                                               G_SIGNAL_RUN_LAST,
+                                               G_STRUCT_OFFSET(XfceSMClientClass,
+                                                               quit_cancelled),
+                                               NULL, NULL,
+                                               g_cclosure_marshal_VOID__VOID,
+                                               G_TYPE_NONE, 0);
+    
+    /**
+     * XfceSMClient::state-changed
+     * @sm_client: An #XfceSMClient
+     * @old_state: The client's previous state
+     * @new_state: The client's new (current) state
+     *
+     * Emitted whenever the client's internal session management state
+     * changes.  This is mainly useful for debugging and other informative
+     * purposes.  No actual actions should be taken based on the emission
+     * of this signal; instead, the application should make use of the other
+     * signals emitted on the object.
+     **/
+    signals[SIG_STATE_CHANGED] = g_signal_new("state-changed",
+                                              G_TYPE_FROM_CLASS(klass),
+                                              G_SIGNAL_RUN_LAST,
+                                              G_STRUCT_OFFSET(XfceSMClientClass,
+                                                              state_changed),
+                                              NULL, NULL,
+                                              _libxfce4ui_marshal_VOID__ENUM_ENUM,
+                                              G_TYPE_NONE, 2,
+                                              XFCE_TYPE_SM_CLIENT_STATE,
+                                              XFCE_TYPE_SM_CLIENT_STATE);
+
+    g_object_class_install_property(gobject_class, PROP_SESSION_CONNECTION,
+                                    g_param_spec_pointer("session-connection",
+                                                         "Session connection descriptor",
+                                                         "The raw pointer to the SmcConnection",
+                                                         G_PARAM_READABLE));
+    g_object_class_install_property(gobject_class, PROP_STATE,
+                                    g_param_spec_enum("state",
+                                                      "State",
+                                                      "The client's current XSMP state",
+                                                      XFCE_TYPE_SM_CLIENT_STATE,
+                                                      XFCE_SM_CLIENT_STATE_DISCONNECTED,
+                                                      G_PARAM_READABLE));
+    g_object_class_install_property(gobject_class, PROP_RESUMED,
+                                    g_param_spec_boolean("resumed",
+                                                         "Resumed",
+                                                         "Whether or not the client was resumed with previous state",
+                                                         FALSE,
+                                                         G_PARAM_READABLE));
+    g_object_class_install_property(gobject_class, PROP_RESTART_STYLE,
+                                    g_param_spec_enum("restart-style",
+                                                      "Restart style",
+                                                      "Specifies how the client should be restarted by the session manager",
+                                                      XFCE_TYPE_SM_CLIENT_RESTART_STYLE,
+                                                      XFCE_SM_CLIENT_RESTART_IF_RUNNING,
+                                                      G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+    g_object_class_install_property(gobject_class, PROP_PRIORITY,
+                                    g_param_spec_char("priority",
+                                                      "Priority",
+                                                      "Determines the ordering in which this client is restarted",
+                                                      G_MININT8, G_MAXINT8, 50,
+                                                      G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+    g_object_class_install_property(gobject_class, PROP_CLIENT_ID,
+                                    g_param_spec_string("client-id",
+                                                        "Client ID",
+                                                        "A string uniquely identifying the current instance of this client",
+                                                        NULL,
+                                                        G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
+    g_object_class_install_property(gobject_class, PROP_CURRENT_DIRECTORY,
+                                    g_param_spec_string("current-directory",
+                                                        "Current working directory",
+                                                        "The directory that should be used as the working directory the next time this client is restarted",
+                                                        NULL,
+                                                        G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+    g_object_class_install_property(gobject_class, PROP_PROGRAM,
+                                    g_param_spec_string("program",
+                                                        "Program name",
+                                                        "An string used to identify the application",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+    g_object_class_install_property(gobject_class, PROP_CLONE_COMMAND,
+                                    g_param_spec_boxed("clone-command",
+                                                       "Clone command",
+                                                       "A command used to launch a fresh instance of the application",
+                                                       G_TYPE_STRV,
+                                                       G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+    g_object_class_install_property(gobject_class, PROP_RESIGN_COMMAND,
+                                    g_param_spec_boxed("resign-command",
+                                                       "Resign command",
+                                                       "A command used to remove any saved state for the 'anyway' restart style",
+                                                       G_TYPE_STRV,
+                                                       G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+    g_object_class_install_property(gobject_class, PROP_RESTART_COMMAND,
+                                    g_param_spec_boxed("restart-command",
+                                                       "Restart command",
+                                                       "A command used to restart this application, preserving the current state",
+                                                       G_TYPE_STRV,
+                                                       G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+    g_object_class_install_property(gobject_class, PROP_DISCARD_COMMAND,
+                                    g_param_spec_boxed("discard-command",
+                                                       "Discard command",
+                                                       "A command used to discard any information about the current saved state",
+                                                       G_TYPE_STRV,
+                                                       G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+    g_object_class_install_property(gobject_class, PROP_SHUTDOWN_COMMAND,
+                                    g_param_spec_boxed("shutdown-command",
+                                                       "Shutdown command",
+                                                       "A command used at session end to clean up after an application started with the 'anyway' restart style",
+                                                       G_TYPE_STRV,
+                                                       G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+    /* these are "private" properties */
+    g_object_class_install_property(gobject_class, PROP_ARGC,
+                                    g_param_spec_uint("argc",
+                                                      "argc",
+                                                      "Argument count passed to program",
+                                                      0, G_MAXUINT, 0,
+                                                      G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
+    g_object_class_install_property(gobject_class, PROP_ARGV,
+                                    g_param_spec_boxed("argv",
+                                                       "argv",
+                                                       "Argument vector passed to program",
+                                                       G_TYPE_STRV,
+                                                       G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+xfce_sm_client_init(XfceSMClient *sm_client)
+{
+    sm_client->priv = G_TYPE_INSTANCE_GET_PRIVATE(sm_client,
+                                                  XFCE_TYPE_SM_CLIENT,
+                                                  XfceSMClientPrivate);
+
+    sm_client->priv->state = XFCE_SM_CLIENT_STATE_DISCONNECTED;
+    sm_client->priv->program = g_get_prgname() ? g_strdup(g_get_prgname()) : g_strdup("<unknown program>");
+}
+
+static void
+xfce_sm_client_get_property(GObject *obj,
+                            guint property_id,
+                            GValue *value,
+                            GParamSpec *pspec)
+{
+    XfceSMClient *sm_client = XFCE_SM_CLIENT(obj);
+
+    switch(property_id) {
+        case PROP_SESSION_CONNECTION:
+#ifdef HAVE_LIBSM
+            g_value_set_pointer(value, sm_client->priv->session_connection);
+#else
+            g_value_set_pointer(value, NULL);
+#endif
+            break;
+            
+        case PROP_STATE:
+            g_value_set_enum(value, sm_client->priv->state);
+            break;
+
+        case PROP_RESUMED:
+            g_value_set_boolean(value, sm_client->priv->resumed);
+            break;
+
+        case PROP_RESTART_STYLE:
+            g_value_set_enum(value, sm_client->priv->restart_style);
+            break;
+
+        case PROP_PRIORITY:
+            g_value_set_char(value, sm_client->priv->priority);
+            break;
+
+        case PROP_CLIENT_ID:
+            g_value_set_string(value, sm_client->priv->client_id);
+            break;
+            
+        case PROP_CURRENT_DIRECTORY:
+            g_value_set_string(value, sm_client->priv->current_directory);
+            break;
+
+        case PROP_PROGRAM:
+            g_value_set_string(value, sm_client->priv->program);
+            break;
+
+        case PROP_CLONE_COMMAND:
+            g_value_set_boxed(value, sm_client->priv->clone_command);
+            break;
+
+        case PROP_RESIGN_COMMAND:
+            g_value_set_boxed(value, sm_client->priv->resign_command);
+            break;
+
+        case PROP_RESTART_COMMAND:
+            g_value_set_boxed(value, sm_client->priv->restart_command);
+            break;
+
+        case PROP_DISCARD_COMMAND:
+            g_value_set_boxed(value, sm_client->priv->discard_command);
+            break;
+
+        case PROP_SHUTDOWN_COMMAND:
+            g_value_set_boxed(value, sm_client->priv->shutdown_command);
+            break;
+
+        case PROP_ARGC:
+        case PROP_ARGV:
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
+    }
+}
+
+static void
+xfce_sm_client_set_property(GObject *obj,
+                            guint property_id,
+                            const GValue *value,
+                            GParamSpec *pspec)
+{
+    XfceSMClient *sm_client = XFCE_SM_CLIENT(obj);
+
+    switch(property_id) {
+        case PROP_RESTART_STYLE:
+            xfce_sm_client_set_restart_style(sm_client,
+                                             g_value_get_enum(value));
+            break;
+
+        case PROP_PRIORITY:
+            xfce_sm_client_set_priority(sm_client, g_value_get_char(value));
+            break;
+
+        case PROP_CLIENT_ID:
+            xfce_sm_client_set_client_id(sm_client, g_value_get_string(value));
+            break;
+
+        case PROP_CURRENT_DIRECTORY:
+            xfce_sm_client_set_current_directory(sm_client,
+                                                 g_value_get_string(value));
+            break;
+
+        case PROP_PROGRAM:
+            xfce_sm_client_set_program(sm_client, g_value_get_string(value));
+            break;
+
+        case PROP_CLONE_COMMAND:
+            xfce_sm_client_set_clone_command(sm_client,
+                                             g_value_get_boxed(value));
+            break;
+
+        case PROP_RESIGN_COMMAND:
+            xfce_sm_client_set_resign_command(sm_client,
+                                              g_value_get_boxed(value));
+            break;
+
+        case PROP_RESTART_COMMAND:
+            xfce_sm_client_set_restart_command(sm_client,
+                                               g_value_get_boxed(value));
+            break;
+
+        case PROP_DISCARD_COMMAND:
+            xfce_sm_client_set_discard_command(sm_client,
+                                               g_value_get_boxed(value));
+            break;
+
+        case PROP_SHUTDOWN_COMMAND:
+            xfce_sm_client_set_shutdown_command(sm_client,
+                                                g_value_get_boxed(value));
+            break;
+
+        case PROP_ARGC:
+            if(sm_client->priv->argc)
+                g_critical("XfceSMClient: Received argc twice");
+            else {
+                sm_client->priv->argc = g_value_get_uint(value);
+                xfce_sm_client_parse_argv(sm_client);
+            }
+            break;
+
+        case PROP_ARGV:
+            if(sm_client->priv->argv)
+                g_critical("XfceSMClient: Received argv twice");
+            else {
+                sm_client->priv->argv = g_value_dup_boxed(value);
+                xfce_sm_client_parse_argv(sm_client);
+            }
+            break;
+
+        case PROP_SESSION_CONNECTION:
+        case PROP_STATE:
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
+    }
+}
+
+static GObject *
+xfce_sm_client_constructor(GType type,
+                           guint n_construct_params,
+                           GObjectConstructParam *construct_params)
+{
+    if(sm_client_singleton)
+        return G_OBJECT(sm_client_singleton);
+
+    return G_OBJECT_CLASS(xfce_sm_client_parent_class)->constructor(type,
+                                                                    n_construct_params,
+                                                                    construct_params);
+}
+
+static void
+xfce_sm_client_finalize(GObject *obj)
+{
+    XfceSMClient *sm_client = XFCE_SM_CLIENT(obj);
+
+    g_assert(sm_client == sm_client_singleton);
+    sm_client_singleton = NULL;
+
+#ifdef HAVE_LIBSM
+    if(sm_client->priv->session_connection)
+        xfce_sm_client_disconnect(sm_client);
+#endif
+
+    g_free(sm_client->priv->state_file);
+
+    g_free(sm_client->priv->client_id);
+    g_free(sm_client->priv->current_directory);
+    g_free(sm_client->priv->program);
+    g_strfreev(sm_client->priv->clone_command);
+    g_strfreev(sm_client->priv->resign_command);
+    g_strfreev(sm_client->priv->restart_command);
+    g_strfreev(sm_client->priv->discard_command);
+    g_strfreev(sm_client->priv->shutdown_command);
+
+    g_strfreev(sm_client->priv->argv);
+
+    G_OBJECT_CLASS(xfce_sm_client_parent_class)->finalize(obj);
+}
+
+
+
+static inline const gchar *
+str_from_state(XfceSMClientState state)
+{
+    switch(state) {
+        case XFCE_SM_CLIENT_STATE_IDLE:
+            return "IDLE";
+        case XFCE_SM_CLIENT_STATE_SAVING_PHASE_1:
+            return "SAVING_PHASE_1";
+        case XFCE_SM_CLIENT_STATE_WAITING_FOR_PHASE_2:
+            return "WAITING_FOR_PHASE_2";
+        case XFCE_SM_CLIENT_STATE_SAVING_PHASE_2:
+            return "SAVING_PHASE_2";
+        case XFCE_SM_CLIENT_STATE_WAITING_FOR_INTERACT:
+            return "WAITING_FOR_INTERACT";
+        case XFCE_SM_CLIENT_STATE_INTERACTING:
+            return "INTERACTING";
+        case XFCE_SM_CLIENT_STATE_FROZEN:
+            return "FROZEN";
+        case XFCE_SM_CLIENT_STATE_DISCONNECTED:
+            return "DISCONNECTED";
+        case XFCE_SM_CLIENT_STATE_REGISTERING:
+            return "REGISTERING";
+        default:
+            return "(unknown)";
+    }   
+}
+
+static void
+xfce_sm_client_set_client_id(XfceSMClient *sm_client,
+                             const gchar *client_id)
+{
+    if(!xfce_safe_strcmp(sm_client->priv->client_id, client_id))
+        return;
+
+    g_free(sm_client->priv->client_id);
+    sm_client->priv->client_id = g_strdup(client_id);
+
+    g_object_notify(G_OBJECT(sm_client), "client-id");
+}
+
+static void
+xfce_sm_client_parse_argv(XfceSMClient *sm_client)
+{
+    guint argc = 0;
+    gchar **argv = NULL;
+    gchar **clone_command = NULL;
+    guint clone_argc = 0;
+    const gchar *client_id = NULL;
+    int i;
+
+    if(sm_client->priv->argc == 0 || !sm_client->priv->argv)
+        return;
+
+    argc = sm_client->priv->argc;
+    argv = sm_client->priv->argv;
+
+    clone_command = g_new0(gchar *, argc + 1);
+
+    for(i = 0; i < argc; ++i) {
+        if(!g_ascii_strncasecmp(argv[i], SM_ID_ARG, strlen(SM_ID_ARG))) {
+            if(argv[i][strlen(SM_ID_ARG)] == '=')
+                client_id = &(argv[i][strlen(SM_ID_ARG)+1]);
+            else
+                client_id = argv[++i];
+        } else
+            clone_command[clone_argc++] = argv[i];
+    }
+
+    if(client_id)
+        xfce_sm_client_set_client_id(sm_client, client_id);
+    xfce_sm_client_set_restart_command(sm_client, argv);
+    xfce_sm_client_set_clone_command(sm_client, clone_command);
+
+    g_free(clone_command);
+
+    sm_client->priv->argc = 0;
+    g_strfreev(sm_client->priv->argv);
+    sm_client->priv->argv = NULL;
+}
+
+#ifdef HAVE_LIBSM
+
+static void
+xfce_sm_client_set_state(XfceSMClient *sm_client,
+                         XfceSMClientState new_state)
+{
+    XfceSMClientState old_state = sm_client->priv->state;
+
+    if(G_UNLIKELY(old_state == new_state))
+        return;
+
+    g_debug("XfceSMClient: %s -> %s", str_from_state(old_state),
+            str_from_state(new_state));
+
+    sm_client->priv->state = new_state;
+
+    g_signal_emit(G_OBJECT(sm_client), signals[SIG_STATE_CHANGED], 0,
+                  old_state, new_state);
+}
+
+static inline char
+xsmp_restart_style_from_enum(XfceSMClientRestartStyle style)
+{
+    switch(style) {
+        case XFCE_SM_CLIENT_RESTART_ANYWAY:
+            return SmRestartAnyway;
+        case XFCE_SM_CLIENT_RESTART_IMMEDIATELY:
+            return SmRestartImmediately;
+        case XFCE_SM_CLIENT_RESTART_NEVER:
+            return SmRestartNever;
+        case XFCE_SM_CLIENT_RESTART_IF_RUNNING:
+        default:
+            return SmRestartIfRunning;
+    }
+}
+
+
+static void xsmp_save_phase_2(SmcConn smc_conn,
+                              SmPointer client_data);
+static void xsmp_interact(SmcConn smc_conn,
+                          SmPointer client_data);
+static void xsmp_shutdown_cancelled(SmcConn smc_conn,
+                                    SmPointer client_data);
+static void xsmp_save_complete(SmcConn smc_conn,
+                               SmPointer client_data);
+static void xsmp_die(SmcConn smc_conn,
+                     SmPointer client_data);
+static void xsmp_save_yourself(SmcConn smc_conn,
+                               SmPointer client_data,
+                               int save_style,
+                               Bool shutdown,
+                               int interact_style,
+                               Bool fast);
+
+static void xfce_sm_client_set_clone_restart_commands(XfceSMClient *sm_client);
+
+
+static IceIOErrorHandler xsmp_ice_installed_handler = NULL;
+
+
+/* This is called when data is available on an ICE connection.  */
+static gboolean
+xsmp_process_ice_messages(GIOChannel *channel,
+                          GIOCondition condition,
+                          gpointer client_data)
+{
+    IceConn connection = (IceConn)client_data;
+    IceProcessMessagesStatus status;
+
+    status = IceProcessMessages(connection, NULL, NULL);
+    if(status == IceProcessMessagesIOError) {
+        g_warning("Disconnected from session manager.");
+        /* We were disconnected */
+        IceSetShutdownNegotiation(connection, False);
+        IceCloseConnection(connection);
+    }
+
+    return TRUE;
+}
+
+/* This is called when a new ICE connection is made.  It arranges for
+   the ICE connection to be handled via the event loop.  */
+static void
+xsmp_new_ice_connection(IceConn connection,
+                        IcePointer client_data,
+                        Bool opening,
+                        IcePointer *watch_data)
+{
+    guint input_id;
+
+    if(opening) {
+        /* Make sure we don't pass on these file descriptors to any
+         * exec'ed children
+         */
+        GIOChannel *channel;
+
+        fcntl(IceConnectionNumber(connection), F_SETFD,
+              fcntl(IceConnectionNumber(connection), F_GETFD) | FD_CLOEXEC);
+
+        channel = g_io_channel_unix_new(IceConnectionNumber(connection));
+
+        input_id = g_io_add_watch(channel,
+                                  G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
+                                  xsmp_process_ice_messages, connection);
+
+        g_io_channel_unref(channel);
+
+        *watch_data = (IcePointer)GUINT_TO_POINTER(input_id);
+    } else {
+        input_id = GPOINTER_TO_UINT((gpointer)*watch_data);
+
+        g_source_remove(input_id);
+    }
+}
+
+static void
+xsmp_ice_io_error_handler(IceConn connection)
+{
+    g_warning("ICE I/O Error");
+    
+    if(xsmp_ice_installed_handler)
+        xsmp_ice_installed_handler(connection);
+}
+
+static void
+xsmp_ice_init(void)
+{
+    static gsize inited = 0;
+
+    if(g_once_init_enter(&inited)) {
+        IceIOErrorHandler default_handler;
+
+        xsmp_ice_installed_handler = IceSetIOErrorHandler(NULL);
+        default_handler = IceSetIOErrorHandler(xsmp_ice_io_error_handler);
+
+        if(xsmp_ice_installed_handler == default_handler)
+            xsmp_ice_installed_handler = NULL;
+
+        IceAddConnectionWatch(xsmp_new_ice_connection, NULL);
+
+        g_once_init_leave(&inited, 1);
+    }
+}
+
+static void
+xfce_sm_client_handle_save_yourself(XfceSMClient *sm_client,
+                                    gboolean do_quit_requested,
+                                    int dialog_type,
+                                    gboolean do_save_state)
+{
+    if(do_quit_requested
+       && g_signal_has_handler_pending(G_OBJECT(sm_client),
+                                       signals[SIG_QUIT_REQUESTED],
+                                       0, FALSE))
+    {
+        Status status;
+
+        status = SmcInteractRequest(sm_client->priv->session_connection,
+                                    dialog_type, xsmp_interact,
+                                    (SmPointer)sm_client);
+
+        if(status) {
+            xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_WAITING_FOR_INTERACT);
+            sm_client->priv->needs_save_state = do_save_state;
+            /* we want save-state to happen *after* quit-requested if we're
+             * doing both, but we can't do quit-requested until we hear
+             * about our interact request */
+            return;
+        } else {
+            /* if the interact request failed, fall through and at least
+             * attempt to save state below if appropriate */
+            g_warning("SmcInteractRequest failed!");
+        }
+    }
+
+    if(do_save_state)
+        g_signal_emit(G_OBJECT(sm_client), signals[SIG_SAVE_STATE], 0, NULL);
+
+    if(sm_client->priv->shutdown_cancelled) {
+        /* this is a slightly bizarre case that probably won't happen.
+         * if we got to this point, then we didn't do quit-requested,
+         * but the system was shutting down, and then it was later
+         * cancelled.  since we never did a quit-requested, the client
+         * probably won't expect a quit-cancelled, so we do nothing here. */
+        sm_client->priv->shutdown_cancelled = FALSE;
+        xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_IDLE);
+    } else {
+        /* otherwise, we're just done with the SaveYourself here */
+        SmcSaveYourselfDone(sm_client->priv->session_connection, True);
+        /* the XSMP spec state diagram says to go right back to IDLE after a
+         * non-shutdown SaveYourself, but everything else in the spec disagrees:
+         * we need to wait for a SaveComplete before going back to IDLE */
+        xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_FROZEN);
+    }
+}
+
+static void
+xsmp_save_phase_2(SmcConn smc_conn,
+                  SmPointer client_data)
+{
+    XfceSMClient *sm_client = XFCE_SM_CLIENT(client_data);
+
+    TRACE("entering");
+
+    /* As a simplification, we won't support interacting in save phase 2,
+     * even though XSMP allows an interact request (errors only) from
+     * phase 2.  In our SM client's terminolgy, we support save-state
+     * but not quit-requested for phase 2. */
+
+    xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_SAVING_PHASE_2);
+
+    g_signal_emit(G_OBJECT(sm_client), signals[SIG_SAVE_STATE_EXTENDED], 0, NULL);
+
+    SmcSaveYourselfDone(sm_client->priv->session_connection, True);
+    /* the XSMP spec state diagram says to go right back to IDLE after a
+     * non-shutdown SaveYourself, but everything else in the spec disagrees:
+     * we need to wait for a SaveComplete before going back to IDLE */
+    xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_FROZEN);
+
+    if(sm_client->priv->shutdown_cancelled) {
+        /* if we get here, we received ShutdownCancelled while in a recursive
+         * invocation of the main loop in save-state-extended.  in this case, we
+         * go back to idle and send quit-cancelled. */
+        sm_client->priv->shutdown_cancelled = FALSE;
+
+        xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_IDLE);
+        g_signal_emit(G_OBJECT(sm_client), signals[SIG_QUIT_CANCELLED], 0,
+                      NULL);
+    }
+}
+
+static void
+xsmp_save_yourself(SmcConn smc_conn,
+                   SmPointer client_data,
+                   int save_style,
+                   Bool shutdown,
+                   int interact_style,
+                   Bool fast)
+{
+    XfceSMClient *sm_client = XFCE_SM_CLIENT(client_data);
+    gboolean do_save_state, do_quit_requested;
+
+    TRACE("entering (save_style=%s, shutdown=%s, interact_style=%s, fast=%s",
+          save_style == SmSaveGlobal ? "global" : (save_style == SmSaveLocal ? "local" : "both"),
+          shutdown ? "true" : "false",
+          interact_style == SmInteractStyleNone ? "none" : (interact_style == SmInteractStyleErrors ? "errors" : "any"),
+          fast ? "true" : "false");
+
+    /* The first SaveYourself after registering for the first time
+     * is a special case (SM specs 7.2).
+     */
+    if(sm_client->priv->state == XFCE_SM_CLIENT_STATE_REGISTERING) {
+        if(save_style == SmSaveLocal
+           && interact_style == SmInteractStyleNone
+           && !shutdown
+           && !fast)
+        {
+            xfce_sm_client_set_clone_restart_commands(sm_client);
+            SmcSaveYourselfDone(sm_client->priv->session_connection, True);
+            /* XSMP spec state diagram says idle, but the rest of the spec disagrees */
+            xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_FROZEN);
+        } else {
+            g_warning("Initial SaveYourself had unexpected parameters");
+            xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_IDLE);
+        }
+
+        return;
+    }
+
+    if(sm_client->priv->state != XFCE_SM_CLIENT_STATE_IDLE) {
+        g_warning("Got SaveYourself while in phase %s, ignoring",
+                  str_from_state(sm_client->priv->state));
+        SmcSaveYourselfDone(sm_client->priv->session_connection, True);
+        xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_FROZEN);
+        return;
+    }
+
+    xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_SAVING_PHASE_1);
+
+    /* Most of this logic is taken from EggSMClient.  There are many
+     * combinations of parameters to the SaveYourself request, but we're
+     * going to boil them down to a few possibilities:
+     *
+     * 1.  Do nothing if:
+     *     a) Global save and we're not shutting down, as this is
+     *        somewhat pointless.
+     *     b) Global save and we're shutting down, but the SM isn't
+     *        going to let us interact, because we can't do anything
+     *        anyway.
+     * 2.  Emit save-state if:
+     *     a) Local (or Both) save and we're not shutting down.  We
+     *        ignore the Global part of the Both save since prompting
+     *        to save files when not shutting down is stupid.
+     *     b) Local (or Both) save and we *are* shutting down, but
+     *        aren't allowed to interact.  Here a save-state is just
+     *        the best we can do.  We ignore the Global part of a
+     *        Both save because it's unlikely we can (or should) save
+     *        without user interaction.
+     * 3.  Emit quit-requested if:
+     *     a) Global save, we're shutting down, and we're allowed to
+     *        interact.
+     * 4.  Emit quit-requested followed by save-state if:
+     *     a) Local or Both save, we're shutting down, and we're
+     *        allowed to interact.  If it's a Local save, we promote
+     *        it to a Both save since shutting down without asking
+     *        the user to save their work when it's allowed is rude.
+     *
+     * We ignore the 'fast' parameter.  I really don't expect any
+     * client to do anything differently based on its value.
+     */
+
+    do_quit_requested = (shutdown && interact_style != SmInteractStyleNone);
+    do_save_state = (save_style != SmSaveGlobal);
+
+    xfce_sm_client_handle_save_yourself(sm_client, do_quit_requested,
+                                        (interact_style == SmInteractStyleAny
+                                         ? SmDialogNormal : SmDialogError),
+                                        do_save_state);
+}
+
+static void
+xsmp_die(SmcConn smc_conn,
+         SmPointer client_data)
+{
+    XfceSMClient *sm_client = XFCE_SM_CLIENT(client_data);
+
+    TRACE("entering");
+
+    xfce_sm_client_disconnect(sm_client);
+
+    /* here we give the app a chance to gracefully quit.  if the app
+     * has indicated it doesn't want to (by not connecting to the quit
+     * signal), then we force the exit here. */
+
+    if(g_signal_has_handler_pending(G_OBJECT(sm_client), signals[SIG_QUIT],
+                                    0, FALSE))
+    {
+        g_signal_emit(G_OBJECT(sm_client), signals[SIG_QUIT], 0, NULL);
+    } else
+        exit(0);
+}
+
+static void
+xsmp_save_complete(SmcConn smc_conn,
+                   SmPointer client_data)
+{
+    XfceSMClient *sm_client = XFCE_SM_CLIENT(client_data);
+
+    TRACE("entering");
+
+    if(sm_client->priv->state != XFCE_SM_CLIENT_STATE_FROZEN)
+        g_warning("Got SaveComplete in unexpected state");
+
+    xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_IDLE);
+}
+
+static void
+xsmp_shutdown_cancelled(SmcConn smc_conn,
+                        SmPointer client_data)
+{
+    XfceSMClient *sm_client = XFCE_SM_CLIENT(client_data);
+
+    TRACE("entering");
+
+    switch(sm_client->priv->state) {
+        case XFCE_SM_CLIENT_STATE_FROZEN:
+        case XFCE_SM_CLIENT_STATE_WAITING_FOR_PHASE_2:
+            /* if the client has already handled quit-requested, we just go
+             * back to idle and inform the client that shutdown was
+             * cancelled. */
+            xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_IDLE);
+            g_signal_emit(G_OBJECT(sm_client), signals[SIG_QUIT_CANCELLED],
+                          0, NULL);
+            break;
+
+        case XFCE_SM_CLIENT_STATE_WAITING_FOR_INTERACT:
+            /* if we're waiting to interact (thus waiting to send
+             * quit-requested), we just cancel that, and in this case we finish
+             * the SaveYourself and move on.  we don't inform the client of
+             * the cancellation since we haven't gotten to inform them about
+             * the shutdown in the first place. */
+            SmcSaveYourselfDone(sm_client->priv->session_connection, True);
+            xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_IDLE);
+            break;
+
+        case XFCE_SM_CLIENT_STATE_INTERACTING:
+        case XFCE_SM_CLIENT_STATE_SAVING_PHASE_1:
+        case XFCE_SM_CLIENT_STATE_SAVING_PHASE_2:
+            /* this should only happen if the client is currently *inside* one
+             * of the quit-requested, save-state, or save-state-extended
+             * handlers and is presumably inside a recursive invocation of the
+             * main loop.  we shouldn't inform them of anything just yet, but
+             * we'll set a flag so we can do so when they return. */
+            sm_client->priv->shutdown_cancelled = TRUE;
+            break;
+
+        default:
+            g_warning("got ShutdownCancelled while in unexpected state");
+            xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_IDLE);
+            break;
+    }
+}
+
+static void
+xsmp_interact(SmcConn smc_conn,
+              SmPointer client_data)
+{
+    XfceSMClient *sm_client = XFCE_SM_CLIENT(client_data);
+    gboolean cancel = FALSE;
+
+    TRACE("entering");
+
+    /* since we don't support interacting during phase 2, the only time
+     * we should receive an interact message is during a normal save
+     * yourself. */
+
+    if(sm_client->priv->state != XFCE_SM_CLIENT_STATE_WAITING_FOR_INTERACT) {
+        g_warning("Got Interact message when not waiting for one");
+        /* FIXME: not sure if this is the best response to this.  we might
+         * want to actually go through with sending quit-requested etc. */
+        SmcInteractDone(sm_client->priv->session_connection, False);
+        SmcSaveYourselfDone(sm_client->priv->session_connection, True);
+        xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_FROZEN);
+        return;
+    }
+
+    xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_INTERACTING);
+
+    /* at this point we're ready to emit quit-requested */
+    g_signal_emit(G_OBJECT(sm_client), signals[SIG_QUIT_REQUESTED], 0,
+                  &cancel);
+
+    if(sm_client->priv->shutdown_cancelled) {
+        /* if we get here, we received ShutdownCancelled while in a recursive
+         * invocation of the main loop in quit-requested.  in this case, we
+         * go back to idle and send quit-cancelled. */
+        sm_client->priv->shutdown_cancelled = FALSE;
+        cancel = TRUE;
+
+        xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_IDLE);
+        g_signal_emit(G_OBJECT(sm_client), signals[SIG_QUIT_CANCELLED], 0,
+                      NULL);
+    } else {
+        /* we only send InteractDone if we didn't get ShutdownCancelled */
+        xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_FROZEN);
+        SmcInteractDone(sm_client->priv->session_connection, cancel);
+    }
+
+    if(cancel) {
+        /* if we requested (or got) a shutdown cancellation, we skip the
+         * save-state signal. */
+        sm_client->priv->needs_save_state = FALSE;
+    } else if(sm_client->priv->needs_save_state) {
+        /* if we still have a pending save-state, send that now */
+        sm_client->priv->needs_save_state = FALSE;
+        g_signal_emit(G_OBJECT(sm_client), signals[SIG_SAVE_STATE], 0, NULL);
+
+        if(sm_client->priv->shutdown_cancelled) {
+            /* this is exceedingly unlikely, but it could happen here too */
+            sm_client->priv->shutdown_cancelled = FALSE;
+            cancel = TRUE;
+
+            xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_IDLE);
+            g_signal_emit(G_OBJECT(sm_client), signals[SIG_QUIT_CANCELLED], 0,
+                          NULL);
+        }
+    }
+
+    /* if the app also wants a phase 2 save, we request it now */
+    if(!cancel && g_signal_has_handler_pending(G_OBJECT(sm_client),
+                                               signals[SIG_SAVE_STATE_EXTENDED],
+                                               0, FALSE))
+    {
+        Status status;
+
+        status = SmcRequestSaveYourselfPhase2(sm_client->priv->session_connection,
+                                              xsmp_save_phase_2,
+                                              (SmPointer)sm_client);
+        if(status) {
+            xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_WAITING_FOR_PHASE_2);
+            return;
+        }
+    }
+
+    /* finally, signal to the SM that we're done.  we also fall through and
+     * bail to this if a phase2 request failed for some reason. */
+    SmcSaveYourselfDone(sm_client->priv->session_connection, True);
+}
+
+static void
+xfce_sm_client_set_property_from_command(XfceSMClient *sm_client,
+                                         const char *property_name,
+                                         gchar **command,
+                                         gint alter_sm_id)
+{
+    SmProp prop, *props[1];
+    gchar **args;
+    gint i;
+    gint argc;
+    SmPropValue *vals;
+
+    if(!command || !sm_client->priv->session_connection)
+        return;
+
+    args = command;
+    for(argc = 0; *args; args++) {
+        if(alter_sm_id == SM_ARG_APPEND
+           && !g_ascii_strncasecmp(*args, SM_ID_ARG, strlen(SM_ID_ARG)))
+        {
+            alter_sm_id = FALSE;
+        }
+        argc++;
+    }
+
+    if(alter_sm_id == SM_ARG_APPEND)
+        argc += 2;
+    vals = g_new(SmPropValue, argc);
+
+    i = 0;
+    while(*command) {
+        if(alter_sm_id == SM_ARG_REMOVE
+           && !g_ascii_strncasecmp(*command, SM_ID_ARG, strlen(SM_ID_ARG)))
+        {
+            /* skip the argument after SM_ID_ARG */
+            if(command[1])
+                command++;
+        } else {
+            vals[i].length = strlen(*command);
+            vals[i++].value = *command;
+        }
+        command++;
+    }
+
+    if(alter_sm_id == SM_ARG_APPEND) {
+        vals[i].length = strlen(SM_ID_ARG);
+        vals[i++].value = SM_ID_ARG;
+        vals[i].length = strlen(sm_client->priv->client_id);
+        vals[i++].value = sm_client->priv->client_id;
+    }
+
+    prop.name = (char *)property_name;
+    prop.type = SmLISTofARRAY8;
+    prop.vals = vals;
+    prop.num_vals = i;
+
+    props[0] = ∝
+    SmcSetProperties(sm_client->priv->session_connection, 1, props);
+    g_free(vals);
+}
+
+static void
+xfce_sm_client_set_clone_restart_commands(XfceSMClient *sm_client)
+{
+    /* Restart */
+    xfce_sm_client_set_property_from_command(sm_client, SmRestartCommand,
+                                             sm_client->priv->restart_command,
+                                             SM_ARG_APPEND);
+
+    /* Clone */
+    xfce_sm_client_set_property_from_command(sm_client, SmCloneCommand,
+                                             (sm_client->priv->clone_command
+                                              ? sm_client->priv->clone_command
+                                              : sm_client->priv->restart_command),
+                                             SM_ARG_REMOVE);
+
+    /* Resign */
+    xfce_sm_client_set_property_from_command(sm_client, SmResignCommand,
+                                             sm_client->priv->resign_command,
+                                             FALSE);
+
+    /* Discard */
+    xfce_sm_client_set_property_from_command(sm_client, SmDiscardCommand,
+                                             sm_client->priv->discard_command,
+                                             FALSE);
+
+    /* Shutdown */
+    xfce_sm_client_set_property_from_command(sm_client, SmShutdownCommand,
+                                             sm_client->priv->shutdown_command,
+                                             FALSE);
+}
+
+#endif
+
+
+/**
+ * xfce_sm_client_get_option_group:
+ * @argc: The application's argument count
+ * @argv: The application's argument vector
+ *
+ * Constructs a #GOptionGroup suitable for using with Glib's
+ * command-line option parser, whether it is being used directly,
+ * or indirectly via gtk_init_with_args() or similar.
+ *
+ * This function is a bit sneaky in that it will make a copy of
+ * the program's argc and argv *before* gtk_init() etc. has a chance
+ * to mess around with it, so #XfceSMClient can later construct an
+ * accurate restart command.
+ *
+ * Returns: A new #GOptionGroup
+ **/
+GOptionGroup *
+xfce_sm_client_get_option_group(guint argc,
+                                gchar **argv)
+{
+    const GOptionEntry entries[] = {
+        { "sm-client-id", 0, 0, G_OPTION_ARG_STRING, &startup_options.client_id, N_("Session management client ID"), N_("ID") },
+        { "sm-client-disable", 0, 0, G_OPTION_ARG_NONE, &startup_options.sm_disable, N_("Disable session management"), NULL },
+        { NULL, },
+    };
+    GOptionGroup *group = NULL;
+
+    startup_options.argc = argc;
+    g_strfreev(startup_options.argv);
+    if(argv)
+        startup_options.argv = g_strdupv(argv);
+
+    group = g_option_group_new("sm-client", _("Session management options"),
+                               _("Show session management options"), NULL,
+                               NULL);
+    g_option_group_add_entries(group, entries);
+    g_option_group_set_translation_domain(group, GETTEXT_PACKAGE);
+
+    return group;
+}
+
+/**
+ * xfce_sm_client_get:
+ *
+ * Gets the application's SM client instance.  This is best
+ * used with xfce_sm_client_get_option_group() above (and using
+ * the returned #GOptionGroup with g_option_context_parse()), as the
+ * command line parsing will figure out many of the SM client's
+ * required property values for you.
+ *
+ * If you are not using Gtk or Glib's command-line option parser,
+ * take a look at xfce_sm_client_new_with_argv() and
+ * xfce_sm_client_new_full().
+ *
+ * If you have already created an #XfceSMClient instance using
+ * this function or one of the xfce_sm_client_new_*() functions,
+ * this will return the same instance.
+ *
+ * Returns: A new or existing #XfceSMClient
+ **/
+XfceSMClient *
+xfce_sm_client_get()
+{
+    return g_object_new(XFCE_TYPE_SM_CLIENT,
+                        "argc", startup_options.argc,
+                        "argv", startup_options.argv,
+                        "client-id", startup_options.client_id,
+                        NULL);
+}
+
+/**
+ * xfce_sm_client_new_with_argv:
+ * @argc: The number of arguments passed to main()
+ * @argv: The argument vector passed to main()
+ * @restart_style: An #XfceSMClientRestartStyle
+ * @priority: A restart priority
+ *
+ * Creates a new #XfceSMClient instance.  It attempts to
+ * set all required properties using the app's command line.
+ * Note that this function does not actually connect to the session
+ * manager, so other actions can be taken (such as setting custom
+ * properties or connecting signals) before calling
+ * xfce_sm_client_connect().
+ *
+ * If you are using Gtk or Glib's command-line option parser,
+ * it is recommended that you use xfce_sm_client_get_option_group()
+ * and xfce_sm_client_get() instead.
+ *
+ * Returns: A new #XfceSMClient instance
+ **/
+XfceSMClient *
+xfce_sm_client_new_with_argv(guint argc,
+                             gchar **argv,
+                             XfceSMClientRestartStyle restart_style,
+                             gchar priority)
+{
+    return g_object_new(XFCE_TYPE_SM_CLIENT,
+                        "argc", argc,
+                        "argv", argv,
+                        "restart-style", restart_style,
+                        "priority", priority,
+                        NULL);
+}
+
+/**
+ * xfce_sm_client_get_full:
+ * @restart_style: An XfceSMClientRestartStyle
+ * @priority: A restart priority
+ * @resumed_client_id: The client id used in the previous session
+ * @program: A program identifier string
+ * @current_directory: The application's working directory
+ * @restart_command: A command that can resume the application's
+ *                   saved state
+ * @clone_command: A command that can start a fresh copy of the
+ *                 application
+ * @discard_command: A command that cleans up the application's
+ *                   state when removed from the session
+ * @resign_command: A command that cleans up the application's
+ *                  state when removed from restart-always state
+ * @shutdown_command: A command run on shutdown for applications
+ *                    with the restart-always style
+ *
+ * Creates a new SM client instance, allowing the application
+ * fine-grained control over the initial properties set.
+ * Note that this function does not actually connect to the session
+ * manager, so other actions can be taken (such as setting custom
+ * properties or connecting signals) before calling
+ * xfce_sm_client_connect().
+ *
+ * It is recommended to use xfce_sm_client_get_with_argv(), or,
+ * if you are using Gtk or Glib's command-line option parser,
+ * xfce_sm_client_get_option_group() and xfce_sm_client_get() instead.
+ *
+ * Returns: A new #XfceSMClient instance
+ **/
+XfceSMClient *
+xfce_sm_client_new_full(XfceSMClientRestartStyle restart_style,
+                        gchar priority,
+                        const gchar *resumed_client_id,
+                        const gchar *program,
+                        const gchar *current_directory,
+                        const gchar **restart_command,
+                        const gchar **clone_command,
+                        const gchar **discard_command,
+                        const gchar **resign_command,
+                        const gchar **shutdown_command)
+{
+    return g_object_new(XFCE_TYPE_SM_CLIENT,
+                        "restart-style", restart_style,
+                        "priority", priority,
+                        "client-id", resumed_client_id,
+                        "program", program,
+                        "current-directory", current_directory,
+                        "restart-command", restart_command,
+                        "clone-command", clone_command,
+                        "resign-command", resign_command,
+                        "shutdown-command", shutdown_command,
+                        NULL);
+}
+
+/**
+ * xfce_sm_client_connect:
+ * @sm_client: An #XfceSMClient
+ * @error: A #GError location.
+ *
+ * Attempts to connect to the session manager.
+ *
+ * Returns: %TRUE on success, %FALSE otherwise.  If an error
+ *          occurs, @error will be set.
+ **/
+gboolean
+xfce_sm_client_connect(XfceSMClient *sm_client,
+                       GError **error)
+{
+#ifdef HAVE_LIBSM
+    char buf[256] = "";
+    unsigned long mask;
+    SmcCallbacks callbacks;
+    SmProp prop1, prop2, prop3, prop4, prop5, prop6, *props[6];
+    SmPropValue prop1val, prop2val, prop3val, prop4val, prop5val, prop6val;
+    char pid[32];
+    char hint = SmRestartIfRunning;
+    char priority = sm_client->priv->priority;
+    char *given_client_id = NULL;
+#endif
+
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client), FALSE);
+    g_return_val_if_fail(!error || !*error, FALSE);
+
+    if(startup_options.sm_disable)
+        return TRUE;
+
+#ifdef HAVE_LIBSM
+    xsmp_ice_init();
+
+    mask = SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask
+           | SmcShutdownCancelledProcMask;
+
+    callbacks.save_yourself.callback = xsmp_save_yourself;
+    callbacks.save_yourself.client_data = (SmPointer)sm_client;
+
+    callbacks.die.callback = xsmp_die;
+    callbacks.die.client_data = (SmPointer)sm_client;
+
+    callbacks.save_complete.callback = xsmp_save_complete;
+    callbacks.save_complete.client_data = (SmPointer)sm_client;
+
+    callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
+    callbacks.shutdown_cancelled.client_data = (SmPointer)sm_client;
+
+    sm_client->priv->session_connection = SmcOpenConnection(NULL, NULL,
+                                                            SmProtoMajor,
+                                                            SmProtoMinor,
+                                                            mask,
+                                                            &callbacks,
+                                                            (char *)sm_client->priv->client_id,
+                                                            &given_client_id,
+                                                            sizeof(buf)-1,
+                                                            buf);
+
+    if(!sm_client->priv->session_connection) {
+        if(error) {
+            /* FIXME: error domain/code */
+            g_set_error(error, 0, 1,
+                        _("Failed to connect to the session manager: %s"), buf);
+        }
+        return FALSE;
+    } else if(!given_client_id) {
+        if(error) {
+            /* FIXME: error domain/code */
+            g_set_error(error, 0, 1,
+                        _("Session manager did not return a valid client id"));
+        }
+        return FALSE;
+    }
+
+    if(sm_client->priv->client_id
+       && !strcmp(sm_client->priv->client_id, given_client_id))
+    {
+        xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_IDLE);
+        sm_client->priv->resumed = TRUE;
+        g_object_notify(G_OBJECT(sm_client), "resumed");
+    } else {
+        xfce_sm_client_set_client_id(sm_client, given_client_id);
+        xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_REGISTERING);
+    }
+
+    free(given_client_id);
+
+    gdk_set_sm_client_id(sm_client->priv->client_id);
+
+    hint = xsmp_restart_style_from_enum(sm_client->priv->restart_style);
+
+    prop1.name = SmProgram;
+    prop1.type = SmARRAY8;
+    prop1.num_vals = 1;
+    prop1.vals = &prop1val;
+    prop1val.value = sm_client->priv->program;
+    prop1val.length = strlen(sm_client->priv->program);
+
+    prop2.name = SmUserID;
+    prop2.type = SmARRAY8;
+    prop2.num_vals = 1;
+    prop2.vals = &prop2val;
+    prop2val.value = (char *)g_get_user_name();
+    prop2val.length = strlen(prop2val.value);
+
+    prop3.name = SmRestartStyleHint;
+    prop3.type = SmCARD8;
+    prop3.num_vals = 1;
+    prop3.vals = &prop3val;
+    prop3val.value = &hint;
+    prop3val.length = 1;
+
+    g_snprintf(pid, sizeof(pid), "%d", getpid());
+    prop4.name = SmProcessID;
+    prop4.type = SmARRAY8;
+    prop4.num_vals = 1;
+    prop4.vals = &prop4val;
+    prop4val.value = pid;
+    prop4val.length = strlen(prop4val.value);
+
+    prop5.name = SmCurrentDirectory;
+    prop5.type = SmARRAY8;
+    prop5.num_vals = 1;
+    prop5.vals = &prop5val;
+    if(sm_client->priv->current_directory)
+        prop5val.value = (char *)sm_client->priv->current_directory;
+    else
+        prop5val.value = (char *)xfce_get_homedir();
+    prop5val.length = strlen(prop5val.value);
+
+    prop6.name = "_GSM_Priority";
+    prop6.type = SmCARD8;
+    prop6.num_vals = 1;
+    prop6.vals = &prop6val;
+    prop6val.value = &priority;
+    prop6val.length = 1;
+
+    props[0] = &prop1;
+    props[1] = &prop2;
+    props[2] = &prop3;
+    props[3] = &prop4;
+    props[4] = &prop5;
+    props[5] = &prop6;
+
+    SmcSetProperties(sm_client->priv->session_connection, 6, props);
+
+    g_object_notify(G_OBJECT(sm_client), "session-connection");
+#endif
+
+    return TRUE;
+}
+
+/**
+ * xfce_sm_client_disconnect:
+ * @sm_client: An #XfceSMClient
+ *
+ * Disconnects the application from the session manager.
+ *
+ * Note: This may not remove the application from the saved
+ * session (if any) if the user later does not choose to save
+ * the session when logging out.
+ *
+ **/
+void
+xfce_sm_client_disconnect(XfceSMClient *sm_client)
+{
+    if(startup_options.sm_disable)
+        return;
+
+#ifdef HAVE_LIBSM
+    if(G_UNLIKELY(!sm_client->priv->session_connection)) {
+        g_warning("%s() called with no session connection", G_STRFUNC);
+        return;
+    }
+
+    if(sm_client->priv->restart_style == XFCE_SM_CLIENT_RESTART_IMMEDIATELY)
+        xfce_sm_client_set_restart_style(sm_client, XFCE_SM_CLIENT_RESTART_IF_RUNNING);
+
+    SmcCloseConnection(sm_client->priv->session_connection, 0, NULL);
+    sm_client->priv->session_connection = NULL;
+    gdk_set_sm_client_id(NULL);
+
+    g_object_notify(G_OBJECT(sm_client), "session-connection");
+    xfce_sm_client_set_state(sm_client, XFCE_SM_CLIENT_STATE_DISCONNECTED);
+#endif
+}
+
+/**
+ * xfce_sm_client_is_resumed:
+ * @sm_client: An #XfceSMClient
+ *
+ * Determines whether the application was resumed from a previous
+ * session, or if the application has been started fresh with no
+ * state information associated with it.
+ *
+ * Returns: %TRUE if resumed from a previous session, %FALSE otherwise
+ **/
+gboolean
+xfce_sm_client_is_resumed(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client), FALSE);
+    return sm_client->priv->resumed;
+}
+
+/**
+ * xfce_sm_client_request_shutdown:
+ * @sm_client: An #XfceSMClient
+ * @shutdown_hint: The type of shutdown requested
+ *
+ * Sends a request to the session manager to end the session.
+ * Depending on @hint, the session manager may prompt for a
+ * certain action (log out, halt, reboot, etc.) or may take the
+ * requested action without user intervention.
+ *
+ * Note: The session manager may or may not support all requested
+ * actions, and is also free to ignore the requested action.
+ **/
+void
+xfce_sm_client_request_shutdown(XfceSMClient *sm_client,
+                                XfceSMClientShutdownHint shutdown_hint)
+{
+    g_return_if_fail(XFCE_IS_SM_CLIENT(sm_client));
+
+    if(startup_options.sm_disable)
+        return;
+
+    /* TODO: support xfce4-session's DBus interface */
+
+#ifdef HAVE_LIBSM
+    if(G_LIKELY(sm_client->priv->session_connection)) {
+        SmcRequestSaveYourself(sm_client->priv->session_connection, SmSaveBoth,
+                               True, SmInteractStyleAny, False, True);
+    }
+#endif
+}
+
+/**
+ * xfce_sm_client_set_restart_style:
+ * @sm_client: An #XfceSMClient
+ * @restart_style: An #XfceSMClientRestartStyle value
+ *
+ * Sets the restart style hint to @restart_style.
+ **/
+void
+xfce_sm_client_set_restart_style(XfceSMClient *sm_client,
+                                 XfceSMClientRestartStyle restart_style)
+{
+    g_return_if_fail(XFCE_IS_SM_CLIENT(sm_client));
+
+    if(sm_client->priv->restart_style == restart_style)
+        return;
+
+    sm_client->priv->restart_style = restart_style;
+
+#ifdef HAVE_LIBSM
+    if(sm_client->priv->session_connection) {
+        SmProp prop, *props[1];
+        SmPropValue propval;
+        char hint;
+
+        hint = xsmp_restart_style_from_enum(sm_client->priv->restart_style);
+
+        prop.name = SmRestartStyleHint;
+        prop.type = SmCARD8;
+        prop.num_vals = 1;
+        prop.vals = &propval;
+        propval.value = &hint;
+        propval.length = 1;
+        props[0] = ∝
+
+        SmcSetProperties(sm_client->priv->session_connection, 1, props);
+    }
+#endif /* HAVE_LIBSM */
+
+    g_object_notify(G_OBJECT(sm_client), "restart-style");
+}
+
+/**
+ * xfce_sm_client_set_priority:
+ * @sm_client: An #XfceSMClient
+ * @priority: A 8-bit signed priority value
+ *
+ * Sets the startup priority for @sm_client to @priority.  Note
+ * that the default priority for applications is 50; lower values
+ * should be reserved for components of the desktop environment.
+ **/
+void
+xfce_sm_client_set_priority(XfceSMClient *sm_client,
+                            gchar priority)
+{
+    g_return_if_fail(XFCE_IS_SM_CLIENT(sm_client));
+
+    if(sm_client->priv->priority == priority)
+        return;
+
+    sm_client->priv->priority = priority;
+
+#ifdef HAVE_LIBSM
+    if(sm_client->priv->session_connection) {
+        SmProp prop, *props[1];
+        SmPropValue propval;
+
+        prop.name = "_GSM_Priority";
+        prop.type = SmCARD8;
+        prop.num_vals = 1;
+        prop.vals = &propval;
+        propval.value = &sm_client->priv->priority;
+        propval.length = 1;
+        props[0] = ∝
+
+        SmcSetProperties(sm_client->priv->session_connection, 1, props);
+    }
+#endif /* HAVE_LIBSM */
+
+    g_object_notify(G_OBJECT(sm_client), "priority");
+}
+
+/**
+ * xfce_sm_client_set_current_directory:
+ * @sm_client: An #XfceSMClient
+ * @current_directory: A valid path name
+ *
+ * Sets the startup working directory of @sm_client to
+ * @current_directory.  If unset, defaults to the user's
+ * home directory.
+ **/
+void
+xfce_sm_client_set_current_directory(XfceSMClient *sm_client,
+                                     const gchar *current_directory)
+{
+    g_return_if_fail(XFCE_IS_SM_CLIENT(sm_client));
+
+    if(!xfce_safe_strcmp(sm_client->priv->current_directory, current_directory))
+        return;
+
+    if(!current_directory)
+        current_directory = xfce_get_homedir();
+
+    g_free(sm_client->priv->current_directory);
+    sm_client->priv->current_directory = g_strdup(current_directory);
+
+#ifdef HAVE_LIBSM
+    if(sm_client->priv->session_connection) {
+        SmProp prop, *props[1];
+        SmPropValue propval;
+
+        prop.name = SmCurrentDirectory;
+        prop.type = SmARRAY8;
+        prop.num_vals = 1;
+        prop.vals = &propval;
+        propval.value = sm_client->priv->current_directory;
+        propval.length = strlen(propval.value);
+        props[0] = ∝
+
+        SmcSetProperties(sm_client->priv->session_connection, 1, props);
+    }
+#endif /* HAVE_LIBSM */
+
+    g_object_notify(G_OBJECT(sm_client), "current-directory");
+}
+
+/**
+ * xfce_sm_client_set_program:
+ * @sm_client: An XfceSMClient
+ * @program: A program identifier string
+ *
+ * Sets an identifier string for your application.  This string is not
+ * intended to be user-visible, but in some cases may be, so it may be
+ * useful to use something that isn't totally incomprehensible.  If
+ * unset, it will default to the value returned by g_get_prgname().
+ **/
+void
+xfce_sm_client_set_program(XfceSMClient *sm_client,
+                           const gchar *program)
+{
+    g_return_if_fail(XFCE_IS_SM_CLIENT(sm_client));
+
+    if(!xfce_safe_strcmp(sm_client->priv->program, program))
+        return;
+
+    if(!program)
+        program = g_get_prgname() ? g_get_prgname() : "<unknown program>";
+
+    g_free(sm_client->priv->program);
+    sm_client->priv->program = g_strdup(program);
+    
+#ifdef HAVE_LIBSM
+    if(sm_client->priv->session_connection) {
+        SmProp prop, *props[1];
+        SmPropValue propval;
+
+        prop.name = SmProgram;
+        prop.type = SmARRAY8;
+        prop.num_vals = 1;
+        prop.vals = &propval;
+        propval.value = sm_client->priv->program;
+        propval.length = strlen(sm_client->priv->program);
+
+        SmcSetProperties(sm_client->priv->session_connection, 1, props);
+    }
+#endif /* HAVE_LIBSM */
+
+    g_object_notify(G_OBJECT(sm_client), "program");
+}
+
+static gchar **
+copy_command(gchar **command,
+             gchar **value)
+{
+    if(command != value) {
+        g_strfreev(command);
+        command = NULL;
+    }
+
+    if(value)
+        command = g_strdupv(value);
+
+    return command;
+}
+
+/**
+ * xfce_sm_client_set_clone_command:
+ * @sm_client: An #XfceSMClient
+ * @clone_command: An argument vector
+ *
+ * Sets the application's "clone" command, which is used to start
+ * a fresh copy of the application, without restoring any state
+ * from a previous run.
+ *
+ * If unset, this will default to the restart command, with
+ * any session management related arguments removed.
+ **/
+void
+xfce_sm_client_set_clone_command(XfceSMClient *sm_client,
+                                 gchar **clone_command)
+{
+    g_return_if_fail(XFCE_IS_SM_CLIENT(sm_client));
+    sm_client->priv->clone_command = copy_command(sm_client->priv->clone_command,
+                                                  clone_command);
+#ifdef HAVE_LIBSM
+    xfce_sm_client_set_property_from_command(sm_client, SmCloneCommand,
+                                             sm_client->priv->clone_command,
+                                             SM_ARG_REMOVE);
+#endif
+    g_object_notify(G_OBJECT(sm_client), "clone-command");
+}
+
+/**
+ * xfce_sm_client_set_resign_command:
+ * @sm_client: An #XfceSMClient
+ * @resign_command: An argument vector
+ *
+ * Sets the application's "resign" command, which is used to clean
+ * up saved state when an appliction with restart style "always"
+ * is removed from the session.
+ *
+ * If unset, defaults to %NULL.
+ **/
+void
+xfce_sm_client_set_resign_command(XfceSMClient *sm_client,
+                                  gchar **resign_command)
+{
+    g_return_if_fail(XFCE_IS_SM_CLIENT(sm_client));
+    sm_client->priv->resign_command = copy_command(sm_client->priv->resign_command,
+                                                   resign_command);
+#ifdef HAVE_LIBSM
+    xfce_sm_client_set_property_from_command(sm_client, SmResignCommand,
+                                             sm_client->priv->resign_command,
+                                             FALSE);
+#endif
+    g_object_notify(G_OBJECT(sm_client), "resign-command");
+}
+
+/**
+ * xfce_sm_client_set_restart_command:
+ * @sm_client: An #XfceSMClient
+ * @restart_command: An argument vector
+ *
+ * Sets the application's "restart" command, which is used to restart
+ * the application and restore any saved state from the previous
+ * run.
+ *
+ * If unset, defaults to the command used to start this instance
+ * of the application, with session management related arguments
+ * added (if not already present).
+ **/
+void
+xfce_sm_client_set_restart_command(XfceSMClient *sm_client,
+                                   gchar **restart_command)
+{
+    g_return_if_fail(XFCE_IS_SM_CLIENT(sm_client));
+    sm_client->priv->restart_command = copy_command(sm_client->priv->restart_command,
+                                                    restart_command);
+#ifdef HAVE_LIBSM
+    xfce_sm_client_set_property_from_command(sm_client, SmRestartCommand,
+                                             sm_client->priv->restart_command,
+                                             SM_ARG_APPEND);
+#endif
+    g_object_notify(G_OBJECT(sm_client), "restart-command");
+}
+
+/**
+ * xfce_sm_client_set_discard_command:
+ * @sm_client: An #XfceSMClient
+ * @discard_command: An argument vector
+ *
+ * Sets the application's "restart" command, which is used to restart
+ * the application and restore any saved state from the previous
+ * run.
+ *
+ * If unset, defaults to the command used to start this instance
+ * of the application, with session management related arguments
+ * added (if not already present).
+ **/
+void
+xfce_sm_client_set_discard_command(XfceSMClient *sm_client,
+                                   gchar **discard_command)
+{
+    g_return_if_fail(XFCE_IS_SM_CLIENT(sm_client));
+    sm_client->priv->discard_command = copy_command(sm_client->priv->discard_command,
+                                                    discard_command);
+#ifdef HAVE_LIBSM
+    xfce_sm_client_set_property_from_command(sm_client, SmDiscardCommand,
+                                             sm_client->priv->discard_command,
+                                             FALSE);
+#endif
+    g_object_notify(G_OBJECT(sm_client), "discard-command");
+}
+
+/**
+ * xfce_sm_client_set_shutdown_command:
+ * @sm_client: An #XfceSMClient
+ * @shutdown_command: An argument vector
+ *
+ * Sets the application's "shutdown" command, which is executed for
+ * 'restart-always' style applications when the session ends.
+ *
+ * If unset, defaults to %NULL.
+ **/
+void
+xfce_sm_client_set_shutdown_command(XfceSMClient *sm_client,
+                                    gchar **shutdown_command)
+{
+    g_return_if_fail(XFCE_IS_SM_CLIENT(sm_client));
+    sm_client->priv->shutdown_command = copy_command(sm_client->priv->shutdown_command,
+                                                     shutdown_command);
+#ifdef HAVE_LIBSM
+    xfce_sm_client_set_property_from_command(sm_client, SmShutdownCommand,
+                                             sm_client->priv->shutdown_command,
+                                             FALSE);
+#endif
+    g_object_notify(G_OBJECT(sm_client), "shutdown-command");
+}
+
+/**
+ * xfce_sm_client_get_state:
+ * @sm_client: An #XfceSMClient
+ *
+ * Retrieves the session client's state.  Note that this value
+ * is very specific to the X Session Management Protocol.
+ *
+ * If the state is %XFCE_SM_CLIENT_STATE_FROZEN, the application
+ * should attempt to disallow user interaction.
+ *
+ * Returns: a value from the #XfceSMClientState enum
+ **/
+XfceSMClientState
+xfce_sm_client_get_state(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client),
+                         XFCE_SM_CLIENT_STATE_DISCONNECTED);
+    return sm_client->priv->state;
+}
+
+/**
+ * xfce_sm_client_get_restart_style:
+ * @sm_client: An #XfceSMClient
+ *
+ * Retrieves the session client's restart style.  See
+ * xfce_sm_client_set_restart_style() for more information.
+ *
+ * Returns: a value from the #XfceSMClientRestartStyle enum
+ **/
+XfceSMClientRestartStyle
+xfce_sm_client_get_restart_style(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client),
+                         XFCE_SM_CLIENT_RESTART_IF_RUNNING);
+    return sm_client->priv->restart_style;
+}
+
+/**
+ * xfce_sm_client_get_priority:
+ * @sm_client: An #XfceSMClient
+ *
+ * Retrieves the session client's restart priority.  See
+ * xfce_sm_client_set_priority() for more information.
+ *
+ * Returns: a value from #G_MININT8 to #G_MAXINT8
+ **/
+gchar
+xfce_sm_client_get_priority(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client),
+                         XFCE_SM_CLIENT_PRIORITY_DEFAULT);
+    return sm_client->priv->priority;
+}
+
+/**
+ * xfce_sm_client_get_client_id:
+ * @sm_client: An #XfceSMClient
+ *
+ * Retrieves the session client's unique ID.  This ID can
+ * be used to construct a filename used to restore the
+ * application's state.  Note that this value is only
+ * guaranteed to be valid if connected to the session manager.
+ *
+ * Note: Instead of constructing a state filename, it is
+ * recommended to use xfce_sm_client_get_state_file().
+ *
+ * Returns: an opaque object-owned string
+ **/
+G_CONST_RETURN gchar *
+xfce_sm_client_get_client_id(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client), NULL);
+    return sm_client->priv->client_id;
+}
+
+/**
+ * xfce_sm_client_get_state_file:
+ * @sm_client: An #XfceSMClient
+ *
+ * Constructs a filename that can be used to restore or save
+ * state information.
+ *
+ * When saving state, ote that this file may already exist (and
+ * may have been used for saving previous state for the
+ * application), so the application should first remove or empty
+ * the file if it requires a fresh state file.
+ *
+ * On the next application start, this function can be used to
+ * check to see if there is any previous saved state, and, if so,
+ * the state can be restored from the file.
+ * 
+ * This function will use a standard location and naming scheme
+ * as handle state cleanup (setting of the discard command or
+ * resign command) for you.
+ *
+ * Before calling this function, the application must have a
+ * valid program identifier set (see xfce_sm_client_set_program())
+ * and a valid client ID (see xfce_sm_client_get_client_id()).
+ *
+ * Returns: a file name string, owned by the object
+ **/
+G_CONST_RETURN gchar *
+xfce_sm_client_get_state_file(XfceSMClient *sm_client)
+{
+    gchar *resource, *p;
+
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client), NULL);
+
+    if(sm_client->priv->state_file)
+        return sm_client->priv->state_file;
+
+    g_return_val_if_fail(sm_client->priv->program, NULL);
+    g_return_val_if_fail(sm_client->priv->client_id, NULL);
+
+    resource = g_strdup_printf("sessions/%s-%s.state",
+                               sm_client->priv->program,
+                               sm_client->priv->client_id);
+    for(p = resource; *p; p++) {
+        if(*p == '/')
+            *p = '_';
+    }
+
+    sm_client->priv->state_file = xfce_resource_save_location(XFCE_RESOURCE_CACHE,
+                                                              resource, TRUE);
+    if(!sm_client->priv->state_file) {
+        g_critical("XfceSMClient: Unable to create state file as "
+                   "\"$XDG_CACHE_HOME/%s\"", resource);
+    }
+
+    g_free(resource);
+
+#ifdef HAVE_LIBSM
+    if(G_LIKELY(sm_client->priv->state_file)
+       && sm_client->priv->session_connection
+       && !sm_client->priv->discard_command)
+    {
+        sm_client->priv->discard_command = g_new0(gchar *, 4);
+        sm_client->priv->discard_command[0] = g_strdup("rm");
+        sm_client->priv->discard_command[1] = g_strdup("-f");
+        sm_client->priv->discard_command[2] = g_strdup(sm_client->priv->state_file);
+
+        xfce_sm_client_set_property_from_command(sm_client, SmDiscardCommand,
+                                                 sm_client->priv->discard_command,
+                                                 FALSE);
+    }
+#endif
+
+    return sm_client->priv->state_file;
+}
+
+/**
+ * xfce_sm_client_get_current_directory:
+ * @sm_client: An #XfceSMClient
+ *
+ * Retrieves the session client's working directory.  See
+ * xfce_sm_client_set_current_directory() for more information.
+ *
+ * Returns: an object-owned string
+ **/
+G_CONST_RETURN gchar *
+xfce_sm_client_get_current_directory(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client), NULL);
+    return sm_client->priv->current_directory;
+}
+
+/**
+ * xfce_sm_client_get_program:
+ * @sm_client: An #XfceSMClient
+ *
+ * Retrieves the session client's program identifier.  See
+ * xfce_sm_client_set_program() for more information.
+ *
+ * Returns: an object-owned string
+ **/
+G_CONST_RETURN gchar *
+xfce_sm_client_get_program(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client), NULL);
+    return sm_client->priv->program;
+}
+
+/**
+ * xfce_sm_client_get_clone_command:
+ * @sm_client: An #XfceSMClient
+ *
+ * Retrieves the session client's clone command.  See
+ * xfce_sm_client_set_clone_command() for more information.
+ *
+ * Returns: an object-owned string vector
+ **/
+gchar **
+xfce_sm_client_get_clone_command(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client), NULL);
+    return sm_client->priv->clone_command;
+}
+
+/**
+ * xfce_sm_client_get_resign_command:
+ * @sm_client: An #XfceSMClient
+ *
+ * Retrieves the session client's resign command.  See
+ * xfce_sm_client_set_resign_command() for more information.
+ *
+ * Returns: an object-owned string vector
+ **/
+gchar **
+xfce_sm_client_get_resign_command(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client), NULL);
+    return sm_client->priv->resign_command;
+}
+
+/**
+ * xfce_sm_client_get_restart_command:
+ * @sm_client: An #XfceSMClient
+ *
+ * Retrieves the session client's restart command.  See
+ * xfce_sm_client_set_restart_command() for more information.
+ *
+ * Returns: an object-owned string vector
+ **/
+gchar **
+xfce_sm_client_get_restart_command(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client), NULL);
+    return sm_client->priv->restart_command;
+}
+
+/**
+ * xfce_sm_client_get_discard_command:
+ * @sm_client: An #XfceSMClient
+ *
+ * Retrieves the session client's discard command.  See
+ * xfce_sm_client_set_discard_command() for more information.
+ *
+ * Returns: an object-owned string vector
+ **/
+gchar **
+xfce_sm_client_get_discard_command(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client), NULL);
+    return sm_client->priv->discard_command;
+}
+
+/**
+ * xfce_sm_client_get_shutdown_command:
+ * @sm_client: An #XfceSMClient
+ *
+ * Retrieves the session client's shutdown command.  See
+ * xfce_sm_client_set_shutdown_command() for more information.
+ *
+ * Returns: an object-owned string vector
+ **/
+gchar **
+xfce_sm_client_get_shutdown_command(XfceSMClient *sm_client)
+{
+    g_return_val_if_fail(XFCE_IS_SM_CLIENT(sm_client), NULL);
+    return sm_client->priv->shutdown_command;
+}
diff --git a/libxfce4ui/xfce-sm-client.h b/libxfce4ui/xfce-sm-client.h
new file mode 100644
index 0000000..439e8fa
--- /dev/null
+++ b/libxfce4ui/xfce-sm-client.h
@@ -0,0 +1,184 @@
+/*  xfce4
+ *  Copyright (C) 1999 Olivier Fourdan (fourdan at xfce.org)
+ *  Copyright (c) 2009 Brian Tarricone <brian at terricone.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XFCE_SM_CLIENT_H__
+#define __XFCE_SM_CLIENT_H__
+
+#if !defined(LIBXFCE4UI_INSIDE_LIBXFCE4UI_H) && !defined(LIBXFCE4UI_COMPILATION)
+#error "Only <libxfce4ui/libxfce4ui.h> can be included directly, this file is not part of the public API."
+#endif
+
+#include <glib-object.h>
+
+#define XFCE_TYPE_SM_CLIENT             (xfce_sm_client_get_type())
+#define XFCE_SM_CLIENT(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), XFCE_TYPE_SM_CLIENT, XfceSMClient))
+#define XFCE_IS_SM_CLIENT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), XFCE_TYPE_SM_CLIENT))
+#define XFCE_SM_CLIENT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), XFCE_TYPE_SM_CLIENT, XfceSMClientClass))
+#define XFCE_IS_SM_CLIENT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), XFCE_TYPE_SM_CLIENT))
+#define XFCE_SM_CLIENT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), XFCE_TYPE_SM_CLIENT, XfceSMClientClass))
+
+G_BEGIN_DECLS
+
+typedef enum /*< prefix=XFCE_SM_CLIENT_RESTART_ >*/
+{
+    XFCE_SM_CLIENT_RESTART_IF_RUNNING = 0,
+    XFCE_SM_CLIENT_RESTART_ANYWAY,  /* TODO: maybe not support this? */
+    XFCE_SM_CLIENT_RESTART_IMMEDIATELY,
+    XFCE_SM_CLIENT_RESTART_NEVER,  /* TODO: maybe not support this? */
+} XfceSMClientRestartStyle;
+
+typedef enum
+{
+    XFCE_SM_CLIENT_STATE_IDLE = 0,
+    XFCE_SM_CLIENT_STATE_SAVING_PHASE_1,
+    XFCE_SM_CLIENT_STATE_WAITING_FOR_PHASE_2,
+    XFCE_SM_CLIENT_STATE_SAVING_PHASE_2,
+    XFCE_SM_CLIENT_STATE_WAITING_FOR_INTERACT,
+    XFCE_SM_CLIENT_STATE_INTERACTING,
+    XFCE_SM_CLIENT_STATE_FROZEN,
+    XFCE_SM_CLIENT_STATE_DISCONNECTED,
+    XFCE_SM_CLIENT_STATE_REGISTERING,
+} XfceSMClientState;
+
+typedef enum
+{
+    XFCE_SM_CLIENT_PRIORITY_HIGHEST = 0,
+    XFCE_SM_CLIENT_PRIORITY_WM = 15,
+    XFCE_SM_CLIENT_PRIORITY_DESKTOP = 25,
+    XFCE_SM_CLIENT_PRIORITY_CORE = 35,
+    XFCE_SM_CLIENT_PRIORITY_DEFAULT = 50,
+} XfceSMClientPriority;
+
+typedef enum
+{
+    XFCE_SM_CLIENT_SHUTDOWN_HINT_ASK = 0,
+    XFCE_SM_CLIENT_SHUTDOWN_HINT_LOGOUT,
+    XFCE_SM_CLIENT_SHUTDOWN_HINT_HALT,
+    XFCE_SM_CLIENT_SHUTDOWN_HINT_REBOOT,
+} XfceSMClientShutdownHint;
+
+typedef struct _XfceSMClient         XfceSMClient;
+typedef struct _XfceSMClientClass    XfceSMClientClass;
+typedef struct _XfceSMClientPrivate  XfceSMClientPrivate;
+
+struct _XfceSMClient
+{
+    GObject parent;
+
+    /*< private >*/
+    XfceSMClientPrivate *priv;
+};
+
+struct _XfceSMClientClass
+{
+    GObjectClass parent;
+
+    /*< signals >*/
+
+    void (*save_state)(XfceSMClient *sm_client);
+    void (*save_state_extended)(XfceSMClient *sm_client);
+
+    /* return TRUE to cancel shutdown */
+    gboolean (*quit_requested)(XfceSMClient *sm_client);
+
+    void (*quit)(XfceSMClient *sm_client);
+
+    void (*quit_cancelled)(XfceSMClient *sm_client);
+
+    void (*state_changed)(XfceSMClient *sm_client,
+                          XfceSMClientState old_state,
+                          XfceSMClientState new_state);
+
+    void (*_xfce_reserved0)(void);
+    void (*_xfce_reserved1)(void);
+    void (*_xfce_reserved2)(void);
+    void (*_xfce_reserved3)(void);
+};
+
+
+GType xfce_sm_client_get_type(void) G_GNUC_CONST;
+
+GOptionGroup *xfce_sm_client_get_option_group(guint argc,
+                                              gchar **argv);
+
+XfceSMClient *xfce_sm_client_get();
+
+XfceSMClient *xfce_sm_client_get_with_argv(guint argc,
+                                           gchar **argv,
+                                           XfceSMClientRestartStyle restart_style,
+                                           gchar priority);
+
+XfceSMClient *xfce_sm_client_get_full(XfceSMClientRestartStyle restart_style,
+                                      gchar priority,
+                                      const gchar *resumed_client_id,
+                                      const gchar *program,
+                                      const gchar *current_directory,
+                                      const gchar **restart_command,
+                                      const gchar **clone_command,
+                                      const gchar **discard_command,
+                                      const gchar **resign_command,
+                                      const gchar **shutdown_command);
+
+gboolean xfce_sm_client_connect(XfceSMClient *sm_client,
+                                GError **error);
+void xfce_sm_client_disconnect(XfceSMClient *sm_client);
+
+gboolean xfce_sm_client_is_resumed(XfceSMClient *sm_client);
+
+void xfce_sm_client_request_shutdown(XfceSMClient *sm_client,
+                                     XfceSMClientShutdownHint shutdown_hint);
+
+XfceSMClientState xfce_sm_client_get_state(XfceSMClient *sm_client);
+
+G_CONST_RETURN gchar *xfce_sm_client_get_client_id(XfceSMClient *sm_client);
+
+G_CONST_RETURN gchar *xfce_sm_client_get_state_file(XfceSMClient *sm_client);
+
+void xfce_sm_client_set_restart_style(XfceSMClient *sm_client,
+                                      XfceSMClientRestartStyle restart_style);
+XfceSMClientRestartStyle xfce_sm_client_get_restart_style(XfceSMClient *sm_client);
+
+void xfce_sm_client_set_priority(XfceSMClient *sm_client, gchar value);
+gchar xfce_sm_client_get_priority(XfceSMClient *sm_client);
+
+void xfce_sm_client_set_current_directory(XfceSMClient *sm_client, gchar const* value);
+G_CONST_RETURN gchar *xfce_sm_client_get_current_directory(XfceSMClient *sm_client);
+
+void xfce_sm_client_set_program(XfceSMClient *sm_client, gchar const* value);
+G_CONST_RETURN gchar *xfce_sm_client_get_program(XfceSMClient *sm_client);
+
+void xfce_sm_client_set_clone_command(XfceSMClient *sm_client, gchar** const value);
+gchar **xfce_sm_client_get_clone_command(XfceSMClient *sm_client);
+
+void xfce_sm_client_set_resign_command(XfceSMClient *sm_client, gchar** const value);
+gchar **xfce_sm_client_get_resign_command(XfceSMClient *sm_client);
+
+void xfce_sm_client_set_restart_command(XfceSMClient *sm_client, gchar** const value);
+gchar **xfce_sm_client_get_restart_command(XfceSMClient *sm_client);
+
+void xfce_sm_client_set_discard_command(XfceSMClient *sm_client, gchar** const value);
+gchar **xfce_sm_client_get_discard_command(XfceSMClient *sm_client);
+
+void xfce_sm_client_set_shutdown_command(XfceSMClient *sm_client, gchar** const value);
+gchar **xfce_sm_client_get_shutdown_command(XfceSMClient *sm_client);
+
+G_END_DECLS
+
+#endif  /* __XFCE_SM_CLIENT_H__ */



More information about the Xfce4-commits mailing list