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