2
* Copyright (c) 2010 LxDE Developers, see the file AUTHORS for details.
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.
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.
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.
25
#include <glib/gi18n.h>
26
#include <sys/types.h>
35
#include <X11/Xatom.h>
38
#include "dbus-interface.h"
40
/* Command parameters. */
41
static char * prompt = NULL;
42
static char * banner_side = NULL;
43
static char * banner_path = NULL;
45
static GOptionEntry opt_entries[] =
47
{ "prompt", 'p', 0, G_OPTION_ARG_STRING, &prompt, N_("Custom message to show on the dialog"), N_("message") },
48
{ "banner", 'b', 0, G_OPTION_ARG_STRING, &banner_path, N_("Banner to show on the dialog"), N_("image file") },
49
{ "side", 's', 0, G_OPTION_ARG_STRING, &banner_side, N_("Position of the banner"), "top|left|right|bottom" },
54
GPid lxsession_pid; /* Process ID of lxsession */
55
GtkWidget * error_label; /* Text of an error, if we get one */
57
int shutdown_available : 1; /* Shutdown is available */
58
int reboot_available : 1; /* Reboot is available */
59
int suspend_available : 1; /* Suspend is available */
60
int hibernate_available : 1; /* Hibernate is available */
61
int switch_user_available : 1; /* Switch User is available */
63
int shutdown_ConsoleKit : 1; /* Shutdown is available via ConsoleKit */
64
int reboot_ConsoleKit : 1; /* Reboot is available via ConsoleKit */
65
int suspend_UPower : 1; /* Suspend is available via UPower */
66
int hibernate_UPower : 1; /* Hibernate is available via UPower */
67
int shutdown_HAL : 1; /* Shutdown is available via HAL */
68
int reboot_HAL : 1; /* Reboot is available via HAL */
69
int suspend_HAL : 1; /* Suspend is available via HAL */
70
int hibernate_HAL : 1; /* Hibernate is available via HAL */
71
int switch_user_GDM : 1; /* Switch User is available via GDM */
72
int switch_user_KDM : 1; /* Switch User is available via KDM */
73
int ltsp : 1; /* Shutdown and reboot is accomplished via LTSP */
76
static gboolean verify_running(char * display_manager, char * executable);
77
static void logout_clicked(GtkButton * button, HandlerContext * handler_context);
78
static void change_root_property(GtkWidget* w, const char* prop_name, const char* value);
79
static void shutdown_clicked(GtkButton * button, HandlerContext * handler_context);
80
static void reboot_clicked(GtkButton * button, HandlerContext * handler_context);
81
static void suspend_clicked(GtkButton * button, HandlerContext * handler_context);
82
static void hibernate_clicked(GtkButton * button, HandlerContext * handler_context);
83
static void switch_user_clicked(GtkButton * button, HandlerContext * handler_context);
84
static void cancel_clicked(GtkButton * button, gpointer user_data);
85
static GtkPositionType get_banner_position(void);
86
static GdkPixbuf * get_background_pixbuf(void);
88
/* Verify that a program is running and that an executable is available. */
89
static gboolean verify_running(char * display_manager, char * executable)
91
/* See if the executable we need to run is in the path. */
92
gchar * full_path = g_find_program_in_path(executable);
93
if (full_path != NULL)
97
/* Form the filespec of the pid file for the display manager. */
98
char buffer[PATH_MAX];
99
sprintf(buffer, "/var/run/%s.pid", display_manager);
101
/* Open the pid file. */
102
int fd = open(buffer, O_RDONLY);
105
/* Pid file exists. Read it. */
106
ssize_t length = read(fd, buffer, sizeof(buffer));
110
/* Null terminate the buffer and convert the pid. */
111
buffer[length] = '\0';
112
pid_t pid = atoi(buffer);
115
/* Form the filespec of the command line file under /proc.
116
* This is Linux specific. Should be conditionalized to the appropriate /proc layout for
117
* other systems. Your humble developer has no way to test on other systems. */
118
sprintf(buffer, "/proc/%d/cmdline", pid);
121
int fd = open(buffer, O_RDONLY);
124
/* Read the command line. */
125
ssize_t length = read(fd, buffer, sizeof(buffer));
129
/* Null terminate the buffer and look for the display manager name in the command.
130
* If found, return success. */
131
buffer[length] = '\0';
132
if (strstr(buffer, display_manager) != NULL)
143
/* Handler for "clicked" signal on Logout button. */
144
static void logout_clicked(GtkButton * button, HandlerContext * handler_context)
146
kill(handler_context->lxsession_pid, SIGTERM);
150
/* Replace a property on the root window. */
151
static void change_root_property(GtkWidget* w, const char* prop_name, const char* value)
153
GdkDisplay* dpy = gtk_widget_get_display(w);
154
GdkWindow* root = gtk_widget_get_root_window(w);
155
XChangeProperty(GDK_DISPLAY_XDISPLAY(dpy), GDK_WINDOW_XID(root),
156
XInternAtom(GDK_DISPLAY_XDISPLAY(dpy), prop_name, False), XA_STRING, 8,
157
PropModeReplace, value, strlen(value) + 1);
160
/* Handler for "clicked" signal on Shutdown button. */
161
static void shutdown_clicked(GtkButton * button, HandlerContext * handler_context)
163
char * error_result = NULL;
164
gtk_label_set_text(GTK_LABEL(handler_context->error_label), NULL);
166
if (handler_context->ltsp)
168
change_root_property(GTK_WIDGET(button), "LTSP_LOGOUT_ACTION", "HALT");
169
kill(handler_context->lxsession_pid, SIGTERM);
171
else if (handler_context->shutdown_ConsoleKit)
172
error_result = dbus_ConsoleKit_Stop();
173
else if (handler_context->shutdown_HAL)
174
error_result = dbus_HAL_Shutdown();
176
if (error_result != NULL)
177
gtk_label_set_text(GTK_LABEL(handler_context->error_label), error_result);
178
else gtk_main_quit();
181
/* Handler for "clicked" signal on Reboot button. */
182
static void reboot_clicked(GtkButton * button, HandlerContext * handler_context)
184
char * error_result = NULL;
185
gtk_label_set_text(GTK_LABEL(handler_context->error_label), NULL);
187
if (handler_context->ltsp)
189
change_root_property(GTK_WIDGET(button), "LTSP_LOGOUT_ACTION", "REBOOT");
190
kill(handler_context->lxsession_pid, SIGTERM);
192
else if (handler_context->reboot_ConsoleKit)
193
error_result = dbus_ConsoleKit_Restart();
194
else if (handler_context->reboot_HAL)
195
error_result = dbus_HAL_Reboot();
197
if (error_result != NULL)
198
gtk_label_set_text(GTK_LABEL(handler_context->error_label), error_result);
199
else gtk_main_quit();
202
/* Handler for "clicked" signal on Suspend button. */
203
static void suspend_clicked(GtkButton * button, HandlerContext * handler_context)
205
char * error_result = NULL;
206
gtk_label_set_text(GTK_LABEL(handler_context->error_label), NULL);
208
if (handler_context->suspend_UPower)
209
error_result = dbus_UPower_Suspend();
210
else if (handler_context->suspend_HAL)
211
error_result = dbus_HAL_Suspend();
213
if (error_result != NULL)
214
gtk_label_set_text(GTK_LABEL(handler_context->error_label), error_result);
215
else gtk_main_quit();
218
/* Handler for "clicked" signal on Hibernate button. */
219
static void hibernate_clicked(GtkButton * button, HandlerContext * handler_context)
221
char * error_result = NULL;
222
gtk_label_set_text(GTK_LABEL(handler_context->error_label), NULL);
224
if (handler_context->hibernate_UPower)
225
error_result = dbus_UPower_Hibernate();
226
else if (handler_context->hibernate_HAL)
227
error_result = dbus_HAL_Hibernate();
229
if (error_result != NULL)
230
gtk_label_set_text(GTK_LABEL(handler_context->error_label), error_result);
231
else gtk_main_quit();
234
/* Handler for "clicked" signal on Switch User button. */
235
static void switch_user_clicked(GtkButton * button, HandlerContext * handler_context)
237
gtk_label_set_text(GTK_LABEL(handler_context->error_label), NULL);
239
if (handler_context->switch_user_GDM)
240
g_spawn_command_line_sync("gdmflexiserver --startnew", NULL, NULL, NULL, NULL);
241
else if (handler_context->switch_user_KDM)
242
g_spawn_command_line_sync("kdmctl reserve", NULL, NULL, NULL, NULL);
246
/* Handler for "clicked" signal on Cancel button. */
247
static void cancel_clicked(GtkButton * button, gpointer user_data)
252
/* Convert the --side parameter to a GtkPositionType. */
253
static GtkPositionType get_banner_position(void)
255
if (banner_side != NULL)
257
if (strcmp(banner_side, "right") == 0)
258
return GTK_POS_RIGHT;
259
if (strcmp(banner_side, "top") == 0)
261
if (strcmp(banner_side, "bottom") == 0)
262
return GTK_POS_BOTTOM;
267
/* Get the background pixbuf. */
268
static GdkPixbuf * get_background_pixbuf(void)
270
/* Get the root window pixmap. */
271
GdkScreen * screen = gdk_screen_get_default();
272
GdkPixbuf * pixbuf = gdk_pixbuf_get_from_drawable(
273
NULL, /* Allocate a new pixbuf */
274
gdk_get_default_root_window(), /* The drawable */
275
NULL, /* Its colormap */
276
0, 0, 0, 0, /* Coordinates */
277
gdk_screen_get_width(screen), /* Width */
278
gdk_screen_get_height(screen)); /* Height */
280
/* Make the background darker. */
283
unsigned char * pixels = gdk_pixbuf_get_pixels(pixbuf);
284
int width = gdk_pixbuf_get_width(pixbuf);
285
int height = gdk_pixbuf_get_height(pixbuf);
286
int pixel_stride = ((gdk_pixbuf_get_has_alpha(pixbuf)) ? 4 : 3);
287
int row_stride = gdk_pixbuf_get_rowstride(pixbuf);
289
for (y = 0; y < height; y += 1)
291
unsigned char * p = pixels;
293
for (x = 0; x < width; x += 1)
300
pixels += row_stride;
306
/* Handler for "expose_event" on background. */
307
gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, GdkPixbuf * pixbuf)
311
/* Copy the appropriate rectangle of the root window pixmap to the drawing area.
312
* All drawing areas are immediate children of the toplevel window, so the allocation yields the source coordinates directly. */
314
widget->window, /* Drawable to render to */
315
NULL, /* GC for clipping */
316
pixbuf, /* Source pixbuf */
317
widget->allocation.x, widget->allocation.y, /* Source coordinates */
318
0, 0, /* Destination coordinates */
319
widget->allocation.width, widget->allocation.height,
320
GDK_RGB_DITHER_NORMAL, /* Dither type */
321
0, 0); /* Dither offsets */
327
int main(int argc, char * argv[])
330
setlocale(LC_ALL, "");
331
bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
332
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
333
textdomain (GETTEXT_PACKAGE);
336
/* Initialize GTK (via g_option_context_parse) and parse command line arguments. */
337
GOptionContext * context = g_option_context_new("");
338
g_option_context_add_main_entries(context, opt_entries, GETTEXT_PACKAGE);
339
g_option_context_add_group(context, gtk_get_option_group(TRUE));
341
if ( ! g_option_context_parse(context, &argc, &argv, &err))
343
g_print(_("Error: %s\n"), err->message);
347
g_option_context_free(context);
349
HandlerContext handler_context;
350
memset(&handler_context, 0, sizeof(handler_context));
352
/* Ensure that we are running under lxsession. */
353
const char * p = g_getenv("_LXSESSION_PID");
354
if (p != NULL) handler_context.lxsession_pid = atoi(p);
355
if (handler_context.lxsession_pid == 0)
357
g_print( _("Error: %s\n"), _("LXSession is not running."));
361
/* Initialize capabilities of the ConsoleKit mechanism. */
362
if (dbus_ConsoleKit_CanStop())
364
handler_context.shutdown_available = TRUE;
365
handler_context.shutdown_ConsoleKit = TRUE;
367
if (dbus_ConsoleKit_CanRestart())
369
handler_context.reboot_available = TRUE;
370
handler_context.reboot_ConsoleKit = TRUE;
373
/* Initialize capabilities of the UPower mechanism. */
374
if (dbus_UPower_CanSuspend())
376
handler_context.suspend_available = TRUE;
377
handler_context.suspend_UPower = TRUE;
379
if (dbus_UPower_CanHibernate())
381
handler_context.hibernate_available = TRUE;
382
handler_context.hibernate_UPower = TRUE;
385
/* Initialize capabilities of the HAL mechanism. */
386
if (!handler_context.shutdown_available && dbus_HAL_CanShutdown())
388
handler_context.shutdown_available = TRUE;
389
handler_context.shutdown_HAL = TRUE;
391
if (!handler_context.reboot_available && dbus_HAL_CanReboot())
393
handler_context.reboot_available = TRUE;
394
handler_context.reboot_HAL = TRUE;
396
if (!handler_context.suspend_available && dbus_HAL_CanSuspend())
398
handler_context.suspend_available = TRUE;
399
handler_context.suspend_HAL = TRUE;
401
if (!handler_context.hibernate_available && dbus_HAL_CanHibernate())
403
handler_context.hibernate_available = TRUE;
404
handler_context.hibernate_HAL = TRUE;
407
/* If we are under GDM, its "Switch User" is available. */
408
if (verify_running("gdm", "gdmflexiserver"))
410
handler_context.switch_user_available = TRUE;
411
handler_context.switch_user_GDM = TRUE;
414
/* If we are under KDM, its "Switch User" is available. */
415
if (verify_running("kdm", "kdmctl"))
417
handler_context.switch_user_available = TRUE;
418
handler_context.switch_user_KDM = TRUE;
422
if (g_getenv("LTSP_CLIENT"))
423
handler_context.ltsp = TRUE;
425
/* Make the button images accessible. */
426
gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/lxsession/images");
428
/* Get the background pixbuf. */
429
GdkPixbuf * pixbuf = get_background_pixbuf();
431
/* Create the toplevel window. */
432
GtkWidget * window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
433
gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
434
gtk_window_fullscreen(GTK_WINDOW(window));
435
GdkScreen* screen = gtk_widget_get_screen(window);
436
gtk_window_set_default_size(GTK_WINDOW(window), gdk_screen_get_width(screen), gdk_screen_get_height(screen));
437
gtk_widget_set_app_paintable(window, TRUE);
438
g_signal_connect(G_OBJECT(window), "expose_event", G_CALLBACK(expose_event), pixbuf);
440
/* Toplevel container */
441
GtkWidget* alignment = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
442
gtk_container_add(GTK_CONTAINER(window), alignment);
444
GtkWidget* center_area = gtk_event_box_new();
445
gtk_container_add(GTK_CONTAINER(alignment), center_area);
447
GtkWidget* center_vbox = gtk_vbox_new(FALSE, 6);
448
gtk_container_set_border_width(GTK_CONTAINER(center_vbox), 12);
449
gtk_container_add(GTK_CONTAINER(center_area), center_vbox);
451
GtkWidget* controls = gtk_vbox_new(FALSE, 6);
453
/* If specified, apply a user-specified banner image. */
454
if (banner_path != NULL)
456
GtkWidget * banner_image = gtk_image_new_from_file(banner_path);
457
GtkPositionType banner_position = get_banner_position();
459
switch (banner_position)
464
/* Create a horizontal box to contain the image and the controls. */
465
GtkWidget * box = gtk_hbox_new(FALSE, 2);
466
gtk_box_pack_start(GTK_BOX(center_vbox), box, FALSE, FALSE, 0);
468
/* Pack the image and a separator. */
469
gtk_misc_set_alignment(GTK_MISC(banner_image), 0.5, 0.0);
470
if (banner_position == GTK_POS_LEFT)
472
gtk_box_pack_start(GTK_BOX(box), banner_image, FALSE, FALSE, 2);
473
gtk_box_pack_start(GTK_BOX(box), gtk_vseparator_new(), FALSE, FALSE, 2);
474
gtk_box_pack_start(GTK_BOX(box), controls, FALSE, FALSE, 2);
478
gtk_box_pack_start(GTK_BOX(box), controls, FALSE, FALSE, 2);
479
gtk_box_pack_end(GTK_BOX(box), gtk_vseparator_new(), FALSE, FALSE, 2);
480
gtk_box_pack_end(GTK_BOX(box), banner_image, FALSE, FALSE, 2);
486
gtk_box_pack_start(GTK_BOX(controls), banner_image, FALSE, FALSE, 2);
487
gtk_box_pack_start(GTK_BOX(controls), gtk_hseparator_new(), FALSE, FALSE, 2);
488
gtk_box_pack_start(GTK_BOX(center_vbox), controls, FALSE, FALSE, 0);
492
gtk_box_pack_end(GTK_BOX(controls), banner_image, FALSE, FALSE, 2);
493
gtk_box_pack_end(GTK_BOX(controls), gtk_hseparator_new(), FALSE, FALSE, 2);
494
gtk_box_pack_start(GTK_BOX(center_vbox), controls, FALSE, FALSE, 0);
499
gtk_box_pack_start(GTK_BOX(center_vbox), controls, FALSE, FALSE, 0);
501
/* Create the label. */
502
GtkWidget * label = gtk_label_new("");
505
const char * session_name = g_getenv("DESKTOP_SESSION");
506
if (session_name == NULL)
507
session_name = "LXDE";
508
prompt = g_strdup_printf(_("<b><big>Logout %s session?</big></b>"), session_name);
510
gtk_label_set_markup(GTK_LABEL(label), prompt);
511
gtk_box_pack_start(GTK_BOX(controls), label, FALSE, FALSE, 4);
513
/* Create the Shutdown button. */
514
if (handler_context.shutdown_available)
516
GtkWidget * shutdown_button = gtk_button_new_with_mnemonic(_("Sh_utdown"));
517
GtkWidget * image = gtk_image_new_from_icon_name("system-shutdown", GTK_ICON_SIZE_BUTTON);
518
gtk_button_set_image(GTK_BUTTON(shutdown_button), image);
519
gtk_button_set_alignment(GTK_BUTTON(shutdown_button), 0.0, 0.5);
520
g_signal_connect(G_OBJECT(shutdown_button), "clicked", G_CALLBACK(shutdown_clicked), &handler_context);
521
gtk_box_pack_start(GTK_BOX(controls), shutdown_button, FALSE, FALSE, 4);
524
/* Create the Reboot button. */
525
if (handler_context.reboot_available)
527
GtkWidget * reboot_button = gtk_button_new_with_mnemonic(_("_Reboot"));
528
GtkWidget * image = gtk_image_new_from_icon_name("gnome-session-reboot", GTK_ICON_SIZE_BUTTON);
529
gtk_button_set_image(GTK_BUTTON(reboot_button), image);
530
gtk_button_set_alignment(GTK_BUTTON(reboot_button), 0.0, 0.5);
531
g_signal_connect(G_OBJECT(reboot_button), "clicked", G_CALLBACK(reboot_clicked), &handler_context);
532
gtk_box_pack_start(GTK_BOX(controls), reboot_button, FALSE, FALSE, 4);
535
/* Create the Suspend button. */
536
if (handler_context.suspend_available && !handler_context.ltsp)
538
GtkWidget * suspend_button = gtk_button_new_with_mnemonic(_("_Suspend"));
539
GtkWidget * image = gtk_image_new_from_icon_name("gnome-session-suspend", GTK_ICON_SIZE_BUTTON);
540
gtk_button_set_image(GTK_BUTTON(suspend_button), image);
541
gtk_button_set_alignment(GTK_BUTTON(suspend_button), 0.0, 0.5);
542
g_signal_connect(G_OBJECT(suspend_button), "clicked", G_CALLBACK(suspend_clicked), &handler_context);
543
gtk_box_pack_start(GTK_BOX(controls), suspend_button, FALSE, FALSE, 4);
546
/* Create the Hibernate button. */
547
if (handler_context.hibernate_available && !handler_context.ltsp)
549
GtkWidget * hibernate_button = gtk_button_new_with_mnemonic(_("_Hibernate"));
550
GtkWidget * image = gtk_image_new_from_icon_name("gnome-session-hibernate", GTK_ICON_SIZE_BUTTON);
551
gtk_button_set_image(GTK_BUTTON(hibernate_button), image);
552
gtk_button_set_alignment(GTK_BUTTON(hibernate_button), 0.0, 0.5);
553
g_signal_connect(G_OBJECT(hibernate_button), "clicked", G_CALLBACK(hibernate_clicked), &handler_context);
554
gtk_box_pack_start(GTK_BOX(controls), hibernate_button, FALSE, FALSE, 4);
557
/* Create the Switch User button. */
558
if (handler_context.switch_user_available && !handler_context.ltsp)
560
GtkWidget * switch_user_button = gtk_button_new_with_mnemonic(_("S_witch User"));
561
GtkWidget * image = gtk_image_new_from_icon_name("gnome-session-switch", GTK_ICON_SIZE_BUTTON);
562
gtk_button_set_image(GTK_BUTTON(switch_user_button), image);
563
gtk_button_set_alignment(GTK_BUTTON(switch_user_button), 0.0, 0.5);
564
g_signal_connect(G_OBJECT(switch_user_button), "clicked", G_CALLBACK(switch_user_clicked), &handler_context);
565
gtk_box_pack_start(GTK_BOX(controls), switch_user_button, FALSE, FALSE, 4);
568
/* Create the Logout button. */
569
GtkWidget * logout_button = gtk_button_new_with_mnemonic(_("_Logout"));
570
GtkWidget * image = gtk_image_new_from_icon_name("system-log-out", GTK_ICON_SIZE_BUTTON);
571
gtk_button_set_image(GTK_BUTTON(logout_button), image);
572
gtk_button_set_alignment(GTK_BUTTON(logout_button), 0.0, 0.5);
573
g_signal_connect(G_OBJECT(logout_button), "clicked", G_CALLBACK(logout_clicked), &handler_context);
574
gtk_box_pack_start(GTK_BOX(controls), logout_button, FALSE, FALSE, 4);
576
/* Create the Cancel button. */
577
GtkWidget * cancel_button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
578
gtk_button_set_alignment(GTK_BUTTON(cancel_button), 0.0, 0.5);
579
g_signal_connect(G_OBJECT(cancel_button), "clicked", G_CALLBACK(cancel_clicked), NULL);
580
gtk_box_pack_start(GTK_BOX(controls), cancel_button, FALSE, FALSE, 4);
582
/* Create the error text. */
583
handler_context.error_label = gtk_label_new("");
584
gtk_label_set_justify(GTK_LABEL(handler_context.error_label), GTK_JUSTIFY_CENTER);
585
gtk_box_pack_start(GTK_BOX(controls), handler_context.error_label, FALSE, FALSE, 4);
587
/* Show everything. */
588
gtk_widget_show_all(window);
590
/* Run the main event loop. */