1 /**
2 * Copyright (c) 2010 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program 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
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <config.h>
20 #include <locale.h>
21 #include <stdlib.h>
22 #include <gdk/gdkkeysyms.h>
23 #include <gtk/gtk.h>
24 #include <gdk/gdk.h>
25 #include <gdk/gdkx.h>
26 #include <glib/gi18n.h>
27 #include <sys/file.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #include <fcntl.h>
32 #include <limits.h>
33 #include <signal.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 #include <X11/Xatom.h>
39 #include <X11/Xlib.h>
40
41 #include "lxsession-logout-dbus-interface.h"
42
43 /* Command parameters. */
44 static char * prompt = NULL;
45 static char * banner_side = NULL;
46 static char * banner_path = NULL;
47
48 static GOptionEntry opt_entries[] =
49 {
50 { "prompt", 'p', 0, G_OPTION_ARG_STRING, &prompt, N_("Custom message to show on the dialog"), N_("message") },
51 { "banner", 'b', 0, G_OPTION_ARG_STRING, &banner_path, N_("Banner to show on the dialog"), N_("image file") },
52 { "side", 's', 0, G_OPTION_ARG_STRING, &banner_side, N_("Position of the banner"), "top|left|right|bottom" },
53 { NULL }
54 };
55
56 typedef struct {
57 GPid lxsession_pid; /* Process ID of lxsession */
58 GtkWidget * error_label; /* Text of an error, if we get one */
59
60 int shutdown_available : 1; /* Shutdown is available */
61 int reboot_available : 1; /* Reboot is available */
62 int suspend_available : 1; /* Suspend is available */
63 int hibernate_available : 1; /* Hibernate is available */
64 int switch_user_available : 1; /* Switch User is available */
65
66 int shutdown_systemd : 1; /* Shutdown is available via systemd */
67 int reboot_systemd : 1; /* Reboot is available via systemd */
68 int suspend_systemd : 1; /* Suspend is available via systemd */
69 int hibernate_systemd : 1; /* Hibernate is available via systemd */
70 int shutdown_ConsoleKit : 1; /* Shutdown is available via ConsoleKit */
71 int reboot_ConsoleKit : 1; /* Reboot is available via ConsoleKit */
72 int suspend_ConsoleKit : 1; /* Suspend is available via ConsoleKit */
73 int hibernate_ConsoleKit : 1; /* Hibernate is available via ConsoleKit */
74 int suspend_UPower : 1; /* Suspend is available via UPower */
75 int hibernate_UPower : 1; /* Hibernate is available via UPower */
76 int switch_user_GDM : 1; /* Switch User is available via GDM */
77 int switch_user_LIGHTDM : 1; /* Switch User is available via GDM */
78 int switch_user_KDM : 1; /* Switch User is available via LIGHTDM */
79 int switch_user_LXDM : 1; /* Switch User is available via LXDM */
80 int ltsp : 1; /* Shutdown and reboot is accomplished via LTSP */
81
82 int lock_screen : 1; /* Lock screen available */
83
84 } HandlerContext;
85
86 static gboolean lock_screen(void);
87 static const gchar* determine_lock_screen(void);
88 static gboolean verify_running(const char * display_manager, const char * executable);
89 static void logout_clicked(GtkButton * button, HandlerContext * handler_context);
90 static void change_root_property(GtkWidget* w, const char* prop_name, const char* value);
91 static void shutdown_clicked(GtkButton * button, HandlerContext * handler_context);
92 static void reboot_clicked(GtkButton * button, HandlerContext * handler_context);
93 static void suspend_clicked(GtkButton * button, HandlerContext * handler_context);
94 static void hibernate_clicked(GtkButton * button, HandlerContext * handler_context);
95 static void switch_user_clicked(GtkButton * button, HandlerContext * handler_context);
96 static void cancel_clicked(GtkButton * button, gpointer user_data);
97 static GtkPositionType get_banner_position(void);
98 static GdkPixbuf * get_background_pixbuf(void);
99 #ifdef USE_GTK3
100 gboolean draw(GtkWidget * widget, cairo_t * cr, GdkPixbuf * pixbuf);
101 #else
102 gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, GdkPixbuf * pixbuf);
103 #endif
104
105 /* Try to run lxlock command in order to lock the screen, return TRUE on
106 * success, FALSE if command execution failed
107 */
lock_screen(void)108 static gboolean lock_screen(void)
109 {
110 const gchar* program = determine_lock_screen();
111
112 if (program)
113 {
114 g_spawn_command_line_async(program, NULL);
115 return TRUE;
116 }
117 return FALSE;
118 }
119
determine_lock_screen(void)120 static const gchar* determine_lock_screen(void)
121 {
122 const gchar* program = NULL;
123
124 if (g_find_program_in_path("lxlock"))
125 {
126 program = "lxlock";
127 }
128 else if (g_find_program_in_path("xdg-screensaver"))
129 {
130 program = "xdg-screensaver lock";
131 }
132 return program;
133 }
134
135
136 /* Verify that a program is running and that an executable is available. */
verify_running(const char * display_manager,const char * executable)137 static gboolean verify_running(const char * display_manager, const char * executable)
138 {
139 /* See if the executable we need to run is in the path. */
140 gchar * full_path = g_find_program_in_path(executable);
141 if (full_path != NULL)
142 {
143 g_free(full_path);
144
145 /* Form the filespec of the pid file for the display manager. */
146 char buffer[PATH_MAX];
147 sprintf(buffer, "/var/run/%s.pid", display_manager);
148
149 if (!g_file_test (buffer, G_FILE_TEST_IS_REGULAR))
150 sprintf(buffer, "/var/run/%s/%s.pid", display_manager, display_manager);
151
152 /* Open the pid file. */
153 int fd = open(buffer, O_RDONLY);
154 if (fd >= 0)
155 {
156 /* Pid file exists. Read it. */
157 ssize_t length = read(fd, buffer, sizeof(buffer));
158 close(fd);
159 if (length > 0)
160 {
161 /* Null terminate the buffer and convert the pid. */
162 buffer[length] = '\0';
163 pid_t pid = atoi(buffer);
164 if (pid > 0)
165 {
166 /* Form the filespec of the command line file under /proc.
167 * This is Linux specific. Should be conditionalized to the appropriate /proc layout for
168 * other systems. Your humble developer has no way to test on other systems. */
169 sprintf(buffer, "/proc/%d/cmdline", pid);
170
171 /* Open the file. */
172 int fd = open(buffer, O_RDONLY);
173 if (fd >= 0)
174 {
175 /* Read the command line. */
176 ssize_t length = read(fd, buffer, sizeof(buffer));
177 close(fd);
178 if (length > 0)
179 {
180 /* Null terminate the buffer and look for the display manager name in the command.
181 * If found, return success. */
182 buffer[length] = '\0';
183 if (strstr(buffer, display_manager) != NULL)
184 return TRUE;
185 }
186 }
187 }
188 }
189 }
190 }
191 return FALSE;
192 }
193
194 /* Handler for "clicked" signal on Logout button. */
logout_clicked(GtkButton * button,HandlerContext * handler_context)195 static void logout_clicked(GtkButton * button, HandlerContext * handler_context)
196 {
197 if (handler_context->lxsession_pid != 0)
198 {
199 kill(handler_context->lxsession_pid, SIGTERM);
200 }
201 else
202 {
203 /* Assume we are under openbox */
204 g_spawn_command_line_async("openbox --exit", NULL);
205 }
206 gtk_main_quit();
207 }
208
209 /* Replace a property on the root window. */
change_root_property(GtkWidget * w,const char * prop_name,const char * value)210 static void change_root_property(GtkWidget* w, const char* prop_name, const char* value)
211 {
212 GdkDisplay* dpy = gtk_widget_get_display(w);
213 GdkWindow* root = gtk_widget_get_root_window(w);
214 XChangeProperty(GDK_DISPLAY_XDISPLAY(dpy), GDK_WINDOW_XID(root),
215 XInternAtom(GDK_DISPLAY_XDISPLAY(dpy), prop_name, False), XA_STRING, 8,
216 PropModeReplace, (unsigned char*) value, strlen(value) + 1);
217 }
218
219 /* Handler for "clicked" signal on Shutdown button. */
shutdown_clicked(GtkButton * button,HandlerContext * handler_context)220 static void shutdown_clicked(GtkButton * button, HandlerContext * handler_context)
221 {
222 GError *err = NULL;
223 gtk_label_set_text(GTK_LABEL(handler_context->error_label), NULL);
224
225 if (handler_context->ltsp)
226 {
227 change_root_property(GTK_WIDGET(button), "LTSP_LOGOUT_ACTION", "HALT");
228 if (handler_context->lxsession_pid != 0)
229 {
230 kill(handler_context->lxsession_pid, SIGTERM);
231 }
232 }
233 else if (handler_context->shutdown_ConsoleKit)
234 dbus_ConsoleKit_PowerOff(&err);
235 else if (handler_context->shutdown_systemd)
236 dbus_systemd_PowerOff(&err);
237
238 if (err)
239 {
240 gtk_label_set_text(GTK_LABEL(handler_context->error_label), err->message);
241 g_error_free (err);
242 }
243 else
244 {
245 gtk_main_quit();
246 }
247 }
248
249 /* Handler for "clicked" signal on Reboot button. */
reboot_clicked(GtkButton * button,HandlerContext * handler_context)250 static void reboot_clicked(GtkButton * button, HandlerContext * handler_context)
251 {
252 GError *err = NULL;
253 gtk_label_set_text(GTK_LABEL(handler_context->error_label), NULL);
254
255 if (handler_context->ltsp)
256 {
257 change_root_property(GTK_WIDGET(button), "LTSP_LOGOUT_ACTION", "REBOOT");
258 if (handler_context->lxsession_pid != 0)
259 {
260 kill(handler_context->lxsession_pid, SIGTERM);
261 }
262 }
263 else if (handler_context->reboot_ConsoleKit)
264 dbus_ConsoleKit_Reboot(&err);
265 else if (handler_context->reboot_systemd)
266 dbus_systemd_Reboot(&err);
267
268 if (err)
269 {
270 gtk_label_set_text(GTK_LABEL(handler_context->error_label), err->message);
271 g_error_free (err);
272 }
273 else
274 {
275 gtk_main_quit();
276 }
277 }
278
279 /* Handler for "clicked" signal on Suspend button. */
suspend_clicked(GtkButton * button,HandlerContext * handler_context)280 static void suspend_clicked(GtkButton * button, HandlerContext * handler_context)
281 {
282 GError *err = NULL;
283 gtk_label_set_text(GTK_LABEL(handler_context->error_label), NULL);
284
285 lock_screen();
286 if (handler_context->suspend_UPower)
287 dbus_UPower_Suspend(&err);
288 else if (handler_context->suspend_ConsoleKit)
289 dbus_ConsoleKit_Suspend(&err);
290 else if (handler_context->suspend_systemd)
291 dbus_systemd_Suspend(&err);
292
293 if (err)
294 {
295 gtk_label_set_text(GTK_LABEL(handler_context->error_label), err->message);
296 g_error_free (err);
297 }
298 else
299 {
300 gtk_main_quit();
301 }
302 }
303
304 /* Handler for "clicked" signal on Hibernate button. */
hibernate_clicked(GtkButton * button,HandlerContext * handler_context)305 static void hibernate_clicked(GtkButton * button, HandlerContext * handler_context)
306 {
307 GError *err = NULL;
308 gtk_label_set_text(GTK_LABEL(handler_context->error_label), NULL);
309
310 lock_screen();
311 if (handler_context->hibernate_UPower)
312 dbus_UPower_Hibernate(&err);
313 else if (handler_context->hibernate_ConsoleKit)
314 dbus_ConsoleKit_Hibernate(&err);
315 else if (handler_context->hibernate_systemd)
316 dbus_systemd_Hibernate(&err);
317
318 if (err)
319 {
320 gtk_label_set_text(GTK_LABEL(handler_context->error_label), err->message);
321 g_error_free (err);
322 }
323 else
324 {
325 gtk_main_quit();
326 }
327 }
328
329 /* Handler for "clicked" signal on Switch User button. */
switch_user_clicked(GtkButton * button,HandlerContext * handler_context)330 static void switch_user_clicked(GtkButton * button, HandlerContext * handler_context)
331 {
332 GError *err = NULL;
333 gtk_label_set_text(GTK_LABEL(handler_context->error_label), NULL);
334
335 lock_screen();
336 if (handler_context->switch_user_GDM)
337 g_spawn_command_line_sync("gdmflexiserver --startnew", NULL, NULL, NULL, NULL);
338 else if (handler_context->switch_user_KDM)
339 g_spawn_command_line_sync("kdmctl reserve", NULL, NULL, NULL, NULL);
340 else if (handler_context->switch_user_LIGHTDM)
341 dbus_Lightdm_SwitchToGreeter(&err);
342 else if(handler_context->switch_user_LXDM)
343 g_spawn_command_line_sync("lxdm-binary -c USER_SWITCH", NULL, NULL, NULL, NULL);
344
345 if (err)
346 {
347 gtk_label_set_text(GTK_LABEL(handler_context->error_label), err->message);
348 g_error_free (err);
349 }
350 else
351 {
352 gtk_main_quit();
353 }
354 }
355
356 /* Handler for "clicked" signal on Lock button. */
lock_screen_clicked(GtkButton * button,HandlerContext * handler_context)357 static void lock_screen_clicked(GtkButton * button, HandlerContext * handler_context)
358 {
359 gtk_label_set_text(GTK_LABEL(handler_context->error_label), NULL);
360
361 lock_screen();
362 gtk_main_quit();
363 }
364
365 /* Handler for "clicked" signal on Cancel button. */
cancel_clicked(GtkButton * button,gpointer user_data)366 static void cancel_clicked(GtkButton * button, gpointer user_data)
367 {
368 gtk_main_quit();
369 }
370
371 /* Convert the --side parameter to a GtkPositionType. */
get_banner_position(void)372 static GtkPositionType get_banner_position(void)
373 {
374 if (banner_side != NULL)
375 {
376 if (strcmp(banner_side, "right") == 0)
377 return GTK_POS_RIGHT;
378 if (strcmp(banner_side, "top") == 0)
379 return GTK_POS_TOP;
380 if (strcmp(banner_side, "bottom") == 0)
381 return GTK_POS_BOTTOM;
382 }
383 return GTK_POS_LEFT;
384 }
385
386 /* Get the background pixbuf. */
get_background_pixbuf(void)387 static GdkPixbuf * get_background_pixbuf(void)
388 {
389 /* Get the root window pixmap. */
390 GdkScreen * screen = gdk_screen_get_default();
391 #ifdef USE_GTK3
392 GdkPixbuf * pixbuf = gdk_pixbuf_get_from_window(
393 gdk_get_default_root_window(),
394 0,
395 0,
396 gdk_screen_get_width(screen), /* Width */
397 gdk_screen_get_height(screen)); /* Height */
398 #else
399 GdkPixbuf * pixbuf = gdk_pixbuf_get_from_drawable(
400 NULL, /* Allocate a new pixbuf */
401 gdk_get_default_root_window(), /* The drawable */
402 NULL, /* Its colormap */
403 0, 0, 0, 0, /* Coordinates */
404 gdk_screen_get_width(screen), /* Width */
405 gdk_screen_get_height(screen)); /* Height */
406 #endif
407
408 /* Make the background darker. */
409 if (pixbuf != NULL)
410 {
411 unsigned char * pixels = gdk_pixbuf_get_pixels(pixbuf);
412 int width = gdk_pixbuf_get_width(pixbuf);
413 int height = gdk_pixbuf_get_height(pixbuf);
414 int pixel_stride = ((gdk_pixbuf_get_has_alpha(pixbuf)) ? 4 : 3);
415 int row_stride = gdk_pixbuf_get_rowstride(pixbuf);
416 int y;
417 for (y = 0; y < height; y += 1)
418 {
419 unsigned char * p = pixels;
420 int x;
421 for (x = 0; x < width; x += 1)
422 {
423 p[0] = p[0] / 2;
424 p[1] = p[1] / 2;
425 p[2] = p[2] / 2;
426 p += pixel_stride;
427 }
428 pixels += row_stride;
429 }
430 }
431 return pixbuf;
432 }
433
434 /* Handler for "expose_event" on background. */
435 #ifdef USE_GTK3
draw(GtkWidget * widget,cairo_t * cr,GdkPixbuf * pixbuf)436 gboolean draw(GtkWidget * widget, cairo_t * cr, GdkPixbuf * pixbuf)
437 #else
438 gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, GdkPixbuf * pixbuf)
439 #endif
440 {
441 gint x, y;
442
443 if (pixbuf != NULL)
444 {
445 /* Copy the appropriate rectangle of the root window pixmap to the drawing area.
446 * All drawing areas are immediate children of the toplevel window, so the allocation yields the source coordinates directly. */
447 #ifdef USE_GTK3
448 #elif GTK_CHECK_VERSION(2,14,0)
449 cairo_t * cr = gdk_cairo_create (gtk_widget_get_window(widget));
450 gdk_window_get_origin(gtk_widget_get_window(widget), &x, &y);
451 #else
452 cairo_t * cr = gdk_cairo_create (widget->window);
453 gdk_window_get_origin(widget->window, &x, &y);
454 #endif
455 gdk_cairo_set_source_pixbuf (
456 cr,
457 pixbuf,
458 -x,
459 -y);
460
461 cairo_paint (cr);
462 #ifndef USE_GTK3
463 cairo_destroy(cr);
464 #endif
465 }
466 return FALSE;
467 }
468
469 static char lockfile[PATH_MAX];
470
471 /* Unlink lockfile on exit. */
main_at_exit(void)472 static void main_at_exit(void)
473 {
474 unlink(lockfile);
475 }
476
477 /* Main program. */
main(int argc,char * argv[])478 int main(int argc, char * argv[])
479 {
480 #ifdef ENABLE_NLS
481 setlocale(LC_ALL, "");
482 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
483 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
484 textdomain (GETTEXT_PACKAGE);
485 #endif
486
487 HandlerContext handler_context;
488 memset(&handler_context, 0, sizeof(handler_context));
489
490 /* Get the lxsession PID. */
491 const char * p = g_getenv("_LXSESSION_PID");
492 if (p != NULL) handler_context.lxsession_pid = atoi(p);
493
494 /* Create lock file to prevent more than one logout dialog per lxsession process. */
495 sprintf(lockfile, "/tmp/.lxsession-logout-%d.lock", handler_context.lxsession_pid);
496 int fd = open(lockfile, O_RDONLY|O_CREAT, 00600);
497 if (fd >= 0)
498 {
499 if (flock(fd, LOCK_EX | LOCK_NB))
500 {
501 exit(EXIT_FAILURE);
502 }
503 }
504 atexit(main_at_exit);
505
506 /* Query DBus before GTK+ initialization!!! Otherwise a race may occur. */
507
508 /* Initialize capabilities of the systemd mechanism. */
509 if (dbus_systemd_CanPowerOff())
510 {
511 handler_context.shutdown_available = TRUE;
512 handler_context.shutdown_systemd = TRUE;
513 }
514 if (dbus_systemd_CanReboot())
515 {
516 handler_context.reboot_available = TRUE;
517 handler_context.reboot_systemd = TRUE;
518 }
519 if (dbus_systemd_CanSuspend())
520 {
521 handler_context.suspend_available = TRUE;
522 handler_context.suspend_systemd = TRUE;
523 }
524 if (dbus_systemd_CanHibernate())
525 {
526 handler_context.hibernate_available = TRUE;
527 handler_context.hibernate_systemd = TRUE;
528 }
529
530 /* Initialize capabilities of the ConsoleKit mechanism. */
531 if (!handler_context.shutdown_available && dbus_ConsoleKit_CanPowerOff())
532 {
533 handler_context.shutdown_available = TRUE;
534 handler_context.shutdown_ConsoleKit = TRUE;
535 }
536 if (!handler_context.reboot_available && dbus_ConsoleKit_CanReboot())
537 {
538 handler_context.reboot_available = TRUE;
539 handler_context.reboot_ConsoleKit = TRUE;
540 }
541 if (!handler_context.suspend_available && dbus_ConsoleKit_CanSuspend())
542 {
543 handler_context.suspend_available = TRUE;
544 handler_context.suspend_ConsoleKit = TRUE;
545 }
546 if (!handler_context.hibernate_available && dbus_ConsoleKit_CanHibernate())
547 {
548 handler_context.hibernate_available = TRUE;
549 handler_context.hibernate_ConsoleKit = TRUE;
550 }
551
552 /* Initialize capabilities of the UPower mechanism. */
553 if (!handler_context.suspend_available && dbus_UPower_CanSuspend())
554 {
555 handler_context.suspend_available = TRUE;
556 handler_context.suspend_UPower = TRUE;
557 }
558 if (!handler_context.hibernate_available && dbus_UPower_CanHibernate())
559 {
560 handler_context.hibernate_available = TRUE;
561 handler_context.hibernate_UPower = TRUE;
562 }
563
564 /* If we are under GDM, its "Switch User" is available. */
565 if (verify_running("gdm", "gdmflexiserver"))
566 {
567 handler_context.switch_user_available = TRUE;
568 handler_context.switch_user_GDM = TRUE;
569 }
570
571 /* If we are under GDM3, its "Switch User" is available. */
572 if (verify_running("gdm3", "gdmflexiserver"))
573 {
574 handler_context.switch_user_available = TRUE;
575 handler_context.switch_user_GDM = TRUE;
576 }
577
578 /* lightdm can be found by the env */
579 if (g_getenv("XDG_SEAT_PATH"))
580 {
581 handler_context.switch_user_available = TRUE;
582 handler_context.switch_user_LIGHTDM = TRUE;
583 }
584
585 /* If we are under KDM, its "Switch User" is available. */
586 if (verify_running("kdm", "kdmctl"))
587 {
588 handler_context.switch_user_available = TRUE;
589 handler_context.switch_user_KDM = TRUE;
590 }
591
592 if (verify_running("lxdm", "lxdm-binary"))
593 {
594 handler_context.switch_user_available = TRUE;
595 handler_context.switch_user_LXDM = TRUE;
596 }
597
598 /* LTSP support */
599 if (g_getenv("LTSP_CLIENT"))
600 {
601 handler_context.ltsp = TRUE;
602 handler_context.shutdown_available = TRUE;
603 handler_context.reboot_available = TRUE;
604 }
605
606 /* Lock screen */
607 const gchar* very_lock_screen = determine_lock_screen();
608 if (very_lock_screen)
609 {
610 handler_context.lock_screen = TRUE;
611 }
612
613 /* Initialize GTK (via g_option_context_parse) and parse command line arguments. */
614 GOptionContext * context = g_option_context_new("");
615 g_option_context_add_main_entries(context, opt_entries, GETTEXT_PACKAGE);
616 g_option_context_add_group(context, gtk_get_option_group(TRUE));
617 GError * err = NULL;
618 if ( ! g_option_context_parse(context, &argc, &argv, &err))
619 {
620 g_print(_("Error: %s\n"), err->message);
621 g_error_free(err);
622 return 1;
623 }
624 g_option_context_free(context);
625
626 /* Make the button images accessible. */
627 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/lxsession/images");
628
629 /* Get the background pixbuf. */
630 GdkPixbuf * pixbuf = get_background_pixbuf();
631
632 /* Create the toplevel window. */
633 GtkWidget * window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
634 gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
635 gtk_window_fullscreen(GTK_WINDOW(window));
636 gtk_widget_set_app_paintable(window, TRUE);
637 #ifdef USE_GTK3
638 g_signal_connect(G_OBJECT(window), "draw", G_CALLBACK(draw), pixbuf);
639 #else
640 g_signal_connect(G_OBJECT(window), "expose_event", G_CALLBACK(expose_event), pixbuf);
641 #endif
642 g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
643
644 /* Toplevel container */
645 GtkWidget* alignment = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
646 gtk_container_add(GTK_CONTAINER(window), alignment);
647
648 GtkWidget* center_area = gtk_event_box_new();
649 gtk_container_add(GTK_CONTAINER(alignment), center_area);
650
651 #ifdef USE_GTK3
652 gtk_style_context_add_class (gtk_widget_get_style_context (center_area), GTK_STYLE_CLASS_BACKGROUND);
653 #endif
654
655 GtkWidget* center_vbox = gtk_vbox_new(FALSE, 6);
656 gtk_container_set_border_width(GTK_CONTAINER(center_vbox), 12);
657 gtk_container_add(GTK_CONTAINER(center_area), center_vbox);
658
659 GtkWidget* controls = gtk_vbox_new(FALSE, 6);
660
661 /* If specified, apply a user-specified banner image. */
662 if (banner_path != NULL)
663 {
664 GtkWidget * banner_image = gtk_image_new_from_file(banner_path);
665 GtkPositionType banner_position = get_banner_position();
666
667 switch (banner_position)
668 {
669 case GTK_POS_LEFT:
670 case GTK_POS_RIGHT:
671 {
672 /* Create a horizontal box to contain the image and the controls. */
673 GtkWidget * box = gtk_hbox_new(FALSE, 2);
674 gtk_box_pack_start(GTK_BOX(center_vbox), box, FALSE, FALSE, 0);
675
676 /* Pack the image and a separator. */
677 gtk_misc_set_alignment(GTK_MISC(banner_image), 0.5, 0.0);
678 if (banner_position == GTK_POS_LEFT)
679 {
680 gtk_box_pack_start(GTK_BOX(box), banner_image, FALSE, FALSE, 2);
681 gtk_box_pack_start(GTK_BOX(box), gtk_vseparator_new(), FALSE, FALSE, 2);
682 gtk_box_pack_start(GTK_BOX(box), controls, FALSE, FALSE, 2);
683 }
684 else
685 {
686 gtk_box_pack_start(GTK_BOX(box), controls, FALSE, FALSE, 2);
687 gtk_box_pack_end(GTK_BOX(box), gtk_vseparator_new(), FALSE, FALSE, 2);
688 gtk_box_pack_end(GTK_BOX(box), banner_image, FALSE, FALSE, 2);
689 }
690 }
691 break;
692
693 case GTK_POS_TOP:
694 gtk_box_pack_start(GTK_BOX(controls), banner_image, FALSE, FALSE, 2);
695 gtk_box_pack_start(GTK_BOX(controls), gtk_hseparator_new(), FALSE, FALSE, 2);
696 gtk_box_pack_start(GTK_BOX(center_vbox), controls, FALSE, FALSE, 0);
697 break;
698
699 case GTK_POS_BOTTOM:
700 gtk_box_pack_end(GTK_BOX(controls), banner_image, FALSE, FALSE, 2);
701 gtk_box_pack_end(GTK_BOX(controls), gtk_hseparator_new(), FALSE, FALSE, 2);
702 gtk_box_pack_start(GTK_BOX(center_vbox), controls, FALSE, FALSE, 0);
703 break;
704 }
705 }
706 else
707 gtk_box_pack_start(GTK_BOX(center_vbox), controls, FALSE, FALSE, 0);
708
709 /* Create the label. */
710 GtkWidget * label = gtk_label_new("");
711 if (prompt == NULL)
712 {
713 const char * session_name = g_getenv("DESKTOP_SESSION");
714 if (session_name == NULL)
715 session_name = "LXDE";
716
717 gchar *output = NULL;
718
719 if (g_find_program_in_path("lsb_release"))
720 {
721 const gchar *command_line = "lsb_release -r -s";
722 GError *error;
723 if (!g_spawn_command_line_sync( command_line,
724 &output,
725 NULL,
726 NULL,
727 &error))
728 {
729
730 fprintf (stderr, "Error: %s\n", error->message);
731 g_error_free (error);
732
733 }
734 }
735
736 if (output == NULL)
737 {
738 output = "";
739 }
740 else
741 {
742 output[strlen ( output ) - 1] = '\0';
743 }
744
745 prompt = g_strdup_printf(_("<b><big>Logout %s %s session ?</big></b>"), session_name, output);
746 }
747 gtk_label_set_markup(GTK_LABEL(label), prompt);
748 gtk_box_pack_start(GTK_BOX(controls), label, FALSE, FALSE, 4);
749
750 /* Create the Shutdown button. */
751 if (handler_context.shutdown_available)
752 {
753 GtkWidget * shutdown_button = gtk_button_new_with_mnemonic(_("Sh_utdown"));
754 GtkWidget * image = gtk_image_new_from_icon_name("system-shutdown", GTK_ICON_SIZE_BUTTON);
755 gtk_button_set_image(GTK_BUTTON(shutdown_button), image);
756 gtk_button_set_alignment(GTK_BUTTON(shutdown_button), 0.0, 0.5);
757 g_signal_connect(G_OBJECT(shutdown_button), "clicked", G_CALLBACK(shutdown_clicked), &handler_context);
758 gtk_box_pack_start(GTK_BOX(controls), shutdown_button, FALSE, FALSE, 4);
759 }
760
761 /* Create the Reboot button. */
762 if (handler_context.reboot_available)
763 {
764 GtkWidget * reboot_button = gtk_button_new_with_mnemonic(_("_Reboot"));
765 GtkWidget * image = gtk_image_new_from_icon_name("gnome-session-reboot", GTK_ICON_SIZE_BUTTON);
766 gtk_button_set_image(GTK_BUTTON(reboot_button), image);
767 gtk_button_set_alignment(GTK_BUTTON(reboot_button), 0.0, 0.5);
768 g_signal_connect(G_OBJECT(reboot_button), "clicked", G_CALLBACK(reboot_clicked), &handler_context);
769 gtk_box_pack_start(GTK_BOX(controls), reboot_button, FALSE, FALSE, 4);
770 }
771
772 /* Create the Suspend button. */
773 if (handler_context.suspend_available && !handler_context.ltsp)
774 {
775 GtkWidget * suspend_button = gtk_button_new_with_mnemonic(_("_Suspend"));
776 GtkWidget * image = gtk_image_new_from_icon_name("gnome-session-suspend", GTK_ICON_SIZE_BUTTON);
777 gtk_button_set_image(GTK_BUTTON(suspend_button), image);
778 gtk_button_set_alignment(GTK_BUTTON(suspend_button), 0.0, 0.5);
779 g_signal_connect(G_OBJECT(suspend_button), "clicked", G_CALLBACK(suspend_clicked), &handler_context);
780 gtk_box_pack_start(GTK_BOX(controls), suspend_button, FALSE, FALSE, 4);
781 }
782
783 /* Create the Hibernate button. */
784 if (handler_context.hibernate_available && !handler_context.ltsp)
785 {
786 GtkWidget * hibernate_button = gtk_button_new_with_mnemonic(_("_Hibernate"));
787 GtkWidget * image = gtk_image_new_from_icon_name("gnome-session-hibernate", GTK_ICON_SIZE_BUTTON);
788 gtk_button_set_image(GTK_BUTTON(hibernate_button), image);
789 gtk_button_set_alignment(GTK_BUTTON(hibernate_button), 0.0, 0.5);
790 g_signal_connect(G_OBJECT(hibernate_button), "clicked", G_CALLBACK(hibernate_clicked), &handler_context);
791 gtk_box_pack_start(GTK_BOX(controls), hibernate_button, FALSE, FALSE, 4);
792 }
793
794 /* Create the Switch User button. */
795 if (handler_context.switch_user_available && !handler_context.ltsp)
796 {
797 GtkWidget * switch_user_button = gtk_button_new_with_mnemonic(_("S_witch User"));
798 GtkWidget * image = gtk_image_new_from_icon_name("gnome-session-switch", GTK_ICON_SIZE_BUTTON);
799 gtk_button_set_image(GTK_BUTTON(switch_user_button), image);
800 gtk_button_set_alignment(GTK_BUTTON(switch_user_button), 0.0, 0.5);
801 g_signal_connect(G_OBJECT(switch_user_button), "clicked", G_CALLBACK(switch_user_clicked), &handler_context);
802 gtk_box_pack_start(GTK_BOX(controls), switch_user_button, FALSE, FALSE, 4);
803 }
804
805 /* Create the Lock Screen button. */
806 if (handler_context.lock_screen && !handler_context.ltsp)
807 {
808 GtkWidget * lock_screen_button = gtk_button_new_with_mnemonic(_("L_ock Screen"));
809 GtkWidget * image = gtk_image_new_from_icon_name("system-lock-screen", GTK_ICON_SIZE_BUTTON);
810 gtk_button_set_image(GTK_BUTTON(lock_screen_button), image);
811 gtk_button_set_alignment(GTK_BUTTON(lock_screen_button), 0.0, 0.5);
812 g_signal_connect(G_OBJECT(lock_screen_button), "clicked", G_CALLBACK(lock_screen_clicked), &handler_context);
813 gtk_box_pack_start(GTK_BOX(controls), lock_screen_button, FALSE, FALSE, 4);
814 }
815
816 /* Create the Logout button. */
817 GtkWidget * logout_button = gtk_button_new_with_mnemonic(_("_Logout"));
818 GtkWidget * image = gtk_image_new_from_icon_name("system-log-out", GTK_ICON_SIZE_BUTTON);
819 gtk_button_set_image(GTK_BUTTON(logout_button), image);
820 gtk_button_set_alignment(GTK_BUTTON(logout_button), 0.0, 0.5);
821 g_signal_connect(G_OBJECT(logout_button), "clicked", G_CALLBACK(logout_clicked), &handler_context);
822 gtk_box_pack_start(GTK_BOX(controls), logout_button, FALSE, FALSE, 4);
823
824 /* Create the Cancel button. */
825 GtkWidget * cancel_button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
826 gtk_button_set_alignment(GTK_BUTTON(cancel_button), 0.0, 0.5);
827 g_signal_connect(G_OBJECT(cancel_button), "clicked", G_CALLBACK(cancel_clicked), NULL);
828 GtkAccelGroup* accel_group = gtk_accel_group_new();
829 gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
830 #if GTK_CHECK_VERSION(3,0,0)
831 gtk_widget_add_accelerator(cancel_button, "activate", accel_group,
832 GDK_KEY_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE);
833 #else
834 gtk_widget_add_accelerator(cancel_button, "activate", accel_group,
835 GDK_Escape, (GdkModifierType)NULL, GTK_ACCEL_VISIBLE);
836 #endif
837 gtk_box_pack_start(GTK_BOX(controls), cancel_button, FALSE, FALSE, 4);
838
839 /* Create the error text. */
840 handler_context.error_label = gtk_label_new("");
841 gtk_label_set_justify(GTK_LABEL(handler_context.error_label), GTK_JUSTIFY_CENTER);
842 gtk_box_pack_start(GTK_BOX(controls), handler_context.error_label, FALSE, FALSE, 4);
843
844 /* Show everything. */
845 gtk_widget_show_all(window);
846
847 /* Run the main event loop. */
848 gtk_main();
849
850 /* Return. */
851 return 0;
852 }
853