source: trunk/athena/bin/gathlogout/gathlogout.c @ 21688

Revision 21688, 10.9 KB checked in by ghudson, 19 years ago (diff)
Some of the dirty hacks here don't work with a modern gtk, so re-adapt from current gnome-session code which uses different dirty hacks.
Line 
1/* gathlogout.c - gnome-session logout substitute for Athena
2   Written by Greg Hudson <ghudson@mit.edu>
3   Copyright (C) MIT
4
5   User interface code based heavily on gnome-session's logout.c
6   Written by Owen Taylor <otaylor@redhat.com>
7   Copyright (C) Red Hat
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2, or (at your option)
12   any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program; if not, write to the Free Software
21   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22   02111-1307, USA.  */
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <ctype.h>
27#include <unistd.h>
28#include <signal.h>
29#include <gtk/gtk.h>
30#include <gtk/gtkinvisible.h>
31#include <gdk/gdkx.h>
32
33typedef struct {
34  GdkScreen    *screen;
35  GdkRectangle  area;
36  int           rowstride;
37  GdkWindow    *root_window;
38  GdkWindow    *draw_window;
39  GdkPixbuf    *start_pb, *end_pb, *frame;
40  guchar       *start_p, *end_p, *frame_p;
41  GTimeVal      start_time;
42  GdkGC        *gc;
43} FadeoutData;
44
45static GList *fadeout_windows = NULL;
46
47/* Go for five seconds */
48#define FADE_DURATION 1500.0
49
50static void
51get_current_frame (FadeoutData *fadeout,
52                   double    sat)
53{
54  guchar *sp, *ep, *fp;
55  int i, j, width, offset;
56
57  width = fadeout->area.width * 3;
58  offset = 0;
59 
60  for (i = 0; i < fadeout->area.height; i++)
61    {
62      sp = fadeout->start_p + offset;
63      ep = fadeout->end_p   + offset;
64      fp = fadeout->frame_p + offset;
65
66      for (j = 0; j < width; j += 3)
67        {
68          guchar r = abs (*(sp++) - ep[0]);
69          guchar g = abs (*(sp++) - ep[1]);
70          guchar b = abs (*(sp++) - ep[2]);
71
72          *(fp++) = *(ep++) + r * sat;
73          *(fp++) = *(ep++) + g * sat;
74          *(fp++) = *(ep++) + b * sat;
75        }
76
77      offset += fadeout->rowstride;
78    }
79}
80
81static void
82darken_pixbuf (GdkPixbuf *pb)
83{
84  int width, height, rowstride;
85  int i, j;
86  guchar *p, *pixels;
87 
88  width     = gdk_pixbuf_get_width (pb) * 3;
89  height    = gdk_pixbuf_get_height (pb);
90  rowstride = gdk_pixbuf_get_rowstride (pb);
91  pixels    = gdk_pixbuf_get_pixels (pb);
92 
93  for (i = 0; i < height; i++)
94    {
95      p = pixels + (i * rowstride);
96      for (j = 0; j < width; j++)
97        p [j] >>= 1;
98    }
99}
100
101static gboolean
102fadeout_callback (FadeoutData *fadeout)
103{
104  GTimeVal current_time;
105  double elapsed, percent;
106
107  g_get_current_time (&current_time);
108  elapsed = ((((double)current_time.tv_sec - fadeout->start_time.tv_sec) * G_USEC_PER_SEC +
109              (current_time.tv_usec - fadeout->start_time.tv_usec))) / 1000.0;
110
111  if (elapsed < 0)
112    {
113      g_warning ("System clock seemed to go backwards?");
114      elapsed = G_MAXDOUBLE;
115    }
116
117  if (elapsed > FADE_DURATION)
118    {
119      gdk_draw_pixbuf (fadeout->draw_window,
120                       fadeout->gc,
121                       fadeout->end_pb,
122                       0, 0,
123                       0, 0,
124                       fadeout->area.width,
125                       fadeout->area.height,
126                       GDK_RGB_DITHER_NONE,
127                       0, 0);
128
129      g_object_unref (fadeout->gc);
130      g_object_unref (fadeout->start_pb);
131      g_object_unref (fadeout->end_pb);
132      g_object_unref (fadeout->frame);
133
134      g_free (fadeout);
135   
136      return FALSE;
137    }
138
139  percent = elapsed / FADE_DURATION;
140
141  get_current_frame (fadeout, 1.0 - percent);
142  gdk_draw_pixbuf (fadeout->draw_window,
143                   fadeout->gc,
144                   fadeout->frame,
145                   0, 0,
146                   0, 0,
147                   fadeout->area.width,
148                   fadeout->area.height,
149                   GDK_RGB_DITHER_NONE,
150                   0, 0);
151
152  gdk_flush ();
153 
154  return TRUE;
155}
156 
157static void
158fadeout_screen (GdkScreen *screen)
159{
160  GdkWindowAttr attr;
161  int attr_mask;
162  GdkGCValues values;
163  FadeoutData *fadeout;
164
165  fadeout = g_new (FadeoutData, 1);
166
167  fadeout->screen = screen;
168  gdk_screen_get_monitor_geometry (screen, 0, &fadeout->area);
169
170  fadeout->root_window = gdk_screen_get_root_window (screen);
171  attr.window_type = GDK_WINDOW_CHILD;
172  attr.x = fadeout->area.x;
173  attr.y = fadeout->area.y;
174  attr.width = fadeout->area.width;
175  attr.height = fadeout->area.height;
176  attr.wclass = GDK_INPUT_OUTPUT;
177  attr.visual = gdk_screen_get_system_visual (fadeout->screen);
178  attr.colormap = gdk_screen_get_default_colormap (fadeout->screen);
179  attr.override_redirect = TRUE;
180  attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_NOREDIR;
181
182  fadeout->draw_window = gdk_window_new (fadeout->root_window, &attr, attr_mask);
183  fadeout_windows = g_list_prepend (fadeout_windows, fadeout->draw_window);
184 
185  fadeout->start_pb = gdk_pixbuf_get_from_drawable (NULL,
186                                                    fadeout->root_window,
187                                                    NULL,
188                                                    fadeout->area.x,
189                                                    fadeout->area.y,
190                                                    0, 0,
191                                                    fadeout->area.width,
192                                                    fadeout->area.height);
193 
194  fadeout->end_pb = gdk_pixbuf_copy (fadeout->start_pb);
195  darken_pixbuf (fadeout->end_pb);
196 
197  fadeout->frame = gdk_pixbuf_copy (fadeout->start_pb);
198  fadeout->rowstride = gdk_pixbuf_get_rowstride (fadeout->start_pb);
199
200  fadeout->start_p = gdk_pixbuf_get_pixels (fadeout->start_pb);
201  fadeout->end_p   = gdk_pixbuf_get_pixels (fadeout->end_pb);
202  fadeout->frame_p = gdk_pixbuf_get_pixels (fadeout->frame);
203 
204  values.subwindow_mode = GDK_INCLUDE_INFERIORS;
205
206  fadeout->gc = gdk_gc_new_with_values (fadeout->root_window, &values, GDK_GC_SUBWINDOW);
207
208  gdk_window_set_back_pixmap (fadeout->draw_window, NULL, FALSE);
209  gdk_window_show (fadeout->draw_window);
210  gdk_draw_pixbuf (fadeout->draw_window,
211                   fadeout->gc,
212                   fadeout->frame,
213                   0, 0,
214                   0, 0,
215                   fadeout->area.width,
216                   fadeout->area.height,
217                   GDK_RGB_DITHER_NONE,
218                   0, 0);
219 
220  g_get_current_time (&fadeout->start_time);
221  g_idle_add ((GSourceFunc) fadeout_callback, fadeout);
222}
223
224static void
225hide_fadeout_windows (void)
226{
227  GList *l;
228
229  for (l = fadeout_windows; l; l = l->next)
230    {
231      gdk_window_hide (GDK_WINDOW (l->data));
232      g_object_unref (l->data);
233    }
234
235  g_list_free (fadeout_windows);
236  fadeout_windows = NULL;
237}
238
239static GtkWidget *
240make_title_label (const char *text)
241{
242  GtkWidget *label;
243  char *full;
244
245  full = g_strdup_printf ("<span weight=\"bold\">%s</span>", text);
246  label = gtk_label_new (full);
247  g_free (full);
248
249  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
250  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
251
252  return label;
253}
254
255static gboolean
256display_gui (void)
257{
258  GtkWidget *box;
259  GtkWidget *title;
260  GtkWidget *hbox;
261  GtkWidget *vbox;
262  GtkWidget *image;
263  GtkWidget *toggle_button = NULL;
264  gint response;
265  GtkWidget *halt = NULL;
266  GtkWidget *reboot = NULL;
267  GtkWidget *invisible;
268  gboolean halt_supported = FALSE;
269  gboolean reboot_supported = FALSE;
270  gboolean retval = FALSE;
271  gboolean save_active = FALSE;
272  gboolean halt_active = FALSE;
273  gboolean reboot_active = FALSE;
274  gboolean a11y_enabled;
275  GError *error = NULL;
276  GdkScreen *screen;
277  int monitor;
278
279  /* It's really bad here if someone else has the pointer
280   * grabbed, so we first grab the pointer and keyboard
281   * to an offscreen window, and then once we have the
282   * server grabbed, move that to our dialog.
283   */
284  gtk_rc_reparse_all ();
285
286  screen = gdk_screen_get_default ();
287
288  invisible = gtk_invisible_new_for_screen (screen);
289
290  gtk_widget_show (invisible);
291
292  a11y_enabled = GTK_IS_ACCESSIBLE (gtk_widget_get_accessible (invisible));
293
294  /* Only create a managed window if a11y is enabled */
295  if (!a11y_enabled)
296    {
297      while (1)
298        {
299          if (gdk_pointer_grab (invisible->window, FALSE, 0,
300                                NULL, NULL, GDK_CURRENT_TIME) == Success)
301            {
302              if (gdk_keyboard_grab (invisible->window, FALSE, GDK_CURRENT_TIME)
303                  == Success)
304                break;
305              gdk_pointer_ungrab (GDK_CURRENT_TIME);
306            }
307          sleep (1);
308        }
309
310      box = g_object_new (GTK_TYPE_DIALOG,
311                          "type", GTK_WINDOW_POPUP,
312                          NULL);
313    }
314  else
315    {
316      box = gtk_dialog_new ();
317      atk_object_set_role (gtk_widget_get_accessible (box), ATK_ROLE_ALERT);
318      gtk_window_set_decorated (GTK_WINDOW (box), FALSE);
319    }
320
321  gtk_dialog_set_has_separator (GTK_DIALOG (box), FALSE);
322
323  vbox = gtk_vbox_new (FALSE, 12);
324  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (box)->vbox), vbox, FALSE, FALSE, 0);
325  gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (box)->vbox), 2);
326  gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
327  gtk_widget_show (vbox);
328 
329  hbox = gtk_hbox_new (FALSE, 12);
330  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
331  gtk_widget_show (hbox);
332
333  image = gtk_image_new_from_stock ("gtk-dialog-question", GTK_ICON_SIZE_DIALOG);
334  gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
335  gtk_widget_show (image);
336       
337  title = make_title_label ("Are you sure you want to log out?");
338  gtk_box_pack_start (GTK_BOX (hbox), title, FALSE, FALSE, 0);
339  gtk_misc_set_alignment (GTK_MISC (title), 0, 0.5);
340  gtk_widget_show (title);
341 
342  gtk_dialog_add_button (GTK_DIALOG (box), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
343  gtk_dialog_add_button (GTK_DIALOG (box), GTK_STOCK_OK, GTK_RESPONSE_OK);
344
345  gtk_dialog_set_default_response (GTK_DIALOG (box), GTK_RESPONSE_OK);
346  gtk_window_set_screen (GTK_WINDOW (box), screen);
347  gtk_window_set_policy (GTK_WINDOW (box), FALSE, FALSE, TRUE);
348
349  gtk_container_set_border_width (GTK_CONTAINER (box), 5);
350
351  gtk_window_set_resizable (GTK_WINDOW (box), FALSE);
352  gtk_window_set_position (GTK_WINDOW (box), GTK_WIN_POS_CENTER);
353
354  /* Grabbing the Xserver when accessibility is enabled will cause
355   * a hang. See #93103 for details.
356   */
357  if (!a11y_enabled)
358    {
359      XGrabServer (GDK_DISPLAY ());
360      fadeout_screen (screen);
361    }
362
363  gtk_widget_show_all (box);
364
365  if (!a11y_enabled)
366    {
367      /* Move the grabs to our message box */
368      gdk_pointer_grab (box->window, TRUE, 0,
369                        NULL, NULL, GDK_CURRENT_TIME);
370      gdk_keyboard_grab (box->window, FALSE, GDK_CURRENT_TIME);
371      XSetInputFocus (GDK_DISPLAY (),
372                      GDK_WINDOW_XWINDOW (box->window),
373                      RevertToParent,
374                      CurrentTime);
375    }
376
377  response = gtk_dialog_run (GTK_DIALOG (box));
378
379  if (halt)
380    halt_active = GTK_TOGGLE_BUTTON (halt)->active;
381
382  if (reboot)
383    reboot_active = GTK_TOGGLE_BUTTON (reboot)->active;
384
385  if (toggle_button)
386    save_active = GTK_TOGGLE_BUTTON (toggle_button)->active;
387
388  gtk_widget_destroy (box);
389  gtk_widget_destroy (invisible);
390
391  if (!a11y_enabled)
392    {
393      hide_fadeout_windows ();
394      XUngrabServer (GDK_DISPLAY ());
395
396      gdk_pointer_ungrab (GDK_CURRENT_TIME);
397      gdk_keyboard_ungrab (GDK_CURRENT_TIME);
398
399      gdk_flush ();
400    }
401
402  return (response == GTK_RESPONSE_OK);
403}
404
405int main (int argc, char **argv)
406{
407  const char *pidstr;
408
409  gtk_init (&argc, &argv);
410  if (display_gui ())
411    {
412      pidstr = getenv ("XSESSION");
413      if (pidstr && isdigit (*pidstr))
414        kill (atoi (pidstr), SIGHUP);
415      else
416        execlp ("/usr/athena/bin/end_session", "end_session", (char *) NULL);
417      return 0;
418    }
419
420  return 1;
421}
Note: See TracBrowser for help on using the repository browser.