8874d4d61e833b5b5192a7829fa81938cbd26ba5
[lxde/lxsession.git] / lxsession-logout / main.c
1 /*
2 * main.c -lxsession-logout for LXSession
3 *
4 * Copyright 2008 PCMan <pcman.tw@gmail.com>
5 * Copyright (c) 2003-2006 Benedikt Meurer <benny@xfce.org>
6 *
7 * HAL-related parts are taken from xfsm-shutdown-helper.c of
8 * xfce4-session originally written by Benedikt Meurer, with some
9 * modification to be used in this project.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 * MA 02110-1301, USA.
25 */
26
27 #include <config.h>
28
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31 #include <signal.h>
32 #include <gdk/gdk.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <locale.h>
38
39 #include "gdm-logout-action.h"
40
41 #ifdef HAVE_HAL
42 #include <dbus/dbus.h>
43 #endif
44
45 typedef enum {
46 LOGOUT_ACTION_NONE = GDM_LOGOUT_ACTION_NONE,
47 LOGOUT_ACTION_SHUTDOWN = GDM_LOGOUT_ACTION_SHUTDOWN,
48 LOGOUT_ACTION_REBOOT = GDM_LOGOUT_ACTION_REBOOT,
49 LOGOUT_ACTION_SUSPEND = GDM_LOGOUT_ACTION_SUSPEND,
50 LOGOUT_ACTION_HIBERNATE = GDM_LOGOUT_ACTION_SUSPEND << 1, /* HAL only */
51 LOGOUT_ACTION_SWITCH_USER = GDM_LOGOUT_ACTION_SUSPEND << 2 /* not supported */
52 }LogoutAction;
53
54 static gboolean use_hal = FALSE;
55 static LogoutAction available_actions = GDM_LOGOUT_ACTION_NONE;
56
57 static char* prompt = NULL;
58 static char* side = NULL;
59 static char* banner = NULL;
60
61 static GOptionEntry opt_entries[] =
62 {
63 { "prompt", 'p', 0, G_OPTION_ARG_STRING, &prompt, N_("Custom message to show on the dialog"), N_("message") },
64 { "banner", 'b', 0, G_OPTION_ARG_STRING, &banner, N_("Banner to show on the dialog"), N_("image file") },
65 { "side", 's', 0, G_OPTION_ARG_STRING, &side, N_("Position of the banner"), "top|left|right|botom" },
66 /* {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &files, NULL, N_("[FILE1, FILE2,...]")}, */
67 { NULL }
68 };
69
70 static gboolean on_expose( GtkWidget* w, GdkEventExpose* evt, GdkPixbuf* shot )
71 {
72 if( GTK_WIDGET_REALIZED(w) && GDK_IS_DRAWABLE(w->window) )
73 {
74 gdk_draw_pixbuf( w->window, w->style->black_gc, shot,
75 evt->area.x, evt->area.y,
76 evt->area.x, evt->area.y,
77 evt->area.width, evt->area.height,
78 GDK_RGB_DITHER_NORMAL, 0, 0 );
79 }
80 return TRUE;
81 }
82
83 static GtkWidget* create_background()
84 {
85 GtkWidget *back = NULL, *img;
86 GdkPixbuf *tmp, *shot;
87 GdkScreen *screen;
88
89 guchar *pixels, *p;
90 int x, y, width, height, rowstride;
91 gboolean has_alpha;
92
93 screen = gdk_screen_get_default();
94
95 shot = gdk_pixbuf_get_from_drawable( NULL,
96 gdk_get_default_root_window(),
97 NULL,
98 0, 0, 0, 0,
99 gdk_screen_get_width(screen),
100 gdk_screen_get_height(screen) );
101
102 /* make the background darker */
103 pixels = gdk_pixbuf_get_pixels(shot);
104 width = gdk_pixbuf_get_width(shot);
105 height = gdk_pixbuf_get_height(shot);
106 has_alpha = gdk_pixbuf_get_has_alpha(shot);
107 rowstride = gdk_pixbuf_get_rowstride(shot);
108
109 for (y = 0; y < height; y++)
110 {
111 p = pixels;
112 for (x = 0; x < width; x++)
113 {
114 p[0] = p[0] / 2;
115 p[1] = p[1] / 2;
116 p[2] = p[2] / 2;
117 if( has_alpha )
118 p += 4;
119 else
120 p += 3;
121 }
122 pixels += rowstride;
123 }
124
125 back = gtk_window_new( GTK_WINDOW_TOPLEVEL );
126 gtk_widget_set_app_paintable( back, TRUE );
127 gtk_widget_set_double_buffered( back, FALSE );
128 g_signal_connect( back, "expose-event", G_CALLBACK(on_expose), shot );
129 g_object_weak_ref( (GObject *) back, (GWeakNotify)g_object_unref, shot );
130
131 gtk_window_fullscreen( GTK_WINDOW(back) );
132 gtk_window_set_decorated( GTK_WINDOW(back), FALSE );
133 gtk_window_set_keep_above( GTK_WINDOW(back), TRUE );
134 gtk_widget_show_all( GTK_WIDGET(back) );
135
136 return back;
137 }
138
139 static void btn_clicked( GtkWidget* btn, gpointer id )
140 {
141 GtkWidget* dlg = gtk_widget_get_toplevel( btn );
142 gtk_dialog_response( GTK_DIALOG(dlg), GPOINTER_TO_INT(id) );
143 }
144
145 static GtkWidget* create_dlg_btn(const char* label, const char* icon, int response )
146 {
147 GtkWidget* btn = gtk_button_new_with_mnemonic( label );
148 gtk_button_set_alignment( GTK_BUTTON(btn), 0.0, 0.5 );
149 g_signal_connect( btn, "clicked", G_CALLBACK(btn_clicked), GINT_TO_POINTER(response) );
150 if( icon )
151 {
152 GtkWidget* img = gtk_image_new_from_icon_name( icon, GTK_ICON_SIZE_BUTTON );
153 gtk_button_set_image( GTK_BUTTON(btn), img );
154 }
155 return btn;
156 }
157
158 GtkPositionType get_banner_position()
159 {
160 if( side )
161 {
162 if( 0 == strcmp( side, "right" ) )
163 return GTK_POS_RIGHT;
164 if( 0 == strcmp( side, "top" ) )
165 return GTK_POS_TOP;
166 if( 0 == strcmp( side, "bottom" ) )
167 return GTK_POS_BOTTOM;
168 }
169 return GTK_POS_LEFT;
170 }
171
172 /*
173 * These functions with the prefix "xfsm_" are taken from
174 * xfsm-shutdown-helper.c of xfce4-session with some modification.
175 * Copyright (c) 2003-2006 Benedikt Meurer <benny@xfce.org>
176 */
177 static gboolean xfsm_shutdown_helper_hal_check ()
178 {
179 #ifdef HAVE_HAL
180 DBusConnection *connection;
181 DBusMessage *message;
182 DBusMessage *result;
183 DBusError error;
184
185 /* initialize the error */
186 dbus_error_init (&error);
187
188 /* connect to the system message bus */
189 connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
190 if (G_UNLIKELY (connection == NULL))
191 {
192 g_warning (G_STRLOC ": Failed to connect to the system message bus: %s", error.message);
193 dbus_error_free (&error);
194 return FALSE;
195 }
196
197 /* this is a simple trick to check whether we are allowed to
198 * use the org.freedesktop.Hal.Device.SystemPowerManagement
199 * interface without shutting down/rebooting now.
200 */
201 message = dbus_message_new_method_call ("org.freedesktop.Hal",
202 "/org/freedesktop/Hal/devices/computer",
203 "org.freedesktop.Hal.Device.SystemPowerManagement",
204 "ThisMethodMustNotExistInHal");
205 result = dbus_connection_send_with_reply_and_block (connection, message, 2000, &error);
206 dbus_message_unref (message);
207
208 /* translate error results appropriately */
209 if (result != NULL && dbus_set_error_from_message (&error, result))
210 {
211 /* release and reset the result */
212 dbus_message_unref (result);
213 result = NULL;
214 }
215 else if (G_UNLIKELY (result != NULL))
216 {
217 /* we received a valid message return?! HAL must be on crack! */
218 dbus_message_unref (result);
219 return FALSE;
220 }
221
222 /* if we receive org.freedesktop.DBus.Error.UnknownMethod, then
223 * we are allowed to shutdown/reboot the computer via HAL.
224 */
225 if (strcmp (error.name, "org.freedesktop.DBus.Error.UnknownMethod") == 0)
226 {
227 dbus_error_free (&error);
228 return TRUE;
229 }
230
231 /* otherwise, we failed for some reason */
232 g_warning (G_STRLOC ": Failed to contact HAL: %s", error.message);
233 dbus_error_free (&error);
234 #endif
235
236 return FALSE;
237 }
238
239 /*
240 * These functions with the prefix "xfsm_" are taken from
241 * xfsm-shutdown-helper.c of xfce4-session with some modification.
242 * Copyright (c) 2003-2006 Benedikt Meurer <benny@xfce.org>
243 */
244 static gboolean
245 xfsm_shutdown_helper_hal_send ( LogoutAction action )
246 {
247 #ifdef HAVE_HAL
248 DBusConnection *connection;
249 DBusMessage *message;
250 DBusMessage *result;
251 DBusError error;
252 const char* method;
253 dbus_int32_t suspend_arg = 0;
254
255 /* The spec:
256 http://people.freedesktop.org/~david/hal-spec/hal-spec.html#interface-device-systempower */
257 switch( action )
258 {
259 case LOGOUT_ACTION_SHUTDOWN:
260 method = "Shutdown";
261 break;
262 case LOGOUT_ACTION_REBOOT:
263 method = "Reboot";
264 break;
265 case LOGOUT_ACTION_SUSPEND:
266 method = "Suspend";
267 break;
268 case LOGOUT_ACTION_HIBERNATE:
269 method = "Hibernate";
270 break;
271 default:
272 return FALSE; /* It's impossible to reach here, or it's a bug. */
273 }
274
275 /* initialize the error */
276 dbus_error_init (&error);
277
278 /* connect to the system message bus */
279 connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
280 if (G_UNLIKELY (connection == NULL))
281 {
282 g_warning (G_STRLOC ": Failed to connect to the system message bus: %s", error.message);
283 dbus_error_free (&error);
284 return FALSE;
285 }
286
287 /* send the appropriate message to HAL, telling it to shutdown or reboot the system */
288 message = dbus_message_new_method_call ("org.freedesktop.Hal",
289 "/org/freedesktop/Hal/devices/computer",
290 "org.freedesktop.Hal.Device.SystemPowerManagement",
291 method );
292 if( action == LOGOUT_ACTION_SUSPEND )
293 dbus_message_append_args( message, DBUS_TYPE_INT32, &suspend_arg, DBUS_TYPE_INVALID );
294
295 result = dbus_connection_send_with_reply_and_block (connection, message, 2000, &error);
296 dbus_message_unref (message);
297
298 /* check if we received a result */
299 if (G_UNLIKELY (result == NULL))
300 {
301 g_warning (G_STRLOC ": Failed to contact HAL: %s", error.message);
302 dbus_error_free (&error);
303 return FALSE;
304 }
305
306 /* pretend that we succeed */
307 dbus_message_unref (result);
308 return TRUE;
309 #else
310 return FALSE;
311 #endif
312 }
313
314 static void check_available_actions()
315 {
316 /* check if we can use HAL to shutdown the computer */
317 use_hal = xfsm_shutdown_helper_hal_check ();
318 if( use_hal ) /* check if hal is available */
319 {
320 available_actions = LOGOUT_ACTION_SHUTDOWN | LOGOUT_ACTION_REBOOT | LOGOUT_ACTION_SUSPEND | LOGOUT_ACTION_HIBERNATE;
321 }
322 else /* check if gdm is available */
323 {
324 if( gdm_supports_logout_action(GDM_LOGOUT_ACTION_SHUTDOWN) )
325 available_actions |= GDM_LOGOUT_ACTION_SHUTDOWN;
326 if( gdm_supports_logout_action(GDM_LOGOUT_ACTION_REBOOT) )
327 available_actions |= GDM_LOGOUT_ACTION_REBOOT;
328 if( gdm_supports_logout_action(GDM_LOGOUT_ACTION_SUSPEND) )
329 available_actions |= GDM_LOGOUT_ACTION_SUSPEND;
330 }
331 }
332
333 int main( int argc, char** argv )
334 {
335 GtkWidget *back = NULL, *dlg, *check, *btn, *label, *box = NULL, *vbox;
336 GtkPositionType banner_pos;
337 int res;
338 const char* p;
339 char* file;
340 GPid pid;
341 GOptionContext *context;
342 GError* err = NULL;
343 gboolean composited;
344
345 #ifdef ENABLE_NLS
346 setlocale(LC_ALL, "");
347 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
348 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
349 textdomain ( GETTEXT_PACKAGE );
350 #endif
351
352 p = g_getenv("_LXSESSION_PID");
353 if( ! p || (pid = atoi( p)) == 0 )
354 {
355 g_print( _("Error: %s\n"), _("LXSession is not running." ));
356 return 1;
357 }
358
359 /* parse command line arguments */
360 context = g_option_context_new ("");
361 g_option_context_add_main_entries (context, opt_entries, GETTEXT_PACKAGE);
362 g_option_context_add_group (context, gtk_get_option_group(TRUE));
363 /* gtk_init( &argc, &argv ); is not needed if g_option_context_parse is called */
364 if( G_UNLIKELY( ! g_option_context_parse (context, &argc, &argv, &err) ) )
365 {
366 g_print( _("Error: %s\n"), err->message );
367 g_error_free( err );
368 return 1;
369 }
370 g_option_context_free( context );
371
372 back = create_background();
373
374 /* check if the window is composited */
375 composited = gtk_widget_is_composited( back );
376
377 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
378 PACKAGE_DATA_DIR "/lxsession/images" );
379
380 dlg = gtk_dialog_new_with_buttons( _("Logout"), (GtkWindow*)back, GTK_DIALOG_MODAL,
381 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL );
382 gtk_container_set_border_width( (GtkContainer*)dlg, 6 );
383 vbox = ((GtkDialog*)dlg)->vbox;
384
385 if( banner )
386 {
387 GtkWidget* img = gtk_image_new_from_file( banner );
388 banner_pos = get_banner_position();
389 switch( banner_pos )
390 {
391 case GTK_POS_LEFT:
392 case GTK_POS_RIGHT:
393 box = gtk_hbox_new( FALSE,2 );
394 gtk_box_pack_start( GTK_BOX(vbox), box, TRUE, TRUE, 2 );
395
396 if( banner_pos == GTK_POS_LEFT )
397 {
398 gtk_box_pack_start( GTK_BOX(box), img, FALSE, TRUE, 2 );
399 gtk_box_pack_start( GTK_BOX(box), gtk_vseparator_new(), FALSE, TRUE, 2 );
400 }
401 else
402 {
403 gtk_box_pack_end( GTK_BOX(box), img, FALSE, TRUE, 2 );
404 gtk_box_pack_end( GTK_BOX(box), gtk_vseparator_new(), FALSE, TRUE, 2 );
405 }
406 vbox = gtk_vbox_new( FALSE, 2 );
407 gtk_box_pack_start( GTK_BOX(box), vbox, TRUE, TRUE, 2 );
408 gtk_misc_set_alignment( GTK_MISC(img), 0.5, 0.0 );
409 break;
410 case GTK_POS_TOP:
411 case GTK_POS_BOTTOM:
412 if( banner_pos == GTK_POS_TOP )
413 {
414 gtk_box_pack_start( GTK_BOX(vbox), img, FALSE, TRUE, 2 );
415 gtk_box_pack_start( GTK_BOX(vbox), gtk_hseparator_new(), FALSE, TRUE, 2 );
416 }
417 else
418 {
419 gtk_box_pack_end( GTK_BOX(vbox), img, FALSE, TRUE, 2 );
420 gtk_box_pack_end( GTK_BOX(vbox), gtk_hseparator_new(), FALSE, TRUE, 2 );
421 }
422 break;
423 }
424 }
425
426 label = gtk_label_new("");
427 if( ! prompt ) {
428 const char* session_name = g_getenv("DESKTOP_SESSION");
429 if( ! session_name )
430 session_name = "LXDE";
431 /* %s is the name of the desktop session */
432 prompt = g_strdup_printf( _("<b><big>Logout %s session?</big></b>"), session_name );
433 }
434
435 gtk_label_set_markup( GTK_LABEL(label), prompt );
436 gtk_box_pack_start( GTK_BOX(vbox), label, FALSE, FALSE, 4 );
437
438 check_available_actions();
439
440 if( available_actions & LOGOUT_ACTION_SHUTDOWN )
441 {
442 btn = create_dlg_btn(_("Sh_utdown"), "gnome-session-halt", LOGOUT_ACTION_SHUTDOWN );
443 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
444 }
445 if( available_actions & LOGOUT_ACTION_REBOOT )
446 {
447 btn = create_dlg_btn(_("_Reboot"), "gnome-session-reboot", LOGOUT_ACTION_REBOOT );
448 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
449 }
450 if( available_actions & LOGOUT_ACTION_SUSPEND )
451 {
452 btn = create_dlg_btn(_("_Suspend"), "gnome-session-suspend", LOGOUT_ACTION_SUSPEND );
453 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
454 }
455 if( available_actions & LOGOUT_ACTION_HIBERNATE )
456 {
457 btn = create_dlg_btn(_("_Hibernate"), "gnome-session-hibernate", LOGOUT_ACTION_HIBERNATE );
458 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
459 }
460
461 /* If GDM or KDM is running */
462 if( g_file_test("/var/run/gdm_socket", G_FILE_TEST_EXISTS)
463 || g_file_test("/tmp/.gdm_socket", G_FILE_TEST_EXISTS)
464 || g_file_test("/var/run/kdm.pid", G_FILE_TEST_EXISTS) )
465 {
466 btn = create_dlg_btn(_("S_witch User"), "gnome-session-switch", LOGOUT_ACTION_SWITCH_USER );
467 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
468 }
469
470 btn = create_dlg_btn(_("_Logout"), "gnome-session-logout", GTK_RESPONSE_OK );
471 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
472
473 gtk_window_set_position( GTK_WINDOW(dlg), GTK_WIN_POS_CENTER_ALWAYS );
474 gtk_window_set_decorated( GTK_WINDOW(dlg), FALSE );
475 gtk_widget_show_all( dlg );
476
477 gtk_window_set_keep_above( (GtkWindow*)dlg, TRUE );
478
479 gdk_pointer_grab( dlg->window, TRUE, 0, NULL, NULL, GDK_CURRENT_TIME );
480 gdk_keyboard_grab( dlg->window, TRUE, GDK_CURRENT_TIME );
481 // if( !composited ) gdk_x11_grab_server();
482
483 switch( (res = gtk_dialog_run( (GtkDialog*)dlg )) )
484 {
485 case LOGOUT_ACTION_SHUTDOWN:
486 case LOGOUT_ACTION_REBOOT:
487 case LOGOUT_ACTION_SUSPEND:
488 case LOGOUT_ACTION_HIBERNATE:
489 case LOGOUT_ACTION_SWITCH_USER:
490 case GTK_RESPONSE_OK:
491 break;
492 default:
493 gtk_widget_destroy( dlg );
494 gtk_widget_destroy( back );
495 gdk_pointer_ungrab( GDK_CURRENT_TIME );
496 gdk_keyboard_ungrab( GDK_CURRENT_TIME );
497 return 0;
498 }
499 // if( !composited ) gdk_x11_ungrab_server();
500 gdk_pointer_ungrab( GDK_CURRENT_TIME );
501 gdk_keyboard_ungrab( GDK_CURRENT_TIME );
502
503 gtk_widget_destroy( dlg );
504 gtk_widget_destroy( back );
505
506 if( res != GTK_RESPONSE_OK )
507 {
508 if( res == LOGOUT_ACTION_SWITCH_USER )
509 {
510 if( g_file_test("/var/run/gdm_socket", G_FILE_TEST_EXISTS) || g_file_test("/tmp/.gdm_socket", G_FILE_TEST_EXISTS) )
511 g_spawn_command_line_sync ("gdmflexiserver --startnew", NULL, NULL, NULL, NULL);
512 else if ( g_file_test("/var/run/kdm.pid", G_FILE_TEST_EXISTS) )
513 g_spawn_command_line_sync ("kdmctl reserve", NULL, NULL, NULL, NULL);
514 return 0;
515 }
516
517 if( use_hal )
518 xfsm_shutdown_helper_hal_send( res );
519 else
520 gdm_set_logout_action( res );
521
522 if( res != LOGOUT_ACTION_SUSPEND && res != LOGOUT_ACTION_HIBERNATE )
523 kill( pid, SIGTERM ); /* ask the session manager to do fast logout */
524 }
525 else
526 {
527 kill( pid, SIGUSR1 ); /* ask the session manager to slow log out */
528 }
529
530 return 0;
531 }