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
G_DEFINE_TYPE(EggSMClientWin32,egg_sm_client_win32,EGG_TYPE_SM_CLIENT)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
egg_sm_client_win32_class_init(EggSMClientWin32Class * klass)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 *
egg_sm_client_win32_new(void)116 egg_sm_client_win32_new (void)
117 {
118 return g_object_new (EGG_TYPE_SM_CLIENT_WIN32, NULL);
119 }
120
121 static void
sm_client_win32_startup(EggSMClient * client,const char * client_id)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
sm_client_win32_will_quit(EggSMClient * client,gboolean will_quit)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
sm_client_win32_end_session(EggSMClient * client,EggSMClientEndStyle style,gboolean request_confirmation)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
emit_quit_requested(gpointer smclient)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
emit_quit(gpointer smclient)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
emit_quit_cancelled(gpointer smclient)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
got_message(gpointer smclient)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
g_win32_handle_source_prepare(GSource * source,gint * timeout)233 g_win32_handle_source_prepare (GSource *source, gint *timeout)
234 {
235 *timeout = -1;
236 return FALSE;
237 }
238
239 static gboolean
g_win32_handle_source_check(GSource * source)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
g_win32_handle_source_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)248 g_win32_handle_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
249 {
250 return (*callback) (user_data);
251 }
252
253 static void
g_win32_handle_source_finalize(GSource * source)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 *
g_win32_handle_source_add(HANDLE handle,GSourceFunc callback,gpointer user_data)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
sm_client_win32_window_procedure(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)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
sm_client_thread(gpointer smclient)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 }
354