]> www.fi.muni.cz Git - evince.git/blob - cut-n-paste/smclient/eggsmclient-win32.c
91a257154a16dfc44978ac1535807b60280e7b98
[evince.git] / cut-n-paste / smclient / eggsmclient-win32.c
1 /*
2  * Copyright (C) 2007 Novell, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /* EggSMClientWin32
21  *
22  * For details on the Windows XP logout process, see:
23  * http://msdn.microsoft.com/en-us/library/aa376876.aspx.
24  *
25  * Vista adds some new APIs which EggSMClient does not make use of; see
26  * http://msdn.microsoft.com/en-us/library/ms700677(VS.85).aspx
27  *
28  * When shutting down, Windows sends every top-level window a
29  * WM_QUERYENDSESSION event, which the application must respond to
30  * synchronously, saying whether or not it will quit. To avoid main
31  * loop re-entrancy problems (and to avoid having to muck about too
32  * much with the guts of the gdk-win32 main loop), we watch for this
33  * event in a separate thread, which then signals the main thread and
34  * waits for the main thread to handle the event. Since we don't want
35  * to require g_thread_init() to be called, we do this all using
36  * Windows-specific thread methods.
37  *
38  * After the application handles the WM_QUERYENDSESSION event,
39  * Windows then sends it a WM_ENDSESSION event with a TRUE or FALSE
40  * parameter indicating whether the session is or is not actually
41  * going to end now. We handle this from the other thread as well.
42  *
43  * As mentioned above, Vista introduces several additional new APIs
44  * that don't fit into the (current) EggSMClient API. Windows also has
45  * an entirely separate shutdown-notification scheme for non-GUI apps,
46  * which we also don't handle here.
47  */
48
49 #include "config.h"
50
51 #include "eggsmclient-private.h"
52 #include <gdk/gdk.h>
53
54 #define WIN32_LEAN_AND_MEAN
55 #define UNICODE
56 #include <windows.h>
57 #include <process.h>
58
59 #define EGG_TYPE_SM_CLIENT_WIN32            (egg_sm_client_win32_get_type ())
60 #define EGG_SM_CLIENT_WIN32(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32))
61 #define EGG_SM_CLIENT_WIN32_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32Class))
62 #define EGG_IS_SM_CLIENT_WIN32(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_WIN32))
63 #define EGG_IS_SM_CLIENT_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_WIN32))
64 #define EGG_SM_CLIENT_WIN32_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32Class))
65
66 typedef struct _EggSMClientWin32        EggSMClientWin32;
67 typedef struct _EggSMClientWin32Class   EggSMClientWin32Class;
68
69 struct _EggSMClientWin32 {
70   EggSMClient parent;
71
72   HANDLE message_event, response_event;
73
74   volatile GSourceFunc event;
75   volatile gboolean will_quit;
76 };
77
78 struct _EggSMClientWin32Class
79 {
80   EggSMClientClass parent_class;
81
82 };
83
84 static void     sm_client_win32_startup (EggSMClient *client,
85                                          const char  *client_id);
86 static void     sm_client_win32_will_quit (EggSMClient *client,
87                                            gboolean     will_quit);
88 static gboolean sm_client_win32_end_session (EggSMClient         *client,
89                                              EggSMClientEndStyle  style,
90                                              gboolean  request_confirmation);
91
92 static GSource *g_win32_handle_source_add (HANDLE handle, GSourceFunc callback,
93                                         gpointer user_data);
94 static gboolean got_message (gpointer user_data);
95 static void sm_client_thread (gpointer data);
96
97 G_DEFINE_TYPE (EggSMClientWin32, egg_sm_client_win32, EGG_TYPE_SM_CLIENT)
98
99 static void
100 egg_sm_client_win32_init (EggSMClientWin32 *win32)
101 {
102   ;
103 }
104
105 static void
106 egg_sm_client_win32_class_init (EggSMClientWin32Class *klass)
107 {
108   EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
109
110   sm_client_class->startup             = sm_client_win32_startup;
111   sm_client_class->will_quit           = sm_client_win32_will_quit;
112   sm_client_class->end_session         = sm_client_win32_end_session;
113 }
114
115 EggSMClient *
116 egg_sm_client_win32_new (void)
117 {
118   return g_object_new (EGG_TYPE_SM_CLIENT_WIN32, NULL);
119 }
120
121 static void
122 sm_client_win32_startup (EggSMClient *client,
123                          const char  *client_id)
124 {
125   EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
126
127   win32->message_event = CreateEvent (NULL, FALSE, FALSE, NULL);
128   win32->response_event = CreateEvent (NULL, FALSE, FALSE, NULL);
129   g_win32_handle_source_add (win32->message_event, got_message, win32);  
130   _beginthread (sm_client_thread, 0, client);
131 }
132
133 static void
134 sm_client_win32_will_quit (EggSMClient *client,
135                            gboolean     will_quit)
136 {
137   EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
138
139   win32->will_quit = will_quit;
140   SetEvent (win32->response_event);
141 }
142
143 static gboolean
144 sm_client_win32_end_session (EggSMClient         *client,
145                              EggSMClientEndStyle  style,
146                              gboolean             request_confirmation)
147 {
148   UINT uFlags = EWX_LOGOFF;
149
150   switch (style)
151     {
152     case EGG_SM_CLIENT_END_SESSION_DEFAULT:
153     case EGG_SM_CLIENT_LOGOUT:
154       uFlags = EWX_LOGOFF;
155       break;
156     case EGG_SM_CLIENT_REBOOT:
157       uFlags = EWX_REBOOT;
158       break;
159     case EGG_SM_CLIENT_SHUTDOWN:
160       uFlags = EWX_POWEROFF;
161       break;
162     }
163
164   /* There's no way to make ExitWindowsEx() show a logout dialog, so
165    * we ignore @request_confirmation.
166    */
167
168 #ifdef SHTDN_REASON_FLAG_PLANNED
169   ExitWindowsEx (uFlags, SHTDN_REASON_FLAG_PLANNED);
170 #else
171   ExitWindowsEx (uFlags, 0);
172 #endif
173
174   return TRUE;
175 }
176
177
178 /* callbacks from logout-listener thread */
179
180 static gboolean
181 emit_quit_requested (gpointer smclient)
182 {
183   gdk_threads_enter ();
184   egg_sm_client_quit_requested (smclient);
185   gdk_threads_leave ();
186
187   return FALSE;
188 }
189
190 static gboolean
191 emit_quit (gpointer smclient)
192 {
193   EggSMClientWin32 *win32 = smclient;
194
195   gdk_threads_enter ();
196   egg_sm_client_quit (smclient);
197   gdk_threads_leave ();
198
199   SetEvent (win32->response_event);
200   return FALSE;
201 }
202
203 static gboolean
204 emit_quit_cancelled (gpointer smclient)
205 {
206   EggSMClientWin32 *win32 = smclient;
207
208   gdk_threads_enter ();
209   egg_sm_client_quit_cancelled (smclient);
210   gdk_threads_leave ();
211
212   SetEvent (win32->response_event);
213   return FALSE;
214 }
215
216 static gboolean
217 got_message (gpointer smclient)
218 {
219   EggSMClientWin32 *win32 = smclient;
220
221   win32->event (win32);
222   return TRUE;
223 }
224
225 /* Windows HANDLE GSource */
226
227 typedef struct {
228   GSource source;
229   GPollFD pollfd;
230 } GWin32HandleSource;
231
232 static gboolean
233 g_win32_handle_source_prepare (GSource *source, gint *timeout)
234 {
235   *timeout = -1;
236   return FALSE;
237 }
238
239 static gboolean
240 g_win32_handle_source_check (GSource *source)
241 {
242   GWin32HandleSource *hsource = (GWin32HandleSource *)source;
243
244   return hsource->pollfd.revents;
245 }
246
247 static gboolean
248 g_win32_handle_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
249 {
250   return (*callback) (user_data);
251 }
252
253 static void
254 g_win32_handle_source_finalize (GSource *source)
255 {
256   ;
257 }
258
259 GSourceFuncs g_win32_handle_source_funcs = {
260   g_win32_handle_source_prepare,
261   g_win32_handle_source_check,
262   g_win32_handle_source_dispatch,
263   g_win32_handle_source_finalize
264 };
265
266 static GSource *
267 g_win32_handle_source_add (HANDLE handle, GSourceFunc callback, gpointer user_data)
268 {
269   GWin32HandleSource *hsource;
270   GSource *source;
271
272   source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource));
273   hsource = (GWin32HandleSource *)source;
274   hsource->pollfd.fd = (int)handle;
275   hsource->pollfd.events = G_IO_IN;
276   hsource->pollfd.revents = 0;
277   g_source_add_poll (source, &hsource->pollfd);
278
279   g_source_set_callback (source, callback, user_data, NULL);
280   g_source_attach (source, NULL);
281   return source;
282 }
283
284 /* logout-listener thread */
285
286 LRESULT CALLBACK
287 sm_client_win32_window_procedure (HWND   hwnd,
288                                   UINT   message,
289                                   WPARAM wParam,
290                                   LPARAM lParam)
291 {
292   EggSMClientWin32 *win32 =
293     (EggSMClientWin32 *)GetWindowLongPtr (hwnd, GWLP_USERDATA);
294
295   switch (message)
296     {
297     case WM_QUERYENDSESSION:
298       win32->event = emit_quit_requested;
299       SetEvent (win32->message_event);
300
301       WaitForSingleObject (win32->response_event, INFINITE);
302       return win32->will_quit;
303
304     case WM_ENDSESSION:
305       if (wParam)
306         {
307           /* The session is ending */
308           win32->event = emit_quit;
309         }
310       else
311         {
312           /* Nope, the session *isn't* ending */
313           win32->event = emit_quit_cancelled;
314         }
315
316       SetEvent (win32->message_event);
317       WaitForSingleObject (win32->response_event, INFINITE);
318
319       return 0;
320
321     default:
322       return DefWindowProc (hwnd, message, wParam, lParam);
323     }
324 }
325
326 static void
327 sm_client_thread (gpointer smclient)
328 {
329   HINSTANCE instance;
330   WNDCLASSEXW wcl; 
331   ATOM klass;
332   HWND window;
333   MSG msg;
334
335   instance = GetModuleHandle (NULL);
336
337   memset (&wcl, 0, sizeof (WNDCLASSEX));
338   wcl.cbSize = sizeof (WNDCLASSEX);
339   wcl.lpfnWndProc = sm_client_win32_window_procedure;
340   wcl.hInstance = instance;
341   wcl.lpszClassName = L"EggSmClientWindow";
342   klass = RegisterClassEx (&wcl);
343
344   window = CreateWindowEx (0, MAKEINTRESOURCE (klass),
345                            L"EggSmClientWindow", 0,
346                            10, 10, 50, 50, GetDesktopWindow (),
347                            NULL, instance, NULL);
348   SetWindowLongPtr (window, GWLP_USERDATA, (LONG_PTR)smclient);
349
350   /* main loop */
351   while (GetMessage (&msg, NULL, 0, 0))
352     DispatchMessage (&msg);
353 }