2
* Copyright (C) 2007 Novell, Inc.
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.
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.
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.
22
* For details on the Windows XP logout process, see:
23
* http://msdn.microsoft.com/en-us/library/aa376876.aspx.
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
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.
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.
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.
51
#include "eggsmclient-private.h"
54
#define WIN32_LEAN_AND_MEAN
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))
66
typedef struct _EggSMClientWin32 EggSMClientWin32;
67
typedef struct _EggSMClientWin32Class EggSMClientWin32Class;
69
struct _EggSMClientWin32 {
72
HANDLE message_event, response_event;
74
volatile GSourceFunc event;
75
volatile gboolean will_quit;
78
struct _EggSMClientWin32Class
80
EggSMClientClass parent_class;
84
static void sm_client_win32_startup (EggSMClient *client,
85
const char *client_id);
86
static void sm_client_win32_will_quit (EggSMClient *client,
88
static gboolean sm_client_win32_end_session (EggSMClient *client,
89
EggSMClientEndStyle style,
90
gboolean request_confirmation);
92
static GSource *g_win32_handle_source_add (HANDLE handle, GSourceFunc callback,
94
static gboolean got_message (gpointer user_data);
95
static void sm_client_thread (gpointer data);
97
G_DEFINE_TYPE (EggSMClientWin32, egg_sm_client_win32, EGG_TYPE_SM_CLIENT)
100
egg_sm_client_win32_init (EggSMClientWin32 *win32)
106
egg_sm_client_win32_class_init (EggSMClientWin32Class *klass)
108
EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
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;
116
egg_sm_client_win32_new (void)
118
return g_object_new (EGG_TYPE_SM_CLIENT_WIN32, NULL);
122
sm_client_win32_startup (EggSMClient *client,
123
const char *client_id)
125
EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
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);
134
sm_client_win32_will_quit (EggSMClient *client,
137
EggSMClientWin32 *win32 = (EggSMClientWin32 *)client;
139
win32->will_quit = will_quit;
140
SetEvent (win32->response_event);
144
sm_client_win32_end_session (EggSMClient *client,
145
EggSMClientEndStyle style,
146
gboolean request_confirmation)
148
UINT uFlags = EWX_LOGOFF;
152
case EGG_SM_CLIENT_END_SESSION_DEFAULT:
153
case EGG_SM_CLIENT_LOGOUT:
156
case EGG_SM_CLIENT_REBOOT:
159
case EGG_SM_CLIENT_SHUTDOWN:
160
uFlags = EWX_POWEROFF;
164
/* There's no way to make ExitWindowsEx() show a logout dialog, so
165
* we ignore @request_confirmation.
168
#ifdef SHTDN_REASON_FLAG_PLANNED
169
ExitWindowsEx (uFlags, SHTDN_REASON_FLAG_PLANNED);
171
ExitWindowsEx (uFlags, 0);
178
/* callbacks from logout-listener thread */
181
emit_quit_requested (gpointer smclient)
183
gdk_threads_enter ();
184
egg_sm_client_quit_requested (smclient);
185
gdk_threads_leave ();
191
emit_quit (gpointer smclient)
193
EggSMClientWin32 *win32 = smclient;
195
gdk_threads_enter ();
196
egg_sm_client_quit (smclient);
197
gdk_threads_leave ();
199
SetEvent (win32->response_event);
204
emit_quit_cancelled (gpointer smclient)
206
EggSMClientWin32 *win32 = smclient;
208
gdk_threads_enter ();
209
egg_sm_client_quit_cancelled (smclient);
210
gdk_threads_leave ();
212
SetEvent (win32->response_event);
217
got_message (gpointer smclient)
219
EggSMClientWin32 *win32 = smclient;
221
win32->event (win32);
225
/* Windows HANDLE GSource */
230
} GWin32HandleSource;
233
g_win32_handle_source_prepare (GSource *source, gint *timeout)
240
g_win32_handle_source_check (GSource *source)
242
GWin32HandleSource *hsource = (GWin32HandleSource *)source;
244
return hsource->pollfd.revents;
248
g_win32_handle_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
250
return (*callback) (user_data);
254
g_win32_handle_source_finalize (GSource *source)
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
267
g_win32_handle_source_add (HANDLE handle, GSourceFunc callback, gpointer user_data)
269
GWin32HandleSource *hsource;
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);
279
g_source_set_callback (source, callback, user_data, NULL);
280
g_source_attach (source, NULL);
284
/* logout-listener thread */
287
sm_client_win32_window_procedure (HWND hwnd,
292
EggSMClientWin32 *win32 =
293
(EggSMClientWin32 *)GetWindowLongPtr (hwnd, GWLP_USERDATA);
297
case WM_QUERYENDSESSION:
298
win32->event = emit_quit_requested;
299
SetEvent (win32->message_event);
301
WaitForSingleObject (win32->response_event, INFINITE);
302
return win32->will_quit;
307
/* The session is ending */
308
win32->event = emit_quit;
312
/* Nope, the session *isn't* ending */
313
win32->event = emit_quit_cancelled;
316
SetEvent (win32->message_event);
317
WaitForSingleObject (win32->response_event, INFINITE);
322
return DefWindowProc (hwnd, message, wParam, lParam);
327
sm_client_thread (gpointer smclient)
335
instance = GetModuleHandle (NULL);
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);
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);
351
while (GetMessage (&msg, NULL, 0, 0))
352
DispatchMessage (&msg);