nautilus r14680 - in trunk: . cut-n-paste-code/libegg src



Author: alexl
Date: Thu Oct  2 14:31:52 2008
New Revision: 14680
URL: http://svn.gnome.org/viewvc/nautilus?rev=14680&view=rev

Log:
2008-10-02  Alexander Larsson  <alexl redhat com>

        * Makefile.am:
        * nautilus-browser.desktop.in.in:
        * nautilus.desktop.in.in:
	Renamed nautilus.desktop to nautilus-browser.desktop
	(this is what it does, launch a browser window with no desktop)
	Created new NoDisplay nautilus.desktop file that is used
	for autostarting nautilus from gnome-session.
	
        * configure.in:
	Pull in the right cflags/libs for libegg
	Add nautilus-browser.desktop.in to output
	
        * cut-n-paste-code/libegg/Makefile.am:
        * cut-n-paste-code/libegg/eggdesktopfile.[ch]: Added.
        * cut-n-paste-code/libegg/eggsmclient-private.h: Added.
        * cut-n-paste-code/libegg/eggsmclient-xsmp.c: Added.
        * cut-n-paste-code/libegg/eggsmclient.[ch]: Added.
	Import eggsmclient from libegg.
	Save data in desktop file instead of a separate file as per the
	new gnome-session.
	
        * cut-n-paste-code/libegg/eggtreemultidnd.c:
	Minor update from libegg
	
        * src/nautilus-application.[ch]:
	Use eggsmclient for session handling instead of gnome-client.
	
        * src/nautilus-main.c:
	Don't use gnome_program, instead use eggsmsession and
	g_option_context_parse directly.
	This removes support for some internal commandline arguments
	that are not used anymore.



Added:
   trunk/cut-n-paste-code/libegg/eggdesktopfile.c
   trunk/cut-n-paste-code/libegg/eggdesktopfile.h
   trunk/cut-n-paste-code/libegg/eggsmclient-private.h
   trunk/cut-n-paste-code/libegg/eggsmclient-xsmp.c
   trunk/cut-n-paste-code/libegg/eggsmclient.c
   trunk/cut-n-paste-code/libegg/eggsmclient.h
   trunk/nautilus-browser.desktop.in.in
Modified:
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/configure.in
   trunk/cut-n-paste-code/libegg/Makefile.am
   trunk/cut-n-paste-code/libegg/eggtreemultidnd.c
   trunk/nautilus.desktop.in.in
   trunk/src/nautilus-application.c
   trunk/src/nautilus-application.h
   trunk/src/nautilus-main.c

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Thu Oct  2 14:31:52 2008
@@ -10,6 +10,7 @@
 	nautilus-computer.desktop.in			\
 	nautilus-folder-handler.desktop.in		\
 	nautilus-file-management-properties.desktop.in  \
+	nautilus-browser.desktop.in                     \
 	nautilus-autorun-software.desktop.in
 
 SUBDIRS =			\

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Thu Oct  2 14:31:52 2008
@@ -370,9 +370,10 @@
 dnl ==========================================================================
 	
 dnl libegg
-LIBEGG_MODULES="gtk+-2.0 libgnome-2.0"
+LIBEGG_MODULES="gtk+-2.0  gthread-2.0"
 LIBEGG_CFLAGS="`$PKG_CONFIG --cflags $LIBEGG_MODULES`"
 AC_SUBST(LIBEGG_CFLAGS)
+AC_SUBST(LIBEGG_LIBS)
 
 dnl libnautilus-extension
 LIBNAUTILUS_EXTENSION_MODULES="glib-2.0 gtk+-2.0"
@@ -461,6 +462,7 @@
 nautilus-home.desktop.in
 nautilus.desktop.in
 nautilus-folder-handler.desktop.in
+nautilus-browser.desktop.in
 nautilus-autorun-software.desktop.in
 po/Makefile.in
 src/Makefile

Modified: trunk/cut-n-paste-code/libegg/Makefile.am
==============================================================================
--- trunk/cut-n-paste-code/libegg/Makefile.am	(original)
+++ trunk/cut-n-paste-code/libegg/Makefile.am	Thu Oct  2 14:31:52 2008
@@ -9,15 +9,38 @@
 	eggtreemultidnd.h	\
 	$(NULL)
 
+EGG_SMCLIENT_FILES = 		\
+	eggdesktopfile.c	\
+	eggdesktopfile.h	\
+	eggsmclient.c		\
+	eggsmclient.h		\
+	eggsmclient-private.h	\
+	eggsmclient-xsmp.c	\
+	$(NULL)
+
 libegg_la_SOURCES = 		\
 	$(EGG_TREE_DND_FILES)	\
+	$(EGG_SMCLIENT_FILES)	\
 	$(NULL)
 
+libegg_la_CFLAGS =				\
+	-DEGG_SM_CLIENT_BACKEND_XSMP		\
+	-DG_LOG_DOMAIN=\""EggSMClient"\"	\	
+	$(LIBEGG_CFLAGS)			\
+	$(WARNING_CFLAGS)			\
+	$(DISABLE_DEPRECATED)
+
+libegg_la_LIBADD = 	\
+	$(LIBEGG_LIBS)	\
+	-lSM -lICE
+
 EXTRA_DIST = 			\
 	update-from-egg.sh	\
 	$(NULL)
 
 EGG_TREE_DND_DIR = $(srcdir)/../../../libegg/libegg/treeviewutils
+EGG_SMCLIENT_DIR = $(srcdir)/../../../libegg/libegg/smclient
 
 regenerate-built-sources:
 	EGGFILES="$(EGG_TREE_DND_FILES)" EGGDIR="$(EGG_TREE_DND_DIR)" $(srcdir)/update-from-egg.sh
+	EGGFILES="$(EGG_SMCLIENT_FILES)" EGGDIR="$(EGG_SMCLIENT_DIR)" $(srcdir)/update-from-egg.sh

Added: trunk/cut-n-paste-code/libegg/eggdesktopfile.c
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste-code/libegg/eggdesktopfile.c	Thu Oct  2 14:31:52 2008
@@ -0,0 +1,1474 @@
+/* eggdesktopfile.c - Freedesktop.Org Desktop Files
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * Based on gnome-desktop-item.c
+ * Copyright (C) 1999, 2000 Red Hat Inc.
+ * Copyright (C) 2001 George Lebl
+ *
+ * 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; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place -
+ * Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "eggdesktopfile.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+struct EggDesktopFile {
+  GKeyFile           *key_file;
+  char               *source;
+
+  char               *name, *icon;
+  EggDesktopFileType  type;
+  char                document_code;
+};
+
+/**
+ * egg_desktop_file_new:
+ * @desktop_file_path: path to a Freedesktop-style Desktop file
+ * @error: error pointer
+ *
+ * Creates a new #EggDesktopFile for @desktop_file.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new (const char *desktop_file_path, GError **error)
+{
+  GKeyFile *key_file;
+
+  key_file = g_key_file_new ();
+  if (!g_key_file_load_from_file (key_file, desktop_file_path, 0, error))
+    {
+      g_key_file_free (key_file);
+      return NULL;
+    }
+
+  return egg_desktop_file_new_from_key_file (key_file, desktop_file_path,
+					     error);
+}
+
+/**
+ * egg_desktop_file_new_from_data_dirs:
+ * @desktop_file_path: relative path to a Freedesktop-style Desktop file
+ * @error: error pointer
+ *
+ * Looks for @desktop_file_path in the paths returned from
+ * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
+ * a new #EggDesktopFile from it.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_data_dirs (const char  *desktop_file_path,
+				     GError     **error)
+{
+  EggDesktopFile *desktop_file;
+  GKeyFile *key_file;
+  char *full_path;
+
+  key_file = g_key_file_new ();
+  if (!g_key_file_load_from_data_dirs (key_file, desktop_file_path,
+				       &full_path, 0, error))
+    {
+      g_key_file_free (key_file);
+      return NULL;
+    }
+
+  desktop_file = egg_desktop_file_new_from_key_file (key_file,
+						     full_path,
+						     error);
+  g_free (full_path);
+  return desktop_file;
+}
+
+/**
+ * egg_desktop_file_new_from_dirs:
+ * @desktop_file_path: relative path to a Freedesktop-style Desktop file
+ * @search_dirs: NULL-terminated array of directories to search
+ * @error: error pointer
+ *
+ * Looks for @desktop_file_path in the paths returned from
+ * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
+ * a new #EggDesktopFile from it.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_dirs (const char  *desktop_file_path,
+				const char **search_dirs,
+				GError     **error)
+{
+  EggDesktopFile *desktop_file;
+  GKeyFile *key_file;
+  char *full_path;
+
+  key_file = g_key_file_new ();
+  if (!g_key_file_load_from_dirs (key_file, desktop_file_path, search_dirs,
+				       &full_path, 0, error))
+    {
+      g_key_file_free (key_file);
+      return NULL;
+    }
+
+  desktop_file = egg_desktop_file_new_from_key_file (key_file,
+						     full_path,
+						     error);
+  g_free (full_path);
+  return desktop_file;
+}
+
+/**
+ * egg_desktop_file_new_from_key_file:
+ * @key_file: a #GKeyFile representing a desktop file
+ * @source: the path or URI that @key_file was loaded from, or %NULL
+ * @error: error pointer
+ *
+ * Creates a new #EggDesktopFile for @key_file. Assumes ownership of
+ * @key_file (on success or failure); you should consider @key_file to
+ * be freed after calling this function.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_key_file (GKeyFile    *key_file,
+				    const char  *source,
+				    GError     **error)
+{
+  EggDesktopFile *desktop_file;
+  char *version, *type;
+
+  if (!g_key_file_has_group (key_file, EGG_DESKTOP_FILE_GROUP))
+    {
+      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		   EGG_DESKTOP_FILE_ERROR_INVALID,
+		   _("File is not a valid .desktop file"));
+      g_key_file_free (key_file);
+      return NULL;
+    }
+
+  version = g_key_file_get_value (key_file, EGG_DESKTOP_FILE_GROUP,
+				  EGG_DESKTOP_FILE_KEY_VERSION,
+				  NULL);
+  if (version)
+    {
+      double version_num;
+      char *end;
+
+      version_num = g_ascii_strtod (version, &end);
+      if (*end)
+	{
+	  g_warning ("Invalid Version string '%s' in %s",
+		     version, source ? source : "(unknown)");
+	}
+      else if (version_num > 1.0)
+	{
+	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		       EGG_DESKTOP_FILE_ERROR_INVALID,
+		       _("Unrecognized desktop file Version '%s'"), version);
+	  g_free (version);
+	  g_key_file_free (key_file);
+	  return NULL;
+	}
+      g_free (version);
+    }
+
+  desktop_file = g_new0 (EggDesktopFile, 1);
+  desktop_file->key_file = key_file;
+
+  if (g_path_is_absolute (source))
+    desktop_file->source = g_filename_to_uri (source, NULL, NULL);
+  else
+    desktop_file->source = g_strdup (source);
+
+  desktop_file->name = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
+					      EGG_DESKTOP_FILE_KEY_NAME, error);
+  if (!desktop_file->name)
+    {
+      egg_desktop_file_free (desktop_file);
+      return NULL;
+    }
+
+  type = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
+				EGG_DESKTOP_FILE_KEY_TYPE, error);
+  if (!type)
+    {
+      egg_desktop_file_free (desktop_file);
+      return NULL;
+    }
+
+  if (!strcmp (type, "Application"))
+    {
+      char *exec, *p;
+
+      desktop_file->type = EGG_DESKTOP_FILE_TYPE_APPLICATION;
+
+      exec = g_key_file_get_string (key_file,
+				    EGG_DESKTOP_FILE_GROUP,
+				    EGG_DESKTOP_FILE_KEY_EXEC,
+				    error);
+      if (!exec)
+	{
+	  egg_desktop_file_free (desktop_file);
+	  g_free (type);
+	  return NULL;
+	}
+
+      /* See if it takes paths or URIs or neither */
+      for (p = exec; *p; p++)
+	{
+	  if (*p == '%')
+	    {
+	      if (p[1] == '\0' || strchr ("FfUu", p[1]))
+		{
+		  desktop_file->document_code = p[1];
+		  break;
+		}
+	      p++;
+	    }
+	}
+
+      g_free (exec);
+    }
+  else if (!strcmp (type, "Link"))
+    {
+      char *url;
+
+      desktop_file->type = EGG_DESKTOP_FILE_TYPE_LINK;
+
+      url = g_key_file_get_string (key_file,
+				   EGG_DESKTOP_FILE_GROUP,
+				   EGG_DESKTOP_FILE_KEY_URL,
+				   error);
+      if (!url)
+	{
+	  egg_desktop_file_free (desktop_file);
+	  g_free (type);
+	  return NULL;
+	}
+      g_free (url);
+    }
+  else if (!strcmp (type, "Directory"))
+    desktop_file->type = EGG_DESKTOP_FILE_TYPE_DIRECTORY;
+  else
+    desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED;
+
+  g_free (type);
+
+  /* Check the Icon key */
+  desktop_file->icon = g_key_file_get_string (key_file,
+					      EGG_DESKTOP_FILE_GROUP,
+					      EGG_DESKTOP_FILE_KEY_ICON,
+					      NULL);
+  if (desktop_file->icon && !g_path_is_absolute (desktop_file->icon))
+    {
+      char *ext;
+
+      /* Lots of .desktop files still get this wrong */
+      ext = strrchr (desktop_file->icon, '.');
+      if (ext && (!strcmp (ext, ".png") ||
+		  !strcmp (ext, ".xpm") ||
+		  !strcmp (ext, ".svg")))
+	{
+	  g_warning ("Desktop file '%s' has malformed Icon key '%s'"
+		     "(should not include extension)",
+		     source ? source : "(unknown)",
+		     desktop_file->icon);
+	  *ext = '\0';
+	}
+    }
+
+  return desktop_file;
+}
+
+/**
+ * egg_desktop_file_free:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Frees @desktop_file.
+ **/
+void
+egg_desktop_file_free (EggDesktopFile *desktop_file)
+{
+  g_key_file_free (desktop_file->key_file);
+  g_free (desktop_file->source);
+  g_free (desktop_file->name);
+  g_free (desktop_file->icon);
+  g_free (desktop_file);
+}
+
+/**
+ * egg_desktop_file_get_source:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the URI that @desktop_file was loaded from.
+ *
+ * Return value: @desktop_file's source URI
+ **/
+const char *
+egg_desktop_file_get_source (EggDesktopFile *desktop_file)
+{
+  return desktop_file->source;
+}
+
+/**
+ * egg_desktop_file_get_desktop_file_type:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the desktop file type of @desktop_file.
+ *
+ * Return value: @desktop_file's type
+ **/
+EggDesktopFileType
+egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file)
+{
+  return desktop_file->type;
+}
+
+/**
+ * egg_desktop_file_get_name:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the (localized) value of @desktop_file's "Name" key.
+ *
+ * Return value: the application/link name
+ **/
+const char *
+egg_desktop_file_get_name (EggDesktopFile *desktop_file)
+{
+  return desktop_file->name;
+}
+
+/**
+ * egg_desktop_file_get_icon:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the value of @desktop_file's "Icon" key.
+ *
+ * If the icon string is a full path (that is, if g_path_is_absolute()
+ * returns %TRUE when called on it), it points to a file containing an
+ * unthemed icon. If the icon string is not a full path, it is the
+ * name of a themed icon, which can be looked up with %GtkIconTheme,
+ * or passed directly to a theme-aware widget like %GtkImage or
+ * %GtkCellRendererPixbuf.
+ *
+ * Return value: the icon path or name
+ **/
+const char *
+egg_desktop_file_get_icon (EggDesktopFile *desktop_file)
+{
+  return desktop_file->icon;
+}
+
+gboolean
+egg_desktop_file_has_key (EggDesktopFile  *desktop_file,
+			  const char      *key,
+			  GError         **error)
+{
+  return g_key_file_has_key (desktop_file->key_file,
+			     EGG_DESKTOP_FILE_GROUP, key,
+			     error);
+}
+
+char *
+egg_desktop_file_get_string (EggDesktopFile  *desktop_file,
+			     const char      *key,
+			     GError         **error)
+{
+  return g_key_file_get_string (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP, key,
+				error);
+}
+
+char *
+egg_desktop_file_get_locale_string (EggDesktopFile  *desktop_file,
+				    const char      *key,
+				    const char      *locale,
+				    GError         **error)
+{
+  return g_key_file_get_locale_string (desktop_file->key_file,
+				       EGG_DESKTOP_FILE_GROUP, key, locale,
+				       error);
+}
+
+gboolean
+egg_desktop_file_get_boolean (EggDesktopFile  *desktop_file,
+			      const char      *key,
+			      GError         **error)
+{
+  return g_key_file_get_boolean (desktop_file->key_file,
+				 EGG_DESKTOP_FILE_GROUP, key,
+				 error);
+}
+
+double
+egg_desktop_file_get_numeric (EggDesktopFile  *desktop_file,
+			      const char      *key,
+			      GError         **error)
+{
+#if 0
+  return g_key_file_get_double (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP, key,
+				error);
+#else
+  return 0.0;
+#endif
+}
+
+char **
+egg_desktop_file_get_string_list (EggDesktopFile  *desktop_file,
+				  const char      *key,
+				  gsize           *length,
+				  GError         **error)
+{
+  return g_key_file_get_string_list (desktop_file->key_file,
+				     EGG_DESKTOP_FILE_GROUP, key, length,
+				     error);
+}
+
+char **
+egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file,
+					 const char      *key,
+					 const char      *locale,
+					 gsize           *length,
+					 GError         **error)
+{
+  return g_key_file_get_locale_string_list (desktop_file->key_file,
+					    EGG_DESKTOP_FILE_GROUP, key,
+					    locale, length,
+					    error);
+}
+
+/**
+ * egg_desktop_file_can_launch:
+ * @desktop_file: an #EggDesktopFile
+ * @desktop_environment: the name of the running desktop environment,
+ * or %NULL
+ *
+ * Tests if @desktop_file can/should be launched in the current
+ * environment. If @desktop_environment is non-%NULL, @desktop_file's
+ * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that
+ * this desktop_file is appropriate for the named environment.
+ *
+ * Furthermore, if @desktop_file has type
+ * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is
+ * also checked, to make sure the binary it points to exists.
+ *
+ * egg_desktop_file_can_launch() does NOT check the value of the
+ * "Hidden" key.
+ *
+ * Return value: %TRUE if @desktop_file can be launched
+ **/
+gboolean
+egg_desktop_file_can_launch (EggDesktopFile *desktop_file,
+			     const char     *desktop_environment)
+{
+  char *try_exec, *found_program;
+  char **only_show_in, **not_show_in;
+  gboolean found;
+  int i;
+
+  if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION &&
+      desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK)
+    return FALSE;
+
+  if (desktop_environment)
+    {
+      only_show_in = g_key_file_get_string_list (desktop_file->key_file,
+						 EGG_DESKTOP_FILE_GROUP,
+						 EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN,
+						 NULL, NULL);
+      if (only_show_in)
+	{
+	  for (i = 0, found = FALSE; only_show_in[i] && !found; i++)
+	    {
+	      if (!strcmp (only_show_in[i], desktop_environment))
+		found = TRUE;
+	    }
+
+	  g_strfreev (only_show_in);
+
+	  if (!found)
+	    return FALSE;
+	}
+
+      not_show_in = g_key_file_get_string_list (desktop_file->key_file,
+						EGG_DESKTOP_FILE_GROUP,
+						EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN,
+						NULL, NULL);
+      if (not_show_in)
+	{
+	  for (i = 0, found = FALSE; not_show_in[i] && !found; i++)
+	    {
+	      if (!strcmp (not_show_in[i], desktop_environment))
+		found = TRUE;
+	    }
+
+	  g_strfreev (not_show_in);
+
+	  if (found)
+	    return FALSE;
+	}
+    }
+
+  if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION)
+    {
+      try_exec = g_key_file_get_string (desktop_file->key_file,
+					EGG_DESKTOP_FILE_GROUP,
+					EGG_DESKTOP_FILE_KEY_TRY_EXEC,
+					NULL);
+      if (try_exec)
+	{
+	  found_program = g_find_program_in_path (try_exec);
+	  g_free (try_exec);
+
+	  if (!found_program)
+	    return FALSE;
+	  g_free (found_program);
+	}
+    }
+
+  return TRUE;
+}
+
+/**
+ * egg_desktop_file_accepts_documents:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file represents an application that can accept
+ * documents on the command line.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file)
+{
+  return desktop_file->document_code != 0;
+}
+
+/**
+ * egg_desktop_file_accepts_multiple:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file can accept multiple documents at once.
+ *
+ * If this returns %FALSE, you can still pass multiple documents to
+ * egg_desktop_file_launch(), but that will result in multiple copies
+ * of the application being launched. See egg_desktop_file_launch()
+ * for more details.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file)
+{
+  return (desktop_file->document_code == 'F' ||
+	  desktop_file->document_code == 'U');
+}
+
+/**
+ * egg_desktop_file_accepts_uris:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file can accept (non-"file:") URIs as documents to
+ * open.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file)
+{
+  return (desktop_file->document_code == 'U' ||
+	  desktop_file->document_code == 'u');
+}
+
+static void
+append_quoted_word (GString    *str,
+		    const char *s,
+		    gboolean    in_single_quotes,
+		    gboolean    in_double_quotes)
+{
+  const char *p;
+
+  if (!in_single_quotes && !in_double_quotes)
+    g_string_append_c (str, '\'');
+  else if (!in_single_quotes && in_double_quotes)
+    g_string_append (str, "\"'");
+
+  if (!strchr (s, '\''))
+    g_string_append (str, s);
+  else
+    {
+      for (p = s; *p != '\0'; p++)
+	{
+	  if (*p == '\'')
+	    g_string_append (str, "'\\''");
+	  else
+	    g_string_append_c (str, *p);
+	}
+    }
+
+  if (!in_single_quotes && !in_double_quotes)
+    g_string_append_c (str, '\'');
+  else if (!in_single_quotes && in_double_quotes)
+    g_string_append (str, "'\"");
+}
+
+static void
+do_percent_subst (EggDesktopFile *desktop_file,
+		  char            code,
+		  GString        *str,
+		  GSList        **documents,
+		  gboolean        in_single_quotes,
+		  gboolean        in_double_quotes)
+{
+  GSList *d;
+  char *doc;
+
+  switch (code)
+    {
+    case '%':
+      g_string_append_c (str, '%');
+      break;
+
+    case 'F':
+    case 'U':
+      for (d = *documents; d; d = d->next)
+	{
+	  doc = d->data;
+	  g_string_append (str, " ");
+	  append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
+	}
+      *documents = NULL;
+      break;
+
+    case 'f':
+    case 'u':
+      if (*documents)
+	{
+	  doc = (*documents)->data;
+	  g_string_append (str, " ");
+	  append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
+	  *documents = (*documents)->next;
+	}
+      break;
+
+    case 'i':
+      if (desktop_file->icon)
+	{
+	  g_string_append (str, "--icon ");
+	  append_quoted_word (str, desktop_file->icon,
+			      in_single_quotes, in_double_quotes);
+	}
+      break;
+
+    case 'c':
+      if (desktop_file->name)
+	{
+	  append_quoted_word (str, desktop_file->name,
+			      in_single_quotes, in_double_quotes);
+	}
+      break;
+
+    case 'k':
+      if (desktop_file->source)
+	{
+	  append_quoted_word (str, desktop_file->source,
+			      in_single_quotes, in_double_quotes);
+	}
+      break;
+
+    case 'D':
+    case 'N':
+    case 'd':
+    case 'n':
+    case 'v':
+    case 'm':
+      /* Deprecated; skip */
+      break;
+
+    default:
+      g_warning ("Unrecognized %%-code '%%%c' in Exec", code);
+      break;
+    }
+}
+
+static char *
+parse_exec (EggDesktopFile  *desktop_file,
+	    GSList         **documents,
+	    GError         **error)
+{
+  char *exec, *p, *command;
+  gboolean escape, single_quot, double_quot;
+  GString *gs;
+
+  exec = g_key_file_get_string (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP,
+				EGG_DESKTOP_FILE_KEY_EXEC,
+				error);
+  if (!exec)
+    return NULL;
+
+  /* Build the command */
+  gs = g_string_new (NULL);
+  escape = single_quot = double_quot = FALSE;
+
+  for (p = exec; *p != '\0'; p++)
+    {
+      if (escape)
+	{
+	  escape = FALSE;
+	  g_string_append_c (gs, *p);
+	}
+      else if (*p == '\\')
+	{
+	  if (!single_quot)
+	    escape = TRUE;
+	  g_string_append_c (gs, *p);
+	}
+      else if (*p == '\'')
+	{
+	  g_string_append_c (gs, *p);
+	  if (!single_quot && !double_quot)
+	    single_quot = TRUE;
+	  else if (single_quot)
+	    single_quot = FALSE;
+	}
+      else if (*p == '"')
+	{
+	  g_string_append_c (gs, *p);
+	  if (!single_quot && !double_quot)
+	    double_quot = TRUE;
+	  else if (double_quot)
+	    double_quot = FALSE;
+	}
+      else if (*p == '%' && p[1])
+	{
+	  do_percent_subst (desktop_file, p[1], gs, documents,
+			    single_quot, double_quot);
+	  p++;
+	}
+      else
+	g_string_append_c (gs, *p);
+    }
+
+  g_free (exec);
+  command = g_string_free (gs, FALSE);
+
+  /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */
+  if (g_key_file_has_key (desktop_file->key_file,
+			  EGG_DESKTOP_FILE_GROUP,
+			  EGG_DESKTOP_FILE_KEY_TERMINAL,
+			  NULL))
+    {
+      GError *terminal_error = NULL;
+      gboolean use_terminal =
+	g_key_file_get_boolean (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP,
+				EGG_DESKTOP_FILE_KEY_TERMINAL,
+				&terminal_error);
+      if (terminal_error)
+	{
+	  g_free (command);
+	  g_propagate_error (error, terminal_error);
+	  return NULL;
+	}
+
+      if (use_terminal)
+	{
+	  gs = g_string_new ("xdg-terminal ");
+	  append_quoted_word (gs, command, FALSE, FALSE);
+	  g_free (command);
+	  command = g_string_free (gs, FALSE);
+	}
+    }
+
+  return command;
+}
+
+static GSList *
+translate_document_list (EggDesktopFile *desktop_file, GSList *documents)
+{
+  gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file);
+  GSList *ret, *d;
+
+  for (d = documents, ret = NULL; d; d = d->next)
+    {
+      const char *document = d->data;
+      gboolean is_uri = !g_path_is_absolute (document);
+      char *translated;
+
+      if (accepts_uris)
+	{
+	  if (is_uri)
+	    translated = g_strdup (document);
+	  else
+	    translated = g_filename_to_uri (document, NULL, NULL);
+	}
+      else
+	{
+	  if (is_uri)
+	    translated = g_filename_from_uri (document, NULL, NULL);
+	  else
+	    translated = g_strdup (document);
+	}
+
+      if (translated)
+	ret = g_slist_prepend (ret, translated);
+    }
+
+  return g_slist_reverse (ret);
+}
+
+static void
+free_document_list (GSList *documents)
+{
+  GSList *d;
+
+  for (d = documents; d; d = d->next)
+    g_free (d->data);
+  g_slist_free (documents);
+}
+
+/**
+ * egg_desktop_file_parse_exec:
+ * @desktop_file: a #EggDesktopFile
+ * @documents: a list of document paths or URIs
+ * @error: error pointer
+ *
+ * Parses @desktop_file's Exec key, inserting @documents into it, and
+ * returns the result.
+ *
+ * If @documents contains non-file: URIs and @desktop_file does not
+ * accept URIs, those URIs will be ignored. Likewise, if @documents
+ * contains more elements than @desktop_file accepts, the extra
+ * documents will be ignored.
+ *
+ * Return value: the parsed Exec string
+ **/
+char *
+egg_desktop_file_parse_exec (EggDesktopFile  *desktop_file,
+			     GSList          *documents,
+			     GError         **error)
+{
+  GSList *translated, *docs;
+  char *command;
+
+  docs = translated = translate_document_list (desktop_file, documents);
+  command = parse_exec (desktop_file, &docs, error);
+  free_document_list (translated);
+
+  return command;
+}
+
+static gboolean
+parse_link (EggDesktopFile  *desktop_file,
+	    EggDesktopFile **app_desktop_file,
+	    GSList         **documents,
+	    GError         **error)
+{
+  char *url;
+  GKeyFile *key_file;
+
+  url = g_key_file_get_string (desktop_file->key_file,
+			       EGG_DESKTOP_FILE_GROUP,
+			       EGG_DESKTOP_FILE_KEY_URL,
+			       error);
+  if (!url)
+    return FALSE;
+  *documents = g_slist_prepend (NULL, url);
+
+  /* FIXME: use gvfs */
+  key_file = g_key_file_new ();
+  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+			 EGG_DESKTOP_FILE_KEY_NAME,
+			 "xdg-open");
+  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+			 EGG_DESKTOP_FILE_KEY_TYPE,
+			 "Application");
+  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+			 EGG_DESKTOP_FILE_KEY_EXEC,
+			 "xdg-open %u");
+  *app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL);
+  return TRUE;
+}
+
+#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
+static char *
+start_startup_notification (GdkDisplay     *display,
+			    EggDesktopFile *desktop_file,
+			    const char     *argv0,
+			    int             screen,
+			    int             workspace,
+			    guint32         launch_time)
+{
+  static int sequence = 0;
+  char *startup_id;
+  char *description, *wmclass;
+  char *screen_str, *workspace_str;
+
+  if (g_key_file_has_key (desktop_file->key_file,
+			  EGG_DESKTOP_FILE_GROUP,
+			  EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
+			  NULL))
+    {
+      if (!g_key_file_get_boolean (desktop_file->key_file,
+				   EGG_DESKTOP_FILE_GROUP,
+				   EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
+				   NULL))
+	return NULL;
+      wmclass = NULL;
+    }
+  else
+    {
+      wmclass = g_key_file_get_string (desktop_file->key_file,
+				       EGG_DESKTOP_FILE_GROUP,
+				       EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS,
+				       NULL);
+      if (!wmclass)
+	return NULL;
+    }
+
+  if (launch_time == (guint32)-1)
+    launch_time = gdk_x11_display_get_user_time (display);
+  startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
+				g_get_prgname (),
+				(unsigned long)getpid (),
+				g_get_host_name (),
+				argv0,
+				sequence++,
+				(unsigned long)launch_time);
+
+  description = g_strdup_printf (_("Starting %s"), desktop_file->name);
+  screen_str = g_strdup_printf ("%d", screen);
+  workspace_str = workspace == -1 ? NULL : g_strdup_printf ("%d", workspace);
+
+  gdk_x11_display_broadcast_startup_message (display, "new",
+					     "ID", startup_id,
+					     "NAME", desktop_file->name,
+					     "SCREEN", screen_str,
+					     "BIN", argv0,
+					     "ICON", desktop_file->icon,
+					     "DESKTOP", workspace_str,
+					     "DESCRIPTION", description,
+					     "WMCLASS", wmclass,
+					     NULL);
+
+  g_free (description);
+  g_free (wmclass);
+  g_free (screen_str);
+  g_free (workspace_str);
+
+  return startup_id;
+}
+
+static void
+end_startup_notification (GdkDisplay *display,
+			  const char *startup_id)
+{
+  gdk_x11_display_broadcast_startup_message (display, "remove",
+					     "ID", startup_id,
+					     NULL);
+}
+
+#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */ * 1000)
+
+typedef struct {
+  GdkDisplay *display;
+  char *startup_id;
+} StartupNotificationData;
+
+static gboolean
+startup_notification_timeout (gpointer data)
+{
+  StartupNotificationData *sn_data = data;
+
+  end_startup_notification (sn_data->display, sn_data->startup_id);
+  g_object_unref (sn_data->display);
+  g_free (sn_data->startup_id);
+  g_free (sn_data);
+
+  return FALSE;
+}
+
+static void
+set_startup_notification_timeout (GdkDisplay *display,
+				  const char *startup_id)
+{
+  StartupNotificationData *sn_data;
+
+  sn_data = g_new (StartupNotificationData, 1);
+  sn_data->display = g_object_ref (display);
+  sn_data->startup_id = g_strdup (startup_id);
+
+  g_timeout_add (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH,
+		 startup_notification_timeout, sn_data);
+}
+#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
+
+extern char **environ;
+
+static GPtrArray *
+array_putenv (GPtrArray *env, char *variable)
+{
+  int i, keylen;
+
+  if (!env)
+    {
+      env = g_ptr_array_new ();
+
+      for (i = 0; environ[i]; i++)
+	g_ptr_array_add (env, g_strdup (environ[i]));
+    }
+
+  keylen = strcspn (variable, "=");
+
+  /* Remove old value of key */
+  for (i = 0; i < env->len; i++)
+    {
+      char *envvar = env->pdata[i];
+
+      if (!strncmp (envvar, variable, keylen) && envvar[keylen] == '=')
+	{
+	  g_free (envvar);
+	  g_ptr_array_remove_index_fast (env, i);
+	  break;
+	}
+    }
+
+  /* Add new value */
+  g_ptr_array_add (env, g_strdup (variable));
+
+  return env;
+}
+
+static gboolean
+egg_desktop_file_launchv (EggDesktopFile *desktop_file,
+			  GSList *documents, va_list args,
+			  GError **error)
+{
+  EggDesktopFileLaunchOption option;
+  GSList *translated_documents = NULL, *docs;
+  char *command, **argv;
+  int argc, i, screen_num;
+  gboolean success, current_success;
+  GdkDisplay *display;
+  char *startup_id;
+
+  GPtrArray   *env = NULL;
+  char       **variables = NULL;
+  GdkScreen   *screen = NULL;
+  int          workspace = -1;
+  const char  *directory = NULL;
+  guint32      launch_time = (guint32)-1;
+  GSpawnFlags  flags = G_SPAWN_SEARCH_PATH;
+  GSpawnChildSetupFunc setup_func = NULL;
+  gpointer     setup_data = NULL;
+
+  GPid        *ret_pid = NULL;
+  int         *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL;
+  char       **ret_startup_id = NULL;
+
+  if (documents && desktop_file->document_code == 0)
+    {
+      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+		   _("Application does not accept documents on command line"));
+      return FALSE;
+    }
+
+  /* Read the options: technically it's incorrect for the caller to
+   * NULL-terminate the list of options (rather than 0-terminating
+   * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED,
+   * it's more consistent with other glib/gtk methods, and it will
+   * work as long as sizeof (int) <= sizeof (NULL), and NULL is
+   * represented as 0. (Which is true everywhere we care about.)
+   */
+  while ((option = va_arg (args, EggDesktopFileLaunchOption)))
+    {
+      switch (option)
+	{
+	case EGG_DESKTOP_FILE_LAUNCH_CLEARENV:
+	  if (env)
+	    g_ptr_array_free (env, TRUE);
+	  env = g_ptr_array_new ();
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_PUTENV:
+	  variables = va_arg (args, char **);
+	  for (i = 0; variables[i]; i++)
+	    env = array_putenv (env, variables[i]);
+	  break;
+
+	case EGG_DESKTOP_FILE_LAUNCH_SCREEN:
+	  screen = va_arg (args, GdkScreen *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE:
+	  workspace = va_arg (args, int);
+	  break;
+
+	case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY:
+	  directory = va_arg (args, const char *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_TIME:
+	  launch_time = va_arg (args, guint32);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_FLAGS:
+	  flags |= va_arg (args, GSpawnFlags);
+	  /* Make sure they didn't set any flags that don't make sense. */
+	  flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO;
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC:
+	  setup_func = va_arg (args, GSpawnChildSetupFunc);
+	  setup_data = va_arg (args, gpointer);
+	  break;
+
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID:
+	  ret_pid = va_arg (args, GPid *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE:
+	  ret_stdin = va_arg (args, int *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE:
+	  ret_stdout = va_arg (args, int *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE:
+	  ret_stderr = va_arg (args, int *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID:
+	  ret_startup_id = va_arg (args, char **);
+	  break;
+
+	default:
+	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		       EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION,
+		       _("Unrecognized launch option: %d"),
+		       GPOINTER_TO_INT (option));
+	  success = FALSE;
+	  goto out;
+	}
+    }
+
+  if (screen)
+    {
+      char *display_name = gdk_screen_make_display_name (screen);
+      char *display_env = g_strdup_printf ("DISPLAY=%s", display_name);
+      env = array_putenv (env, display_env);
+      g_free (display_name);
+      g_free (display_env);
+
+      display = gdk_screen_get_display (screen);
+    }
+  else
+    {
+      display = gdk_display_get_default ();
+      screen = gdk_display_get_default_screen (display);
+    }
+  screen_num = gdk_screen_get_number (screen);
+
+  translated_documents = translate_document_list (desktop_file, documents);
+  docs = translated_documents;
+
+  success = FALSE;
+
+  do
+    {
+      command = parse_exec (desktop_file, &docs, error);
+      if (!command)
+	goto out;
+
+      if (!g_shell_parse_argv (command, &argc, &argv, error))
+	{
+	  g_free (command);
+	  goto out;
+	}
+      g_free (command);
+
+#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
+      startup_id = start_startup_notification (display, desktop_file,
+					       argv[0], screen_num,
+					       workspace, launch_time);
+      if (startup_id)
+	{
+	  char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
+						  startup_id);
+	  env = array_putenv (env, startup_id_env);
+	  g_free (startup_id_env);
+	}
+#else
+      startup_id = NULL;
+#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
+
+      if (env != NULL)
+        {
+          /* Add NULL item in the end of array  */
+          g_ptr_array_add (env, NULL);
+        }
+
+      current_success =
+	g_spawn_async_with_pipes (directory,
+				  argv,
+				  env ? (char **)(env->pdata) : NULL,
+				  flags,
+				  setup_func, setup_data,
+				  ret_pid,
+				  ret_stdin, ret_stdout, ret_stderr,
+				  error);
+      g_strfreev (argv);
+
+      if (startup_id)
+	{
+#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
+	  if (current_success)
+	    {
+	      set_startup_notification_timeout (display, startup_id);
+
+	      if (ret_startup_id)
+		*ret_startup_id = startup_id;
+	      else
+		g_free (startup_id);
+	    }
+	  else
+#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
+	    g_free (startup_id);
+	}
+      else if (ret_startup_id)
+	*ret_startup_id = NULL;
+
+      if (current_success)
+	{
+	  /* If we successfully launch any instances of the app, make
+	   * sure we return TRUE and don't set @error.
+	   */
+	  success = TRUE;
+	  error = NULL;
+
+	  /* Also, only set the output params on the first one */
+	  ret_pid = NULL;
+	  ret_stdin = ret_stdout = ret_stderr = NULL;
+	  ret_startup_id = NULL;
+	}
+    }
+  while (docs && current_success);
+
+ out:
+  if (env)
+    {
+      g_ptr_array_foreach (env, (GFunc)g_free, NULL);
+      g_ptr_array_free (env, TRUE);
+    }
+  free_document_list (translated_documents);
+
+  return success;
+}
+
+/**
+ * egg_desktop_file_launch:
+ * @desktop_file: an #EggDesktopFile
+ * @documents: a list of URIs or paths to documents to open
+ * @error: error pointer
+ * @...: additional options
+ *
+ * Launches @desktop_file with the given arguments. Additional options
+ * can be specified as follows:
+ *
+ *   %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments)
+ *       clears the environment in the child process
+ *   %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables)
+ *       adds the NAME=VALUE strings in the given %NULL-terminated
+ *       array to the child process's environment
+ *   %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen)
+ *       causes the application to be launched on the given screen
+ *   %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace)
+ *       causes the application to be launched on the given workspace
+ *   %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir)
+ *       causes the application to be launched in the given directory
+ *   %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time)
+ *       sets the "launch time" for the application. If the user
+ *       interacts with another window after @launch_time but before
+ *       the launched application creates its first window, the window
+ *       manager may choose to not give focus to the new application.
+ *       Passing 0 for @launch_time will explicitly request that the
+ *       application not receive focus.
+ *   %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags)
+ *       Sets additional #GSpawnFlags to use. See g_spawn_async() for
+ *       more details.
+ *   %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer)
+ *       Sets the child setup callback and the data to pass to it.
+ *       (See g_spawn_async() for more details.)
+ *
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid)
+ *       On a successful launch, sets * pid to the PID of the launched
+ *       application.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id)
+ *       On a successful launch, sets * startup_id to the Startup
+ *       Notification "startup id" of the launched application.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd)
+ *       On a successful launch, sets * fd to the file descriptor of
+ *       a pipe connected to the application's stdin.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd)
+ *       On a successful launch, sets * fd to the file descriptor of
+ *       a pipe connected to the application's stdout.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd)
+ *       On a successful launch, sets * fd to the file descriptor of
+ *       a pipe connected to the application's stderr.
+ *
+ * The options should be terminated with a single %NULL.
+ *
+ * If @documents contains multiple documents, but
+ * egg_desktop_file_accepts_multiple() returns %FALSE for
+ * @desktop_file, then egg_desktop_file_launch() will actually launch
+ * multiple instances of the application. In that case, the return
+ * value (as well as any values passed via
+ * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the
+ * first instance of the application that was launched (but the
+ * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each
+ * instance).
+ *
+ * Return value: %TRUE if the application was successfully launched.
+ **/
+gboolean
+egg_desktop_file_launch (EggDesktopFile *desktop_file,
+			 GSList *documents, GError **error,
+			 ...)
+{
+  va_list args;
+  gboolean success;
+  EggDesktopFile *app_desktop_file;
+
+  switch (desktop_file->type)
+    {
+    case EGG_DESKTOP_FILE_TYPE_APPLICATION:
+      va_start (args, error);
+      success = egg_desktop_file_launchv (desktop_file, documents,
+					  args, error);
+      va_end (args);
+      break;
+
+    case EGG_DESKTOP_FILE_TYPE_LINK:
+      if (documents)
+	{
+	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		       EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+		       _("Can't pass document URIs to a 'Type=Link' desktop entry"));
+	  return FALSE;
+	}	  
+
+      if (!parse_link (desktop_file, &app_desktop_file, &documents, error))
+	return FALSE;
+
+      va_start (args, error);
+      success = egg_desktop_file_launchv (app_desktop_file, documents,
+					  args, error);
+      va_end (args);
+
+      egg_desktop_file_free (app_desktop_file);
+      free_document_list (documents);
+      break;
+
+    default:
+      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+		   _("Not a launchable item"));
+      success = FALSE;
+      break;
+    }
+
+  return success;
+}
+
+
+GQuark
+egg_desktop_file_error_quark (void)
+{
+  return g_quark_from_static_string ("egg-desktop_file-error-quark");
+}
+
+
+G_LOCK_DEFINE_STATIC (egg_desktop_file);
+static EggDesktopFile *egg_desktop_file;
+
+/**
+ * egg_set_desktop_file:
+ * @desktop_file_path: path to the application's desktop file
+ *
+ * Creates an #EggDesktopFile for the application from the data at
+ * @desktop_file_path. This will also call g_set_application_name()
+ * with the localized application name from the desktop file, and
+ * gtk_window_set_default_icon_name() or
+ * gtk_window_set_default_icon_from_file() with the application's
+ * icon. Other code may use additional information from the desktop
+ * file.
+ *
+ * Note that for thread safety reasons, this function can only
+ * be called once.
+ **/
+void
+egg_set_desktop_file (const char *desktop_file_path)
+{
+  GError *error = NULL;
+
+  G_LOCK (egg_desktop_file);
+  if (egg_desktop_file)
+    egg_desktop_file_free (egg_desktop_file);
+
+  egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error);
+  if (error)
+    {
+      g_warning ("Could not load desktop file '%s': %s",
+		 desktop_file_path, error->message);
+      g_error_free (error);
+    }
+
+  /* Set localized application name and default window icon */
+  if (egg_desktop_file->name)
+    g_set_application_name (egg_desktop_file->name);
+  if (egg_desktop_file->icon)
+    {
+      if (g_path_is_absolute (egg_desktop_file->icon))
+	gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL);
+      else
+	gtk_window_set_default_icon_name (egg_desktop_file->icon);
+    }
+
+  G_UNLOCK (egg_desktop_file);
+}
+
+/**
+ * egg_get_desktop_file:
+ * 
+ * Gets the application's #EggDesktopFile, as set by
+ * egg_set_desktop_file().
+ * 
+ * Return value: the #EggDesktopFile, or %NULL if it hasn't been set.
+ **/
+EggDesktopFile *
+egg_get_desktop_file (void)
+{
+  EggDesktopFile *retval;
+
+  G_LOCK (egg_desktop_file);
+  retval = egg_desktop_file;
+  G_UNLOCK (egg_desktop_file);
+
+  return retval;
+}

Added: trunk/cut-n-paste-code/libegg/eggdesktopfile.h
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste-code/libegg/eggdesktopfile.h	Thu Oct  2 14:31:52 2008
@@ -0,0 +1,159 @@
+/* eggdesktopfile.h - Freedesktop.Org Desktop Files
+ * 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; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place -
+ * Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_DESKTOP_FILE_H__
+#define __EGG_DESKTOP_FILE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct EggDesktopFile EggDesktopFile;
+
+typedef enum {
+	EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED,
+
+	EGG_DESKTOP_FILE_TYPE_APPLICATION,
+	EGG_DESKTOP_FILE_TYPE_LINK,
+	EGG_DESKTOP_FILE_TYPE_DIRECTORY
+} EggDesktopFileType;
+
+EggDesktopFile     *egg_desktop_file_new                (const char   *desktop_file_path,
+							 GError      **error);
+
+EggDesktopFile     *egg_desktop_file_new_from_data_dirs (const char   *desktop_file_path,
+							 GError      **error);
+EggDesktopFile     *egg_desktop_file_new_from_dirs      (const char   *desktop_file_path,
+							 const char  **search_dirs,
+							 GError      **error);
+EggDesktopFile     *egg_desktop_file_new_from_key_file  (GKeyFile     *key_file,
+							 const char   *source,
+							 GError      **error);
+
+void                egg_desktop_file_free               (EggDesktopFile  *desktop_file);
+
+const char         *egg_desktop_file_get_source         (EggDesktopFile  *desktop_file) G_GNUC_PURE;
+
+EggDesktopFileType  egg_desktop_file_get_desktop_file_type (EggDesktopFile  *desktop_file) G_GNUC_PURE;
+
+const char         *egg_desktop_file_get_name           (EggDesktopFile  *desktop_file) G_GNUC_PURE;
+const char         *egg_desktop_file_get_icon           (EggDesktopFile  *desktop_file) G_GNUC_PURE;
+
+gboolean            egg_desktop_file_can_launch         (EggDesktopFile  *desktop_file,
+							 const char      *desktop_environment);
+
+gboolean            egg_desktop_file_accepts_documents  (EggDesktopFile  *desktop_file);
+gboolean            egg_desktop_file_accepts_multiple   (EggDesktopFile  *desktop_file);
+gboolean            egg_desktop_file_accepts_uris       (EggDesktopFile  *desktop_file);
+
+char               *egg_desktop_file_parse_exec         (EggDesktopFile  *desktop_file,
+							 GSList          *documents,
+							 GError         **error);
+
+gboolean            egg_desktop_file_launch             (EggDesktopFile  *desktop_file,
+							 GSList          *documents,
+							 GError         **error,
+							 ...) G_GNUC_NULL_TERMINATED;
+
+typedef enum {
+	EGG_DESKTOP_FILE_LAUNCH_CLEARENV = 1,
+	EGG_DESKTOP_FILE_LAUNCH_PUTENV,
+	EGG_DESKTOP_FILE_LAUNCH_SCREEN,
+	EGG_DESKTOP_FILE_LAUNCH_WORKSPACE,
+	EGG_DESKTOP_FILE_LAUNCH_DIRECTORY,
+	EGG_DESKTOP_FILE_LAUNCH_TIME,
+	EGG_DESKTOP_FILE_LAUNCH_FLAGS,
+	EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC,
+	EGG_DESKTOP_FILE_LAUNCH_RETURN_PID,
+	EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE,
+	EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE,
+	EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE,
+	EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID
+} EggDesktopFileLaunchOption;
+
+/* Standard Keys */
+#define EGG_DESKTOP_FILE_GROUP			"Desktop Entry"
+
+#define EGG_DESKTOP_FILE_KEY_TYPE		"Type"
+#define EGG_DESKTOP_FILE_KEY_VERSION		"Version"
+#define EGG_DESKTOP_FILE_KEY_NAME		"Name"
+#define EGG_DESKTOP_FILE_KEY_GENERIC_NAME	"GenericName"
+#define EGG_DESKTOP_FILE_KEY_NO_DISPLAY		"NoDisplay"
+#define EGG_DESKTOP_FILE_KEY_COMMENT		"Comment"
+#define EGG_DESKTOP_FILE_KEY_ICON		"Icon"
+#define EGG_DESKTOP_FILE_KEY_HIDDEN		"Hidden"
+#define EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN	"OnlyShowIn"
+#define EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN	"NotShowIn"
+#define EGG_DESKTOP_FILE_KEY_TRY_EXEC		"TryExec"
+#define EGG_DESKTOP_FILE_KEY_EXEC		"Exec"
+#define EGG_DESKTOP_FILE_KEY_PATH		"Path"
+#define EGG_DESKTOP_FILE_KEY_TERMINAL		"Terminal"
+#define EGG_DESKTOP_FILE_KEY_MIME_TYPE		"MimeType"
+#define EGG_DESKTOP_FILE_KEY_CATEGORIES		"Categories"
+#define EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY	"StartupNotify"
+#define EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS	"StartupWMClass"
+#define EGG_DESKTOP_FILE_KEY_URL		"URL"
+
+/* Accessors */
+gboolean  egg_desktop_file_has_key                (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   GError         **error);
+char     *egg_desktop_file_get_string             (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   GError         **error) G_GNUC_MALLOC;
+char     *egg_desktop_file_get_locale_string      (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   const char      *locale,
+						   GError         **error) G_GNUC_MALLOC;
+gboolean  egg_desktop_file_get_boolean            (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   GError         **error);
+double    egg_desktop_file_get_numeric            (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   GError         **error);
+char    **egg_desktop_file_get_string_list        (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   gsize           *length,
+						   GError         **error) G_GNUC_MALLOC;
+char    **egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   const char      *locale,
+						   gsize           *length,
+						   GError         **error) G_GNUC_MALLOC;
+
+
+/* Errors */
+#define EGG_DESKTOP_FILE_ERROR egg_desktop_file_error_quark()
+
+GQuark egg_desktop_file_error_quark (void);
+
+typedef enum {
+	EGG_DESKTOP_FILE_ERROR_INVALID,
+	EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+	EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION
+} EggDesktopFileError;
+
+/* Global application desktop file */
+void            egg_set_desktop_file (const char *desktop_file_path);
+EggDesktopFile *egg_get_desktop_file (void);
+
+
+G_END_DECLS
+
+#endif /* __EGG_DESKTOP_FILE_H__ */

Added: trunk/cut-n-paste-code/libegg/eggsmclient-private.h
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste-code/libegg/eggsmclient-private.h	Thu Oct  2 14:31:52 2008
@@ -0,0 +1,53 @@
+/* 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 <gdkconfig.h>
+#include "eggsmclient.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__ */

Added: trunk/cut-n-paste-code/libegg/eggsmclient-xsmp.c
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste-code/libegg/eggsmclient-xsmp.c	Thu Oct  2 14:31:52 2008
@@ -0,0 +1,1359 @@
+/*
+ * 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 "eggsmclient.h"
+#include "eggsmclient-private.h"
+
+#include "eggdesktopfile.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>
+
+#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_START,
+  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[] = {
+  "start",
+  "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_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_connect (gpointer user_data)
+{
+  EggSMClientXSMP *xsmp = user_data;
+  SmcCallbacks callbacks;
+  char *client_id;
+  char error_string_ret[256];
+  char pid_str[64];
+  EggDesktopFile *desktop_file;
+  GPtrArray *clone, *restart;
+
+  g_source_remove (xsmp->idle);
+  xsmp->idle = 0;
+
+  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, &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 FALSE;
+    }
+
+  /* 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 ||
+      (client_id && strcmp (xsmp->client_id, client_id) != 0))
+    xsmp->expecting_initial_save_yourself = TRUE;
+
+  if (client_id)
+    {
+      g_free (xsmp->client_id);
+      xsmp->client_id = g_strdup (client_id);
+      free (client_id);
+
+      gdk_threads_enter ();
+      gdk_set_sm_client_id (xsmp->client_id);
+      gdk_threads_leave ();
+
+      g_debug ("Got client ID \"%s\"", xsmp->client_id);
+    }
+
+  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);
+    }
+
+  xsmp->state = XSMP_STATE_IDLE;
+  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;
+
+  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)
+{
+  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+
+  xsmp->state = XSMP_STATE_START;
+  if (xsmp->client_id)
+    g_free (xsmp->client_id);
+  xsmp->client_id = g_strdup (client_id);
+
+  /* Don't connect to the session manager until we reach the main
+   * loop, since the session manager may assume we're fully up and
+   * running once we connect. (This also gives the application a
+   * chance to call egg_set_desktop_file() before we set the initial
+   * properties.)
+   */
+  xsmp->idle = g_idle_add (sm_client_xsmp_connect, 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;
+
+  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;
+
+  /* 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_START:
+	  /* Force the connection to complete (or fail) now. */
+	  sm_client_xsmp_connect (xsmp);
+	  break;
+
+	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->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;
+
+  gdk_threads_enter ();
+
+  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:
+  gdk_threads_leave ();
+  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 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)
+{
+  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;
+
+      merged_file = g_key_file_new ();
+      if (g_key_file_load_from_file (merged_file,
+				     egg_desktop_file_get_source (desktop_file),
+				     G_KEY_FILE_KEEP_COMMENTS |
+				     G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
+	{
+	  int 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);
+
+	}
+    }
+
+  /* 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, "--sm-client-id");
+      g_ptr_array_add (cmd, (char *)client_id);
+    }
+
+  if (state_file)
+    {
+      g_ptr_array_add (cmd, "--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;
+  int 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 = 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;
+  int i;
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = 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 = 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 = 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;
+
+  gdk_threads_enter ();
+  status = IceProcessMessages (ice_conn, NULL, NULL);
+  gdk_threads_leave ();
+
+  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 */
+}

Added: trunk/cut-n-paste-code/libegg/eggsmclient.c
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste-code/libegg/eggsmclient.c	Thu Oct  2 14:31:52 2008
@@ -0,0 +1,578 @@
+/*
+ * 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 "eggsmclient.h"
+#include "eggsmclient-private.h"
+
+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] = { 0 };
+
+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 char *sm_client_state_file = NULL;
+static char *sm_client_id = NULL;
+
+static 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_STRING, &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") },
+  { NULL }
+};
+
+static gboolean
+sm_client_post_parse_func (GOptionContext  *context,
+			   GOptionGroup    *group,
+			   gpointer         data,
+			   GError         **error)
+{
+  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);
+  return 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)
+{
+  GOptionGroup *group;
+
+  /* Use our own debug handler for the "EggSMClient" domain. */
+  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 D-Bus first
+	   * and fall back to XSMP if D-Bus session management isn't
+	   * available.
+	   */
+# ifdef EGG_SM_CLIENT_BACKEND_DBUS
+	  global_client = egg_sm_client_dbus_new ();
+# endif
+# ifdef EGG_SM_CLIENT_BACKEND_XSMP
+	  if (!global_client)
+	    global_client = egg_sm_client_xsmp_new ();
+# endif
+#endif
+	}
+
+      /* Fallback: create a dummy client, so that callers don't have
+       * to worry about a %NULL return value.
+       */
+      if (!global_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_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_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_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_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_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? */
+}
+
+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);
+}

Added: trunk/cut-n-paste-code/libegg/eggsmclient.h
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste-code/libegg/eggsmclient.h	Thu Oct  2 14:31:52 2008
@@ -0,0 +1,117 @@
+/* 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);
+
+/* 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__ */

Modified: trunk/cut-n-paste-code/libegg/eggtreemultidnd.c
==============================================================================
--- trunk/cut-n-paste-code/libegg/eggtreemultidnd.c	(original)
+++ trunk/cut-n-paste-code/libegg/eggtreemultidnd.c	Thu Oct  2 14:31:52 2008
@@ -59,7 +59,7 @@
 
   if (!our_type)
     {
-      const GTypeInfo our_info =
+      static const GTypeInfo our_info =
       {
         sizeof (EggTreeMultiDragSourceIface), /* class_size */
 	NULL,		/* base_init */

Added: trunk/nautilus-browser.desktop.in.in
==============================================================================
--- (empty file)
+++ trunk/nautilus-browser.desktop.in.in	Thu Oct  2 14:31:52 2008
@@ -0,0 +1,16 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=File Browser
+_Comment=Browse the file system with the file manager
+TryExec=nautilus
+Exec=nautilus --no-desktop --browser %U
+Icon=system-file-manager
+Terminal=false
+StartupNotify=true
+Type=Application
+Categories=GNOME;GTK;System;Utility;Core;
+OnlyShowIn=GNOME;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=nautilus
+X-GNOME-Bugzilla-Component=general
+X-GNOME-Bugzilla-Version= VERSION@

Modified: trunk/nautilus.desktop.in.in
==============================================================================
--- trunk/nautilus.desktop.in.in	(original)
+++ trunk/nautilus.desktop.in.in	Thu Oct  2 14:31:52 2008
@@ -1,19 +1,17 @@
 [Desktop Entry]
 Encoding=UTF-8
-_Name=File Browser
-_Comment=Browse the file system with the file manager
-TryExec=nautilus
-Exec=nautilus --no-desktop --browser %U
+_Name=File Manager
+Exec=nautilus
 Icon=system-file-manager
 Terminal=false
-StartupNotify=true
 Type=Application
-Categories=GNOME;GTK;System;Utility;Core;
+StartupNotify=true
+NoDisplay=true
 OnlyShowIn=GNOME;
 X-GNOME-Bugzilla-Bugzilla=GNOME
 X-GNOME-Bugzilla-Product=nautilus
 X-GNOME-Bugzilla-Component=general
 X-GNOME-Bugzilla-Version= VERSION@
 X-GNOME-Autostart-Phase=Desktop
-X-GNOME-Provides=filemanager
 X-GNOME-Autostart-Notify=true
+X-GNOME-Provides=filemanager

Modified: trunk/src/nautilus-application.c
==============================================================================
--- trunk/src/nautilus-application.c	(original)
+++ trunk/src/nautilus-application.c	Thu Oct  2 14:31:52 2008
@@ -82,11 +82,9 @@
 {
   COMMAND_0, /* unused: 0 is an invalid command */
 
-  COMMAND_RESTART,
   COMMAND_START_DESKTOP,
   COMMAND_STOP_DESKTOP,
   COMMAND_OPEN_BROWSER,
-  COMMAND_LOAD_SESSION
 };
 
 /* Needed for the is_kdesktop_present check */
@@ -124,9 +122,9 @@
 						    NautilusApplication      *application);
 static void     drive_listen_for_eject_button      (GDrive *drive, 
 						    NautilusApplication *application);
-static void     update_session                    (gpointer                  callback_data);
-static void     init_session                      (void);
 static gboolean is_kdesktop_present               (void);
+static void     nautilus_application_load_session     (NautilusApplication *application); 
+static char *   nautilus_application_get_session_data (void);
 
 G_DEFINE_TYPE (NautilusApplication, nautilus_application, G_TYPE_OBJECT);
 
@@ -191,31 +189,6 @@
   return result;
 }
 
-/* This is a hack, because there is no unique_message_data_get()... */
-
-typedef struct {
-	guchar *data;
-	gint length;
-	
-	/* etc... */
-}  UniqueMessageDataInternal;
-
-static char *
-_unique_message_data_get_filename (UniqueMessageData *message_data)
-{
-	UniqueMessageDataInternal *internal;
-	internal = (UniqueMessageDataInternal *)message_data;
-	return g_strndup (internal->data, internal->length);
-}
-
-static void
-_unique_message_data_set_filename (UniqueMessageData *message_data,
-				   const char *filename)
-{
-	unique_message_data_set (message_data, filename, strlen (filename));
-}
-
-
 GList *
 nautilus_application_get_window_list (void)
 {
@@ -276,18 +249,50 @@
 }
 
 static void
+smclient_save_state_cb (EggSMClient   *client,
+			GKeyFile      *state_file,
+			NautilusApplication *application)
+{
+	char *data;
+
+	data = nautilus_application_get_session_data ();
+	if (data) {
+		g_key_file_set_string (state_file,
+				       "Nautilus",
+				       "documents", 
+				       data);
+	}
+	g_free (data);
+}
+
+static void
+smclient_quit_cb (EggSMClient   *client,
+		  NautilusApplication *application)
+{
+	nautilus_main_event_loop_quit (TRUE);
+}
+
+static void
 nautilus_application_init (NautilusApplication *application)
 {
 	/* Create an undo manager */
 	application->undo_manager = nautilus_undo_manager_new ();
 
 	application->unique_app = unique_app_new_with_commands ("org.gnome.Nautilus", NULL,
-								"restart", COMMAND_RESTART,
 								"start_desktop", COMMAND_START_DESKTOP,
 								"stop_desktop", COMMAND_STOP_DESKTOP,
 								"open_browser", COMMAND_OPEN_BROWSER,
-								"load_session", COMMAND_LOAD_SESSION,
 								NULL);
+
+	
+        application->smclient = egg_sm_client_get ();
+        g_signal_connect (application->smclient, "save_state",
+                          G_CALLBACK (smclient_save_state_cb),
+                          application);
+	g_signal_connect (application->smclient, "quit",
+			  G_CALLBACK (smclient_quit_cb),
+			  application);
+	/* TODO: Should connect to quit_requested and block logout on active transfer? */
 	
 	/* register views */
 	fm_icon_view_register ();
@@ -326,6 +331,7 @@
 	nautilus_bookmarks_exiting ();
 	
 	g_object_unref (application->undo_manager);
+	g_object_unref (application->smclient);
 
 	if (application->volume_monitor) {
 		g_object_unref (application->volume_monitor);
@@ -627,7 +633,6 @@
 {
 	NautilusApplication *application;
 	UniqueResponse res;
-	char *filename;
 	char **uris;
 	char *geometry;
 	
@@ -640,16 +645,6 @@
 		nautilus_main_event_loop_quit (TRUE);
 		
 		break;
-	case COMMAND_RESTART:
-		filename = nautilus_application_save_session_to_file ();
-		if (filename != NULL) {
-			nautilus_main_event_loop_quit (TRUE);
-			g_setenv ("_NAUTILUS_RESTART_SESSION_FILENAME", filename, 1);
-			g_free (filename);
-		} else {
-			g_message ("Could not save session. Not restarting.");
-		}
-		break;
 	case UNIQUE_OPEN:
 	case COMMAND_OPEN_BROWSER:
 		uris = _unique_message_data_get_geometry_and_uris (message, &geometry);
@@ -667,11 +662,6 @@
 	case COMMAND_STOP_DESKTOP:
 		nautilus_application_close_desktop ();
 		break;
-	case COMMAND_LOAD_SESSION:
-		filename = _unique_message_data_get_filename (message);
-		nautilus_application_load_session (application, filename);
-		g_free (filename);
-		break;
 	default:
 		res = UNIQUE_RESPONSE_PASSTHROUGH;
 		break;
@@ -683,12 +673,10 @@
 void
 nautilus_application_startup (NautilusApplication *application,
 			      gboolean kill_shell,
-			      gboolean restart_shell,
 			      gboolean no_default_window,
 			      gboolean no_desktop,
 			      gboolean browser_window,
 			      const char *geometry,
-			      const char *session_to_load,
 			      char **urls)
 {
 	UniqueMessageData *message;
@@ -708,12 +696,6 @@
 						 UNIQUE_CLOSE, NULL);
 			
 		}
-	} else if (restart_shell) {
-		if (unique_app_is_running (application->unique_app)) {
-			unique_app_send_message (application->unique_app,
-						 COMMAND_RESTART, NULL);
-			
-		}
 	} else {
 		/* If KDE desktop is running, then force no_desktop */
 		if (is_kdesktop_present ()) {
@@ -749,10 +731,6 @@
 
 	  	/* Create the other windows. */
 		if (urls != NULL || !no_default_window) {
-			if (urls == NULL) {
-				g_assert (session_to_load == NULL);
-			}
-
 			if (unique_app_is_running (application->unique_app)) {
 				message = unique_message_data_new ();
 				_unique_message_data_set_geometry_and_uris (message, geometry, urls);
@@ -772,20 +750,8 @@
 			}
 		}
 
-		if (session_to_load != NULL) {
-			if (unique_app_is_running (application->unique_app)) {
-				message = unique_message_data_new ();
-				_unique_message_data_set_filename (message, session_to_load);
-				unique_app_send_message (application->unique_app,
-							 COMMAND_LOAD_SESSION, message);
-				unique_message_data_free (message);				
-			} else {
-				nautilus_application_load_session (application, session_to_load);
-			}
-		}
-		
-		/* Add ourselves to the session */
-		init_session ();
+		/* Load session info if availible */
+		nautilus_application_load_session (application);
 	}
 }
 
@@ -909,9 +875,6 @@
 {
 	if (nautilus_application_desktop_windows == NULL) {
 		nautilus_application_create_desktop_windows (application);
-		
-		/* Make sure we update the session when the desktop is created */
-		update_session (gnome_master_client ());
 	}
 }
 
@@ -923,9 +886,6 @@
 				(GFunc) gtk_widget_destroy, NULL);
 		g_list_free (nautilus_application_desktop_windows);
 		nautilus_application_desktop_windows = NULL;
-
-		/* Make sure we update the session when the desktop goes away */
-		update_session (gnome_master_client ());
 	}
 }
 
@@ -1524,12 +1484,6 @@
 	g_list_free (close_list);
 }
 
-static void
-removed_from_session (GnomeClient *client, gpointer data)
-{
-	nautilus_main_event_loop_quit (FALSE);
-}
-
 static char *
 icon_to_string (GIcon *icon)
 {
@@ -1569,16 +1523,16 @@
 	return NULL;
 }
 
-char *
-nautilus_application_save_session_to_file (void)
+static char *
+nautilus_application_get_session_data (void)
 {
 	xmlDocPtr doc;
 	xmlNodePtr root_node, history_node;
 	GList *l;
-	char *dir, *filename;
+	char *data;
 	unsigned n_processed;
-	int fd;
 	xmlSaveCtxtPtr ctx;
+	xmlBufferPtr buffer;
 
 	doc = xmlNewDoc ("1.0");
 
@@ -1683,294 +1637,221 @@
 		g_list_free (slots);
 	}
 
-	dir = nautilus_get_user_directory ();
-	filename = g_build_filename (dir, "saved-session-XXXXXX", NULL);
-	g_free (dir);
-
-	fd = g_mkstemp (filename);
-	if (fd < 0) {
-		g_message ("failed to open session file %s", filename);
-		g_free (filename);
-		filename = NULL;
-		goto out;
-	}
-
+	buffer = xmlBufferCreate ();
 	xmlIndentTreeOutput = 1;
-	ctx = xmlSaveToFd (fd, NULL, XML_SAVE_FORMAT);
+	ctx = xmlSaveToBuffer (buffer, "UTF-8", XML_SAVE_FORMAT);
 	if (xmlSaveDoc (ctx, doc) < 0 ||
 	    xmlSaveFlush (ctx) < 0) {
-		g_message ("failed to save session to %s", filename);
-		g_free (filename);
-		filename = NULL;
+		g_message ("failed to save session");
 	}
 	
 	xmlSaveClose(ctx);
-	close (fd);	
+	data = g_strndup (buffer->content, buffer->use);
+	xmlBufferFree (buffer);
 
- out:
 	xmlFreeDoc (doc);
 
-	return filename;
+	return data;
 }
 
 void
-nautilus_application_load_session (NautilusApplication *application,
-				   const char *filename)
+nautilus_application_load_session (NautilusApplication *application)
 {
 	xmlDocPtr doc;
 	gboolean bail;
+	xmlNodePtr root_node;
+	GKeyFile *state_file;
+	char *data;
 
-	g_assert (filename != NULL);
-
-	bail = TRUE;
-
-	if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
-		xmlNodePtr root_node;
-
-		doc = xmlReadFile (filename, NULL, 0);
-		if (doc != NULL && (root_node = xmlDocGetRootElement (doc)) != NULL) {
-			xmlNodePtr node;
-
-			bail = FALSE;
-
-			for (node = root_node->children; node != NULL; node = node->next) {
-
-				if (!strcmp (node->name, "text")) {
-					continue;
-				} else if (!strcmp (node->name, "history")) {
-					xmlNodePtr bookmark_node;
-					gboolean emit_change;
-
-					emit_change = FALSE;
-
-					for (bookmark_node = node->children; bookmark_node != NULL; bookmark_node = bookmark_node->next) {
-						if (!strcmp (bookmark_node->name, "text")) {
-							continue;
-						} else if (!strcmp (bookmark_node->name, "bookmark")) {
-							xmlChar *name, *icon_str, *uri;
-							gboolean has_custom_name;
-							GIcon *icon;
-							GFile *location;
-
-							uri = xmlGetProp (bookmark_node, "uri");
-							name = xmlGetProp (bookmark_node, "name");
-							has_custom_name = xmlHasProp (bookmark_node, "has_custom_name") ? TRUE : FALSE;
-							icon_str = xmlGetProp (bookmark_node, "icon");
-							icon = NULL;
-							if (icon_str) {
-								icon = icon_from_string (icon_str);
-							}
-							location = g_file_new_for_uri (uri);
+	if (!egg_sm_client_is_resumed (application->smclient)) {
+		return;
+	}
 
-							emit_change |= nautilus_add_to_history_list_no_notify (location, name, has_custom_name, icon);
+	state_file = egg_sm_client_get_state_file (application->smclient);
+	if (!state_file) {
+		return;
+	}
 
-							g_object_unref (location);
+	data = g_key_file_get_string (state_file,
+				      "Nautilus",
+				      "sessiondata",
+				      NULL);
+	if (data == NULL) {
+		return;
+	}
+	
+	bail = TRUE;
 
-							if (icon) {
-								g_object_unref (icon);
-							}
-							xmlFree (name);
-							xmlFree (uri);
-							xmlFree (icon_str);
-						} else {
-							g_message ("unexpected bookmark node %s while parsing %s", bookmark_node->name, filename);
-							bail = TRUE;
-							continue;
+	doc = xmlReadMemory (data, strlen (data), NULL, "UTF-8", 0);
+	if (doc != NULL && (root_node = xmlDocGetRootElement (doc)) != NULL) {
+		xmlNodePtr node;
+		
+		bail = FALSE;
+		
+		for (node = root_node->children; node != NULL; node = node->next) {
+			
+			if (!strcmp (node->name, "text")) {
+				continue;
+			} else if (!strcmp (node->name, "history")) {
+				xmlNodePtr bookmark_node;
+				gboolean emit_change;
+				
+				emit_change = FALSE;
+				
+				for (bookmark_node = node->children; bookmark_node != NULL; bookmark_node = bookmark_node->next) {
+					if (!strcmp (bookmark_node->name, "text")) {
+						continue;
+					} else if (!strcmp (bookmark_node->name, "bookmark")) {
+						xmlChar *name, *icon_str, *uri;
+						gboolean has_custom_name;
+						GIcon *icon;
+						GFile *location;
+						
+						uri = xmlGetProp (bookmark_node, "uri");
+						name = xmlGetProp (bookmark_node, "name");
+						has_custom_name = xmlHasProp (bookmark_node, "has_custom_name") ? TRUE : FALSE;
+						icon_str = xmlGetProp (bookmark_node, "icon");
+						icon = NULL;
+						if (icon_str) {
+							icon = icon_from_string (icon_str);
 						}
-					}
-
-					if (emit_change) {
-						nautilus_send_history_list_changed ();
-					}
-				} else if (!strcmp (node->name, "window")) {
-					NautilusWindow *window;
-					xmlChar *type, *location_uri, *slot_uri;
-					xmlNodePtr slot_node;
-					GFile *location;
-					int i;
-
-					type = xmlGetProp (node, "type");
-					if (type == NULL) {
-						g_message ("empty type node while parsing %s", filename);
+						location = g_file_new_for_uri (uri);
+						
+						emit_change |= nautilus_add_to_history_list_no_notify (location, name, has_custom_name, icon);
+						
+						g_object_unref (location);
+						
+						if (icon) {
+							g_object_unref (icon);
+						}
+						xmlFree (name);
+						xmlFree (uri);
+						xmlFree (icon_str);
+					} else {
+						g_message ("unexpected bookmark node %s while parsing session data", bookmark_node->name);
 						bail = TRUE;
 						continue;
 					}
-
-					location_uri = xmlGetProp (node, "location");
-					if (location_uri == NULL) {
-						g_message ("empty location node while parsing %s", filename);
-						bail = TRUE;
-						xmlFree (type);
-						continue;
+				}
+				
+				if (emit_change) {
+					nautilus_send_history_list_changed ();
+				}
+			} else if (!strcmp (node->name, "window")) {
+				NautilusWindow *window;
+				xmlChar *type, *location_uri, *slot_uri;
+				xmlNodePtr slot_node;
+				GFile *location;
+				int i;
+				
+				type = xmlGetProp (node, "type");
+				if (type == NULL) {
+					g_message ("empty type node while parsing session data");
+					bail = TRUE;
+					continue;
+				}
+				
+				location_uri = xmlGetProp (node, "location");
+				if (location_uri == NULL) {
+					g_message ("empty location node while parsing session data");
+					bail = TRUE;
+					xmlFree (type);
+					continue;
+				}
+				
+				if (!strcmp (type, "navigation")) {
+					xmlChar *geometry;
+					
+					window = nautilus_application_create_navigation_window (application, NULL, gdk_screen_get_default ());
+					
+					geometry = xmlGetProp (node, "geometry");
+					if (geometry != NULL) {
+						eel_gtk_window_set_initial_geometry_from_string 
+							(GTK_WINDOW (window), 
+							 geometry,
+							 NAUTILUS_WINDOW_MIN_WIDTH, 
+							 NAUTILUS_WINDOW_MIN_HEIGHT,
+							 FALSE);
 					}
-
-					if (!strcmp (type, "navigation")) {
-						xmlChar *geometry;
-
-						window = nautilus_application_create_navigation_window (application, NULL, gdk_screen_get_default ());
-
-						geometry = xmlGetProp (node, "geometry");
-						if (geometry != NULL) {
-							eel_gtk_window_set_initial_geometry_from_string 
-								(GTK_WINDOW (window), 
-								 geometry,
-								 NAUTILUS_WINDOW_MIN_WIDTH, 
-								 NAUTILUS_WINDOW_MIN_HEIGHT,
-								 FALSE);
-						}
-						xmlFree (geometry);
-
-						if (xmlHasProp (node, "maximized")) {
-							gtk_window_maximize (GTK_WINDOW (window));
-						} else {
-							gtk_window_unmaximize (GTK_WINDOW (window));
-						}
-
-						if (xmlHasProp (node, "sticky")) {
-							gtk_window_stick (GTK_WINDOW (window));
-						} else {
-							gtk_window_unstick (GTK_WINDOW (window));
-						}
-
-						if (xmlHasProp (node, "keep-above")) {
-							gtk_window_set_keep_above (GTK_WINDOW (window), TRUE);
-						} else {
-							gtk_window_set_keep_above (GTK_WINDOW (window), FALSE);
-						}
-
-						for (i = 0, slot_node = node->children; slot_node != NULL; slot_node = slot_node->next) {
-							if (!strcmp (slot_node->name, "slot")) {
-								slot_uri = xmlGetProp (slot_node, "location");
-								if (slot_uri != NULL) {
-									NautilusWindowSlot *slot;
-
-									if (i == 0) {
-										slot = window->details->active_slot;
-									} else {
-										slot = nautilus_window_open_slot (window, NAUTILUS_WINDOW_OPEN_SLOT_APPEND);
-									}
-
-									location = g_file_new_for_uri (slot_uri);
-									nautilus_window_slot_open_location (slot, location, FALSE);
-
-									if (xmlHasProp (slot_node, "active")) {
-										nautilus_window_set_active_slot (window, slot);
-									}
-
-									i++;
+					xmlFree (geometry);
+					
+					if (xmlHasProp (node, "maximized")) {
+						gtk_window_maximize (GTK_WINDOW (window));
+					} else {
+						gtk_window_unmaximize (GTK_WINDOW (window));
+					}
+					
+					if (xmlHasProp (node, "sticky")) {
+						gtk_window_stick (GTK_WINDOW (window));
+					} else {
+						gtk_window_unstick (GTK_WINDOW (window));
+					}
+					
+					if (xmlHasProp (node, "keep-above")) {
+						gtk_window_set_keep_above (GTK_WINDOW (window), TRUE);
+					} else {
+						gtk_window_set_keep_above (GTK_WINDOW (window), FALSE);
+					}
+					
+					for (i = 0, slot_node = node->children; slot_node != NULL; slot_node = slot_node->next) {
+						if (!strcmp (slot_node->name, "slot")) {
+							slot_uri = xmlGetProp (slot_node, "location");
+							if (slot_uri != NULL) {
+								NautilusWindowSlot *slot;
+								
+								if (i == 0) {
+									slot = window->details->active_slot;
+								} else {
+									slot = nautilus_window_open_slot (window, NAUTILUS_WINDOW_OPEN_SLOT_APPEND);
+								}
+								
+								location = g_file_new_for_uri (slot_uri);
+								nautilus_window_slot_open_location (slot, location, FALSE);
+								
+								if (xmlHasProp (slot_node, "active")) {
+									nautilus_window_set_active_slot (window, slot);
 								}
-								xmlFree (slot_uri);
+								
+								i++;
 							}
+							xmlFree (slot_uri);
 						}
-
-						if (i == 0) {
-							/* This may be an old session file */
-							location = g_file_new_for_uri (location_uri);
-							nautilus_window_slot_open_location (window->details->active_slot, location, FALSE);
-							g_object_unref (location);
-						}
-					} else if (!strcmp (type, "spatial")) {
+					}
+					
+					if (i == 0) {
+						/* This may be an old session file */
 						location = g_file_new_for_uri (location_uri);
-						window = nautilus_application_present_spatial_window (application, NULL, NULL, location, gdk_screen_get_default ());
+						nautilus_window_slot_open_location (window->details->active_slot, location, FALSE);
 						g_object_unref (location);
-					} else {
-						g_message ("unknown window type \"%s\" while parsing %s", type, filename);
-						bail = TRUE;
 					}
-
-					xmlFree (type);
-					xmlFree (location_uri);
+				} else if (!strcmp (type, "spatial")) {
+					location = g_file_new_for_uri (location_uri);
+					window = nautilus_application_present_spatial_window (application, NULL, NULL, location, gdk_screen_get_default ());
+					g_object_unref (location);
 				} else {
-					g_message ("unexpected node %s while parsing %s", node->name, filename);
+					g_message ("unknown window type \"%s\" while parsing session data", type);
 					bail = TRUE;
-					continue;
 				}
+				
+				xmlFree (type);
+				xmlFree (location_uri);
+			} else {
+				g_message ("unexpected node %s while parsing session data", node->name);
+				bail = TRUE;
+				continue;
 			}
 		}
-
-		if (doc != NULL) {
-			xmlFreeDoc (doc);
-		}
-	}
-
-	if (bail) {
-		g_message ("failed to load session from %s", filename);
-	} else {
-		struct stat buf;
-		
-		/* only remove file if it is regular, user-owned and the user has write access. */
-
-		if (g_stat (filename, &buf) == 0 &&
-		    S_ISREG (buf.st_mode) &&
-		    (buf.st_mode & S_IWUSR) &&
-		    buf.st_uid == geteuid()) {	
-			g_remove (filename);
-		}
-	}
-}
-
-static gint
-save_session (GnomeClient *client, gint phase, GnomeSaveStyle save_style, gint shutdown,
-	      GnomeInteractStyle interact_style, gint fast, gpointer data)
-{
-	char *argv[3] = { NULL };
-
-	argv[0] = "nautilus";
-
-	argv[2] = nautilus_application_save_session_to_file ();
-	if (argv[2] != NULL) {
-		argv[1] = "--load-session";
-	}
-
-	gnome_client_set_restart_command (client, 
-					  G_N_ELEMENTS (argv), 
-					  argv);
-
-	if (argv[2] != NULL) {
-		g_free (argv[2]);
 	}
-
-	return TRUE;
-}
-
-static void
-set_session_restart (GnomeClient *client, gboolean restart)
-{
-	gnome_client_set_priority (client, 40);
-
-	if (restart && g_getenv ("NAUTILUS_DEBUG") == NULL) {
-		/* Don't respawn in debug mode */
-		gnome_client_set_restart_style (client, GNOME_RESTART_IMMEDIATELY);
-	} else {
-		gnome_client_set_restart_style (client, GNOME_RESTART_IF_RUNNING);
+	
+	if (doc != NULL) {
+		xmlFreeDoc (doc);
 	}
-}
 
-static void
-update_session (gpointer callback_data)
-{
-	set_session_restart (callback_data,
-			     /* Only ever add ourselves to the session
-			      * if we have a desktop window. Prevents the
-			      * session thrashing that's seen otherwise
-			      */
-			     nautilus_application_desktop_windows != NULL);
-}
-
-static void
-init_session (void)
-{
-	GnomeClient *client;
-
-	client = gnome_master_client ();
+	g_free (data);
 
-	g_signal_connect (client, "save_yourself",
-			  G_CALLBACK (save_session), NULL);
-	
-	g_signal_connect (client, "die",
-			  G_CALLBACK (removed_from_session), NULL);
-	
-	update_session (client);
+	if (bail) {
+		g_message ("failed to load session");
+	} 
 }
 
 #ifdef UGLY_HACK_TO_DETECT_KDE

Modified: trunk/src/nautilus-application.h
==============================================================================
--- trunk/src/nautilus-application.h	(original)
+++ trunk/src/nautilus-application.h	Thu Oct  2 14:31:52 2008
@@ -30,6 +30,7 @@
 #include <gdk/gdk.h>
 #include <gio/gio.h>
 #include <unique/unique.h>
+#include <libegg/eggsmclient.h>
 #include <libnautilus-private/nautilus-undo-manager.h>
 
 #define NAUTILUS_DESKTOP_ICON_VIEW_IID	"OAFIID:Nautilus_File_Manager_Desktop_Icon_View"
@@ -55,6 +56,7 @@
 typedef struct {
 	GObject parent;
 	UniqueApp *unique_app;
+        EggSMClient *smclient;
 	NautilusUndoManager *undo_manager;
 	GVolumeMonitor *volume_monitor;
 	unsigned int automount_idle_id;
@@ -68,12 +70,10 @@
 NautilusApplication *nautilus_application_new               (void);
 void                 nautilus_application_startup           (NautilusApplication *application,
 							     gboolean             kill_shell,
-							     gboolean             restart_shell,
 							     gboolean             no_default_window,
 							     gboolean             no_desktop,
 							     gboolean             browser_window,
 							     const char          *default_geometry,
-							     const char          *session_to_load,
 							     char               **urls);
 GList *              nautilus_application_get_window_list           (void);
 GList *              nautilus_application_get_spatial_window_list    (void);
@@ -100,7 +100,4 @@
 void                 nautilus_application_close_all_spatial_windows  (void);
 void                 nautilus_application_open_desktop      (NautilusApplication *application);
 void                 nautilus_application_close_desktop     (void);
-void                 nautilus_application_load_session      (NautilusApplication *application,
-							     const char *filename); 
-char *               nautilus_application_save_session_to_file (void);
 #endif /* NAUTILUS_APPLICATION_H */

Modified: trunk/src/nautilus-main.c
==============================================================================
--- trunk/src/nautilus-main.c	(original)
+++ trunk/src/nautilus-main.c	Thu Oct  2 14:31:52 2008
@@ -39,6 +39,8 @@
 #include <eel/eel-debug.h>
 #include <eel/eel-glib-extensions.h>
 #include <eel/eel-self-checks.h>
+#include <libegg/eggsmclient.h>
+#include <libegg/eggdesktopfile.h>
 #include <gdk/gdkx.h>
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
@@ -130,8 +132,9 @@
 {
 	if (explicit) {
 		/* Explicit --quit, make sure we don't restart */
-		gnome_client_set_restart_style (gnome_master_client (),
-						GNOME_RESTART_IF_RUNNING);
+		/* TODO: With the old session we needed to set restart
+		   style to GNOME_RESTART_IF_RUNNING here, but i don't think we need
+		   that now since gnome-session doesn't restart apps except on startup. */
 	}
 	while (event_loop_registrants != NULL) {
 		gtk_object_destroy (event_loop_registrants->data);
@@ -302,26 +305,22 @@
 main (int argc, char *argv[])
 {
 	gboolean kill_shell;
-	gboolean restart_shell;
 	gboolean no_default_window;
 	gboolean browser_window;
 	gboolean no_desktop;
+	gboolean version;
 	gboolean autostart_mode;
-	gboolean has_sm_argv;
 	const char *autostart_id;
-	char *session_to_load;
 	gchar *geometry;
 	const gchar **remaining;
-	char **p;
 	gboolean perform_self_check;
-	GOptionContext *context;
 	NautilusApplication *application;
-	char **argv_copy;
-	GnomeProgram *program;
+	GOptionContext *context;
 	GFile *file;
 	char *uri;
 	char **uris;
 	GPtrArray *uris_array;
+	GError *error;
 	int i;
 	
 	const GOptionEntry options[] = {
@@ -329,6 +328,8 @@
 		{ "check", 'c', 0, G_OPTION_ARG_NONE, &perform_self_check, 
 		  N_("Perform a quick set of self-check tests."), NULL },
 #endif
+		{ "version", '\0', 0, G_OPTION_ARG_NONE, &version,
+		  N_("Show the version of the progam."), NULL },
 		{ "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry,
 		  N_("Create the initial window with the given geometry."), N_("GEOMETRY") },
 		{ "no-default-window", 'n', 0, G_OPTION_ARG_NONE, &no_default_window,
@@ -339,20 +340,13 @@
 		  N_("open a browser window."), NULL },
 		{ "quit", 'q', 0, G_OPTION_ARG_NONE, &kill_shell, 
 		  N_("Quit Nautilus."), NULL },
-		{ "restart", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &restart_shell,
-		  N_("Restart Nautilus."), NULL },
 		{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining, NULL,  N_("[URI...]") },
-		{ "load-session", 'l', 0, G_OPTION_ARG_STRING, &session_to_load,
-		  /* Translators: --no-default-window is a nautilus command line parameter, don't modify it. */
-		  N_("Load a saved session from the specified file. Implies \"--no-default-window\"."), N_("FILENAME") },
 
 		{ NULL }
 	};
 
 	g_thread_init (NULL);
 
-	setlocale (LC_ALL, "");
-
 	/* This will be done by gtk+ later, but for now, force it to GNOME */
 	g_desktop_app_info_set_desktop_env ("GNOME");
 
@@ -372,51 +366,44 @@
 		autostart_mode = TRUE;
         }
 
-	/* detect whether this is a restart request by the SM */
-	has_sm_argv = FALSE;
-	for (p = argv; p - argv < argc; p++) {
-		if (g_str_has_prefix (*p, "--sm-client-id")) {
-			has_sm_argv = TRUE;
-		}
-	}
-
 	/* Get parameters. */
 	remaining = NULL;
 	geometry = NULL;
-	session_to_load = NULL;
+	version = FALSE;
 	kill_shell = FALSE;
 	no_default_window = FALSE;
 	no_desktop = FALSE;
 	perform_self_check = FALSE;
-	restart_shell = FALSE;
 	browser_window = FALSE;
 
+	g_set_prgname ("nautilus");
 	g_set_application_name (_("File Manager"));
-	context = g_option_context_new (_("\n\nBrowse the file system with the file manager"));
+        egg_set_desktop_file (DATADIR "/applications/nautilus.desktop");
 
-	g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
+	context = g_option_context_new (_("\n\nBrowse the file system with the file manager"));
+	g_option_context_add_main_entries (context, options, NULL);
+	
+	g_option_context_add_group (context, gtk_get_option_group (TRUE));
+	g_option_context_add_group (context, egg_sm_client_get_option_group ());
+	
+	error = NULL;
+	if (!g_option_context_parse (context, &argc, &argv, &error)) {
+		g_printerr ("Could not parse arguments: %s\n", error->message);
+		g_error_free (error);
+		return 1;
+	}
 
+	if (version) {
+		g_print ("GNOME nautilus " PACKAGE_VERSION "\n");
+		return 0;
+	}
+	
 #ifdef HAVE_EXEMPI
 	xmp_init();
 #endif
 
-	program = gnome_program_init ("nautilus", VERSION,
-				      LIBGNOMEUI_MODULE, argc, argv,
-				      GNOME_PROGRAM_STANDARD_PROPERTIES,
-				      GNOME_PARAM_GOPTION_CONTEXT, context,
-				      GNOME_PARAM_HUMAN_READABLE_NAME, _("Nautilus"),
-				      NULL);
-
-	/* We do this after gnome_program_init(), since that function sets up
-	 * its own handler for SIGSEGV and others --- we want to chain to those
-	 * handlers.
-	 */
 	setup_debug_log ();
 
-	if (session_to_load != NULL) {
-		no_default_window = TRUE;
-	}
-
 	/* If in autostart mode (aka started by gnome-session), we need to ensure 
          * nautilus starts with the correct options.
          */
@@ -425,16 +412,13 @@
 		no_desktop = FALSE;
 	}
 
-        /* Set default icon for all nautilus windows */
-	gtk_window_set_default_icon_name (NAUTILUS_ICON_FOLDER);
-	
 	if (perform_self_check && remaining != NULL) {
 		/* translators: %s is an option (e.g. --check) */
 		fprintf (stderr, _("nautilus: %s cannot be used with URIs.\n"),
 			"--check");
 		return EXIT_FAILURE;
 	}
-	if (perform_self_check && (kill_shell || restart_shell)) {
+	if (perform_self_check && kill_shell) {
 		fprintf (stderr, _("nautilus: --check cannot be used with other options.\n"));
 		return EXIT_FAILURE;
 	}
@@ -443,11 +427,6 @@
 			"--quit");
 		return EXIT_FAILURE;
 	}
-	if (restart_shell && remaining != NULL) {
-		fprintf (stderr, _("nautilus: %s cannot be used with URIs.\n"),
-			"--restart");
-		return EXIT_FAILURE;
-	}
 	if (geometry != NULL && remaining != NULL && remaining[0] != NULL && remaining[1] != NULL) {
 		fprintf (stderr, _("nautilus: --geometry cannot be used with more than one URI.\n"));
 		return EXIT_FAILURE;
@@ -468,11 +447,6 @@
 			(NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR, TRUE);
 	}
 
-	if (has_sm_argv && eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
-		/* we were restarted by the session manager. Don't show default window */
-		no_default_window = TRUE;
-	}
-	
 	application = NULL;
 
 	/* Do either the self-check or the real work. */
@@ -509,12 +483,16 @@
 		
 		/* Run the nautilus application. */
 		application = nautilus_application_new ();
+
+		if (egg_sm_client_is_resumed (application->smclient)) {
+			no_default_window = TRUE;
+		}
+		
 		nautilus_application_startup
 			(application,
-			 kill_shell, restart_shell, no_default_window, no_desktop,
+			 kill_shell, no_default_window, no_desktop,
 			 browser_window,
 			 geometry,
-			 session_to_load,
 			 uris);
 		g_strfreev (uris);
 
@@ -531,25 +509,5 @@
 
  	eel_debug_shut_down ();
 	
-	/* If told to restart, exec() myself again. This is used when
-	 * the program is told to restart with CORBA, for example when
-	 * an update takes place.
-	 */
-
-	if (g_getenv ("_NAUTILUS_RESTART_SESSION_FILENAME") != NULL) {
-		argv_copy = g_new0 (char *, 4);
-		argv_copy[0] = g_strdup (argv[0]);
-		argv_copy[1] = g_strdup ("--load-session");
-		argv_copy[2] = g_strdup (g_getenv ("_NAUTILUS_RESTART_SESSION_FILENAME"));
-
-		g_unsetenv ("_NAUTILUS_RESTART_SESSION_FILENAME");
-
-		execvp (argv[0], argv_copy);
-
-		g_strfreev (argv_copy);
-	}
-
-	g_object_unref (G_OBJECT (program));
-
 	return EXIT_SUCCESS;
 }



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