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