[nautilus-actions/file-manager-actions] Re-add X session management



commit 9a5a4e6c9aa552baf36c4f0dda597f46310c16d9
Author: Pierre Wieser <pwieser trychlos org>
Date:   Wed May 20 21:38:24 2015 +0200

    Re-add X session management
    
    Because do not know how to use new GtkApplication 'register-session' property
    (and no documentation found)

 configure.ac                     |    2 +
 src/nact/Makefile.am             |    6 +
 src/nact/base-isession.c         |  406 +++++++++++
 src/nact/base-isession.h         |   78 +++
 src/nact/egg-sm-client-private.h |   62 ++
 src/nact/egg-sm-client-xsmp.c    | 1405 ++++++++++++++++++++++++++++++++++++++
 src/nact/egg-sm-client.c         |  614 +++++++++++++++++
 src/nact/egg-sm-client.h         |  118 ++++
 src/nact/nact-application.c      |   24 +-
 src/nact/nact-main-window.c      |    2 +-
 src/nact/nact-main-window.h      |    5 +-
 11 files changed, 2717 insertions(+), 5 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index a204f8e..01ce49a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -102,6 +102,8 @@ NA_CHECK_MODULE([GIO_UNIX],[gio-unix-2.0])
 NA_CHECK_MODULE([GTOP],    [libgtop-2.0],[${gtop_required}])
 NA_CHECK_MODULE([LIBXML],  [libxml-2.0], [${xml_required}])
 NA_CHECK_MODULE([UUID],    [uuid])
+NA_CHECK_MODULE([SM],      [sm >= 1.0])
+NA_CHECK_MODULE([ICE],     [ice])
 
 # target a file manager (nautilus, nemo, ...)
 NA_TARGET_FILE_MANAGER
diff --git a/src/nact/Makefile.am b/src/nact/Makefile.am
index 941f034..639d53d 100644
--- a/src/nact/Makefile.am
+++ b/src/nact/Makefile.am
@@ -55,11 +55,17 @@ nautilus_actions_config_tool_SOURCES = \
        base-dialog.h                                                                           \
        base-gtk-utils.c                                                                        \
        base-gtk-utils.h                                                                        \
+       base-isession.c                                                                         \
+       base-isession.h                                                                         \
        base-keysyms.h                                                                          \
        base-window.c                                                                           \
        base-window.h                                                                           \
        egg-desktop-file.c                                                                      \
        egg-desktop-file.h                                                                      \
+       egg-sm-client.c                                                                         \
+       egg-sm-client.h                                                                         \
+       egg-sm-client-private.h                                                         \
+       egg-sm-client-xsmp.c                                                            \
        egg-tree-multi-dnd.c                                                            \
        egg-tree-multi-dnd.h                                                            \
        main.c                                                                                          \
diff --git a/src/nact/base-isession.c b/src/nact/base-isession.c
new file mode 100644
index 0000000..5d1e094
--- /dev/null
+++ b/src/nact/base-isession.c
@@ -0,0 +1,406 @@
+/*
+ * Nautilus-Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS)
+ *
+ * Nautilus-Actions is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * Nautilus-Actions is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Nautilus-Actions; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *   Frederic Ruaudel <grumz grumz net>
+ *   Rodrigo Moya <rodrigo gnome-db org>
+ *   Pierre Wieser <pwieser trychlos org>
+ *   ... and many others (see AUTHORS)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "base-isession.h"
+#include "egg-sm-client.h"
+
+/* private interface data
+ */
+struct _BaseISessionInterfacePrivate {
+       void *empty;                                            /* so that gcc -pedantic is happy */
+};
+
+/* pseudo-properties, set against the instance
+ */
+typedef struct {
+       EggSMClient *sm_client;
+       gulong       sm_client_quit_handler_id;
+       gulong       sm_client_quit_requested_handler_id;
+}
+       ISessionData;
+
+#define BASE_PROP_ISESSION_DATA                        "base-prop-isession-data"
+
+/* signals defined by the BaseISession interface
+ */
+enum {
+       QUIT_REQUESTED,
+       QUIT,
+       LAST_SIGNAL
+};
+
+static guint st_initializations = 0;   /* interface initialisation count */
+static gint  st_signals[ LAST_SIGNAL ] = { 0 };
+
+static GType         register_type( void );
+static void          interface_base_init( BaseISessionInterface *klass );
+static void          interface_base_finalize( BaseISessionInterface *klass );
+
+static ISessionData *get_isession_data( BaseISession *instance );
+static void          on_instance_finalized( gpointer user_data, BaseISession *instance );
+static void          client_quit_requested_cb( EggSMClient *client, BaseISession *instance );
+static gboolean      on_quit_requested_class_handler( BaseISession *instance, void *user_data );
+static gboolean      signal_accumulator_false_handled( GSignalInvocationHint *hint, GValue *return_accu, 
const GValue *handler_return, gpointer dummy);
+static void          client_quit_cb( EggSMClient *client, BaseISession *instance );
+static void          on_quit_class_handler( BaseISession *instance, void *user_data );
+
+GType
+base_isession_get_type( void )
+{
+       static GType iface_type = 0;
+
+       if( !iface_type ){
+               iface_type = register_type();
+       }
+
+       return( iface_type );
+}
+
+static GType
+register_type( void )
+{
+       static const gchar *thisfn = "base_isession_register_type";
+       GType type;
+
+       static const GTypeInfo info = {
+               sizeof( BaseISessionInterface ),
+               ( GBaseInitFunc ) interface_base_init,
+               ( GBaseFinalizeFunc ) interface_base_finalize,
+               NULL,
+               NULL,
+               NULL,
+               0,
+               0,
+               NULL
+       };
+
+       g_debug( "%s", thisfn );
+
+       type = g_type_register_static( G_TYPE_INTERFACE, "BaseISession", &info, 0 );
+
+       g_type_interface_add_prerequisite( type, G_TYPE_OBJECT );
+
+       return( type );
+}
+
+static void
+interface_base_init( BaseISessionInterface *klass )
+{
+       static const gchar *thisfn = "base_isession_interface_base_init";
+
+       if( !st_initializations ){
+
+               g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
+
+               /**
+                * base-signal-isession-quit-requested:
+                *
+                * The signal is emitted when the session is about to terminate,
+                * to determine if all implementations are actually willing to quit.
+                *
+                * If the implementation is not willing to quit, then the user handler
+                * should return %FALSE to stop the signal emission.
+                *
+                * Returning %TRUE will let the signal be emitted to other connected
+                * handlers, eventually authorizing the application to terminate.
+                *
+                * Notes about GLib signals.
+                *
+                * If the signal is defined as G_SIGNAL_RUN_CLEANUP, then the object
+                * handler does not participate to the return value of the signal
+                * (accumulator is not called). More this object handler is triggered
+                * unconditionnally, event if a user handler has returned %FALSE to
+                * stop the emission.
+                *
+                * Contrarily, if the signal is defined as G_SIGNAL_RUN_LAST, then the
+                * object handler returned value is taken into account by the accumulator,
+                * and can participate to the return value for the signal. If a user
+                * handler returns FALSE to stop the emission, then the object handler
+                * will not be triggered.
+                */
+               st_signals[ QUIT_REQUESTED ] =
+                       g_signal_new_class_handler(
+                                       BASE_SIGNAL_QUIT_REQUESTED,
+                                       G_TYPE_FROM_CLASS( klass ),
+                                       G_SIGNAL_RUN_LAST,
+                                       G_CALLBACK( on_quit_requested_class_handler ),
+                                       ( GSignalAccumulator ) signal_accumulator_false_handled,
+                                       NULL,
+                                       NULL,
+                                       G_TYPE_BOOLEAN,
+                                       0 );
+
+               /**
+                * base-signal-isession-quit:
+                *
+                * The signal is emitted when the session is about to terminate,
+                * and no application has refused to abort then end of session
+                * (all have accepted the end). It is time for the application
+                * to terminate cleanly.
+                */
+               st_signals[ QUIT ] =
+                       g_signal_new_class_handler(
+                                       BASE_SIGNAL_QUIT,
+                                       G_TYPE_FROM_CLASS( klass ),
+                                       G_SIGNAL_RUN_LAST,
+                                       G_CALLBACK( on_quit_class_handler ),
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       G_TYPE_NONE,
+                                       0 );
+
+               klass->private = g_new0( BaseISessionInterfacePrivate, 1 );
+       }
+
+       st_initializations += 1;
+}
+
+static void
+interface_base_finalize( BaseISessionInterface *klass )
+{
+       static const gchar *thisfn = "base_isession_interface_base_finalize";
+
+       st_initializations -= 1;
+
+       if( !st_initializations ){
+
+               g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
+
+               g_free( klass->private );
+       }
+}
+
+static ISessionData *
+get_isession_data( BaseISession *instance )
+{
+       ISessionData *data;
+
+       data = ( ISessionData * ) g_object_get_data( G_OBJECT( instance ), BASE_PROP_ISESSION_DATA );
+
+       if( !data ){
+               data = g_new0( ISessionData, 1 );
+               g_object_set_data( G_OBJECT( instance ), BASE_PROP_ISESSION_DATA, data );
+               g_object_weak_ref( G_OBJECT( instance ), ( GWeakNotify ) on_instance_finalized, NULL );
+       }
+
+       return( data );
+}
+
+static void
+on_instance_finalized( gpointer user_data, BaseISession *instance )
+{
+       static const gchar *thisfn = "base_isession_on_instance_finalized";
+       ISessionData *data;
+
+       g_debug( "%s: instance=%p, user_data=%p", thisfn, ( void * ) instance, ( void * ) user_data );
+
+       data = get_isession_data( instance );
+
+       if( data->sm_client_quit_handler_id &&
+               g_signal_handler_is_connected( data->sm_client, data->sm_client_quit_handler_id )){
+                       g_signal_handler_disconnect( data->sm_client, data->sm_client_quit_handler_id  );
+       }
+
+       if( data->sm_client_quit_requested_handler_id &&
+               g_signal_handler_is_connected( data->sm_client, data->sm_client_quit_requested_handler_id )){
+                       g_signal_handler_disconnect( data->sm_client, 
data->sm_client_quit_requested_handler_id  );
+       }
+
+       if( data->sm_client ){
+               g_object_unref( data->sm_client );
+       }
+
+       g_free( data );
+}
+
+void
+base_isession_init( BaseISession *instance )
+{
+       static const gchar *thisfn = "base_isession_init";
+       ISessionData *data;
+
+       g_return_if_fail( BASE_IS_ISESSION( instance ));
+
+       g_debug( "%s: instance=%p", thisfn, ( void * ) instance );
+
+       data = get_isession_data( instance );
+
+       /* initialize the session manager
+        */
+       egg_sm_client_set_mode( EGG_SM_CLIENT_MODE_NO_RESTART );
+       data->sm_client = egg_sm_client_get();
+       egg_sm_client_startup();
+       g_debug( "%s: sm_client=%p", thisfn, ( void * ) data->sm_client );
+
+       data->sm_client_quit_handler_id =
+                       g_signal_connect(
+                                       data->sm_client,
+                                       "quit-requested",
+                                       G_CALLBACK( client_quit_requested_cb ),
+                                       instance );
+
+       data->sm_client_quit_requested_handler_id =
+                       g_signal_connect(
+                                       data->sm_client,
+                                       "quit",
+                                       G_CALLBACK( client_quit_cb ),
+                                       instance );
+}
+
+/*
+ * base_isession_is_willing_to_quit:
+ * @instance: this #BaseISession instance.
+ *
+ * Returns: %TRUE if the implementation is willing to quit, %FALSE else.
+ *
+ * From the session management strict point of view, this function may be
+ * just static and reserved for the own internal use of the interface.
+ * We make it public because it is also useful for the application itself,
+ * and we are so able to reuse both the signal and its handler mechanisms.
+ */
+gboolean
+base_isession_is_willing_to_quit( const BaseISession *instance )
+{
+       static const gchar *thisfn = "base_isession_is_willing_to_quit";
+       GValue instance_params = {0};
+       GValue return_value = {0};
+
+       g_return_val_if_fail( BASE_IS_ISESSION( instance ), TRUE );
+
+       g_debug( "%s: instance=%p (%s)", thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ));
+
+       g_value_init( &instance_params, G_TYPE_FROM_INSTANCE( instance ));
+       g_value_set_instance( &instance_params, ( gpointer ) instance );
+
+       g_value_init( &return_value, G_TYPE_BOOLEAN );
+       g_value_set_boolean( &return_value, TRUE );
+
+       g_signal_emitv( &instance_params, st_signals[ QUIT_REQUESTED ], 0, &return_value );
+
+       return( g_value_get_boolean( &return_value ));
+}
+
+/*
+ * the session manager advertises us that the session is about to exit
+ *
+ * Returns: %TRUE if the application is willing to quit, %FALSE else.
+ *
+ * This function is called when the session manager detects the end of
+ * session and thus asks its clients if they are willing to quit.
+ */
+static void
+client_quit_requested_cb( EggSMClient *client, BaseISession *instance )
+{
+       static const gchar *thisfn = "base_isession_client_quit_requested_cb";
+       gboolean willing_to;
+
+       g_debug( "%s: client=%p, instance=%p (%s)",
+                       thisfn,
+                       ( void * ) client,
+                       ( void * ) instance, G_OBJECT_TYPE_NAME( instance ));
+
+       willing_to = base_isession_is_willing_to_quit( instance );
+
+       egg_sm_client_will_quit( client, willing_to );
+}
+
+/*
+ * Handler of BASE_SIGNAL_QUIT_REQUESTED message
+ *
+ * Application should handle this signal in order to trap application
+ * termination, returning %FALSE if it is not willing to quit.
+ */
+static gboolean
+on_quit_requested_class_handler( BaseISession *instance, void *user_data )
+{
+       static const gchar *thisfn = "base_isession_on_quit_requested_class_handler";
+
+       g_return_val_if_fail( BASE_IS_ISESSION( instance ), TRUE );
+
+       g_debug( "%s: instance=%p (%s), user_data=%p",
+                       thisfn,
+                       ( void * ) instance, G_OBJECT_TYPE_NAME( instance ),
+                       ( void * ) user_data );
+
+       return( TRUE );
+}
+
+/*
+ * the first handler which returns FALSE stops the emission
+ * this is used on BASE_SIGNAL_QUIT_REQUESTED signal
+ */
+static gboolean
+signal_accumulator_false_handled(
+               GSignalInvocationHint *hint, GValue *return_accu, const GValue *handler_return, gpointer 
dummy)
+{
+       static const gchar *thisfn = "base_isession_signal_accumulator_false_handled";
+       gboolean continue_emission;
+       gboolean willing_to_quit;
+
+       willing_to_quit = g_value_get_boolean( handler_return );
+       g_value_set_boolean( return_accu, willing_to_quit );
+       continue_emission = willing_to_quit;
+
+       g_debug( "%s: willing_to handler returns %s", thisfn, willing_to_quit ? "True":"False" );
+       return( continue_emission );
+}
+
+/*
+ * cleanly terminate the application when exiting the session
+ * -> triggers the implementation
+ */
+static void
+client_quit_cb( EggSMClient *client, BaseISession *instance )
+{
+       static const gchar *thisfn = "base_isession_client_quit_cb";
+
+       g_return_if_fail( BASE_IS_ISESSION( instance ));
+
+       g_debug( "%s: client=%p, instance=%p", thisfn, ( void * ) client, ( void * ) instance );
+
+       g_signal_emit_by_name( G_OBJECT( instance ), BASE_SIGNAL_QUIT, NULL );
+}
+
+static void
+on_quit_class_handler( BaseISession *instance, void *user_data )
+{
+       static const gchar *thisfn = "base_isession_on_quit_class_handler";
+
+       g_return_if_fail( BASE_IS_ISESSION( instance ));
+
+       g_debug( "%s: instance=%p (%s), user_data=%p",
+                       thisfn,
+                       ( void * ) instance, G_OBJECT_TYPE_NAME( instance ),
+                       ( void * ) user_data );
+}
diff --git a/src/nact/base-isession.h b/src/nact/base-isession.h
new file mode 100644
index 0000000..28099ca
--- /dev/null
+++ b/src/nact/base-isession.h
@@ -0,0 +1,78 @@
+/*
+ * Nautilus-Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS)
+ *
+ * Nautilus-Actions is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * Nautilus-Actions is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Nautilus-Actions; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *   Frederic Ruaudel <grumz grumz net>
+ *   Rodrigo Moya <rodrigo gnome-db org>
+ *   Pierre Wieser <pwieser trychlos org>
+ *   ... and many others (see AUTHORS)
+ */
+
+#ifndef __BASE_ISESSION_H__
+#define __BASE_ISESSION_H__
+
+/**
+ * SECTION: base_isession
+ * @short_description: #BaseISession interface definition.
+ * @include: nact/base-isession.h
+ *
+ * This interface implements the features needed to monitor the end of the
+ * session, thus letting the application react if some modifications need
+ * to be saved before allowing to quit.
+ *
+ * This interface is implemented by the BaseApplication class.
+ */
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define BASE_TYPE_ISESSION                      ( base_isession_get_type())
+#define BASE_ISESSION( instance )               ( G_TYPE_CHECK_INSTANCE_CAST( instance, BASE_TYPE_ISESSION, 
BaseISession ))
+#define BASE_IS_ISESSION( instance )            ( G_TYPE_CHECK_INSTANCE_TYPE( instance, BASE_TYPE_ISESSION ))
+#define BASE_ISESSION_GET_INTERFACE( instance ) ( G_TYPE_INSTANCE_GET_INTERFACE(( instance ), 
BASE_TYPE_ISESSION, BaseISessionInterface ))
+
+typedef struct _BaseISession                    BaseISession;
+typedef struct _BaseISessionInterfacePrivate    BaseISessionInterfacePrivate;
+
+typedef struct {
+       /*< private >*/
+       GTypeInterface                parent;
+       BaseISessionInterfacePrivate *private;
+}
+       BaseISessionInterface;
+
+/**
+ * Signals defined by the BaseISession interface
+ */
+#define BASE_SIGNAL_QUIT_REQUESTED                     "base-signal-isession-quit-requested"
+#define BASE_SIGNAL_QUIT                                       "base-signal-isession-quit"
+
+GType    base_isession_get_type          ( void );
+
+void     base_isession_init              ( BaseISession *instance );
+
+gboolean base_isession_is_willing_to_quit( const BaseISession *instance );
+
+G_END_DECLS
+
+#endif /* __BASE_ISESSION_H__ */
diff --git a/src/nact/egg-sm-client-private.h b/src/nact/egg-sm-client-private.h
new file mode 100644
index 0000000..b28361c
--- /dev/null
+++ b/src/nact/egg-sm-client-private.h
@@ -0,0 +1,62 @@
+/* eggsmclient-private.h
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 __EGG_SM_CLIENT_PRIVATE_H__
+#define __EGG_SM_CLIENT_PRIVATE_H__
+
+#include <gtk/gtk.h>
+
+/* patch provided by Mathias Clasen
+ * see http://git.gnome.org/browse/libegg/commit/?id=0be81fa47fb5dabba2be40888ed5d4b16f0ae6a3
+ */
+#if !GTK_CHECK_VERSION( 2, 91, 7 )
+/* GTK+ 3 includes this automatically */
+#include <gdkconfig.h>
+#endif
+
+#include "egg-sm-client.h"
+
+G_BEGIN_DECLS
+
+GKeyFile *egg_sm_client_save_state     (EggSMClient *client);
+void      egg_sm_client_quit_requested (EggSMClient *client);
+void      egg_sm_client_quit_cancelled (EggSMClient *client);
+void      egg_sm_client_quit           (EggSMClient *client);
+
+#if defined (GDK_WINDOWING_X11)
+# ifdef EGG_SM_CLIENT_BACKEND_XSMP
+GType        egg_sm_client_xsmp_get_type (void);
+EggSMClient *egg_sm_client_xsmp_new      (void);
+# endif
+# ifdef EGG_SM_CLIENT_BACKEND_DBUS
+GType        egg_sm_client_dbus_get_type (void);
+EggSMClient *egg_sm_client_dbus_new      (void);
+# endif
+#elif defined (GDK_WINDOWING_WIN32)
+GType        egg_sm_client_win32_get_type (void);
+EggSMClient *egg_sm_client_win32_new      (void);
+#elif defined (GDK_WINDOWING_QUARTZ)
+GType        egg_sm_client_osx_get_type (void);
+EggSMClient *egg_sm_client_osx_new      (void);
+#endif
+
+G_END_DECLS
+
+
+#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */
diff --git a/src/nact/egg-sm-client-xsmp.c b/src/nact/egg-sm-client-xsmp.c
new file mode 100644
index 0000000..0de5a6a
--- /dev/null
+++ b/src/nact/egg-sm-client-xsmp.c
@@ -0,0 +1,1405 @@
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * Inspired by various other pieces of code including GsmClient (C)
+ * 2001 Havoc Pennington, GnomeClient (C) 1998 Carsten Schaar, and twm
+ * session code (C) 1998 The Open Group.
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include "egg-sm-client.h"
+#include "egg-sm-client-private.h"
+
+#include "egg-desktop-file.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <X11/SM/SMlib.h>
+
+#include <gdk/gdk.h>
+
+/* patch provided by Mathias Clasen
+ * see http://git.gnome.org/browse/libegg/commit/?id=0be81fa47fb5dabba2be40888ed5d4b16f0ae6a3
+ */
+#if GTK_CHECK_VERSION( 2, 91, 7 )
+#include <gdk/gdkx.h>
+#endif
+
+#define EGG_TYPE_SM_CLIENT_XSMP            (egg_sm_client_xsmp_get_type ())
+#define EGG_SM_CLIENT_XSMP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, 
EggSMClientXSMP))
+#define EGG_SM_CLIENT_XSMP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_XSMP, 
EggSMClientXSMPClass))
+#define EGG_IS_SM_CLIENT_XSMP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_XSMP))
+#define EGG_IS_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_XSMP))
+#define EGG_SM_CLIENT_XSMP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_XSMP, 
EggSMClientXSMPClass))
+
+typedef struct _EggSMClientXSMP        EggSMClientXSMP;
+typedef struct _EggSMClientXSMPClass   EggSMClientXSMPClass;
+
+/* These mostly correspond to the similarly-named states in section
+ * 9.1 of the XSMP spec. Some of the states there aren't represented
+ * here, because we don't need them. SHUTDOWN_CANCELLED is slightly
+ * different from the spec; we use it when the client is IDLE after a
+ * ShutdownCancelled message, but the application is still interacting
+ * and doesn't know the shutdown has been cancelled yet.
+ */
+typedef enum
+{
+  XSMP_STATE_IDLE,
+  XSMP_STATE_SAVE_YOURSELF,
+  XSMP_STATE_INTERACT_REQUEST,
+  XSMP_STATE_INTERACT,
+  XSMP_STATE_SAVE_YOURSELF_DONE,
+  XSMP_STATE_SHUTDOWN_CANCELLED,
+  XSMP_STATE_CONNECTION_CLOSED
+} EggSMClientXSMPState;
+
+static const char *state_names[] = {
+  "idle",
+  "save-yourself",
+  "interact-request",
+  "interact",
+  "save-yourself-done",
+  "shutdown-cancelled",
+  "connection-closed"
+};
+
+#define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state])
+
+struct _EggSMClientXSMP
+{
+  EggSMClient parent;
+
+  SmcConn connection;
+  char *client_id;
+
+  EggSMClientXSMPState state;
+  char **restart_command;
+  gboolean set_restart_command;
+  int restart_style;
+
+  guint idle;
+
+  /* Current SaveYourself state */
+  guint expecting_initial_save_yourself : 1;
+  guint need_save_state : 1;
+  guint need_quit_requested : 1;
+  guint interact_errors : 1;
+  guint shutting_down : 1;
+
+  /* Todo list */
+  guint waiting_to_set_initial_properties : 1;
+  guint waiting_to_emit_quit : 1;
+  guint waiting_to_emit_quit_cancelled : 1;
+  guint waiting_to_save_myself : 1;
+
+};
+
+struct _EggSMClientXSMPClass
+{
+  EggSMClientClass parent_class;
+
+};
+
+static void     sm_client_xsmp_startup (EggSMClient *client,
+                                       const char  *client_id);
+static void     sm_client_xsmp_set_restart_command (EggSMClient  *client,
+                                                   int           argc,
+                                                   const char  **argv);
+static void     sm_client_xsmp_will_quit (EggSMClient *client,
+                                         gboolean     will_quit);
+static gboolean sm_client_xsmp_end_session (EggSMClient         *client,
+                                           EggSMClientEndStyle  style,
+                                           gboolean  request_confirmation);
+
+static void xsmp_save_yourself      (SmcConn   smc_conn,
+                                    SmPointer client_data,
+                                    int       save_style,
+                                    Bool      shutdown,
+                                    int       interact_style,
+                                    Bool      fast);
+static void xsmp_die                (SmcConn   smc_conn,
+                                    SmPointer client_data);
+static void xsmp_save_complete      (SmcConn   smc_conn,
+                                    SmPointer client_data);
+static void xsmp_shutdown_cancelled (SmcConn   smc_conn,
+                                    SmPointer client_data);
+static void xsmp_interact           (SmcConn   smc_conn,
+                                    SmPointer client_data);
+
+static SmProp *array_prop        (const char    *name,
+                                 ...);
+static SmProp *ptrarray_prop     (const char    *name,
+                                 GPtrArray     *values);
+static SmProp *string_prop       (const char    *name,
+                                 const char    *value);
+static SmProp *card8_prop        (const char    *name,
+                                 unsigned char  value);
+
+static void set_properties         (EggSMClientXSMP *xsmp, ...);
+static void delete_properties      (EggSMClientXSMP *xsmp, ...);
+
+static GPtrArray *generate_command (char       **restart_command,
+                                   const char  *client_id,
+                                   const char  *state_file);
+
+static void save_state            (EggSMClientXSMP *xsmp);
+static void do_save_yourself      (EggSMClientXSMP *xsmp);
+static void update_pending_events (EggSMClientXSMP *xsmp);
+
+static void     ice_init             (void);
+static gboolean process_ice_messages (IceConn       ice_conn);
+static void     smc_error_handler    (SmcConn       smc_conn,
+                                     Bool          swap,
+                                     int           offending_minor_opcode,
+                                     unsigned long offending_sequence,
+                                     int           error_class,
+                                     int           severity,
+                                     SmPointer     values);
+
+G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT)
+
+static void
+egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp)
+{
+  xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
+  xsmp->connection = NULL;
+  xsmp->restart_style = SmRestartIfRunning;
+}
+
+static void
+egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass)
+{
+  EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
+
+  sm_client_class->startup             = sm_client_xsmp_startup;
+  sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command;
+  sm_client_class->will_quit           = sm_client_xsmp_will_quit;
+  sm_client_class->end_session         = sm_client_xsmp_end_session;
+}
+
+EggSMClient *
+egg_sm_client_xsmp_new (void)
+{
+  if (!g_getenv ("SESSION_MANAGER"))
+    return NULL;
+
+  return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL);
+}
+
+static gboolean
+sm_client_xsmp_set_initial_properties (gpointer user_data)
+{
+  EggSMClientXSMP *xsmp = user_data;
+  EggDesktopFile *desktop_file;
+  GPtrArray *clone, *restart;
+  char pid_str[64];
+
+  g_debug( "egg_sm_client_xsmp_set_initial_properties" );
+
+  if (xsmp->idle)
+    {
+      g_source_remove (xsmp->idle);
+      xsmp->idle = 0;
+    }
+  xsmp->waiting_to_set_initial_properties = FALSE;
+
+  if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART)
+    xsmp->restart_style = SmRestartNever;
+
+  /* Parse info out of desktop file */
+  desktop_file = egg_get_desktop_file ();
+  if (desktop_file)
+    {
+      GError *err = NULL;
+      char *cmdline, **argv;
+      int argc;
+
+      if (xsmp->restart_style == SmRestartIfRunning)
+       {
+         if (egg_desktop_file_get_boolean (desktop_file,
+                                           "X-GNOME-AutoRestart", NULL))
+           xsmp->restart_style = SmRestartImmediately;
+       }
+
+      if (!xsmp->set_restart_command)
+       {
+         cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err);
+         if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err))
+           {
+             egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp),
+                                                argc, (const char **)argv);
+             g_strfreev (argv);
+           }
+         else
+           {
+             g_warning ("Could not parse Exec line in desktop file: %s",
+                        err->message);
+             g_error_free (err);
+           }
+         g_free (cmdline);
+       }
+    }
+
+  if (!xsmp->set_restart_command)
+    xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1);
+
+  clone = generate_command (xsmp->restart_command, NULL, NULL);
+  restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
+
+  g_debug ("Setting initial properties");
+
+  /* Program, CloneCommand, RestartCommand, and UserID are required.
+   * ProcessID isn't required, but the SM may be able to do something
+   * useful with it.
+   */
+  g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ());
+  set_properties (xsmp,
+                 string_prop   (SmProgram, g_get_prgname ()),
+                 ptrarray_prop (SmCloneCommand, clone),
+                 ptrarray_prop (SmRestartCommand, restart),
+                 string_prop   (SmUserID, g_get_user_name ()),
+                 string_prop   (SmProcessID, pid_str),
+                 card8_prop    (SmRestartStyleHint, xsmp->restart_style),
+                 NULL);
+  g_ptr_array_free (clone, TRUE);
+  g_ptr_array_free (restart, TRUE);
+
+  if (desktop_file)
+    {
+      set_properties (xsmp,
+                     string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)),
+                     NULL);
+    }
+
+  update_pending_events (xsmp);
+  return FALSE;
+}
+
+/* This gets called from two different places: xsmp_die() (when the
+ * server asks us to disconnect) and process_ice_messages() (when the
+ * server disconnects unexpectedly).
+ */
+static void
+sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
+{
+  SmcConn connection;
+
+  g_debug( "egg_sm_client_xsmp_disconnect" );
+
+  if (!xsmp->connection)
+    return;
+
+  g_debug ("Disconnecting");
+
+  connection = xsmp->connection;
+  xsmp->connection = NULL;
+  SmcCloseConnection (connection, 0, NULL);
+  xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
+
+  xsmp->waiting_to_save_myself = FALSE;
+  update_pending_events (xsmp);
+}
+
+static void
+sm_client_xsmp_startup (EggSMClient *client,
+                       const char  *client_id)
+{
+       g_debug( "sm_client_xsmp_startup: client=%p, client_id=%s", ( void * ) client, client_id );
+
+  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+  SmcCallbacks callbacks;
+  char *ret_client_id;
+  char error_string_ret[256];
+
+  xsmp->client_id = g_strdup (client_id);
+
+  ice_init ();
+  SmcSetErrorHandler (smc_error_handler);
+
+  callbacks.save_yourself.callback      = xsmp_save_yourself;
+  callbacks.die.callback                = xsmp_die;
+  callbacks.save_complete.callback      = xsmp_save_complete;
+  callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
+
+  callbacks.save_yourself.client_data      = xsmp;
+  callbacks.die.client_data                = xsmp;
+  callbacks.save_complete.client_data      = xsmp;
+  callbacks.shutdown_cancelled.client_data = xsmp;
+
+  client_id = NULL;
+  error_string_ret[0] = '\0';
+  xsmp->connection =
+    SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
+                      SmcSaveYourselfProcMask | SmcDieProcMask |
+                      SmcSaveCompleteProcMask |
+                      SmcShutdownCancelledProcMask,
+                      &callbacks,
+                      xsmp->client_id, &ret_client_id,
+                      sizeof (error_string_ret), error_string_ret);
+
+  if (!xsmp->connection)
+    {
+      g_warning ("Failed to connect to the session manager: %s\n",
+                error_string_ret[0] ?
+                error_string_ret : "no error message given");
+      xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
+      return;
+    }
+
+  /* We expect a pointless initial SaveYourself if either (a) we
+   * didn't have an initial client ID, or (b) we DID have an initial
+   * client ID, but the server rejected it and gave us a new one.
+   */
+  if (!xsmp->client_id ||
+      (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0))
+    xsmp->expecting_initial_save_yourself = TRUE;
+
+  if (ret_client_id)
+    {
+      g_free (xsmp->client_id);
+      xsmp->client_id = g_strdup (ret_client_id);
+      free (ret_client_id);
+
+#if !GTK_CHECK_VERSION( 3,6,0 )
+      gdk_threads_enter ();
+#endif
+#if GTK_CHECK_VERSION( 2, 91, 7 )
+      gdk_x11_set_sm_client_id (xsmp->client_id);
+#else
+      gdk_set_sm_client_id (xsmp->client_id);
+#endif
+#if !GTK_CHECK_VERSION( 3,6,0 )
+      gdk_threads_leave ();
+#endif
+
+      g_debug ("Got client ID \"%s\"", xsmp->client_id);
+    }
+
+  xsmp->state = XSMP_STATE_IDLE;
+
+  /* Do not set the initial properties until we reach the main loop,
+   * so that the application has a chance to call
+   * egg_set_desktop_file(). (This may also help the session manager
+   * have a better idea of when the application is fully up and
+   * running.)
+   */
+  xsmp->waiting_to_set_initial_properties = TRUE;
+  xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client);
+}
+
+static void
+sm_client_xsmp_set_restart_command (EggSMClient  *client,
+                                   int           argc,
+                                   const char  **argv)
+{
+  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+  int i;
+
+  g_strfreev (xsmp->restart_command);
+
+  xsmp->restart_command = g_new (char *, argc + 1);
+  for (i = 0; i < argc; i++)
+    xsmp->restart_command[i] = g_strdup (argv[i]);
+  xsmp->restart_command[i] = NULL;
+
+  xsmp->set_restart_command = TRUE;
+}
+
+static void
+sm_client_xsmp_will_quit (EggSMClient *client,
+                         gboolean     will_quit)
+{
+  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+
+  g_debug( "egg_sm_client_xsmp_will_quit" );
+
+  if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED)
+    {
+      /* The session manager has already exited! Schedule a quit
+       * signal.
+       */
+      xsmp->waiting_to_emit_quit = TRUE;
+      update_pending_events (xsmp);
+      return;
+    }
+  else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+      /* We received a ShutdownCancelled message while the application
+       * was interacting; Schedule a quit_cancelled signal.
+       */
+      xsmp->waiting_to_emit_quit_cancelled = TRUE;
+      update_pending_events (xsmp);
+      return;
+    }
+
+  g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT);
+
+  g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True");
+  SmcInteractDone (xsmp->connection, !will_quit);
+
+  if (will_quit && xsmp->need_save_state)
+    save_state (xsmp);
+
+  g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False");
+  SmcSaveYourselfDone (xsmp->connection, will_quit);
+  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+}
+
+static gboolean
+sm_client_xsmp_end_session (EggSMClient         *client,
+                           EggSMClientEndStyle  style,
+                           gboolean             request_confirmation)
+{
+  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+  int save_type;
+
+  g_debug( "egg_sm_client_xsmp_end_session" );
+
+  /* To end the session via XSMP, we have to send a
+   * SaveYourselfRequest. We aren't allowed to do that if anything
+   * else is going on, but we don't want to expose this fact to the
+   * application. So we do our best to patch things up here...
+   *
+   * In the worst case, this method might block for some length of
+   * time in process_ice_messages, but the only time that code path is
+   * honestly likely to get hit is if the application tries to end the
+   * session as the very first thing it does, in which case it
+   * probably won't actually block anyway. It's not worth gunking up
+   * the API to try to deal nicely with the other 0.01% of cases where
+   * this happens.
+   */
+
+  while (xsmp->state != XSMP_STATE_IDLE ||
+        xsmp->expecting_initial_save_yourself)
+    {
+      /* If we're already shutting down, we don't need to do anything. */
+      if (xsmp->shutting_down)
+       return TRUE;
+
+      switch (xsmp->state)
+       {
+       case XSMP_STATE_CONNECTION_CLOSED:
+         return FALSE;
+
+       case XSMP_STATE_SAVE_YOURSELF:
+         /* Trying to log out from the save_state callback? Whatever.
+          * Abort the save_state.
+          */
+         SmcSaveYourselfDone (xsmp->connection, FALSE);
+         xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+         break;
+
+       case XSMP_STATE_INTERACT_REQUEST:
+       case XSMP_STATE_INTERACT:
+       case XSMP_STATE_SHUTDOWN_CANCELLED:
+         /* Already in a shutdown-related state, just ignore
+          * the new shutdown request...
+          */
+         return TRUE;
+
+       case XSMP_STATE_IDLE:
+         if (xsmp->waiting_to_set_initial_properties)
+           sm_client_xsmp_set_initial_properties (xsmp);
+
+         if (!xsmp->expecting_initial_save_yourself)
+           break;
+         /* else fall through */
+
+       case XSMP_STATE_SAVE_YOURSELF_DONE:
+         /* We need to wait for some response from the server.*/
+         process_ice_messages (SmcGetIceConnection (xsmp->connection));
+         break;
+
+       default:
+         /* Hm... shouldn't happen */
+         return FALSE;
+       }
+    }
+
+  /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and
+   * the user chooses to save the session. But gnome-session will do
+   * the wrong thing if we pass SmSaveBoth and the user chooses NOT to
+   * save the session... Sigh.
+   */
+  if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session"))
+    save_type = SmSaveBoth;
+  else
+    save_type = SmSaveGlobal;
+
+  g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", 
request_confirmation ? "!" : "");
+  SmcRequestSaveYourself (xsmp->connection,
+                         save_type,
+                         True, /* shutdown */
+                         SmInteractStyleAny,
+                         !request_confirmation, /* fast */
+                         True /* global */);
+  return TRUE;
+}
+
+static gboolean
+idle_do_pending_events (gpointer data)
+{
+  EggSMClientXSMP *xsmp = data;
+  EggSMClient *client = data;
+
+#if !GTK_CHECK_VERSION( 3,6,0 )
+  gdk_threads_enter ();
+#endif
+
+  xsmp->idle = 0;
+
+  if (xsmp->waiting_to_emit_quit)
+    {
+      xsmp->waiting_to_emit_quit = FALSE;
+      egg_sm_client_quit (client);
+      goto out;
+    }
+
+  if (xsmp->waiting_to_emit_quit_cancelled)
+    {
+      xsmp->waiting_to_emit_quit_cancelled = FALSE;
+      egg_sm_client_quit_cancelled (client);
+      xsmp->state = XSMP_STATE_IDLE;
+    }
+
+  if (xsmp->waiting_to_save_myself)
+    {
+      xsmp->waiting_to_save_myself = FALSE;
+      do_save_yourself (xsmp);
+    }
+
+ out:
+#if !GTK_CHECK_VERSION( 3,6,0 )
+  gdk_threads_leave ();
+#endif
+  return FALSE;
+}
+
+static void
+update_pending_events (EggSMClientXSMP *xsmp)
+{
+  gboolean want_idle =
+    xsmp->waiting_to_emit_quit ||
+    xsmp->waiting_to_emit_quit_cancelled ||
+    xsmp->waiting_to_save_myself;
+
+  if (want_idle)
+    {
+      if (xsmp->idle == 0)
+       xsmp->idle = g_idle_add (idle_do_pending_events, xsmp);
+    }
+  else
+    {
+      if (xsmp->idle != 0)
+       g_source_remove (xsmp->idle);
+      xsmp->idle = 0;
+    }
+}
+
+static void
+fix_broken_state (EggSMClientXSMP *xsmp, const char *message,
+                 gboolean send_interact_done,
+                 gboolean send_save_yourself_done)
+{
+  g_warning ("Received XSMP %s message in state %s: client or server error",
+            message, EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  /* Forget any pending SaveYourself plans we had */
+  xsmp->waiting_to_save_myself = FALSE;
+  update_pending_events (xsmp);
+
+  if (send_interact_done)
+    SmcInteractDone (xsmp->connection, False);
+  if (send_save_yourself_done)
+    SmcSaveYourselfDone (xsmp->connection, True);
+
+  xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE;
+}
+
+/* SM callbacks */
+
+static void
+xsmp_save_yourself (SmcConn   smc_conn,
+                   SmPointer client_data,
+                   int       save_type,
+                   Bool      shutdown,
+                   int       interact_style,
+                   Bool      fast)
+{
+  EggSMClientXSMP *xsmp = client_data;
+  gboolean wants_quit_requested;
+
+  g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
+          save_type == SmSaveLocal ? "SmSaveLocal" :
+          save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
+          shutdown ? "Shutdown" : "!Shutdown",
+          interact_style == SmInteractStyleAny ? "SmInteractStyleAny" :
+          interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
+          "SmInteractStyleNone", fast ? "Fast" : "!Fast",
+          EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  if (xsmp->state != XSMP_STATE_IDLE &&
+      xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+      fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE);
+      return;
+    }
+
+  if (xsmp->waiting_to_set_initial_properties)
+    sm_client_xsmp_set_initial_properties (xsmp);
+
+  /* If this is the initial SaveYourself, ignore it; we've already set
+   * properties and there's no reason to actually save state too.
+   */
+  if (xsmp->expecting_initial_save_yourself)
+    {
+      xsmp->expecting_initial_save_yourself = FALSE;
+
+      if (save_type == SmSaveLocal &&
+         interact_style == SmInteractStyleNone &&
+         !shutdown && !fast)
+       {
+         g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself");
+         SmcSaveYourselfDone (xsmp->connection, True);
+         /* As explained in the comment at the end of
+          * do_save_yourself(), SAVE_YOURSELF_DONE is the correct
+          * state here, not IDLE.
+          */
+         xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+         return;
+       }
+      else
+       g_warning ("First SaveYourself was not the expected one!");
+    }
+
+  /* Even ignoring the "fast" flag completely, there are still 18
+   * different combinations of save_type, shutdown and interact_style.
+   * We interpret them as follows:
+   *
+   *   Type  Shutdown  Interact         Interpretation
+   *     G      F       A/E/N           do nothing (1)
+   *     G      T         N             do nothing (1)*
+   *     G      T        A/E            quit_requested (2)
+   *    L/B     F       A/E/N           save_state (3)
+   *    L/B     T         N             save_state (3)*
+   *    L/B     T        A/E            quit_requested, then save_state (4)
+   *
+   *   1. Do nothing, because the SM asked us to do something
+   *      uninteresting (save open files, but then don't quit
+   *      afterward) or rude (save open files without asking the user
+   *      for confirmation).
+   *
+   *   2. Request interaction and then emit ::quit_requested. This
+   *      perhaps isn't quite correct for the SmInteractStyleErrors
+   *      case, but we don't care.
+   *
+   *   3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these
+   *      rows essentially get demoted to SmSaveLocal, because their
+   *      Global halves correspond to "do nothing".
+   *
+   *   4. Request interaction, emit ::quit_requested, and then emit
+   *      ::save_state after interacting. This is the SmSaveBoth
+   *      equivalent of #2, but we also promote SmSaveLocal shutdown
+   *      SaveYourselfs to SmSaveBoth here, because we want to give
+   *      the user a chance to save open files before quitting.
+   *
+   * (* It would be nice if we could do something useful when the
+   * session manager sends a SaveYourself with shutdown True and
+   * SmInteractStyleNone. But we can't, so we just pretend it didn't
+   * even tell us it was shutting down. The docs for ::quit mention
+   * that it might not always be preceded by ::quit_requested.)
+   */
+
+  /* As an optimization, we don't actually request interaction and
+   * emit ::quit_requested if the application isn't listening to the
+   * signal.
+   */
+  wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", 
EGG_TYPE_SM_CLIENT), 0, FALSE);
+
+  xsmp->need_save_state     = (save_type != SmSaveGlobal);
+  xsmp->need_quit_requested = (shutdown && wants_quit_requested &&
+                              interact_style != SmInteractStyleNone);
+  xsmp->interact_errors     = (interact_style == SmInteractStyleErrors);
+
+  xsmp->shutting_down       = shutdown;
+
+  do_save_yourself (xsmp);
+}
+
+static void
+do_save_yourself (EggSMClientXSMP *xsmp)
+{
+       g_debug( "egg_sm_client_xsmp_do_save_yourself" );
+
+  if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+      /* The SM cancelled a previous SaveYourself, but we haven't yet
+       * had a chance to tell the application, so we can't start
+       * processing this SaveYourself yet.
+       */
+      xsmp->waiting_to_save_myself = TRUE;
+      update_pending_events (xsmp);
+      return;
+    }
+
+  if (xsmp->need_quit_requested)
+    {
+      xsmp->state = XSMP_STATE_INTERACT_REQUEST;
+
+      g_debug ("Sending InteractRequest(%s)",
+              xsmp->interact_errors ? "Error" : "Normal");
+      SmcInteractRequest (xsmp->connection,
+                         xsmp->interact_errors ? SmDialogError : SmDialogNormal,
+                         xsmp_interact,
+                         xsmp);
+      return;
+    }
+
+  if (xsmp->need_save_state)
+    {
+      save_state (xsmp);
+
+      /* Though unlikely, the client could have been disconnected
+       * while the application was saving its state.
+       */
+      if (!xsmp->connection)
+        return;
+    }
+
+  g_debug ("Sending SaveYourselfDone(True)");
+  SmcSaveYourselfDone (xsmp->connection, True);
+
+  /* The client state diagram in the XSMP spec says that after a
+   * non-shutdown SaveYourself, we go directly back to "idle". But
+   * everything else in both the XSMP spec and the libSM docs
+   * disagrees.
+   */
+  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+}
+
+static void
+save_state (EggSMClientXSMP *xsmp)
+{
+  GKeyFile *state_file;
+  char *state_file_path, *data;
+  EggDesktopFile *desktop_file;
+  GPtrArray *restart;
+  int offset, fd;
+
+  /* We set xsmp->state before emitting save_state, but our caller is
+   * responsible for setting it back afterward.
+   */
+  xsmp->state = XSMP_STATE_SAVE_YOURSELF;
+
+  state_file = egg_sm_client_save_state ((EggSMClient *)xsmp);
+  if (!state_file)
+    {
+      restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
+      set_properties (xsmp,
+                     ptrarray_prop (SmRestartCommand, restart),
+                     NULL);
+      g_ptr_array_free (restart, TRUE);
+      delete_properties (xsmp, SmDiscardCommand, NULL);
+      return;
+    }
+
+  desktop_file = egg_get_desktop_file ();
+  if (desktop_file)
+    {
+      GKeyFile *merged_file;
+      char *desktop_file_path;
+
+      merged_file = g_key_file_new ();
+      desktop_file_path =
+       g_filename_from_uri (egg_desktop_file_get_source (desktop_file),
+                            NULL, NULL);
+      if (desktop_file_path &&
+         g_key_file_load_from_file (merged_file, desktop_file_path,
+                                    G_KEY_FILE_KEEP_COMMENTS |
+                                    G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
+       {
+         guint g, k, i;
+         char **groups, **keys, *value, *exec;
+
+         groups = g_key_file_get_groups (state_file, NULL);
+         for (g = 0; groups[g]; g++)
+           {
+             keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL);
+             for (k = 0; keys[k]; k++)
+               {
+                 value = g_key_file_get_value (state_file, groups[g],
+                                               keys[k], NULL);
+                 if (value)
+                   {
+                     g_key_file_set_value (merged_file, groups[g],
+                                           keys[k], value);
+                     g_free (value);
+                   }
+               }
+             g_strfreev (keys);
+           }
+         g_strfreev (groups);
+
+         g_key_file_free (state_file);
+         state_file = merged_file;
+
+         /* Update Exec key using "--sm-client-state-file %k" */
+         restart = generate_command (xsmp->restart_command,
+                                     NULL, "%k");
+         for (i = 0; i < restart->len; i++)
+           restart->pdata[i] = g_shell_quote (restart->pdata[i]);
+         g_ptr_array_add (restart, NULL);
+         exec = g_strjoinv (" ", (char **)restart->pdata);
+         g_strfreev ((char **)restart->pdata);
+         g_ptr_array_free (restart, FALSE);
+
+         g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
+                                EGG_DESKTOP_FILE_KEY_EXEC,
+                                exec);
+         g_free (exec);
+       }
+      else
+       desktop_file = NULL;
+
+      g_free (desktop_file_path);
+    }
+
+  /* Now write state_file to disk. (We can't use mktemp(), because
+   * that requires the filename to end with "XXXXXX", and we want
+   * it to end with ".desktop".)
+   */
+
+  data = g_key_file_to_data (state_file, NULL, NULL);
+  g_key_file_free (state_file);
+
+  offset = 0;
+  while (1)
+    {
+      state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s",
+                                        g_get_user_config_dir (),
+                                        G_DIR_SEPARATOR, G_DIR_SEPARATOR,
+                                        g_get_prgname (),
+                                        (long)time (NULL) + offset,
+                                        desktop_file ? "desktop" : "state");
+
+      fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644);
+      if (fd == -1)
+       {
+         if (errno == EEXIST)
+           {
+             offset++;
+             g_free (state_file_path);
+             continue;
+           }
+         else if (errno == ENOTDIR || errno == ENOENT)
+           {
+             char *sep = strrchr (state_file_path, G_DIR_SEPARATOR);
+
+             *sep = '\0';
+             if (g_mkdir_with_parents (state_file_path, 0755) != 0)
+               {
+                 g_warning ("Could not create directory '%s'",
+                            state_file_path);
+                 g_free (state_file_path);
+                 state_file_path = NULL;
+                 break;
+               }
+
+             continue;
+           }
+
+         g_warning ("Could not create file '%s': %s",
+                    state_file_path, g_strerror (errno));
+         g_free (state_file_path);
+         state_file_path = NULL;
+         break;
+       }
+
+      close (fd);
+      g_file_set_contents (state_file_path, data, -1, NULL);
+      break;
+    }
+  g_free (data);
+
+  restart = generate_command (xsmp->restart_command, xsmp->client_id,
+                             state_file_path);
+  set_properties (xsmp,
+                 ptrarray_prop (SmRestartCommand, restart),
+                 NULL);
+  g_ptr_array_free (restart, TRUE);
+
+  if (state_file_path)
+    {
+      set_properties (xsmp,
+                     array_prop (SmDiscardCommand,
+                                 "/bin/rm", "-rf", state_file_path,
+                                 NULL),
+                     NULL);
+      g_free (state_file_path);
+    }
+}
+
+static void
+xsmp_interact (SmcConn   smc_conn,
+              SmPointer client_data)
+{
+  EggSMClientXSMP *xsmp = client_data;
+  EggSMClient *client = client_data;
+
+  g_debug ("Received Interact message in state %s",
+          EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  if (xsmp->state != XSMP_STATE_INTERACT_REQUEST)
+    {
+      fix_broken_state (xsmp, "Interact", TRUE, TRUE);
+      return;
+    }
+
+  xsmp->state = XSMP_STATE_INTERACT;
+  egg_sm_client_quit_requested (client);
+}
+
+static void
+xsmp_die (SmcConn   smc_conn,
+         SmPointer client_data)
+{
+  EggSMClientXSMP *xsmp = client_data;
+  EggSMClient *client = client_data;
+
+  g_debug ("Received Die message in state %s",
+          EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  sm_client_xsmp_disconnect (xsmp);
+  egg_sm_client_quit (client);
+}
+
+static void
+xsmp_save_complete (SmcConn   smc_conn,
+                   SmPointer client_data)
+{
+  EggSMClientXSMP *xsmp = client_data;
+
+  g_debug ("Received SaveComplete message in state %s",
+          EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
+    xsmp->state = XSMP_STATE_IDLE;
+  else
+    fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE);
+}
+
+static void
+xsmp_shutdown_cancelled (SmcConn   smc_conn,
+                        SmPointer client_data)
+{
+  EggSMClientXSMP *xsmp = client_data;
+  EggSMClient *client = client_data;
+
+  g_debug ("Received ShutdownCancelled message in state %s",
+          EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  xsmp->shutting_down = FALSE;
+
+  if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
+    {
+      /* We've finished interacting and now the SM has agreed to
+       * cancel the shutdown.
+       */
+      xsmp->state = XSMP_STATE_IDLE;
+      egg_sm_client_quit_cancelled (client);
+    }
+  else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+      /* Hm... ok, so we got a shutdown SaveYourself, which got
+       * cancelled, but the application was still interacting, so we
+       * didn't tell it yet, and then *another* SaveYourself arrived,
+       * which we must still be waiting to tell the app about, except
+       * that now that SaveYourself has been cancelled too! Dizzy yet?
+       */
+      xsmp->waiting_to_save_myself = FALSE;
+      update_pending_events (xsmp);
+    }
+  else
+    {
+      g_debug ("Sending SaveYourselfDone(False)");
+      SmcSaveYourselfDone (xsmp->connection, False);
+
+      if (xsmp->state == XSMP_STATE_INTERACT)
+       {
+         /* The application is currently interacting, so we can't
+          * tell it about the cancellation yet; we will wait until
+          * after it calls egg_sm_client_will_quit().
+          */
+         xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED;
+       }
+      else
+       {
+         /* The shutdown was cancelled before the application got a
+          * chance to interact.
+          */
+         xsmp->state = XSMP_STATE_IDLE;
+       }
+    }
+}
+
+/* Utilities */
+
+/* Create a restart/clone/Exec command based on @restart_command.
+ * If @client_id is non-%NULL, add "--sm-client-id @client_id".
+ * If @state_file is non-%NULL, add "--sm-client-state-file @state_file".
+ *
+ * None of the input strings are g_strdup()ed; the caller must keep
+ * them around until it is done with the returned GPtrArray, and must
+ * then free the array, but not its contents.
+ */
+static GPtrArray *
+generate_command (char **restart_command, const char *client_id,
+                 const char *state_file)
+{
+  GPtrArray *cmd;
+  int i;
+
+  cmd = g_ptr_array_new ();
+  g_ptr_array_add (cmd, restart_command[0]);
+
+  if (client_id)
+    {
+      g_ptr_array_add (cmd, (char *)"--sm-client-id");
+      g_ptr_array_add (cmd, (char *)client_id);
+    }
+
+  if (state_file)
+    {
+      g_ptr_array_add (cmd, (char *)"--sm-client-state-file");
+      g_ptr_array_add (cmd, (char *)state_file);
+    }
+
+  for (i = 1; restart_command[i]; i++)
+    g_ptr_array_add (cmd, restart_command[i]);
+
+  return cmd;
+}
+
+/* Takes a NULL-terminated list of SmProp * values, created by
+ * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and
+ * frees them.
+ */
+static void
+set_properties (EggSMClientXSMP *xsmp, ...)
+{
+  GPtrArray *props;
+  SmProp *prop;
+  va_list ap;
+  guint i;
+
+  props = g_ptr_array_new ();
+
+  va_start (ap, xsmp);
+  while ((prop = va_arg (ap, SmProp *)))
+    g_ptr_array_add (props, prop);
+  va_end (ap);
+
+  if (xsmp->connection)
+    {
+      SmcSetProperties (xsmp->connection, props->len,
+                       (SmProp **)props->pdata);
+    }
+
+  for (i = 0; i < props->len; i++)
+    {
+      prop = props->pdata[i];
+      g_free (prop->vals);
+      g_free (prop);
+    }
+  g_ptr_array_free (props, TRUE);
+}
+
+/* Takes a NULL-terminated list of property names and deletes them. */
+static void
+delete_properties (EggSMClientXSMP *xsmp, ...)
+{
+  GPtrArray *props;
+  char *prop;
+  va_list ap;
+
+  if (!xsmp->connection)
+    return;
+
+  props = g_ptr_array_new ();
+
+  va_start (ap, xsmp);
+  while ((prop = va_arg (ap, char *)))
+    g_ptr_array_add (props, prop);
+  va_end (ap);
+
+  SmcDeleteProperties (xsmp->connection, props->len,
+                      (char **)props->pdata);
+
+  g_ptr_array_free (props, TRUE);
+}
+
+/* Takes an array of strings and creates a LISTofARRAY8 property. The
+ * strings are neither dupped nor freed; they need to remain valid
+ * until you're done with the SmProp.
+ */
+static SmProp *
+array_prop (const char *name, ...)
+{
+  SmProp *prop;
+  SmPropValue pv;
+  GArray *vals;
+  char *value;
+  va_list ap;
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = (char *)SmLISTofARRAY8;
+
+  vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
+
+  va_start (ap, name);
+  while ((value = va_arg (ap, char *)))
+    {
+      pv.length = strlen (value);
+      pv.value = value;
+      g_array_append_val (vals, pv);
+    }
+
+  prop->num_vals = vals->len;
+  prop->vals = (SmPropValue *)vals->data;
+
+  g_array_free (vals, FALSE);
+
+  return prop;
+}
+
+/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property.
+ * The array contents are neither dupped nor freed; they need to
+ * remain valid until you're done with the SmProp.
+ */
+static SmProp *
+ptrarray_prop (const char *name, GPtrArray *values)
+{
+  SmProp *prop;
+  SmPropValue pv;
+  GArray *vals;
+  guint i;
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = (char *)SmLISTofARRAY8;
+
+  vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
+
+  for (i = 0; i < values->len; i++)
+    {
+      pv.length = strlen (values->pdata[i]);
+      pv.value = values->pdata[i];
+      g_array_append_val (vals, pv);
+    }
+
+  prop->num_vals = vals->len;
+  prop->vals = (SmPropValue *)vals->data;
+
+  g_array_free (vals, FALSE);
+
+  return prop;
+}
+
+/* Takes a string and creates an ARRAY8 property. The string is
+ * neither dupped nor freed; it needs to remain valid until you're
+ * done with the SmProp.
+ */
+static SmProp *
+string_prop (const char *name, const char *value)
+{
+  SmProp *prop;
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = (char *)SmARRAY8;
+
+  prop->num_vals = 1;
+  prop->vals = g_new (SmPropValue, 1);
+
+  prop->vals[0].length = strlen (value);
+  prop->vals[0].value = (char *)value;
+
+  return prop;
+}
+
+/* Takes a char and creates a CARD8 property. */
+static SmProp *
+card8_prop (const char *name, unsigned char value)
+{
+  SmProp *prop;
+  char *card8val;
+
+  /* To avoid having to allocate and free prop->vals[0], we cheat and
+   * make vals a 2-element-long array and then use the second element
+   * to store value.
+   */
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = (char *)SmCARD8;
+
+  prop->num_vals = 1;
+  prop->vals = g_new (SmPropValue, 2);
+  card8val = (char *)(&prop->vals[1]);
+  card8val[0] = value;
+
+  prop->vals[0].length = 1;
+  prop->vals[0].value = card8val;
+
+  return prop;
+}
+
+/* ICE code. This makes no effort to play nice with anyone else trying
+ * to use libICE. Fortunately, no one uses libICE for anything other
+ * than SM. (DCOP uses ICE, but it has its own private copy of
+ * libICE.)
+ *
+ * When this moves to gtk, it will need to be cleverer, to avoid
+ * tripping over old apps that use GnomeClient or that use libSM
+ * directly.
+ */
+
+#include <X11/ICE/ICElib.h>
+#include <fcntl.h>
+
+static void        ice_error_handler    (IceConn        ice_conn,
+                                        Bool           swap,
+                                        int            offending_minor_opcode,
+                                        unsigned long  offending_sequence,
+                                        int            error_class,
+                                        int            severity,
+                                        IcePointer     values);
+static void        ice_io_error_handler (IceConn        ice_conn);
+static void        ice_connection_watch (IceConn        ice_conn,
+                                        IcePointer     client_data,
+                                        Bool           opening,
+                                        IcePointer    *watch_data);
+
+static void
+ice_init (void)
+{
+  IceSetIOErrorHandler (ice_io_error_handler);
+  IceSetErrorHandler (ice_error_handler);
+  IceAddConnectionWatch (ice_connection_watch, NULL);
+}
+
+static gboolean
+process_ice_messages (IceConn ice_conn)
+{
+  IceProcessMessagesStatus status;
+
+#if !GTK_CHECK_VERSION( 3,6,0 )
+  gdk_threads_enter ();
+#endif
+  status = IceProcessMessages (ice_conn, NULL, NULL);
+#if !GTK_CHECK_VERSION( 3,6,0 )
+  gdk_threads_leave ();
+#endif
+
+  switch (status)
+    {
+    case IceProcessMessagesSuccess:
+      return TRUE;
+
+    case IceProcessMessagesIOError:
+      sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
+      return FALSE;
+
+    case IceProcessMessagesConnectionClosed:
+      return FALSE;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static gboolean
+ice_iochannel_watch (GIOChannel   *channel,
+                    GIOCondition  condition,
+                    gpointer      client_data)
+{
+  return process_ice_messages (client_data);
+}
+
+static void
+ice_connection_watch (IceConn     ice_conn,
+                     IcePointer  client_data,
+                     Bool        opening,
+                     IcePointer *watch_data)
+{
+  guint watch_id;
+
+  if (opening)
+    {
+      GIOChannel *channel;
+      int fd = IceConnectionNumber (ice_conn);
+
+      fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
+      channel = g_io_channel_unix_new (fd);
+      watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR,
+                                ice_iochannel_watch, ice_conn);
+      g_io_channel_unref (channel);
+
+      *watch_data = GUINT_TO_POINTER (watch_id);
+    }
+  else
+    {
+      watch_id = GPOINTER_TO_UINT (*watch_data);
+      g_source_remove (watch_id);
+    }
+}
+
+static void
+ice_error_handler (IceConn       ice_conn,
+                  Bool          swap,
+                  int           offending_minor_opcode,
+                  unsigned long offending_sequence,
+                  int           error_class,
+                  int           severity,
+                  IcePointer    values)
+{
+  /* Do nothing */
+}
+
+static void
+ice_io_error_handler (IceConn ice_conn)
+{
+  /* Do nothing */
+}
+
+static void
+smc_error_handler (SmcConn       smc_conn,
+                   Bool          swap,
+                   int           offending_minor_opcode,
+                   unsigned long offending_sequence,
+                   int           error_class,
+                   int           severity,
+                   SmPointer     values)
+{
+  /* Do nothing */
+}
diff --git a/src/nact/egg-sm-client.c b/src/nact/egg-sm-client.c
new file mode 100644
index 0000000..3e7060d
--- /dev/null
+++ b/src/nact/egg-sm-client.c
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "egg-sm-client.h"
+#include "egg-sm-client-private.h"
+
+/* pwi 2009-11-23 disable this specific log handler */
+/*static void egg_sm_client_debug_handler (const char *log_domain,
+                                        GLogLevelFlags log_level,
+                                        const char *message,
+                                        gpointer user_data);*/
+
+enum {
+  SAVE_STATE,
+  QUIT_REQUESTED,
+  QUIT_CANCELLED,
+  QUIT,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+struct _EggSMClientPrivate {
+  GKeyFile *state_file;
+};
+
+#define EGG_SM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_SM_CLIENT, 
EggSMClientPrivate))
+
+G_DEFINE_TYPE (EggSMClient, egg_sm_client, G_TYPE_OBJECT)
+
+static EggSMClient *global_client;
+static EggSMClientMode global_client_mode = EGG_SM_CLIENT_MODE_NORMAL;
+
+static void
+egg_sm_client_init (EggSMClient *client)
+{
+  ;
+}
+
+static void
+egg_sm_client_class_init (EggSMClientClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (EggSMClientPrivate));
+
+  /**
+   * EggSMClient::save_state:
+   * @client: the client
+   * @state_file: a #GKeyFile to save state information into
+   *
+   * Emitted when the session manager has requested that the
+   * application save information about its current state. The
+   * application should save its state into @state_file, and then the
+   * session manager may then restart the application in a future
+   * session and tell it to initialize itself from that state.
+   *
+   * You should not save any data into @state_file's "start group"
+   * (ie, the %NULL group). Instead, applications should save their
+   * data into groups with names that start with the application name,
+   * and libraries that connect to this signal should save their data
+   * into groups with names that start with the library name.
+   *
+   * Alternatively, rather than (or in addition to) using @state_file,
+   * the application can save its state by calling
+   * egg_sm_client_set_restart_command() during the processing of this
+   * signal (eg, to include a list of files to open).
+   **/
+  signals[SAVE_STATE] =
+    g_signal_new ("save_state",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, save_state),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__POINTER,
+                  G_TYPE_NONE,
+                  1, G_TYPE_POINTER);
+
+  /**
+   * EggSMClient::quit_requested:
+   * @client: the client
+   *
+   * Emitted when the session manager requests that the application
+   * exit (generally because the user is logging out). The application
+   * should decide whether or not it is willing to quit (perhaps after
+   * asking the user what to do with documents that have unsaved
+   * changes) and then call egg_sm_client_will_quit(), passing %TRUE
+   * or %FALSE to give its answer to the session manager. (It does not
+   * need to give an answer before returning from the signal handler;
+   * it can interact with the user asynchronously and then give its
+   * answer later on.) If the application does not connect to this
+   * signal, then #EggSMClient will automatically return %TRUE on its
+   * behalf.
+   *
+   * The application should not save its session state as part of
+   * handling this signal; if the user has requested that the session
+   * be saved when logging out, then ::save_state will be emitted
+   * separately.
+   *
+   * If the application agrees to quit, it should then wait for either
+   * the ::quit_cancelled or ::quit signals to be emitted.
+   **/
+  signals[QUIT_REQUESTED] =
+    g_signal_new ("quit_requested",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, quit_requested),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+
+  /**
+   * EggSMClient::quit_cancelled:
+   * @client: the client
+   *
+   * Emitted when the session manager decides to cancel a logout after
+   * the application has already agreed to quit. After receiving this
+   * signal, the application can go back to what it was doing before
+   * receiving the ::quit_requested signal.
+   **/
+  signals[QUIT_CANCELLED] =
+    g_signal_new ("quit_cancelled",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, quit_cancelled),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+
+  /**
+   * EggSMClient::quit:
+   * @client: the client
+   *
+   * Emitted when the session manager wants the application to quit
+   * (generally because the user is logging out). The application
+   * should exit as soon as possible after receiving this signal; if
+   * it does not, the session manager may choose to forcibly kill it.
+   *
+   * Normally a GUI application would only be sent a ::quit if it
+   * agreed to quit in response to a ::quit_requested signal. However,
+   * this is not guaranteed; in some situations the session manager
+   * may decide to end the session without giving applications a
+   * chance to object.
+   **/
+  signals[QUIT] =
+    g_signal_new ("quit",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, quit),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+}
+
+static gboolean sm_client_disable = FALSE;
+static gboolean has_startup_run = FALSE;
+static char *sm_client_state_file = NULL;
+static char *sm_client_id = NULL;
+static char *sm_config_prefix = NULL;
+
+static gboolean
+sm_client_post_parse_func (GOptionContext  *context,
+                          GOptionGroup    *group,
+                          gpointer         data,
+                          GError         **error)
+{
+  egg_sm_client_startup();
+
+  return TRUE;
+}
+
+void
+egg_sm_client_startup (void)
+{
+       if( has_startup_run )
+               return;
+
+  EggSMClient *client = egg_sm_client_get ();
+
+  if (sm_client_id == NULL)
+    {
+      const gchar *desktop_autostart_id;
+
+      desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
+
+      if (desktop_autostart_id != NULL)
+        sm_client_id = g_strdup (desktop_autostart_id);
+    }
+
+  /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
+   * use the same client id. */
+  g_unsetenv ("DESKTOP_AUTOSTART_ID");
+
+  if (EGG_SM_CLIENT_GET_CLASS (client)->startup)
+    EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id);
+
+  has_startup_run = TRUE;
+}
+
+/**
+ * egg_sm_client_get_option_group:
+ *
+ * Creates a %GOptionGroup containing the session-management-related
+ * options. You should add this group to the application's
+ * %GOptionContext if you want to use #EggSMClient.
+ *
+ * Return value: the %GOptionGroup
+ **/
+GOptionGroup *
+egg_sm_client_get_option_group (void)
+{
+  const GOptionEntry entries[] = {
+    { "sm-client-disable", 0, 0,
+      G_OPTION_ARG_NONE, &sm_client_disable,
+      N_("Disable connection to session manager"), NULL },
+    { "sm-client-state-file", 0, 0,
+      G_OPTION_ARG_FILENAME, &sm_client_state_file,
+      N_("Specify file containing saved configuration"), N_("FILE") },
+    { "sm-client-id", 0, 0,
+      G_OPTION_ARG_STRING, &sm_client_id,
+      N_("Specify session management ID"), N_("ID") },
+    /* GnomeClient compatibility option */
+    { "sm-disable", 0, G_OPTION_FLAG_HIDDEN,
+      G_OPTION_ARG_NONE, &sm_client_disable,
+      NULL, NULL },
+    /* GnomeClient compatibility option. This is a dummy option that only
+     * exists so that sessions saved by apps with GnomeClient can be restored
+     * later when they've switched to EggSMClient. See bug #575308.
+     */
+    { "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN,
+      G_OPTION_ARG_STRING, &sm_config_prefix,
+      NULL, NULL },
+    { NULL }
+  };
+  GOptionGroup *group;
+
+  /* Use our own debug handler for the "EggSMClient" domain. */
+       /* pwi 2009-11-23 disable this specific log handler */
+       /* g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
+                    egg_sm_client_debug_handler, NULL);*/
+
+  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_parse_hooks (group, NULL, sm_client_post_parse_func);
+
+  return group;
+}
+
+/**
+ * egg_sm_client_set_mode:
+ * @mode: an #EggSMClient mode
+ *
+ * Sets the "mode" of #EggSMClient as follows:
+ *
+ *    %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely
+ *    disabled. The application will not even connect to the session
+ *    manager. (egg_sm_client_get() will still return an #EggSMClient,
+ *    but it will just be a dummy object.)
+ *
+ *    %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to
+ *    the session manager (and thus will receive notification when the
+ *    user is logging out, etc), but will request to not be
+ *    automatically restarted with saved state in future sessions.
+ *
+ *    %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will
+ *    function normally.
+ *
+ * This must be called before the application's main loop begins.
+ **/
+void
+egg_sm_client_set_mode (EggSMClientMode mode)
+{
+  global_client_mode = mode;
+}
+
+/**
+ * egg_sm_client_get_mode:
+ *
+ * Gets the global #EggSMClientMode. See egg_sm_client_set_mode()
+ * for details.
+ *
+ * Return value: the global #EggSMClientMode
+ **/
+EggSMClientMode
+egg_sm_client_get_mode (void)
+{
+  return global_client_mode;
+}
+
+/**
+ * egg_sm_client_get:
+ *
+ * Returns the master #EggSMClient for the application.
+ *
+ * On platforms that support saved sessions (ie, POSIX/X11), the
+ * application will only request to be restarted by the session
+ * manager if you call egg_set_desktop_file() to set an application
+ * desktop file. In particular, if the desktop file contains the key
+ * "X
+ *
+ * Return value: the master #EggSMClient.
+ **/
+EggSMClient *
+egg_sm_client_get (void)
+{
+  if (!global_client)
+    {
+      if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED &&
+         !sm_client_disable)
+       {
+#if defined (GDK_WINDOWING_WIN32)
+         global_client = egg_sm_client_win32_new ();
+#elif defined (GDK_WINDOWING_QUARTZ)
+         global_client = egg_sm_client_osx_new ();
+#else
+         /* If both D-Bus and XSMP are compiled in, try XSMP first
+          * (since it supports state saving) and fall back to D-Bus
+          * if XSMP isn't available.
+          */
+# ifdef EGG_SM_CLIENT_BACKEND_XSMP
+         g_debug( "egg_sm_client_get: egg_sm_client_xsmp_new" );
+         global_client = egg_sm_client_xsmp_new ();
+# endif
+# ifdef EGG_SM_CLIENT_BACKEND_DBUS
+         if (!global_client)
+           global_client = egg_sm_client_dbus_new ();
+# endif
+#endif
+       }
+
+      /* Fallback: create a dummy client, so that callers don't have
+       * to worry about a %NULL return value.
+       */
+      if (!global_client) {
+    g_debug( "egg_sm_client_get: allocating dummy client" );
+       global_client = g_object_new (EGG_TYPE_SM_CLIENT, NULL);
+    }
+  }
+
+  return global_client;
+}
+
+/**
+ * egg_sm_client_is_resumed:
+ * @client: the client
+ *
+ * Checks whether or not the current session has been resumed from
+ * a previous saved session. If so, the application should call
+ * egg_sm_client_get_state_file() and restore its state from the
+ * returned #GKeyFile.
+ *
+ * Return value: %TRUE if the session has been resumed
+ **/
+gboolean
+egg_sm_client_is_resumed (EggSMClient *client)
+{
+  g_return_val_if_fail (client == global_client, FALSE);
+
+  return sm_client_state_file != NULL;
+}
+
+/**
+ * egg_sm_client_get_state_file:
+ * @client: the client
+ *
+ * If the application was resumed by the session manager, this will
+ * return the #GKeyFile containing its state from the previous
+ * session.
+ *
+ * Note that other libraries and #EggSMClient itself may also store
+ * state in the key file, so if you call egg_sm_client_get_groups(),
+ * on it, the return value will likely include groups that you did not
+ * put there yourself. (It is also not guaranteed that the first
+ * group created by the application will still be the "start group"
+ * when it is resumed.)
+ *
+ * Return value: the #GKeyFile containing the application's earlier
+ * state, or %NULL on error. You should not free this key file; it
+ * is owned by @client.
+ **/
+GKeyFile *
+egg_sm_client_get_state_file (EggSMClient *client)
+{
+  EggSMClientPrivate *priv = EGG_SM_CLIENT_GET_PRIVATE (client);
+  char *state_file_path;
+  GError *err = NULL;
+
+  g_return_val_if_fail (client == global_client, NULL);
+
+  if (!sm_client_state_file)
+    return NULL;
+  if (priv->state_file)
+    return priv->state_file;
+
+  if (!strncmp (sm_client_state_file, "file://", 7))
+    state_file_path = g_filename_from_uri (sm_client_state_file, NULL, NULL);
+  else
+    state_file_path = g_strdup (sm_client_state_file);
+
+  priv->state_file = g_key_file_new ();
+  if (!g_key_file_load_from_file (priv->state_file, state_file_path, 0, &err))
+    {
+      g_warning ("Could not load SM state file '%s': %s",
+                sm_client_state_file, err->message);
+      g_clear_error (&err);
+      g_key_file_free (priv->state_file);
+      priv->state_file = NULL;
+    }
+
+  g_free (state_file_path);
+  return priv->state_file;
+}
+
+/**
+ * egg_sm_client_set_restart_command:
+ * @client: the client
+ * @argc: the length of @argv
+ * @argv: argument vector
+ *
+ * Sets the command used to restart @client if it does not have a
+ * .desktop file that can be used to find its restart command.
+ *
+ * This can also be used when handling the ::save_state signal, to
+ * save the current state via an updated command line. (Eg, providing
+ * a list of filenames to open when the application is resumed.)
+ **/
+void
+egg_sm_client_set_restart_command (EggSMClient  *client,
+                                  int           argc,
+                                  const char  **argv)
+{
+  g_return_if_fail (EGG_IS_SM_CLIENT (client));
+
+  if (EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command)
+    EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command (client, argc, argv);
+}
+
+/**
+ * egg_sm_client_will_quit:
+ * @client: the client
+ * @will_quit: whether or not the application is willing to quit
+ *
+ * This MUST be called in response to the ::quit_requested signal, to
+ * indicate whether or not the application is willing to quit. The
+ * application may call it either directly from the signal handler, or
+ * at some later point (eg, after asynchronously interacting with the
+ * user).
+ *
+ * If the application does not connect to ::quit_requested,
+ * #EggSMClient will call this method on its behalf (passing %TRUE
+ * for @will_quit).
+ *
+ * After calling this method, the application should wait to receive
+ * either ::quit_cancelled or ::quit.
+ **/
+void
+egg_sm_client_will_quit (EggSMClient *client,
+                        gboolean     will_quit)
+{
+       g_debug( "egg_sm_client_will_quit: will_quit=%s", will_quit ? "True":"False" );
+
+  g_return_if_fail (EGG_IS_SM_CLIENT (client));
+
+  if (EGG_SM_CLIENT_GET_CLASS (client)->will_quit)
+    EGG_SM_CLIENT_GET_CLASS (client)->will_quit (client, will_quit);
+}
+
+/**
+ * egg_sm_client_end_session:
+ * @style: a hint at how to end the session
+ * @request_confirmation: whether or not the user should get a chance
+ * to confirm the action
+ *
+ * Requests that the session manager end the current session. @style
+ * indicates how the session should be ended, and
+ * @request_confirmation indicates whether or not the user should be
+ * given a chance to confirm the logout/reboot/shutdown. Both of these
+ * flags are merely hints though; the session manager may choose to
+ * ignore them.
+ *
+ * Return value: %TRUE if the request was sent; %FALSE if it could not
+ * be (eg, because it could not connect to the session manager).
+ **/
+gboolean
+egg_sm_client_end_session (EggSMClientEndStyle  style,
+                          gboolean             request_confirmation)
+{
+  EggSMClient *client = egg_sm_client_get ();
+
+  g_debug( "egg_sm_client_end_session: request_confirmation=%s", request_confirmation ? "True":"False" );
+  g_return_val_if_fail (EGG_IS_SM_CLIENT (client), FALSE);
+
+  if (EGG_SM_CLIENT_GET_CLASS (client)->end_session)
+    {
+      return EGG_SM_CLIENT_GET_CLASS (client)->end_session (client, style,
+                                                           request_confirmation);
+    }
+  else
+    return FALSE;
+}
+
+/* Signal-emitting callbacks from platform-specific code */
+
+GKeyFile *
+egg_sm_client_save_state (EggSMClient *client)
+{
+  GKeyFile *state_file;
+  char *group;
+
+  g_return_val_if_fail (client == global_client, NULL);
+
+  state_file = g_key_file_new ();
+
+  g_debug ("Emitting save_state");
+  g_signal_emit (client, signals[SAVE_STATE], 0, state_file);
+  g_debug ("Done emitting save_state");
+
+  group = g_key_file_get_start_group (state_file);
+  if (group)
+    {
+      g_free (group);
+      return state_file;
+    }
+  else
+    {
+      g_key_file_free (state_file);
+      return NULL;
+    }
+}
+
+void
+egg_sm_client_quit_requested (EggSMClient *client)
+{
+  g_debug( "egg_sm_client_quit_requested: client=%p", ( void * ) client );
+  g_return_if_fail (client == global_client);
+
+  if (!g_signal_has_handler_pending (client, signals[QUIT_REQUESTED], 0, FALSE))
+    {
+      g_debug ("Not emitting quit_requested because no one is listening");
+      egg_sm_client_will_quit (client, TRUE);
+      return;
+    }
+
+  g_debug ("Emitting quit_requested");
+  g_signal_emit (client, signals[QUIT_REQUESTED], 0);
+  g_debug ("Done emitting quit_requested");
+}
+
+void
+egg_sm_client_quit_cancelled (EggSMClient *client)
+{
+  g_debug( "egg_sm_client_quit_cancelled: client=%p", ( void * ) client );
+  g_return_if_fail (client == global_client);
+
+  g_debug ("Emitting quit_cancelled");
+  g_signal_emit (client, signals[QUIT_CANCELLED], 0);
+  g_debug ("Done emitting quit_cancelled");
+}
+
+void
+egg_sm_client_quit (EggSMClient *client)
+{
+  g_debug( "egg_sm_client_quit: client=%p", ( void * ) client );
+  g_return_if_fail (client == global_client);
+
+  g_debug ("Emitting quit");
+  g_signal_emit (client, signals[QUIT], 0);
+  g_debug ("Done emitting quit");
+
+  /* FIXME: should we just call gtk_main_quit() here? */
+}
+
+/* pwi 2009-11-23 disable this specific log handler */
+/*static void
+egg_sm_client_debug_handler (const char *log_domain,
+                            GLogLevelFlags log_level,
+                            const char *message,
+                            gpointer user_data)
+{
+  static int debug = -1;
+
+  if (debug < 0)
+    debug = (g_getenv ("EGG_SM_CLIENT_DEBUG") != NULL);
+
+  if (debug)
+    g_log_default_handler (log_domain, log_level, message, NULL);
+}*/
diff --git a/src/nact/egg-sm-client.h b/src/nact/egg-sm-client.h
new file mode 100644
index 0000000..357aa37
--- /dev/null
+++ b/src/nact/egg-sm-client.h
@@ -0,0 +1,118 @@
+/* eggsmclient.h
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 __EGG_SM_CLIENT_H__
+#define __EGG_SM_CLIENT_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_SM_CLIENT            (egg_sm_client_get_type ())
+#define EGG_SM_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT, EggSMClient))
+#define EGG_SM_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT, 
EggSMClientClass))
+#define EGG_IS_SM_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT))
+#define EGG_IS_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT))
+#define EGG_SM_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT, 
EggSMClientClass))
+
+typedef struct _EggSMClient        EggSMClient;
+typedef struct _EggSMClientClass   EggSMClientClass;
+typedef struct _EggSMClientPrivate EggSMClientPrivate;
+
+typedef enum {
+  EGG_SM_CLIENT_END_SESSION_DEFAULT,
+  EGG_SM_CLIENT_LOGOUT,
+  EGG_SM_CLIENT_REBOOT,
+  EGG_SM_CLIENT_SHUTDOWN
+} EggSMClientEndStyle;
+
+typedef enum {
+  EGG_SM_CLIENT_MODE_DISABLED,
+  EGG_SM_CLIENT_MODE_NO_RESTART,
+  EGG_SM_CLIENT_MODE_NORMAL
+} EggSMClientMode;
+
+struct _EggSMClient
+{
+  GObject parent;
+
+};
+
+struct _EggSMClientClass
+{
+  GObjectClass parent_class;
+
+  /* signals */
+  void (*save_state)       (EggSMClient *client,
+                           GKeyFile    *state_file);
+
+  void (*quit_requested)   (EggSMClient *client);
+  void (*quit_cancelled)   (EggSMClient *client);
+  void (*quit)             (EggSMClient *client);
+
+  /* virtual methods */
+  void    (*startup)             (EggSMClient          *client,
+                                  const char           *client_id);
+  void    (*set_restart_command) (EggSMClient          *client,
+                                  int                   argc,
+                                  const char          **argv);
+  void    (*will_quit)           (EggSMClient          *client,
+                                  gboolean              will_quit);
+  gboolean (*end_session)         (EggSMClient          *client,
+                                  EggSMClientEndStyle   style,
+                                  gboolean              request_confirmation);
+
+  /* Padding for future expansion */
+  void (*_egg_reserved1) (void);
+  void (*_egg_reserved2) (void);
+  void (*_egg_reserved3) (void);
+  void (*_egg_reserved4) (void);
+};
+
+GType            egg_sm_client_get_type            (void) G_GNUC_CONST;
+
+GOptionGroup    *egg_sm_client_get_option_group    (void);
+
+/* Initialization */
+void             egg_sm_client_set_mode            (EggSMClientMode mode);
+EggSMClientMode  egg_sm_client_get_mode            (void);
+EggSMClient     *egg_sm_client_get                 (void);
+void             egg_sm_client_startup             (void);
+
+/* Resuming a saved session */
+gboolean         egg_sm_client_is_resumed          (EggSMClient *client);
+GKeyFile        *egg_sm_client_get_state_file      (EggSMClient *client);
+
+/* Alternate means of saving state */
+void             egg_sm_client_set_restart_command (EggSMClient  *client,
+                                                   int           argc,
+                                                   const char  **argv);
+
+/* Handling "quit_requested" signal */
+void             egg_sm_client_will_quit           (EggSMClient *client,
+                                                   gboolean     will_quit);
+
+/* Initiate a logout/reboot/shutdown */
+gboolean         egg_sm_client_end_session         (EggSMClientEndStyle  style,
+                                                   gboolean             request_confirmation);
+
+G_END_DECLS
+
+
+#endif /* __EGG_SM_CLIENT_H__ */
diff --git a/src/nact/nact-application.c b/src/nact/nact-application.c
index ae9e8a6..16606ec 100644
--- a/src/nact/nact-application.c
+++ b/src/nact/nact-application.c
@@ -38,6 +38,7 @@
 
 #include "core/na-about.h"
 
+#include "base-isession.h"
 #include "nact-application.h"
 #include "nact-main-window.h"
 #include "nact-menu.h"
@@ -85,6 +86,7 @@ static gboolean manage_options( NactApplication *application );
 static void     application_startup( GApplication *application );
 static void     application_activate( GApplication *application );
 static void     application_open( GApplication *application, GFile **files, gint n_files, const gchar *hint 
);
+static void     isession_iface_init( BaseISessionInterface *iface, void *user_data );
 
 GType
 nact_application_get_type( void )
@@ -116,10 +118,18 @@ register_type( void )
                ( GInstanceInitFunc ) instance_init
        };
 
+       static const GInterfaceInfo isession_iface_info = {
+               ( GInterfaceInitFunc ) isession_iface_init,
+               NULL,
+               NULL
+       };
+
        g_debug( "%s", thisfn );
 
        type = g_type_register_static( GTK_TYPE_APPLICATION, "NactApplication", &info, 0 );
 
+       g_type_add_interface_static( type, BASE_TYPE_ISESSION, &isession_iface_info );
+
        return( type );
 }
 
@@ -265,6 +275,7 @@ nact_application_run_with_args( NactApplication *application, int argc, GStrv ar
                init_i18n( application );
                g_set_application_name( priv->application_name );
                gtk_window_set_default_icon_name( priv->icon_name );
+               base_isession_init( BASE_ISESSION( application ));
 
                if( init_gtk_args( application ) &&
                        manage_options( application )){
@@ -553,5 +564,16 @@ nact_application_get_updater( const NactApplication *application )
 gboolean
 nact_application_is_willing_to_quit( const NactApplication *application )
 {
-       return( FALSE );
+       g_return_val_if_fail( NACT_IS_APPLICATION( application ), TRUE );
+       g_return_val_if_fail( BASE_IS_ISESSION( application ), TRUE );
+
+       return( base_isession_is_willing_to_quit( BASE_ISESSION( application )));
+}
+
+static void
+isession_iface_init( BaseISessionInterface *iface, void *user_data )
+{
+       static const gchar *thisfn = "nact_application_isession_iface_init";
+
+       g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
 }
diff --git a/src/nact/nact-main-window.c b/src/nact/nact-main-window.c
index 39e5b9b..e232874 100644
--- a/src/nact/nact-main-window.c
+++ b/src/nact/nact-main-window.c
@@ -1301,7 +1301,7 @@ warn_modified( NactMainWindow *window )
        first = g_strdup_printf( _( "Some items have been modified." ));
        second = g_strdup( _( "Are you sure you want to quit without saving them ?" ));
 
-       confirm = base_window_display_yesno_dlg( BASE_WINDOW( window ), first, second );
+       confirm = base_window_display_yesno_dlg( NULL, first, second );
 
        g_free( second );
        g_free( first );
diff --git a/src/nact/nact-main-window.h b/src/nact/nact-main-window.h
index a8ff6b0..f68ab6f 100644
--- a/src/nact/nact-main-window.h
+++ b/src/nact/nact-main-window.h
@@ -47,15 +47,14 @@
 G_BEGIN_DECLS
 
 /**
- * Signals defined by the main window
+ * Signals defined on the main window
  */
 #define MAIN_SIGNAL_ITEM_UPDATED            "main-item-updated"
 #define MAIN_SIGNAL_UPDATE_SENSITIVITIES       "main-signal-update-sensitivities"
 
 /**
  * The data which, when modified, should be redisplayed asap.
- * This is used by MAIN_SIGNAL_ITEM_UPDATED and MAIN_SIGNAL_TAB_UPDATED
- * signals.
+ * This is used by MAIN_SIGNAL_ITEM_UPDATED signal.
  */
 enum {
        MAIN_DATA_LABEL    = 1<<0,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]