libegg r904 - trunk/libegg/smclient



Author: danw
Date: Fri Oct  3 16:00:03 2008
New Revision: 904
URL: http://svn.gnome.org/viewvc/libegg?rev=904&view=rev

Log:
        * eggsmclient-osx.c: Implement.

        * Makefile.am: add stuff for OS X build

	* egg-session-end.c: fix the --shutdown/--reboot args to actually
        work

        * README: update


Modified:
   trunk/libegg/smclient/ChangeLog
   trunk/libegg/smclient/Makefile.am
   trunk/libegg/smclient/README
   trunk/libegg/smclient/egg-session-end.c
   trunk/libegg/smclient/eggsmclient-osx.c

Modified: trunk/libegg/smclient/Makefile.am
==============================================================================
--- trunk/libegg/smclient/Makefile.am	(original)
+++ trunk/libegg/smclient/Makefile.am	Fri Oct  3 16:00:03 2008
@@ -1,10 +1,13 @@
 if PLATFORM_WIN32
-platform_defines =
-platform_ltlibraries =
-platform_libs =
 platform_sources = eggsmclient-win32.c
-platform_app_ldflags = -mwindows
-platform_programs =
+platform_logout_test_ldflags = -mwindows
+else
+if PLATFORM_OSX
+platform_defines = -xobjective-c
+platform_ldflags = -framework Carbon
+platform_session_end_ldflags = -framework Carbon
+platform_logout_test_ldflags = -framework Carbon
+platform_sources = eggsmclient-osx.c
 else
 platform_defines = -DEGG_SM_CLIENT_BACKEND_XSMP
 platform_ltlibraries =                   \
@@ -12,9 +15,9 @@
 	libeggsmclient-gnome.la
 platform_libs = libeggdesktopfile.la -lSM -lICE
 platform_sources = eggsmclient-xsmp.c
-platform_app_ldflags =
 platform_programs = egg-launch
 endif
+endif
 
 INCLUDES =                               \
 	-DG_LOG_DOMAIN=\""EggSMClient"\" \
@@ -30,6 +33,9 @@
 	$(EGG_SMCLIENT_LIBS)             \
 	$(platform_libs)
 
+libeggsmclient_la_LDFLAGS =              \
+	$(platform_ldflags)
+
 libeggsmclient_la_SOURCES =              \
 	eggsmclient.c                    \
 	eggsmclient.h                    \
@@ -66,6 +72,9 @@
 egg_session_end_LDADD =                  \
 	libeggsmclient.la
 
+egg_session_end_LDFLAGS =                \
+	$(platform_session_end_ldflags)
+
 logout_test_SOURCES =                    \
 	logout-test.c
 
@@ -73,7 +82,7 @@
 	libeggsmclient.la
 
 logout_test_LDFLAGS =                    \
-	$(platform_app_ldflags)
+	$(platform_logout_test_ldflags)
 
 egg_launch_SOURCES =                     \
 	egg-launch.c

Modified: trunk/libegg/smclient/README
==============================================================================
--- trunk/libegg/smclient/README	(original)
+++ trunk/libegg/smclient/README	Fri Oct  3 16:00:03 2008
@@ -8,7 +8,7 @@
 cleanly when the session ends too. OTOH, making glib depend on libSM
 would obviously suck. Using dlopen may be a good solution.
 
-EggDesktopItem is proposed gtk-level rewrite of GnomeDesktopItem. See
+EggDesktopFile is proposed gtk-level rewrite of GnomeDesktopItem. See
 http://bugzilla.gnome.org/show_bug.cgi?id=415070
 
 
@@ -35,6 +35,15 @@
 
 	You need to call g_thread_null(NULL) to initialize threads.
 
+If you are building on OS X:
+
+	You need to link your application with the Cocoa framework
+	(-framework Cocoa). Libtool doesn't record framework
+	dependencies, and since for libegg purposes we're only
+	building libeggsmclient as a static library, the dependency
+	doesn't get recorded by the linker either, so you have to add
+	it by hand.
+
 
 Using EggSMClient
 -----------------
@@ -71,7 +80,7 @@
         gtk_get_option_group() to it. See egg-session-end.c for an
         example.)
 
-	You should also call egg_set_desktop_file() (in
+	On Linux/Unix, you should also call egg_set_desktop_file() (in
 	"eggdesktopfile.h"), passing it the path to your application's
 	installed .desktop file. (This will be used for some
 	SM-related purposes and will also be used to initialize your
@@ -135,10 +144,6 @@
 on them. (If the application must have its working directory restored,
 it can just save and restore it itself.)
 
-(There probably needs to be a way to set _GSM_Priority, for
-compatibility with the current gnome-session; this will probably be
-done via another .desktop key.)
-
 The "save_yourself" signal/callback is split into two signals in
 EggSMClient: save_state and quit_requested. Most GnomeClient-based
 apps only implement the state-saving functionality currently, so they
@@ -217,7 +222,8 @@
 	- egg-session-end.c: A replacement for gnome-session-save.
 	  (Well, actually only for "gnome-session-save --kill".)
 	  Yes, the --reboot and --shutdown arguments are only there to
-	  tease you.
+	  tease you (with the XSMP backend at least; they work on OS
+	  X.)
 
 	- logout-test.c: Sits around waiting for you to try to log
           out, and then asks "are you sure", to test
@@ -228,14 +234,6 @@
 
 Non-functional backends:
 
-	- OS X: eggsmclient-osx.c is not quite a proof-of-concept.
-          It's more of a handwave-of-concept. I don't have a working
-          OS X machine at the moment, so I can't test/finish this.
-
-	  See http://developer.apple.com/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/BootProcess.html#//apple_ref/doc/uid/20002130-114618
-	  and other URLs linked from it for more info on the OS X
-	  logout process.
-
 	- D-Bus: There are a few references in the code to a
           non-existent D-Bus session management client. The idea is
           that once we have a session manager that implements that,

Modified: trunk/libegg/smclient/egg-session-end.c
==============================================================================
--- trunk/libegg/smclient/egg-session-end.c	(original)
+++ trunk/libegg/smclient/egg-session-end.c	Fri Oct  3 16:00:03 2008
@@ -32,23 +32,33 @@
 gboolean gui = FALSE;
 
 static gboolean
-style_callback (const char *option_name, const char *value,
-		gpointer data, GError **error)
+logout_callback (const char *option_name, const char *value,
+		 gpointer data, GError **error)
 {
-  if (!strcmp (option_name, "logout"))
-    style = EGG_SM_CLIENT_LOGOUT;
-  else if (!strcmp (option_name, "reboot"))
-    style = EGG_SM_CLIENT_REBOOT;
-  else if (!strcmp (option_name, "shutdown"))
-    style = EGG_SM_CLIENT_SHUTDOWN;
+  style = EGG_SM_CLIENT_LOGOUT;
+  return TRUE;
+}
 
+static gboolean
+reboot_callback (const char *option_name, const char *value,
+		 gpointer data, GError **error)
+{
+  style = EGG_SM_CLIENT_REBOOT;
+  return TRUE;
+}
+
+static gboolean
+shutdown_callback (const char *option_name, const char *value,
+		   gpointer data, GError **error)
+{
+  style = EGG_SM_CLIENT_SHUTDOWN;
   return TRUE;
 }
 
 static const GOptionEntry options[] = {
-  { "logout", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, style_callback, N_("Logout (as opposed to rebooting or shutting down)"), NULL },
-  { "reboot", 'r', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, style_callback, N_("Reboot"), NULL },
-  { "shutdown", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, style_callback, N_("Shut down computer"), NULL },
+  { "logout", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, logout_callback, N_("Logout (as opposed to rebooting or shutting down)"), NULL },
+  { "reboot", 'r', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, reboot_callback, N_("Reboot"), NULL },
+  { "shutdown", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, shutdown_callback, N_("Shut down computer"), NULL },
 
   { "gui",  'g', 0, G_OPTION_ARG_NONE, &gui, N_("Use dialog boxes for errors"), NULL },
   { "no-confirmation",  'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &confirm, N_("Don't give the user a chance to confirm"), NULL },

Modified: trunk/libegg/smclient/eggsmclient-osx.c
==============================================================================
--- trunk/libegg/smclient/eggsmclient-osx.c	(original)
+++ trunk/libegg/smclient/eggsmclient-osx.c	Fri Oct  3 16:00:03 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;
 }



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