From 6c162ba243f9e62651a13ce2cb4ca2ec9d422509 Mon Sep 17 00:00:00 2001
From: monsta <monsta@inbox.ru>
Date: Fri, 12 Jan 2018 20:04:25 +0300
Subject: move libegg to the top srcdir and drop some leftovers

---
 Makefile.am                                   |    2 +-
 configure.ac                                  |    3 +-
 cut-n-paste-code/Makefile.am                  |    2 -
 cut-n-paste-code/README                       |   21 -
 cut-n-paste-code/libegg/Makefile.am           |   46 -
 cut-n-paste-code/libegg/eggdesktopfile.c      | 1468 -------------------------
 cut-n-paste-code/libegg/eggdesktopfile.h      |  166 ---
 cut-n-paste-code/libegg/eggsmclient-private.h |   47 -
 cut-n-paste-code/libegg/eggsmclient-xsmp.c    | 1366 -----------------------
 cut-n-paste-code/libegg/eggsmclient.c         |  612 -----------
 cut-n-paste-code/libegg/eggsmclient.h         |  117 --
 cut-n-paste-code/libegg/eggtreemultidnd.c     |  417 -------
 cut-n-paste-code/libegg/eggtreemultidnd.h     |   78 --
 cut-n-paste-code/libegg/update-from-egg.sh    |   25 -
 libcaja-private/Makefile.am                   |    3 +-
 libegg/Makefile.am                            |   35 +
 libegg/eggdesktopfile.c                       | 1468 +++++++++++++++++++++++++
 libegg/eggdesktopfile.h                       |  166 +++
 libegg/eggsmclient-private.h                  |   47 +
 libegg/eggsmclient-xsmp.c                     | 1366 +++++++++++++++++++++++
 libegg/eggsmclient.c                          |  612 +++++++++++
 libegg/eggsmclient.h                          |  117 ++
 libegg/eggtreemultidnd.c                      |  417 +++++++
 libegg/eggtreemultidnd.h                      |   78 ++
 po/POTFILES.in                                |   18 +-
 src/Makefile.am                               |    1 -
 src/file-manager/Makefile.am                  |    1 -
 27 files changed, 4318 insertions(+), 4381 deletions(-)
 delete mode 100644 cut-n-paste-code/Makefile.am
 delete mode 100644 cut-n-paste-code/README
 delete mode 100644 cut-n-paste-code/libegg/Makefile.am
 delete mode 100644 cut-n-paste-code/libegg/eggdesktopfile.c
 delete mode 100644 cut-n-paste-code/libegg/eggdesktopfile.h
 delete mode 100644 cut-n-paste-code/libegg/eggsmclient-private.h
 delete mode 100644 cut-n-paste-code/libegg/eggsmclient-xsmp.c
 delete mode 100644 cut-n-paste-code/libegg/eggsmclient.c
 delete mode 100644 cut-n-paste-code/libegg/eggsmclient.h
 delete mode 100644 cut-n-paste-code/libegg/eggtreemultidnd.c
 delete mode 100644 cut-n-paste-code/libegg/eggtreemultidnd.h
 delete mode 100755 cut-n-paste-code/libegg/update-from-egg.sh
 create mode 100644 libegg/Makefile.am
 create mode 100644 libegg/eggdesktopfile.c
 create mode 100644 libegg/eggdesktopfile.h
 create mode 100644 libegg/eggsmclient-private.h
 create mode 100644 libegg/eggsmclient-xsmp.c
 create mode 100644 libegg/eggsmclient.c
 create mode 100644 libegg/eggsmclient.h
 create mode 100644 libegg/eggtreemultidnd.c
 create mode 100644 libegg/eggtreemultidnd.h

diff --git a/Makefile.am b/Makefile.am
index f00647a5..342951f7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,7 +3,7 @@ include $(top_srcdir)/Makefile.shared
 SUBDIRS = \
 	eel \
 	libcaja-extension \
-	cut-n-paste-code \
+	libegg \
 	libcaja-private \
 	src \
 	test \
diff --git a/configure.ac b/configure.ac
index e9dd423d..27ad9e11 100644
--- a/configure.ac
+++ b/configure.ac
@@ -323,8 +323,7 @@ AM_CONDITIONAL([ICON_UPDATE], [test -n "$UPDATE_ICON_CACHE"])
 
 AC_CONFIG_FILES([
 Makefile
-cut-n-paste-code/Makefile
-cut-n-paste-code/libegg/Makefile
+libegg/Makefile
 data/Makefile
 data/icons/Makefile
 data/patterns/Makefile
diff --git a/cut-n-paste-code/Makefile.am b/cut-n-paste-code/Makefile.am
deleted file mode 100644
index 80f6abb0..00000000
--- a/cut-n-paste-code/Makefile.am
+++ /dev/null
@@ -1,2 +0,0 @@
-SUBDIRS = libegg
-
diff --git a/cut-n-paste-code/README b/cut-n-paste-code/README
deleted file mode 100644
index 374b0704..00000000
--- a/cut-n-paste-code/README
+++ /dev/null
@@ -1,21 +0,0 @@
-README for caja/cut-n-paste-code
-
-The code in this directory hierarchy was cut-n-pasted from
-somewhere else.
-
-In the soon to come, Star Trek future, this code will be available
-as part of standard libraries.
-
-For example, the code in libegg will be one day available as part
-of Gtk+.
-
-Until that happens, DON'T HACK the code, unless you are updating
-from the original cut-n-paste source.
-
-Instead of hacking the code in cut-n-paste-code, create subclasses
-and put them in libcaja-extensions.
-
-If you have any specific questions, comments or complaints about this
-setup, open an issue on GitHub at:
-
-https://github.com/mate-desktop/caja/issues
diff --git a/cut-n-paste-code/libegg/Makefile.am b/cut-n-paste-code/libegg/Makefile.am
deleted file mode 100644
index 3d6e3977..00000000
--- a/cut-n-paste-code/libegg/Makefile.am
+++ /dev/null
@@ -1,46 +0,0 @@
-NULL=
-
-noinst_LTLIBRARIES = libegg.la
-
-AM_CPPFLAGS = $(LIBEGG_CFLAGS)
-
-EGG_TREE_DND_FILES = 		\
-	eggtreemultidnd.c	\
-	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
diff --git a/cut-n-paste-code/libegg/eggdesktopfile.c b/cut-n-paste-code/libegg/eggdesktopfile.c
deleted file mode 100644
index eb227b69..00000000
--- a/cut-n-paste-code/libegg/eggdesktopfile.c
+++ /dev/null
@@ -1,1468 +0,0 @@
-/* eggdesktopfile.c - Freedesktop.Org Desktop Files
- * Copyright (C) 2007 Novell, Inc.
- *
- * Based on mate-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., Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, 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)
-{
-    return g_key_file_get_double (desktop_file->key_file,
-                                  EGG_DESKTOP_FILE_GROUP, key,
-                                  error);
-}
-
-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;
-}
-
-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);
-}
-
-static GPtrArray *
-array_putenv (GPtrArray *env, char *variable)
-{
-    int i, keylen;
-
-    if (!env)
-    {
-        char **envp;
-
-        env = g_ptr_array_new ();
-
-        envp = g_listenv ();
-        for (i = 0; envp[i]; i++)
-        {
-            const char *value;
-
-            value = g_getenv (envp[i]);
-            g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i],
-                                                   value ? value : ""));
-        }
-        g_strfreev (envp);
-    }
-
-    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)
-    {
-        display = gdk_screen_get_display (screen);
-        char *display_name = g_strdup (gdk_display_get_name (display));
-        char *display_env = g_strdup_printf ("DISPLAY=%s", display_name);
-        env = array_putenv (env, display_env);
-        g_free (display_name);
-        g_free (display_env);
-    }
-    else
-    {
-        display = gdk_display_get_default ();
-        screen = gdk_display_get_default_screen (display);
-    }
-    screen_num = gdk_x11_screen_get_screen_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);
-
-        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);
-        }
-
-        if (env != NULL)
-            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)
-        {
-            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
-                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_strfreev ((char **)env->pdata);
-        g_ptr_array_free (env, FALSE);
-    }
-    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;
-}
diff --git a/cut-n-paste-code/libegg/eggdesktopfile.h b/cut-n-paste-code/libegg/eggdesktopfile.h
deleted file mode 100644
index 4a1d4064..00000000
--- a/cut-n-paste-code/libegg/eggdesktopfile.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/* 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., Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifndef __EGG_DESKTOP_FILE_H__
-#define __EGG_DESKTOP_FILE_H__
-
-#include <glib.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-    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);
-
-    EggDesktopFileType  egg_desktop_file_get_desktop_file_type (EggDesktopFile  *desktop_file);
-
-    const char         *egg_desktop_file_get_name           (EggDesktopFile  *desktop_file);
-    const char         *egg_desktop_file_get_icon           (EggDesktopFile  *desktop_file);
-
-    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);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __EGG_DESKTOP_FILE_H__ */
diff --git a/cut-n-paste-code/libegg/eggsmclient-private.h b/cut-n-paste-code/libegg/eggsmclient-private.h
deleted file mode 100644
index 3eec5324..00000000
--- a/cut-n-paste-code/libegg/eggsmclient-private.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* 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., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifndef __EGG_SM_CLIENT_PRIVATE_H__
-#define __EGG_SM_CLIENT_PRIVATE_H__
-
-#include <gtk/gtk.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
-#endif
-
-G_END_DECLS
-
-#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */
diff --git a/cut-n-paste-code/libegg/eggsmclient-xsmp.c b/cut-n-paste-code/libegg/eggsmclient-xsmp.c
deleted file mode 100644
index 808fffc0..00000000
--- a/cut-n-paste-code/libegg/eggsmclient-xsmp.c
+++ /dev/null
@@ -1,1366 +0,0 @@
-/*
- * Copyright (C) 2007 Novell, Inc.
- *
- * Inspired by various other pieces of code including GsmClient (C)
- * 2001 Havoc Pennington, MateClient (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., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, 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 <gtk/gtk.h>
-#include <gdk/gdk.h>
-#include <gdk/gdkx.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_IDLE,
-    XSMP_STATE_SAVE_YOURSELF,
-    XSMP_STATE_INTERACT_REQUEST,
-    XSMP_STATE_INTERACT,
-    XSMP_STATE_SAVE_YOURSELF_DONE,
-    XSMP_STATE_SHUTDOWN_CANCELLED,
-    XSMP_STATE_CONNECTION_CLOSED
-} EggSMClientXSMPState;
-
-static const char *state_names[] =
-{
-    "idle",
-    "save-yourself",
-    "interact-request",
-    "interact",
-    "save-yourself-done",
-    "shutdown-cancelled",
-    "connection-closed"
-};
-
-#define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state])
-
-struct _EggSMClientXSMP
-{
-    EggSMClient parent;
-
-    SmcConn connection;
-    char *client_id;
-
-    EggSMClientXSMPState state;
-    char **restart_command;
-    gboolean set_restart_command;
-    int restart_style;
-
-    guint idle;
-
-    /* Current SaveYourself state */
-    guint expecting_initial_save_yourself : 1;
-    guint need_save_state : 1;
-    guint need_quit_requested : 1;
-    guint interact_errors : 1;
-    guint shutting_down : 1;
-
-    /* Todo list */
-    guint waiting_to_set_initial_properties : 1;
-    guint waiting_to_emit_quit : 1;
-    guint waiting_to_emit_quit_cancelled : 1;
-    guint waiting_to_save_myself : 1;
-
-};
-
-struct _EggSMClientXSMPClass
-{
-    EggSMClientClass parent_class;
-
-};
-
-static void     sm_client_xsmp_startup (EggSMClient *client,
-                                        const char  *client_id);
-static void     sm_client_xsmp_set_restart_command (EggSMClient  *client,
-        int           argc,
-        const char  **argv);
-static void     sm_client_xsmp_will_quit (EggSMClient *client,
-        gboolean     will_quit);
-static gboolean sm_client_xsmp_end_session (EggSMClient         *client,
-        EggSMClientEndStyle  style,
-        gboolean  request_confirmation);
-
-static void xsmp_save_yourself      (SmcConn   smc_conn,
-                                     SmPointer client_data,
-                                     int       save_style,
-                                     Bool      shutdown,
-                                     int       interact_style,
-                                     Bool      fast);
-static void xsmp_die                (SmcConn   smc_conn,
-                                     SmPointer client_data);
-static void xsmp_save_complete      (SmcConn   smc_conn,
-                                     SmPointer client_data);
-static void xsmp_shutdown_cancelled (SmcConn   smc_conn,
-                                     SmPointer client_data);
-static void xsmp_interact           (SmcConn   smc_conn,
-                                     SmPointer client_data);
-
-static SmProp *array_prop        (const char    *name,
-                                  ...);
-static SmProp *ptrarray_prop     (const char    *name,
-                                  GPtrArray     *values);
-static SmProp *string_prop       (const char    *name,
-                                  const char    *value);
-static SmProp *card8_prop        (const char    *name,
-                                  unsigned char  value);
-
-static void set_properties         (EggSMClientXSMP *xsmp, ...);
-static void delete_properties      (EggSMClientXSMP *xsmp, ...);
-
-static GPtrArray *generate_command (char       **restart_command,
-                                    const char  *client_id,
-                                    const char  *state_file);
-
-static void save_state            (EggSMClientXSMP *xsmp);
-static void do_save_yourself      (EggSMClientXSMP *xsmp);
-static void update_pending_events (EggSMClientXSMP *xsmp);
-
-static void     ice_init             (void);
-static gboolean process_ice_messages (IceConn       ice_conn);
-static void     smc_error_handler    (SmcConn       smc_conn,
-                                      Bool          swap,
-                                      int           offending_minor_opcode,
-                                      unsigned long offending_sequence,
-                                      int           error_class,
-                                      int           severity,
-                                      SmPointer     values);
-
-G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT)
-
-static void
-egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp)
-{
-    xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
-    xsmp->connection = NULL;
-    xsmp->restart_style = SmRestartIfRunning;
-}
-
-static void
-egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass)
-{
-    EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
-
-    sm_client_class->startup             = sm_client_xsmp_startup;
-    sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command;
-    sm_client_class->will_quit           = sm_client_xsmp_will_quit;
-    sm_client_class->end_session         = sm_client_xsmp_end_session;
-}
-
-EggSMClient *
-egg_sm_client_xsmp_new (void)
-{
-    if (!g_getenv ("SESSION_MANAGER"))
-        return NULL;
-
-    return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL);
-}
-
-static gboolean
-sm_client_xsmp_set_initial_properties (gpointer user_data)
-{
-    EggSMClientXSMP *xsmp = user_data;
-    EggDesktopFile *desktop_file;
-    GPtrArray *clone, *restart;
-    char pid_str[64];
-
-    if (xsmp->idle)
-    {
-        g_source_remove (xsmp->idle);
-        xsmp->idle = 0;
-    }
-    xsmp->waiting_to_set_initial_properties = FALSE;
-
-    if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART)
-        xsmp->restart_style = SmRestartNever;
-
-    /* Parse info out of desktop file */
-    desktop_file = egg_get_desktop_file ();
-    if (desktop_file)
-    {
-        GError *err = NULL;
-        char *cmdline, **argv;
-        int argc;
-
-        if (xsmp->restart_style == SmRestartIfRunning)
-        {
-            if (egg_desktop_file_get_boolean (desktop_file,
-                                              "X-MATE-AutoRestart", NULL))
-                xsmp->restart_style = SmRestartImmediately;
-        }
-
-        if (!xsmp->set_restart_command)
-        {
-            cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err);
-            if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err))
-            {
-                egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp),
-                                                   argc, (const char **)argv);
-                g_strfreev (argv);
-            }
-            else
-            {
-                g_warning ("Could not parse Exec line in desktop file: %s",
-                           err->message);
-                g_error_free (err);
-            }
-            g_free (cmdline);
-        }
-    }
-
-    if (!xsmp->set_restart_command)
-        xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1);
-
-    clone = generate_command (xsmp->restart_command, NULL, NULL);
-    restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
-
-    g_debug ("Setting initial properties");
-
-    /* Program, CloneCommand, RestartCommand, and UserID are required.
-     * ProcessID isn't required, but the SM may be able to do something
-     * useful with it.
-     */
-    g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ());
-    set_properties (xsmp,
-                    string_prop   (SmProgram, g_get_prgname ()),
-                    ptrarray_prop (SmCloneCommand, clone),
-                    ptrarray_prop (SmRestartCommand, restart),
-                    string_prop   (SmUserID, g_get_user_name ()),
-                    string_prop   (SmProcessID, pid_str),
-                    card8_prop    (SmRestartStyleHint, xsmp->restart_style),
-                    NULL);
-    g_ptr_array_free (clone, TRUE);
-    g_ptr_array_free (restart, TRUE);
-
-    if (desktop_file)
-    {
-        set_properties (xsmp,
-                        string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)),
-                        NULL);
-    }
-
-    update_pending_events (xsmp);
-    return FALSE;
-}
-
-/* This gets called from two different places: xsmp_die() (when the
- * server asks us to disconnect) and process_ice_messages() (when the
- * server disconnects unexpectedly).
- */
-static void
-sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
-{
-    SmcConn connection;
-
-    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;
-    SmcCallbacks callbacks;
-    char *ret_client_id;
-    char error_string_ret[256];
-
-    xsmp->client_id = g_strdup (client_id);
-
-    ice_init ();
-    SmcSetErrorHandler (smc_error_handler);
-
-    callbacks.save_yourself.callback      = xsmp_save_yourself;
-    callbacks.die.callback                = xsmp_die;
-    callbacks.save_complete.callback      = xsmp_save_complete;
-    callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
-
-    callbacks.save_yourself.client_data      = xsmp;
-    callbacks.die.client_data                = xsmp;
-    callbacks.save_complete.client_data      = xsmp;
-    callbacks.shutdown_cancelled.client_data = xsmp;
-
-    client_id = NULL;
-    error_string_ret[0] = '\0';
-    xsmp->connection =
-        SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
-                           SmcSaveYourselfProcMask | SmcDieProcMask |
-                           SmcSaveCompleteProcMask |
-                           SmcShutdownCancelledProcMask,
-                           &callbacks,
-                           xsmp->client_id, &ret_client_id,
-                           sizeof (error_string_ret), error_string_ret);
-
-    if (!xsmp->connection)
-    {
-        g_warning ("Failed to connect to the session manager: %s\n",
-                   error_string_ret[0] ?
-                   error_string_ret : "no error message given");
-        xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
-        return;
-    }
-
-    /* We expect a pointless initial SaveYourself if either (a) we
-     * didn't have an initial client ID, or (b) we DID have an initial
-     * client ID, but the server rejected it and gave us a new one.
-     */
-    if (!xsmp->client_id ||
-            (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0))
-        xsmp->expecting_initial_save_yourself = TRUE;
-
-    if (ret_client_id)
-    {
-        g_free (xsmp->client_id);
-        xsmp->client_id = g_strdup (ret_client_id);
-        free (ret_client_id);
-
-        gdk_x11_set_sm_client_id (xsmp->client_id);
-
-        g_debug ("Got client ID \"%s\"", xsmp->client_id);
-    }
-
-    xsmp->state = XSMP_STATE_IDLE;
-
-    /* Do not set the initial properties until we reach the main loop,
-     * so that the application has a chance to call
-     * egg_set_desktop_file(). (This may also help the session manager
-     * have a better idea of when the application is fully up and
-     * running.)
-     */
-    xsmp->waiting_to_set_initial_properties = TRUE;
-    xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client);
-}
-
-static void
-sm_client_xsmp_set_restart_command (EggSMClient  *client,
-                                    int           argc,
-                                    const char  **argv)
-{
-    EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
-    int i;
-
-    g_strfreev (xsmp->restart_command);
-
-    xsmp->restart_command = g_new (char *, argc + 1);
-    for (i = 0; i < argc; i++)
-        xsmp->restart_command[i] = g_strdup (argv[i]);
-    xsmp->restart_command[i] = NULL;
-
-    xsmp->set_restart_command = TRUE;
-}
-
-static void
-sm_client_xsmp_will_quit (EggSMClient *client,
-                          gboolean     will_quit)
-{
-    EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
-
-    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_CONNECTION_CLOSED:
-            return FALSE;
-
-        case XSMP_STATE_SAVE_YOURSELF:
-            /* Trying to log out from the save_state callback? Whatever.
-             * Abort the save_state.
-             */
-            SmcSaveYourselfDone (xsmp->connection, FALSE);
-            xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
-            break;
-
-        case XSMP_STATE_INTERACT_REQUEST:
-        case XSMP_STATE_INTERACT:
-        case XSMP_STATE_SHUTDOWN_CANCELLED:
-            /* Already in a shutdown-related state, just ignore
-             * the new shutdown request...
-             */
-            return TRUE;
-
-        case XSMP_STATE_IDLE:
-            if (xsmp->waiting_to_set_initial_properties)
-                sm_client_xsmp_set_initial_properties (xsmp);
-
-            if (!xsmp->expecting_initial_save_yourself)
-                break;
-            /* else fall through */
-
-        case XSMP_STATE_SAVE_YOURSELF_DONE:
-            /* We need to wait for some response from the server.*/
-            process_ice_messages (SmcGetIceConnection (xsmp->connection));
-            break;
-
-        default:
-            /* Hm... shouldn't happen */
-            return FALSE;
-        }
-    }
-
-    /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and
-     * the user chooses to save the session. But mate-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;
-
-    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:
-    return FALSE;
-}
-
-static void
-update_pending_events (EggSMClientXSMP *xsmp)
-{
-    gboolean want_idle =
-        xsmp->waiting_to_emit_quit ||
-        xsmp->waiting_to_emit_quit_cancelled ||
-        xsmp->waiting_to_save_myself;
-
-    if (want_idle)
-    {
-        if (xsmp->idle == 0)
-            xsmp->idle = g_idle_add (idle_do_pending_events, xsmp);
-    }
-    else
-    {
-        if (xsmp->idle != 0)
-            g_source_remove (xsmp->idle);
-        xsmp->idle = 0;
-    }
-}
-
-static void
-fix_broken_state (EggSMClientXSMP *xsmp, const char *message,
-                  gboolean send_interact_done,
-                  gboolean send_save_yourself_done)
-{
-    g_warning ("Received XSMP %s message in state %s: client or server error",
-               message, EGG_SM_CLIENT_XSMP_STATE (xsmp));
-
-    /* Forget any pending SaveYourself plans we had */
-    xsmp->waiting_to_save_myself = FALSE;
-    update_pending_events (xsmp);
-
-    if (send_interact_done)
-        SmcInteractDone (xsmp->connection, False);
-    if (send_save_yourself_done)
-        SmcSaveYourselfDone (xsmp->connection, True);
-
-    xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE;
-}
-
-/* SM callbacks */
-
-static void
-xsmp_save_yourself (SmcConn   smc_conn,
-                    SmPointer client_data,
-                    int       save_type,
-                    Bool      shutdown,
-                    int       interact_style,
-                    Bool      fast)
-{
-    EggSMClientXSMP *xsmp = client_data;
-    gboolean wants_quit_requested;
-
-    g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
-             save_type == SmSaveLocal ? "SmSaveLocal" :
-             save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
-             shutdown ? "Shutdown" : "!Shutdown",
-             interact_style == SmInteractStyleAny ? "SmInteractStyleAny" :
-             interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
-             "SmInteractStyleNone", fast ? "Fast" : "!Fast",
-             EGG_SM_CLIENT_XSMP_STATE (xsmp));
-
-    if (xsmp->state != XSMP_STATE_IDLE &&
-            xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
-    {
-        fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE);
-        return;
-    }
-
-    if (xsmp->waiting_to_set_initial_properties)
-        sm_client_xsmp_set_initial_properties (xsmp);
-
-    /* If this is the initial SaveYourself, ignore it; we've already set
-     * properties and there's no reason to actually save state too.
-     */
-    if (xsmp->expecting_initial_save_yourself)
-    {
-        xsmp->expecting_initial_save_yourself = FALSE;
-
-        if (save_type == SmSaveLocal &&
-                interact_style == SmInteractStyleNone &&
-                !shutdown && !fast)
-        {
-            g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself");
-            SmcSaveYourselfDone (xsmp->connection, True);
-            /* As explained in the comment at the end of
-             * do_save_yourself(), SAVE_YOURSELF_DONE is the correct
-             * state here, not IDLE.
-             */
-            xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
-            return;
-        }
-        else
-            g_warning ("First SaveYourself was not the expected one!");
-    }
-
-    /* Even ignoring the "fast" flag completely, there are still 18
-     * different combinations of save_type, shutdown and interact_style.
-     * We interpret them as follows:
-     *
-     *   Type  Shutdown  Interact	 Interpretation
-     *     G      F       A/E/N  	 do nothing (1)
-     *     G      T         N    	 do nothing (1)*
-     *     G      T        A/E   	 quit_requested (2)
-     *    L/B     F       A/E/N  	 save_state (3)
-     *    L/B     T         N    	 save_state (3)*
-     *    L/B     T        A/E   	 quit_requested, then save_state (4)
-     *
-     *   1. Do nothing, because the SM asked us to do something
-     *      uninteresting (save open files, but then don't quit
-     *      afterward) or rude (save open files without asking the user
-     *      for confirmation).
-     *
-     *   2. Request interaction and then emit ::quit_requested. This
-     *      perhaps isn't quite correct for the SmInteractStyleErrors
-     *      case, but we don't care.
-     *
-     *   3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these
-     *      rows essentially get demoted to SmSaveLocal, because their
-     *      Global halves correspond to "do nothing".
-     *
-     *   4. Request interaction, emit ::quit_requested, and then emit
-     *      ::save_state after interacting. This is the SmSaveBoth
-     *      equivalent of #2, but we also promote SmSaveLocal shutdown
-     *      SaveYourselfs to SmSaveBoth here, because we want to give
-     *      the user a chance to save open files before quitting.
-     *
-     * (* It would be nice if we could do something useful when the
-     * session manager sends a SaveYourself with shutdown True and
-     * SmInteractStyleNone. But we can't, so we just pretend it didn't
-     * even tell us it was shutting down. The docs for ::quit mention
-     * that it might not always be preceded by ::quit_requested.)
-     */
-
-    /* As an optimization, we don't actually request interaction and
-     * emit ::quit_requested if the application isn't listening to the
-     * signal.
-     */
-    wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE);
-
-    xsmp->need_save_state     = (save_type != SmSaveGlobal);
-    xsmp->need_quit_requested = (shutdown && wants_quit_requested &&
-                                 interact_style != SmInteractStyleNone);
-    xsmp->interact_errors     = (interact_style == SmInteractStyleErrors);
-
-    xsmp->shutting_down       = shutdown;
-
-    do_save_yourself (xsmp);
-}
-
-static void
-do_save_yourself (EggSMClientXSMP *xsmp)
-{
-    if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
-    {
-        /* The SM cancelled a previous SaveYourself, but we haven't yet
-         * had a chance to tell the application, so we can't start
-         * processing this SaveYourself yet.
-         */
-        xsmp->waiting_to_save_myself = TRUE;
-        update_pending_events (xsmp);
-        return;
-    }
-
-    if (xsmp->need_quit_requested)
-    {
-        xsmp->state = XSMP_STATE_INTERACT_REQUEST;
-
-        g_debug ("Sending InteractRequest(%s)",
-                 xsmp->interact_errors ? "Error" : "Normal");
-        SmcInteractRequest (xsmp->connection,
-                            xsmp->interact_errors ? SmDialogError : SmDialogNormal,
-                            xsmp_interact,
-                            xsmp);
-        return;
-    }
-
-    if (xsmp->need_save_state)
-    {
-        save_state (xsmp);
-
-        /* Though unlikely, the client could have been disconnected
-         * while the application was saving its state.
-         */
-        if (!xsmp->connection)
-            return;
-    }
-
-    g_debug ("Sending SaveYourselfDone(True)");
-    SmcSaveYourselfDone (xsmp->connection, True);
-
-    /* The client state diagram in the XSMP spec says that after a
-     * non-shutdown SaveYourself, we go directly back to "idle". But
-     * everything else in both the XSMP spec and the libSM docs
-     * disagrees.
-     */
-    xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
-}
-
-static void
-save_state (EggSMClientXSMP *xsmp)
-{
-    GKeyFile *state_file;
-    char *state_file_path, *data;
-    EggDesktopFile *desktop_file;
-    GPtrArray *restart;
-    int offset, fd;
-
-    /* We set xsmp->state before emitting save_state, but our caller is
-     * responsible for setting it back afterward.
-     */
-    xsmp->state = XSMP_STATE_SAVE_YOURSELF;
-
-    state_file = egg_sm_client_save_state ((EggSMClient *)xsmp);
-    if (!state_file)
-    {
-        restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
-        set_properties (xsmp,
-                        ptrarray_prop (SmRestartCommand, restart),
-                        NULL);
-        g_ptr_array_free (restart, TRUE);
-        delete_properties (xsmp, SmDiscardCommand, NULL);
-        return;
-    }
-
-    desktop_file = egg_get_desktop_file ();
-    if (desktop_file)
-    {
-        GKeyFile *merged_file;
-        char *desktop_file_path;
-
-        merged_file = g_key_file_new ();
-        desktop_file_path =
-            g_filename_from_uri (egg_desktop_file_get_source (desktop_file),
-                                 NULL, NULL);
-        if (desktop_file_path &&
-                g_key_file_load_from_file (merged_file, desktop_file_path,
-                                           G_KEY_FILE_KEEP_COMMENTS |
-                                           G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
-        {
-            guint g, k, i;
-            char **groups, **keys, *value, *exec;
-
-            groups = g_key_file_get_groups (state_file, NULL);
-            for (g = 0; groups[g]; g++)
-            {
-                keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL);
-                for (k = 0; keys[k]; k++)
-                {
-                    value = g_key_file_get_value (state_file, groups[g],
-                                                  keys[k], NULL);
-                    if (value)
-                    {
-                        g_key_file_set_value (merged_file, groups[g],
-                                              keys[k], value);
-                        g_free (value);
-                    }
-                }
-                g_strfreev (keys);
-            }
-            g_strfreev (groups);
-
-            g_key_file_free (state_file);
-            state_file = merged_file;
-
-            /* Update Exec key using "--sm-client-state-file %k" */
-            restart = generate_command (xsmp->restart_command,
-                                        NULL, "%k");
-            for (i = 0; i < restart->len; i++)
-                restart->pdata[i] = g_shell_quote (restart->pdata[i]);
-            g_ptr_array_add (restart, NULL);
-            exec = g_strjoinv (" ", (char **)restart->pdata);
-            g_strfreev ((char **)restart->pdata);
-            g_ptr_array_free (restart, FALSE);
-
-            g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
-                                   EGG_DESKTOP_FILE_KEY_EXEC,
-                                   exec);
-            g_free (exec);
-        }
-        else
-            desktop_file = NULL;
-
-        g_free (desktop_file_path);
-    }
-
-    /* Now write state_file to disk. (We can't use mktemp(), because
-     * that requires the filename to end with "XXXXXX", and we want
-     * it to end with ".desktop".)
-     */
-
-    data = g_key_file_to_data (state_file, NULL, NULL);
-    g_key_file_free (state_file);
-
-    offset = 0;
-    while (1)
-    {
-        state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s",
-                                           g_get_user_config_dir (),
-                                           G_DIR_SEPARATOR, G_DIR_SEPARATOR,
-                                           g_get_prgname (),
-                                           (long)time (NULL) + offset,
-                                           desktop_file ? "desktop" : "state");
-
-        fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644);
-        if (fd == -1)
-        {
-            if (errno == EEXIST)
-            {
-                offset++;
-                g_free (state_file_path);
-                continue;
-            }
-            else if (errno == ENOTDIR || errno == ENOENT)
-            {
-                char *sep = strrchr (state_file_path, G_DIR_SEPARATOR);
-
-                *sep = '\0';
-                if (g_mkdir_with_parents (state_file_path, 0755) != 0)
-                {
-                    g_warning ("Could not create directory '%s'",
-                               state_file_path);
-                    g_free (state_file_path);
-                    state_file_path = NULL;
-                    break;
-                }
-
-                continue;
-            }
-
-            g_warning ("Could not create file '%s': %s",
-                       state_file_path, g_strerror (errno));
-            g_free (state_file_path);
-            state_file_path = NULL;
-            break;
-        }
-
-        close (fd);
-        g_file_set_contents (state_file_path, data, -1, NULL);
-        break;
-    }
-    g_free (data);
-
-    restart = generate_command (xsmp->restart_command, xsmp->client_id,
-                                state_file_path);
-    set_properties (xsmp,
-                    ptrarray_prop (SmRestartCommand, restart),
-                    NULL);
-    g_ptr_array_free (restart, TRUE);
-
-    if (state_file_path)
-    {
-        set_properties (xsmp,
-                        array_prop (SmDiscardCommand,
-                                    "/bin/rm", "-rf", state_file_path,
-                                    NULL),
-                        NULL);
-        g_free (state_file_path);
-    }
-}
-
-static void
-xsmp_interact (SmcConn   smc_conn,
-               SmPointer client_data)
-{
-    EggSMClientXSMP *xsmp = client_data;
-    EggSMClient *client = client_data;
-
-    g_debug ("Received Interact message in state %s",
-             EGG_SM_CLIENT_XSMP_STATE (xsmp));
-
-    if (xsmp->state != XSMP_STATE_INTERACT_REQUEST)
-    {
-        fix_broken_state (xsmp, "Interact", TRUE, TRUE);
-        return;
-    }
-
-    xsmp->state = XSMP_STATE_INTERACT;
-    egg_sm_client_quit_requested (client);
-}
-
-static void
-xsmp_die (SmcConn   smc_conn,
-          SmPointer client_data)
-{
-    EggSMClientXSMP *xsmp = client_data;
-    EggSMClient *client = client_data;
-
-    g_debug ("Received Die message in state %s",
-             EGG_SM_CLIENT_XSMP_STATE (xsmp));
-
-    sm_client_xsmp_disconnect (xsmp);
-    egg_sm_client_quit (client);
-}
-
-static void
-xsmp_save_complete (SmcConn   smc_conn,
-                    SmPointer client_data)
-{
-    EggSMClientXSMP *xsmp = client_data;
-
-    g_debug ("Received SaveComplete message in state %s",
-             EGG_SM_CLIENT_XSMP_STATE (xsmp));
-
-    if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
-        xsmp->state = XSMP_STATE_IDLE;
-    else
-        fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE);
-}
-
-static void
-xsmp_shutdown_cancelled (SmcConn   smc_conn,
-                         SmPointer client_data)
-{
-    EggSMClientXSMP *xsmp = client_data;
-    EggSMClient *client = client_data;
-
-    g_debug ("Received ShutdownCancelled message in state %s",
-             EGG_SM_CLIENT_XSMP_STATE (xsmp));
-
-    xsmp->shutting_down = FALSE;
-
-    if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
-    {
-        /* We've finished interacting and now the SM has agreed to
-         * cancel the shutdown.
-         */
-        xsmp->state = XSMP_STATE_IDLE;
-        egg_sm_client_quit_cancelled (client);
-    }
-    else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
-    {
-        /* Hm... ok, so we got a shutdown SaveYourself, which got
-         * cancelled, but the application was still interacting, so we
-         * didn't tell it yet, and then *another* SaveYourself arrived,
-         * which we must still be waiting to tell the app about, except
-         * that now that SaveYourself has been cancelled too! Dizzy yet?
-         */
-        xsmp->waiting_to_save_myself = FALSE;
-        update_pending_events (xsmp);
-    }
-    else
-    {
-        g_debug ("Sending SaveYourselfDone(False)");
-        SmcSaveYourselfDone (xsmp->connection, False);
-
-        if (xsmp->state == XSMP_STATE_INTERACT)
-        {
-            /* The application is currently interacting, so we can't
-             * tell it about the cancellation yet; we will wait until
-             * after it calls egg_sm_client_will_quit().
-             */
-            xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED;
-        }
-        else
-        {
-            /* The shutdown was cancelled before the application got a
-             * chance to interact.
-             */
-            xsmp->state = XSMP_STATE_IDLE;
-        }
-    }
-}
-
-/* Utilities */
-
-/* Create a restart/clone/Exec command based on @restart_command.
- * If @client_id is non-%NULL, add "--sm-client-id @client_id".
- * If @state_file is non-%NULL, add "--sm-client-state-file @state_file".
- *
- * None of the input strings are g_strdup()ed; the caller must keep
- * them around until it is done with the returned GPtrArray, and must
- * then free the array, but not its contents.
- */
-static GPtrArray *
-generate_command (char **restart_command, const char *client_id,
-                  const char *state_file)
-{
-    GPtrArray *cmd;
-    int i;
-
-    cmd = g_ptr_array_new ();
-    g_ptr_array_add (cmd, restart_command[0]);
-
-    if (client_id)
-    {
-        g_ptr_array_add (cmd, "--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;
-    guint i;
-
-    props = g_ptr_array_new ();
-
-    va_start (ap, xsmp);
-    while ((prop = va_arg (ap, SmProp *)))
-        g_ptr_array_add (props, prop);
-    va_end (ap);
-
-    if (xsmp->connection)
-    {
-        SmcSetProperties (xsmp->connection, props->len,
-                          (SmProp **)props->pdata);
-    }
-
-    for (i = 0; i < props->len; i++)
-    {
-        prop = props->pdata[i];
-        g_free (prop->vals);
-        g_free (prop);
-    }
-    g_ptr_array_free (props, TRUE);
-}
-
-/* Takes a NULL-terminated list of property names and deletes them. */
-static void
-delete_properties (EggSMClientXSMP *xsmp, ...)
-{
-    GPtrArray *props;
-    char *prop;
-    va_list ap;
-
-    if (!xsmp->connection)
-        return;
-
-    props = g_ptr_array_new ();
-
-    va_start (ap, xsmp);
-    while ((prop = va_arg (ap, char *)))
-        g_ptr_array_add (props, prop);
-    va_end (ap);
-
-    SmcDeleteProperties (xsmp->connection, props->len,
-                         (char **)props->pdata);
-
-    g_ptr_array_free (props, TRUE);
-}
-
-/* Takes an array of strings and creates a LISTofARRAY8 property. The
- * strings are neither dupped nor freed; they need to remain valid
- * until you're done with the SmProp.
- */
-static SmProp *
-array_prop (const char *name, ...)
-{
-    SmProp *prop;
-    SmPropValue pv;
-    GArray *vals;
-    char *value;
-    va_list ap;
-
-    prop = g_new (SmProp, 1);
-    prop->name = (char *)name;
-    prop->type = 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);
-    }
-    va_end (ap);
-
-    prop->num_vals = vals->len;
-    prop->vals = (SmPropValue *)vals->data;
-
-    g_array_free (vals, FALSE);
-
-    return prop;
-}
-
-/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property.
- * The array contents are neither dupped nor freed; they need to
- * remain valid until you're done with the SmProp.
- */
-static SmProp *
-ptrarray_prop (const char *name, GPtrArray *values)
-{
-    SmProp *prop;
-    SmPropValue pv;
-    GArray *vals;
-    guint i;
-
-    prop = g_new (SmProp, 1);
-    prop->name = (char *)name;
-    prop->type = 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 MateClient 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;
-    status = IceProcessMessages (ice_conn, NULL, NULL);
-
-    switch (status)
-    {
-    case IceProcessMessagesSuccess:
-        return TRUE;
-
-    case IceProcessMessagesIOError:
-        sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
-        return FALSE;
-
-    case IceProcessMessagesConnectionClosed:
-        return FALSE;
-
-    default:
-        g_assert_not_reached ();
-    }
-}
-
-static gboolean
-ice_iochannel_watch (GIOChannel   *channel,
-                     GIOCondition  condition,
-                     gpointer      client_data)
-{
-    return process_ice_messages (client_data);
-}
-
-static void
-ice_connection_watch (IceConn     ice_conn,
-                      IcePointer  client_data,
-                      Bool        opening,
-                      IcePointer *watch_data)
-{
-    guint watch_id;
-
-    if (opening)
-    {
-        GIOChannel *channel;
-        int fd = IceConnectionNumber (ice_conn);
-
-        fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
-        channel = g_io_channel_unix_new (fd);
-        watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR,
-                                   ice_iochannel_watch, ice_conn);
-        g_io_channel_unref (channel);
-
-        *watch_data = GUINT_TO_POINTER (watch_id);
-    }
-    else
-    {
-        watch_id = GPOINTER_TO_UINT (*watch_data);
-        g_source_remove (watch_id);
-    }
-}
-
-static void
-ice_error_handler (IceConn       ice_conn,
-                   Bool          swap,
-                   int           offending_minor_opcode,
-                   unsigned long offending_sequence,
-                   int           error_class,
-                   int           severity,
-                   IcePointer    values)
-{
-    /* Do nothing */
-}
-
-static void
-ice_io_error_handler (IceConn ice_conn)
-{
-    /* Do nothing */
-}
-
-static void
-smc_error_handler (SmcConn       smc_conn,
-                   Bool          swap,
-                   int           offending_minor_opcode,
-                   unsigned long offending_sequence,
-                   int           error_class,
-                   int           severity,
-                   SmPointer     values)
-{
-    /* Do nothing */
-}
diff --git a/cut-n-paste-code/libegg/eggsmclient.c b/cut-n-paste-code/libegg/eggsmclient.c
deleted file mode 100644
index 4886f147..00000000
--- a/cut-n-paste-code/libegg/eggsmclient.c
+++ /dev/null
@@ -1,612 +0,0 @@
-/*
- * 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., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, 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 gboolean
-running_in_mate (void)
-{
-    return (g_strcmp0 (g_getenv ("XDG_CURRENT_DESKTOP"), "MATE") == 0)
-        || (g_strcmp0 (g_getenv ("XDG_SESSION_DESKTOP"), "MATE") == 0)
-        || (g_strcmp0 (g_getenv ("DESKTOP_SESSION"), "MATE") == 0);
-}
-
-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 char *sm_config_prefix = 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)
-{
-    const GOptionEntry entries[] =
-    {
-        {
-            "sm-client-disable", 0, 0,
-            G_OPTION_ARG_NONE, &sm_client_disable,
-            N_("Disable connection to session manager"), NULL
-        },
-        {
-            "sm-client-state-file", 0, 0,
-            G_OPTION_ARG_FILENAME, &sm_client_state_file,
-            N_("Specify file containing saved configuration"), N_("FILE")
-        },
-        {
-            "sm-client-id", 0, 0,
-            G_OPTION_ARG_STRING, &sm_client_id,
-            N_("Specify session management ID"), N_("ID")
-        },
-        /* MateClient compatibility option */
-        {
-            "sm-disable", 0, G_OPTION_FLAG_HIDDEN,
-            G_OPTION_ARG_NONE, &sm_client_disable,
-            NULL, NULL
-        },
-        /* MateClient compatibility option. This is a dummy option that only
-         * exists so that sessions saved by apps with MateClient can be restored
-         * later when they've switched to EggSMClient. See bug #575308.
-         */
-        {
-            "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN,
-            G_OPTION_ARG_STRING, &sm_config_prefix,
-            NULL, NULL
-        },
-        { NULL }
-    };
-    GOptionGroup *group;
-
-    /* Use our own debug handler for the "EggSMClient" domain. */
-    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 both D-Bus and XSMP are compiled in, try XSMP first
-             * (since it supports state saving) and fall back to D-Bus
-             * if XSMP isn't available.
-             */
-#ifdef EGG_SM_CLIENT_BACKEND_XSMP
-            global_client = egg_sm_client_xsmp_new ();
-#endif
-#ifdef EGG_SM_CLIENT_BACKEND_DBUS
-            if (!global_client)
-                global_client = egg_sm_client_dbus_new ();
-#endif
-        }
-
-        /* 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);
-        /*FIXME
-          Disabling when root/not in MATE in GtkApplication builds
-          as egg_sm_client_set_mode must be called prior to start of main loop
-          to stop caja restart but this is diffcult in GtkApplication */
-
-		if (geteuid () == 0 || !running_in_mate ()){
-        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);
-}
diff --git a/cut-n-paste-code/libegg/eggsmclient.h b/cut-n-paste-code/libegg/eggsmclient.h
deleted file mode 100644
index 1911b89e..00000000
--- a/cut-n-paste-code/libegg/eggsmclient.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/* 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., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, 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__ */
diff --git a/cut-n-paste-code/libegg/eggtreemultidnd.c b/cut-n-paste-code/libegg/eggtreemultidnd.c
deleted file mode 100644
index 062bb57e..00000000
--- a/cut-n-paste-code/libegg/eggtreemultidnd.c
+++ /dev/null
@@ -1,417 +0,0 @@
-/* eggtreemultidnd.c
- * Copyright (C) 2001  Red Hat, 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., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <gtk/gtk.h>
-#include "eggtreemultidnd.h"
-
-#define EGG_TREE_MULTI_DND_STRING "EggTreeMultiDndString"
-
-typedef struct
-{
-    guint pressed_button;
-    gint x;
-    gint y;
-    guint motion_notify_handler;
-    guint button_release_handler;
-    guint drag_data_get_handler;
-    GSList *event_list;
-} EggTreeMultiDndData;
-
-/* CUT-N-PASTE from gtktreeview.c */
-typedef struct _TreeViewDragInfo TreeViewDragInfo;
-struct _TreeViewDragInfo
-{
-    GdkModifierType start_button_mask;
-    GtkTargetList *source_target_list;
-    GdkDragAction source_actions;
-
-    GtkTargetList *dest_target_list;
-
-    guint source_set : 1;
-    guint dest_set : 1;
-};
-
-
-GType
-egg_tree_multi_drag_source_get_type (void)
-{
-    static GType our_type = 0;
-
-    if (!our_type)
-    {
-        const GTypeInfo our_info =
-        {
-            sizeof (EggTreeMultiDragSourceIface), /* class_size */
-            NULL,		/* base_init */
-            NULL,		/* base_finalize */
-            NULL,
-            NULL,		/* class_finalize */
-            NULL,		/* class_data */
-            0,
-            0,              /* n_preallocs */
-            NULL
-        };
-
-        our_type = g_type_register_static (G_TYPE_INTERFACE, "EggTreeMultiDragSource", &our_info, 0);
-    }
-
-    return our_type;
-}
-
-
-/**
- * egg_tree_multi_drag_source_row_draggable:
- * @drag_source: a #EggTreeMultiDragSource
- * @path: row on which user is initiating a drag
- *
- * Asks the #EggTreeMultiDragSource whether a particular row can be used as
- * the source of a DND operation. If the source doesn't implement
- * this interface, the row is assumed draggable.
- *
- * Return value: %TRUE if the row can be dragged
- **/
-gboolean
-egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source,
-        GList                  *path_list)
-{
-    EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
-
-    g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
-    g_return_val_if_fail (iface->row_draggable != NULL, FALSE);
-    g_return_val_if_fail (path_list != NULL, FALSE);
-
-    if (iface->row_draggable)
-        return (* iface->row_draggable) (drag_source, path_list);
-    else
-        return TRUE;
-}
-
-
-/**
- * egg_tree_multi_drag_source_drag_data_delete:
- * @drag_source: a #EggTreeMultiDragSource
- * @path: row that was being dragged
- *
- * Asks the #EggTreeMultiDragSource to delete the row at @path, because
- * it was moved somewhere else via drag-and-drop. Returns %FALSE
- * if the deletion fails because @path no longer exists, or for
- * some model-specific reason. Should robustly handle a @path no
- * longer found in the model!
- *
- * Return value: %TRUE if the row was successfully deleted
- **/
-gboolean
-egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
-        GList                  *path_list)
-{
-    EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
-
-    g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
-    g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE);
-    g_return_val_if_fail (path_list != NULL, FALSE);
-
-    return (* iface->drag_data_delete) (drag_source, path_list);
-}
-
-/**
- * egg_tree_multi_drag_source_drag_data_get:
- * @drag_source: a #EggTreeMultiDragSource
- * @path: row that was dragged
- * @selection_data: a #EggSelectionData to fill with data from the dragged row
- *
- * Asks the #EggTreeMultiDragSource to fill in @selection_data with a
- * representation of the row at @path. @selection_data->target gives
- * the required type of the data.  Should robustly handle a @path no
- * longer found in the model!
- *
- * Return value: %TRUE if data of the required type was provided
- **/
-gboolean
-egg_tree_multi_drag_source_drag_data_get    (EggTreeMultiDragSource *drag_source,
-        GList                  *path_list,
-        GtkSelectionData  *selection_data)
-{
-    EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
-
-    g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
-    g_return_val_if_fail (iface->drag_data_get != NULL, FALSE);
-    g_return_val_if_fail (path_list != NULL, FALSE);
-    g_return_val_if_fail (selection_data != NULL, FALSE);
-
-    return (* iface->drag_data_get) (drag_source, path_list, selection_data);
-}
-
-static void
-stop_drag_check (GtkWidget *widget)
-{
-    EggTreeMultiDndData *priv_data;
-    GSList *l;
-
-    priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
-
-    for (l = priv_data->event_list; l != NULL; l = l->next)
-        gdk_event_free (l->data);
-
-    g_slist_free (priv_data->event_list);
-    priv_data->event_list = NULL;
-    g_signal_handler_disconnect (widget, priv_data->motion_notify_handler);
-    g_signal_handler_disconnect (widget, priv_data->button_release_handler);
-}
-
-static gboolean
-egg_tree_multi_drag_button_release_event (GtkWidget      *widget,
-        GdkEventButton *event,
-        gpointer        data)
-{
-    EggTreeMultiDndData *priv_data;
-    GSList *l;
-
-    priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
-
-    for (l = priv_data->event_list; l != NULL; l = l->next)
-        gtk_propagate_event (widget, l->data);
-
-    stop_drag_check (widget);
-
-    return FALSE;
-}
-
-static void
-selection_foreach (GtkTreeModel *model,
-                   GtkTreePath  *path,
-                   GtkTreeIter  *iter,
-                   gpointer      data)
-{
-    GList **list_ptr;
-
-    list_ptr = (GList **) data;
-
-    *list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path));
-}
-
-static void
-path_list_free (GList *path_list)
-{
-    g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL);
-    g_list_free (path_list);
-}
-
-static void
-set_context_data (GdkDragContext *context,
-                  GList          *path_list)
-{
-    g_object_set_data_full (G_OBJECT (context),
-                            "egg-tree-view-multi-source-row",
-                            path_list,
-                            (GDestroyNotify) path_list_free);
-}
-
-static GList *
-get_context_data (GdkDragContext *context)
-{
-    return g_object_get_data (G_OBJECT (context),
-                              "egg-tree-view-multi-source-row");
-}
-
-/* CUT-N-PASTE from gtktreeview.c */
-static TreeViewDragInfo*
-get_info (GtkTreeView *tree_view)
-{
-    return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
-}
-
-
-static void
-egg_tree_multi_drag_drag_data_get (GtkWidget        *widget,
-                                   GdkDragContext   *context,
-                                   GtkSelectionData *selection_data,
-                                   guint             info,
-                                   guint             time)
-{
-    GtkTreeView *tree_view;
-    GtkTreeModel *model;
-    TreeViewDragInfo *di;
-    GList *path_list;
-
-    tree_view = GTK_TREE_VIEW (widget);
-
-    model = gtk_tree_view_get_model (tree_view);
-
-    if (model == NULL)
-        return;
-
-    di = get_info (GTK_TREE_VIEW (widget));
-
-    if (di == NULL)
-        return;
-
-    path_list = get_context_data (context);
-
-    if (path_list == NULL)
-        return;
-
-    /* We can implement the GTK_TREE_MODEL_ROW target generically for
-     * any model; for DragSource models there are some other targets
-     * we also support.
-     */
-
-    if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model))
-    {
-        egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model),
-                path_list,
-                selection_data);
-    }
-}
-
-static gboolean
-egg_tree_multi_drag_motion_event (GtkWidget      *widget,
-                                  GdkEventMotion *event,
-                                  gpointer        data)
-{
-    EggTreeMultiDndData *priv_data;
-
-    priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
-
-    if (gtk_drag_check_threshold (widget,
-                                  priv_data->x,
-                                  priv_data->y,
-                                  event->x,
-                                  event->y))
-    {
-        GList *path_list = NULL;
-        GtkTreeSelection *selection;
-        GtkTreeModel *model;
-        GdkDragContext *context;
-        TreeViewDragInfo *di;
-
-        di = get_info (GTK_TREE_VIEW (widget));
-
-        if (di == NULL)
-            return FALSE;
-
-        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
-        stop_drag_check (widget);
-        gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list);
-        path_list = g_list_reverse (path_list);
-        model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
-        if (egg_tree_multi_drag_source_row_draggable (EGG_TREE_MULTI_DRAG_SOURCE (model), path_list))
-        {
-
-            context = gtk_drag_begin_with_coordinates (widget,
-                                                       gtk_drag_source_get_target_list (widget),
-                                                       di->source_actions,
-                                                       priv_data->pressed_button,
-                                                       (GdkEvent*)event,
-                                                       event->x,
-                                                       event->y);
-            set_context_data (context, path_list);
-            gtk_drag_set_icon_default (context);
-
-        }
-        else
-        {
-            path_list_free (path_list);
-        }
-    }
-
-    return TRUE;
-}
-
-static gboolean
-egg_tree_multi_drag_button_press_event (GtkWidget      *widget,
-                                        GdkEventButton *event,
-                                        gpointer        data)
-{
-    GtkTreeView *tree_view;
-    GtkTreePath *path = NULL;
-    GtkTreeViewColumn *column = NULL;
-    gint cell_x, cell_y;
-    GtkTreeSelection *selection;
-    EggTreeMultiDndData *priv_data;
-
-    tree_view = GTK_TREE_VIEW (widget);
-    priv_data = g_object_get_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING);
-    if (priv_data == NULL)
-    {
-        priv_data = g_new0 (EggTreeMultiDndData, 1);
-        g_object_set_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING, priv_data);
-    }
-
-    if (g_slist_find (priv_data->event_list, event))
-        return FALSE;
-
-    if (priv_data->event_list)
-    {
-        /* save the event to be propagated in order */
-        priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
-        return TRUE;
-    }
-
-    if (event->type == GDK_2BUTTON_PRESS)
-        return FALSE;
-
-    gtk_tree_view_get_path_at_pos (tree_view,
-                                   event->x, event->y,
-                                   &path, &column,
-                                   &cell_x, &cell_y);
-
-    selection = gtk_tree_view_get_selection (tree_view);
-
-    if (path && gtk_tree_selection_path_is_selected (selection, path))
-    {
-        priv_data->pressed_button = event->button;
-        priv_data->x = event->x;
-        priv_data->y = event->y;
-        priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
-        priv_data->motion_notify_handler =
-            g_signal_connect (G_OBJECT (tree_view), "motion_notify_event", G_CALLBACK (egg_tree_multi_drag_motion_event), NULL);
-        priv_data->button_release_handler =
-            g_signal_connect (G_OBJECT (tree_view), "button_release_event", G_CALLBACK (egg_tree_multi_drag_button_release_event), NULL);
-
-        if (priv_data->drag_data_get_handler == 0)
-        {
-            priv_data->drag_data_get_handler =
-                g_signal_connect (G_OBJECT (tree_view), "drag_data_get", G_CALLBACK (egg_tree_multi_drag_drag_data_get), NULL);
-        }
-
-        gtk_tree_path_free (path);
-
-        return TRUE;
-    }
-
-    if (path)
-    {
-        gtk_tree_path_free (path);
-    }
-
-    return FALSE;
-}
-
-void
-egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view)
-{
-    g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
-    g_signal_connect (G_OBJECT (tree_view), "button_press_event", G_CALLBACK (egg_tree_multi_drag_button_press_event), NULL);
-}
-
diff --git a/cut-n-paste-code/libegg/eggtreemultidnd.h b/cut-n-paste-code/libegg/eggtreemultidnd.h
deleted file mode 100644
index 0510e517..00000000
--- a/cut-n-paste-code/libegg/eggtreemultidnd.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* eggtreednd.h
- * Copyright (C) 2001  Red Hat, 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., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifndef __EGG_TREE_MULTI_DND_H__
-#define __EGG_TREE_MULTI_DND_H__
-
-#include <gtk/gtk.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define EGG_TYPE_TREE_MULTI_DRAG_SOURCE            (egg_tree_multi_drag_source_get_type ())
-#define EGG_TREE_MULTI_DRAG_SOURCE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSource))
-#define EGG_IS_TREE_MULTI_DRAG_SOURCE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE))
-#define EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSourceIface))
-
-    typedef struct _EggTreeMultiDragSource      EggTreeMultiDragSource; /* Dummy typedef */
-    typedef struct _EggTreeMultiDragSourceIface EggTreeMultiDragSourceIface;
-
-    struct _EggTreeMultiDragSourceIface
-    {
-        GTypeInterface g_iface;
-
-        /* VTable - not signals */
-        gboolean     (* row_draggable)        (EggTreeMultiDragSource   *drag_source,
-                                               GList                    *path_list);
-
-        gboolean     (* drag_data_get)        (EggTreeMultiDragSource   *drag_source,
-                                               GList                    *path_list,
-                                               GtkSelectionData         *selection_data);
-
-        gboolean     (* drag_data_delete)     (EggTreeMultiDragSource *drag_source,
-                                               GList                  *path_list);
-    };
-
-    GType    egg_tree_multi_drag_source_get_type         (void) G_GNUC_CONST;
-
-    /* Returns whether the given row can be dragged */
-    gboolean egg_tree_multi_drag_source_row_draggable    (EggTreeMultiDragSource *drag_source,
-            GList                  *path_list);
-
-    /* Deletes the given row, or returns FALSE if it can't */
-    gboolean egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
-            GList                  *path_list);
-
-
-    /* Fills in selection_data with type selection_data->target based on the row
-     * denoted by path, returns TRUE if it does anything
-     */
-    gboolean egg_tree_multi_drag_source_drag_data_get    (EggTreeMultiDragSource *drag_source,
-            GList                  *path_list,
-            GtkSelectionData       *selection_data);
-    void     egg_tree_multi_drag_add_drag_support        (GtkTreeView            *tree_view);
-
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __EGG_TREE_MULTI_DND_H__ */
diff --git a/cut-n-paste-code/libegg/update-from-egg.sh b/cut-n-paste-code/libegg/update-from-egg.sh
deleted file mode 100755
index 9be68a9b..00000000
--- a/cut-n-paste-code/libegg/update-from-egg.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/sh
-
-function die() {
-  echo $*
-  exit 1
-}
-
-if test -z "$EGGDIR"; then
-   echo "Must set EGGDIR"
-   exit 1
-fi
-
-if test -z "$EGGFILES"; then
-   echo "Must set EGGFILES"
-   exit 1
-fi
-
-for FILE in $EGGFILES; do
-  if cmp -s $EGGDIR/$FILE $FILE; then
-     echo "File $FILE is unchanged"
-  else
-     cp $EGGDIR/$FILE $FILE || die "Could not move $EGGDIR/$FILE to $FILE"
-     echo "Updated $FILE"
-  fi
-done
diff --git a/libcaja-private/Makefile.am b/libcaja-private/Makefile.am
index c4abbd93..7d724641 100644
--- a/libcaja-private/Makefile.am
+++ b/libcaja-private/Makefile.am
@@ -5,7 +5,6 @@ noinst_LTLIBRARIES=libcaja-private.la
 AM_CPPFLAGS = \
 	-I$(top_srcdir) \
 	-I$(top_builddir) \
-	-I$(top_srcdir)/cut-n-paste-code \
 	$(CORE_CFLAGS) \
 	$(WARNING_CFLAGS) \
 	$(DISABLE_DEPRECATED_CFLAGS) \
@@ -23,7 +22,7 @@ BUILT_SOURCES = \
 	$(NULL)
 
 dependency_static_libs = \
-	$(top_builddir)/cut-n-paste-code/libegg/libegg.la \
+	$(top_builddir)/libegg/libegg.la \
 	$(NULL)
 
 libcaja_private_la_LDFLAGS = \
diff --git a/libegg/Makefile.am b/libegg/Makefile.am
new file mode 100644
index 00000000..89e4350a
--- /dev/null
+++ b/libegg/Makefile.am
@@ -0,0 +1,35 @@
+NULL=
+
+noinst_LTLIBRARIES = libegg.la
+
+AM_CPPFLAGS = $(LIBEGG_CFLAGS)
+
+EGG_TREE_DND_FILES = 		\
+	eggtreemultidnd.c	\
+	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
diff --git a/libegg/eggdesktopfile.c b/libegg/eggdesktopfile.c
new file mode 100644
index 00000000..eb227b69
--- /dev/null
+++ b/libegg/eggdesktopfile.c
@@ -0,0 +1,1468 @@
+/* eggdesktopfile.c - Freedesktop.Org Desktop Files
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * Based on mate-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., Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, 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)
+{
+    return g_key_file_get_double (desktop_file->key_file,
+                                  EGG_DESKTOP_FILE_GROUP, key,
+                                  error);
+}
+
+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;
+}
+
+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);
+}
+
+static GPtrArray *
+array_putenv (GPtrArray *env, char *variable)
+{
+    int i, keylen;
+
+    if (!env)
+    {
+        char **envp;
+
+        env = g_ptr_array_new ();
+
+        envp = g_listenv ();
+        for (i = 0; envp[i]; i++)
+        {
+            const char *value;
+
+            value = g_getenv (envp[i]);
+            g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i],
+                                                   value ? value : ""));
+        }
+        g_strfreev (envp);
+    }
+
+    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)
+    {
+        display = gdk_screen_get_display (screen);
+        char *display_name = g_strdup (gdk_display_get_name (display));
+        char *display_env = g_strdup_printf ("DISPLAY=%s", display_name);
+        env = array_putenv (env, display_env);
+        g_free (display_name);
+        g_free (display_env);
+    }
+    else
+    {
+        display = gdk_display_get_default ();
+        screen = gdk_display_get_default_screen (display);
+    }
+    screen_num = gdk_x11_screen_get_screen_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);
+
+        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);
+        }
+
+        if (env != NULL)
+            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)
+        {
+            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
+                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_strfreev ((char **)env->pdata);
+        g_ptr_array_free (env, FALSE);
+    }
+    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;
+}
diff --git a/libegg/eggdesktopfile.h b/libegg/eggdesktopfile.h
new file mode 100644
index 00000000..4a1d4064
--- /dev/null
+++ b/libegg/eggdesktopfile.h
@@ -0,0 +1,166 @@
+/* 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., Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __EGG_DESKTOP_FILE_H__
+#define __EGG_DESKTOP_FILE_H__
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    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);
+
+    EggDesktopFileType  egg_desktop_file_get_desktop_file_type (EggDesktopFile  *desktop_file);
+
+    const char         *egg_desktop_file_get_name           (EggDesktopFile  *desktop_file);
+    const char         *egg_desktop_file_get_icon           (EggDesktopFile  *desktop_file);
+
+    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);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __EGG_DESKTOP_FILE_H__ */
diff --git a/libegg/eggsmclient-private.h b/libegg/eggsmclient-private.h
new file mode 100644
index 00000000..3eec5324
--- /dev/null
+++ b/libegg/eggsmclient-private.h
@@ -0,0 +1,47 @@
+/* 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __EGG_SM_CLIENT_PRIVATE_H__
+#define __EGG_SM_CLIENT_PRIVATE_H__
+
+#include <gtk/gtk.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
+#endif
+
+G_END_DECLS
+
+#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */
diff --git a/libegg/eggsmclient-xsmp.c b/libegg/eggsmclient-xsmp.c
new file mode 100644
index 00000000..808fffc0
--- /dev/null
+++ b/libegg/eggsmclient-xsmp.c
@@ -0,0 +1,1366 @@
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * Inspired by various other pieces of code including GsmClient (C)
+ * 2001 Havoc Pennington, MateClient (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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, 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 <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.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_IDLE,
+    XSMP_STATE_SAVE_YOURSELF,
+    XSMP_STATE_INTERACT_REQUEST,
+    XSMP_STATE_INTERACT,
+    XSMP_STATE_SAVE_YOURSELF_DONE,
+    XSMP_STATE_SHUTDOWN_CANCELLED,
+    XSMP_STATE_CONNECTION_CLOSED
+} EggSMClientXSMPState;
+
+static const char *state_names[] =
+{
+    "idle",
+    "save-yourself",
+    "interact-request",
+    "interact",
+    "save-yourself-done",
+    "shutdown-cancelled",
+    "connection-closed"
+};
+
+#define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state])
+
+struct _EggSMClientXSMP
+{
+    EggSMClient parent;
+
+    SmcConn connection;
+    char *client_id;
+
+    EggSMClientXSMPState state;
+    char **restart_command;
+    gboolean set_restart_command;
+    int restart_style;
+
+    guint idle;
+
+    /* Current SaveYourself state */
+    guint expecting_initial_save_yourself : 1;
+    guint need_save_state : 1;
+    guint need_quit_requested : 1;
+    guint interact_errors : 1;
+    guint shutting_down : 1;
+
+    /* Todo list */
+    guint waiting_to_set_initial_properties : 1;
+    guint waiting_to_emit_quit : 1;
+    guint waiting_to_emit_quit_cancelled : 1;
+    guint waiting_to_save_myself : 1;
+
+};
+
+struct _EggSMClientXSMPClass
+{
+    EggSMClientClass parent_class;
+
+};
+
+static void     sm_client_xsmp_startup (EggSMClient *client,
+                                        const char  *client_id);
+static void     sm_client_xsmp_set_restart_command (EggSMClient  *client,
+        int           argc,
+        const char  **argv);
+static void     sm_client_xsmp_will_quit (EggSMClient *client,
+        gboolean     will_quit);
+static gboolean sm_client_xsmp_end_session (EggSMClient         *client,
+        EggSMClientEndStyle  style,
+        gboolean  request_confirmation);
+
+static void xsmp_save_yourself      (SmcConn   smc_conn,
+                                     SmPointer client_data,
+                                     int       save_style,
+                                     Bool      shutdown,
+                                     int       interact_style,
+                                     Bool      fast);
+static void xsmp_die                (SmcConn   smc_conn,
+                                     SmPointer client_data);
+static void xsmp_save_complete      (SmcConn   smc_conn,
+                                     SmPointer client_data);
+static void xsmp_shutdown_cancelled (SmcConn   smc_conn,
+                                     SmPointer client_data);
+static void xsmp_interact           (SmcConn   smc_conn,
+                                     SmPointer client_data);
+
+static SmProp *array_prop        (const char    *name,
+                                  ...);
+static SmProp *ptrarray_prop     (const char    *name,
+                                  GPtrArray     *values);
+static SmProp *string_prop       (const char    *name,
+                                  const char    *value);
+static SmProp *card8_prop        (const char    *name,
+                                  unsigned char  value);
+
+static void set_properties         (EggSMClientXSMP *xsmp, ...);
+static void delete_properties      (EggSMClientXSMP *xsmp, ...);
+
+static GPtrArray *generate_command (char       **restart_command,
+                                    const char  *client_id,
+                                    const char  *state_file);
+
+static void save_state            (EggSMClientXSMP *xsmp);
+static void do_save_yourself      (EggSMClientXSMP *xsmp);
+static void update_pending_events (EggSMClientXSMP *xsmp);
+
+static void     ice_init             (void);
+static gboolean process_ice_messages (IceConn       ice_conn);
+static void     smc_error_handler    (SmcConn       smc_conn,
+                                      Bool          swap,
+                                      int           offending_minor_opcode,
+                                      unsigned long offending_sequence,
+                                      int           error_class,
+                                      int           severity,
+                                      SmPointer     values);
+
+G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT)
+
+static void
+egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp)
+{
+    xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
+    xsmp->connection = NULL;
+    xsmp->restart_style = SmRestartIfRunning;
+}
+
+static void
+egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass)
+{
+    EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
+
+    sm_client_class->startup             = sm_client_xsmp_startup;
+    sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command;
+    sm_client_class->will_quit           = sm_client_xsmp_will_quit;
+    sm_client_class->end_session         = sm_client_xsmp_end_session;
+}
+
+EggSMClient *
+egg_sm_client_xsmp_new (void)
+{
+    if (!g_getenv ("SESSION_MANAGER"))
+        return NULL;
+
+    return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL);
+}
+
+static gboolean
+sm_client_xsmp_set_initial_properties (gpointer user_data)
+{
+    EggSMClientXSMP *xsmp = user_data;
+    EggDesktopFile *desktop_file;
+    GPtrArray *clone, *restart;
+    char pid_str[64];
+
+    if (xsmp->idle)
+    {
+        g_source_remove (xsmp->idle);
+        xsmp->idle = 0;
+    }
+    xsmp->waiting_to_set_initial_properties = FALSE;
+
+    if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART)
+        xsmp->restart_style = SmRestartNever;
+
+    /* Parse info out of desktop file */
+    desktop_file = egg_get_desktop_file ();
+    if (desktop_file)
+    {
+        GError *err = NULL;
+        char *cmdline, **argv;
+        int argc;
+
+        if (xsmp->restart_style == SmRestartIfRunning)
+        {
+            if (egg_desktop_file_get_boolean (desktop_file,
+                                              "X-MATE-AutoRestart", NULL))
+                xsmp->restart_style = SmRestartImmediately;
+        }
+
+        if (!xsmp->set_restart_command)
+        {
+            cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err);
+            if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err))
+            {
+                egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp),
+                                                   argc, (const char **)argv);
+                g_strfreev (argv);
+            }
+            else
+            {
+                g_warning ("Could not parse Exec line in desktop file: %s",
+                           err->message);
+                g_error_free (err);
+            }
+            g_free (cmdline);
+        }
+    }
+
+    if (!xsmp->set_restart_command)
+        xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1);
+
+    clone = generate_command (xsmp->restart_command, NULL, NULL);
+    restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
+
+    g_debug ("Setting initial properties");
+
+    /* Program, CloneCommand, RestartCommand, and UserID are required.
+     * ProcessID isn't required, but the SM may be able to do something
+     * useful with it.
+     */
+    g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ());
+    set_properties (xsmp,
+                    string_prop   (SmProgram, g_get_prgname ()),
+                    ptrarray_prop (SmCloneCommand, clone),
+                    ptrarray_prop (SmRestartCommand, restart),
+                    string_prop   (SmUserID, g_get_user_name ()),
+                    string_prop   (SmProcessID, pid_str),
+                    card8_prop    (SmRestartStyleHint, xsmp->restart_style),
+                    NULL);
+    g_ptr_array_free (clone, TRUE);
+    g_ptr_array_free (restart, TRUE);
+
+    if (desktop_file)
+    {
+        set_properties (xsmp,
+                        string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)),
+                        NULL);
+    }
+
+    update_pending_events (xsmp);
+    return FALSE;
+}
+
+/* This gets called from two different places: xsmp_die() (when the
+ * server asks us to disconnect) and process_ice_messages() (when the
+ * server disconnects unexpectedly).
+ */
+static void
+sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
+{
+    SmcConn connection;
+
+    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;
+    SmcCallbacks callbacks;
+    char *ret_client_id;
+    char error_string_ret[256];
+
+    xsmp->client_id = g_strdup (client_id);
+
+    ice_init ();
+    SmcSetErrorHandler (smc_error_handler);
+
+    callbacks.save_yourself.callback      = xsmp_save_yourself;
+    callbacks.die.callback                = xsmp_die;
+    callbacks.save_complete.callback      = xsmp_save_complete;
+    callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
+
+    callbacks.save_yourself.client_data      = xsmp;
+    callbacks.die.client_data                = xsmp;
+    callbacks.save_complete.client_data      = xsmp;
+    callbacks.shutdown_cancelled.client_data = xsmp;
+
+    client_id = NULL;
+    error_string_ret[0] = '\0';
+    xsmp->connection =
+        SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
+                           SmcSaveYourselfProcMask | SmcDieProcMask |
+                           SmcSaveCompleteProcMask |
+                           SmcShutdownCancelledProcMask,
+                           &callbacks,
+                           xsmp->client_id, &ret_client_id,
+                           sizeof (error_string_ret), error_string_ret);
+
+    if (!xsmp->connection)
+    {
+        g_warning ("Failed to connect to the session manager: %s\n",
+                   error_string_ret[0] ?
+                   error_string_ret : "no error message given");
+        xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
+        return;
+    }
+
+    /* We expect a pointless initial SaveYourself if either (a) we
+     * didn't have an initial client ID, or (b) we DID have an initial
+     * client ID, but the server rejected it and gave us a new one.
+     */
+    if (!xsmp->client_id ||
+            (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0))
+        xsmp->expecting_initial_save_yourself = TRUE;
+
+    if (ret_client_id)
+    {
+        g_free (xsmp->client_id);
+        xsmp->client_id = g_strdup (ret_client_id);
+        free (ret_client_id);
+
+        gdk_x11_set_sm_client_id (xsmp->client_id);
+
+        g_debug ("Got client ID \"%s\"", xsmp->client_id);
+    }
+
+    xsmp->state = XSMP_STATE_IDLE;
+
+    /* Do not set the initial properties until we reach the main loop,
+     * so that the application has a chance to call
+     * egg_set_desktop_file(). (This may also help the session manager
+     * have a better idea of when the application is fully up and
+     * running.)
+     */
+    xsmp->waiting_to_set_initial_properties = TRUE;
+    xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client);
+}
+
+static void
+sm_client_xsmp_set_restart_command (EggSMClient  *client,
+                                    int           argc,
+                                    const char  **argv)
+{
+    EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+    int i;
+
+    g_strfreev (xsmp->restart_command);
+
+    xsmp->restart_command = g_new (char *, argc + 1);
+    for (i = 0; i < argc; i++)
+        xsmp->restart_command[i] = g_strdup (argv[i]);
+    xsmp->restart_command[i] = NULL;
+
+    xsmp->set_restart_command = TRUE;
+}
+
+static void
+sm_client_xsmp_will_quit (EggSMClient *client,
+                          gboolean     will_quit)
+{
+    EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+
+    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_CONNECTION_CLOSED:
+            return FALSE;
+
+        case XSMP_STATE_SAVE_YOURSELF:
+            /* Trying to log out from the save_state callback? Whatever.
+             * Abort the save_state.
+             */
+            SmcSaveYourselfDone (xsmp->connection, FALSE);
+            xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+            break;
+
+        case XSMP_STATE_INTERACT_REQUEST:
+        case XSMP_STATE_INTERACT:
+        case XSMP_STATE_SHUTDOWN_CANCELLED:
+            /* Already in a shutdown-related state, just ignore
+             * the new shutdown request...
+             */
+            return TRUE;
+
+        case XSMP_STATE_IDLE:
+            if (xsmp->waiting_to_set_initial_properties)
+                sm_client_xsmp_set_initial_properties (xsmp);
+
+            if (!xsmp->expecting_initial_save_yourself)
+                break;
+            /* else fall through */
+
+        case XSMP_STATE_SAVE_YOURSELF_DONE:
+            /* We need to wait for some response from the server.*/
+            process_ice_messages (SmcGetIceConnection (xsmp->connection));
+            break;
+
+        default:
+            /* Hm... shouldn't happen */
+            return FALSE;
+        }
+    }
+
+    /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and
+     * the user chooses to save the session. But mate-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;
+
+    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:
+    return FALSE;
+}
+
+static void
+update_pending_events (EggSMClientXSMP *xsmp)
+{
+    gboolean want_idle =
+        xsmp->waiting_to_emit_quit ||
+        xsmp->waiting_to_emit_quit_cancelled ||
+        xsmp->waiting_to_save_myself;
+
+    if (want_idle)
+    {
+        if (xsmp->idle == 0)
+            xsmp->idle = g_idle_add (idle_do_pending_events, xsmp);
+    }
+    else
+    {
+        if (xsmp->idle != 0)
+            g_source_remove (xsmp->idle);
+        xsmp->idle = 0;
+    }
+}
+
+static void
+fix_broken_state (EggSMClientXSMP *xsmp, const char *message,
+                  gboolean send_interact_done,
+                  gboolean send_save_yourself_done)
+{
+    g_warning ("Received XSMP %s message in state %s: client or server error",
+               message, EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+    /* Forget any pending SaveYourself plans we had */
+    xsmp->waiting_to_save_myself = FALSE;
+    update_pending_events (xsmp);
+
+    if (send_interact_done)
+        SmcInteractDone (xsmp->connection, False);
+    if (send_save_yourself_done)
+        SmcSaveYourselfDone (xsmp->connection, True);
+
+    xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE;
+}
+
+/* SM callbacks */
+
+static void
+xsmp_save_yourself (SmcConn   smc_conn,
+                    SmPointer client_data,
+                    int       save_type,
+                    Bool      shutdown,
+                    int       interact_style,
+                    Bool      fast)
+{
+    EggSMClientXSMP *xsmp = client_data;
+    gboolean wants_quit_requested;
+
+    g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
+             save_type == SmSaveLocal ? "SmSaveLocal" :
+             save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
+             shutdown ? "Shutdown" : "!Shutdown",
+             interact_style == SmInteractStyleAny ? "SmInteractStyleAny" :
+             interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
+             "SmInteractStyleNone", fast ? "Fast" : "!Fast",
+             EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+    if (xsmp->state != XSMP_STATE_IDLE &&
+            xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+        fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE);
+        return;
+    }
+
+    if (xsmp->waiting_to_set_initial_properties)
+        sm_client_xsmp_set_initial_properties (xsmp);
+
+    /* If this is the initial SaveYourself, ignore it; we've already set
+     * properties and there's no reason to actually save state too.
+     */
+    if (xsmp->expecting_initial_save_yourself)
+    {
+        xsmp->expecting_initial_save_yourself = FALSE;
+
+        if (save_type == SmSaveLocal &&
+                interact_style == SmInteractStyleNone &&
+                !shutdown && !fast)
+        {
+            g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself");
+            SmcSaveYourselfDone (xsmp->connection, True);
+            /* As explained in the comment at the end of
+             * do_save_yourself(), SAVE_YOURSELF_DONE is the correct
+             * state here, not IDLE.
+             */
+            xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+            return;
+        }
+        else
+            g_warning ("First SaveYourself was not the expected one!");
+    }
+
+    /* Even ignoring the "fast" flag completely, there are still 18
+     * different combinations of save_type, shutdown and interact_style.
+     * We interpret them as follows:
+     *
+     *   Type  Shutdown  Interact	 Interpretation
+     *     G      F       A/E/N  	 do nothing (1)
+     *     G      T         N    	 do nothing (1)*
+     *     G      T        A/E   	 quit_requested (2)
+     *    L/B     F       A/E/N  	 save_state (3)
+     *    L/B     T         N    	 save_state (3)*
+     *    L/B     T        A/E   	 quit_requested, then save_state (4)
+     *
+     *   1. Do nothing, because the SM asked us to do something
+     *      uninteresting (save open files, but then don't quit
+     *      afterward) or rude (save open files without asking the user
+     *      for confirmation).
+     *
+     *   2. Request interaction and then emit ::quit_requested. This
+     *      perhaps isn't quite correct for the SmInteractStyleErrors
+     *      case, but we don't care.
+     *
+     *   3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these
+     *      rows essentially get demoted to SmSaveLocal, because their
+     *      Global halves correspond to "do nothing".
+     *
+     *   4. Request interaction, emit ::quit_requested, and then emit
+     *      ::save_state after interacting. This is the SmSaveBoth
+     *      equivalent of #2, but we also promote SmSaveLocal shutdown
+     *      SaveYourselfs to SmSaveBoth here, because we want to give
+     *      the user a chance to save open files before quitting.
+     *
+     * (* It would be nice if we could do something useful when the
+     * session manager sends a SaveYourself with shutdown True and
+     * SmInteractStyleNone. But we can't, so we just pretend it didn't
+     * even tell us it was shutting down. The docs for ::quit mention
+     * that it might not always be preceded by ::quit_requested.)
+     */
+
+    /* As an optimization, we don't actually request interaction and
+     * emit ::quit_requested if the application isn't listening to the
+     * signal.
+     */
+    wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE);
+
+    xsmp->need_save_state     = (save_type != SmSaveGlobal);
+    xsmp->need_quit_requested = (shutdown && wants_quit_requested &&
+                                 interact_style != SmInteractStyleNone);
+    xsmp->interact_errors     = (interact_style == SmInteractStyleErrors);
+
+    xsmp->shutting_down       = shutdown;
+
+    do_save_yourself (xsmp);
+}
+
+static void
+do_save_yourself (EggSMClientXSMP *xsmp)
+{
+    if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+        /* The SM cancelled a previous SaveYourself, but we haven't yet
+         * had a chance to tell the application, so we can't start
+         * processing this SaveYourself yet.
+         */
+        xsmp->waiting_to_save_myself = TRUE;
+        update_pending_events (xsmp);
+        return;
+    }
+
+    if (xsmp->need_quit_requested)
+    {
+        xsmp->state = XSMP_STATE_INTERACT_REQUEST;
+
+        g_debug ("Sending InteractRequest(%s)",
+                 xsmp->interact_errors ? "Error" : "Normal");
+        SmcInteractRequest (xsmp->connection,
+                            xsmp->interact_errors ? SmDialogError : SmDialogNormal,
+                            xsmp_interact,
+                            xsmp);
+        return;
+    }
+
+    if (xsmp->need_save_state)
+    {
+        save_state (xsmp);
+
+        /* Though unlikely, the client could have been disconnected
+         * while the application was saving its state.
+         */
+        if (!xsmp->connection)
+            return;
+    }
+
+    g_debug ("Sending SaveYourselfDone(True)");
+    SmcSaveYourselfDone (xsmp->connection, True);
+
+    /* The client state diagram in the XSMP spec says that after a
+     * non-shutdown SaveYourself, we go directly back to "idle". But
+     * everything else in both the XSMP spec and the libSM docs
+     * disagrees.
+     */
+    xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+}
+
+static void
+save_state (EggSMClientXSMP *xsmp)
+{
+    GKeyFile *state_file;
+    char *state_file_path, *data;
+    EggDesktopFile *desktop_file;
+    GPtrArray *restart;
+    int offset, fd;
+
+    /* We set xsmp->state before emitting save_state, but our caller is
+     * responsible for setting it back afterward.
+     */
+    xsmp->state = XSMP_STATE_SAVE_YOURSELF;
+
+    state_file = egg_sm_client_save_state ((EggSMClient *)xsmp);
+    if (!state_file)
+    {
+        restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
+        set_properties (xsmp,
+                        ptrarray_prop (SmRestartCommand, restart),
+                        NULL);
+        g_ptr_array_free (restart, TRUE);
+        delete_properties (xsmp, SmDiscardCommand, NULL);
+        return;
+    }
+
+    desktop_file = egg_get_desktop_file ();
+    if (desktop_file)
+    {
+        GKeyFile *merged_file;
+        char *desktop_file_path;
+
+        merged_file = g_key_file_new ();
+        desktop_file_path =
+            g_filename_from_uri (egg_desktop_file_get_source (desktop_file),
+                                 NULL, NULL);
+        if (desktop_file_path &&
+                g_key_file_load_from_file (merged_file, desktop_file_path,
+                                           G_KEY_FILE_KEEP_COMMENTS |
+                                           G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
+        {
+            guint g, k, i;
+            char **groups, **keys, *value, *exec;
+
+            groups = g_key_file_get_groups (state_file, NULL);
+            for (g = 0; groups[g]; g++)
+            {
+                keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL);
+                for (k = 0; keys[k]; k++)
+                {
+                    value = g_key_file_get_value (state_file, groups[g],
+                                                  keys[k], NULL);
+                    if (value)
+                    {
+                        g_key_file_set_value (merged_file, groups[g],
+                                              keys[k], value);
+                        g_free (value);
+                    }
+                }
+                g_strfreev (keys);
+            }
+            g_strfreev (groups);
+
+            g_key_file_free (state_file);
+            state_file = merged_file;
+
+            /* Update Exec key using "--sm-client-state-file %k" */
+            restart = generate_command (xsmp->restart_command,
+                                        NULL, "%k");
+            for (i = 0; i < restart->len; i++)
+                restart->pdata[i] = g_shell_quote (restart->pdata[i]);
+            g_ptr_array_add (restart, NULL);
+            exec = g_strjoinv (" ", (char **)restart->pdata);
+            g_strfreev ((char **)restart->pdata);
+            g_ptr_array_free (restart, FALSE);
+
+            g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
+                                   EGG_DESKTOP_FILE_KEY_EXEC,
+                                   exec);
+            g_free (exec);
+        }
+        else
+            desktop_file = NULL;
+
+        g_free (desktop_file_path);
+    }
+
+    /* Now write state_file to disk. (We can't use mktemp(), because
+     * that requires the filename to end with "XXXXXX", and we want
+     * it to end with ".desktop".)
+     */
+
+    data = g_key_file_to_data (state_file, NULL, NULL);
+    g_key_file_free (state_file);
+
+    offset = 0;
+    while (1)
+    {
+        state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s",
+                                           g_get_user_config_dir (),
+                                           G_DIR_SEPARATOR, G_DIR_SEPARATOR,
+                                           g_get_prgname (),
+                                           (long)time (NULL) + offset,
+                                           desktop_file ? "desktop" : "state");
+
+        fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644);
+        if (fd == -1)
+        {
+            if (errno == EEXIST)
+            {
+                offset++;
+                g_free (state_file_path);
+                continue;
+            }
+            else if (errno == ENOTDIR || errno == ENOENT)
+            {
+                char *sep = strrchr (state_file_path, G_DIR_SEPARATOR);
+
+                *sep = '\0';
+                if (g_mkdir_with_parents (state_file_path, 0755) != 0)
+                {
+                    g_warning ("Could not create directory '%s'",
+                               state_file_path);
+                    g_free (state_file_path);
+                    state_file_path = NULL;
+                    break;
+                }
+
+                continue;
+            }
+
+            g_warning ("Could not create file '%s': %s",
+                       state_file_path, g_strerror (errno));
+            g_free (state_file_path);
+            state_file_path = NULL;
+            break;
+        }
+
+        close (fd);
+        g_file_set_contents (state_file_path, data, -1, NULL);
+        break;
+    }
+    g_free (data);
+
+    restart = generate_command (xsmp->restart_command, xsmp->client_id,
+                                state_file_path);
+    set_properties (xsmp,
+                    ptrarray_prop (SmRestartCommand, restart),
+                    NULL);
+    g_ptr_array_free (restart, TRUE);
+
+    if (state_file_path)
+    {
+        set_properties (xsmp,
+                        array_prop (SmDiscardCommand,
+                                    "/bin/rm", "-rf", state_file_path,
+                                    NULL),
+                        NULL);
+        g_free (state_file_path);
+    }
+}
+
+static void
+xsmp_interact (SmcConn   smc_conn,
+               SmPointer client_data)
+{
+    EggSMClientXSMP *xsmp = client_data;
+    EggSMClient *client = client_data;
+
+    g_debug ("Received Interact message in state %s",
+             EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+    if (xsmp->state != XSMP_STATE_INTERACT_REQUEST)
+    {
+        fix_broken_state (xsmp, "Interact", TRUE, TRUE);
+        return;
+    }
+
+    xsmp->state = XSMP_STATE_INTERACT;
+    egg_sm_client_quit_requested (client);
+}
+
+static void
+xsmp_die (SmcConn   smc_conn,
+          SmPointer client_data)
+{
+    EggSMClientXSMP *xsmp = client_data;
+    EggSMClient *client = client_data;
+
+    g_debug ("Received Die message in state %s",
+             EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+    sm_client_xsmp_disconnect (xsmp);
+    egg_sm_client_quit (client);
+}
+
+static void
+xsmp_save_complete (SmcConn   smc_conn,
+                    SmPointer client_data)
+{
+    EggSMClientXSMP *xsmp = client_data;
+
+    g_debug ("Received SaveComplete message in state %s",
+             EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+    if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
+        xsmp->state = XSMP_STATE_IDLE;
+    else
+        fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE);
+}
+
+static void
+xsmp_shutdown_cancelled (SmcConn   smc_conn,
+                         SmPointer client_data)
+{
+    EggSMClientXSMP *xsmp = client_data;
+    EggSMClient *client = client_data;
+
+    g_debug ("Received ShutdownCancelled message in state %s",
+             EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+    xsmp->shutting_down = FALSE;
+
+    if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
+    {
+        /* We've finished interacting and now the SM has agreed to
+         * cancel the shutdown.
+         */
+        xsmp->state = XSMP_STATE_IDLE;
+        egg_sm_client_quit_cancelled (client);
+    }
+    else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+        /* Hm... ok, so we got a shutdown SaveYourself, which got
+         * cancelled, but the application was still interacting, so we
+         * didn't tell it yet, and then *another* SaveYourself arrived,
+         * which we must still be waiting to tell the app about, except
+         * that now that SaveYourself has been cancelled too! Dizzy yet?
+         */
+        xsmp->waiting_to_save_myself = FALSE;
+        update_pending_events (xsmp);
+    }
+    else
+    {
+        g_debug ("Sending SaveYourselfDone(False)");
+        SmcSaveYourselfDone (xsmp->connection, False);
+
+        if (xsmp->state == XSMP_STATE_INTERACT)
+        {
+            /* The application is currently interacting, so we can't
+             * tell it about the cancellation yet; we will wait until
+             * after it calls egg_sm_client_will_quit().
+             */
+            xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED;
+        }
+        else
+        {
+            /* The shutdown was cancelled before the application got a
+             * chance to interact.
+             */
+            xsmp->state = XSMP_STATE_IDLE;
+        }
+    }
+}
+
+/* Utilities */
+
+/* Create a restart/clone/Exec command based on @restart_command.
+ * If @client_id is non-%NULL, add "--sm-client-id @client_id".
+ * If @state_file is non-%NULL, add "--sm-client-state-file @state_file".
+ *
+ * None of the input strings are g_strdup()ed; the caller must keep
+ * them around until it is done with the returned GPtrArray, and must
+ * then free the array, but not its contents.
+ */
+static GPtrArray *
+generate_command (char **restart_command, const char *client_id,
+                  const char *state_file)
+{
+    GPtrArray *cmd;
+    int i;
+
+    cmd = g_ptr_array_new ();
+    g_ptr_array_add (cmd, restart_command[0]);
+
+    if (client_id)
+    {
+        g_ptr_array_add (cmd, "--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;
+    guint i;
+
+    props = g_ptr_array_new ();
+
+    va_start (ap, xsmp);
+    while ((prop = va_arg (ap, SmProp *)))
+        g_ptr_array_add (props, prop);
+    va_end (ap);
+
+    if (xsmp->connection)
+    {
+        SmcSetProperties (xsmp->connection, props->len,
+                          (SmProp **)props->pdata);
+    }
+
+    for (i = 0; i < props->len; i++)
+    {
+        prop = props->pdata[i];
+        g_free (prop->vals);
+        g_free (prop);
+    }
+    g_ptr_array_free (props, TRUE);
+}
+
+/* Takes a NULL-terminated list of property names and deletes them. */
+static void
+delete_properties (EggSMClientXSMP *xsmp, ...)
+{
+    GPtrArray *props;
+    char *prop;
+    va_list ap;
+
+    if (!xsmp->connection)
+        return;
+
+    props = g_ptr_array_new ();
+
+    va_start (ap, xsmp);
+    while ((prop = va_arg (ap, char *)))
+        g_ptr_array_add (props, prop);
+    va_end (ap);
+
+    SmcDeleteProperties (xsmp->connection, props->len,
+                         (char **)props->pdata);
+
+    g_ptr_array_free (props, TRUE);
+}
+
+/* Takes an array of strings and creates a LISTofARRAY8 property. The
+ * strings are neither dupped nor freed; they need to remain valid
+ * until you're done with the SmProp.
+ */
+static SmProp *
+array_prop (const char *name, ...)
+{
+    SmProp *prop;
+    SmPropValue pv;
+    GArray *vals;
+    char *value;
+    va_list ap;
+
+    prop = g_new (SmProp, 1);
+    prop->name = (char *)name;
+    prop->type = 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);
+    }
+    va_end (ap);
+
+    prop->num_vals = vals->len;
+    prop->vals = (SmPropValue *)vals->data;
+
+    g_array_free (vals, FALSE);
+
+    return prop;
+}
+
+/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property.
+ * The array contents are neither dupped nor freed; they need to
+ * remain valid until you're done with the SmProp.
+ */
+static SmProp *
+ptrarray_prop (const char *name, GPtrArray *values)
+{
+    SmProp *prop;
+    SmPropValue pv;
+    GArray *vals;
+    guint i;
+
+    prop = g_new (SmProp, 1);
+    prop->name = (char *)name;
+    prop->type = 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 MateClient 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;
+    status = IceProcessMessages (ice_conn, NULL, NULL);
+
+    switch (status)
+    {
+    case IceProcessMessagesSuccess:
+        return TRUE;
+
+    case IceProcessMessagesIOError:
+        sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
+        return FALSE;
+
+    case IceProcessMessagesConnectionClosed:
+        return FALSE;
+
+    default:
+        g_assert_not_reached ();
+    }
+}
+
+static gboolean
+ice_iochannel_watch (GIOChannel   *channel,
+                     GIOCondition  condition,
+                     gpointer      client_data)
+{
+    return process_ice_messages (client_data);
+}
+
+static void
+ice_connection_watch (IceConn     ice_conn,
+                      IcePointer  client_data,
+                      Bool        opening,
+                      IcePointer *watch_data)
+{
+    guint watch_id;
+
+    if (opening)
+    {
+        GIOChannel *channel;
+        int fd = IceConnectionNumber (ice_conn);
+
+        fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
+        channel = g_io_channel_unix_new (fd);
+        watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR,
+                                   ice_iochannel_watch, ice_conn);
+        g_io_channel_unref (channel);
+
+        *watch_data = GUINT_TO_POINTER (watch_id);
+    }
+    else
+    {
+        watch_id = GPOINTER_TO_UINT (*watch_data);
+        g_source_remove (watch_id);
+    }
+}
+
+static void
+ice_error_handler (IceConn       ice_conn,
+                   Bool          swap,
+                   int           offending_minor_opcode,
+                   unsigned long offending_sequence,
+                   int           error_class,
+                   int           severity,
+                   IcePointer    values)
+{
+    /* Do nothing */
+}
+
+static void
+ice_io_error_handler (IceConn ice_conn)
+{
+    /* Do nothing */
+}
+
+static void
+smc_error_handler (SmcConn       smc_conn,
+                   Bool          swap,
+                   int           offending_minor_opcode,
+                   unsigned long offending_sequence,
+                   int           error_class,
+                   int           severity,
+                   SmPointer     values)
+{
+    /* Do nothing */
+}
diff --git a/libegg/eggsmclient.c b/libegg/eggsmclient.c
new file mode 100644
index 00000000..4886f147
--- /dev/null
+++ b/libegg/eggsmclient.c
@@ -0,0 +1,612 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, 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 gboolean
+running_in_mate (void)
+{
+    return (g_strcmp0 (g_getenv ("XDG_CURRENT_DESKTOP"), "MATE") == 0)
+        || (g_strcmp0 (g_getenv ("XDG_SESSION_DESKTOP"), "MATE") == 0)
+        || (g_strcmp0 (g_getenv ("DESKTOP_SESSION"), "MATE") == 0);
+}
+
+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 char *sm_config_prefix = 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)
+{
+    const GOptionEntry entries[] =
+    {
+        {
+            "sm-client-disable", 0, 0,
+            G_OPTION_ARG_NONE, &sm_client_disable,
+            N_("Disable connection to session manager"), NULL
+        },
+        {
+            "sm-client-state-file", 0, 0,
+            G_OPTION_ARG_FILENAME, &sm_client_state_file,
+            N_("Specify file containing saved configuration"), N_("FILE")
+        },
+        {
+            "sm-client-id", 0, 0,
+            G_OPTION_ARG_STRING, &sm_client_id,
+            N_("Specify session management ID"), N_("ID")
+        },
+        /* MateClient compatibility option */
+        {
+            "sm-disable", 0, G_OPTION_FLAG_HIDDEN,
+            G_OPTION_ARG_NONE, &sm_client_disable,
+            NULL, NULL
+        },
+        /* MateClient compatibility option. This is a dummy option that only
+         * exists so that sessions saved by apps with MateClient can be restored
+         * later when they've switched to EggSMClient. See bug #575308.
+         */
+        {
+            "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN,
+            G_OPTION_ARG_STRING, &sm_config_prefix,
+            NULL, NULL
+        },
+        { NULL }
+    };
+    GOptionGroup *group;
+
+    /* Use our own debug handler for the "EggSMClient" domain. */
+    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 both D-Bus and XSMP are compiled in, try XSMP first
+             * (since it supports state saving) and fall back to D-Bus
+             * if XSMP isn't available.
+             */
+#ifdef EGG_SM_CLIENT_BACKEND_XSMP
+            global_client = egg_sm_client_xsmp_new ();
+#endif
+#ifdef EGG_SM_CLIENT_BACKEND_DBUS
+            if (!global_client)
+                global_client = egg_sm_client_dbus_new ();
+#endif
+        }
+
+        /* 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);
+        /*FIXME
+          Disabling when root/not in MATE in GtkApplication builds
+          as egg_sm_client_set_mode must be called prior to start of main loop
+          to stop caja restart but this is diffcult in GtkApplication */
+
+		if (geteuid () == 0 || !running_in_mate ()){
+        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);
+}
diff --git a/libegg/eggsmclient.h b/libegg/eggsmclient.h
new file mode 100644
index 00000000..1911b89e
--- /dev/null
+++ b/libegg/eggsmclient.h
@@ -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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, 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__ */
diff --git a/libegg/eggtreemultidnd.c b/libegg/eggtreemultidnd.c
new file mode 100644
index 00000000..062bb57e
--- /dev/null
+++ b/libegg/eggtreemultidnd.c
@@ -0,0 +1,417 @@
+/* eggtreemultidnd.c
+ * Copyright (C) 2001  Red Hat, 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include "eggtreemultidnd.h"
+
+#define EGG_TREE_MULTI_DND_STRING "EggTreeMultiDndString"
+
+typedef struct
+{
+    guint pressed_button;
+    gint x;
+    gint y;
+    guint motion_notify_handler;
+    guint button_release_handler;
+    guint drag_data_get_handler;
+    GSList *event_list;
+} EggTreeMultiDndData;
+
+/* CUT-N-PASTE from gtktreeview.c */
+typedef struct _TreeViewDragInfo TreeViewDragInfo;
+struct _TreeViewDragInfo
+{
+    GdkModifierType start_button_mask;
+    GtkTargetList *source_target_list;
+    GdkDragAction source_actions;
+
+    GtkTargetList *dest_target_list;
+
+    guint source_set : 1;
+    guint dest_set : 1;
+};
+
+
+GType
+egg_tree_multi_drag_source_get_type (void)
+{
+    static GType our_type = 0;
+
+    if (!our_type)
+    {
+        const GTypeInfo our_info =
+        {
+            sizeof (EggTreeMultiDragSourceIface), /* class_size */
+            NULL,		/* base_init */
+            NULL,		/* base_finalize */
+            NULL,
+            NULL,		/* class_finalize */
+            NULL,		/* class_data */
+            0,
+            0,              /* n_preallocs */
+            NULL
+        };
+
+        our_type = g_type_register_static (G_TYPE_INTERFACE, "EggTreeMultiDragSource", &our_info, 0);
+    }
+
+    return our_type;
+}
+
+
+/**
+ * egg_tree_multi_drag_source_row_draggable:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row on which user is initiating a drag
+ *
+ * Asks the #EggTreeMultiDragSource whether a particular row can be used as
+ * the source of a DND operation. If the source doesn't implement
+ * this interface, the row is assumed draggable.
+ *
+ * Return value: %TRUE if the row can be dragged
+ **/
+gboolean
+egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source,
+        GList                  *path_list)
+{
+    EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+    g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+    g_return_val_if_fail (iface->row_draggable != NULL, FALSE);
+    g_return_val_if_fail (path_list != NULL, FALSE);
+
+    if (iface->row_draggable)
+        return (* iface->row_draggable) (drag_source, path_list);
+    else
+        return TRUE;
+}
+
+
+/**
+ * egg_tree_multi_drag_source_drag_data_delete:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row that was being dragged
+ *
+ * Asks the #EggTreeMultiDragSource to delete the row at @path, because
+ * it was moved somewhere else via drag-and-drop. Returns %FALSE
+ * if the deletion fails because @path no longer exists, or for
+ * some model-specific reason. Should robustly handle a @path no
+ * longer found in the model!
+ *
+ * Return value: %TRUE if the row was successfully deleted
+ **/
+gboolean
+egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
+        GList                  *path_list)
+{
+    EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+    g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+    g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE);
+    g_return_val_if_fail (path_list != NULL, FALSE);
+
+    return (* iface->drag_data_delete) (drag_source, path_list);
+}
+
+/**
+ * egg_tree_multi_drag_source_drag_data_get:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row that was dragged
+ * @selection_data: a #EggSelectionData to fill with data from the dragged row
+ *
+ * Asks the #EggTreeMultiDragSource to fill in @selection_data with a
+ * representation of the row at @path. @selection_data->target gives
+ * the required type of the data.  Should robustly handle a @path no
+ * longer found in the model!
+ *
+ * Return value: %TRUE if data of the required type was provided
+ **/
+gboolean
+egg_tree_multi_drag_source_drag_data_get    (EggTreeMultiDragSource *drag_source,
+        GList                  *path_list,
+        GtkSelectionData  *selection_data)
+{
+    EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+    g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+    g_return_val_if_fail (iface->drag_data_get != NULL, FALSE);
+    g_return_val_if_fail (path_list != NULL, FALSE);
+    g_return_val_if_fail (selection_data != NULL, FALSE);
+
+    return (* iface->drag_data_get) (drag_source, path_list, selection_data);
+}
+
+static void
+stop_drag_check (GtkWidget *widget)
+{
+    EggTreeMultiDndData *priv_data;
+    GSList *l;
+
+    priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+    for (l = priv_data->event_list; l != NULL; l = l->next)
+        gdk_event_free (l->data);
+
+    g_slist_free (priv_data->event_list);
+    priv_data->event_list = NULL;
+    g_signal_handler_disconnect (widget, priv_data->motion_notify_handler);
+    g_signal_handler_disconnect (widget, priv_data->button_release_handler);
+}
+
+static gboolean
+egg_tree_multi_drag_button_release_event (GtkWidget      *widget,
+        GdkEventButton *event,
+        gpointer        data)
+{
+    EggTreeMultiDndData *priv_data;
+    GSList *l;
+
+    priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+    for (l = priv_data->event_list; l != NULL; l = l->next)
+        gtk_propagate_event (widget, l->data);
+
+    stop_drag_check (widget);
+
+    return FALSE;
+}
+
+static void
+selection_foreach (GtkTreeModel *model,
+                   GtkTreePath  *path,
+                   GtkTreeIter  *iter,
+                   gpointer      data)
+{
+    GList **list_ptr;
+
+    list_ptr = (GList **) data;
+
+    *list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path));
+}
+
+static void
+path_list_free (GList *path_list)
+{
+    g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL);
+    g_list_free (path_list);
+}
+
+static void
+set_context_data (GdkDragContext *context,
+                  GList          *path_list)
+{
+    g_object_set_data_full (G_OBJECT (context),
+                            "egg-tree-view-multi-source-row",
+                            path_list,
+                            (GDestroyNotify) path_list_free);
+}
+
+static GList *
+get_context_data (GdkDragContext *context)
+{
+    return g_object_get_data (G_OBJECT (context),
+                              "egg-tree-view-multi-source-row");
+}
+
+/* CUT-N-PASTE from gtktreeview.c */
+static TreeViewDragInfo*
+get_info (GtkTreeView *tree_view)
+{
+    return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
+}
+
+
+static void
+egg_tree_multi_drag_drag_data_get (GtkWidget        *widget,
+                                   GdkDragContext   *context,
+                                   GtkSelectionData *selection_data,
+                                   guint             info,
+                                   guint             time)
+{
+    GtkTreeView *tree_view;
+    GtkTreeModel *model;
+    TreeViewDragInfo *di;
+    GList *path_list;
+
+    tree_view = GTK_TREE_VIEW (widget);
+
+    model = gtk_tree_view_get_model (tree_view);
+
+    if (model == NULL)
+        return;
+
+    di = get_info (GTK_TREE_VIEW (widget));
+
+    if (di == NULL)
+        return;
+
+    path_list = get_context_data (context);
+
+    if (path_list == NULL)
+        return;
+
+    /* We can implement the GTK_TREE_MODEL_ROW target generically for
+     * any model; for DragSource models there are some other targets
+     * we also support.
+     */
+
+    if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model))
+    {
+        egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model),
+                path_list,
+                selection_data);
+    }
+}
+
+static gboolean
+egg_tree_multi_drag_motion_event (GtkWidget      *widget,
+                                  GdkEventMotion *event,
+                                  gpointer        data)
+{
+    EggTreeMultiDndData *priv_data;
+
+    priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+    if (gtk_drag_check_threshold (widget,
+                                  priv_data->x,
+                                  priv_data->y,
+                                  event->x,
+                                  event->y))
+    {
+        GList *path_list = NULL;
+        GtkTreeSelection *selection;
+        GtkTreeModel *model;
+        GdkDragContext *context;
+        TreeViewDragInfo *di;
+
+        di = get_info (GTK_TREE_VIEW (widget));
+
+        if (di == NULL)
+            return FALSE;
+
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+        stop_drag_check (widget);
+        gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list);
+        path_list = g_list_reverse (path_list);
+        model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+        if (egg_tree_multi_drag_source_row_draggable (EGG_TREE_MULTI_DRAG_SOURCE (model), path_list))
+        {
+
+            context = gtk_drag_begin_with_coordinates (widget,
+                                                       gtk_drag_source_get_target_list (widget),
+                                                       di->source_actions,
+                                                       priv_data->pressed_button,
+                                                       (GdkEvent*)event,
+                                                       event->x,
+                                                       event->y);
+            set_context_data (context, path_list);
+            gtk_drag_set_icon_default (context);
+
+        }
+        else
+        {
+            path_list_free (path_list);
+        }
+    }
+
+    return TRUE;
+}
+
+static gboolean
+egg_tree_multi_drag_button_press_event (GtkWidget      *widget,
+                                        GdkEventButton *event,
+                                        gpointer        data)
+{
+    GtkTreeView *tree_view;
+    GtkTreePath *path = NULL;
+    GtkTreeViewColumn *column = NULL;
+    gint cell_x, cell_y;
+    GtkTreeSelection *selection;
+    EggTreeMultiDndData *priv_data;
+
+    tree_view = GTK_TREE_VIEW (widget);
+    priv_data = g_object_get_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING);
+    if (priv_data == NULL)
+    {
+        priv_data = g_new0 (EggTreeMultiDndData, 1);
+        g_object_set_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING, priv_data);
+    }
+
+    if (g_slist_find (priv_data->event_list, event))
+        return FALSE;
+
+    if (priv_data->event_list)
+    {
+        /* save the event to be propagated in order */
+        priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
+        return TRUE;
+    }
+
+    if (event->type == GDK_2BUTTON_PRESS)
+        return FALSE;
+
+    gtk_tree_view_get_path_at_pos (tree_view,
+                                   event->x, event->y,
+                                   &path, &column,
+                                   &cell_x, &cell_y);
+
+    selection = gtk_tree_view_get_selection (tree_view);
+
+    if (path && gtk_tree_selection_path_is_selected (selection, path))
+    {
+        priv_data->pressed_button = event->button;
+        priv_data->x = event->x;
+        priv_data->y = event->y;
+        priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
+        priv_data->motion_notify_handler =
+            g_signal_connect (G_OBJECT (tree_view), "motion_notify_event", G_CALLBACK (egg_tree_multi_drag_motion_event), NULL);
+        priv_data->button_release_handler =
+            g_signal_connect (G_OBJECT (tree_view), "button_release_event", G_CALLBACK (egg_tree_multi_drag_button_release_event), NULL);
+
+        if (priv_data->drag_data_get_handler == 0)
+        {
+            priv_data->drag_data_get_handler =
+                g_signal_connect (G_OBJECT (tree_view), "drag_data_get", G_CALLBACK (egg_tree_multi_drag_drag_data_get), NULL);
+        }
+
+        gtk_tree_path_free (path);
+
+        return TRUE;
+    }
+
+    if (path)
+    {
+        gtk_tree_path_free (path);
+    }
+
+    return FALSE;
+}
+
+void
+egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view)
+{
+    g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+    g_signal_connect (G_OBJECT (tree_view), "button_press_event", G_CALLBACK (egg_tree_multi_drag_button_press_event), NULL);
+}
+
diff --git a/libegg/eggtreemultidnd.h b/libegg/eggtreemultidnd.h
new file mode 100644
index 00000000..0510e517
--- /dev/null
+++ b/libegg/eggtreemultidnd.h
@@ -0,0 +1,78 @@
+/* eggtreednd.h
+ * Copyright (C) 2001  Red Hat, 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __EGG_TREE_MULTI_DND_H__
+#define __EGG_TREE_MULTI_DND_H__
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EGG_TYPE_TREE_MULTI_DRAG_SOURCE            (egg_tree_multi_drag_source_get_type ())
+#define EGG_TREE_MULTI_DRAG_SOURCE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSource))
+#define EGG_IS_TREE_MULTI_DRAG_SOURCE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE))
+#define EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSourceIface))
+
+    typedef struct _EggTreeMultiDragSource      EggTreeMultiDragSource; /* Dummy typedef */
+    typedef struct _EggTreeMultiDragSourceIface EggTreeMultiDragSourceIface;
+
+    struct _EggTreeMultiDragSourceIface
+    {
+        GTypeInterface g_iface;
+
+        /* VTable - not signals */
+        gboolean     (* row_draggable)        (EggTreeMultiDragSource   *drag_source,
+                                               GList                    *path_list);
+
+        gboolean     (* drag_data_get)        (EggTreeMultiDragSource   *drag_source,
+                                               GList                    *path_list,
+                                               GtkSelectionData         *selection_data);
+
+        gboolean     (* drag_data_delete)     (EggTreeMultiDragSource *drag_source,
+                                               GList                  *path_list);
+    };
+
+    GType    egg_tree_multi_drag_source_get_type         (void) G_GNUC_CONST;
+
+    /* Returns whether the given row can be dragged */
+    gboolean egg_tree_multi_drag_source_row_draggable    (EggTreeMultiDragSource *drag_source,
+            GList                  *path_list);
+
+    /* Deletes the given row, or returns FALSE if it can't */
+    gboolean egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
+            GList                  *path_list);
+
+
+    /* Fills in selection_data with type selection_data->target based on the row
+     * denoted by path, returns TRUE if it does anything
+     */
+    gboolean egg_tree_multi_drag_source_drag_data_get    (EggTreeMultiDragSource *drag_source,
+            GList                  *path_list,
+            GtkSelectionData       *selection_data);
+    void     egg_tree_multi_drag_add_drag_support        (GtkTreeView            *tree_view);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __EGG_TREE_MULTI_DND_H__ */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 56a1420c..e29c8e7f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,10 +1,15 @@
 # List of source files containing translatable strings.
 # Please keep this file sorted alphabetically.
 [encoding: UTF-8]
-cut-n-paste-code/libegg/eggdesktopfile.c
-cut-n-paste-code/libegg/eggsmclient.c
 data/browser.xml
 data/caja.appdata.xml.in
+data/caja-autorun-software.desktop.in.in
+data/caja-browser.desktop.in.in
+data/caja-computer.desktop.in.in
+data/caja.desktop.in.in
+data/caja-file-management-properties.desktop.in.in
+data/caja-folder-handler.desktop.in.in
+data/caja-home.desktop.in.in
 data/caja.xml.in
 eel/eel-canvas.c
 eel/eel-editable-label.c
@@ -47,13 +52,8 @@ libcaja-private/caja-undostack-manager.c
 libcaja-private/caja-vfs-file.c
 [type: gettext/gsettings]libcaja-private/org.mate.caja.gschema.xml
 [type: gettext/gsettings]libcaja-private/org.mate.media-handling.gschema.xml
-data/caja-autorun-software.desktop.in.in
-data/caja-browser.desktop.in.in
-data/caja-computer.desktop.in.in
-data/caja-file-management-properties.desktop.in.in
-data/caja-folder-handler.desktop.in.in
-data/caja-home.desktop.in.in
-data/caja.desktop.in.in
+libegg/eggdesktopfile.c
+libegg/eggsmclient.c
 src/file-manager/fm-desktop-icon-view.c
 src/file-manager/fm-directory-view.c
 src/file-manager/fm-ditem-page.c
diff --git a/src/Makefile.am b/src/Makefile.am
index daeaa38b..f4e50024 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,7 +11,6 @@ bin_PROGRAMS = \
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir) \
-	-I$(top_srcdir)/cut-n-paste-code \
 	-I$(top_builddir)/libcaja-private \
 	$(CORE_CFLAGS) \
 	$(WARNING_CFLAGS) \
diff --git a/src/file-manager/Makefile.am b/src/file-manager/Makefile.am
index 1f46cfc2..9e5f5578 100644
--- a/src/file-manager/Makefile.am
+++ b/src/file-manager/Makefile.am
@@ -4,7 +4,6 @@ noinst_LTLIBRARIES=libcaja-file-manager.la
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir) \
-	-I$(top_srcdir)/cut-n-paste-code \
 	$(CORE_CFLAGS) \
 	$(WARNING_CFLAGS) \
 	-DCAJA_DATADIR=\""$(datadir)/caja"\" \
-- 
cgit v1.2.1