1 /*-
2  * Copyright (c) 2003-2004 Benedikt Meurer <benny@xfce.org>
3  * Copyright (c) 2011      Nick Schermer <nick@xfce.org>
4  * All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301 USA.
20  *
21  * Parts of this file where taken from gnome-session/logout.c, which
22  * was written by Owen Taylor <otaylor@redhat.com>.
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #ifdef HAVE_SYS_WAIT_H
30 #include <sys/wait.h>
31 #endif
32 
33 #ifdef HAVE_STDLIB_H
34 #include <stdlib.h>
35 #endif
36 #ifdef HAVE_STRING_H
37 #include <string.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 #ifdef HAVE_MATH_H
43 #include <math.h>
44 #endif
45 
46 #include <libxfce4util/libxfce4util.h>
47 #include <gtk/gtk.h>
48 
49 #include <libxfsm/xfsm-util.h>
50 
51 #include <xfce4-session/xfsm-logout-dialog.h>
52 #include <xfce4-session/xfsm-fadeout.h>
53 #include <xfce4-session/xfsm-global.h>
54 #include <xfce4-session/xfsm-legacy.h>
55 #include <xfce4-session/xfsm-error.h>
56 
57 #ifdef GDK_WINDOWING_X11
58 #include <X11/Xlib.h>
59 #include <gdk/gdkx.h>
60 #endif
61 
62 
63 
64 #define BORDER   6
65 #define SHOTSIZE 64
66 
67 
68 
69 static void       xfsm_logout_dialog_finalize (GObject          *object);
70 static GtkWidget *xfsm_logout_dialog_button   (const gchar      *title,
71                                                const gchar      *icon_name,
72                                                const gchar      *icon_name_fallback,
73                                                const gchar      *icon_name_fallback2,
74                                                XfsmShutdownType  type,
75                                                XfsmLogoutDialog *dialog);
76 
77 
78 
79 enum
80 {
81   MODE_LOGOUT_BUTTONS,
82   MODE_SHOW_ERROR,
83   N_MODES
84 };
85 
86 struct _XfsmLogoutDialogClass
87 {
88   GtkDialogClass __parent__;
89 };
90 
91 struct _XfsmLogoutDialog
92 {
93   GtkDialog __parent__;
94 
95   /* set when a button is clicked */
96   XfsmShutdownType  type_clicked;
97 
98   /* save session checkbox */
99   GtkWidget        *save_session;
100 
101   /* mode widgets */
102   GtkWidget        *box[N_MODES];
103 
104   /* dialog buttons */
105   GtkWidget        *button_cancel;
106 
107   /* error label */
108   GtkWidget        *error_label;
109 
110   /* pm instance */
111   XfsmShutdown     *shutdown;
112 };
113 
114 
115 
G_DEFINE_TYPE(XfsmLogoutDialog,xfsm_logout_dialog,GTK_TYPE_DIALOG)116 G_DEFINE_TYPE (XfsmLogoutDialog, xfsm_logout_dialog, GTK_TYPE_DIALOG)
117 
118 
119 
120 static void
121 xfsm_logout_dialog_class_init (XfsmLogoutDialogClass *klass)
122 {
123   GObjectClass *gobject_class;
124 
125   gobject_class = G_OBJECT_CLASS (klass);
126   gobject_class->finalize = xfsm_logout_dialog_finalize;
127 }
128 
129 
130 
131 static void
xfsm_logout_dialog_init(XfsmLogoutDialog * dialog)132 xfsm_logout_dialog_init (XfsmLogoutDialog *dialog)
133 {
134   const gchar    *username;
135   GtkWidget      *label;
136   gchar          *label_str;
137   PangoAttrList  *attrs;
138   GtkWidget      *vbox;
139   GtkWidget      *button_vbox;
140   GtkWidget      *main_vbox;
141   GtkWidget      *hbox;
142   GtkWidget      *button;
143   gboolean        can_shutdown;
144   gboolean        save_session = FALSE;
145   gboolean        can_restart;
146   gboolean        can_suspend = FALSE;
147   gboolean        can_hibernate = FALSE;
148   gboolean        can_hybrid_sleep = FALSE;
149   gboolean        can_switch_user = FALSE;
150   gboolean        auth_suspend = FALSE;
151   gboolean        auth_hibernate = FALSE;
152   gboolean        auth_hybrid_sleep = FALSE;
153   GError         *error = NULL;
154   XfconfChannel  *channel;
155   GtkWidget      *image;
156   GtkWidget      *separator;
157   gboolean        upower_not_found = FALSE;
158   GtkCssProvider *provider;
159 
160   dialog->type_clicked = XFSM_SHUTDOWN_LOGOUT;
161   dialog->shutdown = xfsm_shutdown_get ();
162 
163   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
164   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
165   gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
166   /* Use Adwaita's keycap style to get a meaningful look out of the box */
167   gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (dialog)), "keycap");
168   gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (dialog)), "xfsm-logout-dialog");
169   provider = gtk_css_provider_new ();
170   gtk_css_provider_load_from_data (provider, ".xfsm-logout-dialog { font-size: initial; }", -1, NULL);
171   gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (dialog)),
172                                   GTK_STYLE_PROVIDER (provider),
173                                   GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
174   g_object_unref (provider);
175 
176   /* load xfconf settings */
177   channel = xfsm_open_config ();
178   if (xfsm_shutdown_can_save_session (dialog->shutdown))
179     save_session = xfconf_channel_get_bool (channel, "/general/SaveOnExit", TRUE);
180 
181   main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, BORDER);
182   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), main_vbox, TRUE, TRUE, 0);
183   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
184   gtk_widget_show (main_vbox);
185 
186   /* label showing the users' name */
187   username = g_get_real_name ();
188   if (username == NULL
189       || *username == '\0'
190       || strcmp ("Unknown", username) == 0)
191     username = g_get_user_name ();
192 
193   label_str = g_strdup_printf (_("Log out %s"), username);
194   label = gtk_label_new (label_str);
195   gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
196   gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (label)), "xfsm-logout-label");
197   gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, TRUE, 0);
198   gtk_widget_show (label);
199   g_free (label_str);
200 
201   attrs = pango_attr_list_new ();
202   pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_LARGE));
203   pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
204   gtk_label_set_attributes (GTK_LABEL (label), attrs);
205   pango_attr_list_unref (attrs);
206 
207   separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
208   gtk_box_pack_start (GTK_BOX (main_vbox), separator, FALSE, TRUE, 0);
209   gtk_widget_show (separator);
210 
211   /**
212    * Start mode MODE_LOGOUT_BUTTONS
213    **/
214   dialog->box[MODE_LOGOUT_BUTTONS] = vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, BORDER);
215   gtk_box_pack_start (GTK_BOX (main_vbox), vbox, TRUE, TRUE, 0);
216 
217   /**
218    * Cancel
219    **/
220   dialog->button_cancel = gtk_dialog_add_button (GTK_DIALOG (dialog),
221                                                  _("_Cancel"),
222                                                  GTK_RESPONSE_CANCEL);
223 
224   button_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, BORDER);
225   gtk_box_pack_start (GTK_BOX (vbox), button_vbox, FALSE, TRUE, 0);
226   gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (button_vbox)), "xfsm-logout-buttons");
227   gtk_widget_show (button_vbox);
228 
229   /* row for logout/shutdown and reboot */
230   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, BORDER);
231   gtk_box_pack_start (GTK_BOX (button_vbox), hbox, FALSE, TRUE, 0);
232   gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
233   gtk_widget_show (hbox);
234 
235   /**
236    * Logout
237    **/
238   button = xfsm_logout_dialog_button (_("_Log Out"), "xfsm-logout",
239                                       "system-log-out", NULL,
240                                       XFSM_SHUTDOWN_LOGOUT, dialog);
241 
242   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
243   gtk_widget_show (button);
244   gtk_widget_grab_focus (button);
245 
246   /**
247    * Reboot
248    **/
249   if (!xfsm_shutdown_can_restart (dialog->shutdown, &can_restart, &error))
250     {
251       g_printerr ("%s: Querying CanRestart failed, %s\n\n",
252                   PACKAGE_NAME, ERROR_MSG (error));
253       g_clear_error (&error);
254 
255       can_restart = FALSE;
256     }
257 
258   button = xfsm_logout_dialog_button (_("_Restart"), "xfsm-reboot",
259                                       "system-reboot", NULL,
260                                       XFSM_SHUTDOWN_RESTART, dialog);
261 
262   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
263   gtk_widget_set_sensitive (button, can_restart);
264   gtk_widget_show (button);
265 
266   /**
267    * Shutdown
268    **/
269   if (!xfsm_shutdown_can_shutdown (dialog->shutdown, &can_shutdown, &error))
270     {
271       g_printerr ("%s: Querying CanShutdown failed. %s\n\n",
272                   PACKAGE_NAME, ERROR_MSG (error));
273       g_clear_error (&error);
274 
275       can_shutdown = FALSE;
276     }
277 
278   button = xfsm_logout_dialog_button (_("Shut _Down"), "xfsm-shutdown",
279                                       "system-shutdown", NULL,
280                                       XFSM_SHUTDOWN_SHUTDOWN, dialog);
281 
282   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
283   gtk_widget_set_sensitive (button, can_shutdown);
284   gtk_widget_show (button);
285 
286   /* new row for suspend/hibernate/hybrid sleep */
287   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, BORDER);
288   gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
289   gtk_box_pack_start (GTK_BOX (button_vbox), hbox, FALSE, TRUE, 0);
290 
291   /**
292    * Suspend
293    *
294    * Hide the button if UPower is not installed or system cannot suspend
295    **/
296   if (xfconf_channel_get_bool (channel, "/shutdown/ShowSuspend", TRUE))
297     {
298       if (xfsm_shutdown_can_suspend (dialog->shutdown, &can_suspend, &auth_suspend, &error))
299         {
300           if (can_suspend)
301             {
302               button = xfsm_logout_dialog_button (_("Sus_pend"), "xfsm-suspend",
303                                                   "system-suspend", NULL,
304                                                   XFSM_SHUTDOWN_SUSPEND, dialog);
305 
306               gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
307               gtk_widget_set_sensitive (button, auth_suspend);
308               gtk_widget_show (button);
309 
310               gtk_widget_show (hbox);
311             }
312         }
313       else
314         {
315           g_printerr ("%s: Querying suspend failed: %s\n\n",
316                       PACKAGE_NAME, ERROR_MSG (error));
317           g_clear_error (&error);
318 
319           /* don't try hibernate again */
320           upower_not_found = TRUE;
321         }
322     }
323 
324   /**
325    * Hibernate
326    *
327    * Hide the button if UPower is not installed or system cannot suspend
328    **/
329   if (!upower_not_found
330       && xfconf_channel_get_bool (channel, "/shutdown/ShowHibernate", TRUE))
331     {
332       if (xfsm_shutdown_can_hibernate (dialog->shutdown, &can_hibernate, &auth_hibernate, &error))
333         {
334           if (can_hibernate)
335             {
336               button = xfsm_logout_dialog_button (_("_Hibernate"), "xfsm-hibernate",
337                                                   "system-hibernate", NULL,
338                                                   XFSM_SHUTDOWN_HIBERNATE, dialog);
339 
340               gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
341               gtk_widget_set_sensitive (button, auth_hibernate);
342               gtk_widget_show (button);
343 
344               gtk_widget_show (hbox);
345             }
346         }
347       else
348         {
349           g_printerr ("%s: Querying hibernate failed: %s\n\n",
350                       PACKAGE_NAME, ERROR_MSG (error));
351           g_clear_error (&error);
352         }
353     }
354 
355   /**
356    * Hybrid Sleep
357    *
358    * Hide the button if UPower is not installed or system cannot suspend
359    **/
360   if (!upower_not_found
361       && xfconf_channel_get_bool (channel, "/shutdown/ShowHybridSleep", TRUE))
362     {
363       if (xfsm_shutdown_can_hybrid_sleep (dialog->shutdown, &can_hybrid_sleep, &auth_hybrid_sleep, &error))
364         {
365           if (can_hybrid_sleep)
366             {
367               button = xfsm_logout_dialog_button (_("H_ybrid Sleep"), "xfsm-hibernate",
368                                                   "system-hibernate", "system-suspend-hibernate",
369                                                   XFSM_SHUTDOWN_HYBRID_SLEEP, dialog);
370 
371               gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
372               gtk_widget_set_sensitive (button, auth_hybrid_sleep);
373               gtk_widget_show (button);
374 
375               gtk_widget_show (hbox);
376             }
377         }
378       else
379         {
380           g_printerr ("%s: Querying hybrid-sleep failed: %s\n\n",
381                       PACKAGE_NAME, ERROR_MSG (error));
382           g_clear_error (&error);
383         }
384     }
385 
386   /**
387    * Switch User
388    *
389    * Hide the button if system cannot switch user, requires the display
390    * manager to provide the org.freedesktop.DisplayManager dbus API
391    **/
392   if (xfconf_channel_get_bool (channel, "/shutdown/ShowSwitchUser", TRUE))
393     {
394       if (xfsm_shutdown_can_switch_user (dialog->shutdown, &can_switch_user, &error))
395         {
396           if (can_switch_user)
397             {
398               button = xfsm_logout_dialog_button (_("Switch _User"), "xfsm-switch-user",
399                                                   "system-users", "system-users-symbolic",
400                                                   XFSM_SHUTDOWN_SWITCH_USER, dialog);
401 
402               gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
403               gtk_widget_set_sensitive (button, can_switch_user);
404               gtk_widget_show (button);
405 
406               gtk_widget_show (hbox);
407             }
408         }
409       else
410         {
411           g_printerr ("%s: Querying switch user failed: %s\n\n",
412                       PACKAGE_NAME, ERROR_MSG (error));
413           g_clear_error (&error);
414         }
415     }
416 
417   /**
418    * Save session
419    **/
420   if (xfsm_shutdown_can_save_session (dialog->shutdown)
421       && !xfconf_channel_get_bool (channel, "/general/AutoSave", FALSE))
422     {
423       dialog->save_session = gtk_check_button_new_with_mnemonic (_("_Save session for future logins"));
424       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->save_session), save_session);
425       gtk_box_pack_start (GTK_BOX (vbox), dialog->save_session, FALSE, TRUE, BORDER);
426       gtk_widget_show (dialog->save_session);
427     }
428 
429   attrs = pango_attr_list_new ();
430   pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
431 
432 
433   /**
434    * Start mode MODE_SHOW_ERROR
435    **/
436   dialog->box[MODE_SHOW_ERROR] = vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, BORDER);
437   gtk_box_pack_start (GTK_BOX (main_vbox), vbox, TRUE, TRUE, 0);
438 
439   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, BORDER * 2);
440   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
441   gtk_widget_show (hbox);
442 
443   image = gtk_image_new_from_icon_name ("dialog-error", GTK_ICON_SIZE_DIALOG);
444   gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
445   gtk_widget_show (image);
446 
447   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, BORDER);
448   gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
449   gtk_widget_show (vbox);
450 
451   label = gtk_label_new (_("An error occurred"));
452   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
453   gtk_label_set_xalign (GTK_LABEL (label), 0.00);
454   gtk_label_set_yalign (GTK_LABEL (label), 0.50);
455   gtk_label_set_attributes (GTK_LABEL (label), attrs);
456   gtk_widget_show (label);
457 
458   pango_attr_list_unref (attrs);
459 }
460 
461 
462 
463 static void
xfsm_logout_dialog_finalize(GObject * object)464 xfsm_logout_dialog_finalize (GObject *object)
465 {
466   XfsmLogoutDialog *dialog = XFSM_LOGOUT_DIALOG (object);
467 
468   g_object_unref (G_OBJECT (dialog->shutdown));
469 
470   (*G_OBJECT_CLASS (xfsm_logout_dialog_parent_class)->finalize) (object);
471 }
472 
473 
474 
475 static void
xfsm_logout_dialog_set_mode(XfsmLogoutDialog * dialog,gint mode)476 xfsm_logout_dialog_set_mode (XfsmLogoutDialog *dialog,
477                              gint              mode)
478 {
479   gint i;
480 
481   for (i = 0; i < N_MODES; i++)
482     gtk_widget_set_visible (dialog->box[i], i == mode);
483 
484   gtk_widget_set_visible (dialog->button_cancel, mode != MODE_SHOW_ERROR);
485 }
486 
487 
488 
489 static void
xfsm_logout_dialog_button_clicked(GtkWidget * button,XfsmLogoutDialog * dialog)490 xfsm_logout_dialog_button_clicked (GtkWidget        *button,
491                                    XfsmLogoutDialog *dialog)
492 {
493   gint *val;
494 
495   val = g_object_get_data (G_OBJECT (button), "shutdown-type");
496   g_assert (val != NULL);
497   dialog->type_clicked = *val;
498 
499   gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
500 }
501 
502 
503 
504 static GtkWidget *
xfsm_logout_dialog_button(const gchar * title,const gchar * icon_name,const gchar * icon_name_fallback,const gchar * icon_name_fallback2,XfsmShutdownType type,XfsmLogoutDialog * dialog)505 xfsm_logout_dialog_button (const gchar      *title,
506                            const gchar      *icon_name,
507                            const gchar      *icon_name_fallback,
508                            const gchar      *icon_name_fallback2,
509                            XfsmShutdownType  type,
510                            XfsmLogoutDialog *dialog)
511 {
512   GtkWidget    *button;
513   GtkWidget    *vbox;
514   GtkWidget    *image;
515   GtkWidget    *label;
516   gint         *val;
517   GtkIconTheme *icon_theme;
518 
519   g_return_val_if_fail (XFSM_IS_LOGOUT_DIALOG (dialog), NULL);
520 
521   val = g_new0 (gint, 1);
522   *val = type;
523 
524   button = gtk_button_new ();
525   g_object_set_data_full (G_OBJECT (button), "shutdown-type", val, g_free);
526   g_signal_connect (G_OBJECT (button), "clicked",
527       G_CALLBACK (xfsm_logout_dialog_button_clicked), dialog);
528 
529   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, BORDER);
530   gtk_container_set_border_width (GTK_CONTAINER (vbox), BORDER);
531   gtk_container_add (GTK_CONTAINER (button), vbox);
532   gtk_widget_show (vbox);
533 
534   icon_theme = gtk_icon_theme_get_default ();
535 
536   image = gtk_image_new ();
537   if (gtk_icon_theme_has_icon (icon_theme, icon_name))
538       gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, GTK_ICON_SIZE_DIALOG);
539   else if (gtk_icon_theme_has_icon (icon_theme, icon_name_fallback))
540       gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name_fallback, GTK_ICON_SIZE_DIALOG);
541   else if (gtk_icon_theme_has_icon (icon_theme, icon_name_fallback2))
542       gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name_fallback2, GTK_ICON_SIZE_DIALOG);
543 
544   gtk_image_set_pixel_size (GTK_IMAGE (image), 48);
545   gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);
546   gtk_widget_show (image);
547 
548   label = gtk_label_new_with_mnemonic (title);
549   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
550   gtk_widget_show (label);
551 
552   return button;
553 }
554 
555 
556 
557 static GdkPixbuf *
xfsm_logout_dialog_screenshot_new(GdkScreen * screen)558 xfsm_logout_dialog_screenshot_new (GdkScreen *screen)
559 {
560   GdkRectangle  rect, screen_rect;
561   GdkWindow    *window;
562   GdkPixbuf    *screenshot;
563   gint          x, y;
564 
565   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
566 
567   screen_rect.x = 0;
568   screen_rect.y = 0;
569   screen_rect.width = gdk_screen_get_width (screen);
570   screen_rect.height = gdk_screen_get_height (screen);
571 
572   window = gdk_screen_get_root_window (screen);
573 
574   rect.width = gdk_window_get_width (window);
575   rect.height = gdk_window_get_height (window);
576 
577   gdk_window_get_origin (window, &x, &y);
578 
579   rect.x = x;
580   rect.y = y;
581 
582   if (!gdk_rectangle_intersect (&rect, &screen_rect, &rect))
583     return NULL;
584 
585   screenshot = gdk_pixbuf_get_from_window  (window, 0, 0, rect.width, rect.height);
586 
587   gdk_display_beep (gdk_screen_get_display (screen));
588 
589   return screenshot;
590 }
591 
592 
593 
594 static GdkPixbuf *
exo_gdk_pixbuf_scale_ratio(GdkPixbuf * source,gint dest_size)595 exo_gdk_pixbuf_scale_ratio (GdkPixbuf *source,
596                             gint       dest_size)
597 {
598   gdouble wratio;
599   gdouble hratio;
600   gint    source_width;
601   gint    source_height;
602   gint    dest_width;
603   gint    dest_height;
604 
605   g_return_val_if_fail (GDK_IS_PIXBUF (source), NULL);
606   g_return_val_if_fail (dest_size > 0, NULL);
607 
608   source_width  = gdk_pixbuf_get_width  (source);
609   source_height = gdk_pixbuf_get_height (source);
610 
611   wratio = (gdouble) source_width  / (gdouble) dest_size;
612   hratio = (gdouble) source_height / (gdouble) dest_size;
613 
614   if (hratio > wratio)
615     {
616       dest_width  = rint (source_width / hratio);
617       dest_height = dest_size;
618     }
619   else
620     {
621       dest_width  = dest_size;
622       dest_height = rint (source_height / wratio);
623     }
624 
625   return gdk_pixbuf_scale_simple (source, MAX (dest_width, 1),
626                                   MAX (dest_height, 1), GDK_INTERP_BILINEAR);
627 }
628 
629 
630 
631 static void
xfsm_logout_dialog_screenshot_save(GdkPixbuf * screenshot,GdkScreen * screen,const gchar * session_name)632 xfsm_logout_dialog_screenshot_save (GdkPixbuf   *screenshot,
633                                     GdkScreen   *screen,
634                                     const gchar *session_name)
635 {
636   GdkPixbuf  *scaled;
637   gchar      *path;
638   gchar      *display_name;
639   GdkDisplay *dpy;
640   GError     *error = NULL;
641   gchar      *filename;
642 
643   g_return_if_fail (GDK_IS_PIXBUF (screenshot));
644   g_return_if_fail (GDK_IS_SCREEN (screen));
645 
646   scaled = exo_gdk_pixbuf_scale_ratio (screenshot, SHOTSIZE);
647   if (G_LIKELY (scaled != NULL))
648     {
649       dpy = gdk_screen_get_display (screen);
650       display_name = xfsm_gdk_display_get_fullname (dpy);
651       path = g_strconcat ("sessions/thumbs-", display_name, "/", session_name, ".png", NULL);
652       filename = xfce_resource_save_location (XFCE_RESOURCE_CACHE, path, TRUE);
653       g_free (display_name);
654       g_free (path);
655 
656       if (!filename)
657         {
658           g_warning ("Unable to save screenshot, "
659                      "error calling xfce_resource_save_location with %s, "
660                      "check your permissions", path);
661           return;
662         }
663 
664       if (!gdk_pixbuf_save (scaled, filename, "png", &error, NULL))
665         {
666           g_warning ("Failed to save session screenshot: %s", error->message);
667           g_error_free (error);
668         }
669 
670       g_free (filename);
671       g_object_unref (G_OBJECT (scaled));
672     }
673 }
674 
675 
676 
677 static void
xfsm_logout_dialog_grab_callback(GdkSeat * seat,GdkWindow * window,gpointer user_data)678 xfsm_logout_dialog_grab_callback (GdkSeat   *seat,
679                                   GdkWindow *window,
680                                   gpointer   user_data)
681 {
682   /* ensure window is mapped to avoid unsuccessful grabs */
683   if (!gdk_window_is_visible (window))
684     gdk_window_show (window);
685 }
686 
687 
688 
689 static gint
xfsm_logout_dialog_run(GtkDialog * dialog,gboolean grab_input)690 xfsm_logout_dialog_run (GtkDialog *dialog,
691                         gboolean   grab_input)
692 {
693   GdkWindow *window;
694   gint       ret;
695   GdkDevice *device;
696   GdkSeat   *seat;
697 
698   if (grab_input)
699     {
700       gtk_widget_show_now (GTK_WIDGET (dialog));
701 
702       window = gtk_widget_get_window (GTK_WIDGET (dialog));
703 
704       device = gtk_get_current_event_device ();
705       seat = device != NULL
706              ? gdk_device_get_seat (device)
707              : gdk_display_get_default_seat (gtk_widget_get_display (GTK_WIDGET (dialog)));
708 
709       if (gdk_seat_grab (seat, window,
710                          GDK_SEAT_CAPABILITY_KEYBOARD,
711                          FALSE, NULL, NULL,
712                          xfsm_logout_dialog_grab_callback,
713                          NULL) != GDK_GRAB_SUCCESS)
714         {
715           g_critical ("Failed to grab the keyboard for logout window");
716         }
717 
718 #ifdef GDK_WINDOWING_X11
719       /* force input to the dialog */
720       gdk_error_trap_push ();
721       XSetInputFocus (gdk_x11_get_default_xdisplay (),
722                       GDK_WINDOW_XID (window),
723                       RevertToParent, CurrentTime);
724       gdk_error_trap_pop_ignored ();
725 #endif
726     }
727 
728   ret = gtk_dialog_run (dialog);
729 
730   if (grab_input)
731     gdk_seat_ungrab (seat);
732 
733   return ret;
734 }
735 
736 
737 
738 gboolean
xfsm_logout_dialog(const gchar * session_name,XfsmShutdownType * return_type,gboolean * return_save_session,gboolean accessibility)739 xfsm_logout_dialog (const gchar      *session_name,
740                     XfsmShutdownType *return_type,
741                     gboolean         *return_save_session,
742                     gboolean          accessibility)
743 {
744   gint              result;
745   GtkWidget        *hidden;
746   GtkWidget        *dialog;
747   GdkScreen        *screen;
748   gint              monitor;
749   GdkPixbuf        *screenshot = NULL;
750   XfsmFadeout      *fadeout = NULL;
751   XfsmLogoutDialog *xfsm_dialog;
752   XfconfChannel    *channel = xfsm_open_config ();
753   gboolean          autosave;
754   XfsmShutdown     *shutdown;
755   GdkDevice        *device;
756   GdkSeat          *seat;
757   gint              grab_count = 0;
758 
759   g_return_val_if_fail (return_type != NULL, FALSE);
760   g_return_val_if_fail (return_save_session != NULL, FALSE);
761 
762   shutdown = xfsm_shutdown_get ();
763   if (xfsm_shutdown_can_save_session (shutdown))
764     autosave = xfconf_channel_get_bool (channel, "/general/AutoSave", FALSE);
765   else
766     autosave = FALSE;
767   g_object_unref (shutdown);
768 
769   /* check if we need to bother the user */
770   if (!xfconf_channel_get_bool (channel, "/general/PromptOnLogout", TRUE))
771     {
772       *return_type = XFSM_SHUTDOWN_LOGOUT;
773       *return_save_session = autosave;
774 
775       return TRUE;
776     }
777 
778   /* decide on which screen we should show the dialog */
779   screen = xfce_gdk_screen_get_active (&monitor);
780   if (G_UNLIKELY (screen == NULL))
781     {
782       screen = gdk_screen_get_default ();
783       monitor = 0;
784     }
785 
786   hidden = gtk_invisible_new_for_screen (screen);
787   gtk_widget_show (hidden);
788 
789   /* check if accessibility is enabled */
790   if (G_LIKELY (!accessibility))
791     {
792       /* wait until we can grab the keyboard, we need this for
793        * the dialog when running it */
794       for (;;)
795         {
796           device = gtk_get_current_event_device ();
797           seat = device != NULL
798                  ? gdk_device_get_seat (device)
799                  : gdk_display_get_default_seat (gtk_widget_get_display (hidden));
800 
801           if (gdk_seat_grab (seat, gtk_widget_get_window (hidden),
802                              GDK_SEAT_CAPABILITY_KEYBOARD,
803                              FALSE, NULL, NULL,
804                              xfsm_logout_dialog_grab_callback,
805                              NULL) == GDK_GRAB_SUCCESS)
806             {
807               gdk_seat_ungrab (seat);
808               break;
809             }
810 
811           if (grab_count++ >= 40)
812             {
813               g_critical ("Failed to grab the keyboard for logout window");
814               break;
815             }
816 
817           g_usleep (G_USEC_PER_SEC / 20);
818         }
819 
820       /* make a screenshot */
821       if (xfconf_channel_get_bool (channel, "/general/ShowScreenshots", TRUE))
822         screenshot = xfsm_logout_dialog_screenshot_new (screen);
823 
824       /* display fadeout */
825       fadeout = xfsm_fadeout_new (gdk_screen_get_display (screen));
826 
827       dialog = g_object_new (XFSM_TYPE_LOGOUT_DIALOG,
828                              "type", GTK_WINDOW_POPUP,
829                              "screen", screen, NULL);
830 
831       gtk_widget_realize (dialog);
832       gdk_window_set_override_redirect (gtk_widget_get_window (dialog), TRUE);
833       gdk_window_raise (gtk_widget_get_window (dialog));
834     }
835   else
836     {
837       dialog = g_object_new (XFSM_TYPE_LOGOUT_DIALOG,
838                              "decorated", !accessibility,
839                              "screen", screen, NULL);
840 
841       gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
842       atk_object_set_role (gtk_widget_get_accessible (dialog), ATK_ROLE_ALERT);
843     }
844 
845   gtk_widget_destroy (hidden);
846 
847   xfsm_dialog = XFSM_LOGOUT_DIALOG (dialog);
848 
849   /* set mode */
850   xfsm_logout_dialog_set_mode (xfsm_dialog, MODE_LOGOUT_BUTTONS);
851 
852   result = xfsm_logout_dialog_run (GTK_DIALOG (dialog), !accessibility);
853 
854   gtk_widget_hide (dialog);
855 
856   if (result == GTK_RESPONSE_OK)
857     {
858       /* store autosave state */
859       if (autosave)
860         *return_save_session = TRUE;
861       else if (xfsm_dialog->save_session != NULL)
862         *return_save_session = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (xfsm_dialog->save_session));
863       else
864         *return_save_session = FALSE;
865 
866       /* return the clicked action */
867       *return_type = xfsm_dialog->type_clicked;
868     }
869 
870   if (fadeout != NULL)
871     xfsm_fadeout_destroy (fadeout);
872 
873   gtk_widget_destroy (dialog);
874 
875   /* store channel settings if everything worked fine */
876   if (result == GTK_RESPONSE_OK)
877     {
878       xfconf_channel_set_string (channel, "/general/SessionName", session_name);
879       xfconf_channel_set_bool (channel, "/general/SaveOnExit", *return_save_session);
880     }
881 
882   /* save the screenshot */
883   if (screenshot != NULL)
884     {
885       if (result == GTK_RESPONSE_OK)
886         xfsm_logout_dialog_screenshot_save (screenshot, screen, session_name);
887       g_object_unref (G_OBJECT (screenshot));
888     }
889 
890   return (result == GTK_RESPONSE_OK);
891 }
892