]> www.fi.muni.cz Git - evince.git/blob - cut-n-paste/smclient/eggsmclient-osx.c
5428c09fa07d3e9d719d9adc317deecee2d4a426
[evince.git] / cut-n-paste / smclient / eggsmclient-osx.c
1 /*
2  * Copyright (C) 2007 Novell, Inc.
3  * Copyright (C) 2008 Red Hat, Inc.
4  *
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.
9  *
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.
14  *
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., 51 Franklin Street, Fifth Floor, 
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /* EggSMClientOSX
22  *
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
25  *
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.)
35  */
36
37 #include "config.h"
38
39 #include "eggsmclient-private.h"
40 #include <gdk/gdkquartz.h>
41 #include <Carbon/Carbon.h>
42 #include <CoreServices/CoreServices.h>
43
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))
50
51 typedef struct _EggSMClientOSX        EggSMClientOSX;
52 typedef struct _EggSMClientOSXClass   EggSMClientOSXClass;
53
54 struct _EggSMClientOSX {
55   EggSMClient parent;
56
57   AppleEvent quit_event, quit_reply;
58   gboolean quit_requested, quitting;
59 };
60
61 struct _EggSMClientOSXClass
62 {
63   EggSMClientClass parent_class;
64
65 };
66
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);
74
75 static pascal OSErr quit_requested (const AppleEvent *, AppleEvent *, long);
76
77 G_DEFINE_TYPE (EggSMClientOSX, egg_sm_client_osx, EGG_TYPE_SM_CLIENT)
78
79 static void
80 egg_sm_client_osx_init (EggSMClientOSX *osx)
81 {
82   ;
83 }
84
85 static void
86 egg_sm_client_osx_class_init (EggSMClientOSXClass *klass)
87 {
88   EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
89
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;
93 }
94
95 EggSMClient *
96 egg_sm_client_osx_new (void)
97 {
98   return g_object_new (EGG_TYPE_SM_CLIENT_OSX, NULL);
99 }
100
101 static void
102 sm_client_osx_startup (EggSMClient *client,
103                        const char  *client_id)
104 {
105   AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
106                          NewAEEventHandlerUPP (quit_requested),
107                          (long)GPOINTER_TO_SIZE (client), false);
108 }
109
110 static gboolean
111 idle_quit_requested (gpointer client)
112 {
113   egg_sm_client_quit_requested (client);
114   return FALSE;
115 }
116
117 static pascal OSErr
118 quit_requested (const AppleEvent *aevt, AppleEvent *reply, long refcon)
119 {
120   EggSMClient *client = GSIZE_TO_POINTER ((gsize)refcon);
121   EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon);
122
123   g_return_val_if_fail (!osx->quit_requested, userCanceledErr);
124     
125   /* FIXME AEInteractWithUser? */
126
127   osx->quit_requested = TRUE;
128   AEDuplicateDesc (aevt, &osx->quit_event);
129   AEDuplicateDesc (reply, &osx->quit_reply);
130   AESuspendTheCurrentEvent (aevt);
131
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
134    */
135   g_idle_add (idle_quit_requested, client);
136   return noErr;
137 }
138
139 static pascal OSErr
140 quit_requested_resumed (const AppleEvent *aevt, AppleEvent *reply, long refcon)
141 {
142   EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon);
143
144   osx->quit_requested = FALSE;
145   return osx->quitting ? noErr : userCanceledErr;
146 }
147
148 static gboolean
149 idle_will_quit (gpointer client)
150 {
151   EggSMClientOSX *osx = (EggSMClientOSX *)client;
152
153   /* Resume the event with a new handler that will return a value to
154    * the system.
155    */
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);
161
162   if (osx->quitting)
163     egg_sm_client_quit (client);
164   return FALSE;
165 }
166
167 static void
168 sm_client_osx_will_quit (EggSMClient *client,
169                          gboolean     will_quit)
170 {
171   EggSMClientOSX *osx = (EggSMClientOSX *)client;
172
173   g_return_if_fail (osx->quit_requested);
174
175   osx->quitting = will_quit;
176
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.
181    */
182   g_idle_add (idle_will_quit, client);
183 }
184
185 static gboolean
186 sm_client_osx_end_session (EggSMClient         *client,
187                            EggSMClientEndStyle  style,
188                            gboolean             request_confirmation)
189 {
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;
195
196   switch (style)
197     {
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;
208     }
209
210   err = AECreateDesc (typeProcessSerialNumber, &loginwindow_psn, 
211                       sizeof (loginwindow_psn), &target);
212   if (err != noErr)
213     {
214       g_warning ("Could not create descriptor for loginwindow: %d", err);
215       return FALSE;
216     }
217
218   err = AECreateAppleEvent (kCoreEventClass, id, &target,
219                             kAutoGenerateReturnID, kAnyTransactionID,
220                             &event);
221   AEDisposeDesc (&target);
222   if (err != noErr)
223     {
224       g_warning ("Could not create logout AppleEvent: %d", err);
225       return FALSE;
226     }
227
228   err = AESend (&event, &reply, kAENoReply, kAENormalPriority,
229                 kAEDefaultTimeout, NULL, NULL);
230   AEDisposeDesc (&event);
231   if (err == noErr)
232     AEDisposeDesc (&reply);
233
234   return err == noErr;
235 }