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.
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
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
{
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
,
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
)
80 egg_sm_client_osx_init (EggSMClientOSX
*osx
)
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
;
96 egg_sm_client_osx_new (void)
98 return g_object_new (EGG_TYPE_SM_CLIENT_OSX
, NULL
);
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);
111 idle_quit_requested (gpointer client
)
113 egg_sm_client_quit_requested (client
);
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
);
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
;
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
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
);
163 egg_sm_client_quit (client
);
168 sm_client_osx_will_quit (EggSMClient
*client
,
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
);
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
;
198 case EGG_SM_CLIENT_END_SESSION_DEFAULT
:
199 case EGG_SM_CLIENT_LOGOUT
:
200 id
= request_confirmation
? kAELogOut
: kAEReallyLogOut
;
202 case EGG_SM_CLIENT_REBOOT
:
203 id
= request_confirmation
? kAEShowRestartDialog
: kAERestart
;
205 case EGG_SM_CLIENT_SHUTDOWN
:
206 id
= request_confirmation
? kAEShowShutdownDialog
: kAEShutDown
;
210 err
= AECreateDesc (typeProcessSerialNumber
, &loginwindow_psn
,
211 sizeof (loginwindow_psn
), &target
);
214 g_warning ("Could not create descriptor for loginwindow: %d", err
);
218 err
= AECreateAppleEvent (kCoreEventClass
, id
, &target
,
219 kAutoGenerateReturnID
, kAnyTransactionID
,
221 AEDisposeDesc (&target
);
224 g_warning ("Could not create logout AppleEvent: %d", err
);
228 err
= AESend (&event
, &reply
, kAENoReply
, kAENormalPriority
,
229 kAEDefaultTimeout
, NULL
, NULL
);
230 AEDisposeDesc (&event
);
232 AEDisposeDesc (&reply
);