gnome-games r7986 - trunk/libgames-support



Author: chpe
Date: Wed Oct  8 20:03:58 2008
New Revision: 7986
URL: http://svn.gnome.org/viewvc/gnome-games?rev=7986&view=rev

Log:
Update from libegg.
Add the diff to libegg upstream also as eggsmclient.patch so that it's
easy to reapply after updating from upstream.

Added:
   trunk/libgames-support/eggsmclient.patch
Modified:
   trunk/libgames-support/eggdesktopfile.c
   trunk/libgames-support/eggdesktopfile.h
   trunk/libgames-support/eggsmclient-osx.c
   trunk/libgames-support/eggsmclient-win32.c
   trunk/libgames-support/eggsmclient-xsmp.c
   trunk/libgames-support/eggsmclient.c

Modified: trunk/libgames-support/eggdesktopfile.c
==============================================================================
--- trunk/libgames-support/eggdesktopfile.c	(original)
+++ trunk/libgames-support/eggdesktopfile.c	Wed Oct  8 20:03:58 2008
@@ -31,7 +31,6 @@
 #include <unistd.h>
 
 #include <glib/gi18n.h>
-#include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #include <gtk/gtk.h>
 
@@ -56,7 +55,6 @@
 EggDesktopFile *
 egg_desktop_file_new (const char *desktop_file_path, GError **error)
 {
-  EggDesktopFile *desktop_file;
   GKeyFile *key_file;
 
   key_file = g_key_file_new ();
@@ -66,13 +64,8 @@
       return NULL;
     }
 
-  desktop_file = egg_desktop_file_new_from_key_file (key_file,
-						     desktop_file_path,
-						     error);
-  if (!desktop_file)
-    g_key_file_free (key_file);
-
-  return desktop_file;
+  return egg_desktop_file_new_from_key_file (key_file, desktop_file_path,
+					     error);
 }
 
 /**
@@ -106,9 +99,42 @@
 						     full_path,
 						     error);
   g_free (full_path);
-  if (!desktop_file)
-    g_key_file_free (key_file);
+  return desktop_file;
+}
+
+/**
+ * egg_desktop_file_new_from_dirs:
+ * @desktop_file_path: relative path to a Freedesktop-style Desktop file
+ * @search_dirs: NULL-terminated array of directories to search
+ * @error: error pointer
+ *
+ * Looks for @desktop_file_path in the paths returned from
+ * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
+ * a new #EggDesktopFile from it.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_dirs (const char  *desktop_file_path,
+				const char **search_dirs,
+				GError     **error)
+{
+  EggDesktopFile *desktop_file;
+  GKeyFile *key_file;
+  char *full_path;
+
+  key_file = g_key_file_new ();
+  if (!g_key_file_load_from_dirs (key_file, desktop_file_path, search_dirs,
+				  &full_path, 0, error))
+    {
+      g_key_file_free (key_file);
+      return NULL;
+    }
 
+  desktop_file = egg_desktop_file_new_from_key_file (key_file,
+						     full_path,
+						     error);
+  g_free (full_path);
   return desktop_file;
 }
 
@@ -119,8 +145,8 @@
  * @error: error pointer
  *
  * Creates a new #EggDesktopFile for @key_file. Assumes ownership of
- * @key_file on success (meaning it will be freed when the desktop_file
- * is freed).
+ * @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.
  **/
@@ -137,6 +163,7 @@
       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;
     }
 
@@ -160,13 +187,14 @@
 		       EGG_DESKTOP_FILE_ERROR_INVALID,
 		       _("Unrecognized desktop file Version '%s'"), version);
 	  g_free (version);
+	  g_key_file_free (key_file);
 	  return NULL;
 	}
-      else 
       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);
@@ -202,7 +230,7 @@
       if (!exec)
 	{
 	  egg_desktop_file_free (desktop_file);
-	  g_free(type);
+	  g_free (type);
 	  return NULL;
 	}
 
@@ -235,7 +263,7 @@
       if (!url)
 	{
 	  egg_desktop_file_free (desktop_file);
-	  g_free(type);
+	  g_free (type);
 	  return NULL;
 	}
       g_free (url);
@@ -245,7 +273,7 @@
   else
     desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED;
 
-  g_free(type);
+  g_free (type);
 
   /* Check the Icon key */
   desktop_file->icon = g_key_file_get_string (key_file,
@@ -270,7 +298,6 @@
 	}
     }
 
-  desktop_file->key_file = key_file;
   return desktop_file;
 }
 
@@ -291,22 +318,6 @@
 }
 
 /**
- * egg_desktop_file_get_key_file:
- * @desktop_file: an #EggDesktopFile
- *
- * Gets the #GKeyFile associated with @desktop_file. You must not free
- * this value, and changes made to it will not be reflected by
- * @desktop_file.
- *
- * Return value: the #GKeyFile associated with @desktop_file.
- **/
-GKeyFile *
-egg_desktop_file_get_key_file (EggDesktopFile *desktop_file)
-{
-  return desktop_file->key_file;
-}
-
-/**
  * egg_desktop_file_get_source:
  * @desktop_file: an #EggDesktopFile
  *
@@ -369,6 +380,81 @@
   return desktop_file->icon;
 }
 
+gboolean
+egg_desktop_file_has_key (EggDesktopFile  *desktop_file,
+			  const char      *key,
+			  GError         **error)
+{
+  return g_key_file_has_key (desktop_file->key_file,
+			     EGG_DESKTOP_FILE_GROUP, key,
+			     error);
+}
+
+char *
+egg_desktop_file_get_string (EggDesktopFile  *desktop_file,
+			     const char      *key,
+			     GError         **error)
+{
+  return g_key_file_get_string (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP, key,
+				error);
+}
+
+char *
+egg_desktop_file_get_locale_string (EggDesktopFile  *desktop_file,
+				    const char      *key,
+				    const char      *locale,
+				    GError         **error)
+{
+  return g_key_file_get_locale_string (desktop_file->key_file,
+				       EGG_DESKTOP_FILE_GROUP, key, locale,
+				       error);
+}
+
+gboolean
+egg_desktop_file_get_boolean (EggDesktopFile  *desktop_file,
+			      const char      *key,
+			      GError         **error)
+{
+  return g_key_file_get_boolean (desktop_file->key_file,
+				 EGG_DESKTOP_FILE_GROUP, key,
+				 error);
+}
+
+double
+egg_desktop_file_get_numeric (EggDesktopFile  *desktop_file,
+			      const char      *key,
+			      GError         **error)
+{
+  return g_key_file_get_double (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP, key,
+				error);
+}
+
+char **
+egg_desktop_file_get_string_list (EggDesktopFile  *desktop_file,
+				  const char      *key,
+				  gsize           *length,
+				  GError         **error)
+{
+  return g_key_file_get_string_list (desktop_file->key_file,
+				     EGG_DESKTOP_FILE_GROUP, key, length,
+				     error);
+}
+
+char **
+egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file,
+					 const char      *key,
+					 const char      *locale,
+					 gsize           *length,
+					 GError         **error)
+{
+  return g_key_file_get_locale_string_list (desktop_file->key_file,
+					    EGG_DESKTOP_FILE_GROUP, key,
+					    locale, length,
+					    error);
+}
+
 /**
  * egg_desktop_file_can_launch:
  * @desktop_file: an #EggDesktopFile
@@ -1136,6 +1222,9 @@
       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,

Modified: trunk/libgames-support/eggdesktopfile.h
==============================================================================
--- trunk/libgames-support/eggdesktopfile.h	(original)
+++ trunk/libgames-support/eggdesktopfile.h	Wed Oct  8 20:03:58 2008
@@ -31,22 +31,23 @@
 
 	EGG_DESKTOP_FILE_TYPE_APPLICATION,
 	EGG_DESKTOP_FILE_TYPE_LINK,
-	EGG_DESKTOP_FILE_TYPE_DIRECTORY,
+	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_key_file  (GKeyFile     *desktop,
+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);
 
-GKeyFile           *egg_desktop_file_get_key_file       (EggDesktopFile  *desktop_file);
-
 const char         *egg_desktop_file_get_source         (EggDesktopFile  *desktop_file);
 
 EggDesktopFileType  egg_desktop_file_get_desktop_file_type (EggDesktopFile  *desktop_file);
@@ -109,6 +110,34 @@
 #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()
 
@@ -117,7 +146,7 @@
 typedef enum {
 	EGG_DESKTOP_FILE_ERROR_INVALID,
 	EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
-	EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION,
+	EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION
 } EggDesktopFileError;
 
 /* Global application desktop file */

Modified: trunk/libgames-support/eggsmclient-osx.c
==============================================================================
--- trunk/libgames-support/eggsmclient-osx.c	(original)
+++ trunk/libgames-support/eggsmclient-osx.c	Wed Oct  8 20:03:58 2008
@@ -1,5 +1,6 @@
 /*
  * 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
@@ -17,11 +18,28 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#include "config.h"
+/* 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.)
+ */
 
-#define	G_LOG_DOMAIN "EggSMClient"
+#include "config.h"
 
-#include "eggsmclient.h"
+#include "eggsmclient-private.h"
+#include <gdk/gdkquartz.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))
@@ -36,7 +54,9 @@
 struct _EggSMClientOSX {
   EggSMClient parent;
 
-}
+  AppleEvent quit_event, quit_reply;
+  gboolean quit_requested, quitting;
+};
 
 struct _EggSMClientOSXClass
 {
@@ -52,14 +72,12 @@
 					   EggSMClientEndStyle  style,
 					   gboolean  request_confirmation);
 
-static GdkFilterReturn sm_client_osx_filter (GdkXEvent *xevent,
-					     GdkEvent  *event,
-					     gpointer   data);
+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 *osxclient)
+egg_sm_client_osx_init (EggSMClientOSX *osx)
 {
   ;
 }
@@ -81,78 +99,137 @@
 }
 
 static void
-sm_client_osx_startup (EggSMClient *client)
+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)
 {
-  gdk_window_add_filter (NULL, sm_client_osx_filter, 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;
 }
 
-void
+static void
 sm_client_osx_will_quit (EggSMClient *client,
 			 gboolean     will_quit)
 {
-  EggSMClientOSX *osxclient = (EggSMClientOSX *)client;
+  EggSMClientOSX *osx = (EggSMClientOSX *)client;
 
-  if (will_quit)
-    {
-      /* OS X doesn't send another message. We're supposed to just
-       * quit after agreeing that it's OK.
-       *
-       * FIXME: do it from an idle handler though.
-       */
-      egg_sm_client_quit (client);
-    }
-  else
-    {
-      /* FIXME: "respond to the event by returning a userCancelledErr
-       * error"
-       */
-    }
+  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);
 }
 
-void
+static gboolean
 sm_client_osx_end_session (EggSMClient         *client,
 			   EggSMClientEndStyle  style,
 			   gboolean             request_confirmation)
 {
-  EggSMClientOSX *osxclient = (EggSMClientOSX *)client;
-  FIXME_t event;
+  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_END_LOGOUT:
-      event = request_confirmation ? kAELogOut : kAEReallyLogOut;
+    case EGG_SM_CLIENT_LOGOUT:
+      id = request_confirmation ? kAELogOut : kAEReallyLogOut;
       break;
-    case EGG_SM_CLIENT_END_REBOOT:
-      event = request_confirmation ? kAEShowRestartDialog : kAERestart;
+    case EGG_SM_CLIENT_REBOOT:
+      id = request_confirmation ? kAEShowRestartDialog : kAERestart;
       break;
-    case EGG_SM_CLIENT_END_SHUTDOWN:
-      event = request_confirmation ? kAEShowShutdownDialog : kAEShutDown;
+    case EGG_SM_CLIENT_SHUTDOWN:
+      id = request_confirmation ? kAEShowShutdownDialog : kAEShutDown;
       break;
     }
 
-  /* FIXME: send event to loginwindow process */
-}
-
-static GdkFilterReturn
-egg_sm_client_osx_filter (GdkXEvent *xevent,
-			  GdkEvent  *event,
-			  gpointer   data)
-{
-  EggSMClientOSX *osxclient = data;
-  EggSMClient *client = data;
-  NSEvent *nsevent = (NSEvent *)xevent;
-
-  switch (FIXME_get_apple_event_type (nsevent))
+  err = AECreateDesc (typeProcessSerialNumber, &loginwindow_psn, 
+		      sizeof (loginwindow_psn), &target);
+  if (err != noErr)
     {
-    case kAEQuitApplication:
-      if (FIXME_app_is_a_foreground_app)
-	egg_sm_client_quit_requested (client);
-      else
-	egg_sm_client_quit (client);
-      return GDK_FILTER_REMOVE;
+      g_warning ("Could not create descriptor for loginwindow: %d", err);
+      return FALSE;
+    }
 
-    default:
-      return GDK_FILTER_CONTINUE;
+  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;
 }

Modified: trunk/libgames-support/eggsmclient-win32.c
==============================================================================
--- trunk/libgames-support/eggsmclient-win32.c	(original)
+++ trunk/libgames-support/eggsmclient-win32.c	Wed Oct  8 20:03:58 2008
@@ -17,6 +17,35 @@
  * 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"
@@ -25,6 +54,7 @@
 #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))
@@ -39,7 +69,10 @@
 struct _EggSMClientWin32 {
   EggSMClient parent;
 
-  GAsyncQueue *msg_queue;
+  HANDLE message_event, response_event;
+
+  volatile GSourceFunc event;
+  volatile gboolean will_quit;
 };
 
 struct _EggSMClientWin32Class
@@ -56,7 +89,10 @@
 					     EggSMClientEndStyle  style,
 					     gboolean  request_confirmation);
 
-static gpointer sm_client_thread (gpointer data);
+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)
 
@@ -88,9 +124,10 @@
 {
   EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
 
-  /* spawn another thread to listen for logout signals on */
-  win32->msg_queue = g_async_queue_new ();
-  g_thread_create (sm_client_thread, client, FALSE, NULL);
+  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
@@ -99,8 +136,8 @@
 {
   EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
 
-  /* Can't push NULL onto a GAsyncQueue, so we add 1 to the value... */
-  g_async_queue_push (win32->msg_queue, GINT_TO_POINTER (will_quit + 1));
+  win32->will_quit = will_quit;
+  SetEvent (win32->response_event);
 }
 
 static gboolean
@@ -159,7 +196,7 @@
   egg_sm_client_quit (smclient);
   gdk_threads_leave ();
 
-  g_async_queue_push (win32->msg_queue, GINT_TO_POINTER (1));
+  SetEvent (win32->response_event);
   return FALSE;
 }
 
@@ -172,25 +209,80 @@
   egg_sm_client_quit_cancelled (smclient);
   gdk_threads_leave ();
 
-  g_async_queue_push (win32->msg_queue, GINT_TO_POINTER (1));
+  SetEvent (win32->response_event);
   return FALSE;
 }
 
+static gboolean
+got_message (gpointer smclient)
+{
+  EggSMClientWin32 *win32 = smclient;
+
+  win32->event (win32);
+  return TRUE;
+}
 
-/* logout-listener thread */
+/* 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)
+{
+  ;
+}
 
-static int
-async_emit (EggSMClientWin32 *win32, GSourceFunc emitter)
+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)
 {
-  /* ensure message queue is empty */
-  while (g_async_queue_try_pop (win32->msg_queue))
-    ;
-
-  /* Emit signal in the main thread and wait for a response */
-  g_idle_add (emitter, win32);
-  return GPOINTER_TO_INT (g_async_queue_pop (win32->msg_queue)) - 1;
+  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,
@@ -203,19 +295,27 @@
   switch (message)
     {
     case WM_QUERYENDSESSION:
-      return async_emit (win32, emit_quit_requested);
+      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 */
-	  async_emit (win32, emit_quit);
+	  win32->event = emit_quit;
 	}
       else
 	{
 	  /* Nope, the session *isn't* ending */
-	  async_emit (win32, emit_quit_cancelled);
+	  win32->event = emit_quit_cancelled;
 	}
+
+      SetEvent (win32->message_event);
+      WaitForSingleObject (win32->response_event, INFINITE);
+
       return 0;
 
     default:
@@ -223,7 +323,7 @@
     }
 }
 
-static gpointer
+static void
 sm_client_thread (gpointer smclient)
 {
   HINSTANCE instance;
@@ -250,6 +350,4 @@
   /* main loop */
   while (GetMessage (&msg, NULL, 0, 0))
     DispatchMessage (&msg);
-
-  return NULL;
 }

Modified: trunk/libgames-support/eggsmclient-xsmp.c
==============================================================================
--- trunk/libgames-support/eggsmclient-xsmp.c	(original)
+++ trunk/libgames-support/eggsmclient-xsmp.c	Wed Oct  8 20:03:58 2008
@@ -62,11 +62,10 @@
   XSMP_STATE_INTERACT,
   XSMP_STATE_SAVE_YOURSELF_DONE,
   XSMP_STATE_SHUTDOWN_CANCELLED,
-  XSMP_STATE_CONNECTION_CLOSED,
+  XSMP_STATE_CONNECTION_CLOSED
 } EggSMClientXSMPState;
 
 static const char *state_names[] = {
-  "start",
   "idle",
   "save-yourself",
   "interact-request",
@@ -227,19 +226,14 @@
   desktop_file = egg_get_desktop_file ();
   if (desktop_file)
     {
-      GKeyFile *key_file;
       GError *err = NULL;
       char *cmdline, **argv;
       int argc;
 
-      key_file = egg_desktop_file_get_key_file (desktop_file);
-
       if (xsmp->restart_style == SmRestartIfRunning)
 	{
-	  if (g_key_file_has_key (key_file, EGG_DESKTOP_FILE_GROUP,
-				  "X-GNOME-AutoRestart", NULL) &&
-	      g_key_file_get_boolean (key_file, EGG_DESKTOP_FILE_GROUP,
-				      "X-GNOME-AutoRestart", NULL))
+	  if (egg_desktop_file_get_boolean (desktop_file, 
+					    "X-GNOME-AutoRestart", NULL))
 	    xsmp->restart_style = SmRestartImmediately;
 	}
 
@@ -423,8 +417,6 @@
   EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
   int i;
 
-  g_return_if_fail (xsmp->set_restart_command);
-
   g_strfreev (xsmp->discard_command);
 
   xsmp->discard_command = g_new (char *, argc + 1);
@@ -798,30 +790,6 @@
 }
 
 static void
-merge_keyfiles (GKeyFile *dest, GKeyFile *source)
-{
-  int g, k;
-  char **groups, **keys, *value;
-
-  groups = g_key_file_get_groups (source, NULL);
-  for (g = 0; groups[g]; g++)
-    {
-      keys = g_key_file_get_keys (source, groups[g], NULL, NULL);
-      for (k = 0; keys[k]; k++)
-	{
-	  value = g_key_file_get_value (source, groups[g], keys[k], NULL);
-	  if (value)
-	    {
-	      g_key_file_set_value (dest, groups[g], keys[k], value);
-	      g_free (value);
-	    }
-	}
-      g_strfreev (keys);
-    }
-  g_strfreev (groups);
-}
-
-static void
 save_state (EggSMClientXSMP *xsmp)
 {
   GKeyFile *state_file;
@@ -862,30 +830,54 @@
   if (desktop_file)
     {
       GKeyFile *merged_file;
-      char *exec;
-      int i;
 
       merged_file = g_key_file_new ();
-      merge_keyfiles (merged_file, egg_desktop_file_get_key_file (desktop_file));
-      merge_keyfiles (merged_file, state_file);
+      if (g_key_file_load_from_file (merged_file,
+				     egg_desktop_file_get_source (desktop_file),
+				     G_KEY_FILE_KEEP_COMMENTS |
+				     G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
+	{
+	  int g, k, i;
+	  char **groups, **keys, *value, *exec;
+
+	  groups = g_key_file_get_groups (state_file, NULL);
+	  for (g = 0; groups[g]; g++)
+	    {
+	      keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL);
+	      for (k = 0; keys[k]; k++)
+		{
+		  value = g_key_file_get_value (state_file, groups[g],
+						keys[k], NULL);
+		  if (value)
+		    {
+		      g_key_file_set_value (merged_file, groups[g],
+					    keys[k], value);
+		      g_free (value);
+		    }
+		}
+	      g_strfreev (keys);
+	    }
+	  g_strfreev (groups);
 
-      g_key_file_free (state_file);
-      state_file = merged_file;
+	  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);
+	  /* Update Exec key using "--sm-client-state-file %k" */
+	  restart = generate_command (xsmp->restart_command,
+				      NULL, "%k");
+	  for (i = 0; i < restart->len; i++)
+	    restart->pdata[i] = g_shell_quote (restart->pdata[i]);
+	  g_ptr_array_add (restart, NULL);
+	  exec = g_strjoinv (" ", (char **)restart->pdata);
+	  g_strfreev ((char **)restart->pdata);
+	  g_ptr_array_free (restart, FALSE);
+
+	  g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
+				 EGG_DESKTOP_FILE_KEY_EXEC,
+				 exec);
+	  g_free (exec);
+
+	}
     }
 
   /* Now write state_file to disk. (We can't use mktemp(), because

Modified: trunk/libgames-support/eggsmclient.c
==============================================================================
--- trunk/libgames-support/eggsmclient.c	(original)
+++ trunk/libgames-support/eggsmclient.c	Wed Oct  8 20:03:58 2008
@@ -187,6 +187,20 @@
 {
   EggSMClient *client = egg_sm_client_get ();
 
+  if (sm_client_id == NULL)
+    {
+      const gchar *desktop_autostart_id;
+
+      desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
+
+      if (desktop_autostart_id != NULL)
+        sm_client_id = g_strdup (desktop_autostart_id);
+    }
+
+  /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
+   * use the same client id. */
+  g_unsetenv ("DESKTOP_AUTOSTART_ID");
+
   if (EGG_SM_CLIENT_GET_CLASS (client)->startup)
     EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id);
   return TRUE;
@@ -303,16 +317,16 @@
 #elif defined (GDK_WINDOWING_QUARTZ)
 	  global_client = egg_sm_client_osx_new ();
 #else
-	  /* If both D-Bus and XSMP are compiled in, try D-Bus first
-	   * and fall back to XSMP if D-Bus session management isn't
-	   * available.
+	  /* 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_DBUS
-	  global_client = egg_sm_client_dbus_new ();
-# endif
 # 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_xsmp_new ();
+	    global_client = egg_sm_client_dbus_new ();
 # endif
 #endif
 	}

Added: trunk/libgames-support/eggsmclient.patch
==============================================================================
--- (empty file)
+++ trunk/libgames-support/eggsmclient.patch	Wed Oct  8 20:03:58 2008
@@ -0,0 +1,245 @@
+diff --git a/libegg/smclient/eggsmclient-xsmp.c b/libegg/smclient/eggsmclient-xsmp.c
+index 86dfdb7..71b0180 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;
+     }
+ 
+@@ -1034,14 +1069,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)
+     {
+@@ -1055,8 +1090,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 b24968d..6bec6ed 100644
+--- a/libegg/smclient/eggsmclient.c
++++ b/libegg/smclient/eggsmclient.c
+@@ -38,7 +38,7 @@ enum {
+   LAST_SIGNAL
+ };
+ 
+-static guint signals[LAST_SIGNAL] = { 0 };
++static guint signals[LAST_SIGNAL];
+ 
+ struct _EggSMClientPrivate {
+   GKeyFile *state_file;
+@@ -179,23 +179,6 @@ static gboolean sm_client_disable = FALSE;
+ static char *sm_client_state_file = NULL;
+ static char *sm_client_id = NULL;
+ 
+-static GOptionEntry entries[] = {
+-  { "sm-client-disable", 0, 0,
+-    G_OPTION_ARG_NONE, &sm_client_disable,
+-    N_("Disable connection to session manager"), NULL },
+-  { "sm-client-state-file", 0, 0,
+-    G_OPTION_ARG_STRING, &sm_client_state_file,
+-    N_("Specify file containing saved configuration"), N_("FILE") },
+-  { "sm-client-id", 0, 0,
+-    G_OPTION_ARG_STRING, &sm_client_id,
+-    N_("Specify session management ID"), N_("ID") },
+-  /* Compatibility options */
+-  { "sm-disable", 0, G_OPTION_FLAG_HIDDEN,
+-    G_OPTION_ARG_NONE, &sm_client_disable,
+-    NULL, NULL },
+-  { NULL }
+-};
+-
+ static gboolean
+ sm_client_post_parse_func (GOptionContext  *context,
+ 			   GOptionGroup    *group,
+@@ -235,6 +218,22 @@ sm_client_post_parse_func (GOptionContext  *context,
+ 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") },
++    /* Compatibility options */
++    { "sm-disable", 0, G_OPTION_FLAG_HIDDEN,
++      G_OPTION_ARG_NONE, &sm_client_disable,
++      NULL, NULL },
++    { NULL }
++  };
+   GOptionGroup *group;
+ 
+   /* Use our own debug handler for the "EggSMClient" domain. */
+@@ -242,8 +241,8 @@ egg_sm_client_get_option_group (void)
+ 		     egg_sm_client_debug_handler, NULL);
+ 
+   group = g_option_group_new ("sm-client",
+-			      _("Session Management Options"),
+-			      _("Show Session Management options"),
++			      _("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);
+@@ -438,6 +437,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,



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