[gnome-games] aisleriot: Decouple libgames-support from aisleriot



commit 6312163cdcb73c2f9b512606f8dd2ea018628f4b
Author: Christian Persch <chpe gnome org>
Date:   Sat Apr 2 21:42:17 2011 +0200

    aisleriot: Decouple libgames-support from aisleriot
    
    Copy the bits that aisleriot uses from libgames-support to
    aisleriot/lib.

 aisleriot/Makefile.am                              |   23 +-
 aisleriot/ar-clock.c                               |  261 ++++
 aisleriot/ar-clock.h                               |   52 +
 aisleriot/ar-fullscreen-button.c                   |    8 +-
 aisleriot/ar-game-chooser.c                        |   12 +-
 aisleriot/ar-stock.c                               |  450 ++++++
 aisleriot/ar-stock.h                               |   62 +
 aisleriot/ar-style-gtk.c                           |   16 +-
 aisleriot/ar-style.c                               |    6 +-
 aisleriot/baize.c                                  |    4 +-
 aisleriot/board-noclutter.c                        |   22 +-
 aisleriot/board.c                                  |   60 +-
 aisleriot/card.c                                   |    2 +-
 aisleriot/conf.c                                   |   28 +-
 aisleriot/conf.h                                   |    2 +-
 aisleriot/game.c                                   |   16 +-
 aisleriot/lib/Makefile.am                          |   96 ++-
 aisleriot/lib/ar-card-images.c                     |    8 +-
 aisleriot/lib/ar-card-surface-cache.c              |    8 +-
 aisleriot/lib/ar-card-textures-cache.c             |    8 +-
 aisleriot/lib/ar-card-theme-fixed.c                |   28 +-
 aisleriot/lib/ar-card-theme-kde.c                  |   40 +-
 aisleriot/lib/ar-card-theme-preimage.c             |   24 +-
 aisleriot/lib/ar-card-theme-private.h              |    6 +-
 aisleriot/lib/ar-card-theme-pysol.c                |    8 +-
 aisleriot/lib/ar-card-theme-sliced.c               |   28 +-
 aisleriot/lib/ar-card-theme-svg.c                  |   32 +-
 aisleriot/lib/ar-card-theme.c                      |   16 +-
 aisleriot/lib/ar-card-themes.c                     |   50 +-
 aisleriot/lib/ar-card.h                            |    2 +-
 aisleriot/lib/ar-conf.c                            | 1340 +++++++++++++++++
 aisleriot/lib/ar-conf.h                            |  114 ++
 aisleriot/lib/ar-debug.c                           |   60 +
 aisleriot/lib/ar-debug.h                           |   91 ++
 aisleriot/lib/ar-glib-compat.h                     |   57 +
 aisleriot/lib/ar-gsettings.c                       |  258 ++++
 aisleriot/lib/ar-gsettings.h                       |   32 +
 aisleriot/lib/ar-gtk-compat.h                      |   51 +
 aisleriot/lib/ar-help.c                            |  156 ++
 aisleriot/lib/ar-help.h                            |   36 +
 aisleriot/lib/ar-marshal.list                      |    3 +
 aisleriot/lib/ar-preimage-private.h                |   40 +
 aisleriot/lib/ar-preimage.c                        |  490 +++++++
 aisleriot/lib/ar-preimage.h                        |   90 ++
 aisleriot/lib/ar-profile.c                         |   64 +
 aisleriot/lib/ar-profile.h                         |   54 +
 aisleriot/lib/ar-runtime.c                         |  605 ++++++++
 aisleriot/lib/ar-runtime.h                         |   79 +
 aisleriot/lib/ar-show.c                            |  159 ++
 aisleriot/lib/ar-show.h                            |   38 +
 aisleriot/lib/ar-sound.c                           |  285 ++++
 aisleriot/lib/ar-sound.h                           |   45 +
 aisleriot/lib/ar-string-utils.c                    |   98 ++
 aisleriot/lib/ar-string-utils.h                    |   30 +
 .../org.gnome.Patience.WindowState.gschema.xml.in  |   41 +
 aisleriot/lib/render-cards.c                       |   10 +-
 aisleriot/slot-renderer.c                          |    2 +-
 aisleriot/smclient/Makefile.am                     |   41 +
 aisleriot/smclient/eggdesktopfile.c                | 1518 ++++++++++++++++++++
 aisleriot/smclient/eggdesktopfile.h                |  160 ++
 aisleriot/smclient/eggsmclient-dbus.c              |  294 ++++
 aisleriot/smclient/eggsmclient-osx.c               |  235 +++
 aisleriot/smclient/eggsmclient-private.h           |   59 +
 aisleriot/smclient/eggsmclient-win32.c             |  353 +++++
 aisleriot/smclient/eggsmclient-xsmp.c              | 1411 ++++++++++++++++++
 aisleriot/smclient/eggsmclient.c                   |  625 ++++++++
 aisleriot/smclient/eggsmclient.h                   |  123 ++
 aisleriot/smclient/eggsmclient.patch               |  202 +++
 aisleriot/sol.c                                    |   32 +-
 aisleriot/stats-dialog.c                           |   18 +-
 aisleriot/util.c                                   |   40 +-
 aisleriot/util.h                                   |    5 +
 aisleriot/window.c                                 |  136 +-
 configure.in                                       |    1 +
 po/POTFILES.in                                     |    7 +
 75 files changed, 10633 insertions(+), 333 deletions(-)
---
diff --git a/aisleriot/Makefile.am b/aisleriot/Makefile.am
index c5047aa..f555654 100644
--- a/aisleriot/Makefile.am
+++ b/aisleriot/Makefile.am
@@ -1,5 +1,9 @@
 SUBDIRS = rules data lib icons
 
+if WITH_SMCLIENT
+SUBDIRS += smclient
+endif
+
 if BUILD_HELP
 SUBDIRS += help
 endif
@@ -24,6 +28,8 @@ sol_SOURCES = \
 	ar-cursor.h	\
 	ar-game-chooser.c \
 	ar-game-chooser.h \
+	ar-stock.c	\
+	ar-stock.h	\
 	ar-style.c	\
 	ar-style.h	\
 	ar-style-private.h \
@@ -44,6 +50,13 @@ sol_SOURCES = \
 	window.h	\
 	$(NULL)
 
+if !HAVE_HILDON
+sol_SOURCES += \
+	ar-clock.c \
+	ar-clock.h \
+	$(NULL)
+endif
+
 if HAVE_MAEMO_5
 sol_SOURCES += \
 	ar-fullscreen-button.c \
@@ -81,11 +94,15 @@ sol_LDFLAGS = \
 
 sol_LDADD = \
 	lib/libaisleriot.la \
-	$(top_builddir)/libgames-support/libgames-support.la \
 	$(GTK_LIBS) \
 	$(GUILE_LIBS) \
 	-lm
 
+if WITH_SMCLIENT
+sol_CPPFLAGS += -I$(srcdir)/smclient -Ismclient
+sol_LDADD += smclient/libsmclient.la
+endif
+
 if HAVE_HILDON
 sol_CFLAGS += $(HILDON_CFLAGS)
 sol_LDADD += $(HILDON_LIBS)
@@ -116,12 +133,16 @@ if ENABLE_AISLERIOT_CLUTTER
 noinst_PROGRAMS = sol-clutter
 
 sol_clutter_SOURCES = \
+	ar-clock.c	\
+	ar-clock.h	\
 	ar-clutter-embed.c \
 	ar-clutter-embed.h \
 	ar-cursor.c	\
 	ar-cursor.h	\
 	ar-game-chooser.c \
 	ar-game-chooser.h \
+	ar-stock.c	\
+	ar-stock.h	\
 	ar-style.c	\
 	ar-style.h	\
 	ar-style-private.h \
diff --git a/aisleriot/ar-clock.c b/aisleriot/ar-clock.c
new file mode 100644
index 0000000..53c2037
--- /dev/null
+++ b/aisleriot/ar-clock.c
@@ -0,0 +1,261 @@
+/*
+ * clock.c: 
+ *
+ * Copyright © 2001, 2003 Iain Holmes
+ *           © 2001 Mark McLoughlin
+ *
+ * Authors: Iain Holmes <iain ximian com>
+ *          Mark McLoughlin <mark skynet ie>
+ */
+
+#include <config.h>
+
+#include "ar-clock.h"
+#include "ar-glib-compat.h"
+
+G_DEFINE_TYPE (ArClock, ar_clock, GTK_TYPE_LABEL)
+
+struct ArClockPrivate {
+  guint update_timeout_id;
+  gboolean update;
+  gboolean started;
+  time_t start_time;
+  time_t stop_time;
+};
+
+static void
+clock_paint (ArClock *clock_widget)
+{
+  char string[32];
+  time_t seconds;
+  int secs;
+  int mins;
+  int hours;
+
+  seconds = ar_clock_get_seconds (clock_widget);
+  hours = seconds / 3600;
+  secs = seconds - hours * 3600;
+  mins = secs / 60;
+  secs = secs - mins * 60;
+
+  /* FIXMEchpe: i18n! */
+  g_snprintf (string, sizeof (string), "%.2d:%.2d:%.2d", hours, mins, secs);
+
+  gtk_label_set_text (GTK_LABEL (clock_widget), string);
+}
+
+static gboolean
+ar_clock_update (ArClock *clock_widget)
+{
+  clock_paint (clock_widget);
+
+  return TRUE;
+}
+
+static void
+ar_clock_start_timer (ArClock *clock_widget)
+{
+  if (clock_widget->priv->update_timeout_id != 0)
+    return;
+
+  clock_widget->priv->update_timeout_id =
+    gdk_threads_add_timeout_seconds (1, (GSourceFunc) ar_clock_update, clock_widget);
+}
+
+static void
+ar_clock_stop_timer (ArClock *clock_widget)
+{
+  if (clock_widget->priv->update_timeout_id != 0) {
+    g_source_remove (clock_widget->priv->update_timeout_id);
+    clock_widget->priv->update_timeout_id = 0;
+  }
+}
+
+static void
+ar_clock_finalize (GObject * object)
+{
+  ArClock *clock_widget = AR_CLOCK (object);
+
+  ar_clock_stop_timer (clock_widget);
+
+  G_OBJECT_CLASS (ar_clock_parent_class)->finalize (object);
+}
+
+static void
+ar_clock_class_init (ArClockClass * klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+
+  object_class->finalize = ar_clock_finalize;
+
+  g_type_class_add_private (object_class, sizeof (ArClockPrivate));
+}
+
+static void
+ar_clock_init (ArClock *clock_widget)
+{
+  clock_widget->priv = G_TYPE_INSTANCE_GET_PRIVATE (clock_widget, AR_TYPE_CLOCK, ArClockPrivate);
+
+  clock_widget->priv->update_timeout_id = 0;
+  clock_widget->priv->start_time = clock_widget->priv->stop_time = 0;
+  clock_widget->priv->started = FALSE;
+  clock_widget->priv->update = TRUE;
+
+  /* FIXMEchpe: call clock_paint() instead */
+  gtk_label_set_text (GTK_LABEL (clock_widget), "00:00:00");
+}
+
+/**
+ * ar_clock_new:
+ * 
+ * Create a new game clock
+ * 
+ * Returns: A new #ArClock
+ **/
+GtkWidget *
+ar_clock_new (void)
+{
+  return g_object_new (AR_TYPE_CLOCK, NULL);
+}
+
+/**
+ * ar_clock_start:
+ * @clock_widget:
+ *
+ * Records the current time as the start time, and starts
+ * updating the clock if updates are enabled (see
+ * ar_clock_set_update()).
+ */
+void
+ar_clock_start (ArClock *clock_widget)
+{
+  g_return_if_fail (GAMES_IS_CLOCK (clock_widget));
+
+  if (clock_widget->priv->started)
+    return; /* nothing to do */
+
+  clock_widget->priv->started = TRUE;
+  clock_widget->priv->start_time = time (NULL) - (clock_widget->priv->stop_time - clock_widget->priv->start_time);
+
+  if (clock_widget->priv->update)
+    ar_clock_start_timer (clock_widget);
+}
+
+/**
+ * ar_clock_is_started:
+ * @clock_widget:
+ *
+ * Returns: whether @clock_widget is running
+ */
+gboolean
+ar_clock_is_started   (ArClock *clock_widget)
+{
+  g_return_val_if_fail (GAMES_IS_CLOCK (clock_widget), FALSE);
+
+  return clock_widget->priv->started;
+}
+
+/**
+ * ar_clock_stop:
+ * @clock_widget:
+ *
+ * Records the current time as the stop time, and stops
+ * updating the clock.
+ */
+void
+ar_clock_stop (ArClock *clock_widget)
+{
+  g_return_if_fail (GAMES_IS_CLOCK (clock_widget));
+
+  if (!clock_widget->priv->started)
+    return;
+
+  clock_widget->priv->started = FALSE;
+  clock_widget->priv->stop_time = time (NULL);
+
+  ar_clock_stop_timer (clock_widget);
+  clock_paint (clock_widget);
+}
+
+/**
+ * ar_clock_reset:
+ * @clock_widget:
+ *
+ * Resets the time in @clock_widget to 0.
+ */
+void
+ar_clock_reset (ArClock *clock_widget)
+{
+  g_return_if_fail (GAMES_IS_CLOCK (clock_widget));
+
+  clock_widget->priv->start_time = clock_widget->priv->stop_time = time (NULL);
+
+  clock_paint (clock_widget);
+}
+
+/**
+ * ar_clock_get_seconds:
+ * @clock_widget:
+ *
+ * Returns: the elapsed time in @clock_widget
+ */
+time_t
+ar_clock_get_seconds (ArClock *clock_widget)
+{
+  g_return_val_if_fail (GAMES_IS_CLOCK (clock_widget), 0);
+
+  if (clock_widget->priv->started)
+    return time (NULL) - clock_widget->priv->start_time;
+  else
+    return clock_widget->priv->stop_time - clock_widget->priv->start_time;
+}
+
+/**
+ * ar_clock_add_seconds:
+ * @clock_widget:
+ * @seconds:
+ *
+ * Adds @seconds to the reported elapsed time in @clock_widget.
+ */
+void
+ar_clock_add_seconds (ArClock *clock_widget,
+                         time_t seconds)
+{
+  g_return_if_fail (GAMES_IS_CLOCK (clock_widget));
+
+  if (!clock_widget->priv->started) {
+    g_warning ("Clock not started, cannot add seconds!\n");
+    return;
+  }
+
+  clock_widget->priv->start_time -= seconds;
+  clock_paint (clock_widget);
+}
+
+/**
+ * ar_clock_set_update:
+ * @clock_widget:
+ *
+ * Sets whether the clock will automatically update itself every second
+ * to display the currently elapsed time.
+ *
+ * Use this e.g. to disable updates while the clock is invisible.
+ */
+void
+ar_clock_set_update (ArClock *clock_widget,
+                        gboolean do_update)
+{
+  g_return_if_fail (GAMES_IS_CLOCK (clock_widget));
+
+  do_update = do_update != FALSE;
+  if (do_update == clock_widget->priv->update)
+    return;
+
+  clock_widget->priv->update = do_update;
+  if (do_update) {
+    ar_clock_start_timer (clock_widget);
+    clock_paint (clock_widget);
+  } else {
+    ar_clock_stop_timer (clock_widget);
+  }
+}
diff --git a/aisleriot/ar-clock.h b/aisleriot/ar-clock.h
new file mode 100644
index 0000000..e57b77d
--- /dev/null
+++ b/aisleriot/ar-clock.h
@@ -0,0 +1,52 @@
+/*
+ *clock_widget.h: Clock widget.
+ *
+ * Copyright © 2001, 2003 Iain Holmes
+ *           © 2001 Mark McLoughlin
+ *
+ * Authors: Iain Holmes <iain ximian com>
+ *          Mark McLoughlin <mark skynet ie>
+ */
+
+#ifndef __AR_CLOCK_H__
+#define __AR_CLOCK_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <time.h>
+
+G_BEGIN_DECLS
+
+#define AR_TYPE_CLOCK            (ar_clock_get_type ())
+#define AR_CLOCK(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), AR_TYPE_CLOCK, ArClock))
+#define AR_CLOCK_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), AR_TYPE_CLOCK, ArClockClass))
+#define GAMES_IS_CLOCK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), AR_TYPE_CLOCK))
+#define GAMES_IS_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), AR_TYPE_CLOCK))
+
+typedef struct ArClockPrivate ArClockPrivate;
+
+typedef struct {
+  GtkLabel label;
+  /*< private >*/
+  ArClockPrivate *priv;
+} ArClock;
+
+typedef struct {
+  GtkLabelClass parent_class;
+} ArClockClass;
+
+GType      ar_clock_get_type     (void);
+GtkWidget *ar_clock_new          (void);
+void       ar_clock_start        (ArClock *clock_widget);
+gboolean   ar_clock_is_started   (ArClock *clock_widget);
+void       ar_clock_stop         (ArClock *clock_widget);
+void       ar_clock_reset        (ArClock *clock_widget);
+time_t     ar_clock_get_seconds  (ArClock *clock_widget);
+void       ar_clock_add_seconds  (ArClock *clock_widget,
+                                     time_t seconds);
+void       ar_clock_set_update   (ArClock *clock_widget,
+                                     gboolean do_update);
+
+G_END_DECLS
+
+#endif /* __AR_CLOCK_H__ */
diff --git a/aisleriot/ar-fullscreen-button.c b/aisleriot/ar-fullscreen-button.c
index a11a64b..6823e63 100644
--- a/aisleriot/ar-fullscreen-button.c
+++ b/aisleriot/ar-fullscreen-button.c
@@ -19,9 +19,9 @@
 
 #include "ar-fullscreen-button.h"
 
-#include <libgames-support/games-stock.h>
-#include <libgames-support/games-glib-compat.h>
-#include <libgames-support/games-gtk-compat.h>
+#include "ar-stock.h"
+#include "ar-glib-compat.h"
+#include "ar-gtk-compat.h"
 
 struct _ArFullscreenButtonPrivate {
   GtkWindow *window;
@@ -384,7 +384,7 @@ ar_fullscreen_button_init (ArFullscreenButton *button)
   gtk_event_box_set_visible_window (GTK_EVENT_BOX (ebox), FALSE);
   gtk_container_add (GTK_CONTAINER (button), ebox);
 
-  image = gtk_image_new_from_stock (GAMES_STOCK_LEAVE_FULLSCREEN, GTK_ICON_SIZE_LARGE_TOOLBAR);
+  image = gtk_image_new_from_stock (AR_STOCK_LEAVE_FULLSCREEN, GTK_ICON_SIZE_LARGE_TOOLBAR);
   gtk_container_add (GTK_CONTAINER (ebox), image);
   gtk_widget_show_all (ebox);
 
diff --git a/aisleriot/ar-game-chooser.c b/aisleriot/ar-game-chooser.c
index 8462ae6..a31a641 100644
--- a/aisleriot/ar-game-chooser.c
+++ b/aisleriot/ar-game-chooser.c
@@ -23,10 +23,10 @@
 
 #include <glib/gi18n.h>
 
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-string-utils.h>
-#include <libgames-support/games-glib-compat.h>
+#include "ar-debug.h"
+#include "ar-runtime.h"
+#include "ar-string-utils.h"
+#include "ar-glib-compat.h"
 
 #ifdef HAVE_MAEMO_5
 #include <hildon/hildon-gtk.h>
@@ -200,7 +200,7 @@ ar_game_chooser_constructor (GType type,
 
   current_game_file = aisleriot_game_get_game_file (aisleriot_window_get_game (priv->window));
 
-  games_dir = games_runtime_get_directory (GAMES_RUNTIME_GAME_GAMES_DIRECTORY);
+  games_dir = ar_runtime_get_directory (AR_RUNTIME_GAME_GAMES_DIRECTORY);
 
   dir = g_dir_open (games_dir, 0, NULL);
   if (dir != NULL) {
@@ -214,7 +214,7 @@ ar_game_chooser_constructor (GType type,
           strcmp (game_file, "sol.scm") == 0)
         continue;
 
-      game_name = games_filename_to_display_name (game_file);
+      game_name = ar_filename_to_display_name (game_file);
 
       gtk_list_store_insert_with_values (GTK_LIST_STORE (list), &iter,
                                          -1,
diff --git a/aisleriot/ar-stock.c b/aisleriot/ar-stock.c
new file mode 100644
index 0000000..f568822
--- /dev/null
+++ b/aisleriot/ar-stock.c
@@ -0,0 +1,450 @@
+/*
+ * Copyright © 2005 Richard Hoelscher
+ * Copyright © 2006 Andreas Røsdal
+ * Copyright © 2007 Christian Persch
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *      Richard Hoelscher <rah rahga com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#if GTK_CHECK_VERSION (2, 90, 7)
+#define GDK_KEY(symbol) GDK_KEY_##symbol
+#else
+#include <gdk/gdkkeysyms.h>
+#define GDK_KEY(symbol) GDK_##symbol
+#endif
+
+#include "ar-runtime.h"
+
+#include "ar-stock.h"
+
+#ifndef HAVE_HILDON
+
+typedef struct {
+  const char *stock_id;
+  const char *tooltip;
+} GamesStockItemTooltip;
+
+static const char *
+ar_stock_get_tooltip_from_stockid (const char* stockid)
+{
+  static const GamesStockItemTooltip stock_item_tooltip[] = {
+    { AR_STOCK_CONTENTS,         N_("View help for this game") },
+    { AR_STOCK_END_GAME,         N_("End the current game") },
+    { AR_STOCK_FULLSCREEN,       N_("Toggle fullscreen mode") },
+    { AR_STOCK_HINT,             N_("Get a hint for your next move") },
+    { AR_STOCK_LEAVE_FULLSCREEN, N_("Leave fullscreen mode") },
+    { AR_STOCK_NETWORK_GAME,     N_("Start a new multiplayer network game") },
+    { AR_STOCK_NETWORK_LEAVE,    N_("End the current network game and return to network server") },
+    { AR_STOCK_NEW_GAME,         N_("Start a new game") },
+    { AR_STOCK_PAUSE_GAME,       N_("Pause the game") },
+    { AR_STOCK_PLAYER_LIST,      N_("Show a list of players in the network game") },
+    { AR_STOCK_REDO_MOVE,        N_("Redo the undone move") },
+    { AR_STOCK_RESTART_GAME,     N_("Restart the game") },
+    { AR_STOCK_RESUME_GAME,      N_("Resume the paused game") },
+    { AR_STOCK_SCORES,           N_("View the scores") },
+    { AR_STOCK_UNDO_MOVE,        N_("Undo the last move") },
+    { GTK_STOCK_ABOUT,              N_("About this game") },
+    { GTK_STOCK_CLOSE,              N_("Close this window") },
+    { GTK_STOCK_PREFERENCES,        N_("Configure the game") },
+    { GTK_STOCK_QUIT,               N_("Quit this game") },
+  };
+
+  guint i;
+  const char *tooltip = NULL;
+
+  if (!stockid)
+    return NULL;
+
+  for (i = 0; i < G_N_ELEMENTS (stock_item_tooltip); i++) {
+    if (strcmp (stock_item_tooltip[i].stock_id, stockid) == 0) {
+      tooltip = stock_item_tooltip[i].tooltip;
+      break;
+    }
+  }
+
+  return tooltip ? _(tooltip) : NULL;
+}
+
+static void
+menu_item_select_cb (GtkWidget * widget, GtkStatusbar * statusbar)
+{
+  GtkAction *action;
+  gchar *tooltip;
+  guint context_id;
+
+  context_id = gtk_statusbar_get_context_id (statusbar, "games-tooltip");
+
+#if GTK_CHECK_VERSION (2, 16, 0)
+  action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (widget));
+#elif GTK_CHECK_VERSION (2, 10, 0)
+  action = gtk_widget_get_action (widget);
+#else
+  action = g_object_get_data (G_OBJECT (widget), "gtk-action");
+#endif
+  g_return_if_fail (action != NULL);
+
+  g_object_get (action, "tooltip", &tooltip, NULL);
+
+  if (tooltip) {
+    gtk_statusbar_push (statusbar, context_id, tooltip);
+    g_free (tooltip);
+  } else {
+    const gchar *stock_tip = NULL;
+    gchar *stockid;
+
+    g_object_get (action, "stock-id", &stockid, NULL);
+    if (stockid != NULL) {
+      stock_tip = ar_stock_get_tooltip_from_stockid (stockid);
+      g_free (stockid);
+    }
+
+    if (stock_tip != NULL) {
+      gtk_statusbar_push (statusbar, context_id, stock_tip);
+    }
+  }
+}
+
+static void
+menu_item_deselect_cb (GtkWidget * widget, GtkStatusbar * statusbar)
+{
+  guint context_id;
+
+  context_id = gtk_statusbar_get_context_id (statusbar, "games-tooltip");
+  gtk_statusbar_pop (statusbar, context_id);
+}
+
+static void
+connect_proxy_cb (GtkUIManager * ui_manager,
+                  GtkAction * action,
+                  GtkWidget * proxy, GtkWidget * statusbar)
+{
+  if (!GTK_IS_MENU_ITEM (proxy))
+    return;
+
+  g_signal_connect (proxy, "select",
+                    G_CALLBACK (menu_item_select_cb), statusbar);
+  g_signal_connect (proxy, "deselect",
+                    G_CALLBACK (menu_item_deselect_cb), statusbar);
+}
+
+static void
+disconnect_proxy_cb (GtkUIManager * ui_manager,
+                     GtkAction * action,
+                     GtkWidget * proxy, GtkWidget * statusbar)
+{
+  if (!GTK_IS_MENU_ITEM (proxy))
+    return;
+
+  g_signal_handlers_disconnect_by_func
+    (proxy, G_CALLBACK (menu_item_select_cb), statusbar);
+  g_signal_handlers_disconnect_by_func
+    (proxy, G_CALLBACK (menu_item_deselect_cb), statusbar);
+}
+
+/** 
+ *  Call this once, after creating the UI Manager but before 
+ *  you start adding actions. Then, whenever an action is added, 
+ *  connect_proxy() will set tooltips to be displayed in the statusbar.
+ */
+void
+ar_stock_prepare_for_statusbar_tooltips (GtkUIManager * ui_manager,
+                                            GtkWidget * statusbar)
+{
+  g_signal_connect (ui_manager, "connect-proxy",
+                    G_CALLBACK (connect_proxy_cb), statusbar);
+  g_signal_connect (ui_manager, "disconnect-proxy",
+                    G_CALLBACK (disconnect_proxy_cb), statusbar);
+}
+
+#endif /* !HAVE_HILDON */
+
+/* This will become GTK_CHECK_VERSION (2, 15, x) once the patch from gtk+ bug 511332 is committed */
+#undef HAVE_GTK_ICON_FACTORY_ADD_ALIAS
+
+#ifndef HAVE_GTK_ICON_FACTORY_ADD_ALIAS
+
+static void
+register_stock_icon (GtkIconFactory * icon_factory,
+                     const char * stock_id,
+                     const char * icon_name)
+{
+  GtkIconSource *source;
+  GtkIconSet *set;
+
+  set = gtk_icon_set_new ();
+
+  source = gtk_icon_source_new ();
+  gtk_icon_source_set_icon_name (source, icon_name);
+  gtk_icon_set_add_source (set, source);
+  gtk_icon_source_free (source);
+
+  gtk_icon_factory_add (icon_factory, stock_id, set);
+  gtk_icon_set_unref (set);
+}
+
+static void
+register_stock_icon_bidi (GtkIconFactory * icon_factory,
+                          const char * stock_id,
+                          const char * icon_name_ltr,
+                          const char * icon_name_rtl)
+{
+  GtkIconSource *source;
+  GtkIconSet *set;
+
+  set = gtk_icon_set_new ();
+
+  source = gtk_icon_source_new ();
+  gtk_icon_source_set_icon_name (source, icon_name_ltr);
+  gtk_icon_source_set_direction (source, GTK_TEXT_DIR_LTR);
+  gtk_icon_source_set_direction_wildcarded (source, FALSE);
+  gtk_icon_set_add_source (set, source);
+  gtk_icon_source_free (source);
+
+  source = gtk_icon_source_new ();
+  gtk_icon_source_set_icon_name (source, icon_name_rtl);
+  gtk_icon_source_set_direction (source, GTK_TEXT_DIR_RTL);
+  gtk_icon_source_set_direction_wildcarded (source, FALSE);
+  gtk_icon_set_add_source (set, source);
+  gtk_icon_source_free (source);
+
+  gtk_icon_factory_add (icon_factory, stock_id, set);
+  gtk_icon_set_unref (set);
+}
+
+#endif /* HAVE_GTK_ICON_FACTORY_ADD_ALIAS */
+
+void
+ar_stock_init (void)
+{
+  /* These stocks have a gtk stock icon */
+  const char *stock_icon_aliases[][2] = {
+    { AR_STOCK_CONTENTS,         GTK_STOCK_HELP },
+    { AR_STOCK_HINT,             GTK_STOCK_DIALOG_INFO },
+    { AR_STOCK_NEW_GAME,         GTK_STOCK_NEW },
+    { AR_STOCK_START_NEW_GAME,   GTK_STOCK_NEW },
+    { AR_STOCK_RESET,            GTK_STOCK_CLEAR },
+    { AR_STOCK_RESTART_GAME,     GTK_STOCK_REFRESH },
+#if GTK_CHECK_VERSION (2, 8, 0)
+    /* This is used on hildon too, but only exists since 2.8 */
+    { AR_STOCK_FULLSCREEN,       GTK_STOCK_FULLSCREEN },
+    /* This is used on maemo 5 */
+    { AR_STOCK_LEAVE_FULLSCREEN, GTK_STOCK_LEAVE_FULLSCREEN },
+#endif /* GTK+ >= 2.8.0 */
+#ifdef HAVE_GTK_ICON_FACTORY_ADD_ALIAS
+    { AR_STOCK_REDO_MOVE,        GTK_STOCK_REDO },
+    { AR_STOCK_UNDO_MOVE,        GTK_STOCK_UNDO },
+#ifndef HAVE_HILDON
+    { AR_STOCK_RESUME_GAME,      GTK_STOCK_MEDIA_PLAY },
+#endif /* HAVE_HILDON */
+#endif
+#ifndef HAVE_HILDON
+    { AR_STOCK_NETWORK_GAME,     GTK_STOCK_NETWORK },
+    { AR_STOCK_NETWORK_LEAVE,    GTK_STOCK_STOP },
+    { AR_STOCK_PLAYER_LIST,      GTK_STOCK_INFO },
+
+    { AR_STOCK_PAUSE_GAME,       GTK_STOCK_MEDIA_PAUSE },
+#endif /* !HAVE_HILDON */
+  };
+
+#ifndef HAVE_GTK_ICON_FACTORY_ADD_ALIAS
+  const char *stock_icon_aliases_bidi[][3] = {
+    { AR_STOCK_REDO_MOVE, GTK_STOCK_REDO "-ltr", GTK_STOCK_REDO "-rtl" },
+    { AR_STOCK_UNDO_MOVE, GTK_STOCK_UNDO "-ltr", GTK_STOCK_UNDO "-rtl" },
+    { AR_STOCK_RESUME_GAME, GTK_STOCK_MEDIA_PLAY "-ltr", GTK_STOCK_MEDIA_PLAY "-rtl" },
+  };
+#endif
+
+  /* Private icon names */
+  const char *private_icon_names[][2] = {
+#ifndef HAVE_HILDON
+    { AR_STOCK_TELEPORT, "teleport" },
+    { AR_STOCK_RTELEPORT, "teleport-random" },
+    { AR_STOCK_SCORES, "scores" },
+#endif /* !HAVE_HILDON */
+    { AR_STOCK_DEAL_CARDS, "cards-deal" }
+  };
+
+/* Use different accels on GTK/GNOME and Maemo */
+#ifdef HAVE_HILDON
+#define STOCK_ACCEL(normal,hildon) (hildon)
+#else
+#define STOCK_ACCEL(normal,hildon) (normal)
+#endif
+
+  static const GtkStockItem ar_stock_items[] = {
+    { AR_STOCK_CONTENTS,         N_("_Contents"),          0, STOCK_ACCEL (GDK_KEY (F1), 0), NULL },
+    { AR_STOCK_FULLSCREEN,       N_("_Fullscreen"),        0, STOCK_ACCEL (GDK_KEY (F11), GDK_KEY (F6)), NULL },
+    { AR_STOCK_HINT,             N_("_Hint"),              STOCK_ACCEL (GDK_CONTROL_MASK, 0), STOCK_ACCEL ('h', GDK_KEY (Return)), NULL },
+    /* Translators: This "_New" is for the menu item 'Game->New', implies "New Game" */
+    { AR_STOCK_NEW_GAME,         N_("_New"),               STOCK_ACCEL (GDK_CONTROL_MASK, 0), STOCK_ACCEL ('n', 0), NULL },
+    /* Translators: This "_New Game" is for the game-over dialogue */
+    { AR_STOCK_START_NEW_GAME,   N_("_New Game"),          0, 0, NULL },
+    { AR_STOCK_REDO_MOVE,        N_("_Redo Move"),         STOCK_ACCEL (GDK_CONTROL_MASK | GDK_SHIFT_MASK, 0), STOCK_ACCEL ('z', GDK_KEY (F7)), NULL },
+    /* Translators: this is the "Reset" scores button in a scores dialogue */
+    { AR_STOCK_RESET,            N_("_Reset"),             0, 0, NULL },
+    /* Translators: "_Restart" is the menu item 'Game->Restart', implies "Restart Game" */
+    { AR_STOCK_RESTART_GAME,     N_("_Restart"),           0, 0, NULL },
+    { AR_STOCK_UNDO_MOVE,        N_("_Undo Move"),         STOCK_ACCEL (GDK_CONTROL_MASK, 0), STOCK_ACCEL ('z', GDK_KEY (F8)), NULL },
+    { AR_STOCK_DEAL_CARDS,       N_("_Deal"),              GDK_CONTROL_MASK, 'd', NULL },
+#ifndef HAVE_HILDON
+    { AR_STOCK_LEAVE_FULLSCREEN, N_("_Leave Fullscreen"),  0, GDK_KEY (F11), NULL },
+    { AR_STOCK_NETWORK_GAME,     N_("Network _Game"),      GDK_CONTROL_MASK, 'g', NULL },
+    { AR_STOCK_NETWORK_LEAVE,    N_("L_eave Game"),        GDK_CONTROL_MASK, 'e', NULL },
+    { AR_STOCK_PLAYER_LIST,      N_("Player _List"),       GDK_CONTROL_MASK, 'l', NULL },
+    { AR_STOCK_PAUSE_GAME,       N_("_Pause"),             0, GDK_KEY (Pause), NULL },
+    { AR_STOCK_RESUME_GAME,      N_("Res_ume"),            0, GDK_KEY (Pause), NULL },
+    { AR_STOCK_SCORES,           N_("_Scores"),            0, 0, NULL },
+    { AR_STOCK_END_GAME,         N_("_End Game"),          0, 0, NULL },
+#endif
+
+#ifdef HAVE_MAEMO_3
+    /* Work around maemo brokenness wrt. stock item translations.
+     * See https://bugs.maemo.org/show_bug.cgi?id=1449 . */
+    { GTK_STOCK_ABOUT,              N_("_About"),             0, 0, NULL },
+    { GTK_STOCK_CANCEL,             N_("_Cancel"),            0, 0, NULL },
+    { GTK_STOCK_CLOSE,              N_("_Close"),             0, 0, NULL },
+    { GTK_STOCK_OK,                 N_("_OK"),                0, 0, NULL },
+#endif /* HAVE_MAEMO_3 */
+  };
+
+#undef STOCK_ACCEL
+
+  guint i;
+  GtkIconFactory *icon_factory;
+
+  icon_factory = gtk_icon_factory_new ();
+
+#ifdef HAVE_GTK_ICON_FACTORY_ADD_ALIAS
+  for (i = 0; i < G_N_ELEMENTS (stock_icon_aliases); ++i) {
+    gtk_icon_factory_add_alias (stock_icon_aliases[i][0],
+                                stock_icon_aliases[i][1]);
+  }
+
+#else
+  for (i = 0; i < G_N_ELEMENTS (stock_icon_aliases); ++i) {
+    register_stock_icon (icon_factory,
+                         stock_icon_aliases[i][0],
+                         stock_icon_aliases[i][1]);
+  }
+
+  for (i = 0; i < G_N_ELEMENTS (stock_icon_aliases_bidi); ++i) {
+    register_stock_icon_bidi (icon_factory,
+                              stock_icon_aliases_bidi[i][0],
+                              stock_icon_aliases_bidi[i][1],
+                              stock_icon_aliases_bidi[i][2]);
+  }
+#endif /* HAVE_GTK_ICON_FACTORY_ADD_ALIAS */
+
+  /* Register our private themeable icons */
+  for (i = 0; i < G_N_ELEMENTS (private_icon_names); i++) {
+    register_stock_icon (icon_factory,
+                         private_icon_names[i][0],
+                         private_icon_names[i][1]);
+  }
+
+  gtk_icon_factory_add_default (icon_factory);
+  g_object_unref (icon_factory);
+
+  /* GtkIconTheme will then look in our custom hicolor dir
+   * for icons as well as the standard search paths.
+   */
+  /* FIXME: multi-head! */
+  gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
+                                     ar_runtime_get_directory (AR_RUNTIME_ICON_THEME_DIRECTORY));
+ 
+  gtk_stock_add_static (ar_stock_items, G_N_ELEMENTS (ar_stock_items));
+}
+
+/* Returns a GPL N+ license string for a specific game. */
+static gchar *
+ar_get_licence_version (const gchar * game_name,
+                           int version)
+{
+  gchar *license_trans, *license_str;
+
+  static const char license0[] =
+    /* %s is replaced with the name of the game in gnome-games. */
+    N_("%s is free software; you can redistribute it and/or modify "
+       "it under the terms of the GNU General Public License as published by "
+       "the Free Software Foundation; either version %d of the License, or "
+       "(at your option) any later version.");
+  static const char license1[] =
+    N_("%s is distributed in the hope that it will be useful, "
+       "but WITHOUT ANY WARRANTY; without even the implied warranty of "
+       "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
+       "GNU General Public License for more details.");
+  static const char license2[] =
+    N_("You should have received a copy of the GNU General Public License "
+       "along with %s; if not, write to the Free Software Foundation, Inc., "
+       "51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA");
+  static const char license3[] =
+    N_("You should have received a copy of the GNU General Public License "
+       "along with this program.  If not, see <http://www.gnu.org/licenses/>.");
+
+  if (version >= 3)
+    license_trans = g_strjoin ("\n\n", _(license0), _(license1), _(license3), NULL);
+  else
+    license_trans = g_strjoin ("\n\n", _(license0), _(license1), _(license2), NULL);
+
+#if !GTK_CHECK_VERSION (2, 8, 0)
+  /* We have to manually wrap the text, since the about dialogue cannot
+   * do it itself before gtk 2.8.
+   */
+  {
+    char *p;
+    gsize line_length;
+
+    line_length = 0;
+    for (p = license_trans; *p; ++p) {
+      if (*p == ' ' && line_length > 42) {
+        *p = '\n';
+        line_length = 0;
+      } else if (*p == '\n') {
+        line_length = 0;
+      } else {
+        ++line_length;
+      }
+    }
+  }
+#endif /* ! GTK+ 2.8.0 */
+
+  license_str =
+    g_strdup_printf (license_trans, game_name, version, game_name, game_name);
+  g_free (license_trans);
+
+  return license_str;
+}
+
+/**
+ * gamess_get_licence:
+ *
+ * Returns: a newly allocated string with a GPL licence notice. The GPL version used
+ *   depends on the game and the configure options and is determined from
+ *   ar_runtime_get_gpl_version()
+ */
+gchar *
+ar_get_licence (const gchar * game_name)
+{
+  return ar_get_licence_version (game_name, ar_runtime_get_gpl_version ());
+}
diff --git a/aisleriot/ar-stock.h b/aisleriot/ar-stock.h
new file mode 100644
index 0000000..4a68562
--- /dev/null
+++ b/aisleriot/ar-stock.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2005 Richard Hoelscher
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *      Richard Hoelscher <rah rahga com>
+ */
+
+#ifndef __AR_STOCK_H__
+#define __AR_STOCK_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/* These use stock gtk icons */
+#define AR_STOCK_SCORES           "games-scores"
+#define AR_STOCK_PAUSE_GAME       "games-pause-game"
+#define AR_STOCK_RESUME_GAME      "games-resume-game"
+
+#define AR_STOCK_FULLSCREEN       "games-fullscreen"
+#define AR_STOCK_LEAVE_FULLSCREEN "games-leave-fullscreen"
+#define AR_STOCK_NEW_GAME         "games-new-game"
+#define AR_STOCK_START_NEW_GAME   "games-start-new-game"
+#define AR_STOCK_NETWORK_GAME     "games-network-game"
+#define AR_STOCK_NETWORK_LEAVE    "games-network-leave"
+#define AR_STOCK_PLAYER_LIST      "games-player-list"
+#define AR_STOCK_RESTART_GAME     "games-restart-game"
+#define AR_STOCK_UNDO_MOVE        "games-undo-move"
+#define AR_STOCK_REDO_MOVE        "games-redo-move"
+#define AR_STOCK_HINT             "games-hint"
+#define AR_STOCK_END_GAME         "games-end-game"
+#define AR_STOCK_CONTENTS         "games-contents"
+#define AR_STOCK_RESET            "games-reset"
+
+/* These belong to us */
+#define AR_STOCK_TELEPORT         "games-teleport"
+#define AR_STOCK_RTELEPORT        "games-teleport-random"
+#define AR_STOCK_DEAL_CARDS       "games-cards-deal"
+
+void   ar_stock_init (void);
+void   ar_stock_prepare_for_statusbar_tooltips (GtkUIManager * ui_manager,
+                                                   GtkWidget * statusbar);
+gchar *ar_get_licence (const gchar * game_name);
+
+G_END_DECLS
+
+#endif /* __AR_STOCK_H__ */
diff --git a/aisleriot/ar-style-gtk.c b/aisleriot/ar-style-gtk.c
index edfe917..1dc9f06 100644
--- a/aisleriot/ar-style-gtk.c
+++ b/aisleriot/ar-style-gtk.c
@@ -22,8 +22,8 @@
 
 #include "ar-style-private.h"
 
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-glib-compat.h>
+#include "ar-debug.h"
+#include "ar-glib-compat.h"
 
 /**
  * SECTION: ar-style-gtk
@@ -51,7 +51,7 @@ sync_settings (GtkSettings *settings,
   else
     pspec_name = NULL;
 
-  _games_debug_print (GAMES_DEBUG_GAME_STYLE,
+  ar_debug_print (AR_DEBUG_GAME_STYLE,
                       "[ArStyle %p] Syncing GtkSettings:%s\n",
                       style, pspec_name ? pspec_name : "*");
 
@@ -139,7 +139,7 @@ direction_changed_cb (GtkWidget *widget,
 
   direction = gtk_widget_get_direction (widget);
 
-  _games_debug_print (GAMES_DEBUG_GAME_STYLE,
+  ar_debug_print (AR_DEBUG_GAME_STYLE,
                       "[ArStyle %p] Widget direction-changed direction %d previous-direction %d\n",
                       style, direction, previous_direction);
 
@@ -171,7 +171,7 @@ screen_changed_cb (GtkWidget *widget,
 
   screen = gtk_widget_get_screen (widget);
 
-  _games_debug_print (GAMES_DEBUG_GAME_STYLE,
+  ar_debug_print (AR_DEBUG_GAME_STYLE,
                       "[ArStyle %p] Widget screen-changed screen %p previous-screen %p\n",
                       style, screen, previous_screen);
 
@@ -217,7 +217,7 @@ style_set_cb (GtkWidget *widget,
   gboolean interior_focus;
   double card_slot_ratio, card_overhang, card_step;
 
-  _games_debug_print (GAMES_DEBUG_GAME_STYLE,
+  ar_debug_print (AR_DEBUG_GAME_STYLE,
                       "[ArStyle %p] Syncing widget style properties\n",
                       style);
 
@@ -387,7 +387,7 @@ _ar_style_gtk_attach (ArStyle *style,
   g_return_if_fail (AR_IS_STYLE (style));
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  _games_debug_print (GAMES_DEBUG_GAME_STYLE,
+  ar_debug_print (AR_DEBUG_GAME_STYLE,
                       "[ArStyle %p] Attaching to widget %p\n", style, widget);
 
   g_assert (g_object_get_data (G_OBJECT (widget), "Ar::Style") == NULL);
@@ -425,7 +425,7 @@ _ar_style_gtk_detach (ArStyle *style,
   g_return_if_fail (AR_IS_STYLE (style));
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  _games_debug_print (GAMES_DEBUG_GAME_STYLE,
+  ar_debug_print (AR_DEBUG_GAME_STYLE,
                       "[ArStyle %p] Detaching from widget %p\n", style, widget);
 
   g_assert (g_object_get_data (G_OBJECT (widget), "Ar::Style") == style);
diff --git a/aisleriot/ar-style.c b/aisleriot/ar-style.c
index 3a074df..c310330 100644
--- a/aisleriot/ar-style.c
+++ b/aisleriot/ar-style.c
@@ -20,8 +20,8 @@
 #include "ar-style.h"
 #include "ar-style-private.h"
 
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-glib-compat.h>
+#include "ar-debug.h"
+#include "ar-glib-compat.h"
 
 enum
 {
@@ -103,7 +103,7 @@ ar_style_init (ArStyle *style)
 }
 #endif /* HAVE_HILDON */
 
-  _games_debug_print (GAMES_DEBUG_GAME_STYLE,
+  ar_debug_print (AR_DEBUG_GAME_STYLE,
                       "[ArStyle %p] Using %s drawing\n",
                       style, priv->pixbuf_drawing ? "pixbuf" : "pixmap");
 
diff --git a/aisleriot/baize.c b/aisleriot/baize.c
index 4496a53..472f8db 100644
--- a/aisleriot/baize.c
+++ b/aisleriot/baize.c
@@ -21,7 +21,7 @@
 
 #include <cogl/cogl.h>
 
-#include <libgames-support/games-runtime.h>
+#include "ar-runtime.h"
 
 /* Special version of ClutterTexture that repeats the texture to fill
    the entire stage. This is used to paint the baize background */
@@ -44,7 +44,7 @@ aisleriot_baize_init (AisleriotBaize *baize)
   char *path;
   GError *error = NULL;
 
-  path = games_runtime_get_file (GAMES_RUNTIME_PIXMAP_DIRECTORY, "baize.png");
+  path = ar_runtime_get_file (AR_RUNTIME_PIXMAP_DIRECTORY, "baize.png");
   if (!clutter_texture_set_from_file (CLUTTER_TEXTURE (baize), path, &error)) {
     g_warning ("Failed to load the baize from '%s': %s\n", path, error->message);
     g_error_free (error);
diff --git a/aisleriot/board-noclutter.c b/aisleriot/board-noclutter.c
index 7ce01fa..7791b6d 100644
--- a/aisleriot/board-noclutter.c
+++ b/aisleriot/board-noclutter.c
@@ -35,11 +35,11 @@
 #define GDK_KEY(symbol) GDK_##symbol
 #endif
 
-#include <libgames-support/games-glib-compat.h>
-#include <libgames-support/games-gtk-compat.h>
-#include <libgames-support/games-marshal.h>
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-sound.h>
+#include "ar-glib-compat.h"
+#include "ar-gtk-compat.h"
+#include "ar-marshal.h"
+#include "ar-runtime.h"
+#include "ar-sound.h"
 
 #include "conf.h"
 #include "game.h"
@@ -1310,10 +1310,10 @@ drop_moving_cards (AisleriotBoard *board,
 
   if (moved) {
     aisleriot_game_end_move (priv->game);
-    games_sound_play ("click");
+    ar_sound_play ("click");
   } else {
     aisleriot_game_discard_move (priv->game);
-    games_sound_play ("slide");
+    ar_sound_play ("slide");
   }
 
   drag_end (board, moved);
@@ -1487,7 +1487,7 @@ aisleriot_board_move_selected_cards_to_slot (AisleriotBoard *board,
   if (moved) {
     aisleriot_game_end_move (priv->game);
 
-    games_sound_play ("click");
+    ar_sound_play ("click");
 
     if (selection_slot->needs_update)
       g_signal_emit_by_name (priv->game, "slot-changed", selection_slot); /* FIXMEchpe! */
@@ -2276,7 +2276,7 @@ aisleriot_board_activate (AisleriotBoard *board)
 
   if (aisleriot_game_button_clicked_lambda (priv->game, focus_slot->id)) {
     aisleriot_game_end_move (priv->game);
-    games_sound_play ("click");
+    ar_sound_play ("click");
     aisleriot_game_test_end_of_game (priv->game);
 
     return;
@@ -3102,7 +3102,7 @@ aisleriot_board_button_release (GtkWidget *widget,
       aisleriot_game_record_move (priv->game, -1, NULL, 0);
       if (aisleriot_game_button_clicked_lambda (priv->game, slot->id)) {
         aisleriot_game_end_move (priv->game);
-	games_sound_play_for_event ("click", (GdkEvent *) event);
+	ar_sound_play_for_event ("click", (GdkEvent *) event);
       } else {
         aisleriot_game_discard_move (priv->game);
       }
@@ -4150,7 +4150,7 @@ aisleriot_board_class_init (AisleriotBoardClass *klass)
                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                   G_STRUCT_OFFSET (AisleriotBoardClass, move_cursor),
                   NULL, NULL,
-                  games_marshal_BOOLEAN__ENUM_INT,
+                  ar_marshal_BOOLEAN__ENUM_INT,
                   G_TYPE_BOOLEAN,
                   2,
                   GTK_TYPE_MOVEMENT_STEP,
diff --git a/aisleriot/board.c b/aisleriot/board.c
index bef8b86..9093f08 100644
--- a/aisleriot/board.c
+++ b/aisleriot/board.c
@@ -30,10 +30,10 @@
 
 #include <clutter/clutter.h>
 
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-glib-compat.h>
-#include <libgames-support/games-marshal.h>
-#include <libgames-support/games-sound.h>
+#include "ar-debug.h"
+#include "ar-glib-compat.h"
+#include "ar-marshal.h"
+#include "ar-sound.h"
 
 #include "conf.h"
 
@@ -1277,10 +1277,10 @@ drop_moving_cards (AisleriotBoard *board,
 
   if (moved) {
     aisleriot_game_end_move (priv->game);
-    games_sound_play ("click");
+    ar_sound_play ("click");
   } else {
     aisleriot_game_discard_move (priv->game);
-    games_sound_play ("slide");
+    ar_sound_play ("slide");
   }
 
   drag_end (board, moved);
@@ -1431,7 +1431,7 @@ aisleriot_board_move_selected_cards_to_slot (AisleriotBoard *board,
   if (moved) {
     aisleriot_game_end_move (priv->game);
 
-    games_sound_play ("click");
+    ar_sound_play ("click");
 
     if (selection_slot->needs_update)
       g_signal_emit_by_name (priv->game, "slot-changed", selection_slot); /* FIXMEchpe! */
@@ -2299,7 +2299,7 @@ aisleriot_board_activate (AisleriotBoard *board,
     return;
 #endif
 
-  _games_debug_print (GAMES_DEBUG_GAME_KEYNAV,
+  ar_debug_print (AR_DEBUG_GAME_KEYNAV,
                       "board ::activate keyval %x modifiers %x\n",
                       keyval, modifiers);
 
@@ -2334,7 +2334,7 @@ aisleriot_board_activate (AisleriotBoard *board,
 
   if (aisleriot_game_button_clicked_lambda (priv->game, focus_slot->id)) {
     aisleriot_game_end_move (priv->game);
-    games_sound_play ("click");
+    ar_sound_play ("click");
     aisleriot_game_test_end_of_game (priv->game);
 
     return;
@@ -2385,7 +2385,7 @@ aisleriot_board_move_cursor (AisleriotBoard *board,
   step = action[0];
   count = (action[1] == MOVE_LEFT ? -1 : 1);
 
-  _games_debug_print (GAMES_DEBUG_GAME_KEYNAV,
+  ar_debug_print (AR_DEBUG_GAME_KEYNAV,
                       "board ::move-cursor keyval %x modifiers %x step '%c' count %d\n",
                       keyval, modifiers,
                       step, count);
@@ -2465,7 +2465,7 @@ aisleriot_board_select_all (AisleriotBoard *board,
   AisleriotBoardPrivate *priv = board->priv;
   ArSlot *focus_slot = priv->focus_slot;
 
-  _games_debug_print (GAMES_DEBUG_GAME_KEYNAV,
+  ar_debug_print (AR_DEBUG_GAME_KEYNAV,
                       "board ::select-all keyval %x modifiers %x\n",
                       keyval, modifiers);
 
@@ -2483,7 +2483,7 @@ aisleriot_board_deselect_all (AisleriotBoard *board,
                               guint keyval,
                               ClutterModifierType modifiers)
 {
-  _games_debug_print (GAMES_DEBUG_GAME_KEYNAV,
+  ar_debug_print (AR_DEBUG_GAME_KEYNAV,
                       "board ::deselect-all keyval %x modifiers %x\n",
                       keyval, modifiers);
 
@@ -2500,7 +2500,7 @@ aisleriot_board_toggle_selection (AisleriotBoard *board,
   ArSlot *focus_slot;
   int focus_card_id;
 
-  _games_debug_print (GAMES_DEBUG_GAME_KEYNAV,
+  ar_debug_print (AR_DEBUG_GAME_KEYNAV,
                       "board ::toggle-selection keyval %x modifiers %x\n",
                       keyval, modifiers);
 
@@ -2585,7 +2585,7 @@ aisleriot_board_allocate (ClutterActor *actor,
 
   is_same = clutter_actor_box_equal (box, &priv->allocation);
 
-  _games_debug_print (GAMES_DEBUG_GAME_SIZING,
+  ar_debug_print (AR_DEBUG_GAME_SIZING,
                       "board ::allocate (%f / %f)-(%f / %f) => %f x %f is-same %s force-update %s\n",
                       box->x1, box->y1, box->x2, box->y2,
                       box->x2 - box->x1, box->y2 - box->y1,
@@ -2611,7 +2611,7 @@ aisleriot_board_get_preferred_width (ClutterActor *actor,
                                      float *min_width_p,
                                      float *natural_width_p)
 {
-  _games_debug_print (GAMES_DEBUG_GAME_SIZING,
+  ar_debug_print (AR_DEBUG_GAME_SIZING,
                       "board ::get-preferred-width\n");
 
   *min_width_p = BOARD_MIN_WIDTH;
@@ -2624,7 +2624,7 @@ aisleriot_board_get_preferred_height (ClutterActor *actor,
                                       float *min_height_p,
                                       float *natural_height_p)
 {
-  _games_debug_print (GAMES_DEBUG_GAME_SIZING,
+  ar_debug_print (AR_DEBUG_GAME_SIZING,
                       "board ::get-preferred-height\n");
 
   *min_height_p = BOARD_MIN_HEIGHT;
@@ -2657,7 +2657,7 @@ aisleriot_board_key_press (ClutterActor *actor,
 {
   ClutterBindingPool *pool;
 
-  _games_debug_print (GAMES_DEBUG_GAME_EVENTS,
+  ar_debug_print (AR_DEBUG_GAME_EVENTS,
                       "board ::key-press keyval %x modifiers %x\n",
                       event->keyval, event->modifier_state);
 
@@ -2731,7 +2731,7 @@ aisleriot_board_button_press (ClutterActor *actor,
   guint state;
   gboolean is_double_click, show_focus;
 
-  _games_debug_print (GAMES_DEBUG_GAME_EVENTS,
+  ar_debug_print (AR_DEBUG_GAME_EVENTS,
                       "board ::button-press @(%f / %f) button %d click-count %d modifiers %x\n",
                       event->x, event->y,
                       event->button, event->click_count,
@@ -2921,7 +2921,7 @@ aisleriot_board_button_release (ClutterActor *actor,
   AisleriotBoardPrivate *priv = board->priv;
   /* guint state; */
 
-  _games_debug_print (GAMES_DEBUG_GAME_EVENTS,
+  ar_debug_print (AR_DEBUG_GAME_EVENTS,
                       "board ::button-release @(%f / %f) button %d click-count %d modifiers %x\n",
                       event->x, event->y,
                       event->button, event->click_count,
@@ -2963,7 +2963,7 @@ aisleriot_board_button_release (ClutterActor *actor,
       aisleriot_game_record_move (priv->game, -1, NULL, 0);
       if (aisleriot_game_button_clicked_lambda (priv->game, slot->id)) {
         aisleriot_game_end_move (priv->game);
-	games_sound_play_for_event ("click", (GdkEvent *) event);
+	ar_sound_play_for_event ("click", (GdkEvent *) event);
       } else {
         aisleriot_game_discard_move (priv->game);
       }
@@ -2991,7 +2991,7 @@ aisleriot_board_motion (ClutterActor *actor,
   AisleriotBoard *board = AISLERIOT_BOARD (actor);
   AisleriotBoardPrivate *priv = board->priv;
 
-  _games_debug_print (GAMES_DEBUG_GAME_EVENTS,
+  ar_debug_print (AR_DEBUG_GAME_EVENTS,
                       "board ::motion @(%f / %f) modifiers %x\n",
                       event->x, event->y,
                       event->modifier_state);
@@ -3052,7 +3052,7 @@ static gboolean
 aisleriot_board_enter (ClutterActor *actor,
                        ClutterCrossingEvent *event)
 {
-  _games_debug_print (GAMES_DEBUG_GAME_EVENTS,
+  ar_debug_print (AR_DEBUG_GAME_EVENTS,
                       "board ::enter @(%f / %f)\n",
                       event->x, event->y);
 
@@ -3069,7 +3069,7 @@ aisleriot_board_leave (ClutterActor *actor,
 {
   AisleriotBoard *board = AISLERIOT_BOARD (actor);
 
-  _games_debug_print (GAMES_DEBUG_GAME_EVENTS,
+  ar_debug_print (AR_DEBUG_GAME_EVENTS,
                       "board ::leave @(%f / %f)\n",
                       event->x, event->y);
 
@@ -3085,14 +3085,14 @@ aisleriot_board_leave (ClutterActor *actor,
 static void
 aisleriot_board_key_focus_in (ClutterActor *actor)
 {
-  _games_debug_print (GAMES_DEBUG_GAME_EVENTS,
+  ar_debug_print (AR_DEBUG_GAME_EVENTS,
                       "board ::key-focus-in\n");
 }
 
 static void
 aisleriot_board_key_focus_out (ClutterActor *actor)
 {
-  _games_debug_print (GAMES_DEBUG_GAME_EVENTS,
+  ar_debug_print (AR_DEBUG_GAME_EVENTS,
                       "board ::key-focus-out\n");
 }
 
@@ -3323,7 +3323,7 @@ aisleriot_board_class_init (AisleriotBoardClass *klass)
                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                   G_STRUCT_OFFSET (AisleriotBoardClass, activate),
                   NULL, NULL,
-                  games_marshal_BOOLEAN__STRING_UINT_ENUM,
+                  ar_marshal_BOOLEAN__STRING_UINT_ENUM,
                   G_TYPE_BOOLEAN,
                   3,
                   G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
@@ -3336,7 +3336,7 @@ aisleriot_board_class_init (AisleriotBoardClass *klass)
                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                   G_STRUCT_OFFSET (AisleriotBoardClass, move_cursor),
                   NULL, NULL,
-                  games_marshal_BOOLEAN__STRING_UINT_ENUM,
+                  ar_marshal_BOOLEAN__STRING_UINT_ENUM,
                   G_TYPE_BOOLEAN,
                   3,
                   G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
@@ -3349,7 +3349,7 @@ aisleriot_board_class_init (AisleriotBoardClass *klass)
                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                   G_STRUCT_OFFSET (AisleriotBoardClass, toggle_selection),
                   NULL, NULL,
-                  games_marshal_BOOLEAN__STRING_UINT_ENUM,
+                  ar_marshal_BOOLEAN__STRING_UINT_ENUM,
                   G_TYPE_BOOLEAN,
                   3,
                   G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
@@ -3362,7 +3362,7 @@ aisleriot_board_class_init (AisleriotBoardClass *klass)
                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                   G_STRUCT_OFFSET (AisleriotBoardClass, select_all),
                   NULL, NULL,
-                  games_marshal_BOOLEAN__STRING_UINT_ENUM,
+                  ar_marshal_BOOLEAN__STRING_UINT_ENUM,
                   G_TYPE_BOOLEAN,
                   3,
                   G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
@@ -3375,7 +3375,7 @@ aisleriot_board_class_init (AisleriotBoardClass *klass)
                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                   G_STRUCT_OFFSET (AisleriotBoardClass, deselect_all),
                   NULL, NULL,
-                  games_marshal_BOOLEAN__STRING_UINT_ENUM,
+                  ar_marshal_BOOLEAN__STRING_UINT_ENUM,
                   G_TYPE_BOOLEAN,
                   3,
                   G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
diff --git a/aisleriot/card.c b/aisleriot/card.c
index 4409b45..6c7138d 100644
--- a/aisleriot/card.c
+++ b/aisleriot/card.c
@@ -23,7 +23,7 @@
 
 #include "card.h"
 
-#include <libgames-support/games-glib-compat.h>
+#include "ar-glib-compat.h"
 
 static void aisleriot_card_paint (ClutterActor *actor);
 
diff --git a/aisleriot/conf.c b/aisleriot/conf.c
index 6a587bc..c0c3739 100644
--- a/aisleriot/conf.c
+++ b/aisleriot/conf.c
@@ -21,7 +21,7 @@
 #include <string.h>
 #include <errno.h>
 
-#include <libgames-support/games-string-utils.h>
+#include "ar-string-utils.h"
 
 #include "util.h"
 #include "conf.h"
@@ -174,15 +174,15 @@ save_statistics (void)
 void
 aisleriot_conf_init (void)
 {
-  if (!games_conf_initialise ("Aisleriot")) {
+  if (!ar_conf_initialise ("Aisleriot")) {
     /* Set defaults */
-    games_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_TOOLBAR), TRUE);
-    games_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_STATUSBAR), TRUE);
-    games_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SOUND), TRUE);
-    games_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_ANIMATIONS), TRUE);
+    ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_TOOLBAR), TRUE);
+    ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_STATUSBAR), TRUE);
+    ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SOUND), TRUE);
+    ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_ANIMATIONS), TRUE);
 
 #ifdef HAVE_HILDON
-    games_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_CLICK_TO_MOVE), TRUE);
+    ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_CLICK_TO_MOVE), TRUE);
 #endif
   }
 
@@ -211,7 +211,7 @@ aisleriot_conf_shutdown (void)
   stats = NULL;
 #endif /* HAVE_GNOME */
 
-  games_conf_shutdown ();
+  ar_conf_shutdown ();
 }
 
 const char *
@@ -249,7 +249,7 @@ aisleriot_conf_get_options (const char *game_file,
 #else
   GError *error = NULL;
 
-  *options = games_conf_get_integer (game_file, "Options", &error);
+  *options = ar_conf_get_integer (game_file, "Options", &error);
   if (error) {
     g_error_free (error);
     return FALSE;
@@ -300,7 +300,7 @@ aisleriot_conf_set_options (const char *game_file,
   gconf_client_set_int (gconf_client, gconf_key, value, NULL);
   g_free (gconf_key);
 #else
-  games_conf_set_integer (game_file, "Options", value);
+  ar_conf_set_integer (game_file, "Options", value);
 #endif /* HAVE_GNOME */
 }
 
@@ -318,7 +318,7 @@ aisleriot_conf_get_statistic (const char *game_file,
     /* Previous versions used the localised name as key, so try it as fall-back.
      * See bug #406267 and bug #525177.
      */
-    display_name = games_filename_to_display_name (game_file);
+    display_name = ar_filename_to_display_name (game_file);
     game_stat = g_hash_table_lookup (stats, display_name);
     g_free (display_name);
   }
@@ -337,7 +337,7 @@ aisleriot_conf_get_statistic (const char *game_file,
 
   memset (statistic, 0, sizeof (AisleriotStatistic));
 
-  values = games_conf_get_integer_list (game_file, "Statistic", &len, &error);
+  values = ar_conf_get_integer_list (game_file, "Statistic", &len, &error);
   if (error) {
     g_error_free (error);
     return;
@@ -379,7 +379,7 @@ aisleriot_conf_set_statistic (const char *game_file,
   if (!game_stat) {
     char *localised_name;
 
-    localised_name = games_filename_to_display_name (game_file);
+    localised_name = ar_filename_to_display_name (game_file);
     game_stat = g_hash_table_lookup (stats, localised_name);
     g_free (localised_name);
   }
@@ -402,6 +402,6 @@ aisleriot_conf_set_statistic (const char *game_file,
   values[2] = statistic->best;
   values[3] = statistic->worst;
 
-  games_conf_set_integer_list (game_file, "Statistic", values, G_N_ELEMENTS (values));
+  ar_conf_set_integer_list (game_file, "Statistic", values, G_N_ELEMENTS (values));
 #endif /* HAVE_GNOME */
 }
diff --git a/aisleriot/conf.h b/aisleriot/conf.h
index 3aa7c57..8a28866 100644
--- a/aisleriot/conf.h
+++ b/aisleriot/conf.h
@@ -19,7 +19,7 @@
 #define AISLERIOT_CONF_H
 
 #include <glib.h>
-#include <libgames-support/games-conf.h>
+#include "ar-conf.h"
 
 G_BEGIN_DECLS
 
diff --git a/aisleriot/game.c b/aisleriot/game.c
index e77e316..0936ac1 100644
--- a/aisleriot/game.c
+++ b/aisleriot/game.c
@@ -35,10 +35,10 @@
 #include <clutter/clutter.h>
 #endif
 
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-glib-compat.h>
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-string-utils.h>
+#include "ar-debug.h"
+#include "ar-glib-compat.h"
+#include "ar-runtime.h"
+#include "ar-string-utils.h"
 
 #include "conf.h"
 #include "util.h"
@@ -614,10 +614,10 @@ cscmi_add_slot (SCM slot_data)
 #undef EQUALS_SYMBOL
 
 #ifdef GNOME_ENABLE_DEBUG
-  _GAMES_DEBUG_IF (GAMES_DEBUG_SCHEME) {
+  _AR_DEBUG_IF (AR_DEBUG_SCHEME) {
     static const char *types[] = { "unknown", "foundation", "reserve", "stock", "tableau", "waste" };
 
-    _games_debug_print (GAMES_DEBUG_SCHEME,
+    ar_debug_print (AR_DEBUG_SCHEME,
                         "Adding new slot %d type %s\n",
                         scm_to_int (SCM_CAR (slot_data)), types[type]);
   }
@@ -1153,7 +1153,7 @@ cscmi_eval_installed_file (const char *filename,
     return FALSE;
   }
 
-  path = games_runtime_get_file (GAMES_RUNTIME_GAME_GAMES_DIRECTORY, filename);
+  path = ar_runtime_get_file (AR_RUNTIME_GAME_GAMES_DIRECTORY, filename);
   if (g_file_test (path, G_FILE_TEST_EXISTS) &&
       g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
     scm_c_primitive_load (path);
@@ -1856,7 +1856,7 @@ aisleriot_game_get_game_file (AisleriotGame *game)
 char *
 aisleriot_game_get_name (AisleriotGame *game)
 {
-  return games_filename_to_display_name (game->game_file);
+  return ar_filename_to_display_name (game->game_file);
 }
 
 /**
diff --git a/aisleriot/lib/Makefile.am b/aisleriot/lib/Makefile.am
index 833daba..17a6aa8 100644
--- a/aisleriot/lib/Makefile.am
+++ b/aisleriot/lib/Makefile.am
@@ -2,6 +2,11 @@ NULL =
 
 noinst_LTLIBRARIES = libaisleriot.la
 
+BUILT_SOURCES = \
+	ar-marshal.c \
+	ar-marshal.h \
+	$(NULL)
+
 libaisleriot_la_SOURCES = \
 	ar-card.c \
 	ar-card.h \
@@ -11,9 +16,43 @@ libaisleriot_la_SOURCES = \
 	ar-card-theme-private.h \
 	ar-card-themes.c \
 	ar-card-themes.h \
+	ar-conf.c \
+	ar-conf.h \
+	ar-debug.c \
+	ar-debug.h \
+	ar-glib-compat.h \
+	ar-gtk-compat.h \
+	ar-help.c \
+	ar-help.h \
 	ar-pixbuf-utils.c \
 	ar-pixbuf-utils.h \
+	ar-profile.c \
+	ar-profile.h \
+	ar-runtime.c \
+	ar-runtime.h \
+	ar-show.c \
+	ar-show.h \
+	ar-sound.c \
+	ar-sound.h \
+	ar-string-utils.c \
+	ar-string-utils.h \
+	$(BUILT_SOURCES) \
+	$(NULL)
+
+if !HAVE_HILDON
+libaisleriot_la_SOURCES += \
+	ar-preimage.c \
+	ar-preimage.h \
+	ar-preimage-private.h \
 	$(NULL)
+endif
+
+if HAVE_GIO_2_26
+libaisleriot_la_SOURCES += \
+	ar-gsettings.c \
+	ar-gsettings.h \
+	$(NULL)
+endif
 
 if HAVE_GTK_2
 libaisleriot_la_SOURCES += \
@@ -66,6 +105,11 @@ endif
 libaisleriot_la_CPPFLAGS = \
 	-I$(top_srcdir) \
 	-I$(top_builddir) \
+	-DPKGDATADIR="\"$(pkgdatadir)\"" \
+	-DPREFIX="\"$(prefix)\"" \
+	-DDATADIR="\"$(datadir)\"" \
+	-DCOMMON_DATADIR="\"$(datadir)/gnome-games-common\"" \
+	-DSCORESDIR="\"$(scoredir)\"" \
 	$(AM_CPPFLAGS)
 
 libaisleriot_la_CFLAGS = \
@@ -73,7 +117,6 @@ libaisleriot_la_CFLAGS = \
 	$(AM_CFLAGS)
 
 libaisleriot_la_LIBADD = \
-	$(top_builddir)/libgames-support/libgames-support.la \
 	$(GTK_LIBS)
 
 if HAVE_GIO_2_26
@@ -96,6 +139,31 @@ libaisleriot_la_CFLAGS += $(HILDON_CFLAGS)
 libaisleriot_la_LIBADD += $(HILDON_LIBS)
 endif
 
+if HAVE_GNOME
+libaisleriot_la_CFLAGS += $(GNOME_CFLAGS)
+libaisleriot_la_LIBADD += $(GNOME_LIBS)
+endif
+
+if ENABLE_SOUND
+libaisleriot_la_CFLAGS += $(CANBERRA_GTK_CFLAGS)
+libaisleriot_la_LIBADD += $(CANBERRA_GTK_LIBS)
+endif
+
+gsettingsschema_in_files = org.gnome.Patience.WindowState.gschema.xml.in
+gsettings_SCHEMAS = $(gsettingsschema_in_files:.gschema.xml.in=.gschema.xml)
+
+ar-marshal.c: stamp-ar-marshal.c
+	@true
+stamp-ar-marshal.c: ar-marshal.list Makefile
+	$(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=ar_marshal $< --header --body $(GLIB_GENMARSHAL_INTERNAL) > ar-marshal.c \
+	&& echo timestamp > $(@F)
+
+ar-marshal.h: stamp-ar-marshal.h
+	@true
+stamp-ar-marshal.h: ar-marshal.list Makefile
+	$(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=ar_marshal $< --header $(GLIB_GENMARSHAL_INTERNAL) > ar-marshal.h \
+	&& echo timestamp > $(@F)
+
 # Auxiliary programme to prerender card images
 
 if HAVE_RSVG
@@ -119,8 +187,7 @@ ar_cards_renderer_LDFLAGS = \
 	$(AM_LDFLAGS)
 
 ar_cards_renderer_LDADD = \
-	libaisleriot.la	\
-	$(top_builddir)/libgames-support/libgames-support.la \
+	libaisleriot.la \
 	$(GTK_LIBS) \
 	$(RSVG_LIBS)
 
@@ -131,11 +198,28 @@ endif # WITH_GTHREAD
 
 endif # HAVE_RSVG
 
-EXTRA_DIST =
+stamp_files = \
+	stamp-ar-marshal.c \
+	stamp-ar-marshal.h \
+	$(NULL)
 
-CLEANFILES =
+EXTRA_DIST = \
+	$(gsettingsschema_in_files) \
+        $(NULL)
 
-DISTCLEANFILES =
+CLEANFILES = \
+	$(BUILT_SOURCES) \
+        $(gsettings_SCHEMAS) \
+	$(stamp_files) \
+	$(NULL)
+
+DISTCLEANFILES = \
+	$(BUILT_SOURCES) \
+        $(gsettings_SCHEMAS) \
+	$(stamp_files) \
+	$(NULL)
 
+ INTLTOOL_XML_NOMERGE_RULE@
+ GSETTINGS_RULES@
 
 -include $(top_srcdir)/git.mk
diff --git a/aisleriot/lib/ar-card-images.c b/aisleriot/lib/ar-card-images.c
index 5375c0b..fb8711d 100644
--- a/aisleriot/lib/ar-card-images.c
+++ b/aisleriot/lib/ar-card-images.c
@@ -26,7 +26,7 @@
 #include <glib.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
-#include <libgames-support/games-debug.h>
+#include "ar-debug.h"
 
 #include "ar-card-images.h"
 
@@ -101,7 +101,7 @@ ar_card_images_clear_cache (ArCardImages * images)
 {
   guint i;
 
-  _games_debug_print (GAMES_DEBUG_CARD_CACHE,
+  ar_debug_print (AR_DEBUG_CARD_CACHE,
                       "ar_card_images_clear_cache\n");
 
   for (i = 0; i < CACHE_SIZE; i++) {
@@ -236,8 +236,8 @@ ar_card_images_finalize (GObject * object)
   }
 
 #ifdef GNOME_ENABLE_DEBUG
-  _GAMES_DEBUG_IF (GAMES_DEBUG_CARD_CACHE) {
-    _games_debug_print (GAMES_DEBUG_CARD_CACHE,
+  _AR_DEBUG_IF (AR_DEBUG_CARD_CACHE) {
+    ar_debug_print (AR_DEBUG_CARD_CACHE,
                         "ArCardImages %p statistics: %u calls with %u hits and %u misses for a hit/total of %.3f\n",
                         images, images->n_calls, images->cache_hits, images->n_calls - images->cache_hits,
                         images->n_calls > 0 ? (double) images->cache_hits / (double) images->n_calls : 0.0);
diff --git a/aisleriot/lib/ar-card-surface-cache.c b/aisleriot/lib/ar-card-surface-cache.c
index 3fa133f..29d5ec2 100644
--- a/aisleriot/lib/ar-card-surface-cache.c
+++ b/aisleriot/lib/ar-card-surface-cache.c
@@ -18,7 +18,7 @@
 
 #include <config.h>
 
-#include <libgames-support/games-debug.h>
+#include "ar-debug.h"
 #include <gdk/gdk.h>
 
 #include "ar-card-surface-cache.h"
@@ -74,7 +74,7 @@ ar_card_surface_cache_clear (ArCardSurfaceCache *cache)
   ArCardSurfaceCachePrivate *priv = cache->priv;
   int i;
 
-  _games_debug_print (GAMES_DEBUG_CARD_CACHE,
+  ar_debug_print (AR_DEBUG_CARD_CACHE,
                       "ar_card_surface_cache_clear\n");
 
   for (i = 0; i < AR_CARDS_TOTAL; i++) {
@@ -134,8 +134,8 @@ ar_card_surface_cache_finalize (GObject *object)
   g_free (priv->cards);
 
 #ifdef GNOME_ENABLE_DEBUG
-  _GAMES_DEBUG_IF (GAMES_DEBUG_CARD_CACHE) {
-    _games_debug_print (GAMES_DEBUG_CARD_CACHE,
+  _AR_DEBUG_IF (AR_DEBUG_CARD_CACHE) {
+    ar_debug_print (AR_DEBUG_CARD_CACHE,
                         "ArCardSurfaceCache %p statistics: %u calls with %u hits and %u misses for a hit/total of %.3f\n",
                         cache, priv->n_calls, priv->cache_hits, priv->n_calls - priv->cache_hits,
                         priv->n_calls > 0 ? (double) priv->cache_hits / (double) priv->n_calls : 0.0);
diff --git a/aisleriot/lib/ar-card-textures-cache.c b/aisleriot/lib/ar-card-textures-cache.c
index b4bea44..57d349d 100644
--- a/aisleriot/lib/ar-card-textures-cache.c
+++ b/aisleriot/lib/ar-card-textures-cache.c
@@ -23,7 +23,7 @@
 
 #include <cogl/cogl.h>
 
-#include <libgames-support/games-debug.h>
+#include "ar-debug.h"
 
 #include "ar-card-textures-cache.h"
 #include "ar-card-private.h"
@@ -77,7 +77,7 @@ ar_card_textures_cache_clear (ArCardTexturesCache *cache)
   ArCardTexturesCachePrivate *priv = cache->priv;
   int i;
 
-  _games_debug_print (GAMES_DEBUG_CARD_CACHE,
+  ar_debug_print (AR_DEBUG_CARD_CACHE,
                       "ar_card_textures_cache_clear\n");
 
   for (i = 0; i < AR_CARDS_TOTAL; i++) {
@@ -137,8 +137,8 @@ ar_card_textures_cache_finalize (GObject *object)
   g_free (priv->cards);
 
 #ifdef GNOME_ENABLE_DEBUG
-  _GAMES_DEBUG_IF (GAMES_DEBUG_CARD_CACHE) {
-    _games_debug_print (GAMES_DEBUG_CARD_CACHE,
+  _AR_DEBUG_IF (AR_DEBUG_CARD_CACHE) {
+    ar_debug_print (AR_DEBUG_CARD_CACHE,
                         "ArCardTexturesCache %p statistics: %u calls with %u hits and %u misses for a hit/total of %.3f\n",
                         cache, priv->n_calls, priv->cache_hits, priv->n_calls - priv->cache_hits,
                         priv->n_calls > 0 ? (double) priv->cache_hits / (double) priv->n_calls : 0.0);
diff --git a/aisleriot/lib/ar-card-theme-fixed.c b/aisleriot/lib/ar-card-theme-fixed.c
index 72bac28..08dfc00 100644
--- a/aisleriot/lib/ar-card-theme-fixed.c
+++ b/aisleriot/lib/ar-card-theme-fixed.c
@@ -25,9 +25,9 @@
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gtk/gtk.h>
 
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-string-utils.h>
+#include "ar-debug.h"
+#include "ar-runtime.h"
+#include "ar-string-utils.h"
 
 #include "ar-card-theme.h"
 #include "ar-card-theme-private.h"
@@ -71,7 +71,7 @@ ar_card_theme_fixed_load (ArCardTheme *card_theme,
 
   key_file = g_key_file_new ();
   if (!g_key_file_load_from_file (key_file, path, 0, &error)) {
-    _games_debug_print (GAMES_DEBUG_CARD_THEME,
+    ar_debug_print (AR_DEBUG_CARD_THEME,
                         "Failed to load prerendered card theme from %s: %s\n", path,
                         error->message);
     g_error_free (error);
@@ -82,14 +82,14 @@ ar_card_theme_fixed_load (ArCardTheme *card_theme,
     g_key_file_get_integer_list (key_file, "Card Theme", "Sizes", &n_sizes,
                                  &error);
   if (error) {
-    _games_debug_print (GAMES_DEBUG_CARD_THEME,
+    ar_debug_print (AR_DEBUG_CARD_THEME,
                         "Failed to get card sizes: %s\n", error->message);
     g_error_free (error);
     goto loser;
   }
 
   if (n_sizes == 0) {
-    _games_debug_print (GAMES_DEBUG_CARD_THEME,
+    ar_debug_print (AR_DEBUG_CARD_THEME,
                         "Card theme contains no sizes\n");
     goto loser;
   }
@@ -106,7 +106,7 @@ ar_card_theme_fixed_load (ArCardTheme *card_theme,
 
     width = g_key_file_get_integer (key_file, group, "Width", &err);
     if (err) {
-      _games_debug_print (GAMES_DEBUG_CARD_THEME,
+      ar_debug_print (AR_DEBUG_CARD_THEME,
                           "Error loading width for size %d: %s\n", sizes[i],
                           err->message);
       g_error_free (err);
@@ -114,7 +114,7 @@ ar_card_theme_fixed_load (ArCardTheme *card_theme,
     }
     height = g_key_file_get_integer (key_file, group, "Height", &err);
     if (err) {
-      _games_debug_print (GAMES_DEBUG_CARD_THEME,
+      ar_debug_print (AR_DEBUG_CARD_THEME,
                           "Error loading height for size %d: %s\n", sizes[i],
                           err->message);
       g_error_free (err);
@@ -227,12 +227,12 @@ ar_card_theme_fixed_set_card_size (ArCardTheme *card_theme,
       theme->size_available = TRUE;
       theme->card_size = size;
 
-      _games_debug_print (GAMES_DEBUG_CARD_THEME,
+      ar_debug_print (AR_DEBUG_CARD_THEME,
                           "Found prerendered card size %dx%d as nearest available size to %dx%d\n",
                           size.width, size.height, twidth, theight);
 
     } else {
-      _games_debug_print (GAMES_DEBUG_CARD_THEME,
+      ar_debug_print (AR_DEBUG_CARD_THEME,
                           "No prerendered size available for %d:%d\n",
                           width, height);
       theme->size_available = FALSE;
@@ -287,7 +287,7 @@ ar_card_theme_fixed_get_card_pixbuf (ArCardTheme *card_theme,
 
   pixbuf = gdk_pixbuf_new_from_file (path, &error);
   if (!pixbuf) {
-    _games_debug_print (GAMES_DEBUG_CARD_THEME,
+    ar_debug_print (AR_DEBUG_CARD_THEME,
                         "Failed to load card image %s: %s\n",
                         filename, error->message);
     g_error_free (error);
@@ -309,7 +309,7 @@ ar_card_theme_fixed_class_get_theme_info (ArCardThemeClass *klass,
   if (!g_str_has_suffix (filename, ".card-theme"))
     return NULL;
 
-  display_name = games_filename_to_display_name (filename);
+  display_name = ar_filename_to_display_name (filename);
 
 #ifdef HAVE_HILDON
   /* On Hildon, fixed is the default. For pref backward compatibility,
@@ -340,13 +340,13 @@ ar_card_theme_fixed_class_foreach_theme_dir (ArCardThemeClass *klass,
   if (!_ar_card_theme_class_foreach_env (klass, "AR_CARD_THEME_PATH_FIXED", callback, data))
     return FALSE;
 
-  if (!callback (klass, games_runtime_get_directory (GAMES_RUNTIME_PRERENDERED_CARDS_DIRECTORY), data))
+  if (!callback (klass, ar_runtime_get_directory (AR_RUNTIME_PRERENDERED_CARDS_DIRECTORY), data))
     return FALSE;
 
   /* If we're installed in a non-system prefix, also load the card themes
    * from the system prefix.
    */
-  if (!games_runtime_is_system_prefix ())
+  if (!ar_runtime_is_system_prefix ())
     return callback (klass, "/usr/share/gnome-games-common/card-themes", data);
 
   return TRUE;
diff --git a/aisleriot/lib/ar-card-theme-kde.c b/aisleriot/lib/ar-card-theme-kde.c
index b91dfe8..c846258 100644
--- a/aisleriot/lib/ar-card-theme-kde.c
+++ b/aisleriot/lib/ar-card-theme-kde.c
@@ -26,12 +26,12 @@
 #include <librsvg/rsvg.h>
 #include <librsvg/rsvg-cairo.h>
 
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-profile.h>
-#include <libgames-support/games-preimage.h>
-#include <libgames-support/games-preimage-private.h>
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-string-utils.h>
+#include "ar-debug.h"
+#include "ar-profile.h"
+#include "ar-preimage.h"
+#include "ar-preimage-private.h"
+#include "ar-runtime.h"
+#include "ar-string-utils.h"
 
 #include "ar-card-theme.h"
 #include "ar-card-theme-private.h"
@@ -189,7 +189,7 @@ ar_card_theme_kde_get_card_extents (ArCardThemeKDE *theme,
                                     int card_id,
                                     const char *node)
 {
-  GamesPreimage *preimage;
+  ArPreimage *preimage;
   cairo_rectangle_t *card_extents;
   cairo_rectangle_t rect;
   cairo_surface_t *surface;
@@ -204,15 +204,15 @@ ar_card_theme_kde_get_card_extents (ArCardThemeKDE *theme,
 
   surface = cairo_recording_surface_create (CAIRO_CONTENT_ALPHA, NULL);
   cr = cairo_create (surface);
-  _games_profile_start ("getting ink extents for node %s", node);
+  ar_profilestart ("getting ink extents for node %s", node);
   rsvg_handle_render_cairo_sub (preimage->rsvg_handle, cr, node);
-  _games_profile_end ("getting ink extents for node %s", node);
+  ar_profileend ("getting ink extents for node %s", node);
   cairo_destroy (cr);
 
   cairo_recording_surface_ink_extents (surface, &rect.x, &rect.y, &rect.width, &rect.height);
   cairo_surface_destroy (surface);
 
-  _games_debug_print (GAMES_DEBUG_CARD_THEME,
+  ar_debug_print (AR_DEBUG_CARD_THEME,
                       "card %s %.3f x%.3f at (%.3f | %.3f)\n",
                       node,
                       card_extents->width, card_extents->height,
@@ -241,7 +241,7 @@ ar_card_theme_kde_load (ArCardTheme *card_theme,
     "#green_back"
   };
   ArCardThemeKDE *theme = (ArCardThemeKDE *) card_theme;
-  GamesPreimage *preimage;
+  ArPreimage *preimage;
   char node[32];
   guint i;
   gboolean has_red_joker, has_black_joker, has_joker;
@@ -318,7 +318,7 @@ ar_card_theme_kde_get_card_pixbuf (ArCardTheme *card_theme,
 {
   ArCardThemePreimage *preimage_card_theme = (ArCardThemePreimage *) card_theme;
   ArCardThemeKDE *theme = (ArCardThemeKDE *) card_theme;
-  GamesPreimage *preimage = preimage_card_theme->cards_preimage;
+  ArPreimage *preimage = preimage_card_theme->cards_preimage;
   GdkPixbuf *subpixbuf;
   double card_width, card_height;
   double width, height;
@@ -327,7 +327,7 @@ ar_card_theme_kde_get_card_pixbuf (ArCardTheme *card_theme,
   cairo_rectangle_t *card_extents;
 
   if (G_UNLIKELY (card_id == AR_CARD_SLOT)) {
-    subpixbuf = games_preimage_render (preimage_card_theme->slot_preimage,
+    subpixbuf = ar_preimage_render (preimage_card_theme->slot_preimage,
                                        preimage_card_theme->card_size.width,
                                        preimage_card_theme->card_size.height);
 
@@ -340,8 +340,8 @@ ar_card_theme_kde_get_card_pixbuf (ArCardTheme *card_theme,
   if (!card_extents)
     return NULL;
 
-  card_width = ((double) games_preimage_get_width (preimage)) / N_COLS;
-  card_height = ((double) games_preimage_get_height (preimage)) / N_ROWS;
+  card_width = ((double) ar_preimage_get_width (preimage)) / N_COLS;
+  card_height = ((double) ar_preimage_get_height (preimage)) / N_ROWS;
 
   width = preimage_card_theme->card_size.width;
   height = preimage_card_theme->card_size.height;
@@ -352,14 +352,14 @@ ar_card_theme_kde_get_card_pixbuf (ArCardTheme *card_theme,
 //   zoomx = width / card_extents->width;
 //   zoomy = height / card_extents->height;
 
-  subpixbuf = games_preimage_render_sub (preimage,
+  subpixbuf = ar_preimage_render_sub (preimage,
                                          node,
                                          preimage_card_theme->card_size.width,
                                          preimage_card_theme->card_size.height,
                                          -card_extents->x, -card_extents->y,
                                          zoomx, zoomy);
 
-  _games_debug_print (GAMES_DEBUG_CARD_THEME,
+  ar_debug_print (AR_DEBUG_CARD_THEME,
                       "Returning %p\n", subpixbuf);
 
   return subpixbuf;
@@ -372,13 +372,13 @@ ar_card_theme_kde_paint_card (ArCardTheme *card_theme,
 {
   ArCardThemePreimage *preimage_card_theme = (ArCardThemePreimage *) card_theme;
   ArCardThemeKDE *theme = (ArCardThemeKDE *) card_theme;
-  GamesPreimage *preimage = preimage_card_theme->cards_preimage;
+  ArPreimage *preimage = preimage_card_theme->cards_preimage;
   char node[32];
   cairo_rectangle_t *card_extents;
   cairo_matrix_t matrix;
 
   if (G_UNLIKELY (card_id == AR_CARD_SLOT)) {
-    games_preimage_render_cairo (preimage_card_theme->slot_preimage,
+    ar_preimage_render_cairo (preimage_card_theme->slot_preimage,
                                  cr,
                                  preimage_card_theme->card_size.width,
                                  preimage_card_theme->card_size.height);
@@ -451,7 +451,7 @@ ar_card_theme_kde_class_get_theme_info (ArCardThemeClass *klass,
   char *svg_filename = NULL, *name = NULL, *display_name, *pref_name;
 
   if (get_is_blacklisted (filename)) {
-    _games_debug_print (GAMES_DEBUG_CARD_THEME,
+    ar_debug_print (AR_DEBUG_CARD_THEME,
                         "KDE card theme %s is blacklisted\n", filename);
     return NULL;
   }
diff --git a/aisleriot/lib/ar-card-theme-preimage.c b/aisleriot/lib/ar-card-theme-preimage.c
index e4ac34c..f459da9 100644
--- a/aisleriot/lib/ar-card-theme-preimage.c
+++ b/aisleriot/lib/ar-card-theme-preimage.c
@@ -29,9 +29,9 @@
 #include <librsvg/librsvg-features.h>
 #endif
 
-#include <libgames-support/games-preimage.h>
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-string-utils.h>
+#include "ar-preimage.h"
+#include "ar-runtime.h"
+#include "ar-string-utils.h"
 
 #include "ar-card-theme.h"
 #include "ar-card-theme-private.h"
@@ -66,9 +66,9 @@ ar_card_theme_preimage_load (ArCardTheme *card_theme,
 
   /* First the slot image */
   /* FIXMEchpe: use uninstalled data dir for rendering the card theme! */
-  slot_dir = games_runtime_get_directory (GAMES_RUNTIME_PIXMAP_DIRECTORY);
+  slot_dir = ar_runtime_get_directory (AR_RUNTIME_PIXMAP_DIRECTORY);
   path = g_build_filename (slot_dir, "slot.svg", NULL);
-  theme->slot_preimage = games_preimage_new_from_file (path, error);
+  theme->slot_preimage = ar_preimage_new_from_file (path, error);
   g_free (path);
   if (!theme->slot_preimage)
     return FALSE;
@@ -76,21 +76,21 @@ ar_card_theme_preimage_load (ArCardTheme *card_theme,
 
   /* Now the main course */
   path = g_build_filename (theme_info->path, theme_info->filename, NULL);
-  theme->cards_preimage = games_preimage_new_from_file (path, error);
+  theme->cards_preimage = ar_preimage_new_from_file (path, error);
   g_free (path);
   if (!theme->cards_preimage)
     return FALSE;
 
   if (AR_CARD_THEME_PREIMAGE_GET_CLASS (theme)->needs_scalable_cards &&
-      !games_preimage_is_scalable (theme->cards_preimage)) {
+      !ar_preimage_is_scalable (theme->cards_preimage)) {
     g_set_error (error, AR_CARD_THEME_ERROR, AR_CARD_THEME_ERROR_NOT_SCALABLE,
                  "Theme is not scalable");
     return FALSE;
   }
 
   if (theme->font_options) {
-    games_preimage_set_font_options (theme->slot_preimage, theme->font_options);
-    games_preimage_set_font_options (theme->cards_preimage, theme->font_options);
+    ar_preimage_set_font_options (theme->slot_preimage, theme->font_options);
+    ar_preimage_set_font_options (theme->cards_preimage, theme->font_options);
   }
 
   return TRUE;
@@ -212,9 +212,9 @@ ar_card_theme_preimage_get_card_aspect (ArCardTheme* card_theme)
   ArCardThemePreimage *theme = (ArCardThemePreimage *) card_theme;
   double aspect;
 aspect =
-      (((double) games_preimage_get_width (theme->cards_preimage))
+      (((double) ar_preimage_get_width (theme->cards_preimage))
        * N_ROWS) /
-      (((double) games_preimage_get_height (theme->cards_preimage))
+      (((double) ar_preimage_get_height (theme->cards_preimage))
        * N_COLS);
 
   return aspect;
@@ -236,7 +236,7 @@ ar_card_theme_preimage_class_get_theme_info (ArCardThemeClass *klass,
      )
     return NULL;
 
-  display_name = games_filename_to_display_name (filename);
+  display_name = ar_filename_to_display_name (filename);
   info = _ar_card_theme_info_new (G_OBJECT_CLASS_TYPE (klass),
                                      path,
                                      filename,
diff --git a/aisleriot/lib/ar-card-theme-private.h b/aisleriot/lib/ar-card-theme-private.h
index 2b37d14..3a88f88 100644
--- a/aisleriot/lib/ar-card-theme-private.h
+++ b/aisleriot/lib/ar-card-theme-private.h
@@ -19,7 +19,7 @@
 #include "ar-card.h"
 
 #ifdef HAVE_RSVG
-#include <libgames-support/games-preimage.h>
+#include "ar-preimage.h"
 #endif
 
 #define FLOAT_TO_INT_CEIL(f) ((int) (f + 0.5f))
@@ -145,8 +145,8 @@ struct _ArCardThemePreimage {
   char *theme_dir;
   char *theme_name;
 
-  GamesPreimage *cards_preimage;
-  GamesPreimage *slot_preimage;
+  ArPreimage *cards_preimage;
+  ArPreimage *slot_preimage;
   GdkPixbuf *source;
   CardSize subsize;
 
diff --git a/aisleriot/lib/ar-card-theme-pysol.c b/aisleriot/lib/ar-card-theme-pysol.c
index 2750e55..b26f0cc 100644
--- a/aisleriot/lib/ar-card-theme-pysol.c
+++ b/aisleriot/lib/ar-card-theme-pysol.c
@@ -24,9 +24,9 @@
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gtk/gtk.h>
 
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-string-utils.h>
+#include "ar-debug.h"
+#include "ar-runtime.h"
+#include "ar-string-utils.h"
 
 #include "ar-card-theme.h"
 #include "ar-card-theme-private.h"
@@ -360,7 +360,7 @@ ar_card_theme_pysol_get_card_pixbuf (ArCardTheme *card_theme,
 
   pixbuf = gdk_pixbuf_new_from_file (path, &error);
   if (!pixbuf) {
-    _games_debug_print (GAMES_DEBUG_CARD_THEME,
+    ar_debug_print (AR_DEBUG_CARD_THEME,
                         "Failed to load card ID %d: %s\n",
                         card_id, error->message);
     g_error_free (error);
diff --git a/aisleriot/lib/ar-card-theme-sliced.c b/aisleriot/lib/ar-card-theme-sliced.c
index 7a27484..b92a9df 100644
--- a/aisleriot/lib/ar-card-theme-sliced.c
+++ b/aisleriot/lib/ar-card-theme-sliced.c
@@ -25,10 +25,10 @@
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gtk/gtk.h>
 
-#include <libgames-support/games-preimage.h>
-#include <libgames-support/games-profile.h>
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-string-utils.h>
+#include "ar-preimage.h"
+#include "ar-profile.h"
+#include "ar-runtime.h"
+#include "ar-string-utils.h"
 
 #include "ar-card-theme.h"
 #include "ar-card-theme-private.h"
@@ -78,11 +78,11 @@ ar_card_theme_sliced_load (ArCardTheme *card_theme,
     return FALSE;
 
   /* If we don't have a scalable format, build an unscaled pixbuf that we'll cut up later */
-  theme->scalable = games_preimage_is_scalable (preimage_card_theme->cards_preimage);
+  theme->scalable = ar_preimage_is_scalable (preimage_card_theme->cards_preimage);
   if (!theme->scalable) {
-    theme->source = games_preimage_render_unscaled_pixbuf (preimage_card_theme->cards_preimage);
+    theme->source = ar_preimage_render_unscaled_pixbuf (preimage_card_theme->cards_preimage);
 
-    /* This is true because in the non-scalable case GamesPreimage directly holds a GdkPixbuf */
+    /* This is true because in the non-scalable case ArPreimage directly holds a GdkPixbuf */
     g_assert (theme->source != NULL);
   
     theme->subsize.width = gdk_pixbuf_get_width (theme->source) / 13;
@@ -99,13 +99,13 @@ ar_card_theme_sliced_prerender_scalable (ArCardThemeSliced *theme)
 
   g_assert (theme->source == NULL);
 
-  _games_profile_start ("prerendering source pixbuf for %s card theme %s", G_OBJECT_TYPE_NAME (theme), ((ArCardTheme*)theme)->theme_info->display_name);
+  ar_profilestart ("prerendering source pixbuf for %s card theme %s", G_OBJECT_TYPE_NAME (theme), ((ArCardTheme*)theme)->theme_info->display_name);
 
-  theme->source = games_preimage_render (preimage_card_theme->cards_preimage,
+  theme->source = ar_preimage_render (preimage_card_theme->cards_preimage,
                                          preimage_card_theme->card_size.width * 13,
                                          preimage_card_theme->card_size.height * 5);
 
-  _games_profile_end ("prerendering source pixbuf for %s card theme %s", G_OBJECT_TYPE_NAME (theme), ((ArCardTheme*)theme)->theme_info->display_name);
+  ar_profileend ("prerendering source pixbuf for %s card theme %s", G_OBJECT_TYPE_NAME (theme), ((ArCardTheme*)theme)->theme_info->display_name);
 
   if (!theme->source)
     return FALSE;
@@ -126,7 +126,7 @@ ar_card_theme_sliced_get_card_pixbuf (ArCardTheme *card_theme,
   int suit, rank;
 
   if (G_UNLIKELY (card_id == AR_CARD_SLOT)) {
-    subpixbuf = games_preimage_render (preimage_card_theme->slot_preimage,
+    subpixbuf = ar_preimage_render (preimage_card_theme->slot_preimage,
                                        preimage_card_theme->card_size.width,
                                        preimage_card_theme->card_size.height);
 
@@ -196,7 +196,7 @@ ar_card_theme_sliced_class_get_theme_info (ArCardThemeClass *klass,
   if (!g_str_has_suffix (filename, ".png"))
     return NULL;
 
-  name = games_filename_to_display_name (filename);
+  name = ar_filename_to_display_name (filename);
   display_name = g_strdup_printf ("%s (Ugly)", name);
   pref_name = g_strdup_printf ("sliced:%s", filename);
   info = _ar_card_theme_info_new (G_OBJECT_CLASS_TYPE (klass),
@@ -223,7 +223,7 @@ ar_card_theme_sliced_class_foreach_theme_dir (ArCardThemeClass *klass,
     return FALSE;
 
   /* Themes in the pre-2.19 theme format: $(datadir)/pixmaps/gnome-games-common/cards */
-  dir = g_build_filename (games_runtime_get_directory (GAMES_RUNTIME_DATA_DIRECTORY),
+  dir = g_build_filename (ar_runtime_get_directory (AR_RUNTIME_DATA_DIRECTORY),
                           "pixmaps", "gnome-games-common", "cards", NULL);
   retval = callback (klass, dir, data);
   g_free (dir);
@@ -234,7 +234,7 @@ ar_card_theme_sliced_class_foreach_theme_dir (ArCardThemeClass *klass,
   /* If we're installed in a non-system prefix, also load the card themes
    * from the system prefix.
    */
-  if (!games_runtime_is_system_prefix ())
+  if (!ar_runtime_is_system_prefix ())
     return callback (klass, "/usr/share/pixmaps/gnome-games-common/cards", data);
 
   return TRUE;
diff --git a/aisleriot/lib/ar-card-theme-svg.c b/aisleriot/lib/ar-card-theme-svg.c
index 7a05d7b..630e44f 100644
--- a/aisleriot/lib/ar-card-theme-svg.c
+++ b/aisleriot/lib/ar-card-theme-svg.c
@@ -25,10 +25,10 @@
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gtk/gtk.h>
 
-#include <libgames-support/games-preimage.h>
-#include <libgames-support/games-preimage-private.h>
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-string-utils.h>
+#include "ar-preimage.h"
+#include "ar-preimage-private.h"
+#include "ar-runtime.h"
+#include "ar-string-utils.h"
 
 #include "ar-card-theme.h"
 #include "ar-card-theme-private.h"
@@ -55,7 +55,7 @@ ar_card_theme_svg_get_card_pixbuf (ArCardTheme *card_theme,
                                       int card_id)
 {
   ArCardThemePreimage *preimage_card_theme = (ArCardThemePreimage *) card_theme;
-  GamesPreimage *preimage = preimage_card_theme->cards_preimage;
+  ArPreimage *preimage = preimage_card_theme->cards_preimage;
   GdkPixbuf *subpixbuf;
   int suit, rank;
   double card_width, card_height;
@@ -65,7 +65,7 @@ ar_card_theme_svg_get_card_pixbuf (ArCardTheme *card_theme,
   char node[32];
 
   if (G_UNLIKELY (card_id == AR_CARD_SLOT)) {
-    subpixbuf = games_preimage_render (preimage_card_theme->slot_preimage,
+    subpixbuf = ar_preimage_render (preimage_card_theme->slot_preimage,
                                        preimage_card_theme->card_size.width,
                                        preimage_card_theme->card_size.height);
 
@@ -75,8 +75,8 @@ ar_card_theme_svg_get_card_pixbuf (ArCardTheme *card_theme,
   suit = card_id / 13;
   rank = card_id % 13;
 
-  card_width = ((double) games_preimage_get_width (preimage)) / N_COLS;
-  card_height = ((double) games_preimage_get_height (preimage)) / N_ROWS;
+  card_width = ((double) ar_preimage_get_width (preimage)) / N_COLS;
+  card_height = ((double) ar_preimage_get_height (preimage)) / N_ROWS;
 
   width = preimage_card_theme->card_size.width - 2 * DELTA;
   height = preimage_card_theme->card_size.height - 2 * DELTA;
@@ -89,7 +89,7 @@ ar_card_theme_svg_get_card_pixbuf (ArCardTheme *card_theme,
 
   ar_card_get_node_by_suit_and_rank_snprintf (node, sizeof (node), suit, rank);
 
-  subpixbuf = games_preimage_render_sub (preimage,
+  subpixbuf = ar_preimage_render_sub (preimage,
                                          node,
                                          preimage_card_theme->card_size.width,
                                          preimage_card_theme->card_size.height,
@@ -106,7 +106,7 @@ ar_card_theme_svg_paint_card (ArCardTheme *card_theme,
                               int card_id)
 {
   ArCardThemePreimage *preimage_card_theme = (ArCardThemePreimage *) card_theme;
-  GamesPreimage *preimage = preimage_card_theme->cards_preimage;
+  ArPreimage *preimage = preimage_card_theme->cards_preimage;
   int suit, rank;
   double card_width, card_height;
   double width, height;
@@ -115,7 +115,7 @@ ar_card_theme_svg_paint_card (ArCardTheme *card_theme,
   char node[32];
 
   if (G_UNLIKELY (card_id == AR_CARD_SLOT)) {
-    games_preimage_render_cairo (preimage_card_theme->slot_preimage,
+    ar_preimage_render_cairo (preimage_card_theme->slot_preimage,
                                  cr,
                                  preimage_card_theme->card_size.width,
                                  preimage_card_theme->card_size.height);
@@ -125,8 +125,8 @@ ar_card_theme_svg_paint_card (ArCardTheme *card_theme,
   suit = card_id / 13;
   rank = card_id % 13;
 
-  card_width = ((double) games_preimage_get_width (preimage)) / N_COLS;
-  card_height = ((double) games_preimage_get_height (preimage)) / N_ROWS;
+  card_width = ((double) ar_preimage_get_width (preimage)) / N_COLS;
+  card_height = ((double) ar_preimage_get_height (preimage)) / N_ROWS;
 
   width = preimage_card_theme->card_size.width - 2 * DELTA;
   height = preimage_card_theme->card_size.height - 2 * DELTA;
@@ -139,7 +139,7 @@ ar_card_theme_svg_paint_card (ArCardTheme *card_theme,
 
   ar_card_get_node_by_suit_and_rank_snprintf (node, sizeof (node), suit, rank);
 
-  games_preimage_render_cairo_sub (preimage,
+  ar_preimage_render_cairo_sub (preimage,
                                    cr,
                                    node,
                                    preimage_card_theme->card_size.width,
@@ -184,13 +184,13 @@ ar_card_theme_svg_class_foreach_theme_dir (ArCardThemeClass *klass,
   if (!_ar_card_theme_class_foreach_env (klass, "AR_CARD_THEME_PATH_SVG", callback, data))
     return FALSE;
 
-  if (!callback (klass, games_runtime_get_directory (GAMES_RUNTIME_SCALABLE_CARDS_DIRECTORY), data))
+  if (!callback (klass, ar_runtime_get_directory (AR_RUNTIME_SCALABLE_CARDS_DIRECTORY), data))
     return FALSE;
 
   /* If we're installed in a non-system prefix, also load the card themes
    * from the system prefix.
    */
-  if (!games_runtime_is_system_prefix ())
+  if (!ar_runtime_is_system_prefix ())
     return callback (klass, "/usr/share/gnome-games-common/cards", data);
 
   return TRUE;
diff --git a/aisleriot/lib/ar-card-theme.c b/aisleriot/lib/ar-card-theme.c
index a56845a..2b7455b 100644
--- a/aisleriot/lib/ar-card-theme.c
+++ b/aisleriot/lib/ar-card-theme.c
@@ -25,9 +25,9 @@
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gtk/gtk.h>
 
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-profile.h>
-#include <libgames-support/games-runtime.h>
+#include "ar-debug.h"
+#include "ar-profile.h"
+#include "ar-runtime.h"
 
 #include "ar-card-theme.h"
 #include "ar-card-theme-private.h"
@@ -442,11 +442,11 @@ ar_card_theme_get_card_pixbuf (ArCardTheme *theme,
 
   g_return_val_if_fail ((card_id >= 0) && (card_id < AR_CARDS_TOTAL), NULL);
 
-  _games_profile_start ("loading card %d from theme %s", card_id, theme->theme_info->display_name);
+  ar_profilestart ("loading card %d from theme %s", card_id, theme->theme_info->display_name);
 
   pixbuf = theme->klass->get_card_pixbuf (theme, card_id);
 
-  _games_profile_end ("loading card %d from theme %s", card_id, theme->theme_info->display_name);
+  ar_profileend ("loading card %d from theme %s", card_id, theme->theme_info->display_name);
 
   return pixbuf;
 }
@@ -467,11 +467,11 @@ ar_card_theme_paint_card (ArCardTheme *theme,
 {
   g_return_if_fail ((cardid >= 0) && (cardid < AR_CARDS_TOTAL));
 
-  _games_profile_start ("loading card %d from theme %s", cardid, theme->theme_info->display_name);
+  ar_profilestart ("loading card %d from theme %s", cardid, theme->theme_info->display_name);
 
   theme->klass->paint_card (theme, cr, cardid);
 
-  _games_profile_end ("loading card %d from theme %s", cardid, theme->theme_info->display_name);
+  ar_profileend ("loading card %d from theme %s", cardid, theme->theme_info->display_name);
 }
 #endif /* GTK 2.10 */
 
@@ -558,7 +558,7 @@ _ar_card_theme_info_new (GType type,
   info->data = data;
   info->destroy_notify = destroy_notify;
 
-  _games_debug_print (GAMES_DEBUG_CARD_THEME,
+  ar_debug_print (AR_DEBUG_CARD_THEME,
                       "Created ArCardThemeInfo for type=%s path=%s filename=%s display-name=%s\n",
                       g_type_name (type), path, filename, display_name);
 
diff --git a/aisleriot/lib/ar-card-themes.c b/aisleriot/lib/ar-card-themes.c
index 3b3a594..9637fdc 100644
--- a/aisleriot/lib/ar-card-themes.c
+++ b/aisleriot/lib/ar-card-themes.c
@@ -33,9 +33,9 @@
 #include <gdk/gdkx.h>
 #endif
 
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-profile.h>
-#include <libgames-support/games-runtime.h>
+#include "ar-debug.h"
+#include "ar-profile.h"
+#include "ar-runtime.h"
 
 #include "ar-card-themes.h"
 #include "ar-card-theme-private.h"
@@ -134,7 +134,7 @@ theme_filename_and_type_from_name (const char *theme_name,
 
   g_return_val_if_fail (type != NULL, NULL);
 
-  _games_debug_print (GAMES_DEBUG_CARD_THEME,
+  ar_debug_print (AR_DEBUG_CARD_THEME,
                       "theme_filename_and_type_from_name %s\n",
                       theme_name ? theme_name : "(null)");
 
@@ -199,9 +199,9 @@ ar_card_themes_foreach_theme_dir (GType type,
   if (!klass)
     return TRUE;
 
-  _games_profile_start ("foreach %s card themes", G_OBJECT_CLASS_NAME (klass));
+  ar_profilestart ("foreach %s card themes", G_OBJECT_CLASS_NAME (klass));
   retval = _ar_card_theme_class_foreach_theme_dir (klass, callback, data);
-  _games_profile_end ("foreach %s card themes", G_OBJECT_CLASS_NAME (klass));
+  ar_profileend ("foreach %s card themes", G_OBJECT_CLASS_NAME (klass));
 
   g_type_class_unref (klass);
   return retval;
@@ -252,12 +252,12 @@ ar_card_themes_get_theme_infos_in_dir (ArCardThemeClass *klass,
   GDir *iter;
   const char *filename;
 
-  _games_debug_print (GAMES_DEBUG_CARD_THEME,
+  ar_debug_print (AR_DEBUG_CARD_THEME,
                       "Looking for %s themes in %s\n",
                       G_OBJECT_CLASS_NAME (klass),
                       path);
 
-  _games_profile_start ("looking for %s card themes in %s", G_OBJECT_CLASS_NAME (klass), path);
+  ar_profilestart ("looking for %s card themes in %s", G_OBJECT_CLASS_NAME (klass), path);
 
   iter = g_dir_open (path, 0, NULL);
   if (!iter)
@@ -266,9 +266,9 @@ ar_card_themes_get_theme_infos_in_dir (ArCardThemeClass *klass,
   while ((filename = g_dir_read_name (iter)) != NULL) {
     ArCardThemeInfo *info;
 
-    _games_profile_start ("checking for %s card theme in file %s", G_OBJECT_CLASS_NAME (klass), filename);
+    ar_profilestart ("checking for %s card theme in file %s", G_OBJECT_CLASS_NAME (klass), filename);
     info = _ar_card_theme_class_get_theme_info (klass, path, filename);
-    _games_profile_end ("checking for %s card theme in file %s", G_OBJECT_CLASS_NAME (klass), filename);
+    ar_profileend ("checking for %s card theme in file %s", G_OBJECT_CLASS_NAME (klass), filename);
 
     if (info != NULL) {
       /* Don't replace an already existing theme info! */
@@ -282,7 +282,7 @@ ar_card_themes_get_theme_infos_in_dir (ArCardThemeClass *klass,
   g_dir_close (iter);
 
 out:
-  _games_profile_end ("looking for %s card themes in %s", G_OBJECT_CLASS_NAME (klass), path);
+  ar_profileend ("looking for %s card themes in %s", G_OBJECT_CLASS_NAME (klass), path);
 
   return TRUE;
 }
@@ -297,7 +297,7 @@ ar_card_themes_try_theme_info_by_filename (ArCardThemeClass *klass,
                                               const char *path,
                                               LookupData *data)
 {
-  _games_debug_print (GAMES_DEBUG_CARD_THEME,
+  ar_debug_print (AR_DEBUG_CARD_THEME,
                       "Looking for theme %s/%s in %s\n",
                       G_OBJECT_CLASS_NAME (klass),
                       data->filename,
@@ -313,16 +313,16 @@ ar_card_themes_try_theme_info_by_filename (ArCardThemeClass *klass,
 static void
 ar_card_themes_load_theme_infos (ArCardThemes *theme_manager)
 {
-  _games_debug_print (GAMES_DEBUG_CARD_THEME,
+  ar_debug_print (AR_DEBUG_CARD_THEME,
                       "Scanning theme directories\n");
 
   /* FIXMEchpe: clear the hash table here? */
 
-  _games_profile_start ("looking for card themes");
+  ar_profilestart ("looking for card themes");
   ar_card_themes_foreach_theme_type_and_dir (theme_manager,
                                                 (ArCardThemeForeachFunc) ar_card_themes_get_theme_infos_in_dir,
                                                 theme_manager);
-  _games_profile_end ("looking for card themes");
+  ar_profileend ("looking for card themes");
 
   theme_manager->theme_infos_loaded = TRUE;
 
@@ -384,7 +384,7 @@ theme_install_reply_cb (GDBusConnection  *connection,
 
   variant = g_dbus_connection_call_finish (connection, result, &error);
   if (variant == NULL) {
-    _games_debug_print (GAMES_DEBUG_CARD_THEME,
+    ar_debug_print (AR_DEBUG_CARD_THEME,
                         "Failed to call InstallPackages: %s\n",
                         error->message);
     g_error_free (error);
@@ -502,11 +502,11 @@ ar_card_themes_get_theme (ArCardThemes *theme_manager,
   if (info->type == G_TYPE_INVALID)
     return NULL;
 
-  _games_profile_start ("loading card theme %s/%s", g_type_name (info->type), info->display_name);
+  ar_profilestart ("loading card theme %s/%s", g_type_name (info->type), info->display_name);
 
   theme = g_object_new (info->type, "theme-info", info, NULL);
   if (!theme->klass->load (theme, &error)) {
-    _games_debug_print (GAMES_DEBUG_CARD_THEME,
+    ar_debug_print (AR_DEBUG_CARD_THEME,
                         "Failed to load card theme %s/%s: %s\n",
                         g_type_name (info->type),
                         info->display_name,
@@ -516,13 +516,13 @@ ar_card_themes_get_theme (ArCardThemes *theme_manager,
     g_object_unref (theme);
     theme = NULL;
   } else {
-    _games_debug_print (GAMES_DEBUG_CARD_THEME,
+    ar_debug_print (AR_DEBUG_CARD_THEME,
                         "Successfully loaded card theme %s/%s\n",
                         g_type_name (info->type),
                         info->display_name);
   }
 
-  _games_profile_end ("loading card theme %s/%s", g_type_name (info->type), info->display_name);
+  ar_profileend ("loading card theme %s/%s", g_type_name (info->type), info->display_name);
 
   return theme;
 }
@@ -549,7 +549,7 @@ ar_card_themes_get_theme_by_name (ArCardThemes *theme_manager,
   g_return_val_if_fail (AR_IS_CARD_THEMES (theme_manager), NULL);
 
   filename = theme_filename_and_type_from_name (theme_name, &type);
-  _games_debug_print (GAMES_DEBUG_CARD_THEME,
+  ar_debug_print (AR_DEBUG_CARD_THEME,
                       "Resolved card type=%s filename=%s\n",
                       g_type_name (type),
                       filename);
@@ -600,7 +600,7 @@ ar_card_themes_get_theme_any (ArCardThemes *theme_manager)
 
   g_return_val_if_fail (AR_IS_CARD_THEMES (theme_manager), NULL);
 
-  _games_debug_print (GAMES_DEBUG_CARD_THEME,
+  ar_debug_print (AR_DEBUG_CARD_THEME,
                       "Fallback: trying to load any theme\n");
 
   ar_card_themes_request_themes (theme_manager);
@@ -696,7 +696,7 @@ ar_card_themes_install_themes (ArCardThemes *theme_manager,
 
   connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
   if (connection == NULL) {
-    _games_debug_print (GAMES_DEBUG_CARD_THEME,
+    ar_debug_print (AR_DEBUG_CARD_THEME,
                         "Failed to get the session bus: %s\n",
                         error->message);
     g_error_free (error);
@@ -704,7 +704,7 @@ ar_card_themes_install_themes (ArCardThemes *theme_manager,
   }
 
   key_file = g_key_file_new ();
-  path = games_runtime_get_file (GAMES_RUNTIME_COMMON_DATA_DIRECTORY, "theme-install.ini");
+  path = ar_runtime_get_file (AR_RUNTIME_COMMON_DATA_DIRECTORY, "theme-install.ini");
   if (!g_key_file_load_from_file (key_file, path, 0, NULL)) {
     g_free (path);
     g_key_file_free (key_file);
@@ -741,7 +741,7 @@ ar_card_themes_install_themes (ArCardThemes *theme_manager,
     
     for (j = 0; j < n_packages; ++j) {
       g_variant_builder_add (&builder, "s", packages[j]);
-      _games_debug_print (GAMES_DEBUG_CARD_THEME, "Requesting pkg '%s'\n",
+      ar_debug_print (AR_DEBUG_CARD_THEME, "Requesting pkg '%s'\n",
                           packages[j]);
     }
 
diff --git a/aisleriot/lib/ar-card.h b/aisleriot/lib/ar-card.h
index a56cc26..0d0da14 100644
--- a/aisleriot/lib/ar-card.h
+++ b/aisleriot/lib/ar-card.h
@@ -25,7 +25,7 @@
 
 #include <glib.h>
 
-#include <libgames-support/games-glib-compat.h>
+#include "ar-glib-compat.h"
 
 G_BEGIN_DECLS
 
diff --git a/aisleriot/lib/ar-conf.c b/aisleriot/lib/ar-conf.c
new file mode 100644
index 0000000..76f8e3b
--- /dev/null
+++ b/aisleriot/lib/ar-conf.c
@@ -0,0 +1,1340 @@
+/*
+ *  Copyright © 2007 Christian Persch
+ *
+ *  This program 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope conf it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <errno.h>
+
+#include <gtk/gtk.h>
+
+#if GTK_CHECK_VERSION (2, 90, 7)
+#define GDK_KEY(symbol) GDK_KEY_##symbol
+#else
+#include <gdk/gdkkeysyms.h>
+#define GDK_KEY(symbol) GDK_##symbol
+#endif
+
+#ifdef HAVE_GNOME
+#include <gconf/gconf-client.h>
+#else
+#define ACCELMAP_EXT "accels"
+#endif
+
+#include "ar-debug.h"
+#include "ar-glib-compat.h"
+#include "ar-gtk-compat.h"
+#include "ar-marshal.h"
+
+#include "ar-conf.h"
+
+#define AR_CONF_GET_PRIVATE(that)(G_TYPE_INSTANCE_GET_PRIVATE ((that), AR_TYPE_CONF, ArConfPrivate))
+
+struct ArConfPrivate {
+  char *game_name;
+
+#ifdef HAVE_GNOME
+  GConfClient *gconf_client;
+  char *base_path;
+  gsize base_path_len;
+#else
+  GKeyFile *key_file;
+  char *main_group;
+#endif
+  guint need_init : 1;
+  guint dirty : 1;
+};
+
+enum
+{
+  PROP_0,
+  PROP_GAME_NAME
+};
+
+enum
+{
+  VALUE_CHANGED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static ArConf *instance;
+
+G_DEFINE_TYPE (ArConf, ar_conf, G_TYPE_OBJECT);
+
+/* helper functions */
+
+#define WINDOW_STATE_TIMEOUT 1 /* s */
+
+enum {
+  STATE_KEY_MAXIMISED,
+  STATE_KEY_FULLSCREEN,
+  STATE_KEY_WIDTH,
+  STATE_KEY_HEIGHT,
+  LAST_STATE_KEY
+};
+
+static const char window_state_key_name[][12] = {
+#ifdef HAVE_GNOME
+  "maximized",
+  "fullscreen",
+  "width",
+  "height"
+#else
+  "Maximised",
+  "Fullscreen",
+  "Width",
+  "Height"
+#endif /* HAVE_GNOME */
+};
+
+typedef struct {
+  GtkWindow *window;
+  char *group;
+  guint timeout_id;
+  int width;
+  int height;
+  guint is_maximised : 1;
+  guint is_fullscreen : 1;
+} WindowState;
+
+static gboolean
+window_state_timeout_cb (WindowState *state)
+{
+  ar_conf_set_integer (state->group, window_state_key_name[STATE_KEY_WIDTH], state->width);
+  ar_conf_set_integer (state->group, window_state_key_name[STATE_KEY_HEIGHT], state->height);
+
+  ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                      "[window %p] timeout: persisting width:%d height:%d\n",
+                      state->window,
+                      state->width, state->height);
+
+  state->timeout_id = 0;
+  return FALSE;
+}
+
+static void
+free_window_state (WindowState *state)
+{
+  if (state->timeout_id != 0) {
+    g_source_remove (state->timeout_id);
+
+    /* And store now */
+    window_state_timeout_cb (state);
+  }
+
+  g_free (state->group);
+
+  g_slice_free (WindowState, state);
+}
+
+static gboolean
+window_configure_event_cb (GtkWidget *widget,
+                           GdkEventConfigure *event,
+                           WindowState *state)
+{
+  ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                      "[window %p] configure event current %dx%d new %dx%d [state: is-maximised:%s is-fullscreen:%s]\n",
+                      state->window,
+                      state->width, state->height,
+                      event->width, event->height,
+                      state->is_maximised ? "t" : "f",
+                      state->is_fullscreen ? "t" : "f");
+
+  if (!state->is_maximised && !state->is_fullscreen &&
+      (state->width != event->width || state->height != event->height)) {
+    state->width = event->width;
+    state->height = event->height;
+
+  ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                      "[window %p] scheduling save of new window size\n",
+                      state->window);
+
+    if (state->timeout_id == 0) {
+      state->timeout_id = g_timeout_add_seconds (WINDOW_STATE_TIMEOUT,
+                                                 (GSourceFunc) window_state_timeout_cb,
+                                                 state);
+    }
+  }
+
+  return FALSE;
+}
+
+static gboolean
+window_state_event_cb (GtkWidget *widget,
+                       GdkEventWindowState *event,
+                       WindowState *state)
+{
+  ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                      "[window %p] state event, mask:%x new-state:%x current state: is-maximised:%s is-fullscreen:%s\n",
+                      state->window,
+                      event->changed_mask, event->new_window_state,
+                      state->is_maximised ? "t" : "f",
+                      state->is_fullscreen ? "t" : "f");
+
+  if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) {
+    state->is_maximised = (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0;
+    ar_conf_set_boolean (state->group, window_state_key_name[STATE_KEY_MAXIMISED], state->is_maximised);
+  }
+  if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
+    state->is_fullscreen = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0;
+    ar_conf_set_boolean (state->group, window_state_key_name[STATE_KEY_FULLSCREEN], state->is_fullscreen);
+  }
+
+  ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                      "  > new state: is-maximised:%s is-fullscreen:%s\n",
+                      state->is_maximised ? "t" : "f",
+                      state->is_fullscreen ? "t" : "f");
+
+
+  return FALSE;
+}
+
+#ifndef HAVE_HILDON
+
+static char *
+ar_conf_get_accel_map_path (ArConf *conf,
+                               gboolean ensure_dir_exists)
+{
+  ArConfPrivate *priv = conf->priv;
+  char *game_name, *conf_dir;
+  char *conf_file = NULL;
+  const char *override;
+
+  game_name = g_ascii_strdown (priv->game_name, -1);
+
+#ifdef HAVE_GNOME
+  override = g_getenv ("GNOME22_USER_DIR");
+  if (override)
+    conf_dir = g_build_filename (override, "accels", NULL);
+  else
+    conf_dir = g_build_filename (g_get_home_dir (), ".gnome2", "accels", NULL);
+#else
+  conf_dir = g_build_filename (g_get_user_config_dir (), "gnome-games", NULL);
+#endif
+  if (!conf_dir)
+    goto loser;
+
+  /* Mode 0700 per the XDG basedir spec */
+  if (ensure_dir_exists &&
+      g_mkdir_with_parents (conf_dir, 0700) < 0) {
+    int err = errno;
+
+    if (err != EEXIST) {
+      g_warning ("Failed to create config directory \"%s\": %s\n", conf_dir, g_strerror (err));
+      goto loser;
+    }
+  }
+
+#ifdef HAVE_GNOME
+  conf_file = g_build_filename (conf_dir, game_name, NULL);
+#else
+{
+  char *accelmap_filename;
+
+  accelmap_filename = g_strdup_printf ("%s.%s", game_name, ACCELMAP_EXT);
+  conf_file = g_build_filename (conf_dir, accelmap_filename, NULL);
+  g_free (accelmap_filename);
+}
+#endif
+
+loser:
+  g_free (conf_dir);
+  g_free (game_name);
+
+  return conf_file;
+}
+
+static void
+ar_conf_load_accel_map (ArConf *conf)
+{
+  char *conf_file;
+
+  conf_file = ar_conf_get_accel_map_path (conf, FALSE);
+  if (!conf_file)
+    return;
+
+  gtk_accel_map_load (conf_file);
+  g_free (conf_file);
+}
+
+static void
+ar_conf_save_accel_map (ArConf *conf)
+{
+  char *conf_file;
+
+  conf_file = ar_conf_get_accel_map_path (conf, TRUE);
+  if (!conf_file)
+    return;
+
+  gtk_accel_map_save (conf_file);
+  g_free (conf_file);
+}
+
+#endif /* !HAVE_HILDON */
+
+#ifdef HAVE_GNOME
+
+static void
+gconf_notify_cb (GConfClient *client,
+                 guint cnxn_id,
+                 GConfEntry *gcentry,
+                 ArConf *conf)
+{
+  ArConfPrivate *priv = conf->priv;
+  char *key;
+  char **path;
+
+  if (!g_str_has_prefix (gcentry->key, priv->base_path))
+    return;
+
+  key = gcentry->key + priv->base_path_len;
+  if (*key != '/')
+    return;
+
+  path = g_strsplit (key + 1, "/", 2);
+  if (!path)
+    return;
+
+  if (path[0] && path[1])
+    g_signal_emit (conf, signals[VALUE_CHANGED], 0, path[0], path[1]);
+  else if (path[0])
+    g_signal_emit (conf, signals[VALUE_CHANGED], 0, NULL, path[0]);
+
+  g_strfreev (path);
+}
+
+static char *
+get_gconf_key_name (const char *group, const char *key)
+{
+  ArConfPrivate *priv = instance->priv;
+
+  if (!group)
+    return g_strdup_printf ("%s/%s", priv->base_path, key);
+
+  return g_strdup_printf ("%s/%s/%s", priv->base_path, group, key);
+}
+
+static GConfValueType
+get_gconf_value_type_from_schema (const char *key_name)
+{
+  ArConfPrivate *priv = instance->priv;
+  GConfSchema *schema;
+  char *schema_key;
+  GConfValueType type = GCONF_VALUE_STRING;
+
+  schema_key = g_strconcat ("/schemas", key_name, NULL);
+  schema = gconf_client_get_schema (priv->gconf_client, schema_key, NULL);
+
+  if (schema) {
+    type = gconf_schema_get_type (schema);
+    gconf_schema_free (schema);
+  }
+
+  g_free (schema_key);
+
+  return type;
+}
+
+#endif /* HAVE_GNOME */
+
+#ifndef HAVE_GNOME
+
+static void
+mark_dirty_cb (ArConf *conf)
+{
+  ArConfPrivate *priv = conf->priv;
+
+  priv->dirty = TRUE;
+}
+
+#endif /* !HAVE_GNOME */
+
+/* Class implementation */
+
+static void
+ar_conf_init (ArConf *conf)
+{
+  ArConfPrivate *priv;
+
+  priv = conf->priv = AR_CONF_GET_PRIVATE (conf);
+
+  priv->need_init = FALSE;
+  priv->dirty = FALSE;
+
+#ifndef HAVE_GNOME
+  g_signal_connect (conf, "value-changed", G_CALLBACK (mark_dirty_cb), NULL);
+#endif
+}
+
+static GObject *
+ar_conf_constructor (GType type,
+			  guint n_construct_properties,
+			  GObjectConstructParam *construct_params)
+{
+  GObject *object;
+  ArConf *conf;
+  ArConfPrivate *priv;
+  char *game_name;
+#ifndef HAVE_GNOME
+  char *conf_file;
+  GError *error = NULL;
+#endif /* HAVE_GNOME */
+
+  g_assert (instance == NULL);
+
+  object = G_OBJECT_CLASS (ar_conf_parent_class)->constructor
+             (type, n_construct_properties, construct_params);
+
+  conf = AR_CONF (object);
+  priv = conf->priv;
+
+  g_assert (priv->game_name);
+
+  game_name = g_ascii_strdown (priv->game_name, -1);
+
+#ifdef HAVE_GNOME
+  priv->gconf_client = gconf_client_get_default ();
+
+  priv->base_path = g_strdup_printf ("/apps/%s", game_name);
+  priv->base_path_len = strlen (priv->base_path);
+
+  gconf_client_add_dir (priv->gconf_client, priv->base_path,
+                        GCONF_CLIENT_PRELOAD_NONE, NULL);
+
+  gconf_client_notify_add (priv->gconf_client,
+                           priv->base_path,
+                           (GConfClientNotifyFunc) gconf_notify_cb,
+                           conf, NULL,
+                           NULL);
+
+#else /* !HAVE_GNOME */
+
+  priv->main_group = g_strdup_printf ("%s Config", priv->game_name);
+
+  conf_file = g_build_filename (g_get_user_config_dir (), "gnome-games", game_name, NULL);
+
+  priv->key_file = g_key_file_new ();
+  if (!g_key_file_load_from_file (priv->key_file, conf_file, 0, &error)) {
+    /* Don't warn on non-existent file */
+    if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
+      g_warning ("Failed to read settings from \"%s\": %s",
+                  conf_file, error->message);
+    }
+
+    g_error_free (error);
+
+    priv->need_init = TRUE;
+  }
+
+  g_free (conf_file);
+
+#endif /* HAVE_GNOME */
+
+#ifndef HAVE_HILDON
+  ar_conf_load_accel_map (conf);
+#endif /* !HAVE_HILDON */
+
+  g_free (game_name);
+
+  return object;
+}
+
+static void
+ar_conf_finalize (GObject *object)
+{
+  ArConf *conf = AR_CONF (object);
+  ArConfPrivate *priv = conf->priv;
+
+#ifndef HAVE_HILDON
+  /* Save the accel map */
+  ar_conf_save_accel_map (conf);
+#endif /* !HAVE_HILDON */
+
+#ifdef HAVE_GNOME
+  gconf_client_remove_dir (priv->gconf_client, priv->base_path, NULL);
+
+  g_free (priv->base_path);
+
+  g_object_unref (priv->gconf_client);
+  priv->gconf_client = NULL;
+
+#else /* !HAVE_GNOME */
+
+  ar_conf_save ();
+
+  g_free (priv->main_group);
+  g_key_file_free (priv->key_file);
+
+#endif /* HAVE_GNOME */
+
+  g_free (priv->game_name);
+
+  G_OBJECT_CLASS (ar_conf_parent_class)->finalize (object);
+
+  instance = NULL;
+}
+
+static void
+ar_conf_set_property (GObject *object,
+			   guint prop_id,
+			   const GValue *value,
+			   GParamSpec *pspec)
+{
+  ArConf *conf = AR_CONF (object);
+  ArConfPrivate *priv = conf->priv;
+
+  switch (prop_id) {
+    case PROP_GAME_NAME:
+      priv->game_name = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+ar_conf_class_init (ArConfClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GType string_types[] = {
+    G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
+    G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE
+  };
+
+  gobject_class->constructor = ar_conf_constructor;
+  gobject_class->finalize = ar_conf_finalize;
+  gobject_class->set_property = ar_conf_set_property;
+
+  signals[VALUE_CHANGED] =
+    g_signal_newv ("value-changed",
+                   G_OBJECT_CLASS_TYPE (gobject_class),
+                   (GSignalFlags) (G_SIGNAL_RUN_LAST),
+                   NULL,
+                   NULL, NULL,
+                   ar_marshal_VOID__STRING_STRING,
+                   G_TYPE_NONE,
+                   2, string_types);
+
+  g_object_class_install_property
+    (gobject_class,
+     PROP_GAME_NAME,
+     g_param_spec_string ("game-name", NULL, NULL,
+                          NULL,
+                          G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
+                          G_PARAM_CONSTRUCT_ONLY));
+
+  g_type_class_add_private (gobject_class, sizeof (ArConfPrivate));
+}
+
+/* public API */
+
+/**
+ * ar_conf_initialise:
+ * @game_name: the name of the game
+ *
+ * Initialises the default #ArConf instance.
+ *
+ * Returns: %TRUE if @game_name had saved settings; %FALSE otherwise
+ */
+gboolean
+ar_conf_initialise (const char *game_name)
+{
+  instance = g_object_new (AR_TYPE_CONF,
+                           "game-name", game_name,
+                           NULL);
+
+#ifdef HAVE_GNOME
+  /* GConf uses ORBit2 which needs threads (but it's too late to call
+   * g_thread_init() here). See bug #547885.
+   */
+  g_assert (g_thread_supported ());
+#endif
+
+  return !instance->priv->need_init;
+}
+
+/**
+ * ar_conf_shutdown:
+ *
+ * Shuts down the default #ArConf instance.
+ */
+void
+ar_conf_shutdown (void)
+{
+  g_assert (instance != NULL);
+
+  g_object_unref (instance);
+  instance = NULL;
+}
+
+/**
+ * ar_conf_get_default:
+ *
+ * Returns the default #ArConf instance. ar_conf_init() must have
+ * been called before this!
+ *
+ * Returns: (transfer none): a #ArConf (no reference)
+ */
+ArConf *
+ar_conf_get_default (void)
+{
+  g_assert (instance != NULL);
+
+  return instance;
+}
+
+/**
+ * ar_conf_save:
+ *
+ * Ensures settings are written to disk.
+ */
+void
+ar_conf_save (void)
+{
+#ifndef HAVE_GNOME
+  ArConfPrivate *priv = instance->priv;
+  char *game_name, *conf_file, *conf_dir, *data = NULL;
+  gsize len = 0;
+  GError *error = NULL;
+
+  if (!priv->dirty)
+    return;
+
+  game_name = g_ascii_strdown (priv->game_name, -1);
+  conf_dir = g_build_filename (g_get_user_config_dir (), "gnome-games", NULL);
+  conf_file = g_build_filename (conf_dir, game_name, NULL);
+
+  /* Ensure the directory exists; mode 0700 per the XDG basedir spec. */
+  if (g_mkdir_with_parents (conf_dir, 0700) < 0) {
+    int err = errno;
+
+    if (err != EEXIST) {
+      g_warning ("Failed to create config directory \"%s\": %s\n", conf_dir, g_strerror (err));
+      goto loser;
+    }
+  }
+
+  data = g_key_file_to_data (priv->key_file, &len, &error);
+  if (!data) {
+    g_warning ("Failed to save settings to \"%s\": %s",
+               conf_file, error->message);
+    g_error_free (error);
+    goto loser;
+  }
+
+  if (!g_file_set_contents (conf_file, data, len, &error)) {
+    g_warning ("Failed to save settings to \"%s\": %s",
+              conf_file, error->message);
+    g_error_free (error);
+    goto loser;
+  }
+
+  /* Sucessfully saved */
+  priv->dirty = FALSE;
+
+loser:
+  g_free (data);
+  g_free (conf_file);
+  g_free (conf_dir);
+  g_free (game_name);
+#endif /* !HAVE_GNOME */
+}
+
+/**
+ * ar_conf_get_string:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @error: (allow-none): a location for a #GError
+ *
+ * Returns the string associated with @key in @group, or %NULL if
+ * @key is not set, or an error occurred
+ *
+ * Returns: a newly allocated string, or %NULL
+ */
+char *
+ar_conf_get_string (const char *group, const char *key,
+                       GError ** error)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  char *key_name, *value;
+
+  key_name = get_gconf_key_name (group, key);
+  value = gconf_client_get_string (priv->gconf_client, key_name, NULL);
+  g_free (key_name);
+
+  return value;
+#else
+  return g_key_file_get_string (priv->key_file, group ? group : priv->main_group, key, error);
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_get_string_with_default:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @def_value: the default value
+ *
+ * Returns the string associated with @key in @group, or a copy of
+ * @def_value if @key is not set, or an error occurred
+ *
+ * Returns: a newly allocated string
+ */
+char *
+ar_conf_get_string_with_default (const char *group, const char *key,
+                                    const char *def_value)
+{
+  GError *error = NULL;
+  char *value;
+
+  value = ar_conf_get_string (group, key, &error);
+  if (value)
+    return value;
+
+  if (error) {
+    g_error_free (error);
+  }
+
+  return g_strdup (def_value);
+}
+
+/**
+ * ar_conf_set_string:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @value: the value to store
+ *
+ * Associates @value with the key @key in group @group.
+ */
+void
+ar_conf_set_string (const char *group, const char *key,
+                       const char *value)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  char *key_name;
+
+  key_name = get_gconf_key_name (group, key);
+  gconf_client_set_string (priv->gconf_client, key_name, value, NULL);
+  g_free (key_name);
+#else
+  g_key_file_set_string (priv->key_file, group ? group : priv->main_group, key, value);
+  g_signal_emit (instance, signals[VALUE_CHANGED], 0, group, key);
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_get_string_list:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @n_values: a location to store the length of the returned array
+ * @error: (allow-none): a location for a #GError
+ *
+ * Returns the string array associated with @key in @group, or %NULL if
+ * @key is not set, or an error occurred
+ *
+ * Returns: (transfer full): a newly allocated string array, or %NULL
+ */
+char **
+ar_conf_get_string_list (const char *group, const char *key,
+                            gsize * n_values, GError ** error)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  char *key_name;
+  GSList *list, *l;
+  char **values = NULL;
+  gsize n = 0;
+
+  key_name = get_gconf_key_name (group, key);
+
+  list = gconf_client_get_list (priv->gconf_client, key_name, GCONF_VALUE_STRING, NULL);
+  if (list != NULL) {
+    values = g_new (char *, g_slist_length (list) + 1);
+
+    for (l = list; l != NULL; l = l->next) {
+      values[n++] = l->data;
+    }
+
+    /* NULL termination */
+    values[n] = NULL;
+
+    g_slist_free (list); /* the strings themselves are now owned by the array */
+  }
+
+  *n_values = n;
+  
+  g_free (key_name);
+  return values;
+#else
+  return g_key_file_get_string_list (priv->key_file, group ? group : priv->main_group, key, n_values, error);
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_set_string_list:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @values: the value to store
+ * @n_values: the length of the @values array
+ *
+ * Associates @value with the key @key in group @group.
+ */
+void
+ar_conf_set_string_list (const char *group, const char *key,
+                            const char * const *values, gsize n_values)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  char *key_name;
+  GSList *list = NULL;
+  gsize i;
+
+  key_name = get_gconf_key_name (group, key);
+
+  for (i = 0; i < n_values; ++i) {
+    list = g_slist_prepend (list, (gpointer) values[i]);
+  }
+  list = g_slist_reverse (list);
+
+  gconf_client_set_list (priv->gconf_client, key_name, GCONF_VALUE_STRING, list, NULL);
+
+  g_slist_free (list);
+
+  g_free (key_name);
+#else
+  g_key_file_set_string_list (priv->key_file, group ? group : priv->main_group, key, values, n_values);
+  g_signal_emit (instance, signals[VALUE_CHANGED], 0, group, key);
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_get_integer:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @error: (allow-none): a location for a #GError
+ *
+ * Returns the integer associated with @key in @group, or 0 if
+ * @key is not set, or an error occurred
+ *
+ * Returns: an integer
+ */
+int
+ar_conf_get_integer (const char *group, const char *key,
+                        GError ** error)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  int value;
+  char *key_name;
+
+  key_name = get_gconf_key_name (group, key);
+  value = gconf_client_get_int (priv->gconf_client, key_name, error);  
+  g_free (key_name);
+
+  return value;
+#else
+  return g_key_file_get_integer (priv->key_file, group ? group : priv->main_group, key, error);
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_get_integer_with_default:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @def_value: the default value
+ *
+ * Returns the integer associated with @key in @group, or @def_value if
+ * @key is not set, or an error occurred
+ *
+ * Returns: an integer
+ */
+int
+ar_conf_get_integer_with_default (const char *group, const char *key,
+                                     int def_value)
+{
+  GError *error = NULL;
+  int value;
+
+  value = ar_conf_get_integer (group, key, &error);
+  if (error) {
+    g_error_free (error);
+    value = def_value;
+  }
+
+  return value;
+}
+
+
+/**
+ * ar_conf_set_integer:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @value: the value to store
+ *
+ * Associates @value with the key @key in group @group.
+ */
+void
+ar_conf_set_integer (const char *group, const char *key, int value)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  char *key_name;
+
+  key_name = get_gconf_key_name (group, key);
+  gconf_client_set_int (priv->gconf_client, key_name, value, NULL);
+  g_free (key_name);
+#else
+  g_key_file_set_integer (priv->key_file, group ? group : priv->main_group, key, value);
+  g_signal_emit (instance, signals[VALUE_CHANGED], 0, group, key);
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_get_integer_list:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @n_values: a location to store the length of the returned array
+ * @error: (allow-none): a location for a #GError
+ *
+ * Returns the integer associated with @key in @group, or 0 if
+ * @key is not set, or an error occurred
+ *
+ * Returns: an integer
+ */
+int *
+ar_conf_get_integer_list (const char *group, const char *key,
+                             gsize * n_values, GError ** error)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  char *key_name;
+  GSList *list, *l;
+  int *values = NULL;
+  gsize n = 0;
+
+  key_name = get_gconf_key_name (group, key);
+
+  list = gconf_client_get_list (priv->gconf_client, key_name, GCONF_VALUE_STRING, NULL);
+  if (list != NULL) {
+    values = g_new (int, g_slist_length (list));
+
+    for (l = list; l != NULL; l = l->next) {
+      values[n++] = GPOINTER_TO_INT (l->data);
+    }
+  }
+
+  *n_values = n;
+  
+  g_free (key_name);
+  return values;
+#else
+  return g_key_file_get_integer_list (priv->key_file, group ? group : priv->main_group, key, n_values, error);
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_set_integer_list:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @values: the value to store
+ * @n_values: the length of the @values array
+ *
+ * Associates @value with the key @key in group @group.
+ */
+void
+ar_conf_set_integer_list (const char *group, const char *key,
+                             int *values, gsize n_values)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  char *key_name;
+  GSList *list = NULL;
+  gsize i;
+
+  key_name = get_gconf_key_name (group, key);
+
+  for (i = 0; i < n_values; ++i) {
+    list = g_slist_prepend (list, GINT_TO_POINTER (values[i]));
+  }
+  list = g_slist_reverse (list);
+
+  gconf_client_set_list (priv->gconf_client, key_name, GCONF_VALUE_INT, list, NULL);
+
+  g_slist_free (list);
+
+  g_free (key_name);
+#else
+  g_key_file_set_integer_list (priv->key_file, group ? group : priv->main_group, key, values, n_values);
+  g_signal_emit (instance, signals[VALUE_CHANGED], 0, group, key);
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_get_boolean:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @error: (allow-none): a location for a #GError
+ *
+ * Returns the boolean associated with @key in @group, or %FALSE if
+ * @key is not set, or an error occurred
+ *
+ * Returns: a boolean
+ */
+gboolean
+ar_conf_get_boolean (const char *group, const char *key,
+                        GError ** error)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  gboolean value;
+  char *key_name;
+
+  key_name = get_gconf_key_name (group, key);
+  value = gconf_client_get_bool (priv->gconf_client, key_name, error);
+  g_free (key_name);
+
+  return value;
+#else
+  return g_key_file_get_boolean (priv->key_file, group ? group : priv->main_group, key, error);
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_get_boolean_with_default:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @def_value: the default value
+ *
+ * Returns the boolean associated with @key in @group, or @def_value if
+ * @key is not set, or an error occurred
+ *
+ * Returns: a boolean
+ */
+gboolean
+ar_conf_get_boolean_with_default (const char *group, const char *key,
+                                     gboolean def_value)
+{
+  GError *error = NULL;
+  gboolean value;
+
+  value = ar_conf_get_boolean (group, key, &error);
+  if (error) {
+    g_error_free (error);
+    value = def_value;
+  }
+
+  return value;
+}
+
+/**
+ * ar_conf_set_boolean:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @value: the value to store
+ *
+ * Associates @value with the key @key in group @group.
+ */
+void
+ar_conf_set_boolean (const char *group, const char *key,
+                        gboolean value)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  char *key_name;
+
+  key_name = get_gconf_key_name (group, key);
+  gconf_client_set_bool (priv->gconf_client, key_name, value, NULL);
+  g_free (key_name);
+#else
+  g_key_file_set_boolean (priv->key_file, group ? group : priv->main_group, key, value);
+  g_signal_emit (instance, signals[VALUE_CHANGED], 0, group, key);
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_get_double:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @error: a location for a #GError
+ *
+ * Returns the value associated with @key in @group, or 0 if
+ * @key is not set, or an error occurred
+ *
+ * Returns: a double
+ */
+double
+ar_conf_get_double (const char *group, const char *key,
+                       GError ** error)
+{
+#if defined(HAVE_GNOME)
+  ArConfPrivate *priv = instance->priv;
+  double value;
+  char *key_name;
+
+  key_name = get_gconf_key_name (group, key);
+  value = gconf_client_get_float (priv->gconf_client, key_name, error);
+  g_free (key_name);
+
+  return value;
+#elif GLIB_CHECK_VERSION (2, 12, 0)
+  ArConfPrivate *priv = instance->priv;
+
+  return g_key_file_get_double (priv->key_file, group ? group : priv->main_group, key, error);
+#else
+#warning ar_conf_get_double not implemented on glib < 2.12!
+  /* Not supported */
+  return 0.0;
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_set_double:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @value: the value to store
+ *
+ * Associates @value with the key @key in group @group.
+ */
+void
+ar_conf_set_double (const char *group, const char *key, double value)
+{
+#if defined(HAVE_GNOME)
+  ArConfPrivate *priv = instance->priv;
+  char *key_name;
+
+  key_name = get_gconf_key_name (group, key);
+  gconf_client_set_float (priv->gconf_client, key_name, value, NULL);
+  g_free (key_name);
+#elif GLIB_CHECK_VERSION (2, 12, 0)
+  ArConfPrivate *priv = instance->priv;
+
+  g_key_file_set_double (priv->key_file, group ? group : priv->main_group, key, value);
+  g_signal_emit (instance, signals[VALUE_CHANGED], 0, group, key);
+#else
+#warning ar_conf_set_double not implemented on glib < 2.12!
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_get_keyval:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @error: (allow-none): a location for a #GError
+ *
+ * Returns the keyboard key associated with @key in @group, or 0 if
+ * @key is not set, or an error occurred
+ *
+ * Returns: a keyboard key value
+ */
+guint
+ar_conf_get_keyval (const char *group, const char *key,
+                       GError ** error)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  GConfValueType type;
+  char *key_name, *value;
+  guint keyval = GDK_KEY (VoidSymbol);
+
+  key_name = get_gconf_key_name (group, key);
+  type = get_gconf_value_type_from_schema (key_name);
+
+  /* The result could be a keycode or a key name. */
+  if (type == GCONF_VALUE_STRING) {
+    value = gconf_client_get_string (priv->gconf_client, key_name, error);
+    if (!value) {
+      keyval = GDK_KEY (VoidSymbol);
+    } else {
+      keyval = gdk_keyval_from_name (value);
+      g_free (value);
+    }
+  } else if (type == GCONF_VALUE_INT) {
+    keyval = gconf_client_get_int (priv->gconf_client, key_name, error);
+    if (*error || keyval == 0)
+      keyval = GDK_KEY (VoidSymbol);
+  } else {
+    g_warning ("Unknown value type for key %s\n", key_name);
+  }
+
+  g_free (key_name);
+
+  return keyval;
+#else
+  char *value;
+  guint keyval = GDK_KEY (VoidSymbol);
+
+  value = g_key_file_get_string (priv->key_file, group, key, error);
+  if (value) {
+    keyval = gdk_keyval_from_name (value);
+    g_free (value);
+  }
+
+  return keyval;
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_get_keyval_with_default:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @default_keyval: the default value
+ *
+ * Returns the keyboard key associated with @key in @group, or @default_keyval
+ * if @key is not set, or an error occurred
+ *
+ * Returns: a keyboard key value
+ */
+guint
+ar_conf_get_keyval_with_default (const char *group, const char *key,
+                                    guint default_keyval)
+{
+  GError *error = NULL;
+  guint value;
+
+  value = ar_conf_get_keyval (group, key, &error);
+  if (error) {
+    g_error_free (error);
+    value = default_keyval;
+  }
+  if (value == GDK_KEY (VoidSymbol)) {
+    value = default_keyval;
+  }
+
+  return value;
+}
+
+/**
+ * ar_conf_set_keyval:
+ * @group: (allow-none): the group name, or %NULL to use the default group
+ * @key: the key name
+ * @value: the value to store
+ *
+ * Associates @value with the key @key in group @group.
+ */
+void
+ar_conf_set_keyval (const char *group, const char *key, guint value)
+{
+  ArConfPrivate *priv = instance->priv;
+
+#ifdef HAVE_GNOME
+  GConfValueType type;
+  char *key_name, *name;
+
+  if (value == GDK_KEY (VoidSymbol))
+    return;
+
+  key_name = get_gconf_key_name (group, key);
+  type = get_gconf_value_type_from_schema (key_name);
+
+  /* The result could be a keycode or a key name. */
+  if (type == GCONF_VALUE_STRING) {
+    name = gdk_keyval_name (value);
+    gconf_client_set_string (priv->gconf_client, key_name, name, NULL);
+  } else if (type == GCONF_VALUE_INT) {
+    gconf_client_set_int (priv->gconf_client, key_name, (int) value, NULL);
+  } else {
+    g_warning ("Unknown value type for key %s\n", key_name);
+  }
+
+  g_free (key_name);
+#else
+  char *name;
+
+  if (value == GDK_KEY (VoidSymbol))
+    return;
+  
+  name = gdk_keyval_name (value);
+  g_key_file_set_string (priv->key_file, group, key, name);
+  g_signal_emit (instance, signals[VALUE_CHANGED], 0, group, key);
+#endif /* HAVE_GNOME */
+}
+
+/**
+ * ar_conf_add_window:
+ * @window: a #GtkWindow
+ * @group: (allow-none): the group to store the state in, or %NULL to use
+ * the default group
+ * 
+ * Restore the window configuration, and persist changes to the window configuration:
+ * window width and height, and maximised and fullscreen state.
+ * @window must not be realised yet.
+ */
+void
+ar_conf_add_window (GtkWindow *window,
+                       const char *group)
+{
+  WindowState *state;
+  int width, height;
+  gboolean maximised, fullscreen;
+
+  g_return_if_fail (GTK_IS_WINDOW (window));
+  g_return_if_fail (!gtk_widget_get_realized (GTK_WIDGET (window)));
+
+  state = g_slice_new0 (WindowState);
+
+  state->window = window;
+  state->group = g_strdup (group);
+  g_object_set_data_full (G_OBJECT (window), "ArConf::WindowState",
+                          state, (GDestroyNotify) free_window_state);
+
+  g_signal_connect (window, "configure-event",
+                    G_CALLBACK (window_configure_event_cb), state);
+  g_signal_connect (window, "window-state-event",
+                    G_CALLBACK (window_state_event_cb), state);
+
+  maximised = ar_conf_get_boolean (group, window_state_key_name[STATE_KEY_MAXIMISED], NULL);
+  fullscreen = ar_conf_get_boolean (group, window_state_key_name[STATE_KEY_FULLSCREEN], NULL);
+  width = ar_conf_get_integer (group, window_state_key_name[STATE_KEY_WIDTH], NULL);
+  height = ar_conf_get_integer (group, window_state_key_name[STATE_KEY_HEIGHT], NULL);
+
+  if (width > 0 && height > 0) {
+    ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                        "[window %p] restoring size %dx%d\n",
+                        state->window,
+                        width, height);
+    gtk_window_set_default_size (GTK_WINDOW (window), width, height);
+  }
+  if (maximised) {
+    ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                        "[window %p] restoring maximised state\n",
+                        state->window);
+    gtk_window_maximize (GTK_WINDOW (window));
+  }
+  if (fullscreen) {
+    ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                        "[window %p] restoring fullscreen state\n",
+                        state->window);
+    gtk_window_fullscreen (GTK_WINDOW (window));
+  }
+}
diff --git a/aisleriot/lib/ar-conf.h b/aisleriot/lib/ar-conf.h
new file mode 100644
index 0000000..6c44584
--- /dev/null
+++ b/aisleriot/lib/ar-conf.h
@@ -0,0 +1,114 @@
+/*
+ *  Copyright © 2007 Christian Persch
+ *
+ *  This program 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope conf it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef AR_CONF_H
+#define AR_CONF_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define AR_TYPE_CONF		(ar_conf_get_type ())
+#define AR_CONF(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), AR_TYPE_CONF, ArConf))
+#define AR_CONF_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), AR_TYPE_CONF, ArConfClass))
+#define AR_IS_CONF(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), AR_TYPE_CONF))
+#define AR_IS_CONF_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), AR_TYPE_CONF))
+#define AR_CONF_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), AR_TYPE_CONF, ArConfClass))
+
+typedef struct ArConfPrivate  ArConfPrivate;
+
+typedef struct {
+  GObject parent_instance;
+  /*< private >*/
+  ArConfPrivate *priv;
+} ArConf;
+
+typedef struct {
+  GObjectClass parent_class;  
+} ArConfClass;
+
+GType      ar_conf_get_type                 (void);
+gboolean   ar_conf_initialise               (const char *game_name);
+void       ar_conf_shutdown                 (void);
+ArConf *ar_conf_get_default              (void);
+void       ar_conf_save                     (void);
+char      *ar_conf_get_string               (const char *group,
+                                                const char *key,
+                                                GError ** error);
+char      *ar_conf_get_string_with_default  (const char *group,
+                                                const char *key,
+                                                const char *def_value);
+void       ar_conf_set_string               (const char *group,
+                                                const char *key,
+                                                const char *value);
+char     **ar_conf_get_string_list          (const char *group,
+                                                const char *key,
+                                                gsize * n_values,
+                                                GError ** error);
+void       ar_conf_set_string_list          (const char *group,
+                                                const char *key,
+                                                const char * const *values,
+                                                gsize n_values);
+int        ar_conf_get_integer              (const char *group,
+                                                const char *key,
+                                                GError ** error);
+int        ar_conf_get_integer_with_default (const char *group,
+                                                const char *key,
+                                                int def_value);
+void       ar_conf_set_integer              (const char *group,
+                                                const char *key,
+                                                int value);
+int       *ar_conf_get_integer_list         (const char *group,
+                                                const char *key,
+                                                gsize * n_values,
+                                                GError ** error);
+void       ar_conf_set_integer_list         (const char *group,
+                                                const char *key,
+                                                int *values,
+                                                gsize n_values);
+gboolean   ar_conf_get_boolean              (const char *group,
+                                                const char *key,
+                                                GError ** error);
+gboolean   ar_conf_get_boolean_with_default (const char *group,
+                                                const char *key,
+                                                gboolean def_value);
+void       ar_conf_set_boolean              (const char *group,
+                                                const char *key,
+                                                gboolean value);
+double     ar_conf_get_double               (const char *group,
+                                                const char *key,
+                                                GError ** error);
+void       ar_conf_set_double               (const char *group,
+                                                const char *key,
+                                                double value);
+guint      ar_conf_get_keyval               (const char *group,
+                                                const char *key,
+                                                GError ** error);
+guint      ar_conf_get_keyval_with_default  (const char *group,
+                                                const char *key,
+                                                guint default_keyval);
+void       ar_conf_set_keyval               (const char *group,
+                                                const char *key,
+                                                guint value);
+void       ar_conf_add_window               (GtkWindow *window,
+                                                const char *group);
+
+G_END_DECLS
+
+#endif /* !AR_CONF_H */
diff --git a/aisleriot/lib/ar-debug.c b/aisleriot/lib/ar-debug.c
new file mode 100644
index 0000000..2587508
--- /dev/null
+++ b/aisleriot/lib/ar-debug.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2002,2003 Red Hat, Inc.
+ *
+ * This 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 program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <glib.h>
+
+#include "ar-debug.h"
+
+#ifdef GNOME_ENABLE_DEBUG
+ArDebugFlags ar_debug_flags;
+#endif
+
+void
+ar_debug_init (void)
+{
+#ifdef GNOME_ENABLE_DEBUG
+  const GDebugKey keys[] = {
+    { "blocks-cache", AR_DEBUG_BLOCKS_CACHE },
+    { "card-cache",   AR_DEBUG_CARD_CACHE   },
+    { "card-theme",   AR_DEBUG_CARD_THEME   },
+    { "runtime",      AR_DEBUG_RUNTIME      },
+    { "scheme",       AR_DEBUG_SCHEME       },
+    { "sound",        AR_DEBUG_SOUND        },
+    { "window-state", AR_DEBUG_WINDOW_STATE },
+
+    { "game-drawing", AR_DEBUG_GAME_DRAWING },
+    { "game-events",  AR_DEBUG_GAME_EVENTS  },
+    { "game-keynav",  AR_DEBUG_GAME_KEYNAV  },
+    { "game-sizing",  AR_DEBUG_GAME_SIZING  },
+    { "game-style",   AR_DEBUG_GAME_STYLE   }
+  };
+  const char *env;
+
+  env = g_getenv ("AR_DEBUG");
+
+#if !GLIB_CHECK_VERSION (2, 16, 0)
+  /* g_parse_debug_string is only NULL-safe since 2.16 */
+  if (env == NULL)
+    return;
+#endif
+
+  ar_debug_flags = g_parse_debug_string (env, keys, G_N_ELEMENTS (keys));
+#endif /* GNOME_ENABLE_DEBUG */
+}
diff --git a/aisleriot/lib/ar-debug.h b/aisleriot/lib/ar-debug.h
new file mode 100644
index 0000000..4482a1f
--- /dev/null
+++ b/aisleriot/lib/ar-debug.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright © 2002 Red Hat, Inc.
+ *
+ * This 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 program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* The interfaces in this file are subject to change at any time. */
+
+#ifndef GNOME_DEBUG_H
+#define GNOME_DEBUG_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define AR_DEBUG_LAST_RESERVED_BIT (8)
+
+typedef enum {
+  AR_DEBUG_BLOCKS_CACHE  = 1 << 0,
+  AR_DEBUG_CARD_CACHE    = 1 << 1,
+  AR_DEBUG_CARD_THEME    = 1 << 2,
+  AR_DEBUG_RUNTIME       = 1 << 3,
+  AR_DEBUG_SCHEME        = 1 << 4,
+  AR_DEBUG_SOUND         = 1 << 5,
+  AR_DEBUG_WINDOW_STATE  = 1 << 6,
+
+  AR_DEBUG_GAME_DRAWING  = 1 << 7,
+  AR_DEBUG_GAME_EVENTS   = 1 << 8,
+  AR_DEBUG_GAME_KEYNAV   = 1 << 9,
+  AR_DEBUG_GAME_SIZING   = 1 << 10,
+  AR_DEBUG_GAME_STYLE    = 1 << 11
+} ArDebugFlags;
+
+#ifdef GNOME_ENABLE_DEBUG
+extern ArDebugFlags ar_debug_flags;
+#endif
+
+void ar_debug_init (void);
+
+static inline gboolean ar_debug_on (ArDebugFlags flags) G_GNUC_CONST G_GNUC_UNUSED;
+
+static inline gboolean
+ar_debug_on (ArDebugFlags flags)
+{
+#ifdef GNOME_ENABLE_DEBUG
+  return (ar_debug_flags & flags) == flags;
+#else
+  return FALSE;
+#endif
+}
+
+#ifdef GNOME_ENABLE_DEBUG
+#define _AR_DEBUG_IF(flags) if (G_UNLIKELY (ar_debug_on (flags)))
+
+#if defined(__GNUC__) && G_HAVE_GNUC_VARARGS
+#define ar_debug_print(flags, fmt, ...) \
+  G_STMT_START { _AR_DEBUG_IF(flags) g_printerr(fmt, ##__VA_ARGS__); } G_STMT_END
+#else
+#include <stdarg.h>
+#include <glib/gstdio.h>
+static void ar_debug_print (guint flags, const char *fmt, ...)
+{
+  if (ar_debug_on (flags)) {
+    va_list  ap;
+    va_start (ap, fmt);
+    g_vfprintf (stderr, fmt, ap);
+    va_end (ap);
+  }
+}
+#endif
+
+#else
+#define _AR_DEBUG_IF(flags) if (0)
+#define ar_debug_print(...)
+#endif /* GNOME_ENABLE_DEBUG */
+
+G_END_DECLS
+
+#endif /* !GNOME_DEBUG_H */
diff --git a/aisleriot/lib/ar-glib-compat.h b/aisleriot/lib/ar-glib-compat.h
new file mode 100644
index 0000000..0e2ba08
--- /dev/null
+++ b/aisleriot/lib/ar-glib-compat.h
@@ -0,0 +1,57 @@
+/*
+ *  Copyright © 2009 Thomas H.P. Andersen <phomes gmail com>
+ *  Copyright © 2010 Christian Persch
+ *
+ *  This runtime 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This runtime is distributed in the hope runtime it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this runtime; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef AR_GLIB_COMPAT_H
+#define AR_GLIB_COMPAT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#if !GLIB_CHECK_VERSION (2, 10, 0)
+
+#define g_slice_new(T) g_new (T, 1)
+#define g_slice_new0(T) g_new0 (T, 1)
+#define g_slice_free(T,ptr) g_free (ptr)
+
+#define g_intern_static_string (string) g_quark_to_string(g_quark_from_static_string(string))
+
+#endif /* GLIB < 2.10 */
+
+#if !GLIB_CHECK_VERSION (2, 14, 0)
+
+#define G_PARAM_STATIC_STRINGS (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)
+
+#define g_timeout_add_seconds(timeout,callback,data) g_timeout_add ((timeout)*1000, callback, data)
+#define gdk_threads_add_timeout_seconds(timeout,callback,data) g_timeout_add ((timeout)*1000, callback, data)
+
+#endif /* GLIB < 2.14 */
+
+#if !GLIB_CHECK_VERSION (2, 20, 0)
+
+#define G_PASTE_ARGS(identifier1,identifier2) identifier1 ## identifier2
+#define G_PASTE(identifier1,identifier2)      G_PASTE_ARGS (identifier1, identifier2)
+#define G_STATIC_ASSERT(expr) typedef struct { char Compile_Time_Assertion[(expr) ? 1 : -1]; } G_PASTE (_GStaticAssert_, __LINE__)
+
+#endif /* GLIB < 2.20 */
+
+G_END_DECLS
+
+#endif /* !AR_GLIB_COMPAT_H */
diff --git a/aisleriot/lib/ar-gsettings.c b/aisleriot/lib/ar-gsettings.c
new file mode 100644
index 0000000..7d6d5aa
--- /dev/null
+++ b/aisleriot/lib/ar-gsettings.c
@@ -0,0 +1,258 @@
+/*
+ *  Copyright © 2007, 2010 Christian Persch
+ *
+ *  This program 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 3, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope conf it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "ar-gsettings.h"
+
+#include <gtk/gtk.h>
+
+#include "ar-gtk-compat.h"
+#include "ar-debug.h"
+
+#define I_(string) g_intern_static_string (string)
+
+#define SCHEMA_NAME           I_("org.gnome.Patience.WindowState")
+
+#define STATE_KEY_MAXIMIZED   I_("maximized")
+#define STATE_KEY_FULLSCREEN  I_("fullscreen")
+#define STATE_KEY_WIDTH       I_("width")
+#define STATE_KEY_HEIGHT      I_("height")
+
+typedef struct {
+  GSettings *settings;
+  GtkWindow *window;
+  int width;
+  int height;
+  guint is_maximised : 1;
+  guint is_fullscreen : 1;
+} WindowState;
+
+static void
+free_window_state (WindowState *state)
+{
+  /* Now store the settings */
+  g_settings_set_int (state->settings, STATE_KEY_WIDTH, state->width);
+  g_settings_set_int (state->settings, STATE_KEY_HEIGHT, state->height);
+  g_settings_set_boolean (state->settings, STATE_KEY_MAXIMIZED, state->is_maximised);
+  g_settings_set_boolean (state->settings, STATE_KEY_FULLSCREEN, state->is_fullscreen);
+
+  g_settings_apply (state->settings);
+
+  g_object_unref (state->settings);
+
+  g_slice_free (WindowState, state);
+}
+
+static gboolean
+window_configure_event_cb (GtkWidget *widget,
+                           GdkEventConfigure *event,
+                           WindowState *state)
+{
+  ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                      "[window %p] configure event current %dx%d new %dx%d [state: is-maximised:%s is-fullscreen:%s]\n",
+                      state->window,
+                      state->width, state->height,
+                      event->width, event->height,
+                      state->is_maximised ? "t" : "f",
+                      state->is_fullscreen ? "t" : "f");
+
+  if (!state->is_maximised && !state->is_fullscreen) {
+    state->width = event->width;
+    state->height = event->height;
+  }
+
+  return FALSE;
+}
+
+static gboolean
+window_state_event_cb (GtkWidget *widget,
+                       GdkEventWindowState *event,
+                       WindowState *state)
+{
+  ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                      "[window %p] state event, mask:%x new-state:%x current state: is-maximised:%s is-fullscreen:%s\n",
+                      state->window,
+                      event->changed_mask, event->new_window_state,
+                      state->is_maximised ? "t" : "f",
+                      state->is_fullscreen ? "t" : "f");
+
+  if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) {
+    state->is_maximised = (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0;
+  }
+  if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
+    state->is_fullscreen = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0;
+  }
+
+  ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                      "  > new state: is-maximised:%s is-fullscreen:%s\n",
+                      state->is_maximised ? "t" : "f",
+                      state->is_fullscreen ? "t" : "f");
+
+
+  return FALSE;
+}
+
+#if 0 //#ifndef HAVE_HILDON
+
+#define ACCELMAP_EXT "accels"
+
+static char *
+ar_conf_get_accel_map_path (GamesConf *conf,
+                               gboolean ensure_dir_exists)
+{
+  GamesConfPrivate *priv = conf->priv;
+  char *game_name, *conf_dir;
+  char *conf_file = NULL;
+  const char *override;
+
+  game_name = g_ascii_strdown (priv->game_name, -1);
+
+#ifdef HAVE_GNOME
+  override = g_getenv ("GNOME22_USER_DIR");
+  if (override)
+    conf_dir = g_build_filename (override, "accels", NULL);
+  else
+    conf_dir = g_build_filename (g_get_home_dir (), ".gnome2", "accels", NULL);
+#else
+  conf_dir = g_build_filename (g_get_user_config_dir (), "gnome-games", NULL);
+#endif
+  if (!conf_dir)
+    goto loser;
+
+  /* Mode 0700 per the XDG basedir spec */
+  if (ensure_dir_exists &&
+      g_mkdir_with_parents (conf_dir, 0700) < 0) {
+    int err = errno;
+
+    if (err != EEXIST) {
+      g_warning ("Failed to create config directory \"%s\": %s\n", conf_dir, g_strerror (err));
+      goto loser;
+    }
+  }
+
+#ifdef HAVE_GNOME
+  conf_file = g_build_filename (conf_dir, game_name, NULL);
+#else
+{
+  char *accelmap_filename;
+
+  accelmap_filename = g_strdup_printf ("%s.%s", game_name, ACCELMAP_EXT);
+  conf_file = g_build_filename (conf_dir, accelmap_filename, NULL);
+  g_free (accelmap_filename);
+}
+#endif
+
+loser:
+  g_free (conf_dir);
+  g_free (game_name);
+
+  return conf_file;
+}
+
+static void
+ar_conf_load_accel_map (GamesConf *conf)
+{
+  char *conf_file;
+
+  conf_file = ar_conf_get_accel_map_path (conf, FALSE);
+  if (!conf_file)
+    return;
+
+  gtk_accel_map_load (conf_file);
+  g_free (conf_file);
+}
+
+static void
+ar_conf_save_accel_map (GamesConf *conf)
+{
+  char *conf_file;
+
+  conf_file = ar_conf_get_accel_map_path (conf, TRUE);
+  if (!conf_file)
+    return;
+
+  gtk_accel_map_save (conf_file);
+  g_free (conf_file);
+}
+
+#endif /* !HAVE_HILDON */
+
+/**
+ * ar_gsettings_bind_window_state:
+ * @path: a valid #GSettings path
+ * @window: a #GtkWindow
+ *
+ * Restore the window configuration, and persist changes to the window configuration:
+ * window width and height, and maximised and fullscreen state.
+ * @window must not be realised yet.
+ *
+ * To make sure the state is saved at exit, g_settings_sync() must be called.
+ */
+void
+ar_gsettings_bind_window_state (const char *path,
+                                  GtkWindow *window)
+{
+  WindowState *state;
+  int width, height;
+  gboolean maximised, fullscreen;
+
+  g_return_if_fail (GTK_IS_WINDOW (window));
+  g_return_if_fail (!gtk_widget_get_realized (GTK_WIDGET (window)));
+
+  state = g_slice_new0 (WindowState);
+
+  state->window = window;
+  state->settings = g_settings_new_with_path (SCHEMA_NAME, path);
+
+  /* We delay storing the state until exit */
+  g_settings_delay (state->settings);
+
+  g_object_set_data_full (G_OBJECT (window), "GamesSettings::WindowState",
+                          state, (GDestroyNotify) free_window_state);
+
+  g_signal_connect (window, "configure-event",
+                    G_CALLBACK (window_configure_event_cb), state);
+  g_signal_connect (window, "window-state-event",
+                    G_CALLBACK (window_state_event_cb), state);
+
+  maximised = g_settings_get_boolean (state->settings, STATE_KEY_MAXIMIZED);
+  fullscreen = g_settings_get_boolean (state->settings, STATE_KEY_FULLSCREEN);
+  width = g_settings_get_int (state->settings, STATE_KEY_WIDTH);
+  height = g_settings_get_int (state->settings, STATE_KEY_HEIGHT);
+
+  if (width > 0 && height > 0) {
+    ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                        "[window %p] restoring size %dx%d\n",
+                        state->window,
+                        width, height);
+    gtk_window_set_default_size (GTK_WINDOW (window), width, height);
+  }
+  if (maximised) {
+    ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                        "[window %p] restoring maximised state\n",
+                        state->window);
+    gtk_window_maximize (GTK_WINDOW (window));
+  }
+  if (fullscreen) {
+    ar_debug_print (AR_DEBUG_WINDOW_STATE,
+                        "[window %p] restoring fullscreen state\n",
+                        state->window);
+    gtk_window_fullscreen (GTK_WINDOW (window));
+  }
+}
diff --git a/aisleriot/lib/ar-gsettings.h b/aisleriot/lib/ar-gsettings.h
new file mode 100644
index 0000000..7714572
--- /dev/null
+++ b/aisleriot/lib/ar-gsettings.h
@@ -0,0 +1,32 @@
+/*
+ *  Copyright © 2007, 2010 Christian Persch
+ *
+ *  This program 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 3, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope conf it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef AR_SETTINGS_H
+#define AR_SETTINGS_H
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void ar_gsettings_bind_window_state (const char *path,
+                                     GtkWindow *window);
+
+G_END_DECLS
+
+#endif /* !AR_GSETTINGS_H */
diff --git a/aisleriot/lib/ar-gtk-compat.h b/aisleriot/lib/ar-gtk-compat.h
new file mode 100644
index 0000000..9970937
--- /dev/null
+++ b/aisleriot/lib/ar-gtk-compat.h
@@ -0,0 +1,51 @@
+/*
+ *  Copyright © 2009 Thomas H.P. Andersen <phomes gmail com>
+ *
+ *  This runtime 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This runtime is distributed in the hope runtime it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this runtime; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef AR_GTK_COMPAT_H
+#define AR_GTK_COMPAT_H
+
+G_BEGIN_DECLS
+#if !GTK_CHECK_VERSION (2, 21, 1)
+#define gdk_visual_get_depth(widget) ((widget)->depth)
+#endif /* GTK < 2.21.1 */
+
+#if !GTK_CHECK_VERSION (2, 20, 0)
+#define gtk_widget_get_realized(widget)                 (GTK_WIDGET_REALIZED(widget))
+#define gtk_widget_set_realized(w,realized)             ((realized) ? (GTK_WIDGET_SET_FLAGS (w, GTK_REALIZED)) : (GTK_WIDGET_UNSET_FLAGS (w, GTK_REALIZED)))
+#define gtk_widget_get_mapped(widget)                   (GTK_WIDGET_MAPPED(widget))
+#endif /* GTK < 2.20.0 */
+
+#if !GTK_CHECK_VERSION (2, 18, 0)
+#define gtk_widget_set_allocation(widget, alloc) ((widget)->allocation=*(alloc))
+#define gtk_widget_get_allocation(widget, alloc) (*(alloc)=(widget)->allocation)
+#define gtk_widget_set_window(widget, window_arg) ((widget)->window=window_arg)
+#define gtk_widget_has_focus(widget) (GTK_WIDGET_HAS_FOCUS (widget))
+#define gtk_widget_get_state(widget) ((widget)->state)
+#define gtk_widget_get_visible(widget) (GTK_WIDGET_VISIBLE (widget))
+#define gtk_widget_is_drawable(widget) (GTK_WIDGET_DRAWABLE (widget))
+#define gtk_widget_set_can_focus(w,can_focus)           ((can_focus) ? (GTK_WIDGET_SET_FLAGS (w, GTK_CAN_FOCUS)) : (GTK_WIDGET_UNSET_FLAGS (w, GTK_CAN_FOCUS)))
+#endif /* GTK < 2.18.0 */
+
+#if !GTK_CHECK_VERSION (2, 14, 0)
+#define gtk_dialog_get_content_area(dialog) ((dialog)->vbox)
+#define gtk_widget_get_window(widget) ((widget)->window)
+#endif /* GTK < 2.14.0 */
+
+G_END_DECLS
+
+#endif /* !AR_GTK_COMPAT_H */
diff --git a/aisleriot/lib/ar-help.c b/aisleriot/lib/ar-help.c
new file mode 100644
index 0000000..d3a8973
--- /dev/null
+++ b/aisleriot/lib/ar-help.c
@@ -0,0 +1,156 @@
+/*
+ *  Copyright © 2008 Thomas H.P. Andersen <phomes gmail com>
+ *  Copyright © 2007, 2008, 2009 Christian Persch
+ *
+ *  This runtime 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This runtime is distributed in the hope runtime it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this runtime; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "ar-show.h"
+#include "ar-runtime.h"
+
+#include "ar-help.h"
+
+/**
+ * ar_help_display_full:
+ * @window: a #GdkWindow get the #GdkScreen from, and to use
+ *   as parent window for an error dialogue
+ * @doc_module: the doc module name (same as DOC_MODULE from help/Makefile.am)
+ * @section: a section name, or %NULL
+ * @error: a #GError location, or %NULL
+ *
+ * Opens help or returns an error.
+ *
+ * Returns: %TRUE on success, or %FALSE on failure with @error filled in
+ */
+gboolean
+ar_help_display_full (GtkWidget *window,
+                         const char *doc_module,
+                         const char *section,
+                         GError **error)
+{
+  GdkScreen *screen;
+  char *help_uri;
+  gboolean ret;
+
+  g_return_val_if_fail (doc_module != NULL, TRUE);
+
+  screen = gtk_widget_get_screen (GTK_WIDGET (window));
+
+  /* FIXME: do we need to use g_uri_escape_string for doc_module and section? */
+
+#if defined(WITH_HELP_METHOD_GHELP)
+  if (section != NULL) {
+#if GLIB_CHECK_VERSION (2, 16, 0)
+    char *escaped_section;
+
+    escaped_section = g_uri_escape_string  (section, NULL, TRUE);
+    help_uri = g_strdup_printf ("ghelp:%s?%s", doc_module, escaped_section);
+    g_free (escaped_section);
+#else
+    /* Not ideal, but the best we can do */
+    help_uri = g_strdup_printf ("ghelp:%s?%s", doc_module, section);
+#endif /* GLIB >= 2.16.0 */
+  } else {
+    help_uri = g_strdup_printf ("ghelp:%s", doc_module);
+  }
+#elif defined(WITH_HELP_METHOD_FILE)
+  const char *help_dir;
+  const char * const *langs;
+  guint i;
+
+  langs = g_get_language_names ();
+  help_dir = ar_runtime_get_directory (AR_RUNTIME_GAME_HELP_DIRECTORY);
+
+  help_uri = NULL;
+  for (i = 0; langs[i] != NULL; ++i) {
+    const char *lang = langs[i];
+    char *help_file_name, *path;
+
+    /* Filter out variants */
+    if (strchr (lang, '.') != NULL ||
+        strchr (lang, '@') != NULL)
+      continue;
+
+    help_file_name = g_strdup_printf ("%s." HELP_FILE_FORMAT,
+                                      section ? section : doc_module);
+    path = g_build_filename (help_dir,
+                             lang,
+                             help_file_name,
+                             NULL);
+    g_free (help_file_name);
+
+    if (g_file_test (path, G_FILE_TEST_EXISTS)) {
+      help_uri = g_filename_to_uri (path, NULL, NULL);
+      g_free (path);
+      break;
+    }
+
+    g_free (path);
+  }
+
+  if (help_uri == NULL) {
+    g_set_error (error,
+                 g_quark_from_static_string ("games-help-error"), 0,
+                 /* %s.%s is the game name + the extension HTML or XHTML, e.g. Klondike.html" */
+                 _("Help file â??%s.%sâ?? not found"),
+                 section ? section : doc_module,
+                 HELP_FILE_FORMAT);
+    return FALSE;
+  }
+    
+#elif defined(WITH_HELP_METHOD_LIBRARY)
+  if (section != NULL) {
+    help_uri = g_strdup_printf ("http://library.gnome.org/users/%s/stable/%s.html";, doc_module, section);
+  } else {
+    help_uri = g_strdup_printf ("http://library.gnome.org/users/%s/stable/";, doc_module);
+  }
+#endif
+
+  ret = ar_show_uri (screen, help_uri, gtk_get_current_event_time (), error);
+
+  g_free (help_uri);
+  return ret;
+}
+
+/**
+ * ar_help_display:
+ * @window: a #GdkWindow get the #GdkScreen from, and to use
+ *   as parent window for an error dialogue
+ * @doc_module: the doc module name (same as DOC_MODULE from help/Makefile.am)
+ * @section: a section name, or %NULL
+ *
+ * Opens help or displays error dialog when unable to open help.
+ */
+void
+ar_help_display (GtkWidget *window,
+                    const char *doc_module,
+                    const char *section)
+{
+  GError *error = NULL;
+
+  if (!ar_help_display_full (window, doc_module, section, &error)) {
+    ar_show_error (window, error,
+                      _("Could not show help for â??%sâ??"),
+                      section ? section : g_get_application_name ());
+    g_error_free (error);
+  }
+}
diff --git a/aisleriot/lib/ar-help.h b/aisleriot/lib/ar-help.h
new file mode 100644
index 0000000..75ee0f0
--- /dev/null
+++ b/aisleriot/lib/ar-help.h
@@ -0,0 +1,36 @@
+/*
+ *  Copyright © 2008 Thomas H.P. Andersen <phomes gmail com>
+ *
+ *  This runtime 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This runtime is distributed in the hope runtime it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this runtime; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef AR_HELP_H
+#define AR_HELP_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void     ar_help_display      (GtkWidget *window,
+                                  const char *doc_module,
+                                  const char *section);
+gboolean ar_help_display_full (GtkWidget *window,
+                                  const char *doc_module,
+                                  const char *section,
+                                  GError **error);
+
+G_END_DECLS
+
+#endif /* !AR_HELP_H */
diff --git a/aisleriot/lib/ar-marshal.list b/aisleriot/lib/ar-marshal.list
new file mode 100644
index 0000000..a80e48c
--- /dev/null
+++ b/aisleriot/lib/ar-marshal.list
@@ -0,0 +1,3 @@
+BOOLEAN:ENUM,INT
+BOOLEAN:STRING,UINT,ENUM
+VOID:STRING,STRING
diff --git a/aisleriot/lib/ar-preimage-private.h b/aisleriot/lib/ar-preimage-private.h
new file mode 100644
index 0000000..b86f4c5
--- /dev/null
+++ b/aisleriot/lib/ar-preimage-private.h
@@ -0,0 +1,40 @@
+/*
+   Copyright © 2004 Richard Hoelscher
+   Copyright © 2007 Christian Persch
+   
+   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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* Authors:   Richard Hoelscher <rah rahga com> */
+
+#ifdef HAVE_RSVG
+#include <librsvg/rsvg.h>
+#endif
+
+struct _ArPreimage {
+  GObject parent;
+
+  gint width;
+  gint height;
+
+#ifdef HAVE_RSVG
+  RsvgHandle *rsvg_handle;
+  cairo_font_options_t *font_options;
+#endif
+
+  /* raster pixbuf data */
+  GdkPixbuf *pixbuf;
+
+  guint scalable : 1;
+};
diff --git a/aisleriot/lib/ar-preimage.c b/aisleriot/lib/ar-preimage.c
new file mode 100644
index 0000000..49bc9a1
--- /dev/null
+++ b/aisleriot/lib/ar-preimage.c
@@ -0,0 +1,490 @@
+/*
+   Copyright © 2004 Richard Hoelscher
+   Copyright © 2007 Christian Persch
+   
+   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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* Authors:   Richard Hoelscher <rah rahga com> */
+
+/* Cache raster and vector images and render them to a specific size. */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+/* For gdkcairo */
+#include <gdk/gdk.h>
+
+#ifdef HAVE_RSVG
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+#endif /* HAVE_RSVG */
+
+#include "ar-profile.h"
+
+#include "ar-preimage.h"
+#include "ar-preimage-private.h"
+
+G_DEFINE_TYPE (ArPreimage, ar_preimage, G_TYPE_OBJECT);
+
+static void
+ar_preimage_init (ArPreimage * preimage)
+{
+  preimage->scalable = FALSE;
+  preimage->width = 0;
+  preimage->height = 0;
+}
+
+static void
+ar_preimage_finalize (GObject * object)
+{
+  ArPreimage *preimage = AR_PREIMAGE (object);
+
+#ifdef HAVE_RSVG
+  if (preimage->rsvg_handle != NULL) {
+    g_object_unref (preimage->rsvg_handle);
+  }
+  if (preimage->font_options) {
+    cairo_font_options_destroy (preimage->font_options);
+  }
+#endif
+
+  if (preimage->pixbuf != NULL) {
+    g_object_unref (preimage->pixbuf);
+  }
+
+  G_OBJECT_CLASS (ar_preimage_parent_class)->finalize (object);
+}
+
+static void
+ar_preimage_class_init (ArPreimageClass * klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  oclass->finalize = ar_preimage_finalize;
+
+#ifdef HAVE_RSVG
+  rsvg_init ();
+#endif
+}
+
+/**
+ * ar_preimage_render:
+ * @preimage: the image to render
+ * @width: the desired width
+ * @height: the desired height
+ *
+ * Creates a #GdkPixbuf from @preimage's image at the specified
+ * @width and @height.
+ *
+ * Returns: (transfer full): the new #GdkPixbuf
+ **/
+GdkPixbuf *
+ar_preimage_render (ArPreimage * preimage, gint width, gint height)
+{
+  GdkPixbuf *pixbuf;
+
+  g_return_val_if_fail (width > 0 && height > 0, NULL);
+  g_return_val_if_fail (preimage != NULL, NULL);
+
+#ifdef HAVE_RSVG
+  if (preimage->scalable) {     /* Render vector image */
+    pixbuf = ar_preimage_render_sub (preimage,
+                                        NULL,
+                                        width,
+                                        height,
+                                        0.0, 0.0,
+                                        ((double) width) /
+                                        ((double) preimage->width),
+                                        ((double) height) /
+                                        ((double) preimage->height));
+  } else
+#endif /* HAVE_RSVG */
+  {
+    /* Render raster image */
+    pixbuf = gdk_pixbuf_scale_simple (preimage->pixbuf,
+                                      width, height, GDK_INTERP_BILINEAR);
+  }
+
+  return pixbuf;
+}
+
+/**
+ * ar_preimage_render_cairo:
+ * @preimage:
+ * @cr:
+ * @width: the desired width
+ * @height: the desired height
+ *
+ * Renders from @preimage's image at the specified
+ * @width and @height to @cr.
+ **/
+void
+ar_preimage_render_cairo (ArPreimage * preimage,
+                             cairo_t *cr,
+                             gint width,
+                             gint height)
+{
+  g_return_if_fail (width > 0 && height > 0);
+  g_return_if_fail (preimage != NULL);
+
+#ifdef HAVE_RSVG
+  if (preimage->scalable) {     /* Render vector image */
+    ar_preimage_render_cairo_sub (preimage,
+                                     cr,
+                                     NULL,
+                                     width,
+                                     height,
+                                     0.0, 0.0,
+                                     ((double) width) /
+                                     ((double) preimage->width),
+                                     ((double) height) /
+                                     ((double) preimage->height));
+  } else
+#endif /* HAVE_RSVG */
+  {
+    GdkPixbuf *pixbuf;
+
+    /* FIXMEchpe: we don't really need this fallback anymore */
+    /* Render raster image */
+    pixbuf = gdk_pixbuf_scale_simple (preimage->pixbuf,
+                                      width, height, GDK_INTERP_BILINEAR);
+
+    cairo_save (cr);
+    gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    g_object_unref (pixbuf);
+  }
+}
+
+#ifdef HAVE_RSVG
+
+/* This routine is copied from librsvg:
+   Copyright © 2005 Dom Lachowicz <cinamod hotmail com>
+   Copyright © 2005 Caleb Moore <c moore student unsw edu au>
+   Copyright © 2005 Red Hat, Inc.
+ */
+static void
+cairo_pixels_to_pixbuf (guint8 * pixels, int rowstride, int height)
+{
+  int row;
+
+  /* un-premultiply data */
+  for (row = 0; row < height; row++) {
+    guint8 *row_data = (pixels + (row * rowstride));
+    int i;
+
+    for (i = 0; i < rowstride; i += 4) {
+      guint8 *b = &row_data[i];
+      guint32 pixel;
+      guint8 alpha;
+
+      memcpy (&pixel, b, sizeof (guint32));
+      alpha = (pixel & 0xff000000) >> 24;
+      if (alpha == 0) {
+        b[0] = b[1] = b[2] = b[3] = 0;
+      } else {
+        b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+        b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
+        b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
+        b[3] = alpha;
+      }
+    }
+  }
+}
+
+/**
+ * ar_preimage_render_cairo_sub:
+ * @preimage:
+ * @cr:
+ * @node: a SVG node ID (starting with "#"), or %NULL
+ * @width: the width of the clip region
+ * @height: the height of the clip region
+ * @xoffset: the x offset of the clip region
+ * @yoffset: the y offset of the clip region
+ * @xzoom: the x zoom factor
+ * @yzoom: the y zoom factor
+ *
+ * Creates a #GdkPixbuf with the dimensions @width by @height,
+ * and renders the subimage of @preimage specified by @node to it,
+ * transformed by @xzoom, @yzoom and offset by @xoffset and @yoffset,
+ * clipped to @width and @height.
+ * If @node is NULL, the whole image is rendered into tha clip region.
+ *
+ * Returns: %TRUE, of %FALSE if there was an error or @preimage
+ * isn't a scalable SVG image
+ **/
+void
+ar_preimage_render_cairo_sub (ArPreimage * preimage,
+                                 cairo_t *cr,
+                                 const char *node,
+                                 int width,
+                                 int height,
+                                 double xoffset,
+                                 double yoffset,
+                                 double xzoom,
+                                 double yzoom)
+{
+  cairo_matrix_t matrix;
+
+  if (!preimage->scalable)
+    return;
+
+  if (preimage->font_options) {
+    cairo_set_antialias (cr, cairo_font_options_get_antialias (preimage->font_options));
+
+    cairo_set_font_options (cr, preimage->font_options);
+  }
+
+  cairo_matrix_init_identity (&matrix);
+  cairo_matrix_scale (&matrix, xzoom, yzoom);
+  cairo_matrix_translate (&matrix, xoffset, yoffset);
+
+  cairo_set_matrix (cr, &matrix);
+
+  rsvg_handle_render_cairo_sub (preimage->rsvg_handle, cr, node);
+}
+
+/**
+ * ar_preimage_render_sub:
+ * @preimage:
+ * @node: a SVG node ID (starting with "#"), or %NULL
+ * @width: the width of the clip region
+ * @height: the height of the clip region
+ * @xoffset: the x offset of the clip region
+ * @yoffset: the y offset of the clip region
+ * @xzoom: the x zoom factor
+ * @yzoom: the y zoom factor
+ *
+ * Creates a #GdkPixbuf with the dimensions @width by @height,
+ * and renders the subimage of @preimage specified by @node to it,
+ * transformed by @xzoom, @yzoom and offset by @xoffset and @yoffset,
+ * clipped to @width and @height.
+ * If @node is NULL, the whole image is rendered into tha clip region.
+ *
+ * Returns: (transfer full) (allow-none): a new #GdkPixbuf, or %NULL if there was an error or @preimage
+ * isn't a scalable SVG image
+ */
+GdkPixbuf *
+ar_preimage_render_sub (ArPreimage * preimage,
+                           const char *node,
+                           int width,
+                           int height,
+                           double xoffset,
+                           double yoffset, double xzoom, double yzoom)
+{
+  int rowstride;
+  guint8 *data;
+  cairo_surface_t *surface;
+  cairo_t *cr;
+
+  if (!preimage->scalable)
+    return NULL;
+
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE (1, 6, 0)
+  rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width);
+#else
+  rowstride = width * 4;
+#endif
+
+  data = g_try_malloc0 (rowstride * height);
+  if (!data)
+    return NULL;
+
+  surface = cairo_image_surface_create_for_data (data,
+                                                 CAIRO_FORMAT_ARGB32,
+                                                 width, height, rowstride);
+  cr = cairo_create (surface);
+  ar_preimage_render_cairo_sub (preimage, cr, node, width, height,
+                                   xoffset, yoffset, xzoom, yzoom);
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
+  cairo_pixels_to_pixbuf (data, rowstride, height);
+
+  return gdk_pixbuf_new_from_data (data,
+                                   GDK_COLORSPACE_RGB,
+                                   TRUE,
+                                   8,
+                                   width, height,
+                                   rowstride,
+                                   (GdkPixbufDestroyNotify) g_free, data);
+}
+
+#endif /* HAVE_RSVG */
+
+/**
+ * ar_preimage_new_from_file:
+ * @filename:
+ * @error: a location for a #GError
+ *
+ * Creates a new #ArPreimage from the image in @filename.
+ *
+ * Returns: (allow-none): a new #ArPreimage, or %NULL if there was an error
+ */
+ArPreimage *
+ar_preimage_new_from_file (const gchar * filename, GError ** error)
+{
+  ArPreimage *preimage;
+  GdkPixbuf *pixbuf;
+
+  g_return_val_if_fail (filename != NULL, NULL);
+
+  ar_profilestart ("creating ArPreimage from %s", filename);
+
+  preimage = g_object_new (AR_TYPE_PREIMAGE, NULL);
+
+#ifdef HAVE_RSVG
+  preimage->rsvg_handle = rsvg_handle_new_from_file (filename, NULL);
+  if (preimage->rsvg_handle) {
+    RsvgDimensionData data;
+
+    preimage->scalable = TRUE;
+
+    rsvg_handle_get_dimensions (preimage->rsvg_handle, &data);
+
+    ar_profileend ("creating ArPreimage from %s", filename);
+
+    if (data.width == 0 || data.height == 0) {
+      g_set_error (error,
+                   GDK_PIXBUF_ERROR,
+                   GDK_PIXBUF_ERROR_FAILED, "Image has zero extent");
+      g_object_unref (preimage);
+      return NULL;
+    }
+
+    preimage->width = data.width;
+    preimage->height = data.height;
+
+    return preimage;
+  }
+#endif /* HAVE_RSVG */
+
+  /* Not an SVG */
+  preimage->scalable = FALSE;
+
+  pixbuf = gdk_pixbuf_new_from_file (filename, error);
+  ar_profileend ("creating ArPreimage from %s", filename);
+
+  if (!pixbuf) {
+    g_object_unref (preimage);
+    return NULL;
+  }
+
+  preimage->pixbuf = pixbuf;
+  preimage->width = gdk_pixbuf_get_width (pixbuf);
+  preimage->height = gdk_pixbuf_get_height (pixbuf);
+
+  return preimage;
+}
+
+/**
+ * ar_preimage_set_font_options:
+ * @preimage: a #ArPreimage
+ * @font_options: the font options
+ *
+ * Turns on antialising of @preimage, if it contains an SVG image.
+ */
+void
+ar_preimage_set_font_options (ArPreimage * preimage,
+                                 const cairo_font_options_t * font_options)
+{
+#ifdef HAVE_RSVG
+  g_return_if_fail (GAMES_IS_PREIMAGE (preimage));
+
+  if (preimage->font_options) {
+    cairo_font_options_destroy (preimage->font_options);
+  }
+
+  if (font_options) {
+    preimage->font_options = cairo_font_options_copy (font_options);
+  } else {
+    preimage->font_options = NULL;
+  }
+#endif /* HAVE_RSVG */
+}
+
+/**
+ * ar_preimage_is_scalable:
+ * @preimage:
+ *
+ * Returns: %TRUE iff @preimage contains an SVG image
+ */
+gboolean
+ar_preimage_is_scalable (ArPreimage * preimage)
+{
+  g_return_val_if_fail (GAMES_IS_PREIMAGE (preimage), FALSE);
+
+  return preimage->scalable;
+}
+
+/**
+ * ar_preimage_get_width:
+ * @preimage:
+ *
+ * Returns: the natural width of the image in @preimage
+ */
+gint
+ar_preimage_get_width (ArPreimage * preimage)
+{
+  g_return_val_if_fail (GAMES_IS_PREIMAGE (preimage), 0);
+
+  return preimage->width;
+}
+
+/**
+ * ar_preimage_get_height:
+ * @preimage:
+ *
+ * Returns: the natural height of the image in @preimage
+ */
+gint
+ar_preimage_get_height (ArPreimage * preimage)
+{
+  g_return_val_if_fail (GAMES_IS_PREIMAGE (preimage), 0);
+
+  return preimage->height;
+}
+
+/**
+ * ar_preimage_render_unscaled_pixbuf:
+ * @preimage:
+ *
+ * Renders @preimage onto a new #GdkPixbuf at its natural size
+ *
+ * Returns: (transfer full) (allow-none): a reference to a #GdkPixbuf possibly owned by @images which
+ * you must not modify; or %NULL if there was an error
+ */
+GdkPixbuf *
+ar_preimage_render_unscaled_pixbuf (ArPreimage * preimage)
+{
+  GdkPixbuf *unscaled_pixbuf;
+
+  g_return_val_if_fail (GAMES_IS_PREIMAGE (preimage), NULL);
+
+  if ((unscaled_pixbuf = preimage->pixbuf)) {
+    g_object_ref (unscaled_pixbuf);
+  } else {
+    unscaled_pixbuf = ar_preimage_render (preimage,
+                                             preimage->width,
+                                             preimage->height);
+  }
+
+  return unscaled_pixbuf;
+}
diff --git a/aisleriot/lib/ar-preimage.h b/aisleriot/lib/ar-preimage.h
new file mode 100644
index 0000000..d3aacb6
--- /dev/null
+++ b/aisleriot/lib/ar-preimage.h
@@ -0,0 +1,90 @@
+/*
+  Copyright © 2004 Richard Hoelscher
+
+  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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* Authors:   Richard Hoelscher <rah rahga com> */
+
+/* Cache raster and vector images and render them to a specific size. */
+
+#ifndef AR_PREIMAGE_H
+#define AR_PREIMAGE_H
+
+#include <glib.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+#define AR_TYPE_PREIMAGE             (ar_preimage_get_type ())
+#define AR_PREIMAGE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), AR_TYPE_PREIMAGE, ArPreimage))
+#define AR_PREIMAGE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), AR_TYPE_PREIMAGE, ArPreimageClass))
+#define GAMES_IS_PREIMAGE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), AR_TYPE_PREIMAGE))
+#define GAMES_IS_PREIMAGE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), AR_TYPE_PREIMAGE))
+#define GAMES_GET_PREIMAGE_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), AR_TYPE_PREIMAGE, ArPreimageClass))
+
+typedef struct _ArPreimage ArPreimage;
+
+typedef struct {
+  GObjectClass parent_class;
+} ArPreimageClass;
+
+GType ar_preimage_get_type (void);
+
+ArPreimage *ar_preimage_new (void);
+
+ArPreimage *ar_preimage_new_from_file (const gchar * filename,
+                                             GError ** error);
+
+void ar_preimage_set_font_options (ArPreimage * preimage,
+                                      const cairo_font_options_t *font_options);
+
+GdkPixbuf *ar_preimage_render (ArPreimage * preimage,
+                                  gint width,
+                                  gint height);
+void ar_preimage_render_cairo (ArPreimage * preimage,
+                                  cairo_t *cr,
+                                  gint width,
+                                  gint height);
+
+GdkPixbuf *ar_preimage_render_sub (ArPreimage * preimage,
+                                      const char *node,
+                                      int width,
+                                      int height,
+                                      double xoffset,
+                                      double yoffset,
+                                      double xzoom, double yzoom);
+
+void ar_preimage_render_cairo_sub (ArPreimage * preimage,
+                                      cairo_t *cr,
+                                      const char *node,
+                                      int width,
+                                      int height,
+                                      double xoffset,
+                                      double yoffset,
+                                      double xzoom,
+                                      double yzoom);
+
+gboolean ar_preimage_is_scalable (ArPreimage * preimage);
+
+gint ar_preimage_get_width (ArPreimage * preimage);
+
+gint ar_preimage_get_height (ArPreimage * preimage);
+
+GdkPixbuf *ar_preimage_render_unscaled_pixbuf (ArPreimage * preimage);
+
+G_END_DECLS
+
+#endif /* AR_PREIMAGE_H */
diff --git a/aisleriot/lib/ar-profile.c b/aisleriot/lib/ar-profile.c
new file mode 100644
index 0000000..09cb1ef
--- /dev/null
+++ b/aisleriot/lib/ar-profile.c
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright © 2005 William Jon McCann <mccann jhu edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: William Jon McCann <mccann jhu edu>
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "ar-profile.h"
+
+void
+ar_profilelog (const char *func,
+                    const char *note,
+                    const char *format,
+                    ...)
+{
+        va_list args;
+        char   *str;
+        char   *formatted;
+
+        if (format == NULL) {
+                formatted = g_strdup ("");
+        } else {
+                va_start (args, format);
+                formatted = g_strdup_vprintf (format, args);
+                va_end (args);
+        }
+
+        if (func != NULL) {
+                str = g_strdup_printf ("MARK: %s %s: %s %s", g_get_prgname(), func, note ? note : "", formatted);
+        } else {
+                str = g_strdup_printf ("MARK: %s: %s %s", g_get_prgname(), note ? note : "", formatted);
+        }
+
+        g_free (formatted);
+
+        g_access (str, F_OK);
+        g_free (str);
+}
diff --git a/aisleriot/lib/ar-profile.h b/aisleriot/lib/ar-profile.h
new file mode 100644
index 0000000..3dbfdc5
--- /dev/null
+++ b/aisleriot/lib/ar-profile.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright © 2005 William Jon McCann <mccann jhu edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: William Jon McCann <mccann jhu edu>
+ */
+
+#ifndef AR_PROFILE_H
+#define AR_PROFILE_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#ifdef ENABLE_PROFILING
+#ifdef G_HAVE_ISO_VARARGS
+#define ar_profilestart(...) ar_profilelog (G_STRFUNC, "start", __VA_ARGS__)
+#define ar_profileend(...)   ar_profilelog (G_STRFUNC, "end", __VA_ARGS__)
+#define ar_profilemsg(...)   ar_profilelog (NULL, NULL, __VA_ARGS__)
+#elif defined(G_HAVE_GNUC_VARARGS)
+#define ar_profilestart(format...) ar_profilelog (G_STRFUNC, "start", format)
+#define ar_profileend(format...)   ar_profilelog (G_STRFUNC, "end", format)
+#define ar_profilemsg(format...)   ar_profilelog (NULL, NULL, format)
+#else
+#error Need either ISO or GNUC varargs macros!
+#endif
+#else
+#define ar_profilestart(...)
+#define ar_profileend(...)
+#define ar_profilemsg(...)
+#endif
+
+void ar_profilelog (const char *func,
+                         const char *note,
+                         const char *format,
+                         ...) G_GNUC_PRINTF (3, 4);
+
+G_END_DECLS
+
+#endif /* AR_PROFILE_H */
diff --git a/aisleriot/lib/ar-runtime.c b/aisleriot/lib/ar-runtime.c
new file mode 100644
index 0000000..3875225
--- /dev/null
+++ b/aisleriot/lib/ar-runtime.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright © 2007 Andreas Røsdal <andreasr gnome org>
+ * Copyright © 2007, 2008 Christian Persch
+ * Copyright © 2009 Tor Lillqvist
+ *
+ * This game is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This runtime is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this runtime; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+ * USA
+ */
+
+#include <config.h>
+
+#include <locale.h>
+
+#ifdef G_OS_WIN32
+#include <io.h>
+#include <conio.h>
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#endif /* G_OS_WIN32 */
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "ar-debug.h"
+#include "ar-profile.h"
+#include "ar-show.h"
+
+#include "ar-runtime.h"
+
+#ifdef HAVE_HILDON
+static osso_context_t *osso_context;
+#endif
+
+#if defined(G_OS_WIN32) && !defined(ENABLE_BINRELOC)
+#error binreloc must be enabled on win32
+#endif
+
+#if defined(ENABLE_BINRELOC) && !defined(G_OS_WIN32)
+
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h lai chello nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/** These error codes can be returned by br_init(), br_init_lib(), gbr_init() or gbr_init_lib(). */
+typedef enum {
+	/** Cannot allocate memory. */
+	GBR_INIT_ERROR_NOMEM,
+	/** Unable to open /proc/self/maps; see errno for details. */
+	GBR_INIT_ERROR_OPEN_MAPS,
+	/** Unable to read from /proc/self/maps; see errno for details. */
+	GBR_INIT_ERROR_READ_MAPS,
+	/** The file format of /proc/self/maps is invalid; kernel bug? */
+	GBR_INIT_ERROR_INVALID_MAPS,
+	/** BinReloc is disabled (the ENABLE_BINRELOC macro is not defined). */
+	GBR_INIT_ERROR_DISABLED
+} GbrInitError;
+
+/** @internal
+ * Find the canonical filename of the executable. Returns the filename
+ * (which must be freed) or NULL on error. If the parameter 'error' is
+ * not NULL, the error code will be stored there, if an error occured.
+ */
+static char *
+_br_find_exe (GbrInitError *error)
+{
+	char *path, *path2, *line, *result;
+	size_t buf_size;
+	ssize_t size;
+	struct stat stat_buf;
+	FILE *f;
+
+	/* Read from /proc/self/exe (symlink) */
+	if (sizeof (path) > SSIZE_MAX)
+		buf_size = SSIZE_MAX - 1;
+	else
+		buf_size = PATH_MAX - 1;
+	path = (char *) g_try_malloc (buf_size);
+	if (path == NULL) {
+		/* Cannot allocate memory. */
+		if (error)
+			*error = GBR_INIT_ERROR_NOMEM;
+		return NULL;
+	}
+	path2 = (char *) g_try_malloc (buf_size);
+	if (path2 == NULL) {
+		/* Cannot allocate memory. */
+		if (error)
+			*error = GBR_INIT_ERROR_NOMEM;
+		g_free (path);
+		return NULL;
+	}
+
+	strncpy (path2, "/proc/self/exe", buf_size - 1);
+
+	while (1) {
+		int i;
+
+		size = readlink (path2, path, buf_size - 1);
+		if (size == -1) {
+			/* Error. */
+			g_free (path2);
+			break;
+		}
+
+		/* readlink() success. */
+		path[size] = '\0';
+
+		/* Check whether the symlink's target is also a symlink.
+		 * We want to get the final target. */
+		i = stat (path, &stat_buf);
+		if (i == -1) {
+			/* Error. */
+			g_free (path2);
+			break;
+		}
+
+		/* stat() success. */
+		if (!S_ISLNK (stat_buf.st_mode)) {
+			/* path is not a symlink. Done. */
+			g_free (path2);
+			return path;
+		}
+
+		/* path is a symlink. Continue loop and resolve this. */
+		strncpy (path, path2, buf_size - 1);
+	}
+
+
+	/* readlink() or stat() failed; this can happen when the program is
+	 * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
+
+	buf_size = PATH_MAX + 128;
+	line = (char *) g_try_realloc (path, buf_size);
+	if (line == NULL) {
+		/* Cannot allocate memory. */
+		g_free (path);
+		if (error)
+			*error = GBR_INIT_ERROR_NOMEM;
+		return NULL;
+	}
+
+	f = fopen ("/proc/self/maps", "r");
+	if (f == NULL) {
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_OPEN_MAPS;
+		return NULL;
+	}
+
+	/* The first entry should be the executable name. */
+	result = fgets (line, (int) buf_size, f);
+	if (result == NULL) {
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_READ_MAPS;
+		return NULL;
+	}
+
+	/* Get rid of newline character. */
+	buf_size = strlen (line);
+	if (buf_size <= 0) {
+		/* Huh? An empty string? */
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_INVALID_MAPS;
+		return NULL;
+	}
+	if (line[buf_size - 1] == 10)
+		line[buf_size - 1] = 0;
+
+	/* Extract the filename; it is always an absolute path. */
+	path = strchr (line, '/');
+
+	/* Sanity check. */
+	if (strstr (line, " r-xp ") == NULL || path == NULL) {
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_INVALID_MAPS;
+		return NULL;
+	}
+
+	path = g_strdup (path);
+	g_free (line);
+	fclose (f);
+	return path;
+}
+
+#endif /* ENABLE_BINRELOC && !G_OS_WIN32 */
+
+static char *app_name;
+static int gpl_version;
+static char *cached_directories[AR_RUNTIME_LAST_DIRECTORY];
+
+typedef struct {
+  ArRuntimeDirectory base_dir;
+  const char *name;
+} DerivedDirectory;
+
+static const DerivedDirectory derived_directories[] = {
+  /* Keep this in the same order as in the ArRuntimeDirectory enum! */
+#ifdef ENABLE_BINRELOC
+  { AR_RUNTIME_PREFIX,             "share"              }, /* AR_RUNTIME_DATA_DIRECTORY              */
+  { AR_RUNTIME_DATA_DIRECTORY,     "gnome-games-common" }, /* AR_RUNTIME_COMMON_DATA_DIRECTORY       */
+  { AR_RUNTIME_DATA_DIRECTORY,     "gnome-games"        }, /* AR_RUNTIME_PKG_DATA_DIRECTORY          */
+  { AR_RUNTIME_DATA_DIRECTORY,     "scores"             }, /* AR_RUNTIME_SCORES_DIRECTORY            */
+#endif /* ENABLE_BINRELOC */
+  { AR_RUNTIME_DATA_DIRECTORY,         "locale"         }, /* AR_RUNTIME_LOCALE_DIRECTORY            */
+  { AR_RUNTIME_COMMON_DATA_DIRECTORY,  "pixmaps"        }, /* AR_RUNTIME_COMMON_PIXMAP_DIRECTORY     */
+  { AR_RUNTIME_COMMON_DATA_DIRECTORY,  "card-themes"    }, /* AR_RUNTIME_PRERENDERED_CARDS_DIRECTORY */
+  { AR_RUNTIME_COMMON_DATA_DIRECTORY,  "cards"          }, /* AR_RUNTIME_SCALABLE_CARDS_DIRECTORY    */
+  { AR_RUNTIME_PKG_DATA_DIRECTORY,     "icons"          }, /* AR_RUNTIME_ICON_THEME_DIRECTORY        */
+  { AR_RUNTIME_PKG_DATA_DIRECTORY,     "pixmaps"        }, /* AR_RUNTIME_PIXMAP_DIRECTORY            */
+  { AR_RUNTIME_PKG_DATA_DIRECTORY,     "sounds"         }, /* AR_RUNTIME_SOUNDS_DIRECTORY            */
+  { AR_RUNTIME_PKG_DATA_DIRECTORY,     NULL             }, /* AR_RUNTIME_GAME_DATA_DIRECTORY         */
+  { AR_RUNTIME_GAME_DATA_DIRECTORY,    "games"          }, /* AR_RUNTIME_GAME_GAMES_DIRECTORY        */
+  { AR_RUNTIME_GAME_DATA_DIRECTORY,    "pixmaps"        }, /* AR_RUNTIME_GAME_PIXMAP_DIRECTORY       */
+  { AR_RUNTIME_GAME_DATA_DIRECTORY,    "themes"         }, /* AR_RUNTIME_GAME_THEME_DIRECTORY        */
+  { AR_RUNTIME_GAME_DATA_DIRECTORY,    "help"           }, /* AR_RUNTIME_GAME_HELP_DIRECTORY         */
+};
+
+typedef int _assertion[G_N_ELEMENTS (derived_directories) + AR_RUNTIME_FIRST_DERIVED_DIRECTORY == AR_RUNTIME_LAST_DIRECTORY ? 1 : -1];
+
+#if !GTK_CHECK_VERSION (2, 17, 0)
+/* Since version 2.17.0, gtk has default about dialogue hook functions
+ * using gtk_show_uri(). For earlier versions, we need to implement
+ * our own hooks.
+ */
+
+static void
+about_url_hook (GtkAboutDialog *about,
+                const char *uri,
+                gpointer user_data)
+{
+  GdkScreen *screen;
+  GError *error = NULL;
+
+  screen = gtk_widget_get_screen (GTK_WIDGET (about));
+
+  if (!ar_show_uri (screen, uri, gtk_get_current_event_time (), &error)) {
+    ar_show_error (GTK_WIDGET (about),
+                      error,
+                      "%s", _("Could not show link"));
+    g_error_free (error);
+  }
+}
+
+static void
+about_email_hook (GtkAboutDialog *about,
+                  const char *email_address,
+                  gpointer user_data)
+{
+  char *uri;
+
+#if GLIB_CHECK_VERSION (2, 16, 0)
+  char *escaped_email_address;
+
+  escaped_email_address = g_uri_escape_string (email_address, NULL, FALSE);
+  uri = g_strdup_printf ("mailto:%s";, escaped_email_address);
+  g_free (escaped_email_address);
+#else
+  /* Not really correct, but the best we can do */
+  uri = g_strdup_printf ("mailto:%s";, email_address);
+#endif
+
+  about_url_hook (about, uri, user_data);
+  g_free (uri);
+}
+
+#endif /* GTK < 2.17.0 */
+
+/* public API */
+
+/**
+ * ar_runtime_init:
+ *
+ * Initialises the runtime file localisator. This also calls setlocale,
+ * and initialises gettext support and gnome-games debug support.
+ *
+ * NOTE: This must be called before using ANY other glib/gtk/etc function!
+ * 
+ * Returns: %TRUE iff initialisation succeeded
+ */
+gboolean
+ar_runtime_init (const char *name)
+{
+  gboolean retval;
+
+  setlocale (LC_ALL, "");
+
+#ifdef G_OS_WIN32
+  /* On Windows, when called from a console, get console output. This works
+   * only with Windows XP or higher; Windows 2000 will not have console
+   * output but it will just work fine.
+   */
+  if (fileno (stdout) != -1 &&
+      _get_osfhandle (fileno (stdout)) != -1) {
+    /* stdout is fine, presumably redirected to a file or pipe.
+     * Make sure stdout goes somewhere, too.
+     */
+    if (_get_osfhandle (fileno (stderr)) == -1) {
+      dup2 (fileno (stdout), fileno (stderr));
+    }
+  } else {
+    typedef BOOL (* WINAPI AttachConsole_t) (DWORD);
+
+    AttachConsole_t p_AttachConsole =
+      (AttachConsole_t) GetProcAddress (GetModuleHandle ("kernel32.dll"), "AttachConsole");
+
+    if (p_AttachConsole != NULL && p_AttachConsole (ATTACH_PARENT_PROCESS)) {
+      freopen ("CONOUT$", "w", stdout);
+      dup2 (fileno (stdout), 1);
+      freopen ("CONOUT$", "w", stderr);
+      dup2 (fileno (stderr), 2);
+    }
+  }
+#endif /* G_OS_WIN32 */
+
+#if defined(HAVE_GNOME) || defined(HAVE_RSVG_GNOMEVFS) || defined(ENABLE_SOUND)
+  /* If we're going to use gconf, gnome-vfs, or canberra, we need to
+   * init threads; and this has to be done before calling any other glib functions.
+   */
+#if defined(LIBGAMES_SUPPORT_GI)
+  /* Seed has already called g_thread_init() */
+  g_assert (g_thread_get_initialized());
+#else
+  g_thread_init (NULL);
+#endif
+#endif
+  /* May call any glib function after this point */
+
+  ar_profilestart ("ar_runtime_init");
+
+  ar_debug_init ();
+
+  app_name = g_strdup (name);
+
+  bindtextdomain (GETTEXT_PACKAGE, ar_runtime_get_directory (AR_RUNTIME_LOCALE_DIRECTORY));
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+  textdomain(GETTEXT_PACKAGE);
+
+#ifdef ENABLE_BINRELOC
+{
+  const char *path;
+
+  /* Now check that we can get the module installation directory */
+  path = ar_runtime_get_directory (AR_RUNTIME_PREFIX);
+
+  ar_debug_print (AR_DEBUG_RUNTIME,
+                      "Relocation path: %s\n", path ? path : "(null)");
+
+  retval = path != NULL;
+}
+#else /* !ENABLE_BINRELOC */
+  retval = TRUE;
+#endif /* ENABLE_BINRELOC */
+
+  if (strcmp (app_name, "aisleriot") == 0) {
+    gpl_version = 3;
+  } else {
+    gpl_version = 2;
+  }
+
+#if !GTK_CHECK_VERSION (2, 17, 0)
+  gtk_about_dialog_set_url_hook (about_url_hook, NULL, NULL);
+  gtk_about_dialog_set_email_hook (about_email_hook, NULL, NULL);
+#endif
+
+  ar_profileend ("ar_runtime_init");
+
+  return retval;
+}
+
+#ifdef HAVE_HILDON
+
+/**
+ * ar_runtime_init_with_osso:
+ *
+ * Like ar_runtime_init(), but also initialises the osso context.
+ *
+ * NOTE: This must be called before using ANY other glib/gtk/etc function!
+ * 
+ * Returns: %TRUE iff initialisation succeeded
+ */
+gboolean
+ar_runtime_init_with_osso (const char *name,
+                              const char *service_name)
+{
+  if (!ar_runtime_init (name))
+    return FALSE;
+
+  osso_context = osso_initialize (service_name, VERSION, FALSE, NULL);
+  if (osso_context == NULL) {
+    g_printerr ("Failed to initialise osso\n");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+ * ar_runtime_get_osso_context:
+ *
+ * Returns the osso context. May only be called after
+ * ar_runtime_init_with_osso().
+ *
+ * Returns: a #osso_context_t
+ */
+osso_context_t*
+ar_runtime_get_osso_context (void)
+{
+  g_assert (osso_context != NULL);
+  return osso_context;
+}
+
+#endif /* HAVE_HILDON */
+
+/**
+ * ar_runtime_shutdown:
+ *
+ * Shuts down the runtime file localator.
+ */
+void
+ar_runtime_shutdown (void)
+{
+  guint i;
+
+  for (i = 0; i < AR_RUNTIME_LAST_DIRECTORY; ++i) {
+    g_free (cached_directories[i]);
+    cached_directories[i] = NULL;
+  }
+
+  g_free (app_name);
+  app_name = NULL;
+
+#ifdef HAVE_HILDON
+  if (osso_context != NULL) {
+    osso_deinitialize (osso_context);
+    osso_context = NULL;
+  }
+#endif /* HAVE_HILDON */
+}
+
+/**
+ * ar_runtime_get_directory:
+ * @directory:
+ *
+ * Returns: the path to use for @directory
+ */
+const char *
+ar_runtime_get_directory (ArRuntimeDirectory directory)
+{
+
+  char *path = NULL;
+
+  g_return_val_if_fail (app_name != NULL, NULL);
+  g_return_val_if_fail (directory < AR_RUNTIME_LAST_DIRECTORY, NULL);
+
+  if (cached_directories[directory])
+    return cached_directories[directory];
+
+  switch ((int) directory) {
+#ifdef ENABLE_BINRELOC
+    case AR_RUNTIME_PREFIX:
+#ifdef G_OS_WIN32
+      path = g_win32_get_package_installation_directory_of_module (NULL);
+#else
+      {
+        GbrInitError errv = 0;
+        const char *env;
+
+        if ((env = g_getenv ("GAMES_RELOC_ROOT")) != NULL) {
+          path = g_strdup (env);
+        } else {
+          char *exe, *bindir, *prefix;
+
+          exe = _br_find_exe (&errv);
+          if (exe == NULL) {
+            g_printerr ("Failed to locate the binary relocation prefix (error code %u)\n", errv);
+          } else {
+            bindir = g_path_get_dirname (exe);
+            g_free (exe);
+            prefix = g_path_get_dirname (bindir);
+            g_free (bindir);
+
+            if (prefix != NULL && strcmp (prefix, ".") != 0) {
+              path = prefix;
+            } else {
+              g_free (prefix);
+            }
+          }
+        }
+      }
+#endif /* G_OS_WIN32 */
+      break;
+#else /* !ENABLE_BINRELOC */
+
+    case AR_RUNTIME_PREFIX:
+      path = g_strdup (PREFIX);
+      break;
+
+    case AR_RUNTIME_DATA_DIRECTORY:
+      path = g_strdup (DATADIR);
+      break;
+
+    case AR_RUNTIME_COMMON_DATA_DIRECTORY:
+      path = g_build_filename (DATADIR, "gnome-games-common", NULL);
+      break;
+
+    case AR_RUNTIME_PKG_DATA_DIRECTORY:
+      path = g_strdup (PKGDATADIR);
+      break;
+
+    case AR_RUNTIME_SCORES_DIRECTORY:
+      path = g_strdup (SCORESDIR);
+      break;
+#endif /* ENABLE_BINRELOC */
+
+    default: {
+      const DerivedDirectory *base = &derived_directories[directory - AR_RUNTIME_FIRST_DERIVED_DIRECTORY];
+
+      path = g_build_filename (ar_runtime_get_directory (base->base_dir),
+                               base->name ? base->name : app_name,
+                               NULL);
+    }
+  }
+
+  cached_directories[directory] = path;
+  return path;
+}
+
+/**
+ * ar_runtime_get_file:
+ * @directory:
+ * @name:
+ *
+ * Returns: a newly allocated string containing the path to the file with name @name
+ *   in the directory specicifed by @directory
+ */
+char *
+ar_runtime_get_file (ArRuntimeDirectory directory,
+                        const char *name)
+{
+  const char *dir;
+
+  g_return_val_if_fail (app_name != NULL, NULL);
+
+  dir = ar_runtime_get_directory (directory);
+  if (!dir)
+    return NULL;
+
+  return g_build_filename (dir, name, NULL);
+}
+
+/**
+ * ar_runtime_get_gpl_version:
+ *
+ * Returns: the minimum GPL version that the executable is licensed under
+ */
+int
+ar_runtime_get_gpl_version (void)
+{
+  return gpl_version;
+}
+
+/**
+ * ar_runtime_is_system_prefix:
+ *
+ * Returns: whether the runtime prefix is "/usr".
+ */
+gboolean
+ar_runtime_is_system_prefix (void)
+{
+  return strcmp (ar_runtime_get_directory (AR_RUNTIME_PREFIX), "/usr") == 0;
+}
diff --git a/aisleriot/lib/ar-runtime.h b/aisleriot/lib/ar-runtime.h
new file mode 100644
index 0000000..b1180af
--- /dev/null
+++ b/aisleriot/lib/ar-runtime.h
@@ -0,0 +1,79 @@
+/*
+ *  Copyright © 2007, 2008 Christian Persch
+ *
+ *  This runtime 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This runtime is distributed in the hope runtime it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this runtime; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef AR_RUNTIME_H
+#define AR_RUNTIME_H
+
+#include <glib.h>
+
+#ifdef HAVE_HILDON
+#include <libosso.h>
+#endif
+
+G_BEGIN_DECLS
+
+typedef enum {
+  /* Base directories */
+  AR_RUNTIME_PREFIX,
+  AR_RUNTIME_DATA_DIRECTORY,
+  AR_RUNTIME_COMMON_DATA_DIRECTORY,
+  AR_RUNTIME_PKG_DATA_DIRECTORY,
+  AR_RUNTIME_SCORES_DIRECTORY,
+
+  /* Derived directories */
+  AR_RUNTIME_LOCALE_DIRECTORY,
+
+  AR_RUNTIME_COMMON_PIXMAP_DIRECTORY,
+  AR_RUNTIME_PRERENDERED_CARDS_DIRECTORY,
+  AR_RUNTIME_SCALABLE_CARDS_DIRECTORY,
+
+  AR_RUNTIME_ICON_THEME_DIRECTORY,
+  AR_RUNTIME_PIXMAP_DIRECTORY,
+  AR_RUNTIME_SOUND_DIRECTORY,
+
+  AR_RUNTIME_GAME_DATA_DIRECTORY,
+  AR_RUNTIME_GAME_GAMES_DIRECTORY,
+  AR_RUNTIME_GAME_PIXMAP_DIRECTORY,
+  AR_RUNTIME_GAME_THEME_DIRECTORY,
+  /* FIXME On hildon and win32 help is created as html with gnome-doc-tool, and put manually in this directory */
+  AR_RUNTIME_GAME_HELP_DIRECTORY,
+
+  AR_RUNTIME_LAST_DIRECTORY,
+#ifdef ENABLE_BINRELOC
+  AR_RUNTIME_FIRST_DERIVED_DIRECTORY = AR_RUNTIME_DATA_DIRECTORY,
+#else
+  AR_RUNTIME_FIRST_DERIVED_DIRECTORY = AR_RUNTIME_LOCALE_DIRECTORY,
+#endif
+} ArRuntimeDirectory;
+
+gboolean        ar_runtime_init             (const char *name);
+#ifdef HAVE_HILDON
+gboolean        ar_runtime_init_with_osso   (const char *name,
+                                                const char *service_name);
+osso_context_t* ar_runtime_get_osso_context (void);
+#endif /* HAVE_HILDON */
+void            ar_runtime_shutdown         (void);
+const char     *ar_runtime_get_directory    (ArRuntimeDirectory directory);
+char           *ar_runtime_get_file         (ArRuntimeDirectory directory,
+                                                const char *name);
+int             ar_runtime_get_gpl_version  (void);
+gboolean        ar_runtime_is_system_prefix (void);
+
+G_END_DECLS
+
+#endif /* !AR_RUNTIME_H */
diff --git a/aisleriot/lib/ar-show.c b/aisleriot/lib/ar-show.c
new file mode 100644
index 0000000..dcba64d
--- /dev/null
+++ b/aisleriot/lib/ar-show.c
@@ -0,0 +1,159 @@
+/*
+ *  Copyright © 2008 Thomas H.P. Andersen <phomes gmail com>
+ *  Copyright © 2007, 2008, 2009 Christian Persch
+ *
+ *  This runtime 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This runtime is distributed in the hope runtime it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this runtime; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#ifdef HAVE_MAEMO
+#ifdef HAVE_MAEMO_3
+#include <osso-browser-interface.h>
+#else
+#include <tablet-browser-interface.h>
+#endif /* HAVE_MAEMO_3 */
+#endif /* HAVE_MAEMO */
+
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <io.h>
+#endif /* G_OS_WIN32 */
+
+#include "ar-runtime.h"
+
+#include "ar-show.h"
+
+/**
+ * ar_show_uri:
+ * @screen: screen to show the uri on or %NULL for the default screen
+ * @uri: the uri to show
+ * @timestamp: a timestamp to prevent focus stealing.
+ * @error: a #GError that is returned in case of errors
+ *
+ * This is a convenience function for launching the default application
+ * to show the uri.
+ * Ideally the timestamp is taken from the event triggering
+ * the gtk_show_uri() call, or use gtk_get_current_event_time().
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+ar_show_uri (GdkScreen *screen,
+                const char *uri,
+                guint32 timestamp,
+                GError **error)
+{
+#ifdef HAVE_MAEMO
+  osso_rpc_run_with_defaults (ar_runtime_get_osso_context (),
+                              "osso_browser",
+                              OSSO_BROWSER_OPEN_NEW_WINDOW_REQ,
+                              NULL,
+                              DBUS_TYPE_STRING, uri,
+                              DBUS_TYPE_INVALID);
+  return TRUE;
+#else
+
+#ifdef G_OS_WIN32
+  ShellExecute (NULL, "open", uri, NULL, NULL, SW_SHOWNORMAL);
+  return TRUE;
+#else /* !G_OS_WIN32 */
+
+#if GTK_CHECK_VERSION (2, 14, 0)
+  return gtk_show_uri (screen, uri, timestamp, error);
+#else /* GTK+ < 2.14 */
+  char *argv[3] = { (char *) "xdg-open", (char *) uri, NULL };
+ 
+  if (gdk_spawn_on_screen (screen,
+                           NULL /* working directory */,
+                           argv,
+                           NULL /* environment */,
+                           G_SPAWN_SEARCH_PATH,
+                           NULL, NULL,
+                           NULL,
+                           error))
+    return TRUE;
+
+  g_clear_error (error);
+
+  /* Try falling back to gnome-open */
+  argv[0] = (char *) "gnome-open";
+  if (gdk_spawn_on_screen (screen,
+                           NULL /* working directory */,
+                           argv,
+                           NULL /* environment */,
+                           G_SPAWN_SEARCH_PATH,
+                           NULL, NULL,
+                           NULL,
+                           error))
+    return TRUE;
+
+  g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+               "%s", "Failed to show help");
+  return FALSE;
+#endif /* GTK+ >= 2.14 */
+#endif /* G_OS_WIN32 */
+#endif /* HAVE_MAEMO */
+}
+
+/**
+ * ar_show_error:
+ * @window: a transient parent window
+ * @error: a #GError
+ * @primary_text_format:
+ * @...:
+ *
+ * Shows a message dialog with the given primary text, and @error's message
+ * as secondary text. The dialog will be transient to @parent, and modal.
+ * However, this function will *not* block until the dialogue has been dismissed.
+ */
+void
+ar_show_error (GtkWidget *window,
+                  GError *error,
+                  const char *primary_text_format,
+                  ...)
+{
+  GtkWidget *dialog;
+  char *primary_text;
+  va_list args;
+
+  va_start (args, primary_text_format);
+  primary_text = g_strdup_vprintf (primary_text_format, args);
+  va_end (args);
+
+  dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                   GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
+                                   "%s", primary_text);
+  g_free (primary_text);
+
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                            "%s", error->message);
+
+#ifdef HAVE_HILDON
+  /* Empty title shows up as "<unnamed>" on maemo */
+  gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
+#else
+  gtk_window_set_title (GTK_WINDOW (dialog), "");
+#endif /* HAVE_HILDON */
+
+  g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+
+  gtk_window_present (GTK_WINDOW (dialog));
+}
diff --git a/aisleriot/lib/ar-show.h b/aisleriot/lib/ar-show.h
new file mode 100644
index 0000000..3ea6335
--- /dev/null
+++ b/aisleriot/lib/ar-show.h
@@ -0,0 +1,38 @@
+/*
+ *  Copyright © 2008 Thomas H.P. Andersen <phomes gmail com>
+ *  Copyright © 2007, 2008, 2009 Christian Persch
+ *
+ *  This runtime 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This runtime is distributed in the hope runtime it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this runtime; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef AR_SHOW_H
+#define AR_SHOW_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+gboolean ar_show_uri   (GdkScreen *screen,
+                        const char *uri,
+                        guint32 timestamp,
+                        GError **error);
+void     ar_show_error (GtkWidget *window,
+                        GError *error,
+                        const char *primary_text_format,
+                        ...) G_GNUC_PRINTF (3, 4);
+
+G_END_DECLS
+
+#endif /* !AR_SHOW_H */
diff --git a/aisleriot/lib/ar-sound.c b/aisleriot/lib/ar-sound.c
new file mode 100644
index 0000000..a752d76
--- /dev/null
+++ b/aisleriot/lib/ar-sound.c
@@ -0,0 +1,285 @@
+/*
+ * games-sound.c: common sound player for gnome-games 
+ *
+ * Copyright © 2007-2008 Andreas Røsdal
+ * Copyright © 2009 Christian Persch
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#ifdef ENABLE_SOUND
+#include <canberra-gtk.h>
+#endif
+
+#include "ar-debug.h"
+#include "ar-runtime.h"
+
+#include "ar-sound.h"
+
+#ifdef ENABLE_SOUND
+
+#ifdef CA_CHECK_VERSION
+#if CA_CHECK_VERSION (0, 13)
+#define HAVE_CANBERRA_GTK_MULTIHEAD_SAFE
+#endif
+#endif
+
+static gboolean sound_enabled = FALSE;
+
+typedef enum {
+  GAMES_SOUND_PLAIN,
+  GAMES_SOUND_FOR_EVENT,
+  GAMES_SOUND_FOR_WIDGET
+} GamesSoundCanberraType;
+
+static void
+ar_sound_canberra_play (const char *sound_name,
+                           GamesSoundCanberraType type,
+                           gpointer data)
+{
+  char *name, *path;
+  int rv;
+
+  if (!sound_enabled)
+    return;
+
+  name = g_strdup_printf ("%s.ogg", sound_name);
+  path = ar_runtime_get_file (AR_RUNTIME_SOUND_DIRECTORY, name);
+  g_free (name);
+
+  switch (type) {
+    case GAMES_SOUND_PLAIN:
+#if 1
+      /* FIXMEchpe: instead, make sure all games call ar_sound_init()
+       * themselves!
+       */
+      ar_sound_init (data);
+#endif
+
+#ifdef GNOME_ENABLE_DEBUG
+      _AR_DEBUG_IF (AR_DEBUG_SOUND) {
+        if (!GPOINTER_TO_INT (g_object_get_data (G_OBJECT (data), "games-sound-initialised")))
+          ar_debug_print (AR_DEBUG_SOUND,
+                              "ar_sound_play_for_screen called for display %s screen %d but canberra-gtk context not initialised!",
+                              gdk_display_get_name (gdk_screen_get_display (data)),
+                              gdk_screen_get_number (data));
+      }
+#endif /* GNOME_ENABLE_DEBUG */
+
+      rv =  ca_context_play (
+#ifdef HAVE_CANBERRA_GTK_MULTIHEAD_SAFE
+                             ca_gtk_context_get_for_screen (data),
+#else
+                             ca_gtk_context_get (),
+#endif /* HAVE_CANBERRA_GTK_MULTIHEAD_SAFE */
+                             0,
+                             CA_PROP_MEDIA_NAME, sound_name,
+                             CA_PROP_MEDIA_FILENAME, path,
+                             NULL);
+      break;
+    case GAMES_SOUND_FOR_EVENT:
+      rv =  ca_gtk_play_for_event (data,
+                                   0,
+                                   CA_PROP_MEDIA_NAME, sound_name,
+                                   CA_PROP_MEDIA_FILENAME, path,
+                                   NULL);
+      break;
+    case GAMES_SOUND_FOR_WIDGET:
+      rv =  ca_gtk_play_for_widget (data,
+                                    0,
+                                    CA_PROP_MEDIA_NAME, sound_name,
+                                    CA_PROP_MEDIA_FILENAME, path,
+                                    NULL);
+      break;
+    default:
+      g_assert_not_reached ();
+  }
+
+  ar_debug_print (AR_DEBUG_SOUND,
+                      "libcanberra playing sound %s [file %s]: %s\n",
+                      sound_name, path, ca_strerror (rv));
+
+  g_free (path);
+}
+
+#endif /* ENABLE_SOUND */
+
+/**
+ * ar_sound_init:
+ * @screen: a #GdkScreen
+ *
+ * Initialises the sound context of @screen. Should be called
+ * when creating a window on @screen, or when a window is moved to
+ * @screen. It is safe to call this multiple times for the same
+ * @screen.
+ */
+void
+ar_sound_init (GdkScreen *screen)
+{
+#ifdef ENABLE_SOUND
+  ca_context *context;
+
+  if (screen == NULL)
+    screen = gdk_screen_get_default ();
+
+  if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (screen), "games-sound-initialised")))
+    return;
+
+  ar_debug_print (AR_DEBUG_SOUND,
+                      "Initialising canberra-gtk context for display %s screen %d\n",
+                      gdk_display_get_name (gdk_screen_get_display (screen)),
+                      gdk_screen_get_number (screen));
+
+#ifdef HAVE_CANBERRA_GTK_MULTIHEAD_SAFE
+  context = ca_gtk_context_get_for_screen (screen);
+#else
+  context = ca_gtk_context_get ();
+#endif /* HAVE_CANBERRA_GTK_MULTIHEAD_SAFE */
+
+  if (!context)
+    return;
+
+  ca_context_change_props (context,
+                           CA_PROP_MEDIA_ROLE, "game",
+                           NULL);
+
+  g_object_set_data (G_OBJECT (screen), "games-sound-initialised", GINT_TO_POINTER (TRUE));
+#endif /* ENABLE_SOUND */
+}
+
+/**
+ * ar_sound_play:
+ * @sound_name: the sound file to player
+ * 
+ * Plays a sound with the given filename from the
+ * AR_RUNTIME_SOUND_DIRECTORY directory in .ogg format.
+ * Sound is played asynchronously; i.e. this function does not block
+ * until the sound has finished playing.
+ *
+ * Use ar_sound_play_for_screen(), ar_sound_play_for_event()
+ * or ar_sound_play_for_widget() instead.
+ */
+void
+ar_sound_play (const gchar * sound_name)
+{
+  ar_sound_play_for_screen (sound_name, gdk_screen_get_default ());
+}
+    
+/**
+ * ar_sound_play_for_screen:
+ * @sound_name: the sound file to player
+ * 
+ * Plays a sound with the given filename from the
+ * AR_RUNTIME_SOUND_DIRECTORY directory in .ogg format.
+ * Sound is played asynchronously; i.e. this function does not block
+ * until the sound has finished playing.
+ *
+ * Consider using ar_sound_play_for_event() or ar_sound_play_for_widget()
+ * instead.
+ */
+void
+ar_sound_play_for_screen (const gchar * sound_name,
+                             GdkScreen *screen)
+{
+#if defined(ENABLE_SOUND)
+  ar_sound_canberra_play (sound_name, GAMES_SOUND_PLAIN, screen);
+#endif
+}
+
+/**
+ * ar_sound_play_for_event:
+ * @sound_name: the name of the sound to play
+ * @event: the #GdkEvent associated with the sound
+ *
+ * Plays a sound for @event.
+ * See ar_sound_play() for more information.
+ */
+void
+ar_sound_play_for_event (const gchar *sound_name,
+                            GdkEvent *event)
+{
+#ifdef ENABLE_SOUND
+  ar_sound_canberra_play (sound_name, GAMES_SOUND_FOR_EVENT, event);
+#endif /* ENABLE_SOUND */
+}
+
+/**
+ * ar_sound_play_for_widget:
+ * @sound_name: the name of the sound to play
+ * @widget: the #GtkWidget to play the sound for
+ *
+ * Plays a sound for @widget. Use ar_sound_play_for_event() instead
+ * if the sound is associated with an event.
+ * 
+ * See ar_sound_play() for more information.
+ */
+void
+ar_sound_play_for_widget (const gchar *sound_name,
+                             GtkWidget *widget)
+{
+#ifdef ENABLE_SOUND
+  ar_sound_canberra_play (sound_name, GAMES_SOUND_FOR_WIDGET, widget);
+#endif /* ENABLE_SOUND */
+}
+
+/**
+ * ar_sound_enable:
+ * @enabled:
+ *
+ * Enables or disables sound support.
+ */
+void
+ar_sound_enable (gboolean enabled)
+{
+#ifdef ENABLE_SOUND
+  sound_enabled = enabled;
+#endif /* ENABLE_SOUND */
+}
+
+/**
+ * ar_sound_is_enabled:
+ *
+ * Returns: %TRUE iff sound support is enabled.
+ */
+gboolean
+ar_sound_is_enabled (void)
+{
+#ifdef ENABLE_SOUND
+  return sound_enabled;
+#else
+  return FALSE;
+#endif /* ENABLE_SOUND */
+}
+
+/**
+ * ar_sound_is_available:
+ *
+ * Returns: whether sound is available
+ */
+gboolean
+ar_sound_is_available (void)
+{
+#ifdef ENABLE_SOUND
+  return TRUE;
+#else
+  return FALSE;
+#endif /* ENABLE_SOUND */
+}
diff --git a/aisleriot/lib/ar-sound.h b/aisleriot/lib/ar-sound.h
new file mode 100644
index 0000000..38a2543
--- /dev/null
+++ b/aisleriot/lib/ar-sound.h
@@ -0,0 +1,45 @@
+/*
+ * games-sound.h: common sound player for gnome-games
+ *
+ * Copyright © 2007-2008 Andreas Røsdal
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef GAMES_SOUND_H
+#define GAMES_SOUND_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+gboolean ar_sound_is_available (void);
+void ar_sound_init             (GdkScreen *screen);
+#ifndef GDK_MULTIHEAD_SAFE
+void ar_sound_play             (const gchar *sound_name);
+#endif
+void ar_sound_play_for_screen  (const gchar *sound_name,
+                                GdkScreen *screen);
+void ar_sound_play_for_event   (const gchar *sound_name,
+                                GdkEvent *event);
+void ar_sound_play_for_widget  (const gchar *sound_name,
+                                GtkWidget *widget);
+void ar_sound_enable           (gboolean enabled);
+gboolean ar_sound_is_enabled   (void);
+
+G_END_DECLS
+
+#endif /* !GAMES_SOUND_H */
diff --git a/aisleriot/lib/ar-string-utils.c b/aisleriot/lib/ar-string-utils.c
new file mode 100644
index 0000000..b5d8bef
--- /dev/null
+++ b/aisleriot/lib/ar-string-utils.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright © 1998, 2001, 2003, 2006 Jonathan Blandford <jrb alum mit edu>
+ * Copyright © 2007 Christian Persch
+ *
+ * Copyright © 2007 Christian Persch
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+
+#include "ar-string-utils.h"
+
+/**
+ * ar_filename_to_display_name:
+ * @filename:
+ *
+ * Transforms @filename from filename encoding into a
+ * translated string in UTF-8 that can be shown to the
+ * user.
+ *
+ * Returns: a newly allocated UTF-8 string
+ */
+char *
+ar_filename_to_display_name (const char *filename)
+{
+  char *base_name, *display_name, *translated, *p;
+  GString *prettified_name;
+  gboolean start_of_word, free_segment;
+  gunichar c;
+  char utf8[7];
+  gsize len;
+
+  g_return_val_if_fail (filename != NULL, NULL);
+
+  base_name = g_path_get_basename (filename);
+  g_return_val_if_fail (base_name != NULL, NULL);
+
+  /* Hide extension */
+  g_strdelimit (base_name, ".", '\0');
+  /* Hide undesirable characters */
+  g_strdelimit (base_name, NULL, ' ');
+
+  g_strstrip (base_name);
+
+  display_name = g_filename_display_name (base_name);
+  g_free (base_name);
+
+  g_return_val_if_fail (display_name != NULL, NULL);
+
+  /* Now turn the first character in each word to uppercase */
+
+  prettified_name = g_string_sized_new (strlen (display_name) + 8);
+  start_of_word = TRUE;
+  for (p = display_name; p && *p; p = g_utf8_next_char (p)) {
+    if (start_of_word) {
+      c = g_unichar_toupper (g_utf8_get_char (p));
+    } else {
+      c = g_utf8_get_char (p);
+    }
+
+    len = g_unichar_to_utf8 (c, utf8);
+    g_string_append_len (prettified_name, utf8, len);
+
+    start_of_word = g_unichar_isspace (c);
+  }
+  g_free (display_name);
+
+  translated = gettext (prettified_name->str);
+  if (translated != prettified_name->str) {
+    display_name = g_strdup (translated);
+    free_segment = TRUE;
+  } else {
+    display_name = prettified_name->str;
+    free_segment = FALSE;
+  }
+
+  g_string_free (prettified_name, free_segment);
+
+  return display_name;
+}
diff --git a/aisleriot/lib/ar-string-utils.h b/aisleriot/lib/ar-string-utils.h
new file mode 100644
index 0000000..272ae81
--- /dev/null
+++ b/aisleriot/lib/ar-string-utils.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2007 Christian Persch
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GAMES_STRING_UTILS_H
+#define GAMES_STRING_UTILS_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+char *ar_filename_to_display_name (const char *filename);
+
+G_END_DECLS
+
+#endif /* !GAMES_STRING_UTILS_H */
diff --git a/aisleriot/lib/org.gnome.Patience.WindowState.gschema.xml.in b/aisleriot/lib/org.gnome.Patience.WindowState.gschema.xml.in
new file mode 100644
index 0000000..38861ff
--- /dev/null
+++ b/aisleriot/lib/org.gnome.Patience.WindowState.gschema.xml.in
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright © 2010 Christian Persch
+
+  This program 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 3, or (at your option)
+  any later version.
+
+  This program is distributed in the hope conf it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+-->
+<schemalist>
+  <!-- This schema is relocatable -->
+  <schema id="org.gnome.Patience.WindowState" gettext-domain="gnome-games">
+    <key name="maximized" type="b">
+      <default>false</default>
+      <_summary>Whether the window is maximized</_summary>
+    </key>
+    <key name="fullscreen" type="b">
+      <default>false</default>
+      <_summary>Whether the window is fullscreen</_summary>
+    </key>
+    <key name="width" type="i">
+      <!-- <range min="-1" max="65535" /> -->
+      <default>-1</default>
+      <_summary>Window width</_summary>
+    </key>
+    <key name="height" type="i">
+      <!-- <range min="-1" max="65535" /> -->
+      <default>-1</default>
+      <_summary>Window height</_summary>
+    </key>
+  </schema>
+</schemalist>
diff --git a/aisleriot/lib/render-cards.c b/aisleriot/lib/render-cards.c
index 662f487..4709844 100644
--- a/aisleriot/lib/render-cards.c
+++ b/aisleriot/lib/render-cards.c
@@ -26,8 +26,8 @@
 
 #include <gtk/gtk.h>
 
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-string-utils.h>
+#include "ar-runtime.h"
+#include "ar-string-utils.h"
 
 #include "ar-card-theme.h"
 #include "ar-card-themes.h"
@@ -69,7 +69,7 @@ main (int argc, char *argv[])
     { NULL }
   };
 
-  if (!games_runtime_init ("aisleriot"))
+  if (!ar_runtime_init ("aisleriot"))
     exit (1);
 
   if (!gtk_init_with_args
@@ -133,9 +133,9 @@ main (int argc, char *argv[])
 
   theme_filename = g_strdup_printf ("%s.svg", theme_name);
   theme_info = _ar_card_theme_info_new (AR_TYPE_CARD_THEME_SVG,
-                                        theme_dir ? theme_dir : games_runtime_get_directory (GAMES_RUNTIME_SCALABLE_CARDS_DIRECTORY),
+                                        theme_dir ? theme_dir : ar_runtime_get_directory (AR_RUNTIME_SCALABLE_CARDS_DIRECTORY),
                                         theme_filename,
-                                        games_filename_to_display_name (theme_name),
+                                        ar_filename_to_display_name (theme_name),
                                         g_strdup_printf ("svg:%s", theme_filename) /* FIXMEchpe is this correct? */,
                                         TRUE /* scalable */,
                                         NULL, NULL);
diff --git a/aisleriot/slot-renderer.c b/aisleriot/slot-renderer.c
index 8e233c7..ca2ae45 100644
--- a/aisleriot/slot-renderer.c
+++ b/aisleriot/slot-renderer.c
@@ -26,7 +26,7 @@
 #include "slot-renderer.h"
 #include "card.h"
 
-#include <libgames-support/games-glib-compat.h>
+#include "ar-glib-compat.h"
 
 static void aisleriot_slot_renderer_dispose (GObject *object);
 static void aisleriot_slot_renderer_finalize (GObject *object);
diff --git a/aisleriot/smclient/Makefile.am b/aisleriot/smclient/Makefile.am
new file mode 100644
index 0000000..183e8a5
--- /dev/null
+++ b/aisleriot/smclient/Makefile.am
@@ -0,0 +1,41 @@
+NULL =
+
+noinst_LTLIBRARIES = libsmclient.la
+
+libsmclient_la_SOURCES = \
+	eggsmclient.c \
+	eggsmclient.h \
+	eggsmclient-private.h \
+	$(NULL)
+
+libsmclient_la_CPPFLAGS = \
+	-DPKGDATADIR="\"$(pkgdatadir)\"" \
+	-DPREFIX="\"$(prefix)\"" \
+	-DDATADIR="\"$(datadir)\"" \
+	$(AM_CPPFLAGS)
+
+libsmclient_la_CFLAGS = \
+	$(GTK_CFLAGS) \
+	$(SMCLIENT_CFLAGS) \
+	$(AM_CFLAGS)
+
+libsmclient_la_LIBADD = \
+	$(GTK_LIBS) \
+	$(SMCLIENT_LIBS)
+
+if WITH_SMCLIENT_XSMP
+libsmclient_la_SOURCES += \
+	eggdesktopfile.c \
+	eggdesktopfile.h \
+	eggsmclient-xsmp.c \
+	$(NULL)
+libsmclient_la_CPPFLAGS += -DEGG_SM_CLIENT_BACKEND_XSMP
+endif
+if WITH_SMCLIENT_WIN32
+libsmclient_la_SOURCES += eggsmclient-win32.c
+endif
+if WITH_SMCLIENT_QUARTZ
+libsmclient_la_SOURCES += eggsmclient-osx.c
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/aisleriot/smclient/eggdesktopfile.c b/aisleriot/smclient/eggdesktopfile.c
new file mode 100644
index 0000000..e3d19b2
--- /dev/null
+++ b/aisleriot/smclient/eggdesktopfile.c
@@ -0,0 +1,1518 @@
+/* eggdesktopfile.c - Freedesktop.Org Desktop Files
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * Based on gnome-desktop-item.c
+ * Copyright (C) 1999, 2000 Red Hat Inc.
+ * Copyright (C) 2001 George Lebl
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place -
+ * Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "eggdesktopfile.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+struct EggDesktopFile {
+  GKeyFile           *key_file;
+  char               *source;
+
+  char               *name, *icon;
+  EggDesktopFileType  type;
+  char                document_code;
+};
+
+/**
+ * egg_desktop_file_new:
+ * @desktop_file_path: path to a Freedesktop-style Desktop file
+ * @error: error pointer
+ *
+ * Creates a new #EggDesktopFile for @desktop_file.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new (const char *desktop_file_path, GError **error)
+{
+  GKeyFile *key_file;
+
+  key_file = g_key_file_new ();
+  if (!g_key_file_load_from_file (key_file, desktop_file_path, 0, error))
+    {
+      g_key_file_free (key_file);
+      return NULL;
+    }
+
+  return egg_desktop_file_new_from_key_file (key_file, desktop_file_path,
+					     error);
+}
+
+/**
+ * egg_desktop_file_new_from_data_dirs:
+ * @desktop_file_path: relative path to a Freedesktop-style Desktop file
+ * @error: error pointer
+ *
+ * Looks for @desktop_file_path in the paths returned from
+ * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
+ * a new #EggDesktopFile from it.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_data_dirs (const char  *desktop_file_path,
+				     GError     **error)
+{
+  EggDesktopFile *desktop_file;
+  GKeyFile *key_file;
+  char *full_path;
+
+  key_file = g_key_file_new ();
+  if (!g_key_file_load_from_data_dirs (key_file, desktop_file_path,
+				       &full_path, 0, error))
+    {
+      g_key_file_free (key_file);
+      return NULL;
+    }
+
+  desktop_file = egg_desktop_file_new_from_key_file (key_file,
+						     full_path,
+						     error);
+  g_free (full_path);
+  return desktop_file;
+}
+
+#if GLIB_CHECK_VERSION (2, 14, 0)
+
+/**
+ * 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;
+}
+
+#endif /* GLIB >= 2.14.0 */
+
+/**
+ * egg_desktop_file_new_from_key_file:
+ * @key_file: a #GKeyFile representing a desktop file
+ * @source: the path or URI that @key_file was loaded from, or %NULL
+ * @error: error pointer
+ *
+ * Creates a new #EggDesktopFile for @key_file. Assumes ownership of
+ * @key_file (on success or failure); you should consider @key_file to
+ * be freed after calling this function.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_key_file (GKeyFile    *key_file,
+				    const char  *source,
+				    GError     **error)
+{
+  EggDesktopFile *desktop_file;
+  char *version, *type;
+
+  if (!g_key_file_has_group (key_file, EGG_DESKTOP_FILE_GROUP))
+    {
+      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		   EGG_DESKTOP_FILE_ERROR_INVALID,
+		   _("File is not a valid .desktop file"));
+      g_key_file_free (key_file);
+      return NULL;
+    }
+
+  version = g_key_file_get_value (key_file, EGG_DESKTOP_FILE_GROUP,
+				  EGG_DESKTOP_FILE_KEY_VERSION,
+				  NULL);
+  if (version)
+    {
+      double version_num;
+      char *end;
+
+      version_num = g_ascii_strtod (version, &end);
+      if (*end)
+	{
+	  g_warning ("Invalid Version string '%s' in %s",
+		     version, source ? source : "(unknown)");
+	}
+      else if (version_num > 1.0)
+	{
+	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		       EGG_DESKTOP_FILE_ERROR_INVALID,
+		       _("Unrecognized desktop file Version '%s'"), version);
+	  g_free (version);
+	  g_key_file_free (key_file);
+	  return NULL;
+	}
+      g_free (version);
+    }
+
+  desktop_file = g_new0 (EggDesktopFile, 1);
+  desktop_file->key_file = key_file;
+
+  if (g_path_is_absolute (source))
+    desktop_file->source = g_filename_to_uri (source, NULL, NULL);
+  else
+    desktop_file->source = g_strdup (source);
+
+  desktop_file->name = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
+					      EGG_DESKTOP_FILE_KEY_NAME, error);
+  if (!desktop_file->name)
+    {
+      egg_desktop_file_free (desktop_file);
+      return NULL;
+    }
+
+  type = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
+				EGG_DESKTOP_FILE_KEY_TYPE, error);
+  if (!type)
+    {
+      egg_desktop_file_free (desktop_file);
+      return NULL;
+    }
+
+  if (!strcmp (type, "Application"))
+    {
+      char *exec, *p;
+
+      desktop_file->type = EGG_DESKTOP_FILE_TYPE_APPLICATION;
+
+      exec = g_key_file_get_string (key_file,
+				    EGG_DESKTOP_FILE_GROUP,
+				    EGG_DESKTOP_FILE_KEY_EXEC,
+				    error);
+      if (!exec)
+	{
+	  egg_desktop_file_free (desktop_file);
+	  g_free (type);
+	  return NULL;
+	}
+
+      /* See if it takes paths or URIs or neither */
+      for (p = exec; *p; p++)
+	{
+	  if (*p == '%')
+	    {
+	      if (p[1] == '\0' || strchr ("FfUu", p[1]))
+		{
+		  desktop_file->document_code = p[1];
+		  break;
+		}
+	      p++;
+	    }
+	}
+
+      g_free (exec);
+    }
+  else if (!strcmp (type, "Link"))
+    {
+      char *url;
+
+      desktop_file->type = EGG_DESKTOP_FILE_TYPE_LINK;
+
+      url = g_key_file_get_string (key_file,
+				   EGG_DESKTOP_FILE_GROUP,
+				   EGG_DESKTOP_FILE_KEY_URL,
+				   error);
+      if (!url)
+	{
+	  egg_desktop_file_free (desktop_file);
+	  g_free (type);
+	  return NULL;
+	}
+      g_free (url);
+    }
+  else if (!strcmp (type, "Directory"))
+    desktop_file->type = EGG_DESKTOP_FILE_TYPE_DIRECTORY;
+  else
+    desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED;
+
+  g_free (type);
+
+  /* Check the Icon key */
+  desktop_file->icon = g_key_file_get_string (key_file,
+					      EGG_DESKTOP_FILE_GROUP,
+					      EGG_DESKTOP_FILE_KEY_ICON,
+					      NULL);
+  if (desktop_file->icon && !g_path_is_absolute (desktop_file->icon))
+    {
+      char *ext;
+
+      /* Lots of .desktop files still get this wrong */
+      ext = strrchr (desktop_file->icon, '.');
+      if (ext && (!strcmp (ext, ".png") ||
+		  !strcmp (ext, ".xpm") ||
+		  !strcmp (ext, ".svg")))
+	{
+	  g_warning ("Desktop file '%s' has malformed Icon key '%s'"
+		     "(should not include extension)",
+		     source ? source : "(unknown)",
+		     desktop_file->icon);
+	  *ext = '\0';
+	}
+    }
+
+  return desktop_file;
+}
+
+/**
+ * egg_desktop_file_free:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Frees @desktop_file.
+ **/
+void
+egg_desktop_file_free (EggDesktopFile *desktop_file)
+{
+  g_key_file_free (desktop_file->key_file);
+  g_free (desktop_file->source);
+  g_free (desktop_file->name);
+  g_free (desktop_file->icon);
+  g_free (desktop_file);
+}
+
+/**
+ * egg_desktop_file_get_source:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the URI that @desktop_file was loaded from.
+ *
+ * Return value: @desktop_file's source URI
+ **/
+const char *
+egg_desktop_file_get_source (EggDesktopFile *desktop_file)
+{
+  return desktop_file->source;
+}
+
+/**
+ * egg_desktop_file_get_desktop_file_type:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the desktop file type of @desktop_file.
+ *
+ * Return value: @desktop_file's type
+ **/
+EggDesktopFileType
+egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file)
+{
+  return desktop_file->type;
+}
+
+/**
+ * egg_desktop_file_get_name:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the (localized) value of @desktop_file's "Name" key.
+ *
+ * Return value: the application/link name
+ **/
+const char *
+egg_desktop_file_get_name (EggDesktopFile *desktop_file)
+{
+  return desktop_file->name;
+}
+
+/**
+ * egg_desktop_file_get_icon:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the value of @desktop_file's "Icon" key.
+ *
+ * If the icon string is a full path (that is, if g_path_is_absolute()
+ * returns %TRUE when called on it), it points to a file containing an
+ * unthemed icon. If the icon string is not a full path, it is the
+ * name of a themed icon, which can be looked up with %GtkIconTheme,
+ * or passed directly to a theme-aware widget like %GtkImage or
+ * %GtkCellRendererPixbuf.
+ *
+ * Return value: the icon path or name
+ **/
+const char *
+egg_desktop_file_get_icon (EggDesktopFile *desktop_file)
+{
+  return desktop_file->icon;
+}
+
+gboolean
+egg_desktop_file_has_key (EggDesktopFile  *desktop_file,
+			  const char      *key,
+			  GError         **error)
+{
+  return g_key_file_has_key (desktop_file->key_file,
+			     EGG_DESKTOP_FILE_GROUP, key,
+			     error);
+}
+
+char *
+egg_desktop_file_get_string (EggDesktopFile  *desktop_file,
+			     const char      *key,
+			     GError         **error)
+{
+  return g_key_file_get_string (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP, key,
+				error);
+}
+
+char *
+egg_desktop_file_get_locale_string (EggDesktopFile  *desktop_file,
+				    const char      *key,
+				    const char      *locale,
+				    GError         **error)
+{
+  return g_key_file_get_locale_string (desktop_file->key_file,
+				       EGG_DESKTOP_FILE_GROUP, key, locale,
+				       error);
+}
+
+gboolean
+egg_desktop_file_get_boolean (EggDesktopFile  *desktop_file,
+			      const char      *key,
+			      GError         **error)
+{
+  return g_key_file_get_boolean (desktop_file->key_file,
+				 EGG_DESKTOP_FILE_GROUP, key,
+				 error);
+}
+
+double
+egg_desktop_file_get_numeric (EggDesktopFile  *desktop_file,
+			      const char      *key,
+			      GError         **error)
+{
+#if GLIB_CHECK_VERSION (2, 12, 0)
+  return g_key_file_get_double (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP, key,
+				error);
+#else
+  return 0.0;
+#endif
+}
+
+char **
+egg_desktop_file_get_string_list (EggDesktopFile  *desktop_file,
+				  const char      *key,
+				  gsize           *length,
+				  GError         **error)
+{
+  return g_key_file_get_string_list (desktop_file->key_file,
+				     EGG_DESKTOP_FILE_GROUP, key, length,
+				     error);
+}
+
+char **
+egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file,
+					 const char      *key,
+					 const char      *locale,
+					 gsize           *length,
+					 GError         **error)
+{
+  return g_key_file_get_locale_string_list (desktop_file->key_file,
+					    EGG_DESKTOP_FILE_GROUP, key,
+					    locale, length,
+					    error);
+}
+
+/**
+ * egg_desktop_file_can_launch:
+ * @desktop_file: an #EggDesktopFile
+ * @desktop_environment: the name of the running desktop environment,
+ * or %NULL
+ *
+ * Tests if @desktop_file can/should be launched in the current
+ * environment. If @desktop_environment is non-%NULL, @desktop_file's
+ * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that
+ * this desktop_file is appropriate for the named environment.
+ *
+ * Furthermore, if @desktop_file has type
+ * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is
+ * also checked, to make sure the binary it points to exists.
+ *
+ * egg_desktop_file_can_launch() does NOT check the value of the
+ * "Hidden" key.
+ *
+ * Return value: %TRUE if @desktop_file can be launched
+ **/
+gboolean
+egg_desktop_file_can_launch (EggDesktopFile *desktop_file,
+			     const char     *desktop_environment)
+{
+  char *try_exec, *found_program;
+  char **only_show_in, **not_show_in;
+  gboolean found;
+  int i;
+
+  if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION &&
+      desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK)
+    return FALSE;
+
+  if (desktop_environment)
+    {
+      only_show_in = g_key_file_get_string_list (desktop_file->key_file,
+						 EGG_DESKTOP_FILE_GROUP,
+						 EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN,
+						 NULL, NULL);
+      if (only_show_in)
+	{
+	  for (i = 0, found = FALSE; only_show_in[i] && !found; i++)
+	    {
+	      if (!strcmp (only_show_in[i], desktop_environment))
+		found = TRUE;
+	    }
+
+	  g_strfreev (only_show_in);
+
+	  if (!found)
+	    return FALSE;
+	}
+
+      not_show_in = g_key_file_get_string_list (desktop_file->key_file,
+						EGG_DESKTOP_FILE_GROUP,
+						EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN,
+						NULL, NULL);
+      if (not_show_in)
+	{
+	  for (i = 0, found = FALSE; not_show_in[i] && !found; i++)
+	    {
+	      if (!strcmp (not_show_in[i], desktop_environment))
+		found = TRUE;
+	    }
+
+	  g_strfreev (not_show_in);
+
+	  if (found)
+	    return FALSE;
+	}
+    }
+
+  if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION)
+    {
+      try_exec = g_key_file_get_string (desktop_file->key_file,
+					EGG_DESKTOP_FILE_GROUP,
+					EGG_DESKTOP_FILE_KEY_TRY_EXEC,
+					NULL);
+      if (try_exec)
+	{
+	  found_program = g_find_program_in_path (try_exec);
+	  g_free (try_exec);
+
+	  if (!found_program)
+	    return FALSE;
+	  g_free (found_program);
+	}
+    }
+
+  return TRUE;
+}
+
+/**
+ * egg_desktop_file_accepts_documents:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file represents an application that can accept
+ * documents on the command line.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file)
+{
+  return desktop_file->document_code != 0;
+}
+
+/**
+ * egg_desktop_file_accepts_multiple:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file can accept multiple documents at once.
+ *
+ * If this returns %FALSE, you can still pass multiple documents to
+ * egg_desktop_file_launch(), but that will result in multiple copies
+ * of the application being launched. See egg_desktop_file_launch()
+ * for more details.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file)
+{
+  return (desktop_file->document_code == 'F' ||
+	  desktop_file->document_code == 'U');
+}
+
+/**
+ * egg_desktop_file_accepts_uris:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file can accept (non-"file:") URIs as documents to
+ * open.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file)
+{
+  return (desktop_file->document_code == 'U' ||
+	  desktop_file->document_code == 'u');
+}
+
+static void
+append_quoted_word (GString    *str,
+		    const char *s,
+		    gboolean    in_single_quotes,
+		    gboolean    in_double_quotes)
+{
+  const char *p;
+
+  if (!in_single_quotes && !in_double_quotes)
+    g_string_append_c (str, '\'');
+  else if (!in_single_quotes && in_double_quotes)
+    g_string_append (str, "\"'");
+
+  if (!strchr (s, '\''))
+    g_string_append (str, s);
+  else
+    {
+      for (p = s; *p != '\0'; p++)
+	{
+	  if (*p == '\'')
+	    g_string_append (str, "'\\''");
+	  else
+	    g_string_append_c (str, *p);
+	}
+    }
+
+  if (!in_single_quotes && !in_double_quotes)
+    g_string_append_c (str, '\'');
+  else if (!in_single_quotes && in_double_quotes)
+    g_string_append (str, "'\"");
+}
+
+static void
+do_percent_subst (EggDesktopFile *desktop_file,
+		  char            code,
+		  GString        *str,
+		  GSList        **documents,
+		  gboolean        in_single_quotes,
+		  gboolean        in_double_quotes)
+{
+  GSList *d;
+  char *doc;
+
+  switch (code)
+    {
+    case '%':
+      g_string_append_c (str, '%');
+      break;
+
+    case 'F':
+    case 'U':
+      for (d = *documents; d; d = d->next)
+	{
+	  doc = d->data;
+	  g_string_append (str, " ");
+	  append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
+	}
+      *documents = NULL;
+      break;
+
+    case 'f':
+    case 'u':
+      if (*documents)
+	{
+	  doc = (*documents)->data;
+	  g_string_append (str, " ");
+	  append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
+	  *documents = (*documents)->next;
+	}
+      break;
+
+    case 'i':
+      if (desktop_file->icon)
+	{
+	  g_string_append (str, "--icon ");
+	  append_quoted_word (str, desktop_file->icon,
+			      in_single_quotes, in_double_quotes);
+	}
+      break;
+
+    case 'c':
+      if (desktop_file->name)
+	{
+	  append_quoted_word (str, desktop_file->name,
+			      in_single_quotes, in_double_quotes);
+	}
+      break;
+
+    case 'k':
+      if (desktop_file->source)
+	{
+	  append_quoted_word (str, desktop_file->source,
+			      in_single_quotes, in_double_quotes);
+	}
+      break;
+
+    case 'D':
+    case 'N':
+    case 'd':
+    case 'n':
+    case 'v':
+    case 'm':
+      /* Deprecated; skip */
+      break;
+
+    default:
+      g_warning ("Unrecognized %%-code '%%%c' in Exec", code);
+      break;
+    }
+}
+
+static char *
+parse_exec (EggDesktopFile  *desktop_file,
+	    GSList         **documents,
+	    GError         **error)
+{
+  char *exec, *p, *command;
+  gboolean escape, single_quot, double_quot;
+  GString *gs;
+
+  exec = g_key_file_get_string (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP,
+				EGG_DESKTOP_FILE_KEY_EXEC,
+				error);
+  if (!exec)
+    return NULL;
+
+  /* Build the command */
+  gs = g_string_new (NULL);
+  escape = single_quot = double_quot = FALSE;
+
+  for (p = exec; *p != '\0'; p++)
+    {
+      if (escape)
+	{
+	  escape = FALSE;
+	  g_string_append_c (gs, *p);
+	}
+      else if (*p == '\\')
+	{
+	  if (!single_quot)
+	    escape = TRUE;
+	  g_string_append_c (gs, *p);
+	}
+      else if (*p == '\'')
+	{
+	  g_string_append_c (gs, *p);
+	  if (!single_quot && !double_quot)
+	    single_quot = TRUE;
+	  else if (single_quot)
+	    single_quot = FALSE;
+	}
+      else if (*p == '"')
+	{
+	  g_string_append_c (gs, *p);
+	  if (!single_quot && !double_quot)
+	    double_quot = TRUE;
+	  else if (double_quot)
+	    double_quot = FALSE;
+	}
+      else if (*p == '%' && p[1])
+	{
+	  do_percent_subst (desktop_file, p[1], gs, documents,
+			    single_quot, double_quot);
+	  p++;
+	}
+      else
+	g_string_append_c (gs, *p);
+    }
+
+  g_free (exec);
+  command = g_string_free (gs, FALSE);
+
+  /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */
+  if (g_key_file_has_key (desktop_file->key_file,
+			  EGG_DESKTOP_FILE_GROUP,
+			  EGG_DESKTOP_FILE_KEY_TERMINAL,
+			  NULL))
+    {
+      GError *terminal_error = NULL;
+      gboolean use_terminal =
+	g_key_file_get_boolean (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP,
+				EGG_DESKTOP_FILE_KEY_TERMINAL,
+				&terminal_error);
+      if (terminal_error)
+	{
+	  g_free (command);
+	  g_propagate_error (error, terminal_error);
+	  return NULL;
+	}
+
+      if (use_terminal)
+	{
+	  gs = g_string_new ("xdg-terminal ");
+	  append_quoted_word (gs, command, FALSE, FALSE);
+	  g_free (command);
+	  command = g_string_free (gs, FALSE);
+	}
+    }
+
+  return command;
+}
+
+static GSList *
+translate_document_list (EggDesktopFile *desktop_file, GSList *documents)
+{
+  gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file);
+  GSList *ret, *d;
+
+  for (d = documents, ret = NULL; d; d = d->next)
+    {
+      const char *document = d->data;
+      gboolean is_uri = !g_path_is_absolute (document);
+      char *translated;
+
+      if (accepts_uris)
+	{
+	  if (is_uri)
+	    translated = g_strdup (document);
+	  else
+	    translated = g_filename_to_uri (document, NULL, NULL);
+	}
+      else
+	{
+	  if (is_uri)
+	    translated = g_filename_from_uri (document, NULL, NULL);
+	  else
+	    translated = g_strdup (document);
+	}
+
+      if (translated)
+	ret = g_slist_prepend (ret, translated);
+    }
+
+  return g_slist_reverse (ret);
+}
+
+static void
+free_document_list (GSList *documents)
+{
+  GSList *d;
+
+  for (d = documents; d; d = d->next)
+    g_free (d->data);
+  g_slist_free (documents);
+}
+
+/**
+ * egg_desktop_file_parse_exec:
+ * @desktop_file: a #EggDesktopFile
+ * @documents: a list of document paths or URIs
+ * @error: error pointer
+ *
+ * Parses @desktop_file's Exec key, inserting @documents into it, and
+ * returns the result.
+ *
+ * If @documents contains non-file: URIs and @desktop_file does not
+ * accept URIs, those URIs will be ignored. Likewise, if @documents
+ * contains more elements than @desktop_file accepts, the extra
+ * documents will be ignored.
+ *
+ * Return value: the parsed Exec string
+ **/
+char *
+egg_desktop_file_parse_exec (EggDesktopFile  *desktop_file,
+			     GSList          *documents,
+			     GError         **error)
+{
+  GSList *translated, *docs;
+  char *command;
+
+  docs = translated = translate_document_list (desktop_file, documents);
+  command = parse_exec (desktop_file, &docs, error);
+  free_document_list (translated);
+
+  return command;
+}
+
+static gboolean
+parse_link (EggDesktopFile  *desktop_file,
+	    EggDesktopFile **app_desktop_file,
+	    GSList         **documents,
+	    GError         **error)
+{
+  char *url;
+  GKeyFile *key_file;
+
+  url = g_key_file_get_string (desktop_file->key_file,
+			       EGG_DESKTOP_FILE_GROUP,
+			       EGG_DESKTOP_FILE_KEY_URL,
+			       error);
+  if (!url)
+    return FALSE;
+  *documents = g_slist_prepend (NULL, url);
+
+  /* FIXME: use gvfs */
+  key_file = g_key_file_new ();
+  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+			 EGG_DESKTOP_FILE_KEY_NAME,
+			 "xdg-open");
+  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+			 EGG_DESKTOP_FILE_KEY_TYPE,
+			 "Application");
+  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+			 EGG_DESKTOP_FILE_KEY_EXEC,
+			 "xdg-open %u");
+  *app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL);
+  return TRUE;
+}
+
+#if GTK_CHECK_VERSION (2, 12, 0)
+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 */)
+
+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_seconds (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH,
+			 startup_notification_timeout, sn_data);
+}
+#endif /* GTK 2.12 */
+
+static GPtrArray *
+array_putenv (GPtrArray *env, char *variable)
+{
+  guint 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 = NULL;
+  char *command, **argv;
+  int argc, i, screen_num;
+  gboolean success, current_success;
+  GdkDisplay *display;
+  char *startup_id;
+
+  GPtrArray   *env = NULL;
+  char       **variables = NULL;
+  GdkScreen   *screen = NULL;
+  int          workspace = -1;
+  const char  *directory = NULL;
+  guint32      launch_time = (guint32)-1;
+  GSpawnFlags  flags = G_SPAWN_SEARCH_PATH;
+  GSpawnChildSetupFunc setup_func = NULL;
+  gpointer     setup_data = NULL;
+
+  GPid        *ret_pid = NULL;
+  int         *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL;
+  char       **ret_startup_id = NULL;
+
+  if (documents && desktop_file->document_code == 0)
+    {
+      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+		   _("Application does not accept documents on command line"));
+      return FALSE;
+    }
+
+  /* Read the options: technically it's incorrect for the caller to
+   * NULL-terminate the list of options (rather than 0-terminating
+   * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED,
+   * it's more consistent with other glib/gtk methods, and it will
+   * work as long as sizeof (int) <= sizeof (NULL), and NULL is
+   * represented as 0. (Which is true everywhere we care about.)
+   */
+  while ((option = va_arg (args, EggDesktopFileLaunchOption)))
+    {
+      switch (option)
+	{
+	case EGG_DESKTOP_FILE_LAUNCH_CLEARENV:
+	  if (env)
+	    g_ptr_array_free (env, TRUE);
+	  env = g_ptr_array_new ();
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_PUTENV:
+	  variables = va_arg (args, char **);
+	  for (i = 0; variables[i]; i++)
+	    env = array_putenv (env, variables[i]);
+	  break;
+
+	case EGG_DESKTOP_FILE_LAUNCH_SCREEN:
+	  screen = va_arg (args, GdkScreen *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE:
+	  workspace = va_arg (args, int);
+	  break;
+
+	case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY:
+	  directory = va_arg (args, const char *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_TIME:
+	  launch_time = va_arg (args, guint32);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_FLAGS:
+	  flags |= va_arg (args, GSpawnFlags);
+	  /* Make sure they didn't set any flags that don't make sense. */
+	  flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO;
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC:
+	  setup_func = va_arg (args, GSpawnChildSetupFunc);
+	  setup_data = va_arg (args, gpointer);
+	  break;
+
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID:
+	  ret_pid = va_arg (args, GPid *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE:
+	  ret_stdin = va_arg (args, int *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE:
+	  ret_stdout = va_arg (args, int *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE:
+	  ret_stderr = va_arg (args, int *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID:
+	  ret_startup_id = va_arg (args, char **);
+	  break;
+
+	default:
+	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		       EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION,
+		       _("Unrecognized launch option: %d"),
+		       GPOINTER_TO_INT (option));
+	  success = FALSE;
+	  goto out;
+	}
+    }
+
+  if (screen)
+    {
+      char *display_name = gdk_screen_make_display_name (screen);
+      char *display_env = g_strdup_printf ("DISPLAY=%s", display_name);
+      env = array_putenv (env, display_env);
+      g_free (display_name);
+      g_free (display_env);
+
+      display = gdk_screen_get_display (screen);
+    }
+  else
+    {
+      display = gdk_display_get_default ();
+      screen = gdk_display_get_default_screen (display);
+    }
+  screen_num = gdk_screen_get_number (screen);
+
+  translated_documents = translate_document_list (desktop_file, documents);
+  docs = translated_documents;
+
+  success = FALSE;
+
+  do
+    {
+      command = parse_exec (desktop_file, &docs, error);
+      if (!command)
+	goto out;
+
+      if (!g_shell_parse_argv (command, &argc, &argv, error))
+	{
+	  g_free (command);
+	  goto out;
+	}
+      g_free (command);
+
+#if GTK_CHECK_VERSION (2, 12, 0)
+      startup_id = start_startup_notification (display, desktop_file,
+					       argv[0], screen_num,
+					       workspace, launch_time);
+      if (startup_id)
+	{
+	  char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
+						  startup_id);
+	  env = array_putenv (env, startup_id_env);
+	  g_free (startup_id_env);
+	}
+#else
+      startup_id = NULL;
+#endif /* GTK 2.12 */
+
+      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 GTK_CHECK_VERSION (2, 12, 0)
+	  if (current_success)
+	    {
+	      set_startup_notification_timeout (display, startup_id);
+
+	      if (ret_startup_id)
+		*ret_startup_id = startup_id;
+	      else
+		g_free (startup_id);
+	    }
+	  else
+#endif /* GTK 2.12 */
+	    g_free (startup_id);
+	}
+      else if (ret_startup_id)
+	*ret_startup_id = NULL;
+
+      if (current_success)
+	{
+	  /* If we successfully launch any instances of the app, make
+	   * sure we return TRUE and don't set @error.
+	   */
+	  success = TRUE;
+	  error = NULL;
+
+	  /* Also, only set the output params on the first one */
+	  ret_pid = NULL;
+	  ret_stdin = ret_stdout = ret_stderr = NULL;
+	  ret_startup_id = NULL;
+	}
+    }
+  while (docs && current_success);
+
+ out:
+  if (env)
+    {
+      g_ptr_array_foreach (env, (GFunc)g_free, NULL);
+      g_ptr_array_free (env, TRUE);
+    }
+  free_document_list (translated_documents);
+
+  return success;
+}
+
+/**
+ * egg_desktop_file_launch:
+ * @desktop_file: an #EggDesktopFile
+ * @documents: a list of URIs or paths to documents to open
+ * @error: error pointer
+ * @...: additional options
+ *
+ * Launches @desktop_file with the given arguments. Additional options
+ * can be specified as follows:
+ *
+ *   %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments)
+ *       clears the environment in the child process
+ *   %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables)
+ *       adds the NAME=VALUE strings in the given %NULL-terminated
+ *       array to the child process's environment
+ *   %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen)
+ *       causes the application to be launched on the given screen
+ *   %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace)
+ *       causes the application to be launched on the given workspace
+ *   %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir)
+ *       causes the application to be launched in the given directory
+ *   %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time)
+ *       sets the "launch time" for the application. If the user
+ *       interacts with another window after @launch_time but before
+ *       the launched application creates its first window, the window
+ *       manager may choose to not give focus to the new application.
+ *       Passing 0 for @launch_time will explicitly request that the
+ *       application not receive focus.
+ *   %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags)
+ *       Sets additional #GSpawnFlags to use. See g_spawn_async() for
+ *       more details.
+ *   %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer)
+ *       Sets the child setup callback and the data to pass to it.
+ *       (See g_spawn_async() for more details.)
+ *
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid)
+ *       On a successful launch, sets * pid to the PID of the launched
+ *       application.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id)
+ *       On a successful launch, sets * startup_id to the Startup
+ *       Notification "startup id" of the launched application.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd)
+ *       On a successful launch, sets * fd to the file descriptor of
+ *       a pipe connected to the application's stdin.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd)
+ *       On a successful launch, sets * fd to the file descriptor of
+ *       a pipe connected to the application's stdout.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd)
+ *       On a successful launch, sets * fd to the file descriptor of
+ *       a pipe connected to the application's stderr.
+ *
+ * The options should be terminated with a single %NULL.
+ *
+ * If @documents contains multiple documents, but
+ * egg_desktop_file_accepts_multiple() returns %FALSE for
+ * @desktop_file, then egg_desktop_file_launch() will actually launch
+ * multiple instances of the application. In that case, the return
+ * value (as well as any values passed via
+ * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the
+ * first instance of the application that was launched (but the
+ * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each
+ * instance).
+ *
+ * Return value: %TRUE if the application was successfully launched.
+ **/
+gboolean
+egg_desktop_file_launch (EggDesktopFile *desktop_file,
+			 GSList *documents, GError **error,
+			 ...)
+{
+  va_list args;
+  gboolean success;
+  EggDesktopFile *app_desktop_file;
+
+  switch (desktop_file->type)
+    {
+    case EGG_DESKTOP_FILE_TYPE_APPLICATION:
+      va_start (args, error);
+      success = egg_desktop_file_launchv (desktop_file, documents,
+					  args, error);
+      va_end (args);
+      break;
+
+    case EGG_DESKTOP_FILE_TYPE_LINK:
+      if (documents)
+	{
+	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		       EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+		       _("Can't pass document URIs to a 'Type=Link' desktop entry"));
+	  return FALSE;
+	}	  
+
+      if (!parse_link (desktop_file, &app_desktop_file, &documents, error))
+	return FALSE;
+
+      va_start (args, error);
+      success = egg_desktop_file_launchv (app_desktop_file, documents,
+					  args, error);
+      va_end (args);
+
+      egg_desktop_file_free (app_desktop_file);
+      free_document_list (documents);
+      break;
+
+    case EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED:
+    case EGG_DESKTOP_FILE_TYPE_DIRECTORY:
+    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;
+
+static void
+egg_set_desktop_file_internal (const char *desktop_file_path,
+                               gboolean set_defaults)
+{
+  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);
+    }
+
+  if (set_defaults && egg_desktop_file != NULL) {
+    /* 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_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.
+ * See egg_set_desktop_file_without_defaults() for a variant of this
+ * function that does not set the application name and default window
+ * icon.
+ *
+ * Note that for thread safety reasons, this function can only
+ * be called once, and is mutually exclusive with calling
+ * egg_set_desktop_file_without_defaults().
+ **/
+void
+egg_set_desktop_file (const char *desktop_file_path)
+{
+  egg_set_desktop_file_internal (desktop_file_path, TRUE);
+}
+
+/**
+ * egg_set_desktop_file_without_defaults:
+ * @desktop_file_path: path to the application's desktop file
+ *
+ * Creates an #EggDesktopFile for the application from the data at
+ * @desktop_file_path.
+ * See egg_set_desktop_file() for a variant of this function that
+ * sets the application name and default window icon from the information
+ * in the desktop file.
+ *
+ * Note that for thread safety reasons, this function can only
+ * be called once, and is mutually exclusive with calling
+ * egg_set_desktop_file().
+ **/
+void
+egg_set_desktop_file_without_defaults (const char *desktop_file_path)
+{
+  egg_set_desktop_file_internal (desktop_file_path, FALSE);
+}
+
+/**
+ * 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/aisleriot/smclient/eggdesktopfile.h b/aisleriot/smclient/eggdesktopfile.h
new file mode 100644
index 0000000..18fe463
--- /dev/null
+++ b/aisleriot/smclient/eggdesktopfile.h
@@ -0,0 +1,160 @@
+/* eggdesktopfile.h - Freedesktop.Org Desktop Files
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place -
+ * Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_DESKTOP_FILE_H__
+#define __EGG_DESKTOP_FILE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct EggDesktopFile EggDesktopFile;
+
+typedef enum {
+	EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED,
+
+	EGG_DESKTOP_FILE_TYPE_APPLICATION,
+	EGG_DESKTOP_FILE_TYPE_LINK,
+	EGG_DESKTOP_FILE_TYPE_DIRECTORY
+} EggDesktopFileType;
+
+EggDesktopFile     *egg_desktop_file_new                (const char   *desktop_file_path,
+							 GError      **error);
+
+EggDesktopFile     *egg_desktop_file_new_from_data_dirs (const char   *desktop_file_path,
+							 GError      **error);
+EggDesktopFile     *egg_desktop_file_new_from_dirs      (const char   *desktop_file_path,
+							 const char  **search_dirs,
+							 GError      **error);
+EggDesktopFile     *egg_desktop_file_new_from_key_file  (GKeyFile     *key_file,
+							 const char   *source,
+							 GError      **error);
+
+void                egg_desktop_file_free               (EggDesktopFile  *desktop_file);
+
+const char         *egg_desktop_file_get_source         (EggDesktopFile  *desktop_file);
+
+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);
+void            egg_set_desktop_file_without_defaults (const char *desktop_file_path);
+EggDesktopFile *egg_get_desktop_file                  (void);
+
+
+G_END_DECLS
+
+#endif /* __EGG_DESKTOP_FILE_H__ */
diff --git a/aisleriot/smclient/eggsmclient-dbus.c b/aisleriot/smclient/eggsmclient-dbus.c
new file mode 100644
index 0000000..21d9199
--- /dev/null
+++ b/aisleriot/smclient/eggsmclient-dbus.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2008 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "eggsmclient.h"
+#include "eggsmclient-private.h"
+
+#include "eggdesktopfile.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <gdk/gdk.h>
+#include <dbus/dbus-glib.h>
+
+#define GSM_DBUS_NAME      "org.gnome.SessionManager"
+#define GSM_DBUS_PATH      "/org/gnome/SessionManager"
+#define GSM_DBUS_INTERFACE "org.gnome.SessionManager"
+
+#define GSM_CLIENT_PRIVATE_DBUS_INTERFACE "org.gnome.SessionManager.ClientPrivate"
+#define GSM_CLIENT_DBUS_INTERFACE         "org.gnome.SessionManager.Client"
+
+#define EGG_TYPE_SM_CLIENT_DBUS            (egg_sm_client_dbus_get_type ())
+#define EGG_SM_CLIENT_DBUS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_DBUS, EggSMClientDBus))
+#define EGG_SM_CLIENT_DBUS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_DBUS, EggSMClientDBusClass))
+#define EGG_IS_SM_CLIENT_DBUS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_DBUS))
+#define EGG_IS_SM_CLIENT_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_DBUS))
+#define EGG_SM_CLIENT_DBUS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_DBUS, EggSMClientDBusClass))
+
+typedef struct _EggSMClientDBus        EggSMClientDBus;
+typedef struct _EggSMClientDBusClass   EggSMClientDBusClass;
+
+struct _EggSMClientDBus
+{
+  EggSMClient parent;
+
+  DBusGConnection *conn;
+  DBusGProxy *sm_proxy, *client_proxy;
+  char *client_path;
+};
+
+struct _EggSMClientDBusClass
+{
+  EggSMClientClass parent_class;
+
+};
+
+static void     sm_client_dbus_startup     (EggSMClient *client,
+					    const char  *client_id);
+static void     sm_client_dbus_will_quit   (EggSMClient *client,
+					    gboolean     will_quit);
+static gboolean sm_client_dbus_end_session (EggSMClient *client,
+					    EggSMClientEndStyle style,
+					    gboolean     request_confirmation);
+
+static void dbus_client_query_end_session  (DBusGProxy  *proxy,
+					    guint        flags,
+					    gpointer     smclient);
+static void dbus_client_end_session        (DBusGProxy  *proxy,
+					    guint        flags,
+					    gpointer     smclient);
+static void dbus_client_cancel_end_session (DBusGProxy  *proxy,
+					    gpointer     smclient);
+static void dbus_client_stop               (DBusGProxy  *proxy,
+					    gpointer     smclient);
+
+G_DEFINE_TYPE (EggSMClientDBus, egg_sm_client_dbus, EGG_TYPE_SM_CLIENT)
+
+static void
+egg_sm_client_dbus_init (EggSMClientDBus *dbus)
+{
+  ;
+}
+
+static void
+egg_sm_client_dbus_class_init (EggSMClientDBusClass *klass)
+{
+  EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
+
+  sm_client_class->startup     = sm_client_dbus_startup;
+  sm_client_class->will_quit   = sm_client_dbus_will_quit;
+  sm_client_class->end_session = sm_client_dbus_end_session;
+}
+
+EggSMClient *
+egg_sm_client_dbus_new (void)
+{
+  DBusGConnection *conn;
+  DBusGProxy *proxy;
+  EggSMClientDBus *dbus;
+
+  conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+  if (!conn)
+    return NULL;
+
+  proxy = dbus_g_proxy_new_for_name (conn, GSM_DBUS_NAME, GSM_DBUS_PATH,
+				     GSM_DBUS_INTERFACE);
+  if (!proxy)
+    {
+      g_object_unref (conn);
+      return NULL;
+    }
+
+  dbus = g_object_new (EGG_TYPE_SM_CLIENT_DBUS, NULL);
+  dbus->conn = conn;
+  dbus->sm_proxy = proxy;
+  return (EggSMClient *)dbus;
+}
+
+static void
+sm_client_dbus_startup (EggSMClient *client,
+			const char  *client_id)
+{
+  EggSMClientDBus *dbus = (EggSMClientDBus *)client;
+  GError *error = NULL;
+  char *client_path, *ret_client_id;
+  DBusGProxy *client_public;
+
+  if (!dbus_g_proxy_call (dbus->sm_proxy, "RegisterClient", &error,
+			  G_TYPE_STRING, g_get_prgname (),
+			  G_TYPE_STRING, client_id,
+			  G_TYPE_INVALID,
+			  DBUS_TYPE_G_OBJECT_PATH, &client_path,
+			  G_TYPE_INVALID))
+    {
+      g_warning ("Failed to register client: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  g_debug ("Client registered with session manager: %s", client_path);
+  dbus->client_proxy = dbus_g_proxy_new_for_name (dbus->conn, GSM_DBUS_NAME,
+                                                  client_path,
+                                                  GSM_CLIENT_PRIVATE_DBUS_INTERFACE);
+  dbus_g_proxy_add_signal (dbus->client_proxy, "QueryEndSession",
+			   G_TYPE_UINT,
+			   G_TYPE_INVALID);
+  dbus_g_proxy_connect_signal (dbus->client_proxy, "QueryEndSession",
+			       G_CALLBACK (dbus_client_query_end_session),
+			       dbus, NULL);
+  dbus_g_proxy_add_signal (dbus->client_proxy, "EndSession",
+			   G_TYPE_UINT,
+			   G_TYPE_INVALID);
+  dbus_g_proxy_connect_signal (dbus->client_proxy, "EndSession",
+			       G_CALLBACK (dbus_client_end_session),
+			       dbus, NULL);
+  dbus_g_proxy_add_signal (dbus->client_proxy, "CancelEndSession",
+			   G_TYPE_UINT,
+			   G_TYPE_INVALID);
+  dbus_g_proxy_connect_signal (dbus->client_proxy, "CancelEndSession",
+			       G_CALLBACK (dbus_client_cancel_end_session),
+			       dbus, NULL);
+  dbus_g_proxy_add_signal (dbus->client_proxy, "Stop",
+			   G_TYPE_INVALID);
+  dbus_g_proxy_connect_signal (dbus->client_proxy, "Stop",
+			       G_CALLBACK (dbus_client_stop),
+			       dbus, NULL);
+
+  client_public = dbus_g_proxy_new_for_name (dbus->conn, GSM_DBUS_NAME,
+					     client_path,
+					     GSM_CLIENT_DBUS_INTERFACE);
+  if (dbus_g_proxy_call (client_public, "GetStartupId", &error,
+			 G_TYPE_INVALID,
+			 G_TYPE_STRING, &ret_client_id,
+			 G_TYPE_INVALID))
+    {
+      gdk_threads_enter ();
+      gdk_set_sm_client_id (ret_client_id);
+      gdk_threads_leave ();
+
+      g_debug ("Got client ID \"%s\"", ret_client_id);
+      g_free (ret_client_id);
+    }
+  else
+    {
+      g_warning ("Could not get client id: %s", error->message);
+      g_error_free (error);
+    }
+  g_object_unref (client_public);
+}
+
+static void
+sm_client_dbus_shutdown (EggSMClient *client)
+{
+  EggSMClientDBus *dbus = EGG_SM_CLIENT_DBUS (client);
+  GError *error = NULL;
+
+  if (!dbus_g_proxy_call (dbus->sm_proxy, "UnregisterClient", &error,
+			  DBUS_TYPE_G_OBJECT_PATH, dbus->client_path,
+			  G_TYPE_INVALID,
+			  G_TYPE_INVALID))
+    {
+      g_warning ("Failed to unregister client: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  g_free (dbus->client_path);
+  dbus->client_path = NULL;
+
+  g_object_unref (dbus->client_proxy);
+  dbus->client_proxy = NULL;
+}
+
+static void
+dbus_client_query_end_session (DBusGProxy *proxy,
+			       guint       flags,
+			       gpointer    smclient)
+{
+  egg_sm_client_quit_requested (smclient);
+}
+
+static void
+sm_client_dbus_will_quit (EggSMClient *client,
+			  gboolean     will_quit)
+{
+  EggSMClientDBus *dbus = (EggSMClientDBus *)client;
+
+  g_return_if_fail (dbus->client_proxy != NULL);
+
+  dbus_g_proxy_call (dbus->client_proxy, "EndSessionResponse", NULL,
+		     G_TYPE_BOOLEAN, will_quit,
+		     G_TYPE_STRING, NULL,
+		     G_TYPE_INVALID,
+		     G_TYPE_INVALID);
+}
+
+static void
+dbus_client_end_session (DBusGProxy *proxy,
+			 guint       flags,
+			 gpointer    smclient)
+{
+  sm_client_dbus_will_quit (smclient, TRUE);
+  sm_client_dbus_shutdown (smclient);
+  egg_sm_client_quit (smclient);
+}
+
+static void
+dbus_client_cancel_end_session (DBusGProxy *proxy,
+				gpointer    smclient)
+{
+  egg_sm_client_quit_cancelled (smclient);
+}
+
+static void
+dbus_client_stop (DBusGProxy *proxy,
+		  gpointer    smclient)
+{
+  sm_client_dbus_shutdown (smclient);
+  egg_sm_client_quit (smclient);
+}
+
+static gboolean
+sm_client_dbus_end_session (EggSMClient         *client,
+			    EggSMClientEndStyle  style,
+			    gboolean             request_confirmation)
+{
+  EggSMClientDBus *dbus = (EggSMClientDBus *)client;
+
+  if (style == EGG_SM_CLIENT_END_SESSION_DEFAULT ||
+      style == EGG_SM_CLIENT_LOGOUT)
+    {
+      return dbus_g_proxy_call (dbus->sm_proxy, "Logout", NULL,
+				G_TYPE_UINT, request_confirmation ? 0 : 1,
+				G_TYPE_INVALID,
+				G_TYPE_INVALID);
+    }
+  else
+    {
+      return dbus_g_proxy_call (dbus->sm_proxy, "Shutdown", NULL,
+				G_TYPE_INVALID,
+				G_TYPE_INVALID);
+    }
+}
diff --git a/aisleriot/smclient/eggsmclient-osx.c b/aisleriot/smclient/eggsmclient-osx.c
new file mode 100644
index 0000000..7d3ff4b
--- /dev/null
+++ b/aisleriot/smclient/eggsmclient-osx.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ * Copyright (C) 2008 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* EggSMClientOSX
+ *
+ * For details on the OS X logout process, see:
+ * http://developer.apple.com/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/BootProcess.html#//apple_ref/doc/uid/20002130-114618
+ *
+ * EggSMClientOSX registers for the kAEQuitApplication AppleEvent; the
+ * handler we register (quit_requested()) will be invoked from inside
+ * the quartz event-handling code (specifically, from inside
+ * [NSApplication nextEventMatchingMask]) when an AppleEvent arrives.
+ * We use AESuspendTheCurrentEvent() and AEResumeTheCurrentEvent() to
+ * allow asynchronous / non-main-loop-reentering processing of the
+ * quit request. (These are part of the Carbon framework; it doesn't
+ * seem to be possible to handle AppleEvents asynchronously from
+ * Cocoa.)
+ */
+
+#include "config.h"
+
+#include "eggsmclient-private.h"
+#include <glib.h>
+#include <Carbon/Carbon.h>
+#include <CoreServices/CoreServices.h>
+
+#define EGG_TYPE_SM_CLIENT_OSX            (egg_sm_client_osx_get_type ())
+#define EGG_SM_CLIENT_OSX(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSX))
+#define EGG_SM_CLIENT_OSX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass))
+#define EGG_IS_SM_CLIENT_OSX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_OSX))
+#define EGG_IS_SM_CLIENT_OSX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_OSX))
+#define EGG_SM_CLIENT_OSX_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass))
+
+typedef struct _EggSMClientOSX        EggSMClientOSX;
+typedef struct _EggSMClientOSXClass   EggSMClientOSXClass;
+
+struct _EggSMClientOSX {
+  EggSMClient parent;
+
+  AppleEvent quit_event, quit_reply;
+  gboolean quit_requested, quitting;
+};
+
+struct _EggSMClientOSXClass
+{
+  EggSMClientClass parent_class;
+
+};
+
+static void     sm_client_osx_startup (EggSMClient *client,
+				       const char  *client_id);
+static void     sm_client_osx_will_quit (EggSMClient *client,
+					 gboolean     will_quit);
+static gboolean sm_client_osx_end_session (EggSMClient         *client,
+					   EggSMClientEndStyle  style,
+					   gboolean  request_confirmation);
+
+static pascal OSErr quit_requested (const AppleEvent *, AppleEvent *, long);
+
+G_DEFINE_TYPE (EggSMClientOSX, egg_sm_client_osx, EGG_TYPE_SM_CLIENT)
+
+static void
+egg_sm_client_osx_init (EggSMClientOSX *osx)
+{
+  ;
+}
+
+static void
+egg_sm_client_osx_class_init (EggSMClientOSXClass *klass)
+{
+  EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
+
+  sm_client_class->startup             = sm_client_osx_startup;
+  sm_client_class->will_quit           = sm_client_osx_will_quit;
+  sm_client_class->end_session         = sm_client_osx_end_session;
+}
+
+EggSMClient *
+egg_sm_client_osx_new (void)
+{
+  return g_object_new (EGG_TYPE_SM_CLIENT_OSX, NULL);
+}
+
+static void
+sm_client_osx_startup (EggSMClient *client,
+		       const char  *client_id)
+{
+  AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
+			 NewAEEventHandlerUPP (quit_requested),
+			 (long)GPOINTER_TO_SIZE (client), false);
+}
+
+static gboolean
+idle_quit_requested (gpointer client)
+{
+  egg_sm_client_quit_requested (client);
+  return FALSE;
+}
+
+static pascal OSErr
+quit_requested (const AppleEvent *aevt, AppleEvent *reply, long refcon)
+{
+  EggSMClient *client = GSIZE_TO_POINTER ((gsize)refcon);
+  EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon);
+
+  g_return_val_if_fail (!osx->quit_requested, userCanceledErr);
+    
+  /* FIXME AEInteractWithUser? */
+
+  osx->quit_requested = TRUE;
+  AEDuplicateDesc (aevt, &osx->quit_event);
+  AEDuplicateDesc (reply, &osx->quit_reply);
+  AESuspendTheCurrentEvent (aevt);
+
+  /* Don't emit the "quit_requested" signal immediately, since we're
+   * called from a weird point in the guts of gdkeventloop-quartz.c
+   */
+  g_idle_add (idle_quit_requested, client);
+  return noErr;
+}
+
+static pascal OSErr
+quit_requested_resumed (const AppleEvent *aevt, AppleEvent *reply, long refcon)
+{
+  EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon);
+
+  osx->quit_requested = FALSE;
+  return osx->quitting ? noErr : userCanceledErr;
+}
+
+static gboolean
+idle_will_quit (gpointer client)
+{
+  EggSMClientOSX *osx = (EggSMClientOSX *)client;
+
+  /* Resume the event with a new handler that will return a value to
+   * the system.
+   */
+  AEResumeTheCurrentEvent (&osx->quit_event, &osx->quit_reply,
+			   NewAEEventHandlerUPP (quit_requested_resumed),
+			   (long)GPOINTER_TO_SIZE (client));
+  AEDisposeDesc (&osx->quit_event);
+  AEDisposeDesc (&osx->quit_reply);
+
+  if (osx->quitting)
+    egg_sm_client_quit (client);
+  return FALSE;
+}
+
+static void
+sm_client_osx_will_quit (EggSMClient *client,
+			 gboolean     will_quit)
+{
+  EggSMClientOSX *osx = (EggSMClientOSX *)client;
+
+  g_return_if_fail (osx->quit_requested);
+
+  osx->quitting = will_quit;
+
+  /* Finish in an idle handler since the caller might have called
+   * egg_sm_client_will_quit() from inside the "quit_requested" signal
+   * handler, but may not expect the "quit" signal to arrive during
+   * the _will_quit() call.
+   */
+  g_idle_add (idle_will_quit, client);
+}
+
+static gboolean
+sm_client_osx_end_session (EggSMClient         *client,
+			   EggSMClientEndStyle  style,
+			   gboolean             request_confirmation)
+{
+  static const ProcessSerialNumber loginwindow_psn = { 0, kSystemProcess };
+  AppleEvent event = { typeNull, NULL }, reply = { typeNull, NULL };
+  AEAddressDesc target;
+  AEEventID id;
+  OSErr err;
+
+  switch (style)
+    {
+    case EGG_SM_CLIENT_END_SESSION_DEFAULT:
+    case EGG_SM_CLIENT_LOGOUT:
+      id = request_confirmation ? kAELogOut : kAEReallyLogOut;
+      break;
+    case EGG_SM_CLIENT_REBOOT:
+      id = request_confirmation ? kAEShowRestartDialog : kAERestart;
+      break;
+    case EGG_SM_CLIENT_SHUTDOWN:
+      id = request_confirmation ? kAEShowShutdownDialog : kAEShutDown;
+      break;
+    }
+
+  err = AECreateDesc (typeProcessSerialNumber, &loginwindow_psn, 
+		      sizeof (loginwindow_psn), &target);
+  if (err != noErr)
+    {
+      g_warning ("Could not create descriptor for loginwindow: %d", err);
+      return FALSE;
+    }
+
+  err = AECreateAppleEvent (kCoreEventClass, id, &target,
+			    kAutoGenerateReturnID, kAnyTransactionID,
+			    &event);
+  AEDisposeDesc (&target);
+  if (err != noErr)
+    {
+      g_warning ("Could not create logout AppleEvent: %d", err);
+      return FALSE;
+    }
+
+  err = AESend (&event, &reply, kAENoReply, kAENormalPriority,
+		kAEDefaultTimeout, NULL, NULL);
+  AEDisposeDesc (&event);
+  if (err == noErr)
+    AEDisposeDesc (&reply);
+
+  return err == noErr;
+}
diff --git a/aisleriot/smclient/eggsmclient-private.h b/aisleriot/smclient/eggsmclient-private.h
new file mode 100644
index 0000000..0c98eee
--- /dev/null
+++ b/aisleriot/smclient/eggsmclient-private.h
@@ -0,0 +1,59 @@
+/* eggsmclient-private.h
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_SM_CLIENT_PRIVATE_H__
+#define __EGG_SM_CLIENT_PRIVATE_H__
+
+#include <gtk/gtk.h>
+
+#if !GTK_CHECK_VERSION(2,91,7) && !GTK_CHECK_VERSION(3,0,0)
+/* GTK+ 3 includes this automatically */
+#include <gdkconfig.h>
+#endif
+
+#include "eggsmclient.h"
+
+G_BEGIN_DECLS
+
+GKeyFile *egg_sm_client_save_state     (EggSMClient *client);
+void      egg_sm_client_quit_requested (EggSMClient *client);
+void      egg_sm_client_quit_cancelled (EggSMClient *client);
+void      egg_sm_client_quit           (EggSMClient *client);
+
+#if defined (GDK_WINDOWING_X11)
+# ifdef EGG_SM_CLIENT_BACKEND_XSMP
+GType        egg_sm_client_xsmp_get_type (void);
+EggSMClient *egg_sm_client_xsmp_new      (void);
+# endif
+# ifdef EGG_SM_CLIENT_BACKEND_DBUS
+GType        egg_sm_client_dbus_get_type (void);
+EggSMClient *egg_sm_client_dbus_new      (void);
+# endif
+#elif defined (GDK_WINDOWING_WIN32)
+GType        egg_sm_client_win32_get_type (void);
+EggSMClient *egg_sm_client_win32_new      (void);
+#elif defined (GDK_WINDOWING_QUARTZ)
+GType        egg_sm_client_osx_get_type (void);
+EggSMClient *egg_sm_client_osx_new      (void);
+#endif
+
+G_END_DECLS
+
+
+#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */
diff --git a/aisleriot/smclient/eggsmclient-win32.c b/aisleriot/smclient/eggsmclient-win32.c
new file mode 100644
index 0000000..91a2571
--- /dev/null
+++ b/aisleriot/smclient/eggsmclient-win32.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* EggSMClientWin32
+ *
+ * For details on the Windows XP logout process, see:
+ * http://msdn.microsoft.com/en-us/library/aa376876.aspx.
+ *
+ * Vista adds some new APIs which EggSMClient does not make use of; see
+ * http://msdn.microsoft.com/en-us/library/ms700677(VS.85).aspx
+ *
+ * When shutting down, Windows sends every top-level window a
+ * WM_QUERYENDSESSION event, which the application must respond to
+ * synchronously, saying whether or not it will quit. To avoid main
+ * loop re-entrancy problems (and to avoid having to muck about too
+ * much with the guts of the gdk-win32 main loop), we watch for this
+ * event in a separate thread, which then signals the main thread and
+ * waits for the main thread to handle the event. Since we don't want
+ * to require g_thread_init() to be called, we do this all using
+ * Windows-specific thread methods.
+ *
+ * After the application handles the WM_QUERYENDSESSION event,
+ * Windows then sends it a WM_ENDSESSION event with a TRUE or FALSE
+ * parameter indicating whether the session is or is not actually
+ * going to end now. We handle this from the other thread as well.
+ *
+ * As mentioned above, Vista introduces several additional new APIs
+ * that don't fit into the (current) EggSMClient API. Windows also has
+ * an entirely separate shutdown-notification scheme for non-GUI apps,
+ * which we also don't handle here.
+ */
+
+#include "config.h"
+
+#include "eggsmclient-private.h"
+#include <gdk/gdk.h>
+
+#define WIN32_LEAN_AND_MEAN
+#define UNICODE
+#include <windows.h>
+#include <process.h>
+
+#define EGG_TYPE_SM_CLIENT_WIN32            (egg_sm_client_win32_get_type ())
+#define EGG_SM_CLIENT_WIN32(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32))
+#define EGG_SM_CLIENT_WIN32_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32Class))
+#define EGG_IS_SM_CLIENT_WIN32(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_WIN32))
+#define EGG_IS_SM_CLIENT_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_WIN32))
+#define EGG_SM_CLIENT_WIN32_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32Class))
+
+typedef struct _EggSMClientWin32        EggSMClientWin32;
+typedef struct _EggSMClientWin32Class   EggSMClientWin32Class;
+
+struct _EggSMClientWin32 {
+  EggSMClient parent;
+
+  HANDLE message_event, response_event;
+
+  volatile GSourceFunc event;
+  volatile gboolean will_quit;
+};
+
+struct _EggSMClientWin32Class
+{
+  EggSMClientClass parent_class;
+
+};
+
+static void     sm_client_win32_startup (EggSMClient *client,
+					 const char  *client_id);
+static void     sm_client_win32_will_quit (EggSMClient *client,
+					   gboolean     will_quit);
+static gboolean sm_client_win32_end_session (EggSMClient         *client,
+					     EggSMClientEndStyle  style,
+					     gboolean  request_confirmation);
+
+static GSource *g_win32_handle_source_add (HANDLE handle, GSourceFunc callback,
+					gpointer user_data);
+static gboolean got_message (gpointer user_data);
+static void sm_client_thread (gpointer data);
+
+G_DEFINE_TYPE (EggSMClientWin32, egg_sm_client_win32, EGG_TYPE_SM_CLIENT)
+
+static void
+egg_sm_client_win32_init (EggSMClientWin32 *win32)
+{
+  ;
+}
+
+static void
+egg_sm_client_win32_class_init (EggSMClientWin32Class *klass)
+{
+  EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
+
+  sm_client_class->startup             = sm_client_win32_startup;
+  sm_client_class->will_quit           = sm_client_win32_will_quit;
+  sm_client_class->end_session         = sm_client_win32_end_session;
+}
+
+EggSMClient *
+egg_sm_client_win32_new (void)
+{
+  return g_object_new (EGG_TYPE_SM_CLIENT_WIN32, NULL);
+}
+
+static void
+sm_client_win32_startup (EggSMClient *client,
+			 const char  *client_id)
+{
+  EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
+
+  win32->message_event = CreateEvent (NULL, FALSE, FALSE, NULL);
+  win32->response_event = CreateEvent (NULL, FALSE, FALSE, NULL);
+  g_win32_handle_source_add (win32->message_event, got_message, win32);  
+  _beginthread (sm_client_thread, 0, client);
+}
+
+static void
+sm_client_win32_will_quit (EggSMClient *client,
+			   gboolean     will_quit)
+{
+  EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
+
+  win32->will_quit = will_quit;
+  SetEvent (win32->response_event);
+}
+
+static gboolean
+sm_client_win32_end_session (EggSMClient         *client,
+			     EggSMClientEndStyle  style,
+			     gboolean             request_confirmation)
+{
+  UINT uFlags = EWX_LOGOFF;
+
+  switch (style)
+    {
+    case EGG_SM_CLIENT_END_SESSION_DEFAULT:
+    case EGG_SM_CLIENT_LOGOUT:
+      uFlags = EWX_LOGOFF;
+      break;
+    case EGG_SM_CLIENT_REBOOT:
+      uFlags = EWX_REBOOT;
+      break;
+    case EGG_SM_CLIENT_SHUTDOWN:
+      uFlags = EWX_POWEROFF;
+      break;
+    }
+
+  /* There's no way to make ExitWindowsEx() show a logout dialog, so
+   * we ignore @request_confirmation.
+   */
+
+#ifdef SHTDN_REASON_FLAG_PLANNED
+  ExitWindowsEx (uFlags, SHTDN_REASON_FLAG_PLANNED);
+#else
+  ExitWindowsEx (uFlags, 0);
+#endif
+
+  return TRUE;
+}
+
+
+/* callbacks from logout-listener thread */
+
+static gboolean
+emit_quit_requested (gpointer smclient)
+{
+  gdk_threads_enter ();
+  egg_sm_client_quit_requested (smclient);
+  gdk_threads_leave ();
+
+  return FALSE;
+}
+
+static gboolean
+emit_quit (gpointer smclient)
+{
+  EggSMClientWin32 *win32 = smclient;
+
+  gdk_threads_enter ();
+  egg_sm_client_quit (smclient);
+  gdk_threads_leave ();
+
+  SetEvent (win32->response_event);
+  return FALSE;
+}
+
+static gboolean
+emit_quit_cancelled (gpointer smclient)
+{
+  EggSMClientWin32 *win32 = smclient;
+
+  gdk_threads_enter ();
+  egg_sm_client_quit_cancelled (smclient);
+  gdk_threads_leave ();
+
+  SetEvent (win32->response_event);
+  return FALSE;
+}
+
+static gboolean
+got_message (gpointer smclient)
+{
+  EggSMClientWin32 *win32 = smclient;
+
+  win32->event (win32);
+  return TRUE;
+}
+
+/* Windows HANDLE GSource */
+
+typedef struct {
+  GSource source;
+  GPollFD pollfd;
+} GWin32HandleSource;
+
+static gboolean
+g_win32_handle_source_prepare (GSource *source, gint *timeout)
+{
+  *timeout = -1;
+  return FALSE;
+}
+
+static gboolean
+g_win32_handle_source_check (GSource *source)
+{
+  GWin32HandleSource *hsource = (GWin32HandleSource *)source;
+
+  return hsource->pollfd.revents;
+}
+
+static gboolean
+g_win32_handle_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
+{
+  return (*callback) (user_data);
+}
+
+static void
+g_win32_handle_source_finalize (GSource *source)
+{
+  ;
+}
+
+GSourceFuncs g_win32_handle_source_funcs = {
+  g_win32_handle_source_prepare,
+  g_win32_handle_source_check,
+  g_win32_handle_source_dispatch,
+  g_win32_handle_source_finalize
+};
+
+static GSource *
+g_win32_handle_source_add (HANDLE handle, GSourceFunc callback, gpointer user_data)
+{
+  GWin32HandleSource *hsource;
+  GSource *source;
+
+  source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource));
+  hsource = (GWin32HandleSource *)source;
+  hsource->pollfd.fd = (int)handle;
+  hsource->pollfd.events = G_IO_IN;
+  hsource->pollfd.revents = 0;
+  g_source_add_poll (source, &hsource->pollfd);
+
+  g_source_set_callback (source, callback, user_data, NULL);
+  g_source_attach (source, NULL);
+  return source;
+}
+
+/* logout-listener thread */
+
+LRESULT CALLBACK
+sm_client_win32_window_procedure (HWND   hwnd,
+				  UINT   message,
+				  WPARAM wParam,
+				  LPARAM lParam)
+{
+  EggSMClientWin32 *win32 =
+    (EggSMClientWin32 *)GetWindowLongPtr (hwnd, GWLP_USERDATA);
+
+  switch (message)
+    {
+    case WM_QUERYENDSESSION:
+      win32->event = emit_quit_requested;
+      SetEvent (win32->message_event);
+
+      WaitForSingleObject (win32->response_event, INFINITE);
+      return win32->will_quit;
+
+    case WM_ENDSESSION:
+      if (wParam)
+	{
+	  /* The session is ending */
+	  win32->event = emit_quit;
+	}
+      else
+	{
+	  /* Nope, the session *isn't* ending */
+	  win32->event = emit_quit_cancelled;
+	}
+
+      SetEvent (win32->message_event);
+      WaitForSingleObject (win32->response_event, INFINITE);
+
+      return 0;
+
+    default:
+      return DefWindowProc (hwnd, message, wParam, lParam);
+    }
+}
+
+static void
+sm_client_thread (gpointer smclient)
+{
+  HINSTANCE instance;
+  WNDCLASSEXW wcl; 
+  ATOM klass;
+  HWND window;
+  MSG msg;
+
+  instance = GetModuleHandle (NULL);
+
+  memset (&wcl, 0, sizeof (WNDCLASSEX));
+  wcl.cbSize = sizeof (WNDCLASSEX);
+  wcl.lpfnWndProc = sm_client_win32_window_procedure;
+  wcl.hInstance = instance;
+  wcl.lpszClassName = L"EggSmClientWindow";
+  klass = RegisterClassEx (&wcl);
+
+  window = CreateWindowEx (0, MAKEINTRESOURCE (klass),
+			   L"EggSmClientWindow", 0,
+			   10, 10, 50, 50, GetDesktopWindow (),
+			   NULL, instance, NULL);
+  SetWindowLongPtr (window, GWLP_USERDATA, (LONG_PTR)smclient);
+
+  /* main loop */
+  while (GetMessage (&msg, NULL, 0, 0))
+    DispatchMessage (&msg);
+}
diff --git a/aisleriot/smclient/eggsmclient-xsmp.c b/aisleriot/smclient/eggsmclient-xsmp.c
new file mode 100644
index 0000000..da86d3c
--- /dev/null
+++ b/aisleriot/smclient/eggsmclient-xsmp.c
@@ -0,0 +1,1411 @@
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * Inspired by various other pieces of code including GsmClient (C)
+ * 2001 Havoc Pennington, GnomeClient (C) 1998 Carsten Schaar, and twm
+ * session code (C) 1998 The Open Group.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "eggsmclient.h"
+#include "eggsmclient-private.h"
+
+#include "eggdesktopfile.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <X11/SM/SMlib.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;
+  char **discard_command;
+  gboolean set_discard_command;
+
+  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_set_discard_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       **argv,
+				    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->set_discard_command = sm_client_xsmp_set_discard_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-GNOME-AutoRestart", NULL))
+	    xsmp->restart_style = SmRestartImmediately;
+	}
+
+      if (!xsmp->set_restart_command)
+	{
+	  cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err);
+	  if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err))
+	    {
+	      egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp),
+						 argc, (const char **)argv);
+	      g_strfreev (argv);
+	    }
+	  else
+	    {
+	      g_warning ("Could not parse Exec line in desktop file: %s",
+			 err->message);
+	      g_error_free (err);
+	    }
+	  g_free (cmdline);
+	}
+    }
+
+  if (!xsmp->set_restart_command)
+    xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1);
+
+  clone = generate_command (xsmp->restart_command, NULL, NULL);
+  restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
+
+  g_debug ("Setting initial properties");
+
+  /* Program, CloneCommand, RestartCommand, and UserID are required.
+   * ProcessID isn't required, but the SM may be able to do something
+   * useful with it.
+   */
+  g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ());
+  set_properties (xsmp,
+		  string_prop   (SmProgram, g_get_prgname ()),
+		  ptrarray_prop (SmCloneCommand, clone),
+		  ptrarray_prop (SmRestartCommand, restart),
+		  string_prop   (SmUserID, g_get_user_name ()),
+		  string_prop   (SmProcessID, pid_str),
+		  card8_prop    (SmRestartStyleHint, xsmp->restart_style),
+		  NULL);
+  g_ptr_array_free (clone, TRUE);
+  g_ptr_array_free (restart, TRUE);
+
+  if (desktop_file)
+    {
+      set_properties (xsmp,
+		      string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)),
+		      NULL);
+    }
+
+  update_pending_events (xsmp);
+  return FALSE;
+}
+
+/* This gets called from two different places: xsmp_die() (when the
+ * server asks us to disconnect) and process_ice_messages() (when the
+ * server disconnects unexpectedly).
+ */
+static void
+sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
+{
+  SmcConn connection;
+
+  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_threads_enter ();
+#if !GTK_CHECK_VERSION(2,91,7) && !GTK_CHECK_VERSION(3,0,0)
+      gdk_set_sm_client_id (xsmp->client_id);
+#else
+      gdk_x11_set_sm_client_id (xsmp->client_id);
+#endif
+      gdk_threads_leave ();
+
+      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_set_discard_command (EggSMClient  *client,
+				    int           argc,
+				    const char  **argv)
+{
+  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+  int i;
+
+  g_strfreev (xsmp->discard_command);
+
+  xsmp->discard_command = g_new (char *, argc + 1);
+  for (i = 0; i < argc; i++)
+    xsmp->discard_command[i] = g_strdup (argv[i]);
+  xsmp->discard_command[i] = NULL;
+
+  xsmp->set_discard_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 gnome-session will do
+   * the wrong thing if we pass SmSaveBoth and the user chooses NOT to
+   * save the session... Sigh.
+   */
+  if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session"))
+    save_type = SmSaveBoth;
+  else
+    save_type = SmSaveGlobal;
+
+  g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : "");
+  SmcRequestSaveYourself (xsmp->connection,
+			  save_type,
+			  True, /* shutdown */
+			  SmInteractStyleAny,
+			  !request_confirmation, /* fast */
+			  True /* global */);
+  return TRUE;
+}
+
+static gboolean
+idle_do_pending_events (gpointer data)
+{
+  EggSMClientXSMP *xsmp = data;
+  EggSMClient *client = data;
+
+  gdk_threads_enter ();
+
+  xsmp->idle = 0;
+
+  if (xsmp->waiting_to_emit_quit)
+    {
+      xsmp->waiting_to_emit_quit = FALSE;
+      egg_sm_client_quit (client);
+      goto out;
+    }
+
+  if (xsmp->waiting_to_emit_quit_cancelled)
+    {
+      xsmp->waiting_to_emit_quit_cancelled = FALSE;
+      egg_sm_client_quit_cancelled (client);
+      xsmp->state = XSMP_STATE_IDLE;
+    }
+
+  if (xsmp->waiting_to_save_myself)
+    {
+      xsmp->waiting_to_save_myself = FALSE;
+      do_save_yourself (xsmp);
+    }
+
+ out:
+  gdk_threads_leave ();
+  return FALSE;
+}
+
+static void
+update_pending_events (EggSMClientXSMP *xsmp)
+{
+  gboolean want_idle =
+    xsmp->waiting_to_emit_quit ||
+    xsmp->waiting_to_emit_quit_cancelled ||
+    xsmp->waiting_to_save_myself;
+
+  if (want_idle)
+    {
+      if (xsmp->idle == 0)
+	xsmp->idle = g_idle_add (idle_do_pending_events, xsmp);
+    }
+  else
+    {
+      if (xsmp->idle != 0)
+	g_source_remove (xsmp->idle);
+      xsmp->idle = 0;
+    }
+}
+
+static void
+fix_broken_state (EggSMClientXSMP *xsmp, const char *message,
+		  gboolean send_interact_done,
+		  gboolean send_save_yourself_done)
+{
+  g_warning ("Received XSMP %s message in state %s: client or server error",
+	     message, EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  /* Forget any pending SaveYourself plans we had */
+  xsmp->waiting_to_save_myself = FALSE;
+  update_pending_events (xsmp);
+
+  if (send_interact_done)
+    SmcInteractDone (xsmp->connection, False);
+  if (send_save_yourself_done)
+    SmcSaveYourselfDone (xsmp->connection, True);
+
+  xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE;
+}
+
+/* SM callbacks */
+
+static void
+xsmp_save_yourself (SmcConn   smc_conn,
+		    SmPointer client_data,
+		    int       save_type,
+		    Bool      shutdown,
+		    int       interact_style,
+		    Bool      fast)
+{
+  EggSMClientXSMP *xsmp = client_data;
+  gboolean wants_quit_requested;
+
+  g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
+	   save_type == SmSaveLocal ? "SmSaveLocal" :
+	   save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
+	   shutdown ? "Shutdown" : "!Shutdown",
+	   interact_style == SmInteractStyleAny ? "SmInteractStyleAny" :
+	   interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
+	   "SmInteractStyleNone", fast ? "Fast" : "!Fast",
+	   EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  if (xsmp->state != XSMP_STATE_IDLE &&
+      xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+      fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE);
+      return;
+    }
+
+  if (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, *discard;
+  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);
+
+      if (xsmp->set_discard_command)
+        {
+          discard = generate_command (xsmp->discard_command, NULL, NULL);
+          set_properties (xsmp,
+                          ptrarray_prop (SmDiscardCommand, discard),
+                          NULL);
+          g_ptr_array_free (discard, TRUE);
+        }
+      else
+        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 **argv, const char *client_id,
+		  const char *state_file)
+{
+  GPtrArray *cmd;
+  int i;
+
+  cmd = g_ptr_array_new ();
+  g_ptr_array_add (cmd, argv[0]);
+
+  if (client_id)
+    {
+      g_ptr_array_add (cmd, (char *)"--sm-client-id");
+      g_ptr_array_add (cmd, (char *)client_id);
+    }
+
+  if (state_file)
+    {
+      g_ptr_array_add (cmd, (char *)"--sm-client-state-file");
+      g_ptr_array_add (cmd, (char *)state_file);
+    }
+
+  for (i = 1; argv[i]; i++)
+    g_ptr_array_add (cmd, argv[i]);
+
+  return cmd;
+}
+
+/* Takes a NULL-terminated list of SmProp * values, created by
+ * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and
+ * frees them.
+ */
+static void
+set_properties (EggSMClientXSMP *xsmp, ...)
+{
+  GPtrArray *props;
+  SmProp *prop;
+  va_list ap;
+  guint i;
+
+  props = g_ptr_array_new ();
+
+  va_start (ap, xsmp);
+  while ((prop = va_arg (ap, SmProp *)))
+    g_ptr_array_add (props, prop);
+  va_end (ap);
+
+  if (xsmp->connection)
+    {
+      SmcSetProperties (xsmp->connection, props->len,
+			(SmProp **)props->pdata);
+    }
+
+  for (i = 0; i < props->len; i++)
+    {
+      prop = props->pdata[i];
+      g_free (prop->vals);
+      g_free (prop);
+    }
+  g_ptr_array_free (props, TRUE);
+}
+
+/* Takes a NULL-terminated list of property names and deletes them. */
+static void
+delete_properties (EggSMClientXSMP *xsmp, ...)
+{
+  GPtrArray *props;
+  char *prop;
+  va_list ap;
+
+  if (!xsmp->connection)
+    return;
+
+  props = g_ptr_array_new ();
+
+  va_start (ap, xsmp);
+  while ((prop = va_arg (ap, char *)))
+    g_ptr_array_add (props, prop);
+  va_end (ap);
+
+  SmcDeleteProperties (xsmp->connection, props->len,
+		       (char **)props->pdata);
+
+  g_ptr_array_free (props, TRUE);
+}
+
+/* Takes an array of strings and creates a LISTofARRAY8 property. The
+ * strings are neither dupped nor freed; they need to remain valid
+ * until you're done with the SmProp.
+ */
+static SmProp *
+array_prop (const char *name, ...) 
+{
+  SmProp *prop;
+  SmPropValue pv;
+  GArray *vals;
+  char *value;
+  va_list ap;
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = (char *)SmLISTofARRAY8;
+
+  vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
+
+  va_start (ap, name);
+  while ((value = va_arg (ap, char *)))
+    {
+      pv.length = strlen (value);
+      pv.value = value;
+      g_array_append_val (vals, pv);
+    }
+
+  prop->num_vals = vals->len;
+  prop->vals = (SmPropValue *)vals->data;
+
+  g_array_free (vals, FALSE);
+
+  return prop;
+}
+
+/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property.
+ * The array contents are neither dupped nor freed; they need to
+ * remain valid until you're done with the SmProp.
+ */
+static SmProp *
+ptrarray_prop (const char *name, GPtrArray *values)
+{
+  SmProp *prop;
+  SmPropValue pv;
+  GArray *vals;
+  guint i;
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = (char *)SmLISTofARRAY8;
+
+  vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
+
+  for (i = 0; i < values->len; i++)
+    {
+      pv.length = strlen (values->pdata[i]);
+      pv.value = values->pdata[i];
+      g_array_append_val (vals, pv);
+    }
+
+  prop->num_vals = vals->len;
+  prop->vals = (SmPropValue *)vals->data;
+
+  g_array_free (vals, FALSE);
+
+  return prop;
+}
+
+/* Takes a string and creates an ARRAY8 property. The string is
+ * neither dupped nor freed; it needs to remain valid until you're
+ * done with the SmProp.
+ */
+static SmProp *
+string_prop (const char *name, const char *value)
+{
+  SmProp *prop;
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = (char *)SmARRAY8;
+
+  prop->num_vals = 1;
+  prop->vals = g_new (SmPropValue, 1);
+
+  prop->vals[0].length = strlen (value);
+  prop->vals[0].value = (char *)value;
+
+  return prop;
+}
+
+/* Takes a char and creates a CARD8 property. */
+static SmProp *
+card8_prop (const char *name, unsigned char value)
+{
+  SmProp *prop;
+  char *card8val;
+
+  /* To avoid having to allocate and free prop->vals[0], we cheat and
+   * make vals a 2-element-long array and then use the second element
+   * to store value.
+   */
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = (char *)SmCARD8;
+
+  prop->num_vals = 1;
+  prop->vals = g_new (SmPropValue, 2);
+  card8val = (char *)(&prop->vals[1]);
+  card8val[0] = value;
+
+  prop->vals[0].length = 1;
+  prop->vals[0].value = card8val;
+
+  return prop;
+}
+
+/* ICE code. This makes no effort to play nice with anyone else trying
+ * to use libICE. Fortunately, no one uses libICE for anything other
+ * than SM. (DCOP uses ICE, but it has its own private copy of
+ * libICE.)
+ *
+ * When this moves to gtk, it will need to be cleverer, to avoid
+ * tripping over old apps that use GnomeClient or that use libSM
+ * directly.
+ */
+
+#include <X11/ICE/ICElib.h>
+#include <fcntl.h>
+
+static void        ice_error_handler    (IceConn        ice_conn,
+					 Bool           swap,
+					 int            offending_minor_opcode,
+					 unsigned long  offending_sequence,
+					 int            error_class,
+					 int            severity,
+					 IcePointer     values);
+static void        ice_io_error_handler (IceConn        ice_conn);
+static void        ice_connection_watch (IceConn        ice_conn,
+					 IcePointer     client_data,
+					 Bool           opening,
+					 IcePointer    *watch_data);
+
+static void
+ice_init (void)
+{
+  IceSetIOErrorHandler (ice_io_error_handler);
+  IceSetErrorHandler (ice_error_handler);
+  IceAddConnectionWatch (ice_connection_watch, NULL);
+}
+
+static gboolean
+process_ice_messages (IceConn ice_conn)
+{
+  IceProcessMessagesStatus status;
+
+  gdk_threads_enter ();
+  status = IceProcessMessages (ice_conn, NULL, NULL);
+  gdk_threads_leave ();
+
+  switch (status)
+    {
+    case IceProcessMessagesSuccess:
+      return TRUE;
+
+    case IceProcessMessagesIOError:
+      sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
+      return FALSE;
+
+    case IceProcessMessagesConnectionClosed:
+      return FALSE;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static gboolean
+ice_iochannel_watch (GIOChannel   *channel,
+		     GIOCondition  condition,
+		     gpointer      client_data)
+{
+  return process_ice_messages (client_data);
+}
+
+static void
+ice_connection_watch (IceConn     ice_conn,
+		      IcePointer  client_data,
+		      Bool        opening,
+		      IcePointer *watch_data)
+{
+  guint watch_id;
+
+  if (opening)
+    {
+      GIOChannel *channel;
+      int fd = IceConnectionNumber (ice_conn);
+
+      fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
+      channel = g_io_channel_unix_new (fd);
+      watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR,
+				 ice_iochannel_watch, ice_conn);
+      g_io_channel_unref (channel);
+
+      *watch_data = GUINT_TO_POINTER (watch_id);
+    }
+  else
+    {
+      watch_id = GPOINTER_TO_UINT (*watch_data);
+      g_source_remove (watch_id);
+    }
+}
+
+static void
+ice_error_handler (IceConn       ice_conn,
+		   Bool          swap,
+		   int           offending_minor_opcode,
+		   unsigned long offending_sequence,
+		   int           error_class,
+		   int           severity,
+		   IcePointer    values)
+{
+  /* Do nothing */
+} 
+
+static void
+ice_io_error_handler (IceConn ice_conn)
+{
+  /* Do nothing */
+} 
+
+static void
+smc_error_handler (SmcConn       smc_conn,
+                   Bool          swap,
+                   int           offending_minor_opcode,
+                   unsigned long offending_sequence,
+                   int           error_class,
+                   int           severity,
+                   SmPointer     values)
+{
+  /* Do nothing */
+}
diff --git a/aisleriot/smclient/eggsmclient.c b/aisleriot/smclient/eggsmclient.c
new file mode 100644
index 0000000..2198a55
--- /dev/null
+++ b/aisleriot/smclient/eggsmclient.c
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "eggsmclient.h"
+#include "eggsmclient-private.h"
+
+static void egg_sm_client_debug_handler (const char *log_domain,
+					 GLogLevelFlags log_level,
+					 const char *message,
+					 gpointer user_data);
+
+enum {
+  SAVE_STATE,
+  QUIT_REQUESTED,
+  QUIT_CANCELLED,
+  QUIT,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+struct _EggSMClientPrivate {
+  GKeyFile *state_file;
+};
+
+#define EGG_SM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_SM_CLIENT, EggSMClientPrivate))
+
+G_DEFINE_TYPE (EggSMClient, egg_sm_client, G_TYPE_OBJECT)
+
+static EggSMClient *global_client;
+static EggSMClientMode global_client_mode = EGG_SM_CLIENT_MODE_NORMAL;
+
+static void
+egg_sm_client_init (EggSMClient *client)
+{
+  ;
+}
+
+static void
+egg_sm_client_class_init (EggSMClientClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (EggSMClientPrivate));
+
+  /**
+   * EggSMClient::save_state:
+   * @client: the client
+   * @state_file: a #GKeyFile to save state information into
+   *
+   * Emitted when the session manager has requested that the
+   * application save information about its current state. The
+   * application should save its state into @state_file, and then the
+   * session manager may then restart the application in a future
+   * session and tell it to initialize itself from that state.
+   *
+   * You should not save any data into @state_file's "start group"
+   * (ie, the %NULL group). Instead, applications should save their
+   * data into groups with names that start with the application name,
+   * and libraries that connect to this signal should save their data
+   * into groups with names that start with the library name.
+   *
+   * Alternatively, rather than (or in addition to) using @state_file,
+   * the application can save its state by calling
+   * egg_sm_client_set_restart_command() during the processing of this
+   * signal (eg, to include a list of files to open).
+   **/
+  signals[SAVE_STATE] =
+    g_signal_new ("save_state",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, save_state),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__POINTER,
+                  G_TYPE_NONE,
+                  1, G_TYPE_POINTER);
+
+  /**
+   * EggSMClient::quit_requested:
+   * @client: the client
+   *
+   * Emitted when the session manager requests that the application
+   * exit (generally because the user is logging out). The application
+   * should decide whether or not it is willing to quit (perhaps after
+   * asking the user what to do with documents that have unsaved
+   * changes) and then call egg_sm_client_will_quit(), passing %TRUE
+   * or %FALSE to give its answer to the session manager. (It does not
+   * need to give an answer before returning from the signal handler;
+   * it can interact with the user asynchronously and then give its
+   * answer later on.) If the application does not connect to this
+   * signal, then #EggSMClient will automatically return %TRUE on its
+   * behalf.
+   *
+   * The application should not save its session state as part of
+   * handling this signal; if the user has requested that the session
+   * be saved when logging out, then ::save_state will be emitted
+   * separately.
+   * 
+   * If the application agrees to quit, it should then wait for either
+   * the ::quit_cancelled or ::quit signals to be emitted.
+   **/
+  signals[QUIT_REQUESTED] =
+    g_signal_new ("quit_requested",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, quit_requested),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+
+  /**
+   * EggSMClient::quit_cancelled:
+   * @client: the client
+   *
+   * Emitted when the session manager decides to cancel a logout after
+   * the application has already agreed to quit. After receiving this
+   * signal, the application can go back to what it was doing before
+   * receiving the ::quit_requested signal.
+   **/
+  signals[QUIT_CANCELLED] =
+    g_signal_new ("quit_cancelled",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, quit_cancelled),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+
+  /**
+   * EggSMClient::quit:
+   * @client: the client
+   *
+   * Emitted when the session manager wants the application to quit
+   * (generally because the user is logging out). The application
+   * should exit as soon as possible after receiving this signal; if
+   * it does not, the session manager may choose to forcibly kill it.
+   *
+   * Normally a GUI application would only be sent a ::quit if it
+   * agreed to quit in response to a ::quit_requested signal. However,
+   * this is not guaranteed; in some situations the session manager
+   * may decide to end the session without giving applications a
+   * chance to object.
+   **/
+  signals[QUIT] =
+    g_signal_new ("quit",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, quit),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+}
+
+static gboolean sm_client_disable = FALSE;
+static char *sm_client_state_file = NULL;
+static char *sm_client_id = NULL;
+static 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 (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED &&
+      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") },
+    /* GnomeClient compatibility option */
+    { "sm-disable", 0, G_OPTION_FLAG_HIDDEN,
+      G_OPTION_ARG_NONE, &sm_client_disable,
+      NULL, NULL },
+    /* GnomeClient compatibility option. This is a dummy option that only
+     * exists so that sessions saved by apps with GnomeClient can be restored
+     * later when they've switched to EggSMClient. See bug #575308.
+     */
+    { "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN,
+      G_OPTION_ARG_STRING, &sm_config_prefix,
+      NULL, NULL },
+    { NULL }
+  };
+  GOptionGroup *group;
+
+  /* Use our own debug handler for the "EggSMClient" domain. */
+  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, until the mode is changed again. The application will
+ *    not even connect to the session manager. (egg_sm_client_get()
+ *    will still return an #EggSMClient 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 and
+ * before any call to egg_sm_client_get(), unless the mode was set
+ * earlier to %EGG_SM_CLIENT_MODE_DISABLED and this call enables
+ * session management. Note that option parsing will call
+ * egg_sm_client_get().
+ **/
+void
+egg_sm_client_set_mode (EggSMClientMode mode)
+{
+  EggSMClientMode old_mode = global_client_mode;
+
+  g_return_if_fail (global_client == NULL || global_client_mode == EGG_SM_CLIENT_MODE_DISABLED);
+  g_return_if_fail (!(global_client != NULL && mode == EGG_SM_CLIENT_MODE_DISABLED));
+
+  global_client_mode = mode;
+
+  if (global_client != NULL && old_mode == EGG_SM_CLIENT_MODE_DISABLED)
+    {
+      if (EGG_SM_CLIENT_GET_CLASS (global_client)->startup)
+        EGG_SM_CLIENT_GET_CLASS (global_client)->startup (global_client, sm_client_id);
+    }
+}
+
+/**
+ * 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 (!sm_client_disable)
+	{
+#if defined (GDK_WINDOWING_WIN32)
+	  global_client = egg_sm_client_win32_new ();
+#elif defined (GDK_WINDOWING_QUARTZ)
+	  global_client = egg_sm_client_osx_new ();
+#else
+	  /* If both D-Bus and XSMP are compiled in, try XSMP first
+	   * (since it supports state saving) and fall back to D-Bus
+	   * if XSMP isn't available.
+	   */
+# ifdef EGG_SM_CLIENT_BACKEND_XSMP
+	  global_client = egg_sm_client_xsmp_new ();
+# endif
+# ifdef EGG_SM_CLIENT_BACKEND_DBUS
+	  if (!global_client)
+	    global_client = egg_sm_client_dbus_new ();
+# endif
+#endif
+	}
+
+      /* Fallback: create a dummy client, so that callers don't have
+       * to worry about a %NULL return value.
+       */
+      if (!global_client)
+	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_set_discard_command:
+ * @client: the client
+ * @argc: the length of @argv
+ * @argv: argument vector
+ *
+ * Sets the command used to discard a custom state file if using
+ * egg_sm_client_set_restart_command(), which must be called before 
+ * using this function.
+ **/
+void
+egg_sm_client_set_discard_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_discard_command)
+    EGG_SM_CLIENT_GET_CLASS (client)->set_discard_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/aisleriot/smclient/eggsmclient.h b/aisleriot/smclient/eggsmclient.h
new file mode 100644
index 0000000..f13bcec
--- /dev/null
+++ b/aisleriot/smclient/eggsmclient.h
@@ -0,0 +1,123 @@
+/* eggsmclient.h
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_SM_CLIENT_H__
+#define __EGG_SM_CLIENT_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_SM_CLIENT            (egg_sm_client_get_type ())
+#define EGG_SM_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT, EggSMClient))
+#define EGG_SM_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT, EggSMClientClass))
+#define EGG_IS_SM_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT))
+#define EGG_IS_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT))
+#define EGG_SM_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT, EggSMClientClass))
+
+typedef struct _EggSMClient        EggSMClient;
+typedef struct _EggSMClientClass   EggSMClientClass;
+typedef struct _EggSMClientPrivate EggSMClientPrivate;
+
+typedef enum {
+  EGG_SM_CLIENT_END_SESSION_DEFAULT,
+  EGG_SM_CLIENT_LOGOUT,
+  EGG_SM_CLIENT_REBOOT,
+  EGG_SM_CLIENT_SHUTDOWN
+} EggSMClientEndStyle;
+
+typedef enum {
+  EGG_SM_CLIENT_MODE_DISABLED,
+  EGG_SM_CLIENT_MODE_NO_RESTART,
+  EGG_SM_CLIENT_MODE_NORMAL
+} EggSMClientMode;
+
+struct _EggSMClient
+{
+  GObject parent;
+
+};
+
+struct _EggSMClientClass
+{
+  GObjectClass parent_class;
+
+  /* signals */
+  void (*save_state)       (EggSMClient *client,
+			    GKeyFile    *state_file);
+
+  void (*quit_requested)   (EggSMClient *client);
+  void (*quit_cancelled)   (EggSMClient *client);
+  void (*quit)             (EggSMClient *client);
+
+  /* virtual methods */
+  void	   (*startup)             (EggSMClient          *client,
+				   const char           *client_id);
+  void	   (*set_restart_command) (EggSMClient          *client,
+				   int                   argc,
+				   const char          **argv);
+  void	   (*set_discard_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);
+void             egg_sm_client_set_discard_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/aisleriot/smclient/eggsmclient.patch b/aisleriot/smclient/eggsmclient.patch
new file mode 100644
index 0000000..9634d3a
--- /dev/null
+++ b/aisleriot/smclient/eggsmclient.patch
@@ -0,0 +1,202 @@
+diff --git a/libegg/smclient/eggdesktopfile.c b/libegg/smclient/eggdesktopfile.c
+index 357e548..ce0c6f3 100644
+--- a/libegg/smclient/eggdesktopfile.c
++++ b/libegg/smclient/eggdesktopfile.c
+@@ -1440,19 +1440,6 @@ egg_set_desktop_file (const char *desktop_file_path)
+       g_error_free (error);
+     }
+ 
+-  if (egg_desktop_file) {
+-    /* 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);
+ }
+ 
+diff --git a/libegg/smclient/eggsmclient-xsmp.c b/libegg/smclient/eggsmclient-xsmp.c
+index 1a56156..81af7d2 100644
+--- a/libegg/smclient/eggsmclient-xsmp.c
++++ b/libegg/smclient/eggsmclient-xsmp.c
+@@ -88,6 +88,8 @@ struct _EggSMClientXSMP
+   char **restart_command;
+   gboolean set_restart_command;
+   int restart_style;
++  char **discard_command;
++  gboolean set_discard_command;
+ 
+   guint idle;
+ 
+@@ -117,6 +119,9 @@ static void     sm_client_xsmp_startup (EggSMClient *client,
+ static void     sm_client_xsmp_set_restart_command (EggSMClient  *client,
+ 						    int           argc,
+ 						    const char  **argv);
++static void     sm_client_xsmp_set_discard_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,
+@@ -150,7 +155,7 @@ static SmProp *card8_prop        (const char    *name,
+ static void set_properties         (EggSMClientXSMP *xsmp, ...);
+ static void delete_properties      (EggSMClientXSMP *xsmp, ...);
+ 
+-static GPtrArray *generate_command (char       **restart_command,
++static GPtrArray *generate_command (char       **argv,
+ 				    const char  *client_id,
+ 				    const char  *state_file);
+ 
+@@ -185,6 +190,7 @@ egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass)
+ 
+   sm_client_class->startup             = sm_client_xsmp_startup;
+   sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command;
++  sm_client_class->set_discard_command = sm_client_xsmp_set_discard_command;
+   sm_client_class->will_quit           = sm_client_xsmp_will_quit;
+   sm_client_class->end_session         = sm_client_xsmp_end_session;
+ }
+@@ -404,6 +410,24 @@ sm_client_xsmp_set_restart_command (EggSMClient  *client,
+ }
+ 
+ static void
++sm_client_xsmp_set_discard_command (EggSMClient  *client,
++				    int           argc,
++				    const char  **argv)
++{
++  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
++  int i;
++
++  g_strfreev (xsmp->discard_command);
++
++  xsmp->discard_command = g_new (char *, argc + 1);
++  for (i = 0; i < argc; i++)
++    xsmp->discard_command[i] = g_strdup (argv[i]);
++  xsmp->discard_command[i] = NULL;
++
++  xsmp->set_discard_command = TRUE;
++}
++
++static void
+ sm_client_xsmp_will_quit (EggSMClient *client,
+ 			  gboolean     will_quit)
+ {
+@@ -771,7 +795,7 @@ save_state (EggSMClientXSMP *xsmp)
+   GKeyFile *state_file;
+   char *state_file_path, *data;
+   EggDesktopFile *desktop_file;
+-  GPtrArray *restart;
++  GPtrArray *restart, *discard;
+   int offset, fd;
+ 
+   /* We set xsmp->state before emitting save_state, but our caller is
+@@ -787,7 +811,18 @@ save_state (EggSMClientXSMP *xsmp)
+ 		      ptrarray_prop (SmRestartCommand, restart),
+ 		      NULL);
+       g_ptr_array_free (restart, TRUE);
+-      delete_properties (xsmp, SmDiscardCommand, NULL);
++
++      if (xsmp->set_discard_command)
++        {
++          discard = generate_command (xsmp->discard_command, NULL, NULL);
++          set_properties (xsmp,
++                          ptrarray_prop (SmDiscardCommand, discard),
++                          NULL);
++          g_ptr_array_free (discard, TRUE);
++        }
++      else
++        delete_properties (xsmp, SmDiscardCommand, NULL);
++
+       return;
+     }
+ 
+@@ -1041,14 +1076,14 @@ xsmp_shutdown_cancelled (SmcConn   smc_conn,
+  * then free the array, but not its contents.
+  */
+ static GPtrArray *
+-generate_command (char **restart_command, const char *client_id,
++generate_command (char **argv, 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]);
++  g_ptr_array_add (cmd, argv[0]);
+ 
+   if (client_id)
+     {
+@@ -1062,8 +1097,8 @@ generate_command (char **restart_command, const char *client_id,
+       g_ptr_array_add (cmd, (char *)state_file);
+     }
+ 
+-  for (i = 1; restart_command[i]; i++)
+-    g_ptr_array_add (cmd, restart_command[i]);
++  for (i = 1; argv[i]; i++)
++    g_ptr_array_add (cmd, argv[i]);
+ 
+   return cmd;
+ }
+diff --git a/libegg/smclient/eggsmclient.c b/libegg/smclient/eggsmclient.c
+index efa901d..85aaee4 100644
+--- a/libegg/smclient/eggsmclient.c
++++ b/libegg/smclient/eggsmclient.c
+@@ -445,6 +445,27 @@ egg_sm_client_set_restart_command (EggSMClient  *client,
+ }
+ 
+ /**
++ * egg_sm_client_set_discard_command:
++ * @client: the client
++ * @argc: the length of @argv
++ * @argv: argument vector
++ *
++ * Sets the command used to discard a custom state file if using
++ * egg_sm_client_set_restart_command(), which must be called before 
++ * using this function.
++ **/
++void
++egg_sm_client_set_discard_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_discard_command)
++    EGG_SM_CLIENT_GET_CLASS (client)->set_discard_command (client, argc, argv);
++}
++
++/**
+  * egg_sm_client_will_quit:
+  * @client: the client
+  * @will_quit: whether or not the application is willing to quit
+diff --git a/libegg/smclient/eggsmclient.h b/libegg/smclient/eggsmclient.h
+index e620b75..f13bcec 100644
+--- a/libegg/smclient/eggsmclient.h
++++ b/libegg/smclient/eggsmclient.h
+@@ -72,6 +72,9 @@ struct _EggSMClientClass
+   void	   (*set_restart_command) (EggSMClient          *client,
+ 				   int                   argc,
+ 				   const char          **argv);
++  void	   (*set_discard_command) (EggSMClient          *client,
++				   int                   argc,
++				   const char          **argv);
+   void	   (*will_quit)           (EggSMClient          *client,
+ 				   gboolean              will_quit);
+   gboolean (*end_session)         (EggSMClient          *client,
+@@ -102,6 +105,9 @@ GKeyFile        *egg_sm_client_get_state_file      (EggSMClient *client);
+ void             egg_sm_client_set_restart_command (EggSMClient  *client,
+ 						    int           argc,
+ 						    const char  **argv);
++void             egg_sm_client_set_discard_command (EggSMClient  *client,
++						    int           argc,
++						    const char  **argv);
+ 
+ /* Handling "quit_requested" signal */
+ void             egg_sm_client_will_quit           (EggSMClient *client,
diff --git a/aisleriot/sol.c b/aisleriot/sol.c
index fe3bf46..8f26a01 100644
--- a/aisleriot/sol.c
+++ b/aisleriot/sol.c
@@ -50,13 +50,13 @@
 #define SERVICE_NAME "org.gnome.Games.AisleRiot"
 #endif /* HAVE_HILDON */
 
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-stock.h>
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-sound.h>
+#include "ar-debug.h"
+#include "ar-stock.h"
+#include "ar-runtime.h"
+#include "ar-sound.h"
 
 #ifdef WITH_SMCLIENT
-#include <libgames-support/eggsmclient.h>
+#include "eggsmclient.h"
 #endif /* WITH_SMCLIENT */
 
 #include "conf.h"
@@ -142,7 +142,7 @@ osso_hw_event_cb (osso_hw_state_t *state,
   if (data->program == NULL)
     return;
 
-  games_conf_save ();
+  ar_conf_save ();
 
   if (state->memory_low_ind) {
     /* Run garbage collection */
@@ -191,7 +191,7 @@ sync_is_topmost_cb (HildonProgram *program,
     hildon_program_set_can_hibernate (program, FALSE);
   } else {
     /* Ensure settings are saved to disk */
-    games_conf_save ();
+    ar_conf_save ();
 
     /* FIXMEchpe: save state here */
 
@@ -246,10 +246,10 @@ main_prog (void *closure, int argc, char *argv[])
 
 #ifdef HAVE_MAEMO
   /* Set OSSO callbacks */
-  if (osso_rpc_set_default_cb_f (games_runtime_get_osso_context (),
+  if (osso_rpc_set_default_cb_f (ar_runtime_get_osso_context (),
                                  osso_rpc_cb,
                                  &data) != OSSO_OK ||
-      osso_hw_set_event_cb (games_runtime_get_osso_context (),
+      osso_hw_set_event_cb (ar_runtime_get_osso_context (),
                             &hw_events,
                             osso_hw_event_cb,
                             &data) != OSSO_OK) {
@@ -265,7 +265,7 @@ main_prog (void *closure, int argc, char *argv[])
 
   add_main_options (option_context, &data);
 
-  games_sound_enable (FALSE);
+  ar_sound_enable (FALSE);
 
   g_option_context_add_group (option_context, gtk_get_option_group (TRUE));
 #ifdef WITH_SMCLIENT
@@ -288,7 +288,7 @@ main_prog (void *closure, int argc, char *argv[])
      * g_option_context_parse()) rather than parsing the file directly afterward, in
      * order to get priority over the theme.
      */
-    rc_file = games_runtime_get_file (GAMES_RUNTIME_GAME_DATA_DIRECTORY, "gtkrc-maemo");
+    rc_file = ar_runtime_get_file (AR_RUNTIME_GAME_DATA_DIRECTORY, "gtkrc-maemo");
     gtk_rc_add_default_file (rc_file);
     g_free (rc_file);
   }
@@ -338,12 +338,12 @@ main_prog (void *closure, int argc, char *argv[])
   }
 
   if (!data.freecell && !data.variation) {
-    data.variation = games_conf_get_string_with_default (NULL, aisleriot_conf_get_key (CONF_VARIATION), DEFAULT_VARIATION);
+    data.variation = ar_conf_get_string_with_default (NULL, aisleriot_conf_get_key (CONF_VARIATION), DEFAULT_VARIATION);
   }
 
   g_assert (data.variation != NULL || data.freecell);
 
-  games_stock_init ();
+  ar_stock_init ();
 
   gtk_window_set_default_icon_name (data.freecell ? "gnome-freecell" : "gnome-aisleriot");
 
@@ -422,16 +422,16 @@ cleanup:
   g_settings_sync ();
 #endif
 
-  games_runtime_shutdown ();
+  ar_runtime_shutdown ();
 }
 
 int
 main (int argc, char *argv[])
 {
 #ifndef HAVE_HILDON
-  if (!games_runtime_init ("aisleriot"))
+  if (!ar_runtime_init ("aisleriot"))
 #else
-  if (!games_runtime_init_with_osso ("aisleriot", SERVICE_NAME))
+  if (!ar_runtime_init_with_osso ("aisleriot", SERVICE_NAME))
 #endif /* !HAVE_HILDON */
     return 1;
 
diff --git a/aisleriot/stats-dialog.c b/aisleriot/stats-dialog.c
index 506efdf..2986c19 100644
--- a/aisleriot/stats-dialog.c
+++ b/aisleriot/stats-dialog.c
@@ -22,12 +22,10 @@
 
 #include <gtk/gtk.h>
 
-#include <libgames-support/games-gtk-compat.h>
-#include <libgames-support/games-stock.h>
+#include "ar-gtk-compat.h"
+#include "util.h"
 
-#ifndef HAVE_HILDON
-#include <libgames-support/games-atk-utils.h>
-#endif
+#include "ar-stock.h"
 
 #include "conf.h"
 #include "util.h"
@@ -78,8 +76,8 @@ pack_in_frame (GtkWidget *box,
   gtk_widget_show_all (frame);
 
 #ifndef HAVE_HILDON
-  games_atk_util_add_atk_relation (label, frame, ATK_RELATION_LABEL_FOR);
-  games_atk_util_add_atk_relation (frame, label, ATK_RELATION_LABELLED_BY);
+  ar_atk_util_add_atk_relation (label, frame, ATK_RELATION_LABEL_FOR);
+  ar_atk_util_add_atk_relation (frame, label, ATK_RELATION_LABELLED_BY);
 #endif /* !HAVE_HILDON */
 }
 
@@ -103,8 +101,8 @@ add_row (GtkTable *table,
                              1, 2, row, row + 1);
 
 #ifndef HAVE_HILDON
-  games_atk_util_add_atk_relation (label, data_label, ATK_RELATION_LABEL_FOR);
-  games_atk_util_add_atk_relation (data_label, label, ATK_RELATION_LABELLED_BY);
+  ar_atk_util_add_atk_relation (label, data_label, ATK_RELATION_LABEL_FOR);
+  ar_atk_util_add_atk_relation (data_label, label, ATK_RELATION_LABELLED_BY);
 #endif /* !HAVE_HILDON */
 
   return GTK_LABEL (data_label);
@@ -176,7 +174,7 @@ aisleriot_stats_dialog_init (AisleriotStatsDialog *stats_dialog)
   pack_in_frame (hbox, GTK_WIDGET (table), _("Time"));
 
   gtk_dialog_add_buttons (dialog,
-                          GAMES_STOCK_RESET, GTK_RESPONSE_REJECT,
+                          AR_STOCK_RESET, GTK_RESPONSE_REJECT,
                           GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
                           NULL);
   gtk_dialog_set_alternative_button_order (dialog,
diff --git a/aisleriot/util.c b/aisleriot/util.c
index 2784914..4b9e9a4 100644
--- a/aisleriot/util.c
+++ b/aisleriot/util.c
@@ -24,9 +24,9 @@
 
 #include <gtk/gtk.h>
 
-#include <libgames-support/games-help.h>
-#include <libgames-support/games-show.h>
-#include <libgames-support/games-string-utils.h>
+#include "ar-help.h"
+#include "ar-show.h"
+#include "ar-string-utils.h"
 
 #include "util.h"
 
@@ -76,17 +76,17 @@ aisleriot_show_help (GtkWidget *window,
     help_section = game_file_to_help_section (game_file);
   }
 
-  if (!games_help_display_full (GTK_WIDGET (window), DOC_MODULE, help_section, &error)) {
+  if (!ar_help_display_full (GTK_WIDGET (window), DOC_MODULE, help_section, &error)) {
     if (game_file != NULL) {
       char *help_section_display;
 
-      help_section_display = games_filename_to_display_name (game_file);
+      help_section_display = ar_filename_to_display_name (game_file);
 
-      games_show_error (window, error,
+      ar_show_error (window, error,
                         _("Could not show help for â??%sâ??"),
                         help_section_display);
     } else {
-      games_show_error (window, error,
+      ar_show_error (window, error,
                         _("Could not show help for â??%sâ??"),
                         g_get_application_name ());
     }
@@ -128,3 +128,29 @@ aisleriot_variation_to_game_file (const char *variation)
 
   return s;
 }
+
+/**
+ * ar_atk_util_add_atk_relation:
+ * @widget:
+ * @other:
+ * @type:
+ *
+ * Adds an AtkRelation of type @type to @other into @widget's
+ * AtkRelationSet.
+ */
+void
+ar_atk_util_add_atk_relation (GtkWidget *widget,
+                              GtkWidget *other,
+                              AtkRelationType type)
+{
+  AtkRelationSet *set;
+  AtkRelation *relation;
+  AtkObject *object;
+
+  object = gtk_widget_get_accessible (other);
+  set = atk_object_ref_relation_set (gtk_widget_get_accessible (widget));
+  relation = atk_relation_new (&object, 1, type);
+  atk_relation_set_add (set, relation);
+  g_object_unref (relation);
+  g_object_unref (set);
+}
diff --git a/aisleriot/util.h b/aisleriot/util.h
index d4726d5..b9ca39f 100644
--- a/aisleriot/util.h
+++ b/aisleriot/util.h
@@ -1,4 +1,5 @@
 /*
+ * Copyright © 1998, 2001, 2003, 2006 Jonathan Blandford <jrb alum mit edu>
  * Copyright © 2007 Christian Persch
  *
  * This program is free software: you can redistribute it and/or modify
@@ -28,6 +29,10 @@ void aisleriot_show_help (GtkWidget *window,
 
 char *aisleriot_variation_to_game_file (const char *variation);
 
+void ar_atk_util_add_atk_relation (GtkWidget *widget,
+                                   GtkWidget *other,
+                                   AtkRelationType type);
+
 G_END_DECLS
 
 #endif /* !UTIL_H */
diff --git a/aisleriot/window.c b/aisleriot/window.c
index 03eeed2..b7c58de 100644
--- a/aisleriot/window.c
+++ b/aisleriot/window.c
@@ -38,20 +38,16 @@
 #endif
 #endif /* HAVE_HILDON */
 
-#include <libgames-support/games-clock.h>
-#include <libgames-support/games-debug.h>
-#include <libgames-support/games-glib-compat.h>
-#include <libgames-support/games-stock.h>
-#include <libgames-support/games-runtime.h>
-#include <libgames-support/games-sound.h>
-#include <libgames-support/games-string-utils.h>
-
-#ifndef HAVE_HILDON
-#include <libgames-support/games-atk-utils.h>
-#endif
-
-#if GLIB_CHECK_VERSION (2, 25, 15)
-#include <libgames-support/games-settings.h>
+#include "ar-clock.h"
+#include "ar-debug.h"
+#include "ar-glib-compat.h"
+#include "ar-stock.h"
+#include "ar-runtime.h"
+#include "ar-sound.h"
+#include "ar-string-utils.h"
+
+#if GLIB_CHECK_VERSION (2, 26, 0)
+#include "ar-gsettings.h"
 #endif
 
 #ifdef HAVE_CLUTTER
@@ -259,11 +255,11 @@ show_game_over_dialog (AisleriotWindow *window)
 
   if (game_won) {
     message = _("Congratulations, you have won!");
-    games_sound_play ("victory");
+    ar_sound_play ("victory");
 
   } else {
     message =  _("There are no more moves");
-    games_sound_play ("splat");
+    ar_sound_play ("splat");
   }
 
   dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window),
@@ -285,7 +281,7 @@ show_game_over_dialog (AisleriotWindow *window)
   if (game_won) {
     gtk_dialog_add_buttons (GTK_DIALOG (dialog),
                             GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
-                            GAMES_STOCK_START_NEW_GAME, RESPONSE_NEW_GAME,
+                            AR_STOCK_START_NEW_GAME, RESPONSE_NEW_GAME,
                             NULL);
     gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                              RESPONSE_NEW_GAME,
@@ -293,10 +289,10 @@ show_game_over_dialog (AisleriotWindow *window)
                                              -1);
   } else {
     gtk_dialog_add_buttons (GTK_DIALOG (dialog),
-			    GAMES_STOCK_UNDO_MOVE, RESPONSE_UNDO,
+			    AR_STOCK_UNDO_MOVE, RESPONSE_UNDO,
                             GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
-                            GAMES_STOCK_RESTART_GAME, RESPONSE_RESTART,
-                            GAMES_STOCK_START_NEW_GAME, RESPONSE_NEW_GAME,
+                            AR_STOCK_RESTART_GAME, RESPONSE_RESTART,
+                            AR_STOCK_START_NEW_GAME, RESPONSE_NEW_GAME,
                             NULL);
     gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                              RESPONSE_NEW_GAME,
@@ -452,7 +448,7 @@ help_about_cb (GtkAction *action,
 
   char *licence;
 
-  licence = games_get_license (priv->freecell_mode ? _("FreeCell Solitaire") : ("AisleRiot"));
+  licence = ar_get_licence (priv->freecell_mode ? _("FreeCell Solitaire") : ("AisleRiot"));
 
   gtk_show_about_dialog (GTK_WINDOW (window),
 #if GTK_CHECK_VERSION (2, 11, 0)
@@ -660,7 +656,7 @@ debug_ensure_game_list (AisleriotWindow *window)
   if (data != NULL)
     return data;
 
-  games_dir = games_runtime_get_directory (GAMES_RUNTIME_GAME_GAMES_DIRECTORY);
+  games_dir = ar_runtime_get_directory (AR_RUNTIME_GAME_GAMES_DIRECTORY);
   dir = g_dir_open (games_dir, 0, NULL);
   if (dir != NULL) {
     const char *game_file;
@@ -927,7 +923,7 @@ toolbar_toggled_cb (GtkToggleAction *action,
 
   priv->toolbar_visible = state != FALSE;
 
-  games_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_TOOLBAR), state);
+  ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_TOOLBAR), state);
 
   set_fullscreen_button_active (window);
 }
@@ -946,11 +942,11 @@ statusbar_toggled_cb (GtkToggleAction *action,
   g_object_set (priv->statusbar, "visible", state, NULL);
 
   /* Only update the clock continually if it's visible */
-  games_clock_set_update (GAMES_CLOCK (priv->clock), state);
+  ar_clock_set_update (AR_CLOCK (priv->clock), state);
 
   priv->statusbar_visible = state != FALSE;
 
-  games_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_STATUSBAR), state);
+  ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_STATUSBAR), state);
 }
 
 #endif /* !HAVE_HILDON */
@@ -981,7 +977,7 @@ static void
 fullscreen_toggled_cb (GtkToggleAction *action,
                        GtkWindow *window)
 {
-  _games_debug_print (GAMES_DEBUG_WINDOW_STATE,
+  ar_debug_print (AR_DEBUG_WINDOW_STATE,
                       "[window %p] fullscreen_toggled_cb, %s fullscreen\n",
                       window,
                       gtk_toggle_action_get_active (action) ? "going" : "leaving");
@@ -1012,7 +1008,7 @@ clickmove_toggle_cb (GtkToggleAction *action,
   aisleriot_game_set_click_to_move (priv->game, click_to_move);
   ar_style_set_click_to_move (priv->board_style, click_to_move);
   
-  games_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_CLICK_TO_MOVE), click_to_move);
+  ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_CLICK_TO_MOVE), click_to_move);
 }
 
 #ifdef ENABLE_SOUND
@@ -1025,9 +1021,9 @@ sound_toggle_cb (GtkToggleAction *action,
 
   sound_enabled = gtk_toggle_action_get_active (action);
 
-  games_sound_enable (sound_enabled);
+  ar_sound_enable (sound_enabled);
   
-  games_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SOUND), sound_enabled);
+  ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SOUND), sound_enabled);
 }
 
 #endif /* ENABLE_SOUND */
@@ -1045,7 +1041,7 @@ animations_toggle_cb (GtkToggleAction *action,
 
   ar_style_set_enable_animations (priv->board_style, enabled);
   
-  games_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_ANIMATIONS), enabled);
+  ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_ANIMATIONS), enabled);
 }
 
 #endif /* HAVE_CLUTTER */
@@ -1295,7 +1291,7 @@ add_recently_played_game (AisleriotWindow *window,
   if (priv->freecell_mode)
     return;
 
-  recent_games = games_conf_get_string_list (NULL, aisleriot_conf_get_key (CONF_RECENT_GAMES), &n_recent, NULL);
+  recent_games = ar_conf_get_string_list (NULL, aisleriot_conf_get_key (CONF_RECENT_GAMES), &n_recent, NULL);
 
   if (recent_games == NULL) {
     new_recent = g_new (char *, 2);
@@ -1320,7 +1316,7 @@ add_recently_played_game (AisleriotWindow *window,
     g_strfreev (recent_games);
   }
 
-  games_conf_set_string_list (NULL, aisleriot_conf_get_key (CONF_RECENT_GAMES),
+  ar_conf_set_string_list (NULL, aisleriot_conf_get_key (CONF_RECENT_GAMES),
                               (const char * const *) new_recent, n_new_recent);
   g_strfreev (new_recent);
 }
@@ -1336,7 +1332,7 @@ recent_game_cb (GtkAction *action,
 
   aisleriot_window_set_game (window, game_file, 0);
   
-  games_conf_set_string (NULL, aisleriot_conf_get_key (CONF_VARIATION), game_file);
+  ar_conf_set_string (NULL, aisleriot_conf_get_key (CONF_VARIATION), game_file);
 }
 
 static void
@@ -1363,7 +1359,7 @@ install_recently_played_menu (AisleriotWindow *window)
 
   priv->recent_games_merge_id = gtk_ui_manager_new_merge_id (priv->ui_manager);
 
-  recent_games = games_conf_get_string_list (NULL, aisleriot_conf_get_key (CONF_RECENT_GAMES), &n_recent, NULL);
+  recent_games = ar_conf_get_string_list (NULL, aisleriot_conf_get_key (CONF_RECENT_GAMES), &n_recent, NULL);
 
   for (i = 0; i < n_recent; ++i) {
     GtkAction *action;
@@ -1371,7 +1367,7 @@ install_recently_played_menu (AisleriotWindow *window)
     char *game_name, *tooltip;
 
     g_snprintf (actionname, sizeof (actionname), "Recent%"G_GSIZE_FORMAT, i);
-    game_name = games_filename_to_display_name (recent_games[i]);
+    game_name = ar_filename_to_display_name (recent_games[i]);
 #ifdef HAVE_HILDON
     tooltip = NULL;
 #else
@@ -1487,7 +1483,7 @@ card_theme_changed_cb (GtkToggleAction *action,
   aisleriot_window_take_card_theme (window, theme);
 
   theme_name = ar_card_theme_info_get_persistent_name (new_theme_info);
-  games_conf_set_string (NULL, aisleriot_conf_get_key (CONF_THEME), theme_name);
+  ar_conf_set_string (NULL, aisleriot_conf_get_key (CONF_THEME), theme_name);
 }
 
 static void
@@ -1671,9 +1667,9 @@ sync_game_state (AisleriotGame *game,
 
 #ifndef HAVE_HILDON
   if (state == GAME_RUNNING) {
-    games_clock_start (GAMES_CLOCK (priv->clock));
+    ar_clock_start (AR_CLOCK (priv->clock));
   } else {
-    games_clock_stop (GAMES_CLOCK (priv->clock));
+    ar_clock_stop (AR_CLOCK (priv->clock));
   }
 #endif
 
@@ -1769,7 +1765,7 @@ game_type_changed_cb (AisleriotGame *game,
 
 #ifdef HAVE_HILDON
 #else
-  games_clock_reset (GAMES_CLOCK (priv->clock));
+  ar_clock_reset (AR_CLOCK (priv->clock));
 
   gtk_statusbar_pop (priv->statusbar, priv->game_message_id);
   gtk_statusbar_pop (priv->statusbar, priv->board_message_id);
@@ -1792,7 +1788,7 @@ game_new_cb (AisleriotGame *game,
   update_statistics_display (window);
 
 #ifndef HAVE_HILDON
-  games_clock_reset (GAMES_CLOCK (priv->clock));
+  ar_clock_reset (AR_CLOCK (priv->clock));
 
   gtk_statusbar_pop (priv->statusbar, priv->game_message_id);
   gtk_statusbar_pop (priv->statusbar, priv->board_message_id);
@@ -1997,7 +1993,7 @@ screen_changed_cb (GtkWidget *widget,
   if (screen == NULL)
     return;
 
-  games_sound_init (screen);
+  ar_sound_init (screen);
 
   settings = gtk_widget_get_settings (widget);
   settings_changed_cb (settings, NULL, window);
@@ -2165,9 +2161,9 @@ aisleriot_window_state_event (GtkWidget *widget,
 
       aisleriot_game_set_paused (priv->game, is_iconified);
       if (is_iconified) {
-        games_clock_stop (GAMES_CLOCK (priv->clock));
+        ar_clock_stop (AR_CLOCK (priv->clock));
       } else {
-        games_clock_start (GAMES_CLOCK (priv->clock));
+        ar_clock_start (AR_CLOCK (priv->clock));
       }
     }
   }
@@ -2209,11 +2205,11 @@ aisleriot_window_init (AisleriotWindow *window)
     { "HelpMenu", NULL, N_("_Help") },
 
     /* Menu item actions */
-    { "NewGame", GAMES_STOCK_NEW_GAME, NULL,
+    { "NewGame", AR_STOCK_NEW_GAME, NULL,
       ACTION_ACCEL ("<ctrl>N", NULL),
       ACTION_TOOLTIP (N_("Start a new game")),
       G_CALLBACK (new_game_cb) },
-    { "RestartGame", GAMES_STOCK_RESTART_GAME, NULL, NULL,
+    { "RestartGame", AR_STOCK_RESTART_GAME, NULL, NULL,
        ACTION_TOOLTIP (N_("Restart the game")),
       G_CALLBACK (restart_game) },
     { "Select", GTK_STOCK_INDEX, N_("_Select Game..."),
@@ -2227,22 +2223,22 @@ aisleriot_window_init (AisleriotWindow *window)
     { "CloseWindow", GTK_STOCK_CLOSE, NULL, NULL,
       ACTION_TOOLTIP (N_("Close this window")),
       G_CALLBACK (close_window_cb) },
-    { "UndoMove", GAMES_STOCK_UNDO_MOVE, NULL, NULL,
+    { "UndoMove", AR_STOCK_UNDO_MOVE, NULL, NULL,
       ACTION_TOOLTIP (N_("Undo the last move")),
       G_CALLBACK (undo_cb) },
-    { "RedoMove", GAMES_STOCK_REDO_MOVE, NULL, NULL,
+    { "RedoMove", AR_STOCK_REDO_MOVE, NULL, NULL,
       ACTION_TOOLTIP (N_("Redo the undone move")),
       G_CALLBACK (redo_cb) },
-    { "Deal", GAMES_STOCK_DEAL_CARDS, NULL, NULL,
+    { "Deal", AR_STOCK_DEAL_CARDS, NULL, NULL,
       ACTION_TOOLTIP (N_("Deal next card or cards")),
       G_CALLBACK (deal_cb) },
-    { "Hint", GAMES_STOCK_HINT, NULL, NULL,
+    { "Hint", AR_STOCK_HINT, NULL, NULL,
       ACTION_TOOLTIP (N_("Get a hint for your next move")),
       G_CALLBACK (show_hint_cb) },
-    { "Contents", GAMES_STOCK_CONTENTS, NULL, NULL,
+    { "Contents", AR_STOCK_CONTENTS, NULL, NULL,
       ACTION_TOOLTIP (N_("View help for Aisleriot")),
       G_CALLBACK (help_general_cb) },
-    { "HelpGame", GAMES_STOCK_CONTENTS, NULL,
+    { "HelpGame", AR_STOCK_CONTENTS, NULL,
       ACTION_ACCEL ("<shift>F1", NULL),
       ACTION_TOOLTIP (N_("View help for this game")),
       G_CALLBACK (help_on_game_cb) },
@@ -2256,7 +2252,7 @@ aisleriot_window_init (AisleriotWindow *window)
 #endif /* HAVE_HILDON */
 
     /* Toolbar-only actions */
-    { "LeaveFullscreen", GAMES_STOCK_LEAVE_FULLSCREEN, NULL, NULL, NULL,
+    { "LeaveFullscreen", AR_STOCK_LEAVE_FULLSCREEN, NULL, NULL, NULL,
       G_CALLBACK (leave_fullscreen_cb) },
 #ifndef HAVE_HILDON
     { "ThemeMenu", NULL, N_("_Card Style"), NULL, NULL, NULL },
@@ -2299,7 +2295,7 @@ aisleriot_window_init (AisleriotWindow *window)
   };
 
   const GtkToggleActionEntry toggle_actions[] = {
-    { "Fullscreen", GAMES_STOCK_FULLSCREEN, NULL, NULL, NULL,
+    { "Fullscreen", AR_STOCK_FULLSCREEN, NULL, NULL, NULL,
       G_CALLBACK (fullscreen_toggled_cb),
       FALSE },
     { "Toolbar", NULL, N_("_Toolbar"), NULL,
@@ -2539,7 +2535,7 @@ aisleriot_window_init (AisleriotWindow *window)
   priv->board = AISLERIOT_BOARD (aisleriot_board_new (priv->board_style, priv->game));
 #endif /* HAVE_CLUTTER */
 
-  theme_name = games_conf_get_string (NULL, aisleriot_conf_get_key (CONF_THEME), NULL);
+  theme_name = ar_conf_get_string (NULL, aisleriot_conf_get_key (CONF_THEME), NULL);
   theme = ar_card_themes_get_theme_by_name (priv->theme_manager, theme_name);
   g_free (theme_name);
   if (!theme) {
@@ -2584,7 +2580,7 @@ aisleriot_window_init (AisleriotWindow *window)
 #ifndef HAVE_HILDON
   statusbar = priv->statusbar = GTK_STATUSBAR (gtk_statusbar_new ());
   priv->game_message_id = gtk_statusbar_get_context_id (priv->statusbar, "game-message");
-  games_stock_prepare_for_statusbar_tooltips (priv->ui_manager,
+  ar_stock_prepare_for_statusbar_tooltips (priv->ui_manager,
                                               GTK_WIDGET (priv->statusbar));
 
   priv->game_message_id = gtk_statusbar_get_context_id (priv->statusbar, "board-message");
@@ -2643,19 +2639,19 @@ aisleriot_window_init (AisleriotWindow *window)
   gtk_box_pack_start (GTK_BOX (priv->score_box), priv->score_label, FALSE, FALSE, 0);
   gtk_box_pack_end (GTK_BOX (statusbar_hbox), priv->score_box, FALSE, FALSE, 0);
 
-  games_atk_util_add_atk_relation (label, priv->score_label, ATK_RELATION_LABEL_FOR);
-  games_atk_util_add_atk_relation (priv->score_label, label, ATK_RELATION_LABELLED_BY);
+  ar_atk_util_add_atk_relation (label, priv->score_label, ATK_RELATION_LABEL_FOR);
+  ar_atk_util_add_atk_relation (priv->score_label, label, ATK_RELATION_LABELLED_BY);
 
   time_box = gtk_hbox_new (12, FALSE);
   label = gtk_label_new (_("Time:"));
   gtk_box_pack_start (GTK_BOX (time_box), label, FALSE, FALSE, 0);
-  priv->clock = games_clock_new ();
+  priv->clock = ar_clock_new ();
   gtk_box_pack_start (GTK_BOX (time_box), priv->clock, FALSE, FALSE, 0);
   gtk_box_pack_end (GTK_BOX (statusbar_hbox), time_box, FALSE, FALSE, 0);
   gtk_widget_show_all (time_box);
 
-  games_atk_util_add_atk_relation (label, priv->clock, ATK_RELATION_LABEL_FOR);
-  games_atk_util_add_atk_relation (priv->clock, label, ATK_RELATION_LABELLED_BY);
+  ar_atk_util_add_atk_relation (label, priv->clock, ATK_RELATION_LABEL_FOR);
+  ar_atk_util_add_atk_relation (priv->clock, label, ATK_RELATION_LABELLED_BY);
 #endif /* !HAVE_HILDON */
 
   /* Load the UI after we've connected the statusbar,
@@ -2711,12 +2707,12 @@ aisleriot_window_init (AisleriotWindow *window)
   gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
 
   action = gtk_action_group_get_action (priv->action_group, "Toolbar");
-  priv->toolbar_visible = games_conf_get_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_TOOLBAR), NULL) != FALSE;
+  priv->toolbar_visible = ar_conf_get_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_TOOLBAR), NULL) != FALSE;
   gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
                                 priv->toolbar_visible);
   action = gtk_action_group_get_action (priv->action_group, "ClickToMove");
   gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
-                                games_conf_get_boolean (NULL, aisleriot_conf_get_key (CONF_CLICK_TO_MOVE), NULL));
+                                ar_conf_get_boolean (NULL, aisleriot_conf_get_key (CONF_CLICK_TO_MOVE), NULL));
 
   action = gtk_action_group_get_action (priv->action_group, "RecentMenu");
   g_object_set (action, "hide-if-empty", FALSE, NULL);
@@ -2724,13 +2720,13 @@ aisleriot_window_init (AisleriotWindow *window)
 #ifdef ENABLE_SOUND
   action = gtk_action_group_get_action (priv->action_group, "Sound");
   gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
-                                games_conf_get_boolean (NULL, aisleriot_conf_get_key (CONF_SOUND), NULL));
-  gtk_action_set_visible (action, games_sound_is_available ());
+                                ar_conf_get_boolean (NULL, aisleriot_conf_get_key (CONF_SOUND), NULL));
+  gtk_action_set_visible (action, ar_sound_is_available ());
 #endif /* ENABLE_SOUND */
 
 #ifndef HAVE_HILDON
   action = gtk_action_group_get_action (priv->action_group, "Statusbar");
-  priv->statusbar_visible = games_conf_get_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_STATUSBAR), NULL) != FALSE;
+  priv->statusbar_visible = ar_conf_get_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_STATUSBAR), NULL) != FALSE;
   gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
                                 priv->statusbar_visible);
 #endif /* !HAVE_HILDON */
@@ -2745,7 +2741,7 @@ aisleriot_window_init (AisleriotWindow *window)
 #ifdef HAVE_CLUTTER
   action = gtk_action_group_get_action (priv->action_group, "Animations");
   gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
-                                games_conf_get_boolean (NULL, aisleriot_conf_get_key (CONF_ANIMATIONS), NULL));
+                                ar_conf_get_boolean (NULL, aisleriot_conf_get_key (CONF_ANIMATIONS), NULL));
 
 #endif /* HAVE_CLUTTER */
 
@@ -2807,9 +2803,9 @@ aisleriot_window_init (AisleriotWindow *window)
 
   /* Restore window state */
 #if GLIB_CHECK_VERSION (2, 25, 15)
-  games_settings_bind_window_state (AR_SETTINGS_WINDOW_STATE_PATH, GTK_WINDOW (window));
+  ar_gsettings_bind_window_state (AR_SETTINGS_WINDOW_STATE_PATH, GTK_WINDOW (window));
 #else
-  games_conf_add_window (GTK_WINDOW (window), NULL);
+  ar_conf_add_window (GTK_WINDOW (window), NULL);
 #endif
 
   /* Initial focus is in the board */
@@ -2995,7 +2991,7 @@ load_idle_cb (LoadIdleData *data)
     GtkWidget *dialog;
     char *name;
 
-    name = games_filename_to_display_name (data->game_file);
+    name = ar_filename_to_display_name (data->game_file);
 
     dialog = gtk_message_dialog_new (GTK_WINDOW (data->window),
                                      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -3030,7 +3026,7 @@ load_idle_cb (LoadIdleData *data)
    * store it in conf, except when we're running in freecell mode.
    */
   if (!priv->freecell_mode) {
-    games_conf_set_string (NULL, aisleriot_conf_get_key (CONF_VARIATION), data->game_file);
+    ar_conf_set_string (NULL, aisleriot_conf_get_key (CONF_VARIATION), data->game_file);
   }
 
   aisleriot_game_new_game (priv->game, data->seed != 0 ? &data->seed : NULL);
diff --git a/configure.in b/configure.in
index 904df12..c48841d 100644
--- a/configure.in
+++ b/configure.in
@@ -1267,6 +1267,7 @@ aisleriot/icons/Makefile
 aisleriot/icons/gnome/Makefile
 aisleriot/icons/hildon/Makefile
 aisleriot/lib/Makefile
+aisleriot/smclient/Makefile
 aisleriot/rules/Makefile
 aisleriot/help/Makefile
 glchess/Makefile
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 35f8dc5..0bb19c2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -16,7 +16,14 @@ aisleriot/sol.scm
 aisleriot/stats-dialog.c
 aisleriot/util.c
 aisleriot/window.c
+aisleriot/ar-stock.c
+aisleriot/lib/ar-help.c
+aisleriot/lib/ar-runtime.c
+aisleriot/lib/ar-show.c
+aisleriot/lib/org.gnome.Patience.WindowState.gschema.xml.in
 aisleriot/lib/ar-card.c
+aisleriot/smclient/eggdesktopfile.c
+aisleriot/smclient/eggsmclient.c
 aisleriot/rules/accordion.scm
 aisleriot/rules/agnes.scm
 aisleriot/rules/athena.scm



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