[gnome-games] aisleriot: Decouple libgames-support from aisleriot
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games] aisleriot: Decouple libgames-support from aisleriot
- Date: Thu, 7 Apr 2011 12:38:28 +0000 (UTC)
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]