Move new lxsession with built-in settings daemon to trunk.
[lxde/lxsession.git] / lxsession-logout / main.c
CommitLineData
dd69beb8
FC
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>
d174588a 37#include <locale.h>
dd69beb8
FC
38
39#include "gdm-logout-action.h"
40
41#ifdef HAVE_HAL
42#include <dbus/dbus.h>
43#endif
44
45typedef 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
54static gboolean use_hal = FALSE;
55static LogoutAction available_actions = GDM_LOGOUT_ACTION_NONE;
56
57static char* prompt = NULL;
58static char* side = NULL;
59static char* banner = NULL;
60
61static 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
70static 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
051008c5 83static GtkWidget* create_background()
dd69beb8
FC
84{
85 GtkWidget *back = NULL, *img;
86 GdkPixbuf *tmp, *shot;
87 GdkScreen *screen;
88
4db3ed5b
HJYP
89 guchar *pixels, *p;
90 int x, y, width, height, rowstride;
91 gboolean has_alpha;
92
dd69beb8
FC
93 screen = gdk_screen_get_default();
94
4db3ed5b 95 shot = gdk_pixbuf_get_from_drawable( NULL,
ea137bf5
MTW
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) );
dd69beb8 101
4db3ed5b
HJYP
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);
ea137bf5 108
4db3ed5b
HJYP
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 }
ea137bf5 124
dd69beb8
FC
125 back = gtk_window_new( GTK_WINDOW_TOPLEVEL );
126 gtk_widget_set_app_paintable( back, TRUE );
127 gtk_widget_set_double_buffered( back, FALSE );
ea137bf5 128 g_signal_connect( back, "expose-event", G_CALLBACK(on_expose), shot );
b16cbcd6 129 g_object_weak_ref( (GObject *) back, (GWeakNotify)g_object_unref, shot );
dd69beb8 130
b16cbcd6 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) );
dd69beb8
FC
135
136 return back;
137}
138
139static 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
145static GtkWidget* create_dlg_btn(const char* label, const char* icon, int response )
146{
147 GtkWidget* btn = gtk_button_new_with_mnemonic( label );
b16cbcd6 148 gtk_button_set_alignment( GTK_BUTTON(btn), 0.0, 0.5 );
dd69beb8
FC
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 );
b16cbcd6 153 gtk_button_set_image( GTK_BUTTON(btn), img );
dd69beb8
FC
154 }
155 return btn;
156}
157
158GtkPositionType 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 */
177static 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",
ea137bf5
MTW
202 "/org/freedesktop/Hal/devices/computer",
203 "org.freedesktop.Hal.Device.SystemPowerManagement",
204 "ThisMethodMustNotExistInHal");
dd69beb8
FC
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 */
244static gboolean
245xfsm_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
314static 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
333int main( int argc, char** argv )
334{
051008c5 335 GtkWidget *back = NULL, *dlg, *check, *btn, *label, *box = NULL, *vbox;
dd69beb8
FC
336 GtkPositionType banner_pos;
337 int res;
338 const char* p;
339 char* file;
340 GPid pid;
341 GOptionContext *context;
342 GError* err = NULL;
ea137bf5 343 gboolean composited;
dd69beb8
FC
344
345#ifdef ENABLE_NLS
65a445aa 346 setlocale(LC_ALL, "");
dd69beb8
FC
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
051008c5 372 back = create_background();
ea137bf5 373
051008c5
MTW
374 /* check if the window is composited */
375 composited = gtk_widget_is_composited( back );
dd69beb8
FC
376
377 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
cdee6793 378 PACKAGE_DATA_DIR "/lxsession/images" );
dd69beb8
FC
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 );
b16cbcd6 394 gtk_box_pack_start( GTK_BOX(vbox), box, TRUE, TRUE, 2 );
dd69beb8
FC
395
396 if( banner_pos == GTK_POS_LEFT )
397 {
b16cbcd6 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 );
dd69beb8
FC
400 }
401 else
402 {
b16cbcd6 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 );
dd69beb8
FC
405 }
406 vbox = gtk_vbox_new( FALSE, 2 );
b16cbcd6 407 gtk_box_pack_start( GTK_BOX(box), vbox, TRUE, TRUE, 2 );
408 gtk_misc_set_alignment( GTK_MISC(img), 0.5, 0.0 );
dd69beb8
FC
409 break;
410 case GTK_POS_TOP:
411 case GTK_POS_BOTTOM:
412 if( banner_pos == GTK_POS_TOP )
413 {
b16cbcd6 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 );
dd69beb8
FC
416 }
417 else
418 {
b16cbcd6 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 );
dd69beb8
FC
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
b16cbcd6 435 gtk_label_set_markup( GTK_LABEL(label), prompt );
436 gtk_box_pack_start( GTK_BOX(vbox), label, FALSE, FALSE, 4 );
dd69beb8 437
dd69beb8
FC
438 check_available_actions();
439
440 if( available_actions & LOGOUT_ACTION_SHUTDOWN )
441 {
f8d2bceb 442 btn = create_dlg_btn(_("Sh_utdown"), "system-shutdown", LOGOUT_ACTION_SHUTDOWN );
b16cbcd6 443 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
dd69beb8
FC
444 }
445 if( available_actions & LOGOUT_ACTION_REBOOT )
446 {
447 btn = create_dlg_btn(_("_Reboot"), "gnome-session-reboot", LOGOUT_ACTION_REBOOT );
b16cbcd6 448 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
dd69beb8
FC
449 }
450 if( available_actions & LOGOUT_ACTION_SUSPEND )
451 {
452 btn = create_dlg_btn(_("_Suspend"), "gnome-session-suspend", LOGOUT_ACTION_SUSPEND );
b16cbcd6 453 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
dd69beb8
FC
454 }
455 if( available_actions & LOGOUT_ACTION_HIBERNATE )
456 {
457 btn = create_dlg_btn(_("_Hibernate"), "gnome-session-hibernate", LOGOUT_ACTION_HIBERNATE );
b16cbcd6 458 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
dd69beb8
FC
459 }
460
47e59587 461 /* If GDM or KDM is running */
dd69beb8 462 if( g_file_test("/var/run/gdm_socket", G_FILE_TEST_EXISTS)
47e59587
HJYP
463 || g_file_test("/tmp/.gdm_socket", G_FILE_TEST_EXISTS)
464 || g_file_test("/var/run/kdm.pid", G_FILE_TEST_EXISTS) )
dd69beb8
FC
465 {
466 btn = create_dlg_btn(_("S_witch User"), "gnome-session-switch", LOGOUT_ACTION_SWITCH_USER );
b16cbcd6 467 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
dd69beb8
FC
468 }
469
f8d2bceb 470 btn = create_dlg_btn(_("_Logout"), "system-log-out", GTK_RESPONSE_OK );
b16cbcd6 471 gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, FALSE, 4 );
dd69beb8 472
dd69beb8 473 gtk_window_set_position( GTK_WINDOW(dlg), GTK_WIN_POS_CENTER_ALWAYS );
b16cbcd6 474 gtk_window_set_decorated( GTK_WINDOW(dlg), FALSE );
dd69beb8
FC
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 );
b16cbcd6 481// if( !composited ) gdk_x11_grab_server();
dd69beb8
FC
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 }
b16cbcd6 499// if( !composited ) gdk_x11_ungrab_server();
dd69beb8
FC
500 gdk_pointer_ungrab( GDK_CURRENT_TIME );
501 gdk_keyboard_ungrab( GDK_CURRENT_TIME );
502
dd69beb8
FC
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 {
47e59587
HJYP
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);
dd69beb8
FC
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}