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