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
);