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