Modify version string to post-release version 0.18.101
[gmpc.git] / src / smclient / eggsmclient-osx.c
blob6f3c481876a2d4edd89ff77ecf72e23cdc963d29
1 /*
2 * Copyright (C) 2007 Novell, Inc.
3 * Copyright (C) 2008 Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
21 /* EggSMClientOSX
23 * For details on the OS X logout process, see:
24 * http://developer.apple.com/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/BootProcess.html#//apple_ref/doc/uid/20002130-114618
26 * EggSMClientOSX registers for the kAEQuitApplication AppleEvent; the
27 * handler we register (quit_requested()) will be invoked from inside
28 * the quartz event-handling code (specifically, from inside
29 * [NSApplication nextEventMatchingMask]) when an AppleEvent arrives.
30 * We use AESuspendTheCurrentEvent() and AEResumeTheCurrentEvent() to
31 * allow asynchronous / non-main-loop-reentering processing of the
32 * quit request. (These are part of the Carbon framework; it doesn't
33 * seem to be possible to handle AppleEvents asynchronously from
34 * Cocoa.)
37 #include "config.h"
39 #include "eggsmclient-private.h"
40 #include <gdk/gdkquartz.h>
41 #include <Carbon/Carbon.h>
42 #include <CoreServices/CoreServices.h>
44 #define EGG_TYPE_SM_CLIENT_OSX (egg_sm_client_osx_get_type ())
45 #define EGG_SM_CLIENT_OSX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSX))
46 #define EGG_SM_CLIENT_OSX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass))
47 #define EGG_IS_SM_CLIENT_OSX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_OSX))
48 #define EGG_IS_SM_CLIENT_OSX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_OSX))
49 #define EGG_SM_CLIENT_OSX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass))
51 typedef struct _EggSMClientOSX EggSMClientOSX;
52 typedef struct _EggSMClientOSXClass EggSMClientOSXClass;
54 struct _EggSMClientOSX {
55 EggSMClient parent;
57 AppleEvent quit_event, quit_reply;
58 gboolean quit_requested, quitting;
61 struct _EggSMClientOSXClass
63 EggSMClientClass parent_class;
67 static void sm_client_osx_startup (EggSMClient *client,
68 const char *client_id);
69 static void sm_client_osx_will_quit (EggSMClient *client,
70 gboolean will_quit);
71 static gboolean sm_client_osx_end_session (EggSMClient *client,
72 EggSMClientEndStyle style,
73 gboolean request_confirmation);
75 static pascal OSErr quit_requested (const AppleEvent *, AppleEvent *, long);
77 G_DEFINE_TYPE (EggSMClientOSX, egg_sm_client_osx, EGG_TYPE_SM_CLIENT)
79 static void
80 egg_sm_client_osx_init (EggSMClientOSX *osx)
85 static void
86 egg_sm_client_osx_class_init (EggSMClientOSXClass *klass)
88 EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
90 sm_client_class->startup = sm_client_osx_startup;
91 sm_client_class->will_quit = sm_client_osx_will_quit;
92 sm_client_class->end_session = sm_client_osx_end_session;
95 EggSMClient *
96 egg_sm_client_osx_new (void)
98 return g_object_new (EGG_TYPE_SM_CLIENT_OSX, NULL);
101 static void
102 sm_client_osx_startup (EggSMClient *client,
103 const char *client_id)
105 AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
106 NewAEEventHandlerUPP (quit_requested),
107 (long)GPOINTER_TO_SIZE (client), false);
110 static gboolean
111 idle_quit_requested (gpointer client)
113 egg_sm_client_quit_requested (client);
114 return FALSE;
117 static pascal OSErr
118 quit_requested (const AppleEvent *aevt, AppleEvent *reply, long refcon)
120 EggSMClient *client = GSIZE_TO_POINTER ((gsize)refcon);
121 EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon);
123 g_return_val_if_fail (!osx->quit_requested, userCanceledErr);
125 /* FIXME AEInteractWithUser? */
127 osx->quit_requested = TRUE;
128 AEDuplicateDesc (aevt, &osx->quit_event);
129 AEDuplicateDesc (reply, &osx->quit_reply);
130 AESuspendTheCurrentEvent (aevt);
132 /* Don't emit the "quit_requested" signal immediately, since we're
133 * called from a weird point in the guts of gdkeventloop-quartz.c
135 g_idle_add (idle_quit_requested, client);
136 return noErr;
139 static pascal OSErr
140 quit_requested_resumed (const AppleEvent *aevt, AppleEvent *reply, long refcon)
142 EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon);
144 osx->quit_requested = FALSE;
145 return osx->quitting ? noErr : userCanceledErr;
148 static gboolean
149 idle_will_quit (gpointer client)
151 EggSMClientOSX *osx = (EggSMClientOSX *)client;
153 /* Resume the event with a new handler that will return a value to
154 * the system.
156 AEResumeTheCurrentEvent (&osx->quit_event, &osx->quit_reply,
157 NewAEEventHandlerUPP (quit_requested_resumed),
158 (long)GPOINTER_TO_SIZE (client));
159 AEDisposeDesc (&osx->quit_event);
160 AEDisposeDesc (&osx->quit_reply);
162 if (osx->quitting)
163 egg_sm_client_quit (client);
164 return FALSE;
167 static void
168 sm_client_osx_will_quit (EggSMClient *client,
169 gboolean will_quit)
171 EggSMClientOSX *osx = (EggSMClientOSX *)client;
173 g_return_if_fail (osx->quit_requested);
175 osx->quitting = will_quit;
177 /* Finish in an idle handler since the caller might have called
178 * egg_sm_client_will_quit() from inside the "quit_requested" signal
179 * handler, but may not expect the "quit" signal to arrive during
180 * the _will_quit() call.
182 g_idle_add (idle_will_quit, client);
185 static gboolean
186 sm_client_osx_end_session (EggSMClient *client,
187 EggSMClientEndStyle style,
188 gboolean request_confirmation)
190 static const ProcessSerialNumber loginwindow_psn = { 0, kSystemProcess };
191 AppleEvent event = { typeNull, NULL }, reply = { typeNull, NULL };
192 AEAddressDesc target;
193 AEEventID id;
194 OSErr err;
196 switch (style)
198 case EGG_SM_CLIENT_END_SESSION_DEFAULT:
199 case EGG_SM_CLIENT_LOGOUT:
200 id = request_confirmation ? kAELogOut : kAEReallyLogOut;
201 break;
202 case EGG_SM_CLIENT_REBOOT:
203 id = request_confirmation ? kAEShowRestartDialog : kAERestart;
204 break;
205 case EGG_SM_CLIENT_SHUTDOWN:
206 id = request_confirmation ? kAEShowShutdownDialog : kAEShutDown;
207 break;
210 err = AECreateDesc (typeProcessSerialNumber, &loginwindow_psn,
211 sizeof (loginwindow_psn), &target);
212 if (err != noErr)
214 g_warning ("Could not create descriptor for loginwindow: %d", err);
215 return FALSE;
218 err = AECreateAppleEvent (kCoreEventClass, id, &target,
219 kAutoGenerateReturnID, kAnyTransactionID,
220 &event);
221 AEDisposeDesc (&target);
222 if (err != noErr)
224 g_warning ("Could not create logout AppleEvent: %d", err);
225 return FALSE;
228 err = AESend (&event, &reply, kAENoReply, kAENormalPriority,
229 kAEDefaultTimeout, NULL, NULL);
230 AEDisposeDesc (&event);
231 if (err == noErr)
232 AEDisposeDesc (&reply);
234 return err == noErr;