http://ftp.nluug.nl/pub/os/Linux/distr/pardusrepo/sources/xscreensaver-5.02.tar.gz
[xscreensaver] / driver / windows.c
1 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
2  * xscreensaver, Copyright (c) 1991-2007 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         }
1048
1049       /* Find the Xinerama rectangle that contains the mouse position. */
1050       for (i = 0; i < si->nscreens; i++)
1051         {
1052           if (target_x >= si->screens[i].x &&
1053               target_y >= si->screens[i].y &&
1054               target_x <  si->screens[i].x + si->screens[i].width &&
1055               target_y <  si->screens[i].y + si->screens[i].height)
1056             which = i;
1057         }
1058       if (which == -1) which = 0;  /* didn't find it?  Use the first. */
1059       *x_ret = si->screens[which].x;
1060       *y_ret = si->screens[which].y;
1061       *w_ret = si->screens[which].width;
1062       *h_ret = si->screens[which].height;
1063
1064       if (verbose_p)
1065         {
1066           fprintf (stderr, "%s: %d: xinerama vp: %dx%d+%d+%d",
1067                    blurb(), which,
1068                    si->screens[which].width, si->screens[which].height,
1069                    si->screens[which].x, si->screens[which].y);
1070           if (mouse_p)
1071             fprintf (stderr, "; mouse at %d,%d", target_x, target_y);
1072           fprintf (stderr, ".\n");
1073         }
1074
1075       return;
1076     }
1077 #  endif /* HAVE_XINERAMA */
1078
1079   if (!xinerama_p &&  /* Xinerama + VidMode = broken. */
1080       XF86VidModeQueryExtension (si->dpy, &event, &error) &&
1081       safe_XF86VidModeGetViewPort (si->dpy, ssi->number, &x, &y) &&
1082       XF86VidModeGetModeLine (si->dpy, ssi->number, &dot, &ml))
1083     {
1084       char msg[512];
1085       *x_ret = x;
1086       *y_ret = y;
1087       *w_ret = ml.hdisplay;
1088       *h_ret = ml.vdisplay;
1089
1090       if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
1091         /* There is no viewport -- the screen does not scroll. */
1092         return;
1093
1094
1095       /* Apparently some versions of XFree86 return nonsense here!
1096          I've had reports of 1024x768 viewports at -1936862040, -1953705044.
1097          So, sanity-check the values and give up if they are out of range.
1098        */
1099       if (*x_ret <  0 || *x_ret >= w ||
1100           *y_ret <  0 || *y_ret >= h ||
1101           *w_ret <= 0 || *w_ret >  w ||
1102           *h_ret <= 0 || *h_ret >  h)
1103         {
1104           static int warned_once = 0;
1105           if (!warned_once)
1106             {
1107               fprintf (stderr, "\n"
1108                   "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
1109                   "%s: The XVidMode server extension is returning nonsense.\n"
1110                   "%s: Please report this bug to your X server vendor.\n\n",
1111                        blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
1112                        blurb(), blurb());
1113               warned_once = 1;
1114             }
1115           *x_ret = 0;
1116           *y_ret = 0;
1117           *w_ret = w;
1118           *h_ret = h;
1119           return;
1120         }
1121
1122       sprintf (msg, "%s: %d: vp is %dx%d+%d+%d",
1123                blurb(), ssi->number,
1124                *w_ret, *h_ret, *x_ret, *y_ret);
1125
1126
1127       if (p->getviewport_full_of_lies_p)
1128         {
1129           /* XF86VidModeGetViewPort() tends to be full of lies on laptops
1130              that have a docking station or external monitor that runs in
1131              a different resolution than the laptop's screen:
1132
1133                  http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=81593
1134                  http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=208417
1135                  http://bugs.xfree86.org/show_bug.cgi?id=421
1136
1137              The XFree86 developers have closed the bug. As far as I can
1138              tell, their reason for this was, "this is an X server bug,
1139              but it's pretty hard to fix. Therefore, we are closing it."
1140
1141              So, now there's a preference item for those unfortunate users to
1142              tell us not to trust a word that XF86VidModeGetViewPort() says.
1143            */
1144           static int warned_once = 0;
1145           if (!warned_once && verbose_p)
1146             {
1147               warned_once = 1;
1148               fprintf (stderr,
1149                   "%s: %d: XF86VidModeGetViewPort() says vp is %dx%d+%d+%d;\n"
1150                   "%s: %d:     assuming that is a pack of lies;\n"
1151                   "%s: %d:     using %dx%d+0+0 instead.\n",
1152                        blurb(), ssi->number,
1153                        *w_ret, *h_ret, *x_ret, *y_ret,
1154                        blurb(), ssi->number,
1155                        blurb(), ssi->number, w, h);
1156             }
1157
1158           *x_ret = 0;
1159           *y_ret = 0;
1160           *w_ret = w;
1161           *h_ret = h;
1162           return;
1163         }
1164
1165
1166       /* Apparently, though the server stores the X position in increments of
1167          1 pixel, it will only make changes to the *display* in some other
1168          increment.  With XF86_SVGA on a Thinkpad, the display only updates
1169          in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
1170          pixels in 16-bit mode.  I don't know what it does in 24- and 32-bit
1171          mode, because I don't have enough video memory to find out.
1172
1173          I consider it a bug that XF86VidModeGetViewPort() is telling me the
1174          server's *target* scroll position rather than the server's *actual*
1175          scroll position.  David Dawes agrees, and says they may fix this in
1176          XFree86 4.0, but it's notrivial.
1177
1178          He also confirms that this behavior is server-dependent, so the
1179          actual scroll position cannot be reliably determined by the client.
1180          So... that means the only solution is to provide a ``sandbox''
1181          around the blackout window -- we make the window be up to N pixels
1182          larger than the viewport on both the left and right sides.  That
1183          means some part of the outer edges of each hack might not be
1184          visible, but screw it.
1185
1186          I'm going to guess that 16 pixels is enough, and that the Y dimension
1187          doesn't have this problem.
1188
1189          The drawback of doing this, of course, is that some of the screenhacks
1190          will still look pretty stupid -- for example, "slidescreen" will cut
1191          off the left and right edges of the grid, etc.
1192       */
1193 #  define FUDGE 16
1194       if (x > 0 && x < w - ml.hdisplay)  /* not at left edge or right edge */
1195         {
1196           /* Round X position down to next lower multiple of FUDGE.
1197              Increase width by 2*FUDGE in case some server rounds up.
1198            */
1199           *x_ret = ((x - 1) / FUDGE) * FUDGE;
1200           *w_ret += (FUDGE * 2);
1201         }
1202 #  undef FUDGE
1203
1204       if (*x_ret != x ||
1205           *y_ret != y ||
1206           *w_ret != ml.hdisplay ||
1207           *h_ret != ml.vdisplay)
1208         sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
1209                  *w_ret, *h_ret, *x_ret, *y_ret);
1210
1211       if (verbose_p)
1212         fprintf (stderr, "%s.\n", msg);
1213
1214       return;
1215     }
1216
1217 # endif /* HAVE_XF86VMODE */
1218
1219   *x_ret = 0;
1220   *y_ret = 0;
1221   *w_ret = w;
1222   *h_ret = h;
1223 }
1224
1225
1226 static Bool error_handler_hit_p = False;
1227
1228 static int
1229 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1230 {
1231   error_handler_hit_p = True;
1232   return 0;
1233 }
1234
1235
1236 /* Returns True if successful, False if an X error occurred.
1237    We need this because other programs might have done things to
1238    our window that will cause XChangeWindowAttributes() to fail:
1239    if that happens, we give up, destroy the window, and re-create
1240    it.
1241  */
1242 static Bool
1243 safe_XChangeWindowAttributes (Display *dpy, Window window,
1244                               unsigned long mask,
1245                               XSetWindowAttributes *attrs)
1246 {
1247   XErrorHandler old_handler;
1248   XSync (dpy, False);
1249   error_handler_hit_p = False;
1250   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1251
1252   XChangeWindowAttributes (dpy, window, mask, attrs);
1253
1254   XSync (dpy, False);
1255   XSetErrorHandler (old_handler);
1256   XSync (dpy, False);
1257
1258   return (!error_handler_hit_p);
1259 }
1260
1261
1262 /* This might not be necessary, but just in case. */
1263 static Bool
1264 safe_XConfigureWindow (Display *dpy, Window window,
1265                        unsigned long mask, XWindowChanges *changes)
1266 {
1267   XErrorHandler old_handler;
1268   XSync (dpy, False);
1269   error_handler_hit_p = False;
1270   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1271
1272   XConfigureWindow (dpy, window, mask, changes);
1273
1274   XSync (dpy, False);
1275   XSetErrorHandler (old_handler);
1276   XSync (dpy, False);
1277
1278   return (!error_handler_hit_p);
1279 }
1280
1281 /* This might not be necessary, but just in case. */
1282 static Bool
1283 safe_XDestroyWindow (Display *dpy, Window window)
1284 {
1285   XErrorHandler old_handler;
1286   XSync (dpy, False);
1287   error_handler_hit_p = False;
1288   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1289
1290   XDestroyWindow (dpy, window);
1291
1292   XSync (dpy, False);
1293   XSetErrorHandler (old_handler);
1294   XSync (dpy, False);
1295
1296   return (!error_handler_hit_p);
1297 }
1298
1299
1300 static Bool
1301 safe_XKillClient (Display *dpy, XID id)
1302 {
1303   XErrorHandler old_handler;
1304   XSync (dpy, False);
1305   error_handler_hit_p = False;
1306   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1307
1308   XKillClient (dpy, id);
1309
1310   XSync (dpy, False);
1311   XSetErrorHandler (old_handler);
1312   XSync (dpy, False);
1313
1314   return (!error_handler_hit_p);
1315 }
1316
1317
1318 #ifdef HAVE_XF86VMODE
1319 static Bool
1320 safe_XF86VidModeGetViewPort (Display *dpy, int screen, int *xP, int *yP)
1321 {
1322   Bool result;
1323   XErrorHandler old_handler;
1324   XSync (dpy, False);
1325   error_handler_hit_p = False;
1326   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1327
1328   result = XF86VidModeGetViewPort (dpy, screen, xP, yP);
1329
1330   XSync (dpy, False);
1331   XSetErrorHandler (old_handler);
1332   XSync (dpy, False);
1333
1334   return (error_handler_hit_p
1335           ? False
1336           : result);
1337 }
1338
1339 /* There is no "safe_XF86VidModeGetModeLine" because it fails with an
1340    untrappable I/O error instead of an X error -- so one must call
1341    safe_XF86VidModeGetViewPort first, and assume that both have the
1342    same error condition.  Thank you XFree, may I have another.
1343  */
1344
1345 #endif /* HAVE_XF86VMODE */
1346
1347
1348 static void
1349 initialize_screensaver_window_1 (saver_screen_info *ssi)
1350 {
1351   saver_info *si = ssi->global;
1352   saver_preferences *p = &si->prefs;
1353   Bool install_cmap_p = ssi->install_cmap_p;   /* not p->install_cmap_p */
1354
1355   /* This resets the screensaver window as fully as possible, since there's
1356      no way of knowing what some random client may have done to us in the
1357      meantime.  We could just destroy and recreate the window, but that has
1358      its own set of problems...
1359    */
1360   XColor black;
1361   XSetWindowAttributes attrs;
1362   unsigned long attrmask;
1363   int x, y, width, height;
1364   static Bool printed_visual_info = False;  /* only print the message once. */
1365   Window horked_window = 0;
1366
1367   get_screen_viewport (ssi, &x, &y, &width, &height, -1, -1,
1368                        (p->verbose_p && !si->screen_blanked_p));
1369
1370   black.red = black.green = black.blue = 0;
1371
1372   if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
1373     ssi->cmap = 0;
1374
1375   if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
1376     /* It's not the default visual, so we have no choice but to install. */
1377     install_cmap_p = True;
1378
1379   if (install_cmap_p)
1380     {
1381       if (! ssi->cmap)
1382         {
1383           ssi->cmap = XCreateColormap (si->dpy,
1384                                        RootWindowOfScreen (ssi->screen),
1385                                       ssi->current_visual, AllocNone);
1386           if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
1387           ssi->black_pixel = black.pixel;
1388         }
1389     }
1390   else
1391     {
1392       Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
1393       if (ssi->cmap)
1394         {
1395           XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
1396           if (ssi->cmap != ssi->demo_cmap &&
1397               ssi->cmap != def_cmap)
1398             XFreeColormap (si->dpy, ssi->cmap);
1399         }
1400       ssi->cmap = def_cmap;
1401       ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
1402     }
1403
1404   attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
1405               CWBackPixel | CWBackingPixel | CWBorderPixel);
1406   attrs.override_redirect = True;
1407
1408   /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
1409      actually be reading these events during normal operation; but we still
1410      need to see Button events for demo-mode to work properly.
1411    */
1412   attrs.event_mask = (KeyPressMask | KeyReleaseMask |
1413                       ButtonPressMask | ButtonReleaseMask |
1414                       PointerMotionMask);
1415
1416   attrs.backing_store = NotUseful;
1417   attrs.colormap = ssi->cmap;
1418   attrs.background_pixel = ssi->black_pixel;
1419   attrs.backing_pixel = ssi->black_pixel;
1420   attrs.border_pixel = ssi->black_pixel;
1421
1422   if (p->debug_p
1423 # ifdef QUAD_MODE
1424       && !p->quad_p
1425 # endif
1426       )
1427     width = width / 2;
1428
1429   if (!p->verbose_p || printed_visual_info)
1430     ;
1431   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
1432     {
1433       fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number);
1434       describe_visual (stderr, ssi->screen, ssi->current_visual,
1435                        install_cmap_p);
1436     }
1437   else
1438     {
1439       fprintf (stderr, "%s: using visual:   ", blurb());
1440       describe_visual (stderr, ssi->screen, ssi->current_visual,
1441                        install_cmap_p);
1442       fprintf (stderr, "%s: default visual: ", blurb());
1443       describe_visual (stderr, ssi->screen,
1444                        DefaultVisualOfScreen (ssi->screen),
1445                        ssi->install_cmap_p);
1446     }
1447   printed_visual_info = True;
1448
1449 #ifdef HAVE_MIT_SAVER_EXTENSION
1450   if (si->using_mit_saver_extension)
1451     {
1452       XScreenSaverInfo *info;
1453       Window root = RootWindowOfScreen (ssi->screen);
1454
1455 #if 0
1456       /* This call sets the server screensaver timeouts to what we think
1457          they should be (based on the resources and args xscreensaver was
1458          started with.)  It's important that we do this to sync back up
1459          with the server - if we have turned on prematurely, as by an
1460          ACTIVATE ClientMessage, then the server may decide to activate
1461          the screensaver while it's already active.  That's ok for us,
1462          since we would know to ignore that ScreenSaverActivate event,
1463          but a side effect of this would be that the server would map its
1464          saver window (which we then hide again right away) meaning that
1465          the bits currently on the screen get blown away.  Ugly. */
1466
1467       /* #### Ok, that doesn't work - when we tell the server that the
1468          screensaver is "off" it sends us a Deactivate event, which is
1469          sensible... but causes the saver to never come on.  Hmm. */
1470       disable_builtin_screensaver (si, True);
1471 #endif /* 0 */
1472
1473 #if 0
1474       /* #### The MIT-SCREEN-SAVER extension gives us access to the
1475          window that the server itself uses for saving the screen.
1476          However, using this window in any way, in particular, calling
1477          XScreenSaverSetAttributes() as below, tends to make the X server
1478          crash.  So fuck it, let's try and get along without using it...
1479
1480          It's also inconvenient to use this window because it doesn't
1481          always exist (though the ID is constant.)  So to use this
1482          window, we'd have to reimplement the ACTIVATE ClientMessage to
1483          tell the *server* to tell *us* to turn on, to cause the window
1484          to get created at the right time.  Gag.  */
1485       XScreenSaverSetAttributes (si->dpy, root,
1486                                  0, 0, width, height, 0,
1487                                  current_depth, InputOutput, visual,
1488                                  attrmask, &attrs);
1489       XSync (si->dpy, False);
1490 #endif /* 0 */
1491
1492       info = XScreenSaverAllocInfo ();
1493       XScreenSaverQueryInfo (si->dpy, root, info);
1494       ssi->server_mit_saver_window = info->window;
1495       if (! ssi->server_mit_saver_window) abort ();
1496       XFree (info);
1497     }
1498 #endif /* HAVE_MIT_SAVER_EXTENSION */
1499
1500   if (ssi->screensaver_window)
1501     {
1502       XWindowChanges changes;
1503       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1504       changes.x = x;
1505       changes.y = y;
1506       changes.width = width;
1507       changes.height = height;
1508       changes.border_width = 0;
1509
1510       if (! (safe_XConfigureWindow (si->dpy, ssi->screensaver_window,
1511                                   changesmask, &changes) &&
1512              safe_XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
1513                                            attrmask, &attrs)))
1514         {
1515           horked_window = ssi->screensaver_window;
1516           ssi->screensaver_window = 0;
1517         }
1518     }
1519
1520   if (!ssi->screensaver_window)
1521     {
1522       ssi->screensaver_window =
1523         XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
1524                        x, y, width, height,
1525                        0, ssi->current_depth, InputOutput,
1526                        ssi->current_visual, attrmask, &attrs);
1527
1528       reset_stderr (ssi);
1529
1530       if (horked_window)
1531         {
1532           fprintf (stderr,
1533             "%s: someone horked our saver window (0x%lx)!  Recreating it...\n",
1534                    blurb(), (unsigned long) horked_window);
1535           maybe_transfer_grabs (ssi, horked_window, ssi->screensaver_window,
1536                                 ssi->number);
1537           safe_XDestroyWindow (si->dpy, horked_window);
1538           horked_window = 0;
1539         }
1540
1541       if (p->verbose_p)
1542         fprintf (stderr, "%s: %d: saver window is 0x%lx.\n",
1543                  blurb(), ssi->number,
1544                  (unsigned long) ssi->screensaver_window);
1545     }
1546
1547   store_saver_id (ssi);       /* store window name and IDs */
1548
1549   if (!ssi->cursor)
1550     {
1551       Pixmap bit;
1552       bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
1553                                          "\000", 1, 1,
1554                                          BlackPixelOfScreen (ssi->screen),
1555                                          BlackPixelOfScreen (ssi->screen),
1556                                          1);
1557       ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
1558                                          0, 0);
1559       XFreePixmap (si->dpy, bit);
1560     }
1561
1562   XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
1563
1564   if (si->demoing_p)
1565     XUndefineCursor (si->dpy, ssi->screensaver_window);
1566   else
1567     XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1568 }
1569
1570 void
1571 initialize_screensaver_window (saver_info *si)
1572 {
1573   int i;
1574   for (i = 0; i < si->nscreens; i++)
1575     initialize_screensaver_window_1 (&si->screens[i]);
1576 }
1577
1578
1579 /* Called when the RANDR (Resize and Rotate) extension tells us that the
1580    size of the screen has changed while the screen was blanked.  If we
1581    don't do this, then the screen saver will no longer fully fill the
1582    screen, and some of the underlying desktop may be visible.
1583  */
1584 void
1585 resize_screensaver_window (saver_info *si)
1586 {
1587   saver_preferences *p = &si->prefs;
1588   int i;
1589
1590   /* First update the size info in the saver_screen_info structs.
1591    */
1592
1593 # ifdef HAVE_XINERAMA
1594   if (si->xinerama_p)
1595     {
1596       /* As of XFree86 4.3.0, the RANDR and XINERAMA extensions cannot coexist.
1597          However, maybe they will someday, so I'm guessing that the right thing
1598          to do in that case will be to re-query the Xinerama rectangles after
1599          a RANDR size change is received: presumably, if the resolution of one
1600          or more of the monitors has changed, then the Xinerama rectangle
1601          corresponding to that monitor will also have been updated.
1602        */
1603       int nscreens;
1604       XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &nscreens);
1605
1606       if (nscreens != si->nscreens) {
1607         /* Apparently some Xinerama implementations let you use a hot-key
1608            to change the number of screens in use!  This is, of course,
1609            documented nowhere.  Let's try to do something marginally less
1610            bad than crashing.
1611          */
1612         fprintf (stderr, "%s: bad craziness: xinerama screen count changed "
1613                  "from %d to %d!\n", blurb(), si->nscreens, nscreens);
1614         if (nscreens > si->nscreens)
1615           nscreens = si->nscreens;
1616       }
1617
1618       if (!xsi) abort();
1619       for (i = 0; i < nscreens; i++)
1620         {
1621           saver_screen_info *ssi = &si->screens[i];
1622           if (p->verbose_p &&
1623               (ssi->x      != xsi[i].x_org ||
1624                ssi->y      != xsi[i].y_org ||
1625                ssi->width  != xsi[i].width ||
1626                ssi->height != xsi[i].height))
1627             fprintf (stderr,
1628                    "%s: %d: resize xinerama from %dx%d+%d+%d to %dx%d+%d+%d\n",
1629                      blurb(), i,
1630                      ssi->width,   ssi->height,   ssi->x,       ssi->y,
1631                      xsi[i].width, xsi[i].height, xsi[i].x_org, xsi[i].y_org);
1632
1633           ssi->x      = xsi[i].x_org;
1634           ssi->y      = xsi[i].y_org;
1635           ssi->width  = xsi[i].width;
1636           ssi->height = xsi[i].height;
1637         }
1638       XFree (xsi);
1639     }
1640   else
1641 # endif /* HAVE_XINERAMA */
1642     {
1643       /* Not Xinerama -- get the real sizes of the root windows. */
1644       for (i = 0; i < si->nscreens; i++)
1645         {
1646           saver_screen_info *ssi = &si->screens[i];
1647           XWindowAttributes xgwa;
1648           XGetWindowAttributes (si->dpy, RootWindowOfScreen (ssi->screen),
1649                                 &xgwa);
1650
1651           if (p->verbose_p &&
1652               (ssi->x      != xgwa.x ||
1653                ssi->y      != xgwa.y ||
1654                ssi->width  != xgwa.width ||
1655                ssi->height != xgwa.height))
1656             fprintf (stderr,
1657                      "%s: %d: resize screen from %dx%d+%d+%d to %dx%d+%d+%d\n",
1658                      blurb(), i,
1659                      ssi->width, ssi->height, ssi->x, ssi->y,
1660                      xgwa.width, xgwa.height, xgwa.x, xgwa.y);
1661
1662           ssi->x      = xgwa.x;
1663           ssi->y      = xgwa.y;
1664           ssi->width  = xgwa.width;
1665           ssi->height = xgwa.height;
1666         }
1667     }
1668
1669   /* Next, ensure that the screensaver windows are the right size, taking
1670      into account both the new size of the screen in question's root window,
1671      and any viewport within that.
1672    */
1673
1674   for (i = 0; i < si->nscreens; i++)
1675     {
1676       saver_screen_info *ssi = &si->screens[i];
1677       XWindowAttributes xgwa;
1678       XWindowChanges changes;
1679       int x, y, width, height;
1680       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1681
1682       XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa);
1683       get_screen_viewport (ssi, &x, &y, &width, &height, -1, -1,
1684                            (p->verbose_p && !si->screen_blanked_p));
1685       if (xgwa.x == x &&
1686           xgwa.y == y &&
1687           xgwa.width  == width &&
1688           xgwa.height == height)
1689         continue;  /* no change! */
1690
1691       changes.x = x;
1692       changes.y = y;
1693       changes.width  = width;
1694       changes.height = height;
1695       changes.border_width = 0;
1696
1697       if (p->debug_p
1698 # ifdef QUAD_MODE
1699           && !p->quad_p
1700 # endif
1701           ) 
1702         changes.width = changes.width / 2;
1703
1704       if (p->verbose_p)
1705         fprintf (stderr,
1706                  "%s: %d: resize 0x%lx from %dx%d+%d+%d to %dx%d+%d+%d\n",
1707                  blurb(), i, (unsigned long) ssi->screensaver_window,
1708                  xgwa.width, xgwa.height, xgwa.x, xgwa.y,
1709                  width, height, x, y);
1710       if (! safe_XConfigureWindow (si->dpy, ssi->screensaver_window,
1711                                    changesmask, &changes))
1712         {
1713           fprintf (stderr,
1714     "%s: %d: someone horked our saver window (0x%lx)!  Unable to resize it!\n",
1715                    blurb(), i, (unsigned long) ssi->screensaver_window);
1716         }
1717     }
1718 }
1719
1720
1721 void 
1722 raise_window (saver_info *si,
1723               Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
1724 {
1725   saver_preferences *p = &si->prefs;
1726   int i;
1727
1728   if (si->demoing_p)
1729     inhibit_fade = True;
1730
1731   if (si->emergency_lock_p)
1732     inhibit_fade = True;
1733
1734   if (!dont_clear)
1735     initialize_screensaver_window (si);
1736
1737   reset_watchdog_timer (si, True);
1738
1739   if (p->fade_p && si->fading_possible_p && !inhibit_fade)
1740     {
1741       Window *current_windows = (Window *)
1742         calloc(sizeof(Window), si->nscreens);
1743       Colormap *current_maps = (Colormap *)
1744         calloc(sizeof(Colormap), si->nscreens);
1745
1746       for (i = 0; i < si->nscreens; i++)
1747         {
1748           saver_screen_info *ssi = &si->screens[i];
1749           current_windows[i] = ssi->screensaver_window;
1750           current_maps[i] = (between_hacks_p
1751                              ? ssi->cmap
1752                              : DefaultColormapOfScreen (ssi->screen));
1753           /* Ensure that the default background of the window is really black,
1754              not a pixmap or something.  (This does not clear the window.) */
1755           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1756                                 ssi->black_pixel);
1757         }
1758
1759       if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1760
1761       XGrabServer (si->dpy);                    /* ############ DANGER! */
1762
1763       /* Clear the stderr layer on each screen.
1764        */
1765       if (!dont_clear)
1766         for (i = 0; i < si->nscreens; i++)
1767           {
1768             saver_screen_info *ssi = &si->screens[i];
1769             if (ssi->stderr_overlay_window)
1770               /* Do this before the fade, since the stderr cmap won't fade
1771                  even if we uninstall it (beats me...) */
1772               clear_stderr (ssi);
1773           }
1774
1775       /* Note!  The server is grabbed, and this will take several seconds
1776          to complete! */
1777       fade_screens (si->dpy, current_maps,
1778                     current_windows, si->nscreens,
1779                     p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1780
1781       free(current_maps);
1782       free(current_windows);
1783       current_maps = 0;
1784       current_windows = 0;
1785
1786       if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1787
1788 #ifdef HAVE_MIT_SAVER_EXTENSION
1789       for (i = 0; i < si->nscreens; i++)
1790         {
1791           saver_screen_info *ssi = &si->screens[i];
1792           if (ssi->server_mit_saver_window &&
1793               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1794             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1795         }
1796 #endif /* HAVE_MIT_SAVER_EXTENSION */
1797
1798       XUngrabServer (si->dpy);
1799       XSync (si->dpy, False);                   /* ###### (danger over) */
1800     }
1801   else
1802     {
1803       for (i = 0; i < si->nscreens; i++)
1804         {
1805           saver_screen_info *ssi = &si->screens[i];
1806           if (!dont_clear)
1807             XClearWindow (si->dpy, ssi->screensaver_window);
1808           if (!dont_clear || ssi->stderr_overlay_window)
1809             clear_stderr (ssi);
1810           XMapRaised (si->dpy, ssi->screensaver_window);
1811 #ifdef HAVE_MIT_SAVER_EXTENSION
1812           if (ssi->server_mit_saver_window &&
1813               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1814             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1815 #endif /* HAVE_MIT_SAVER_EXTENSION */
1816         }
1817     }
1818
1819   for (i = 0; i < si->nscreens; i++)
1820     {
1821       saver_screen_info *ssi = &si->screens[i];
1822       if (ssi->cmap)
1823         XInstallColormap (si->dpy, ssi->cmap);
1824     }
1825 }
1826
1827
1828 int
1829 mouse_screen (saver_info *si)
1830 {
1831   saver_preferences *p = &si->prefs;
1832   Window pointer_root, pointer_child;
1833   int root_x, root_y, win_x, win_y;
1834   unsigned int mask;
1835   int i;
1836
1837   if (si->nscreens == 1)
1838     return 0;
1839
1840   for (i = 0; i < si->nscreens; i++)
1841     {
1842       saver_screen_info *ssi = &si->screens[i];
1843       if (XQueryPointer (si->dpy, RootWindowOfScreen (ssi->screen),
1844                          &pointer_root, &pointer_child,
1845                          &root_x, &root_y, &win_x, &win_y, &mask) &&
1846           root_x >= ssi->x &&
1847           root_y >= ssi->y &&
1848           root_x <  ssi->x + ssi->width &&
1849           root_y <  ssi->y + ssi->height)
1850         {
1851           if (p->verbose_p)
1852             fprintf (stderr, "%s: mouse is on screen %d of %d\n",
1853                      blurb(), i, si->nscreens);
1854           return i;
1855         }
1856     }
1857
1858   /* couldn't figure out where the mouse is?  Oh well. */
1859   return 0;
1860 }
1861
1862
1863 Bool
1864 blank_screen (saver_info *si)
1865 {
1866   int i;
1867   Bool ok;
1868   Window w;
1869   int mscreen;
1870
1871   /* Note: we do our grabs on the root window, not on the screensaver window.
1872      If we grabbed on the saver window, then the demo mode and lock dialog
1873      boxes wouldn't get any events.
1874
1875      By "the root window", we mean "the root window that contains the mouse."
1876      We use to always grab the mouse on screen 0, but that has the effect of
1877      moving the mouse to screen 0 from whichever screen it was on, on
1878      multi-head systems.
1879    */
1880   mscreen = mouse_screen (si);
1881   w = RootWindowOfScreen(si->screens[mscreen].screen);
1882   ok = grab_keyboard_and_mouse (si, w,
1883                                 (si->demoing_p ? 0 : si->screens[0].cursor),
1884                                 mscreen);
1885
1886
1887 # if 0
1888   if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1889     /* If we're using a server extension, then failure to get a grab is
1890        not a big deal -- even without the grab, we will still be able
1891        to un-blank when there is user activity, since the server will
1892        tell us. */
1893     /* #### No, that's not true: if we don't have a keyboard grab,
1894             then we can't read passwords to unlock.
1895      */
1896     ok = True;
1897 # endif /* 0 */
1898
1899   if (!ok)
1900     return False;
1901
1902   for (i = 0; i < si->nscreens; i++)
1903     {
1904       saver_screen_info *ssi = &si->screens[i];
1905       if (ssi->real_screen_p)
1906         save_real_vroot (ssi);
1907       store_vroot_property (si->dpy,
1908                             ssi->screensaver_window,
1909                             ssi->screensaver_window);
1910
1911 #ifdef HAVE_XF86VMODE
1912       {
1913         int ev, er;
1914         if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
1915             !safe_XF86VidModeGetViewPort (si->dpy, i,
1916                                           &ssi->blank_vp_x,
1917                                           &ssi->blank_vp_y))
1918           ssi->blank_vp_x = ssi->blank_vp_y = -1;
1919       }
1920 #endif /* HAVE_XF86VMODE */
1921     }
1922
1923   raise_window (si, False, False, False);
1924
1925   si->screen_blanked_p = True;
1926   si->blank_time = time ((time_t) 0);
1927   si->last_wall_clock_time = 0;
1928
1929   store_saver_status (si);  /* store blank time */
1930
1931   return True;
1932 }
1933
1934
1935 void
1936 unblank_screen (saver_info *si)
1937 {
1938   saver_preferences *p = &si->prefs;
1939   Bool unfade_p = (si->fading_possible_p && p->unfade_p);
1940   int i;
1941
1942   monitor_power_on (si);
1943   reset_watchdog_timer (si, False);
1944
1945   if (si->demoing_p)
1946     unfade_p = False;
1947
1948   if (unfade_p)
1949     {
1950       Window *current_windows = (Window *)
1951         calloc(sizeof(Window), si->nscreens);
1952
1953       for (i = 0; i < si->nscreens; i++)
1954         {
1955           saver_screen_info *ssi = &si->screens[i];
1956           current_windows[i] = ssi->screensaver_window;
1957           /* Ensure that the default background of the window is really black,
1958              not a pixmap or something.  (This does not clear the window.) */
1959           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1960                                 ssi->black_pixel);
1961         }
1962
1963       if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1964
1965
1966       XSync (si->dpy, False);
1967       XGrabServer (si->dpy);                    /* ############ DANGER! */
1968       XSync (si->dpy, False);
1969
1970       /* Clear the stderr layer on each screen.
1971        */
1972       for (i = 0; i < si->nscreens; i++)
1973         {
1974           saver_screen_info *ssi = &si->screens[i];
1975           clear_stderr (ssi);
1976         }
1977
1978       XUngrabServer (si->dpy);
1979       XSync (si->dpy, False);                   /* ###### (danger over) */
1980
1981       fade_screens (si->dpy, 0,
1982                     current_windows, si->nscreens,
1983                     p->fade_seconds/1000, p->fade_ticks,
1984                     False, False);
1985
1986       free(current_windows);
1987       current_windows = 0;
1988
1989       if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1990     }
1991   else
1992     {
1993       for (i = 0; i < si->nscreens; i++)
1994         {
1995           saver_screen_info *ssi = &si->screens[i];
1996           if (ssi->cmap)
1997             {
1998               Colormap c = DefaultColormapOfScreen (ssi->screen);
1999               /* avoid technicolor */
2000               XClearWindow (si->dpy, ssi->screensaver_window);
2001               if (c) XInstallColormap (si->dpy, c);
2002             }
2003           XUnmapWindow (si->dpy, ssi->screensaver_window);
2004         }
2005     }
2006
2007
2008   /* If the focus window does has a non-default colormap, then install
2009      that colormap as well.  (On SGIs, this will cause both the root map
2010      and the focus map to be installed simultaneously.  It'd be nice to
2011      pick up the other colormaps that had been installed, too; perhaps
2012      XListInstalledColormaps could be used for that?)
2013    */
2014   {
2015     Window focus = 0;
2016     int revert_to;
2017     XGetInputFocus (si->dpy, &focus, &revert_to);
2018     if (focus && focus != PointerRoot && focus != None)
2019       {
2020         XWindowAttributes xgwa;
2021         xgwa.colormap = 0;
2022         XGetWindowAttributes (si->dpy, focus, &xgwa);
2023         if (xgwa.colormap &&
2024             xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
2025           XInstallColormap (si->dpy, xgwa.colormap);
2026       }
2027   }
2028
2029
2030   for (i = 0; i < si->nscreens; i++)
2031     {
2032       saver_screen_info *ssi = &si->screens[i];
2033       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
2034     }
2035
2036   store_saver_status (si);  /* store unblank time */
2037   ungrab_keyboard_and_mouse (si);
2038   restore_real_vroot (si);
2039
2040   /* Unmap the windows a second time, dammit -- just to avoid a race
2041      with the screen-grabbing hacks.  (I'm not sure if this is really
2042      necessary; I'm stabbing in the dark now.)
2043   */
2044   for (i = 0; i < si->nscreens; i++)
2045     XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
2046
2047   si->screen_blanked_p = False;
2048   si->blank_time = time ((time_t) 0);
2049   si->last_wall_clock_time = 0;
2050
2051   store_saver_status (si);  /* store unblank time */
2052 }
2053
2054
2055 /* Transfer any grabs from the old window to the new.
2056    Actually I think none of this is necessary, since we always
2057    hold our grabs on the root window, but I wrote this before
2058    re-discovering that...
2059  */
2060 static void
2061 maybe_transfer_grabs (saver_screen_info *ssi,
2062                       Window old_w, Window new_w,
2063                       int new_screen_no)
2064 {
2065   saver_info *si = ssi->global;
2066
2067   /* If the old window held our mouse grab, transfer the grab to the new
2068      window.  (Grab the server while so doing, to avoid a race condition.)
2069    */
2070   if (old_w == si->mouse_grab_window)
2071     {
2072       XGrabServer (si->dpy);            /* ############ DANGER! */
2073       ungrab_mouse (si);
2074       grab_mouse (si, ssi->screensaver_window,
2075                   (si->demoing_p ? 0 : ssi->cursor),
2076                   new_screen_no);
2077       XUngrabServer (si->dpy);
2078       XSync (si->dpy, False);           /* ###### (danger over) */
2079     }
2080
2081   /* If the old window held our keyboard grab, transfer the grab to the new
2082      window.  (Grab the server while so doing, to avoid a race condition.)
2083    */
2084   if (old_w == si->keyboard_grab_window)
2085     {
2086       XGrabServer (si->dpy);            /* ############ DANGER! */
2087       ungrab_kbd(si);
2088       grab_kbd(si, ssi->screensaver_window, ssi->number);
2089       XUngrabServer (si->dpy);
2090       XSync (si->dpy, False);           /* ###### (danger over) */
2091     }
2092 }
2093
2094
2095
2096 Bool
2097 select_visual (saver_screen_info *ssi, const char *visual_name)
2098 {
2099   saver_info *si = ssi->global;
2100   saver_preferences *p = &si->prefs;
2101   Bool install_cmap_p = p->install_cmap_p;
2102   Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
2103   Visual *new_v = 0;
2104   Bool got_it;
2105
2106   /* On some systems (most recently, MacOS X) OpenGL programs get confused
2107      when you kill one and re-start another on the same window.  So maybe
2108      it's best to just always destroy and recreate the xscreensaver window
2109      when changing hacks, instead of trying to reuse the old one?
2110    */
2111   Bool always_recreate_window_p = True;
2112
2113   if (visual_name && *visual_name)
2114     {
2115       if (!strcmp(visual_name, "default-i") ||
2116           !strcmp(visual_name, "Default-i") ||
2117           !strcmp(visual_name, "Default-I")
2118           )
2119         {
2120           visual_name = "default";
2121           install_cmap_p = True;
2122         }
2123       else if (!strcmp(visual_name, "default-n") ||
2124                !strcmp(visual_name, "Default-n") ||
2125                !strcmp(visual_name, "Default-N"))
2126         {
2127           visual_name = "default";
2128           install_cmap_p = False;
2129         }
2130       else if (!strcmp(visual_name, "gl") ||
2131                !strcmp(visual_name, "Gl") ||
2132                !strcmp(visual_name, "GL"))
2133         {
2134           new_v = ssi->best_gl_visual;
2135           if (!new_v && p->verbose_p)
2136             fprintf (stderr, "%s: no GL visuals.\n", progname);
2137         }
2138
2139       if (!new_v)
2140         new_v = get_visual (ssi->screen, visual_name, True, False);
2141     }
2142   else
2143     {
2144       new_v = ssi->default_visual;
2145     }
2146
2147   got_it = !!new_v;
2148
2149   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
2150     /* It's not the default visual, so we have no choice but to install. */
2151     install_cmap_p = True;
2152
2153   ssi->install_cmap_p = install_cmap_p;
2154
2155   if (new_v &&
2156       (always_recreate_window_p ||
2157        (ssi->current_visual != new_v) ||
2158        (install_cmap_p != was_installed_p)))
2159     {
2160       Colormap old_c = ssi->cmap;
2161       Window old_w = ssi->screensaver_window;
2162
2163       if (p->verbose_p)
2164         {
2165           fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number);
2166           describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
2167 #if 0
2168           fprintf (stderr, "%s:                  from ", blurb());
2169           describe_visual (stderr, ssi->screen, ssi->current_visual,
2170                            was_installed_p);
2171 #endif
2172         }
2173
2174       reset_stderr (ssi);
2175       ssi->current_visual = new_v;
2176       ssi->current_depth = visual_depth(ssi->screen, new_v);
2177       ssi->cmap = 0;
2178       ssi->screensaver_window = 0;
2179
2180       initialize_screensaver_window_1 (ssi);
2181
2182       /* stderr_overlay_window is a child of screensaver_window, so we need
2183          to destroy that as well (actually, we just need to invalidate and
2184          drop our pointers to it, but this will destroy it, which is ok so
2185          long as it happens before old_w itself is destroyed.) */
2186       reset_stderr (ssi);
2187
2188       raise_window (si, True, True, False);
2189       store_vroot_property (si->dpy,
2190                             ssi->screensaver_window, ssi->screensaver_window);
2191
2192       /* Transfer any grabs from the old window to the new. */
2193       maybe_transfer_grabs (ssi, old_w, ssi->screensaver_window, ssi->number);
2194
2195       /* Now we can destroy the old window without horking our grabs. */
2196       XDestroyWindow (si->dpy, old_w);
2197
2198       if (p->verbose_p)
2199         fprintf (stderr, "%s: %d: destroyed old saver window 0x%lx.\n",
2200                  blurb(), ssi->number, (unsigned long) old_w);
2201
2202       if (old_c &&
2203           old_c != DefaultColormapOfScreen (ssi->screen) &&
2204           old_c != ssi->demo_cmap)
2205         XFreeColormap (si->dpy, old_c);
2206     }
2207
2208   return got_it;
2209 }