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