44cce89faca0141b68fabdfd3b6bf2a754cf2210
[xscreensaver] / driver / windows.c
1 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
2  * xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #ifdef VMS
18 # include <unixlib.h>           /* for getpid() */
19 # include "vms-gtod.h"          /* for gettimeofday() */
20 #endif /* VMS */
21
22 #ifndef VMS
23 # include <pwd.h>               /* for getpwuid() */
24 #else /* VMS */
25 # include "vms-pwd.h"
26 #endif /* VMS */
27
28 #ifdef HAVE_UNAME
29 # include <sys/utsname.h>       /* for uname() */
30 #endif /* HAVE_UNAME */
31
32 #include <stdio.h>
33 /* #include <X11/Xproto.h>      / * for CARD32 */
34 #include <X11/Xlib.h>
35 #include <X11/Xutil.h>          /* for XSetClassHint() */
36 #include <X11/Xatom.h>
37 #include <X11/Xos.h>            /* for time() */
38 #include <signal.h>             /* for the signal names */
39 #include <time.h>
40 #include <sys/time.h>
41
42 /* You might think that to store an array of 32-bit quantities onto a
43    server-side property, you would pass an array of 32-bit data quantities
44    into XChangeProperty().  You would be wrong.  You have to use an array
45    of longs, even if long is 64 bits (using 32 of each 64.)
46  */
47 typedef long PROP32;
48
49 #ifdef HAVE_MIT_SAVER_EXTENSION
50 # include <X11/extensions/scrnsaver.h>
51 #endif /* HAVE_MIT_SAVER_EXTENSION */
52
53 #ifdef HAVE_XF86VMODE
54 # include <X11/extensions/xf86vmode.h>
55 #endif /* HAVE_XF86VMODE */
56
57 #ifdef HAVE_XINERAMA
58 # include <X11/extensions/Xinerama.h>
59 #endif /* HAVE_XINERAMA */
60
61 /* This file doesn't need the Xt headers, so stub these types out... */
62 #undef XtPointer
63 #define XtAppContext void*
64 #define XrmDatabase  void*
65 #define XtIntervalId void*
66 #define XtPointer    void*
67 #define Widget       void*
68
69 #include "xscreensaver.h"
70 #include "visual.h"
71 #include "fade.h"
72
73
74 extern int kill (pid_t, int);           /* signal() is in sys/signal.h... */
75
76 Atom XA_VROOT, XA_XSETROOT_ID, XA_ESETROOT_PMAP_ID, XA_XROOTPMAP_ID;
77 Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
78 Atom XA_SCREENSAVER_STATUS;
79
80
81 extern saver_info *global_si_kludge;    /* I hate C so much... */
82
83 static void maybe_transfer_grabs (saver_screen_info *ssi,
84                                   Window old_w, Window new_w, int new_screen);
85
86 #define ALL_POINTER_EVENTS \
87         (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
88          LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
89          Button1MotionMask | Button2MotionMask | Button3MotionMask | \
90          Button4MotionMask | Button5MotionMask | ButtonMotionMask)
91
92
93 static const char *
94 grab_string(int status)
95 {
96   switch (status)
97     {
98     case GrabSuccess:     return "GrabSuccess";
99     case AlreadyGrabbed:  return "AlreadyGrabbed";
100     case GrabInvalidTime: return "GrabInvalidTime";
101     case GrabNotViewable: return "GrabNotViewable";
102     case GrabFrozen:      return "GrabFrozen";
103     default:
104       {
105         static char foo[255];
106         sprintf(foo, "unknown status: %d", status);
107         return foo;
108       }
109     }
110 }
111
112 static int
113 grab_kbd(saver_info *si, Window w, int screen_no)
114 {
115   saver_preferences *p = &si->prefs;
116   int status = XGrabKeyboard (si->dpy, w, True,
117                               /* I don't really understand Sync vs Async,
118                                  but these seem to work... */
119                               GrabModeSync, GrabModeAsync,
120                               CurrentTime);
121   if (status == GrabSuccess)
122     {
123       si->keyboard_grab_window = w;
124       si->keyboard_grab_screen = screen_no;
125     }
126
127   if (p->verbose_p)
128     fprintf(stderr, "%s: %d: grabbing keyboard on 0x%lx... %s.\n",
129             blurb(), screen_no, (unsigned long) w, grab_string(status));
130   return status;
131 }
132
133
134 static int
135 grab_mouse (saver_info *si, Window w, Cursor cursor, int screen_no)
136 {
137   saver_preferences *p = &si->prefs;
138   int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS,
139                              GrabModeAsync, GrabModeAsync, w,
140                              cursor, CurrentTime);
141   if (status == GrabSuccess)
142     {
143       si->mouse_grab_window = w;
144       si->mouse_grab_screen = screen_no;
145     }
146
147   if (p->verbose_p)
148     fprintf(stderr, "%s: %d: grabbing mouse on 0x%lx... %s.\n",
149             blurb(), screen_no, (unsigned long) w, grab_string(status));
150   return status;
151 }
152
153
154 static void
155 ungrab_kbd(saver_info *si)
156 {
157   saver_preferences *p = &si->prefs;
158   XUngrabKeyboard(si->dpy, CurrentTime);
159   if (p->verbose_p)
160     fprintf(stderr, "%s: %d: ungrabbing keyboard (was 0x%lx).\n",
161             blurb(), si->keyboard_grab_screen,
162             (unsigned long) si->keyboard_grab_window);
163   si->keyboard_grab_window = 0;
164 }
165
166
167 static void
168 ungrab_mouse(saver_info *si)
169 {
170   saver_preferences *p = &si->prefs;
171   XUngrabPointer(si->dpy, CurrentTime);
172   if (p->verbose_p)
173     fprintf(stderr, "%s: %d: ungrabbing mouse (was 0x%lx).\n",
174             blurb(), si->mouse_grab_screen,
175             (unsigned long) si->mouse_grab_window);
176   si->mouse_grab_window = 0;
177 }
178
179
180 /* Apparently there is this program called "rdesktop" which is a windows
181    terminal server client for Unix.  It would seem that this program holds
182    the keyboard GRABBED the whole time it has focus!  This is, of course,
183    completely idiotic: the whole point of grabbing is to get events when
184    you do *not* have focus, so grabbing *only when* you have focus is
185    completely redundant -- unless your goal is to make xscreensaver not
186    able to ever lock the screen when your program is running.
187
188    If xscreensaver blanks while rdesktop still has a keyboard grab, then
189    when we try to prompt for the password, we won't get the characters:
190    they'll be typed into rdesktop.
191
192    Perhaps rdesktop will release its keyboard grab if it loses focus?
193    What the hell, let's give it a try.  If we fail to grab the keyboard
194    four times in a row, we forcibly set focus to "None" and try four
195    more times.  (We don't touch focus unless we're already having a hard
196    time getting a grab.)
197  */
198 static void
199 nuke_focus (saver_info *si, int screen_no)
200 {
201   saver_preferences *p = &si->prefs;
202   Window focus = 0;
203   int rev = 0;
204
205   XGetInputFocus (si->dpy, &focus, &rev);
206
207   if (p->verbose_p)
208     {
209       char w[255], r[255];
210
211       if      (focus == PointerRoot) strcpy (w, "PointerRoot");
212       else if (focus == None)        strcpy (w, "None");
213       else    sprintf (w, "0x%lx", (unsigned long) focus);
214
215       if      (rev == RevertToParent)      strcpy (r, "RevertToParent");
216       else if (rev == RevertToPointerRoot) strcpy (r, "RevertToPointerRoot");
217       else if (rev == RevertToNone)        strcpy (r, "RevertToNone");
218       else    sprintf (r, "0x%x", rev);
219
220       fprintf (stderr, "%s: %d: removing focus from %s / %s.\n",
221                blurb(), screen_no, w, r);
222     }
223
224   XSetInputFocus (si->dpy, None, RevertToNone, CurrentTime);
225   XSync (si->dpy, False);
226 }
227
228
229 static void
230 ungrab_keyboard_and_mouse (saver_info *si)
231 {
232   ungrab_mouse (si);
233   ungrab_kbd (si);
234 }
235
236
237 static Bool
238 grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor,
239                          int screen_no)
240 {
241   Status mstatus = 0, kstatus = 0;
242   int i;
243   int retries = 4;
244   Bool focus_fuckus = False;
245
246  AGAIN:
247
248   for (i = 0; i < retries; i++)
249     {
250       XSync (si->dpy, False);
251       kstatus = grab_kbd (si, window, screen_no);
252       if (kstatus == GrabSuccess)
253         break;
254
255       /* else, wait a second and try to grab again. */
256       sleep (1);
257     }
258
259   if (kstatus != GrabSuccess)
260     {
261       fprintf (stderr, "%s: couldn't grab keyboard!  (%s)\n",
262                blurb(), grab_string(kstatus));
263
264       if (! focus_fuckus)
265         {
266           focus_fuckus = True;
267           nuke_focus (si, screen_no);
268           goto AGAIN;
269         }
270     }
271
272   for (i = 0; i < retries; i++)
273     {
274       XSync (si->dpy, False);
275       mstatus = grab_mouse (si, window, cursor, screen_no);
276       if (mstatus == GrabSuccess)
277         break;
278
279       /* else, wait a second and try to grab again. */
280       sleep (1);
281     }
282
283   if (mstatus != GrabSuccess)
284     fprintf (stderr, "%s: couldn't grab pointer!  (%s)\n",
285              blurb(), grab_string(mstatus));
286
287
288   /* When should we allow blanking to proceed?  The current theory
289      is that a keyboard grab is manditory; a mouse grab is optional.
290
291      - If we don't have a keyboard grab, then we won't be able to
292        read a password to unlock, so the kbd grab is manditory.
293        (We can't conditionalize this on locked_p, because someone
294        might run "xscreensaver-command -lock" at any time.)
295
296      - If we don't have a mouse grab, then we might not see mouse
297        clicks as a signal to unblank -- but we will still see kbd
298        activity, so that's not a disaster.
299    */
300
301   if (kstatus != GrabSuccess)   /* Do not blank without a kbd grab.   */
302     {
303       /* If we didn't get both grabs, release the one we did get. */
304       ungrab_keyboard_and_mouse (si);
305       return False;
306     }
307
308   return True;                  /* Grab is good, go ahead and blank.  */
309 }
310
311
312 int
313 move_mouse_grab (saver_info *si, Window to, Cursor cursor, int to_screen_no)
314 {
315   Window old = si->mouse_grab_window;
316
317   if (old == 0)
318     return grab_mouse (si, to, cursor, to_screen_no);
319   else
320     {
321       saver_preferences *p = &si->prefs;
322       int status;
323
324       XSync (si->dpy, False);
325       XGrabServer (si->dpy);                    /* ############ DANGER! */
326       XSync (si->dpy, False);
327
328       if (p->verbose_p)
329         fprintf(stderr, "%s: grabbing server...\n", blurb());
330
331       ungrab_mouse (si);
332       status = grab_mouse (si, to, cursor, to_screen_no);
333
334       if (status != GrabSuccess)   /* Augh! */
335         {
336           sleep (1);               /* Note dramatic evil of sleeping
337                                       with server grabbed. */
338           XSync (si->dpy, False);
339           status = grab_mouse (si, to, cursor, to_screen_no);
340         }
341
342       if (status != GrabSuccess)   /* Augh!  Try to get the old one back... */
343         grab_mouse (si, old, cursor, to_screen_no);
344
345       XUngrabServer (si->dpy);
346       XSync (si->dpy, False);                   /* ###### (danger over) */
347
348       if (p->verbose_p)
349         fprintf(stderr, "%s: ungrabbing server.\n", blurb());
350
351       return status;
352     }
353 }
354
355
356 /* Prints an error message to stderr and returns True if there is another
357    xscreensaver running already.  Silently returns False otherwise. */
358 Bool
359 ensure_no_screensaver_running (Display *dpy, Screen *screen)
360 {
361   Bool status = 0;
362   int i;
363   Window root = RootWindowOfScreen (screen);
364   Window root2, parent, *kids;
365   unsigned int nkids;
366   XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
367
368   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
369     abort ();
370   if (root != root2)
371     abort ();
372   if (parent)
373     abort ();
374   for (i = 0; i < nkids; i++)
375     {
376       Atom type;
377       int format;
378       unsigned long nitems, bytesafter;
379       unsigned char *version;
380
381       if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
382                               False, XA_STRING, &type, &format, &nitems,
383                               &bytesafter, &version)
384           == Success
385           && type != None)
386         {
387           unsigned char *id;
388           if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
389                                    False, XA_STRING, &type, &format, &nitems,
390                                    &bytesafter, &id)
391               == Success
392               || type == None)
393             id = (unsigned char *) "???";
394
395           fprintf (stderr,
396       "%s: already running on display %s (window 0x%x)\n from process %s.\n",
397                    blurb(), DisplayString (dpy), (int) kids [i],
398                    (char *) id);
399           status = True;
400         }
401
402       else if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
403                                    False, XA_STRING, &type, &format, &nitems,
404                                    &bytesafter, &version)
405                == Success
406                && type != None
407                && !strcmp ((char *) version, "gnome-screensaver"))
408         {
409           fprintf (stderr,
410                  "%s: \"%s\" is already running on display %s (window 0x%x)\n",
411                    blurb(), (char *) version,
412                    DisplayString (dpy), (int) kids [i]);
413           status = True;
414           break;
415         }
416     }
417
418   if (kids) XFree ((char *) kids);
419   XSync (dpy, False);
420   XSetErrorHandler (old_handler);
421   return status;
422 }
423
424
425 \f
426 /* Virtual-root hackery */
427
428 #ifdef _VROOT_H_
429 ERROR!  You must not include vroot.h in this file.
430 #endif
431
432 static void
433 store_vroot_property (Display *dpy, Window win, Window value)
434 {
435 #if 0
436   if (p->verbose_p)
437     fprintf (stderr,
438              "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(), 
439              win,
440              (win == screensaver_window ? "ScreenSaver" :
441               (win == real_vroot ? "VRoot" :
442                (win == real_vroot_value ? "Vroot_value" : "???"))),
443              value,
444              (value == screensaver_window ? "ScreenSaver" :
445               (value == real_vroot ? "VRoot" :
446                (value == real_vroot_value ? "Vroot_value" : "???"))));
447 #endif
448   XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
449                    (unsigned char *) &value, 1);
450 }
451
452 static void
453 remove_vroot_property (Display *dpy, Window win)
454 {
455 #if 0
456   if (p->verbose_p)
457     fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win, 
458              (win == screensaver_window ? "ScreenSaver" :
459               (win == real_vroot ? "VRoot" :
460                (win == real_vroot_value ? "Vroot_value" : "???"))));
461 #endif
462   XDeleteProperty (dpy, win, XA_VROOT);
463 }
464
465
466 static Bool safe_XKillClient (Display *dpy, XID id);
467
468 static void
469 kill_xsetroot_data_1 (Display *dpy, Window window,
470                       Atom prop, const char *atom_name,
471                       Bool verbose_p)
472 {
473   Atom type;
474   int format;
475   unsigned long nitems, bytesafter;
476   unsigned char *dataP = 0;
477
478   /* If the user has been using xv or xsetroot as a screensaver (to display
479      an image on the screensaver window, as a kind of slideshow) then the
480      pixmap and its associated color cells have been put in RetainPermanent
481      CloseDown mode.  Since we're not destroying the xscreensaver window,
482      but merely unmapping it, we need to free these resources or those
483      colormap cells will stay allocated while the screensaver is off.  (We
484      could just delete the screensaver window and recreate it later, but
485      that could cause other problems.)  This code does an atomic read-and-
486      delete of the _XSETROOT_ID property, and if it held a pixmap, then we
487      cause the RetainPermanent resources of the client which created it
488      (and which no longer exists) to be freed.
489
490      Update: it seems that Gnome and KDE do this same trick, but with the
491      properties "ESETROOT_PMAP_ID" and/or "_XROOTPMAP_ID" instead of
492      "_XSETROOT_ID".  So, we'll kill those too.
493    */
494   if (XGetWindowProperty (dpy, window, prop, 0, 1,
495                           True, AnyPropertyType, &type, &format, &nitems, 
496                           &bytesafter, &dataP)
497       == Success
498       && type != None)
499     {
500       Pixmap *pixP = (Pixmap *) dataP;
501       if (pixP && *pixP && type == XA_PIXMAP && format == 32 &&
502           nitems == 1 && bytesafter == 0)
503         {
504           if (verbose_p)
505             fprintf (stderr, "%s: destroying %s data (0x%lX).\n",
506                      blurb(), atom_name, *pixP);
507           safe_XKillClient (dpy, *pixP);
508         }
509       else
510         fprintf (stderr,
511                  "%s: deleted unrecognised %s property: \n"
512                  "\t%lu, %lu; type: %lu, format: %d, "
513                  "nitems: %lu, bytesafter %ld\n",
514                  blurb(), atom_name,
515                  (unsigned long) pixP, (pixP ? *pixP : 0), type,
516                  format, nitems, bytesafter);
517     }
518 }
519
520
521 static void
522 kill_xsetroot_data (Display *dpy, Window w, Bool verbose_p)
523 {
524   kill_xsetroot_data_1 (dpy, w, XA_XSETROOT_ID, "_XSETROOT_ID", verbose_p);
525   kill_xsetroot_data_1 (dpy, w, XA_ESETROOT_PMAP_ID, "ESETROOT_PMAP_ID",
526                         verbose_p);
527   kill_xsetroot_data_1 (dpy, w, XA_XROOTPMAP_ID, "_XROOTPMAP_ID", verbose_p);
528 }
529
530
531 static void
532 save_real_vroot (saver_screen_info *ssi)
533 {
534   saver_info *si = ssi->global;
535   Display *dpy = si->dpy;
536   Screen *screen = ssi->screen;
537   int i;
538   Window root = RootWindowOfScreen (screen);
539   Window root2, parent, *kids;
540   unsigned int nkids;
541   XErrorHandler old_handler;
542
543   /* It's possible that a window might be deleted between our call to
544      XQueryTree() and our call to XGetWindowProperty().  Don't die if
545      that happens (but just ignore that window, it's not the one we're
546      interested in anyway.)
547    */
548   XSync (dpy, False);
549   old_handler = XSetErrorHandler (BadWindow_ehandler);
550   XSync (dpy, False);
551
552   ssi->real_vroot = 0;
553   ssi->real_vroot_value = 0;
554   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
555     abort ();
556   if (root != root2)
557     abort ();
558   if (parent)
559     abort ();
560   for (i = 0; i < nkids; i++)
561     {
562       Atom type;
563       int format;
564       unsigned long nitems, bytesafter;
565       unsigned char *dataP = 0;
566       Window *vrootP;
567       int j;
568
569       /* Skip this window if it is the xscreensaver window of any other
570          screen (this can happen in the Xinerama case.)
571        */
572       for (j = 0; j < si->nscreens; j++)
573         {
574           saver_screen_info *ssi2 = &si->screens[j];
575           if (kids[i] == ssi2->screensaver_window)
576             goto SKIP;
577         }
578
579       if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
580                               &type, &format, &nitems, &bytesafter,
581                               &dataP)
582           != Success)
583         continue;
584       if (! dataP)
585         continue;
586
587       vrootP = (Window *) dataP;
588       if (ssi->real_vroot)
589         {
590           if (*vrootP == ssi->screensaver_window) abort ();
591           fprintf (stderr,
592             "%s: more than one virtual root window found (0x%x and 0x%x).\n",
593                    blurb(), (int) ssi->real_vroot, (int) kids [i]);
594           exit (1);
595         }
596       ssi->real_vroot = kids [i];
597       ssi->real_vroot_value = *vrootP;
598     SKIP:
599       ;
600     }
601
602   XSync (dpy, False);
603   XSetErrorHandler (old_handler);
604   XSync (dpy, False);
605
606   if (ssi->real_vroot)
607     {
608       remove_vroot_property (si->dpy, ssi->real_vroot);
609       XSync (dpy, False);
610     }
611
612   XFree ((char *) kids);
613 }
614
615
616 static Bool
617 restore_real_vroot_1 (saver_screen_info *ssi)
618 {
619   saver_info *si = ssi->global;
620   saver_preferences *p = &si->prefs;
621   if (p->verbose_p && ssi->real_vroot)
622     fprintf (stderr,
623              "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
624              blurb(), (unsigned long) ssi->real_vroot);
625   if (ssi->screensaver_window)
626     remove_vroot_property (si->dpy, ssi->screensaver_window);
627   if (ssi->real_vroot)
628     {
629       store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value);
630       ssi->real_vroot = 0;
631       ssi->real_vroot_value = 0;
632       /* make sure the property change gets there before this process
633          terminates!  We might be doing this because we have intercepted
634          SIGTERM or something. */
635       XSync (si->dpy, False);
636       return True;
637     }
638   return False;
639 }
640
641 Bool
642 restore_real_vroot (saver_info *si)
643 {
644   int i;
645   Bool did_any = False;
646   for (i = 0; i < si->nscreens; i++)
647     {
648       saver_screen_info *ssi = &si->screens[i];
649       if (restore_real_vroot_1 (ssi))
650         did_any = True;
651     }
652   return did_any;
653 }
654
655 \f
656 /* Signal hackery to ensure that the vroot doesn't get left in an 
657    inconsistent state
658  */
659
660 const char *
661 signal_name(int signal)
662 {
663   switch (signal) {
664   case SIGHUP:    return "SIGHUP";
665   case SIGINT:    return "SIGINT";
666   case SIGQUIT:   return "SIGQUIT";
667   case SIGILL:    return "SIGILL";
668   case SIGTRAP:   return "SIGTRAP";
669 #ifdef SIGABRT
670   case SIGABRT:   return "SIGABRT";
671 #endif
672   case SIGFPE:    return "SIGFPE";
673   case SIGKILL:   return "SIGKILL";
674   case SIGBUS:    return "SIGBUS";
675   case SIGSEGV:   return "SIGSEGV";
676   case SIGPIPE:   return "SIGPIPE";
677   case SIGALRM:   return "SIGALRM";
678   case SIGTERM:   return "SIGTERM";
679 #ifdef SIGSTOP
680   case SIGSTOP:   return "SIGSTOP";
681 #endif
682 #ifdef SIGCONT
683   case SIGCONT:   return "SIGCONT";
684 #endif
685 #ifdef SIGUSR1
686   case SIGUSR1:   return "SIGUSR1";
687 #endif
688 #ifdef SIGUSR2
689   case SIGUSR2:   return "SIGUSR2";
690 #endif
691 #ifdef SIGEMT
692   case SIGEMT:    return "SIGEMT";
693 #endif
694 #ifdef SIGSYS
695   case SIGSYS:    return "SIGSYS";
696 #endif
697 #ifdef SIGCHLD
698   case SIGCHLD:   return "SIGCHLD";
699 #endif
700 #ifdef SIGPWR
701   case SIGPWR:    return "SIGPWR";
702 #endif
703 #ifdef SIGWINCH
704   case SIGWINCH:  return "SIGWINCH";
705 #endif
706 #ifdef SIGURG
707   case SIGURG:    return "SIGURG";
708 #endif
709 #ifdef SIGIO
710   case SIGIO:     return "SIGIO";
711 #endif
712 #ifdef SIGVTALRM
713   case SIGVTALRM: return "SIGVTALRM";
714 #endif
715 #ifdef SIGXCPU
716   case SIGXCPU:   return "SIGXCPU";
717 #endif
718 #ifdef SIGXFSZ
719   case SIGXFSZ:   return "SIGXFSZ";
720 #endif
721 #ifdef SIGDANGER
722   case SIGDANGER: return "SIGDANGER";
723 #endif
724   default:
725     {
726       static char buf[50];
727       sprintf(buf, "signal %d\n", signal);
728       return buf;
729     }
730   }
731 }
732
733
734
735 static RETSIGTYPE
736 restore_real_vroot_handler (int sig)
737 {
738   saver_info *si = global_si_kludge;    /* I hate C so much... */
739
740   signal (sig, SIG_DFL);
741   if (restore_real_vroot (si))
742     fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
743              blurb(), signal_name(sig));
744   kill (getpid (), sig);
745 }
746
747 static void
748 catch_signal (saver_info *si, int sig, RETSIGTYPE (*handler) (int))
749 {
750 # ifdef HAVE_SIGACTION
751
752   struct sigaction a;
753   a.sa_handler = handler;
754   sigemptyset (&a.sa_mask);
755   a.sa_flags = 0;
756
757   /* On Linux 2.4.9 (at least) we need to tell the kernel to not mask delivery
758      of this signal from inside its handler, or else when we execvp() the
759      process again, it starts up with SIGHUP blocked, meaning that killing
760      it with -HUP only works *once*.  You'd think that execvp() would reset
761      all the signal masks, but it doesn't.
762    */
763 #  if defined(SA_NOMASK)
764   a.sa_flags |= SA_NOMASK;
765 #  elif defined(SA_NODEFER)
766   a.sa_flags |= SA_NODEFER;
767 #  endif
768
769   if (sigaction (sig, &a, 0) < 0)
770 # else  /* !HAVE_SIGACTION */
771   if (((long) signal (sig, handler)) == -1L)
772 # endif /* !HAVE_SIGACTION */
773     {
774       char buf [255];
775       sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
776       perror (buf);
777       saver_exit (si, 1, 0);
778     }
779 }
780
781 static RETSIGTYPE saver_sighup_handler (int sig);
782
783 void
784 handle_signals (saver_info *si)
785 {
786   catch_signal (si, SIGHUP, saver_sighup_handler);
787
788   catch_signal (si, SIGINT,  restore_real_vroot_handler);
789   catch_signal (si, SIGQUIT, restore_real_vroot_handler);
790   catch_signal (si, SIGILL,  restore_real_vroot_handler);
791   catch_signal (si, SIGTRAP, restore_real_vroot_handler);
792 #ifdef SIGIOT
793   catch_signal (si, SIGIOT,  restore_real_vroot_handler);
794 #endif
795   catch_signal (si, SIGABRT, restore_real_vroot_handler);
796 #ifdef SIGEMT
797   catch_signal (si, SIGEMT,  restore_real_vroot_handler);
798 #endif
799   catch_signal (si, SIGFPE,  restore_real_vroot_handler);
800   catch_signal (si, SIGBUS,  restore_real_vroot_handler);
801   catch_signal (si, SIGSEGV, restore_real_vroot_handler);
802 #ifdef SIGSYS
803   catch_signal (si, SIGSYS,  restore_real_vroot_handler);
804 #endif
805   catch_signal (si, SIGTERM, restore_real_vroot_handler);
806 #ifdef SIGXCPU
807   catch_signal (si, SIGXCPU, restore_real_vroot_handler);
808 #endif
809 #ifdef SIGXFSZ
810   catch_signal (si, SIGXFSZ, restore_real_vroot_handler);
811 #endif
812 #ifdef SIGDANGER
813   catch_signal (si, SIGDANGER, restore_real_vroot_handler);
814 #endif
815 }
816
817
818 static RETSIGTYPE
819 saver_sighup_handler (int sig)
820 {
821   saver_info *si = global_si_kludge;    /* I hate C so much... */
822
823   /* Re-establish SIGHUP handler */
824   catch_signal (si, SIGHUP, saver_sighup_handler);
825
826   fprintf (stderr, "%s: %s received: restarting...\n",
827            blurb(), signal_name(sig));
828
829   if (si->screen_blanked_p)
830     {
831       int i;
832       for (i = 0; i < si->nscreens; i++)
833         kill_screenhack (&si->screens[i]);
834       unblank_screen (si);
835       XSync (si->dpy, False);
836     }
837
838   restart_process (si);   /* Does not return */
839   abort ();
840 }
841
842
843
844 void
845 saver_exit (saver_info *si, int status, const char *dump_core_reason)
846 {
847   saver_preferences *p = &si->prefs;
848   static Bool exiting = False;
849   Bool bugp;
850   Bool vrs;
851
852   if (exiting)
853     exit(status);
854
855   exiting = True;
856   
857   vrs = restore_real_vroot (si);
858   emergency_kill_subproc (si);
859   shutdown_stderr (si);
860
861   if (p->verbose_p && vrs)
862     fprintf (real_stderr, "%s: old vroot restored.\n", blurb());
863
864   fflush(real_stdout);
865
866 #ifdef VMS      /* on VMS, 1 is the "normal" exit code instead of 0. */
867   if (status == 0) status = 1;
868   else if (status == 1) status = -1;
869 #endif
870
871   bugp = !!dump_core_reason;
872
873   if (si->prefs.debug_p && !dump_core_reason)
874     dump_core_reason = "because of -debug";
875
876   if (dump_core_reason)
877     {
878       /* Note that the Linux man page for setuid() says If uid is
879          different from the old effective uid, the process will be
880          forbidden from leaving core dumps.
881       */
882       char cwd[4096]; /* should really be PATH_MAX, but who cares. */
883       cwd[0] = 0;
884       fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
885               dump_core_reason);
886
887       if (bugp)
888         fprintf(real_stderr,
889                 "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
890                 "\t\t\tfor bug reporting information.\n\n",
891                 blurb());
892
893 # if defined(HAVE_GETCWD)
894       if (!getcwd (cwd, sizeof(cwd)))
895 # elif defined(HAVE_GETWD)
896       if (!getwd (cwd))
897 # endif
898         strcpy(cwd, "unknown.");
899
900       fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
901       describe_uids (si, real_stderr);
902
903       /* Do this to drop a core file, so that we can get a stack trace. */
904       abort();
905     }
906
907   exit (status);
908 }
909
910 \f
911 /* Managing the actual screensaver window */
912
913 Bool
914 window_exists_p (Display *dpy, Window window)
915 {
916   XErrorHandler old_handler;
917   XWindowAttributes xgwa;
918   xgwa.screen = 0;
919   old_handler = XSetErrorHandler (BadWindow_ehandler);
920   XGetWindowAttributes (dpy, window, &xgwa);
921   XSync (dpy, False);
922   XSetErrorHandler (old_handler);
923   return (xgwa.screen != 0);
924 }
925
926 static void
927 store_saver_id (saver_screen_info *ssi)
928 {
929   XClassHint class_hints;
930   saver_info *si = ssi->global;
931   unsigned long pid = (unsigned long) getpid ();
932   char buf[20];
933   struct passwd *p = getpwuid (getuid ());
934   const char *name, *host;
935   char *id;
936
937   /* First store the name and class on the window.
938    */
939   class_hints.res_name = progname;
940   class_hints.res_class = progclass;
941   XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
942   XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
943
944   /* Then store the xscreensaver version number.
945    */
946   XChangeProperty (si->dpy, ssi->screensaver_window,
947                    XA_SCREENSAVER_VERSION,
948                    XA_STRING, 8, PropModeReplace,
949                    (unsigned char *) si->version,
950                    strlen (si->version));
951
952   /* Now store the XSCREENSAVER_ID property, that says what user and host
953      xscreensaver is running as.
954    */
955
956   if (p && p->pw_name && *p->pw_name)
957     name = p->pw_name;
958   else if (p)
959     {
960       sprintf (buf, "%lu", (unsigned long) p->pw_uid);
961       name = buf;
962     }
963   else
964     name = "???";
965
966 # if defined(HAVE_UNAME)
967   {
968     struct utsname uts;
969     if (uname (&uts) < 0)
970       host = "???";
971     else
972       host = uts.nodename;
973   }
974 # elif defined(VMS)
975   host = getenv("SYS$NODE");
976 # else  /* !HAVE_UNAME && !VMS */
977   host = "???";
978 # endif /* !HAVE_UNAME && !VMS */
979
980   id = (char *) malloc (strlen(name) + strlen(host) + 50);
981   sprintf (id, "%lu (%s@%s)", pid, name, host);
982
983   XChangeProperty (si->dpy, ssi->screensaver_window,
984                    XA_SCREENSAVER_ID, XA_STRING,
985                    8, PropModeReplace,
986                    (unsigned char *) id, strlen (id));
987   free (id);
988 }
989
990
991 void
992 store_saver_status (saver_info *si)
993 {
994   PROP32 *status;
995   int size = si->nscreens + 2;
996   int i;
997
998   status = (PROP32 *) calloc (size, sizeof(PROP32));
999
1000   status[0] = (PROP32) (si->screen_blanked_p
1001                         ? (si->locked_p ? XA_LOCK : XA_BLANK)
1002                         : 0);
1003   status[1] = (PROP32) si->blank_time;
1004
1005   for (i = 0; i < si->nscreens; i++)
1006     {
1007       saver_screen_info *ssi = &si->screens[i];
1008       status [2 + i] = ssi->current_hack + 1;
1009     }
1010
1011   XChangeProperty (si->dpy,
1012                    RootWindow (si->dpy, 0),  /* always screen #0 */
1013                    XA_SCREENSAVER_STATUS,
1014                    XA_INTEGER, 32, PropModeReplace,
1015                    (unsigned char *) status, size);
1016   free (status);
1017 }
1018
1019
1020 static Bool error_handler_hit_p = False;
1021
1022 static int
1023 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1024 {
1025   error_handler_hit_p = True;
1026   return 0;
1027 }
1028
1029
1030 /* Returns True if successful, False if an X error occurred.
1031    We need this because other programs might have done things to
1032    our window that will cause XChangeWindowAttributes() to fail:
1033    if that happens, we give up, destroy the window, and re-create
1034    it.
1035  */
1036 static Bool
1037 safe_XChangeWindowAttributes (Display *dpy, Window window,
1038                               unsigned long mask,
1039                               XSetWindowAttributes *attrs)
1040 {
1041   XErrorHandler old_handler;
1042   XSync (dpy, False);
1043   error_handler_hit_p = False;
1044   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1045
1046   XChangeWindowAttributes (dpy, window, mask, attrs);
1047
1048   XSync (dpy, False);
1049   XSetErrorHandler (old_handler);
1050   XSync (dpy, False);
1051
1052   return (!error_handler_hit_p);
1053 }
1054
1055
1056 /* This might not be necessary, but just in case. */
1057 static Bool
1058 safe_XConfigureWindow (Display *dpy, Window window,
1059                        unsigned long mask, XWindowChanges *changes)
1060 {
1061   XErrorHandler old_handler;
1062   XSync (dpy, False);
1063   error_handler_hit_p = False;
1064   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1065
1066   XConfigureWindow (dpy, window, mask, changes);
1067
1068   XSync (dpy, False);
1069   XSetErrorHandler (old_handler);
1070   XSync (dpy, False);
1071
1072   return (!error_handler_hit_p);
1073 }
1074
1075 /* This might not be necessary, but just in case. */
1076 static Bool
1077 safe_XDestroyWindow (Display *dpy, Window window)
1078 {
1079   XErrorHandler old_handler;
1080   XSync (dpy, False);
1081   error_handler_hit_p = False;
1082   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1083
1084   XDestroyWindow (dpy, window);
1085
1086   XSync (dpy, False);
1087   XSetErrorHandler (old_handler);
1088   XSync (dpy, False);
1089
1090   return (!error_handler_hit_p);
1091 }
1092
1093
1094 static Bool
1095 safe_XKillClient (Display *dpy, XID id)
1096 {
1097   XErrorHandler old_handler;
1098   XSync (dpy, False);
1099   error_handler_hit_p = False;
1100   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1101
1102   XKillClient (dpy, id);
1103
1104   XSync (dpy, False);
1105   XSetErrorHandler (old_handler);
1106   XSync (dpy, False);
1107
1108   return (!error_handler_hit_p);
1109 }
1110
1111
1112 #ifdef HAVE_XF86VMODE
1113 Bool
1114 safe_XF86VidModeGetViewPort (Display *dpy, int screen, int *xP, int *yP)
1115 {
1116   Bool result;
1117   XErrorHandler old_handler;
1118   XSync (dpy, False);
1119   error_handler_hit_p = False;
1120   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1121
1122   result = XF86VidModeGetViewPort (dpy, screen, xP, yP);
1123
1124   XSync (dpy, False);
1125   XSetErrorHandler (old_handler);
1126   XSync (dpy, False);
1127
1128   return (error_handler_hit_p
1129           ? False
1130           : result);
1131 }
1132
1133 /* There is no "safe_XF86VidModeGetModeLine" because it fails with an
1134    untrappable I/O error instead of an X error -- so one must call
1135    safe_XF86VidModeGetViewPort first, and assume that both have the
1136    same error condition.  Thank you XFree, may I have another.
1137  */
1138
1139 #endif /* HAVE_XF86VMODE */
1140
1141
1142 static void
1143 initialize_screensaver_window_1 (saver_screen_info *ssi)
1144 {
1145   saver_info *si = ssi->global;
1146   saver_preferences *p = &si->prefs;
1147   Bool install_cmap_p = ssi->install_cmap_p;   /* not p->install_cmap_p */
1148
1149   /* This resets the screensaver window as fully as possible, since there's
1150      no way of knowing what some random client may have done to us in the
1151      meantime.  We could just destroy and recreate the window, but that has
1152      its own set of problems...
1153    */
1154   XColor black;
1155   XSetWindowAttributes attrs;
1156   unsigned long attrmask;
1157   static Bool printed_visual_info = False;  /* only print the message once. */
1158   Window horked_window = 0;
1159
1160   black.red = black.green = black.blue = 0;
1161
1162   if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
1163     ssi->cmap = 0;
1164
1165   if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
1166     /* It's not the default visual, so we have no choice but to install. */
1167     install_cmap_p = True;
1168
1169   if (install_cmap_p)
1170     {
1171       if (! ssi->cmap)
1172         {
1173           ssi->cmap = XCreateColormap (si->dpy,
1174                                        RootWindowOfScreen (ssi->screen),
1175                                       ssi->current_visual, AllocNone);
1176           if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
1177           ssi->black_pixel = black.pixel;
1178         }
1179     }
1180   else
1181     {
1182       Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
1183       if (ssi->cmap)
1184         {
1185           XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
1186           if (ssi->cmap != ssi->demo_cmap &&
1187               ssi->cmap != def_cmap)
1188             XFreeColormap (si->dpy, ssi->cmap);
1189         }
1190       ssi->cmap = def_cmap;
1191       ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
1192     }
1193
1194   attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
1195               CWBackPixel | CWBackingPixel | CWBorderPixel);
1196   attrs.override_redirect = True;
1197
1198   /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
1199      actually be reading these events during normal operation; but we still
1200      need to see Button events for demo-mode to work properly.
1201    */
1202   attrs.event_mask = (KeyPressMask | KeyReleaseMask |
1203                       ButtonPressMask | ButtonReleaseMask |
1204                       PointerMotionMask);
1205
1206   attrs.backing_store = NotUseful;
1207   attrs.colormap = ssi->cmap;
1208   attrs.background_pixel = ssi->black_pixel;
1209   attrs.backing_pixel = ssi->black_pixel;
1210   attrs.border_pixel = ssi->black_pixel;
1211
1212   if (!p->verbose_p || printed_visual_info)
1213     ;
1214   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
1215     {
1216       fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number);
1217       describe_visual (stderr, ssi->screen, ssi->current_visual,
1218                        install_cmap_p);
1219     }
1220   else
1221     {
1222       fprintf (stderr, "%s: using visual:   ", blurb());
1223       describe_visual (stderr, ssi->screen, ssi->current_visual,
1224                        install_cmap_p);
1225       fprintf (stderr, "%s: default visual: ", blurb());
1226       describe_visual (stderr, ssi->screen,
1227                        DefaultVisualOfScreen (ssi->screen),
1228                        ssi->install_cmap_p);
1229     }
1230   printed_visual_info = True;
1231
1232 #ifdef HAVE_MIT_SAVER_EXTENSION
1233   if (si->using_mit_saver_extension)
1234     {
1235       XScreenSaverInfo *info;
1236       Window root = RootWindowOfScreen (ssi->screen);
1237
1238 #if 0
1239       /* This call sets the server screensaver timeouts to what we think
1240          they should be (based on the resources and args xscreensaver was
1241          started with.)  It's important that we do this to sync back up
1242          with the server - if we have turned on prematurely, as by an
1243          ACTIVATE ClientMessage, then the server may decide to activate
1244          the screensaver while it's already active.  That's ok for us,
1245          since we would know to ignore that ScreenSaverActivate event,
1246          but a side effect of this would be that the server would map its
1247          saver window (which we then hide again right away) meaning that
1248          the bits currently on the screen get blown away.  Ugly. */
1249
1250       /* #### Ok, that doesn't work - when we tell the server that the
1251          screensaver is "off" it sends us a Deactivate event, which is
1252          sensible... but causes the saver to never come on.  Hmm. */
1253       disable_builtin_screensaver (si, True);
1254 #endif /* 0 */
1255
1256 #if 0
1257       /* #### The MIT-SCREEN-SAVER extension gives us access to the
1258          window that the server itself uses for saving the screen.
1259          However, using this window in any way, in particular, calling
1260          XScreenSaverSetAttributes() as below, tends to make the X server
1261          crash.  So fuck it, let's try and get along without using it...
1262
1263          It's also inconvenient to use this window because it doesn't
1264          always exist (though the ID is constant.)  So to use this
1265          window, we'd have to reimplement the ACTIVATE ClientMessage to
1266          tell the *server* to tell *us* to turn on, to cause the window
1267          to get created at the right time.  Gag.  */
1268       XScreenSaverSetAttributes (si->dpy, root,
1269                                  0, 0, width, height, 0,
1270                                  current_depth, InputOutput, visual,
1271                                  attrmask, &attrs);
1272       XSync (si->dpy, False);
1273 #endif /* 0 */
1274
1275       info = XScreenSaverAllocInfo ();
1276       XScreenSaverQueryInfo (si->dpy, root, info);
1277       ssi->server_mit_saver_window = info->window;
1278       if (! ssi->server_mit_saver_window) abort ();
1279       XFree (info);
1280     }
1281 #endif /* HAVE_MIT_SAVER_EXTENSION */
1282
1283   if (ssi->screensaver_window)
1284     {
1285       XWindowChanges changes;
1286       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1287       changes.x = ssi->x;
1288       changes.y = ssi->y;
1289       changes.width = ssi->width;
1290       changes.height = ssi->height;
1291       changes.border_width = 0;
1292
1293       if (! (safe_XConfigureWindow (si->dpy, ssi->screensaver_window,
1294                                   changesmask, &changes) &&
1295              safe_XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
1296                                            attrmask, &attrs)))
1297         {
1298           horked_window = ssi->screensaver_window;
1299           ssi->screensaver_window = 0;
1300         }
1301     }
1302
1303   if (!ssi->screensaver_window)
1304     {
1305       ssi->screensaver_window =
1306         XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
1307                        ssi->x, ssi->y, ssi->width, ssi->height,
1308                        0, ssi->current_depth, InputOutput,
1309                        ssi->current_visual, attrmask, &attrs);
1310       reset_stderr (ssi);
1311
1312       if (horked_window)
1313         {
1314           fprintf (stderr,
1315             "%s: someone horked our saver window (0x%lx)!  Recreating it...\n",
1316                    blurb(), (unsigned long) horked_window);
1317           maybe_transfer_grabs (ssi, horked_window, ssi->screensaver_window,
1318                                 ssi->number);
1319           safe_XDestroyWindow (si->dpy, horked_window);
1320           horked_window = 0;
1321         }
1322
1323       if (p->verbose_p)
1324         fprintf (stderr, "%s: %d: saver window is 0x%lx.\n",
1325                  blurb(), ssi->number,
1326                  (unsigned long) ssi->screensaver_window);
1327     }
1328
1329   store_saver_id (ssi);       /* store window name and IDs */
1330
1331   if (!ssi->cursor)
1332     {
1333       Pixmap bit;
1334       bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
1335                                          "\000", 1, 1,
1336                                          BlackPixelOfScreen (ssi->screen),
1337                                          BlackPixelOfScreen (ssi->screen),
1338                                          1);
1339       ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
1340                                          0, 0);
1341       XFreePixmap (si->dpy, bit);
1342     }
1343
1344   XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
1345
1346   if (si->demoing_p)
1347     XUndefineCursor (si->dpy, ssi->screensaver_window);
1348   else
1349     XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1350 }
1351
1352 void
1353 initialize_screensaver_window (saver_info *si)
1354 {
1355   int i;
1356   for (i = 0; i < si->nscreens; i++)
1357     initialize_screensaver_window_1 (&si->screens[i]);
1358 }
1359
1360
1361 /* Called when the RANDR (Resize and Rotate) extension tells us that
1362    the size of the screen has changed while the screen was blanked.
1363    Call update_screen_layout() first, then call this to synchronize
1364    the size of the saver windows to the new sizes of the screens.
1365  */
1366 void
1367 resize_screensaver_window (saver_info *si)
1368 {
1369   saver_preferences *p = &si->prefs;
1370   int i;
1371
1372   for (i = 0; i < si->nscreens; i++)
1373     {
1374       saver_screen_info *ssi = &si->screens[i];
1375       XWindowAttributes xgwa;
1376
1377       /* Make sure a window exists -- it might not if a monitor was just
1378          added for the first time.
1379        */
1380       if (! ssi->screensaver_window)
1381         {
1382           initialize_screensaver_window_1 (ssi);
1383           if (p->verbose_p)
1384             fprintf (stderr,
1385                      "%s: %d: newly added window 0x%lx %dx%d+%d+%d\n",
1386                      blurb(), i, (unsigned long) ssi->screensaver_window,
1387                      ssi->width, ssi->height, ssi->x, ssi->y);
1388         }
1389
1390       /* Make sure the window is the right size -- it might not be if
1391          the monitor changed resolution, or if a badly-behaved hack
1392          screwed with it.
1393        */
1394       XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa);
1395       if (xgwa.x      != ssi->x ||
1396           xgwa.y      != ssi->y ||
1397           xgwa.width  != ssi->width ||
1398           xgwa.height != ssi->height)
1399         {
1400           XWindowChanges changes;
1401           unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1402           changes.x      = ssi->x;
1403           changes.y      = ssi->y;
1404           changes.width  = ssi->width;
1405           changes.height = ssi->height;
1406           changes.border_width = 0;
1407
1408           if (p->verbose_p)
1409             fprintf (stderr,
1410                      "%s: %d: resize 0x%lx from %dx%d+%d+%d to %dx%d+%d+%d\n",
1411                      blurb(), i, (unsigned long) ssi->screensaver_window,
1412                      xgwa.width, xgwa.height, xgwa.x, xgwa.y,
1413                      ssi->width, ssi->height, ssi->x, ssi->y);
1414
1415           if (! safe_XConfigureWindow (si->dpy, ssi->screensaver_window,
1416                                        changesmask, &changes))
1417             fprintf (stderr, "%s: %d: someone horked our saver window"
1418                      " (0x%lx)!  Unable to resize it!\n",
1419                      blurb(), i, (unsigned long) ssi->screensaver_window);
1420         }
1421
1422       /* Now (if blanked) make sure that it's mapped and running a hack --
1423          it might not be if we just added it.  (We also might be re-using
1424          an old window that existed for a previous monitor that was
1425          removed and re-added.)
1426
1427          Note that spawn_screenhack() calls select_visual() which may destroy
1428          and re-create the window via initialize_screensaver_window_1().
1429        */
1430       if (si->screen_blanked_p)
1431         {
1432           if (ssi->cmap)
1433             XInstallColormap (si->dpy, ssi->cmap);
1434           XMapRaised (si->dpy, ssi->screensaver_window);
1435           if (! ssi->pid)
1436             spawn_screenhack (ssi);
1437
1438           /* Make sure the act of adding a screen doesn't present as 
1439              pointer motion (and thus cause an unblank). */
1440           {
1441             Window root, child;
1442             int x, y;
1443             unsigned int mask;
1444             XQueryPointer (si->dpy, ssi->screensaver_window, &root, &child,
1445                            &ssi->poll_mouse_last_root_x,
1446                            &ssi->poll_mouse_last_root_y,
1447                            &x, &y, &mask);
1448           }
1449         }
1450     }
1451
1452   /* Kill off any savers running on no-longer-extant monitors.
1453    */
1454   for (; i < si->ssi_count; i++)
1455     {
1456       saver_screen_info *ssi = &si->screens[i];
1457       if (ssi->pid)
1458         kill_screenhack (ssi);
1459       if (ssi->screensaver_window)
1460         {
1461           XUnmapWindow (si->dpy, ssi->screensaver_window);
1462           restore_real_vroot_1 (ssi);
1463         }
1464     }
1465 }
1466
1467
1468 void 
1469 raise_window (saver_info *si,
1470               Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
1471 {
1472   saver_preferences *p = &si->prefs;
1473   int i;
1474
1475   if (si->demoing_p)
1476     inhibit_fade = True;
1477
1478   if (si->emergency_lock_p)
1479     inhibit_fade = True;
1480
1481   if (!dont_clear)
1482     initialize_screensaver_window (si);
1483
1484   reset_watchdog_timer (si, True);
1485
1486   if (p->fade_p && si->fading_possible_p && !inhibit_fade)
1487     {
1488       Window *current_windows = (Window *)
1489         calloc(sizeof(Window), si->nscreens);
1490       Colormap *current_maps = (Colormap *)
1491         calloc(sizeof(Colormap), si->nscreens);
1492
1493       for (i = 0; i < si->nscreens; i++)
1494         {
1495           saver_screen_info *ssi = &si->screens[i];
1496           current_windows[i] = ssi->screensaver_window;
1497           current_maps[i] = (between_hacks_p
1498                              ? ssi->cmap
1499                              : DefaultColormapOfScreen (ssi->screen));
1500           /* Ensure that the default background of the window is really black,
1501              not a pixmap or something.  (This does not clear the window.) */
1502           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1503                                 ssi->black_pixel);
1504         }
1505
1506       if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1507
1508       XGrabServer (si->dpy);                    /* ############ DANGER! */
1509
1510       /* Clear the stderr layer on each screen.
1511        */
1512       if (!dont_clear)
1513         for (i = 0; i < si->nscreens; i++)
1514           {
1515             saver_screen_info *ssi = &si->screens[i];
1516             if (ssi->stderr_overlay_window)
1517               /* Do this before the fade, since the stderr cmap won't fade
1518                  even if we uninstall it (beats me...) */
1519               clear_stderr (ssi);
1520           }
1521
1522       /* Note!  The server is grabbed, and this will take several seconds
1523          to complete! */
1524       fade_screens (si->dpy, current_maps,
1525                     current_windows, si->nscreens,
1526                     p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1527
1528       free(current_maps);
1529       free(current_windows);
1530       current_maps = 0;
1531       current_windows = 0;
1532
1533       if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1534
1535 #ifdef HAVE_MIT_SAVER_EXTENSION
1536       for (i = 0; i < si->nscreens; i++)
1537         {
1538           saver_screen_info *ssi = &si->screens[i];
1539           if (ssi->server_mit_saver_window &&
1540               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1541             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1542         }
1543 #endif /* HAVE_MIT_SAVER_EXTENSION */
1544
1545       XUngrabServer (si->dpy);
1546       XSync (si->dpy, False);                   /* ###### (danger over) */
1547     }
1548   else
1549     {
1550       for (i = 0; i < si->nscreens; i++)
1551         {
1552           saver_screen_info *ssi = &si->screens[i];
1553           if (!dont_clear)
1554             XClearWindow (si->dpy, ssi->screensaver_window);
1555           if (!dont_clear || ssi->stderr_overlay_window)
1556             clear_stderr (ssi);
1557           XMapRaised (si->dpy, ssi->screensaver_window);
1558 #ifdef HAVE_MIT_SAVER_EXTENSION
1559           if (ssi->server_mit_saver_window &&
1560               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1561             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1562 #endif /* HAVE_MIT_SAVER_EXTENSION */
1563         }
1564     }
1565
1566   for (i = 0; i < si->nscreens; i++)
1567     {
1568       saver_screen_info *ssi = &si->screens[i];
1569       if (ssi->cmap)
1570         XInstallColormap (si->dpy, ssi->cmap);
1571     }
1572 }
1573
1574
1575 int
1576 mouse_screen (saver_info *si)
1577 {
1578   saver_preferences *p = &si->prefs;
1579   Window pointer_root, pointer_child;
1580   int root_x, root_y, win_x, win_y;
1581   unsigned int mask;
1582   int i;
1583
1584   if (si->nscreens == 1)
1585     return 0;
1586
1587   for (i = 0; i < si->nscreens; i++)
1588     {
1589       saver_screen_info *ssi = &si->screens[i];
1590       if (XQueryPointer (si->dpy, RootWindowOfScreen (ssi->screen),
1591                          &pointer_root, &pointer_child,
1592                          &root_x, &root_y, &win_x, &win_y, &mask) &&
1593           root_x >= ssi->x &&
1594           root_y >= ssi->y &&
1595           root_x <  ssi->x + ssi->width &&
1596           root_y <  ssi->y + ssi->height)
1597         {
1598           if (p->verbose_p)
1599             fprintf (stderr, "%s: mouse is on screen %d of %d\n",
1600                      blurb(), i, si->nscreens);
1601           return i;
1602         }
1603     }
1604
1605   /* couldn't figure out where the mouse is?  Oh well. */
1606   return 0;
1607 }
1608
1609
1610 Bool
1611 blank_screen (saver_info *si)
1612 {
1613   int i;
1614   Bool ok;
1615   Window w;
1616   int mscreen;
1617
1618   /* Note: we do our grabs on the root window, not on the screensaver window.
1619      If we grabbed on the saver window, then the demo mode and lock dialog
1620      boxes wouldn't get any events.
1621
1622      By "the root window", we mean "the root window that contains the mouse."
1623      We use to always grab the mouse on screen 0, but that has the effect of
1624      moving the mouse to screen 0 from whichever screen it was on, on
1625      multi-head systems.
1626    */
1627   mscreen = mouse_screen (si);
1628   w = RootWindowOfScreen(si->screens[mscreen].screen);
1629   ok = grab_keyboard_and_mouse (si, w,
1630                                 (si->demoing_p ? 0 : si->screens[0].cursor),
1631                                 mscreen);
1632
1633
1634 # if 0
1635   if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1636     /* If we're using a server extension, then failure to get a grab is
1637        not a big deal -- even without the grab, we will still be able
1638        to un-blank when there is user activity, since the server will
1639        tell us. */
1640     /* #### No, that's not true: if we don't have a keyboard grab,
1641             then we can't read passwords to unlock.
1642      */
1643     ok = True;
1644 # endif /* 0 */
1645
1646   if (!ok)
1647     return False;
1648
1649   for (i = 0; i < si->nscreens; i++)
1650     {
1651       saver_screen_info *ssi = &si->screens[i];
1652       if (ssi->real_screen_p)
1653         save_real_vroot (ssi);
1654       store_vroot_property (si->dpy,
1655                             ssi->screensaver_window,
1656                             ssi->screensaver_window);
1657
1658 #ifdef HAVE_XF86VMODE
1659       {
1660         int ev, er;
1661         if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
1662             !safe_XF86VidModeGetViewPort (si->dpy, i,
1663                                           &ssi->blank_vp_x,
1664                                           &ssi->blank_vp_y))
1665           ssi->blank_vp_x = ssi->blank_vp_y = -1;
1666       }
1667 #endif /* HAVE_XF86VMODE */
1668     }
1669
1670   raise_window (si, False, False, False);
1671
1672   si->screen_blanked_p = True;
1673   si->blank_time = time ((time_t) 0);
1674   si->last_wall_clock_time = 0;
1675
1676   store_saver_status (si);  /* store blank time */
1677
1678   return True;
1679 }
1680
1681
1682 void
1683 unblank_screen (saver_info *si)
1684 {
1685   saver_preferences *p = &si->prefs;
1686   Bool unfade_p = (si->fading_possible_p && p->unfade_p);
1687   int i;
1688
1689   monitor_power_on (si);
1690   reset_watchdog_timer (si, False);
1691
1692   if (si->demoing_p)
1693     unfade_p = False;
1694
1695   if (unfade_p)
1696     {
1697       Window *current_windows = (Window *)
1698         calloc(sizeof(Window), si->nscreens);
1699
1700       for (i = 0; i < si->nscreens; i++)
1701         {
1702           saver_screen_info *ssi = &si->screens[i];
1703           current_windows[i] = ssi->screensaver_window;
1704           /* Ensure that the default background of the window is really black,
1705              not a pixmap or something.  (This does not clear the window.) */
1706           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1707                                 ssi->black_pixel);
1708         }
1709
1710       if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1711
1712
1713       XSync (si->dpy, False);
1714       XGrabServer (si->dpy);                    /* ############ DANGER! */
1715       XSync (si->dpy, False);
1716
1717       /* Clear the stderr layer on each screen.
1718        */
1719       for (i = 0; i < si->nscreens; i++)
1720         {
1721           saver_screen_info *ssi = &si->screens[i];
1722           clear_stderr (ssi);
1723         }
1724
1725       XUngrabServer (si->dpy);
1726       XSync (si->dpy, False);                   /* ###### (danger over) */
1727
1728       fade_screens (si->dpy, 0,
1729                     current_windows, si->nscreens,
1730                     p->fade_seconds/1000, p->fade_ticks,
1731                     False, False);
1732
1733       free(current_windows);
1734       current_windows = 0;
1735
1736       if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1737     }
1738   else
1739     {
1740       for (i = 0; i < si->nscreens; i++)
1741         {
1742           saver_screen_info *ssi = &si->screens[i];
1743           if (ssi->cmap)
1744             {
1745               Colormap c = DefaultColormapOfScreen (ssi->screen);
1746               /* avoid technicolor */
1747               XClearWindow (si->dpy, ssi->screensaver_window);
1748               if (c) XInstallColormap (si->dpy, c);
1749             }
1750           XUnmapWindow (si->dpy, ssi->screensaver_window);
1751         }
1752     }
1753
1754
1755   /* If the focus window does has a non-default colormap, then install
1756      that colormap as well.  (On SGIs, this will cause both the root map
1757      and the focus map to be installed simultaneously.  It'd be nice to
1758      pick up the other colormaps that had been installed, too; perhaps
1759      XListInstalledColormaps could be used for that?)
1760    */
1761   {
1762     Window focus = 0;
1763     int revert_to;
1764     XGetInputFocus (si->dpy, &focus, &revert_to);
1765     if (focus && focus != PointerRoot && focus != None)
1766       {
1767         XWindowAttributes xgwa;
1768         xgwa.colormap = 0;
1769         XGetWindowAttributes (si->dpy, focus, &xgwa);
1770         if (xgwa.colormap &&
1771             xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1772           XInstallColormap (si->dpy, xgwa.colormap);
1773       }
1774   }
1775
1776
1777   for (i = 0; i < si->nscreens; i++)
1778     {
1779       saver_screen_info *ssi = &si->screens[i];
1780       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1781     }
1782
1783   store_saver_status (si);  /* store unblank time */
1784   ungrab_keyboard_and_mouse (si);
1785   restore_real_vroot (si);
1786
1787   /* Unmap the windows a second time, dammit -- just to avoid a race
1788      with the screen-grabbing hacks.  (I'm not sure if this is really
1789      necessary; I'm stabbing in the dark now.)
1790   */
1791   for (i = 0; i < si->nscreens; i++)
1792     XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1793
1794   si->screen_blanked_p = False;
1795   si->blank_time = time ((time_t) 0);
1796   si->last_wall_clock_time = 0;
1797
1798   store_saver_status (si);  /* store unblank time */
1799 }
1800
1801
1802 /* Transfer any grabs from the old window to the new.
1803    Actually I think none of this is necessary, since we always
1804    hold our grabs on the root window, but I wrote this before
1805    re-discovering that...
1806  */
1807 static void
1808 maybe_transfer_grabs (saver_screen_info *ssi,
1809                       Window old_w, Window new_w,
1810                       int new_screen_no)
1811 {
1812   saver_info *si = ssi->global;
1813
1814   /* If the old window held our mouse grab, transfer the grab to the new
1815      window.  (Grab the server while so doing, to avoid a race condition.)
1816    */
1817   if (old_w == si->mouse_grab_window)
1818     {
1819       XGrabServer (si->dpy);            /* ############ DANGER! */
1820       ungrab_mouse (si);
1821       grab_mouse (si, ssi->screensaver_window,
1822                   (si->demoing_p ? 0 : ssi->cursor),
1823                   new_screen_no);
1824       XUngrabServer (si->dpy);
1825       XSync (si->dpy, False);           /* ###### (danger over) */
1826     }
1827
1828   /* If the old window held our keyboard grab, transfer the grab to the new
1829      window.  (Grab the server while so doing, to avoid a race condition.)
1830    */
1831   if (old_w == si->keyboard_grab_window)
1832     {
1833       XGrabServer (si->dpy);            /* ############ DANGER! */
1834       ungrab_kbd(si);
1835       grab_kbd(si, ssi->screensaver_window, ssi->number);
1836       XUngrabServer (si->dpy);
1837       XSync (si->dpy, False);           /* ###### (danger over) */
1838     }
1839 }
1840
1841
1842 static Visual *
1843 get_screen_gl_visual (saver_info *si, int real_screen_number)
1844 {
1845   int i;
1846   int nscreens = ScreenCount (si->dpy);
1847
1848   if (! si->best_gl_visuals)
1849     si->best_gl_visuals = (Visual **) 
1850       calloc (nscreens + 1, sizeof (*si->best_gl_visuals));
1851
1852   for (i = 0; i < nscreens; i++)
1853     if (! si->best_gl_visuals[i])
1854       si->best_gl_visuals[i] = 
1855         get_best_gl_visual (si, ScreenOfDisplay (si->dpy, i));
1856
1857   if (real_screen_number < 0 || real_screen_number >= nscreens) abort();
1858   return si->best_gl_visuals[real_screen_number];
1859 }
1860
1861
1862 Bool
1863 select_visual (saver_screen_info *ssi, const char *visual_name)
1864 {
1865   XWindowAttributes xgwa;
1866   saver_info *si = ssi->global;
1867   saver_preferences *p = &si->prefs;
1868   Bool install_cmap_p = p->install_cmap_p;
1869   Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1870   Visual *new_v = 0;
1871   Bool got_it;
1872
1873   /* On some systems (most recently, MacOS X) OpenGL programs get confused
1874      when you kill one and re-start another on the same window.  So maybe
1875      it's best to just always destroy and recreate the xscreensaver window
1876      when changing hacks, instead of trying to reuse the old one?
1877    */
1878   Bool always_recreate_window_p = True;
1879
1880   get_screen_gl_visual (si, 0);   /* let's probe all the GL visuals early */
1881
1882   /* We make sure the existing window is actually on ssi->screen before
1883      trying to use it, in case things moved around radically when monitors
1884      were added or deleted.  If we don't do this we could get a BadMatch
1885      even though the depths match.  I think.
1886    */
1887   memset (&xgwa, 0, sizeof(xgwa));
1888   if (ssi->screensaver_window)
1889     XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa);
1890
1891   if (visual_name && *visual_name)
1892     {
1893       if (!strcmp(visual_name, "default-i") ||
1894           !strcmp(visual_name, "Default-i") ||
1895           !strcmp(visual_name, "Default-I")
1896           )
1897         {
1898           visual_name = "default";
1899           install_cmap_p = True;
1900         }
1901       else if (!strcmp(visual_name, "default-n") ||
1902                !strcmp(visual_name, "Default-n") ||
1903                !strcmp(visual_name, "Default-N"))
1904         {
1905           visual_name = "default";
1906           install_cmap_p = False;
1907         }
1908       else if (!strcmp(visual_name, "gl") ||
1909                !strcmp(visual_name, "Gl") ||
1910                !strcmp(visual_name, "GL"))
1911         {
1912           new_v = get_screen_gl_visual (si, ssi->real_screen_number);
1913           if (!new_v && p->verbose_p)
1914             fprintf (stderr, "%s: no GL visuals.\n", progname);
1915         }
1916
1917       if (!new_v)
1918         new_v = get_visual (ssi->screen, visual_name, True, False);
1919     }
1920   else
1921     {
1922       new_v = ssi->default_visual;
1923     }
1924
1925   got_it = !!new_v;
1926
1927   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1928     /* It's not the default visual, so we have no choice but to install. */
1929     install_cmap_p = True;
1930
1931   ssi->install_cmap_p = install_cmap_p;
1932
1933   if ((ssi->screen != xgwa.screen) ||
1934       (new_v &&
1935        (always_recreate_window_p ||
1936         (ssi->current_visual != new_v) ||
1937         (install_cmap_p != was_installed_p))))
1938     {
1939       Colormap old_c = ssi->cmap;
1940       Window old_w = ssi->screensaver_window;
1941       if (! new_v) 
1942         new_v = ssi->current_visual;
1943
1944       if (p->verbose_p)
1945         {
1946           fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number);
1947           describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
1948 #if 0
1949           fprintf (stderr, "%s:                  from ", blurb());
1950           describe_visual (stderr, ssi->screen, ssi->current_visual,
1951                            was_installed_p);
1952 #endif
1953         }
1954
1955       reset_stderr (ssi);
1956       ssi->current_visual = new_v;
1957       ssi->current_depth = visual_depth(ssi->screen, new_v);
1958       ssi->cmap = 0;
1959       ssi->screensaver_window = 0;
1960
1961       initialize_screensaver_window_1 (ssi);
1962
1963       /* stderr_overlay_window is a child of screensaver_window, so we need
1964          to destroy that as well (actually, we just need to invalidate and
1965          drop our pointers to it, but this will destroy it, which is ok so
1966          long as it happens before old_w itself is destroyed.) */
1967       reset_stderr (ssi);
1968
1969       raise_window (si, True, True, False);
1970       store_vroot_property (si->dpy,
1971                             ssi->screensaver_window, ssi->screensaver_window);
1972
1973       /* Transfer any grabs from the old window to the new. */
1974       maybe_transfer_grabs (ssi, old_w, ssi->screensaver_window, ssi->number);
1975
1976       /* Now we can destroy the old window without horking our grabs. */
1977       XDestroyWindow (si->dpy, old_w);
1978
1979       if (p->verbose_p)
1980         fprintf (stderr, "%s: %d: destroyed old saver window 0x%lx.\n",
1981                  blurb(), ssi->number, (unsigned long) old_w);
1982
1983       if (old_c &&
1984           old_c != DefaultColormapOfScreen (ssi->screen) &&
1985           old_c != ssi->demo_cmap)
1986         XFreeColormap (si->dpy, old_c);
1987     }
1988
1989   return got_it;
1990 }