ftp://updates.redhat.com/enterprise/2.1AS/en/os/SRPMS/xscreensaver-3.33-4.rhel21...
[xscreensaver] / driver / windows.c
1 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
2  * xscreensaver, Copyright (c) 1991-2001 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
40 #ifdef HAVE_MIT_SAVER_EXTENSION
41 # include <X11/extensions/scrnsaver.h>
42 #endif /* HAVE_MIT_SAVER_EXTENSION */
43
44 #ifdef HAVE_XF86VMODE
45 # include <X11/extensions/xf86vmode.h>
46 #endif /* HAVE_XF86VMODE */
47
48
49 /* This file doesn't need the Xt headers, so stub these types out... */
50 #undef XtPointer
51 #define XtAppContext void*
52 #define XrmDatabase  void*
53 #define XtIntervalId void*
54 #define XtPointer    void*
55 #define Widget       void*
56
57 #include "xscreensaver.h"
58 #include "visual.h"
59 #include "fade.h"
60
61
62 extern int kill (pid_t, int);           /* signal() is in sys/signal.h... */
63
64 Atom XA_VROOT, XA_XSETROOT_ID;
65 Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
66 Atom XA_SCREENSAVER_STATUS;
67
68
69 extern saver_info *global_si_kludge;    /* I hate C so much... */
70
71 static void maybe_transfer_grabs (saver_screen_info *ssi,
72                                   Window old_w, Window new_w);
73
74 #define ALL_POINTER_EVENTS \
75         (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
76          LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
77          Button1MotionMask | Button2MotionMask | Button3MotionMask | \
78          Button4MotionMask | Button5MotionMask | ButtonMotionMask)
79
80
81 static const char *
82 grab_string(int status)
83 {
84   switch (status)
85     {
86     case GrabSuccess:     return "GrabSuccess";
87     case AlreadyGrabbed:  return "AlreadyGrabbed";
88     case GrabInvalidTime: return "GrabInvalidTime";
89     case GrabNotViewable: return "GrabNotViewable";
90     case GrabFrozen:      return "GrabFrozen";
91     default:
92       {
93         static char foo[255];
94         sprintf(foo, "unknown status: %d", status);
95         return foo;
96       }
97     }
98 }
99
100 static int
101 grab_kbd(saver_info *si, Window w)
102 {
103   saver_preferences *p = &si->prefs;
104   int status = XGrabKeyboard (si->dpy, w, True,
105                               /* I don't really understand Sync vs Async,
106                                  but these seem to work... */
107                               GrabModeSync, GrabModeAsync,
108                               CurrentTime);
109   if (status == GrabSuccess)
110     si->keyboard_grab_window = w;
111
112   if (p->verbose_p)
113     fprintf(stderr, "%s: grabbing keyboard on 0x%x... %s.\n",
114             blurb(), (unsigned long) w, grab_string(status));
115   return status;
116 }
117
118
119 static int
120 grab_mouse (saver_info *si, Window w, Cursor cursor)
121 {
122   saver_preferences *p = &si->prefs;
123   int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS,
124                              GrabModeAsync, GrabModeAsync, w,
125                              cursor, CurrentTime);
126   if (status == GrabSuccess)
127     si->mouse_grab_window = w;
128
129   if (p->verbose_p)
130     fprintf(stderr, "%s: grabbing mouse on 0x%x... %s.\n",
131             blurb(), (unsigned long) w, grab_string(status));
132   return status;
133 }
134
135
136 static void
137 ungrab_kbd(saver_info *si)
138 {
139   saver_preferences *p = &si->prefs;
140   XUngrabKeyboard(si->dpy, CurrentTime);
141   if (p->verbose_p)
142     fprintf(stderr, "%s: ungrabbing keyboard (was 0x%x).\n", blurb(),
143             (unsigned long) si->keyboard_grab_window);
144   si->keyboard_grab_window = 0;
145 }
146
147
148 static void
149 ungrab_mouse(saver_info *si)
150 {
151   saver_preferences *p = &si->prefs;
152   XUngrabPointer(si->dpy, CurrentTime);
153   if (p->verbose_p)
154     fprintf(stderr, "%s: ungrabbing mouse (was 0x%x).\n", blurb(),
155             (unsigned long) si->mouse_grab_window);
156   si->mouse_grab_window = 0;
157 }
158
159
160 static Bool
161 grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
162 {
163   Status mstatus, kstatus;
164   int i;
165   int retries = 4;
166
167   for (i = 0; i < retries; i++)
168     {
169       XSync (si->dpy, False);
170       kstatus = grab_kbd (si, window);
171       if (kstatus == GrabSuccess)
172         break;
173
174       /* else, wait a second and try to grab again. */
175       sleep (1);
176     }
177
178   if (kstatus != GrabSuccess)
179     fprintf (stderr, "%s: couldn't grab keyboard!  (%s)\n",
180              blurb(), grab_string(kstatus));
181
182   for (i = 0; i < retries; i++)
183     {
184       XSync (si->dpy, False);
185       mstatus = grab_mouse (si, window, cursor);
186       if (mstatus == GrabSuccess)
187         break;
188
189       /* else, wait a second and try to grab again. */
190       sleep (1);
191     }
192
193   if (mstatus != GrabSuccess)
194     fprintf (stderr, "%s: couldn't grab pointer!  (%s)\n",
195              blurb(), grab_string(mstatus));
196
197   return (kstatus == GrabSuccess ||
198           mstatus == GrabSuccess);
199 }
200
201 static void
202 ungrab_keyboard_and_mouse (saver_info *si)
203 {
204   ungrab_mouse (si);
205   ungrab_kbd (si);
206 }
207
208
209 int
210 move_mouse_grab (saver_info *si, Window to, Cursor cursor)
211 {
212   Window old = si->mouse_grab_window;
213
214   if (old == 0)
215     return grab_mouse (si, to, cursor);
216   else
217     {
218       saver_preferences *p = &si->prefs;
219       int status;
220
221       XSync (si->dpy, False);
222       XGrabServer (si->dpy);                    /* ############ DANGER! */
223       XSync (si->dpy, False);
224
225       if (p->verbose_p)
226         fprintf(stderr, "%s: grabbing server...\n", blurb());
227
228       ungrab_mouse (si);
229       status = grab_mouse (si, to, cursor);
230
231       if (status != GrabSuccess)   /* Augh! */
232         {
233           sleep (1);               /* Note dramatic evil of sleeping
234                                       with server grabbed. */
235           XSync (si->dpy, False);
236           status = grab_mouse (si, to, cursor);
237         }
238
239       if (status != GrabSuccess)   /* Augh!  Try to get the old one back... */
240         grab_mouse (si, to, cursor);
241
242       XUngrabServer (si->dpy);
243       XSync (si->dpy, False);                   /* ###### (danger over) */
244
245       if (p->verbose_p)
246         fprintf(stderr, "%s: ungrabbing server.\n", blurb());
247
248       return status;
249     }
250 }
251
252
253 /* Prints an error message to stderr and returns True if there is another
254    xscreensaver running already.  Silently returns False otherwise. */
255 Bool
256 ensure_no_screensaver_running (Display *dpy, Screen *screen)
257 {
258   Bool status = 0;
259   int i;
260   Window root = RootWindowOfScreen (screen);
261   Window root2, parent, *kids;
262   unsigned int nkids;
263   XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
264
265   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
266     abort ();
267   if (root != root2)
268     abort ();
269   if (parent)
270     abort ();
271   for (i = 0; i < nkids; i++)
272     {
273       Atom type;
274       int format;
275       unsigned long nitems, bytesafter;
276       char *version;
277
278       if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
279                               False, XA_STRING, &type, &format, &nitems,
280                               &bytesafter, (unsigned char **) &version)
281           == Success
282           && type != None)
283         {
284           char *id;
285           if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
286                                    False, XA_STRING, &type, &format, &nitems,
287                                    &bytesafter, (unsigned char **) &id)
288               == Success
289               || type == None)
290             id = "???";
291
292           fprintf (stderr,
293       "%s: already running on display %s (window 0x%x)\n from process %s.\n",
294                    blurb(), DisplayString (dpy), (int) kids [i], id);
295           status = True;
296         }
297     }
298
299   if (kids) XFree ((char *) kids);
300   XSync (dpy, False);
301   XSetErrorHandler (old_handler);
302   return status;
303 }
304
305
306 \f
307 /* Virtual-root hackery */
308
309 #ifdef _VROOT_H_
310 ERROR!  You must not include vroot.h in this file.
311 #endif
312
313 static void
314 store_vroot_property (Display *dpy, Window win, Window value)
315 {
316 #if 0
317   if (p->verbose_p)
318     fprintf (stderr,
319              "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(), 
320              win,
321              (win == screensaver_window ? "ScreenSaver" :
322               (win == real_vroot ? "VRoot" :
323                (win == real_vroot_value ? "Vroot_value" : "???"))),
324              value,
325              (value == screensaver_window ? "ScreenSaver" :
326               (value == real_vroot ? "VRoot" :
327                (value == real_vroot_value ? "Vroot_value" : "???"))));
328 #endif
329   XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
330                    (unsigned char *) &value, 1);
331 }
332
333 static void
334 remove_vroot_property (Display *dpy, Window win)
335 {
336 #if 0
337   if (p->verbose_p)
338     fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win, 
339              (win == screensaver_window ? "ScreenSaver" :
340               (win == real_vroot ? "VRoot" :
341                (win == real_vroot_value ? "Vroot_value" : "???"))));
342 #endif
343   XDeleteProperty (dpy, win, XA_VROOT);
344 }
345
346
347 static void
348 kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
349 {
350   Atom type;
351   int format;
352   unsigned long nitems, bytesafter;
353   Pixmap *dataP = 0;
354
355   /* If the user has been using xv or xsetroot as a screensaver (to display
356      an image on the screensaver window, as a kind of slideshow) then the
357      pixmap and its associated color cells have been put in RetainPermanent
358      CloseDown mode.  Since we're not destroying the xscreensaver window,
359      but merely unmapping it, we need to free these resources or those
360      colormap cells will stay allocated while the screensaver is off.  (We
361      could just delete the screensaver window and recreate it later, but
362      that could cause other problems.)  This code does an atomic read-and-
363      delete of the _XSETROOT_ID property, and if it held a pixmap, then we
364      cause the RetainPermanent resources of the client which created it
365      (and which no longer exists) to be freed.
366    */
367   if (XGetWindowProperty (dpy, window, XA_XSETROOT_ID, 0, 1,
368                           True, AnyPropertyType, &type, &format, &nitems, 
369                           &bytesafter, (unsigned char **) &dataP)
370       == Success
371       && type != None)
372     {
373       if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
374           nitems == 1 && bytesafter == 0)
375         {
376           if (verbose_p)
377             fprintf (stderr, "%s: destroying xsetroot data (0x%lX).\n",
378                      blurb(), *dataP);
379           XKillClient (dpy, *dataP);
380         }
381       else
382         fprintf (stderr, "%s: deleted unrecognised _XSETROOT_ID property: \n\
383         %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
384                  blurb(), (unsigned long) dataP, (dataP ? *dataP : 0), type,
385                  format, nitems, bytesafter);
386     }
387 }
388
389
390 static void handle_signals (saver_info *si, Bool on_p);
391
392 static void
393 save_real_vroot (saver_screen_info *ssi)
394 {
395   saver_info *si = ssi->global;
396   Display *dpy = si->dpy;
397   Screen *screen = ssi->screen;
398   int i;
399   Window root = RootWindowOfScreen (screen);
400   Window root2, parent, *kids;
401   unsigned int nkids;
402   XErrorHandler old_handler;
403
404   /* It's possible that a window might be deleted between our call to
405      XQueryTree() and our call to XGetWindowProperty().  Don't die if
406      that happens (but just ignore that window, it's not the one we're
407      interested in anyway.)
408    */
409   XSync (dpy, False);
410   old_handler = XSetErrorHandler (BadWindow_ehandler);
411   XSync (dpy, False);
412
413   ssi->real_vroot = 0;
414   ssi->real_vroot_value = 0;
415   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
416     abort ();
417   if (root != root2)
418     abort ();
419   if (parent)
420     abort ();
421   for (i = 0; i < nkids; i++)
422     {
423       Atom type;
424       int format;
425       unsigned long nitems, bytesafter;
426       Window *vrootP = 0;
427
428       if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
429                               &type, &format, &nitems, &bytesafter,
430                               (unsigned char **) &vrootP)
431           != Success)
432         continue;
433       if (! vrootP)
434         continue;
435       if (ssi->real_vroot)
436         {
437           if (*vrootP == ssi->screensaver_window) abort ();
438           fprintf (stderr,
439             "%s: more than one virtual root window found (0x%x and 0x%x).\n",
440                    blurb(), (int) ssi->real_vroot, (int) kids [i]);
441           exit (1);
442         }
443       ssi->real_vroot = kids [i];
444       ssi->real_vroot_value = *vrootP;
445     }
446
447   XSync (dpy, False);
448   XSetErrorHandler (old_handler);
449   XSync (dpy, False);
450
451   if (ssi->real_vroot)
452     {
453       handle_signals (si, True);
454       remove_vroot_property (si->dpy, ssi->real_vroot);
455       XSync (dpy, False);
456     }
457
458   XFree ((char *) kids);
459 }
460
461
462 static Bool
463 restore_real_vroot_2 (saver_screen_info *ssi)
464 {
465   saver_info *si = ssi->global;
466   saver_preferences *p = &si->prefs;
467   if (p->verbose_p && ssi->real_vroot)
468     fprintf (stderr,
469              "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
470              blurb(), (unsigned long) ssi->real_vroot);
471   remove_vroot_property (si->dpy, ssi->screensaver_window);
472   if (ssi->real_vroot)
473     {
474       store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value);
475       ssi->real_vroot = 0;
476       ssi->real_vroot_value = 0;
477       /* make sure the property change gets there before this process
478          terminates!  We might be doing this because we have intercepted
479          SIGTERM or something. */
480       XSync (si->dpy, False);
481       return True;
482     }
483   return False;
484 }
485
486 static Bool
487 restore_real_vroot_1 (saver_info *si)
488 {
489   int i;
490   Bool did_any = False;
491   for (i = 0; i < si->nscreens; i++)
492     {
493       saver_screen_info *ssi = &si->screens[i];
494       if (restore_real_vroot_2 (ssi))
495         did_any = True;
496     }
497   return did_any;
498 }
499
500 void
501 restore_real_vroot (saver_info *si)
502 {
503   if (restore_real_vroot_1 (si))
504     handle_signals (si, False);
505 }
506
507 \f
508 /* Signal hackery to ensure that the vroot doesn't get left in an 
509    inconsistent state
510  */
511
512 const char *
513 signal_name(int signal)
514 {
515   switch (signal) {
516   case SIGHUP:    return "SIGHUP";
517   case SIGINT:    return "SIGINT";
518   case SIGQUIT:   return "SIGQUIT";
519   case SIGILL:    return "SIGILL";
520   case SIGTRAP:   return "SIGTRAP";
521 #ifdef SIGABRT
522   case SIGABRT:   return "SIGABRT";
523 #endif
524   case SIGFPE:    return "SIGFPE";
525   case SIGKILL:   return "SIGKILL";
526   case SIGBUS:    return "SIGBUS";
527   case SIGSEGV:   return "SIGSEGV";
528   case SIGPIPE:   return "SIGPIPE";
529   case SIGALRM:   return "SIGALRM";
530   case SIGTERM:   return "SIGTERM";
531 #ifdef SIGSTOP
532   case SIGSTOP:   return "SIGSTOP";
533 #endif
534 #ifdef SIGCONT
535   case SIGCONT:   return "SIGCONT";
536 #endif
537 #ifdef SIGUSR1
538   case SIGUSR1:   return "SIGUSR1";
539 #endif
540 #ifdef SIGUSR2
541   case SIGUSR2:   return "SIGUSR2";
542 #endif
543 #ifdef SIGEMT
544   case SIGEMT:    return "SIGEMT";
545 #endif
546 #ifdef SIGSYS
547   case SIGSYS:    return "SIGSYS";
548 #endif
549 #ifdef SIGCHLD
550   case SIGCHLD:   return "SIGCHLD";
551 #endif
552 #ifdef SIGPWR
553   case SIGPWR:    return "SIGPWR";
554 #endif
555 #ifdef SIGWINCH
556   case SIGWINCH:  return "SIGWINCH";
557 #endif
558 #ifdef SIGURG
559   case SIGURG:    return "SIGURG";
560 #endif
561 #ifdef SIGIO
562   case SIGIO:     return "SIGIO";
563 #endif
564 #ifdef SIGVTALRM
565   case SIGVTALRM: return "SIGVTALRM";
566 #endif
567 #ifdef SIGXCPU
568   case SIGXCPU:   return "SIGXCPU";
569 #endif
570 #ifdef SIGXFSZ
571   case SIGXFSZ:   return "SIGXFSZ";
572 #endif
573 #ifdef SIGDANGER
574   case SIGDANGER: return "SIGDANGER";
575 #endif
576   default:
577     {
578       static char buf[50];
579       sprintf(buf, "signal %d\n", signal);
580       return buf;
581     }
582   }
583 }
584
585
586
587 static RETSIGTYPE
588 restore_real_vroot_handler (int sig)
589 {
590   saver_info *si = global_si_kludge;    /* I hate C so much... */
591
592   signal (sig, SIG_DFL);
593   if (restore_real_vroot_1 (si))
594     fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
595              blurb(), signal_name(sig));
596   kill (getpid (), sig);
597 }
598
599 static void
600 catch_signal (saver_info *si, int sig, Bool on_p)
601 {
602   if (! on_p)
603     signal (sig, SIG_DFL);
604   else
605     {
606       if (((long) signal (sig, restore_real_vroot_handler)) == -1L)
607         {
608           char buf [255];
609           sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
610           perror (buf);
611           saver_exit (si, 1, 0);
612         }
613     }
614 }
615
616 static void
617 handle_signals (saver_info *si, Bool on_p)
618 {
619 #if 0
620   if (on_p) fprintf (stderr, "handling signals\n");
621   else fprintf (stderr, "unhandling signals\n");
622 #endif
623
624   catch_signal (si, SIGHUP,  on_p);
625   catch_signal (si, SIGINT,  on_p);
626   catch_signal (si, SIGQUIT, on_p);
627   catch_signal (si, SIGILL,  on_p);
628   catch_signal (si, SIGTRAP, on_p);
629   catch_signal (si, SIGIOT,  on_p);
630   catch_signal (si, SIGABRT, on_p);
631 #ifdef SIGEMT
632   catch_signal (si, SIGEMT,  on_p);
633 #endif
634   catch_signal (si, SIGFPE,  on_p);
635   catch_signal (si, SIGBUS,  on_p);
636   catch_signal (si, SIGSEGV, on_p);
637 #ifdef SIGSYS
638   catch_signal (si, SIGSYS,  on_p);
639 #endif
640   catch_signal (si, SIGTERM, on_p);
641 #ifdef SIGXCPU
642   catch_signal (si, SIGXCPU, on_p);
643 #endif
644 #ifdef SIGXFSZ
645   catch_signal (si, SIGXFSZ, on_p);
646 #endif
647 #ifdef SIGDANGER
648   catch_signal (si, SIGDANGER, on_p);
649 #endif
650 }
651
652 void
653 saver_exit (saver_info *si, int status, const char *dump_core_reason)
654 {
655   saver_preferences *p = &si->prefs;
656   static Bool exiting = False;
657   Bool bugp;
658   Bool vrs;
659
660   if (exiting)
661     exit(status);
662
663   exiting = True;
664   
665   vrs = restore_real_vroot_1 (si);
666   emergency_kill_subproc (si);
667   shutdown_stderr (si);
668
669   if (p->verbose_p && vrs)
670     fprintf (real_stderr, "%s: old vroot restored.\n", blurb());
671
672   fflush(real_stdout);
673
674 #ifdef VMS      /* on VMS, 1 is the "normal" exit code instead of 0. */
675   if (status == 0) status = 1;
676   else if (status == 1) status = -1;
677 #endif
678
679   bugp = !!dump_core_reason;
680
681   if (si->prefs.debug_p && !dump_core_reason)
682     dump_core_reason = "because of -debug";
683
684   if (dump_core_reason)
685     {
686       /* Note that the Linux man page for setuid() says If uid is
687          different from the old effective uid, the process will be
688          forbidden from leaving core dumps.
689       */
690       char cwd[4096]; /* should really be PATH_MAX, but who cares. */
691       cwd[0] = 0;
692       fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
693               dump_core_reason);
694
695       if (bugp)
696         fprintf(real_stderr,
697                 "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
698                 "\t\tfor bug reporting information.\n\n",
699                 blurb());
700
701 # if defined(HAVE_GETCWD)
702       if (!getcwd (cwd, sizeof(cwd)))
703 # elif defined(HAVE_GETWD)
704       if (!getwd (cwd))
705 # endif
706         strcpy(cwd, "unknown.");
707
708       fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
709       describe_uids (si, real_stderr);
710
711       /* Do this to drop a core file, so that we can get a stack trace. */
712       abort();
713     }
714
715   exit (status);
716 }
717
718 \f
719 /* Managing the actual screensaver window */
720
721 Bool
722 window_exists_p (Display *dpy, Window window)
723 {
724   XErrorHandler old_handler;
725   XWindowAttributes xgwa;
726   xgwa.screen = 0;
727   old_handler = XSetErrorHandler (BadWindow_ehandler);
728   XGetWindowAttributes (dpy, window, &xgwa);
729   XSync (dpy, False);
730   XSetErrorHandler (old_handler);
731   return (xgwa.screen != 0);
732 }
733
734 static void
735 store_saver_id (saver_screen_info *ssi)
736 {
737   XClassHint class_hints;
738   saver_info *si = ssi->global;
739   unsigned long pid = (unsigned long) getpid ();
740   char buf[20];
741   struct passwd *p = getpwuid (getuid ());
742   const char *name, *host;
743   char *id;
744
745   /* First store the name and class on the window.
746    */
747   class_hints.res_name = progname;
748   class_hints.res_class = progclass;
749   XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
750   XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
751
752   /* Then store the xscreensaver version number.
753    */
754   XChangeProperty (si->dpy, ssi->screensaver_window,
755                    XA_SCREENSAVER_VERSION,
756                    XA_STRING, 8, PropModeReplace,
757                    (unsigned char *) si->version,
758                    strlen (si->version));
759
760   /* Now store the XSCREENSAVER_ID property, that says what user and host
761      xscreensaver is running as.
762    */
763
764   if (p && p->pw_name && *p->pw_name)
765     name = p->pw_name;
766   else if (p)
767     {
768       sprintf (buf, "%lu", (unsigned long) p->pw_uid);
769       name = buf;
770     }
771   else
772     name = "???";
773
774 # if defined(HAVE_UNAME)
775   {
776     struct utsname uts;
777     if (uname (&uts) < 0)
778       host = "???";
779     else
780       host = uts.nodename;
781   }
782 # elif defined(VMS)
783   host = getenv("SYS$NODE");
784 # else  /* !HAVE_UNAME && !VMS */
785   host = "???";
786 # endif /* !HAVE_UNAME && !VMS */
787
788   id = (char *) malloc (strlen(name) + strlen(host) + 50);
789   sprintf (id, "%lu (%s@%s)", pid, name, host);
790
791   XChangeProperty (si->dpy, ssi->screensaver_window,
792                    XA_SCREENSAVER_ID, XA_STRING,
793                    8, PropModeReplace,
794                    (unsigned char *) id, strlen (id));
795   free (id);
796 }
797
798
799 void
800 store_saver_status (saver_info *si)
801 {
802   CARD32 *status;
803   int size = si->nscreens + 2;
804   int i;
805
806   status = (CARD32 *) calloc (size, sizeof(CARD32));
807
808   status[0] = (CARD32) (si->screen_blanked_p
809                         ? (si->locked_p ? XA_LOCK : XA_BLANK)
810                         : 0);
811   status[1] = (CARD32) si->blank_time;
812
813   for (i = 0; i < si->nscreens; i++)
814     {
815       saver_screen_info *ssi = &si->screens[i];
816       status [2 + i] = ssi->current_hack + 1;
817     }
818
819   XChangeProperty (si->dpy,
820                    RootWindow (si->dpy, 0),  /* always screen #0 */
821                    XA_SCREENSAVER_STATUS,
822                    XA_INTEGER, 32, PropModeReplace,
823                    (unsigned char *) status, size);
824 }
825
826
827
828 /* Returns the area of the screen which the xscreensaver window should cover.
829    Normally this is the whole screen, but if the X server's root window is
830    actually larger than the monitor's displayable area, then we want to
831    operate in the currently-visible portion of the desktop instead.
832  */
833 void
834 get_screen_viewport (saver_screen_info *ssi,
835                      int *x_ret, int *y_ret,
836                      int *w_ret, int *h_ret,
837                      Bool verbose_p)
838 {
839   int w = WidthOfScreen (ssi->screen);
840   int h = HeightOfScreen (ssi->screen);
841
842 #ifdef HAVE_XF86VMODE
843   saver_info *si = ssi->global;
844   int screen_no = screen_number (ssi->screen);
845   int op, event, error;
846   int dot;
847   XF86VidModeModeLine ml;
848   int x, y;
849
850   /* Check for Xinerama first, because the VidModeExtension is broken
851      when Xinerama is present.  Wheee!
852    */
853
854   if (!XQueryExtension (si->dpy, "XINERAMA", &op, &event, &error) &&
855       XF86VidModeQueryExtension (si->dpy, &event, &error) &&
856       XF86VidModeGetModeLine (si->dpy, screen_no, &dot, &ml) &&
857       XF86VidModeGetViewPort (si->dpy, screen_no, &x, &y))
858     {
859       char msg[512];
860       *x_ret = x;
861       *y_ret = y;
862       *w_ret = ml.hdisplay;
863       *h_ret = ml.vdisplay;
864
865       if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
866         /* There is no viewport -- the screen does not scroll. */
867         return;
868
869
870       /* Apparently some versions of XFree86 return nonsense here!
871          I've had reports of 1024x768 viewports at -1936862040, -1953705044.
872          So, sanity-check the values and give up if they are out of range.
873        */
874       if (*x_ret <  0 || *x_ret >= w ||
875           *y_ret <  0 || *y_ret >= h ||
876           *w_ret <= 0 || *w_ret >  w ||
877           *h_ret <= 0 || *h_ret >  h)
878         {
879           static int warned_once = 0;
880           if (!warned_once)
881             {
882               fprintf (stderr, "\n"
883                   "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
884                   "%s: The XVidMode server extension is returning nonsense.\n"
885                   "%s: Please report this bug to your X server vendor.\n\n",
886                        blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
887                        blurb(), blurb());
888               warned_once = 1;
889             }
890           *x_ret = 0;
891           *y_ret = 0;
892           *w_ret = w;
893           *h_ret = h;
894           return;
895         }
896           
897
898       sprintf (msg, "%s: vp is %dx%d+%d+%d",
899                blurb(), *w_ret, *h_ret, *x_ret, *y_ret);
900
901
902       /* Apparently, though the server stores the X position in increments of
903          1 pixel, it will only make changes to the *display* in some other
904          increment.  With XF86_SVGA on a Thinkpad, the display only updates
905          in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
906          pixels in 16-bit mode.  I don't know what it does in 24- and 32-bit
907          mode, because I don't have enough video memory to find out.
908
909          I consider it a bug that XF86VidModeGetViewPort() is telling me the
910          server's *target* scroll position rather than the server's *actual*
911          scroll position.  David Dawes agrees, and says they may fix this in
912          XFree86 4.0, but it's notrivial.
913
914          He also confirms that this behavior is server-dependent, so the
915          actual scroll position cannot be reliably determined by the client.
916          So... that means the only solution is to provide a ``sandbox''
917          around the blackout window -- we make the window be up to N pixels
918          larger than the viewport on both the left and right sides.  That
919          means some part of the outer edges of each hack might not be
920          visible, but screw it.
921
922          I'm going to guess that 16 pixels is enough, and that the Y dimension
923          doesn't have this problem.
924
925          The drawback of doing this, of course, is that some of the screenhacks
926          will still look pretty stupid -- for example, "slidescreen" will cut
927          off the left and right edges of the grid, etc.
928       */
929 # define FUDGE 16
930       if (x > 0 && x < w - ml.hdisplay)  /* not at left edge or right edge */
931         {
932           /* Round X position down to next lower multiple of FUDGE.
933              Increase width by 2*FUDGE in case some server rounds up.
934            */
935           *x_ret = ((x - 1) / FUDGE) * FUDGE;
936           *w_ret += (FUDGE * 2);
937         }
938 # undef FUDGE
939
940       if (*x_ret != x ||
941           *y_ret != y ||
942           *w_ret != ml.hdisplay ||
943           *h_ret != ml.vdisplay)
944         sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
945                  *w_ret, *h_ret, *x_ret, *y_ret);
946
947       if (verbose_p)
948         fprintf (stderr, "%s.\n", msg);
949
950       return;
951     }
952
953 #endif /* HAVE_XF86VMODE */
954
955   *x_ret = 0;
956   *y_ret = 0;
957   *w_ret = w;
958   *h_ret = h;
959 }
960
961
962 static Bool error_handler_hit_p = False;
963
964 static int
965 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
966 {
967   error_handler_hit_p = True;
968   return 0;
969 }
970
971
972 /* Returns True if successful, False if an X error occurred.
973    We need this because other programs might have done things to
974    our window that will cause XChangeWindowAttributes() to fail:
975    if that happens, we give up, destroy the window, and re-create
976    it.
977  */
978 static Bool
979 safe_XChangeWindowAttributes (Display *dpy, Window window,
980                               unsigned long mask,
981                               XSetWindowAttributes *attrs)
982 {
983   XErrorHandler old_handler;
984   XSync (dpy, False);
985   error_handler_hit_p = False;
986   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
987
988   XChangeWindowAttributes (dpy, window, mask, attrs);
989
990   XSync (dpy, False);
991   XSetErrorHandler (old_handler);
992   XSync (dpy, False);
993
994   return (!error_handler_hit_p);
995 }
996
997
998 /* This might not be necessary, but just in case. */
999 static Bool
1000 safe_XConfigureWindow (Display *dpy, Window window,
1001                        unsigned long mask, XWindowChanges *changes)
1002 {
1003   XErrorHandler old_handler;
1004   XSync (dpy, False);
1005   error_handler_hit_p = False;
1006   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1007
1008   XConfigureWindow (dpy, window, mask, changes);
1009
1010   XSync (dpy, False);
1011   XSetErrorHandler (old_handler);
1012   XSync (dpy, False);
1013
1014   return (!error_handler_hit_p);
1015 }
1016
1017 /* This might not be necessary, but just in case. */
1018 static Bool
1019 safe_XDestroyWindow (Display *dpy, Window window)
1020 {
1021   XErrorHandler old_handler;
1022   XSync (dpy, False);
1023   error_handler_hit_p = False;
1024   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1025
1026   XDestroyWindow (dpy, window);
1027
1028   XSync (dpy, False);
1029   XSetErrorHandler (old_handler);
1030   XSync (dpy, False);
1031
1032   return (!error_handler_hit_p);
1033 }
1034
1035
1036 static void
1037 initialize_screensaver_window_1 (saver_screen_info *ssi)
1038 {
1039   saver_info *si = ssi->global;
1040   saver_preferences *p = &si->prefs;
1041   Bool install_cmap_p = ssi->install_cmap_p;   /* not p->install_cmap_p */
1042
1043   /* This resets the screensaver window as fully as possible, since there's
1044      no way of knowing what some random client may have done to us in the
1045      meantime.  We could just destroy and recreate the window, but that has
1046      its own set of problems...
1047    */
1048   XColor black;
1049   XSetWindowAttributes attrs;
1050   unsigned long attrmask;
1051   int x, y, width, height;
1052   static Bool printed_visual_info = False;  /* only print the message once. */
1053   Window horked_window = 0;
1054
1055   get_screen_viewport (ssi, &x, &y, &width, &height,
1056                        (p->verbose_p && !si->screen_blanked_p));
1057
1058   black.red = black.green = black.blue = 0;
1059
1060   if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
1061     ssi->cmap = 0;
1062
1063   if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
1064     /* It's not the default visual, so we have no choice but to install. */
1065     install_cmap_p = True;
1066
1067   if (install_cmap_p)
1068     {
1069       if (! ssi->cmap)
1070         {
1071           ssi->cmap = XCreateColormap (si->dpy,
1072                                        RootWindowOfScreen (ssi->screen),
1073                                       ssi->current_visual, AllocNone);
1074           if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
1075           ssi->black_pixel = black.pixel;
1076         }
1077     }
1078   else
1079     {
1080       Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
1081       if (ssi->cmap)
1082         {
1083           XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
1084           if (ssi->cmap != ssi->demo_cmap &&
1085               ssi->cmap != def_cmap)
1086             XFreeColormap (si->dpy, ssi->cmap);
1087         }
1088       ssi->cmap = def_cmap;
1089       ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
1090     }
1091
1092   attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
1093               CWBackPixel | CWBackingPixel | CWBorderPixel);
1094   attrs.override_redirect = True;
1095
1096   /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
1097      actually be reading these events during normal operation; but we still
1098      need to see Button events for demo-mode to work properly.
1099    */
1100   attrs.event_mask = (KeyPressMask | KeyReleaseMask |
1101                       ButtonPressMask | ButtonReleaseMask |
1102                       PointerMotionMask);
1103
1104   attrs.backing_store = NotUseful;
1105   attrs.colormap = ssi->cmap;
1106   attrs.background_pixel = ssi->black_pixel;
1107   attrs.backing_pixel = ssi->black_pixel;
1108   attrs.border_pixel = ssi->black_pixel;
1109
1110   if (p->debug_p) width = width / 2;
1111
1112   if (!p->verbose_p || printed_visual_info)
1113     ;
1114   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
1115     {
1116       fprintf (stderr, "%s: using default visual ", blurb());
1117       describe_visual (stderr, ssi->screen, ssi->current_visual,
1118                        install_cmap_p);
1119     }
1120   else
1121     {
1122       fprintf (stderr, "%s: using visual:   ", blurb());
1123       describe_visual (stderr, ssi->screen, ssi->current_visual,
1124                        install_cmap_p);
1125       fprintf (stderr, "%s: default visual: ", blurb());
1126       describe_visual (stderr, ssi->screen,
1127                        DefaultVisualOfScreen (ssi->screen),
1128                        ssi->install_cmap_p);
1129     }
1130   printed_visual_info = True;
1131
1132 #ifdef HAVE_MIT_SAVER_EXTENSION
1133   if (si->using_mit_saver_extension)
1134     {
1135       XScreenSaverInfo *info;
1136       Window root = RootWindowOfScreen (ssi->screen);
1137
1138 #if 0
1139       /* This call sets the server screensaver timeouts to what we think
1140          they should be (based on the resources and args xscreensaver was
1141          started with.)  It's important that we do this to sync back up
1142          with the server - if we have turned on prematurely, as by an
1143          ACTIVATE ClientMessage, then the server may decide to activate
1144          the screensaver while it's already active.  That's ok for us,
1145          since we would know to ignore that ScreenSaverActivate event,
1146          but a side effect of this would be that the server would map its
1147          saver window (which we then hide again right away) meaning that
1148          the bits currently on the screen get blown away.  Ugly. */
1149
1150       /* #### Ok, that doesn't work - when we tell the server that the
1151          screensaver is "off" it sends us a Deactivate event, which is
1152          sensible... but causes the saver to never come on.  Hmm. */
1153       disable_builtin_screensaver (si, True);
1154 #endif /* 0 */
1155
1156 #if 0
1157       /* #### The MIT-SCREEN-SAVER extension gives us access to the
1158          window that the server itself uses for saving the screen.
1159          However, using this window in any way, in particular, calling
1160          XScreenSaverSetAttributes() as below, tends to make the X server
1161          crash.  So fuck it, let's try and get along without using it...
1162
1163          It's also inconvenient to use this window because it doesn't
1164          always exist (though the ID is constant.)  So to use this
1165          window, we'd have to reimplement the ACTIVATE ClientMessage to
1166          tell the *server* to tell *us* to turn on, to cause the window
1167          to get created at the right time.  Gag.  */
1168       XScreenSaverSetAttributes (si->dpy, root,
1169                                  0, 0, width, height, 0,
1170                                  current_depth, InputOutput, visual,
1171                                  attrmask, &attrs);
1172       XSync (si->dpy, False);
1173 #endif /* 0 */
1174
1175       info = XScreenSaverAllocInfo ();
1176       XScreenSaverQueryInfo (si->dpy, root, info);
1177       ssi->server_mit_saver_window = info->window;
1178       if (! ssi->server_mit_saver_window) abort ();
1179       XFree (info);
1180     }
1181 #endif /* HAVE_MIT_SAVER_EXTENSION */
1182
1183   if (ssi->screensaver_window)
1184     {
1185       XWindowChanges changes;
1186       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1187       changes.x = x;
1188       changes.y = y;
1189       changes.width = width;
1190       changes.height = height;
1191       changes.border_width = 0;
1192
1193       if (! (safe_XConfigureWindow (si->dpy, ssi->screensaver_window,
1194                                   changesmask, &changes) &&
1195              safe_XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
1196                                            attrmask, &attrs)))
1197         {
1198           horked_window = ssi->screensaver_window;
1199           ssi->screensaver_window = 0;
1200         }
1201     }
1202
1203   if (!ssi->screensaver_window)
1204     {
1205       ssi->screensaver_window =
1206         XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
1207                        x, y, width, height,
1208                        0, ssi->current_depth, InputOutput,
1209                        ssi->current_visual, attrmask, &attrs);
1210
1211       reset_stderr (ssi);
1212
1213       if (horked_window)
1214         {
1215           fprintf (stderr,
1216             "%s: someone horked our saver window (0x%lx)!  Recreating it...\n",
1217                    blurb(), (unsigned long) horked_window);
1218           maybe_transfer_grabs (ssi, horked_window, ssi->screensaver_window);
1219           safe_XDestroyWindow (si->dpy, horked_window);
1220           horked_window = 0;
1221         }
1222
1223       if (p->verbose_p)
1224         fprintf (stderr, "%s: saver window is 0x%lx.\n",
1225                  blurb(), (unsigned long) ssi->screensaver_window);
1226     }
1227
1228   store_saver_id (ssi);       /* store window name and IDs */
1229
1230   if (!ssi->cursor)
1231     {
1232       Pixmap bit;
1233       bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
1234                                          "\000", 1, 1,
1235                                          BlackPixelOfScreen (ssi->screen),
1236                                          BlackPixelOfScreen (ssi->screen),
1237                                          1);
1238       ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
1239                                          0, 0);
1240       XFreePixmap (si->dpy, bit);
1241     }
1242
1243   XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
1244
1245   if (si->demoing_p)
1246     XUndefineCursor (si->dpy, ssi->screensaver_window);
1247   else
1248     XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1249 }
1250
1251 void
1252 initialize_screensaver_window (saver_info *si)
1253 {
1254   int i;
1255   for (i = 0; i < si->nscreens; i++)
1256     initialize_screensaver_window_1 (&si->screens[i]);
1257 }
1258
1259
1260 void 
1261 raise_window (saver_info *si,
1262               Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
1263 {
1264   saver_preferences *p = &si->prefs;
1265   int i;
1266
1267   if (si->demoing_p)
1268     inhibit_fade = True;
1269
1270   if (si->emergency_lock_p)
1271     inhibit_fade = True;
1272
1273   if (!dont_clear)
1274     initialize_screensaver_window (si);
1275
1276   reset_watchdog_timer (si, True);
1277
1278   if (p->fade_p && si->fading_possible_p && !inhibit_fade)
1279     {
1280       Window *current_windows = (Window *)
1281         calloc(sizeof(Window), si->nscreens);
1282       Colormap *current_maps = (Colormap *)
1283         calloc(sizeof(Colormap), si->nscreens);
1284
1285       for (i = 0; i < si->nscreens; i++)
1286         {
1287           saver_screen_info *ssi = &si->screens[i];
1288           current_windows[i] = ssi->screensaver_window;
1289           current_maps[i] = (between_hacks_p
1290                              ? ssi->cmap
1291                              : DefaultColormapOfScreen (ssi->screen));
1292           /* Ensure that the default background of the window is really black,
1293              not a pixmap or something.  (This does not clear the window.) */
1294           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1295                                 ssi->black_pixel);
1296         }
1297
1298       if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1299
1300       XGrabServer (si->dpy);                    /* ############ DANGER! */
1301
1302       /* Clear the stderr layer on each screen.
1303        */
1304       if (!dont_clear)
1305         for (i = 0; i < si->nscreens; i++)
1306           {
1307             saver_screen_info *ssi = &si->screens[i];
1308             if (ssi->stderr_overlay_window)
1309               /* Do this before the fade, since the stderr cmap won't fade
1310                  even if we uninstall it (beats me...) */
1311               clear_stderr (ssi);
1312           }
1313
1314       /* Note!  The server is grabbed, and this will take several seconds
1315          to complete! */
1316       fade_screens (si->dpy, current_maps, current_windows,
1317                     p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1318
1319       free(current_maps);
1320       free(current_windows);
1321       current_maps = 0;
1322       current_windows = 0;
1323
1324       if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1325
1326 #ifdef HAVE_MIT_SAVER_EXTENSION
1327       for (i = 0; i < si->nscreens; i++)
1328         {
1329           saver_screen_info *ssi = &si->screens[i];
1330           if (ssi->server_mit_saver_window &&
1331               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1332             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1333         }
1334 #endif /* HAVE_MIT_SAVER_EXTENSION */
1335
1336       XUngrabServer (si->dpy);
1337       XSync (si->dpy, False);                   /* ###### (danger over) */
1338     }
1339   else
1340     {
1341       for (i = 0; i < si->nscreens; i++)
1342         {
1343           saver_screen_info *ssi = &si->screens[i];
1344           if (!dont_clear)
1345             XClearWindow (si->dpy, ssi->screensaver_window);
1346           if (!dont_clear || ssi->stderr_overlay_window)
1347             clear_stderr (ssi);
1348           XMapRaised (si->dpy, ssi->screensaver_window);
1349 #ifdef HAVE_MIT_SAVER_EXTENSION
1350           if (ssi->server_mit_saver_window &&
1351               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1352             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1353 #endif /* HAVE_MIT_SAVER_EXTENSION */
1354         }
1355     }
1356
1357   for (i = 0; i < si->nscreens; i++)
1358     {
1359       saver_screen_info *ssi = &si->screens[i];
1360       if (ssi->cmap)
1361         XInstallColormap (si->dpy, ssi->cmap);
1362     }
1363 }
1364
1365 Bool
1366 blank_screen (saver_info *si)
1367 {
1368   int i;
1369   Bool ok;
1370
1371   /* Note: we do our grabs on the root window, not on the screensaver window.
1372      If we grabbed on the saver window, then the demo mode and lock dialog
1373      boxes wouldn't get any events.
1374    */
1375   ok = grab_keyboard_and_mouse (si,
1376                                 /*si->screens[0].screensaver_window,*/
1377                                 RootWindowOfScreen(si->screens[0].screen),
1378                                 (si->demoing_p
1379                                  ? 0
1380                                  : si->screens[0].cursor));
1381
1382
1383   if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1384     /* If we're using a server extension, then failure to get a grab is
1385        not a big deal -- even without the grab, we will still be able
1386        to un-blank when there is user activity, since the server will
1387        tell us. */
1388     ok = True;
1389
1390   if (!ok)
1391     return False;
1392
1393   for (i = 0; i < si->nscreens; i++)
1394     {
1395       saver_screen_info *ssi = &si->screens[i];
1396
1397       save_real_vroot (ssi);
1398       store_vroot_property (si->dpy,
1399                             ssi->screensaver_window,
1400                             ssi->screensaver_window);
1401
1402 #ifdef HAVE_XF86VMODE
1403       {
1404         int ev, er;
1405         if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
1406             !XF86VidModeGetViewPort (si->dpy, i,
1407                                      &ssi->blank_vp_x,
1408                                      &ssi->blank_vp_y))
1409           ssi->blank_vp_x = ssi->blank_vp_y = -1;
1410       }
1411 #endif /* HAVE_XF86VMODE */
1412     }
1413
1414   raise_window (si, False, False, False);
1415
1416   si->screen_blanked_p = True;
1417   si->blank_time = time ((time_t) 0);
1418   si->last_wall_clock_time = 0;
1419
1420   store_saver_status (si);  /* store blank time */
1421
1422   return True;
1423 }
1424
1425
1426 void
1427 unblank_screen (saver_info *si)
1428 {
1429   saver_preferences *p = &si->prefs;
1430   Bool unfade_p = (si->fading_possible_p && p->unfade_p);
1431   int i;
1432
1433   monitor_power_on (si);
1434   reset_watchdog_timer (si, False);
1435
1436   if (si->demoing_p)
1437     unfade_p = False;
1438
1439   if (unfade_p)
1440     {
1441       Window *current_windows = (Window *)
1442         calloc(sizeof(Window), si->nscreens);
1443
1444       for (i = 0; i < si->nscreens; i++)
1445         {
1446           saver_screen_info *ssi = &si->screens[i];
1447           current_windows[i] = ssi->screensaver_window;
1448           /* Ensure that the default background of the window is really black,
1449              not a pixmap or something.  (This does not clear the window.) */
1450           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1451                                 ssi->black_pixel);
1452         }
1453
1454       if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1455
1456
1457       XSync (si->dpy, False);
1458       XGrabServer (si->dpy);                    /* ############ DANGER! */
1459       XSync (si->dpy, False);
1460
1461       /* Clear the stderr layer on each screen.
1462        */
1463       for (i = 0; i < si->nscreens; i++)
1464         {
1465           saver_screen_info *ssi = &si->screens[i];
1466           clear_stderr (ssi);
1467         }
1468
1469       XUngrabServer (si->dpy);
1470       XSync (si->dpy, False);                   /* ###### (danger over) */
1471
1472
1473       fade_screens (si->dpy, 0, current_windows,
1474                     p->fade_seconds/1000, p->fade_ticks,
1475                     False, False);
1476
1477       free(current_windows);
1478       current_windows = 0;
1479
1480       if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1481     }
1482   else
1483     {
1484       for (i = 0; i < si->nscreens; i++)
1485         {
1486           saver_screen_info *ssi = &si->screens[i];
1487           if (ssi->cmap)
1488             {
1489               Colormap c = DefaultColormapOfScreen (ssi->screen);
1490               /* avoid technicolor */
1491               XClearWindow (si->dpy, ssi->screensaver_window);
1492               if (c) XInstallColormap (si->dpy, c);
1493             }
1494           XUnmapWindow (si->dpy, ssi->screensaver_window);
1495         }
1496     }
1497
1498
1499   /* If the focus window does has a non-default colormap, then install
1500      that colormap as well.  (On SGIs, this will cause both the root map
1501      and the focus map to be installed simultaniously.  It'd be nice to
1502      pick up the other colormaps that had been installed, too; perhaps
1503      XListInstalledColormaps could be used for that?)
1504    */
1505   {
1506     Window focus = 0;
1507     int revert_to;
1508     XGetInputFocus (si->dpy, &focus, &revert_to);
1509     if (focus && focus != PointerRoot && focus != None)
1510       {
1511         XWindowAttributes xgwa;
1512         xgwa.colormap = 0;
1513         XGetWindowAttributes (si->dpy, focus, &xgwa);
1514         if (xgwa.colormap &&
1515             xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1516           XInstallColormap (si->dpy, xgwa.colormap);
1517       }
1518   }
1519
1520
1521   for (i = 0; i < si->nscreens; i++)
1522     {
1523       saver_screen_info *ssi = &si->screens[i];
1524       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1525     }
1526
1527   store_saver_status (si);  /* store unblank time */
1528   ungrab_keyboard_and_mouse (si);
1529   restore_real_vroot (si);
1530
1531   /* Unmap the windows a second time, dammit -- just to avoid a race
1532      with the screen-grabbing hacks.  (I'm not sure if this is really
1533      necessary; I'm stabbing in the dark now.)
1534   */
1535   for (i = 0; i < si->nscreens; i++)
1536     XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1537
1538   si->screen_blanked_p = False;
1539   si->blank_time = time ((time_t) 0);
1540   si->last_wall_clock_time = 0;
1541
1542   store_saver_status (si);  /* store unblank time */
1543 }
1544
1545
1546 /* Transfer any grabs from the old window to the new.
1547    Actually I think none of this is necessary, since we always
1548    hold our grabs on the root window, but I wrote this before
1549    re-discovering that...
1550  */
1551 static void
1552 maybe_transfer_grabs (saver_screen_info *ssi,
1553                       Window old_w, Window new_w)
1554 {
1555   saver_info *si = ssi->global;
1556
1557   /* If the old window held our mouse grab, transfer the grab to the new
1558      window.  (Grab the server while so doing, to avoid a race condition.)
1559    */
1560   if (old_w == si->mouse_grab_window)
1561     {
1562       XGrabServer (si->dpy);            /* ############ DANGER! */
1563       ungrab_mouse (si);
1564       grab_mouse (si, ssi->screensaver_window,
1565                   (si->demoing_p
1566                    ? 0
1567                    : ssi->cursor));
1568       XUngrabServer (si->dpy);
1569       XSync (si->dpy, False);           /* ###### (danger over) */
1570     }
1571
1572   /* If the old window held our keyboard grab, transfer the grab to the new
1573      window.  (Grab the server while so doing, to avoid a race condition.)
1574    */
1575   if (old_w == si->keyboard_grab_window)
1576     {
1577       XGrabServer (si->dpy);            /* ############ DANGER! */
1578       ungrab_kbd(si);
1579       grab_kbd(si, ssi->screensaver_window);
1580       XUngrabServer (si->dpy);
1581       XSync (si->dpy, False);           /* ###### (danger over) */
1582     }
1583 }
1584
1585
1586
1587 Bool
1588 select_visual (saver_screen_info *ssi, const char *visual_name)
1589 {
1590   saver_info *si = ssi->global;
1591   saver_preferences *p = &si->prefs;
1592   Bool install_cmap_p = p->install_cmap_p;
1593   Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1594   Visual *new_v = 0;
1595   Bool got_it;
1596
1597   if (visual_name && *visual_name)
1598     {
1599       if (!strcmp(visual_name, "default-i") ||
1600           !strcmp(visual_name, "Default-i") ||
1601           !strcmp(visual_name, "Default-I")
1602           )
1603         {
1604           visual_name = "default";
1605           install_cmap_p = True;
1606         }
1607       else if (!strcmp(visual_name, "default-n") ||
1608                !strcmp(visual_name, "Default-n") ||
1609                !strcmp(visual_name, "Default-N"))
1610         {
1611           visual_name = "default";
1612           install_cmap_p = False;
1613         }
1614       else if (!strcmp(visual_name, "gl") ||
1615                !strcmp(visual_name, "Gl") ||
1616                !strcmp(visual_name, "GL"))
1617         {
1618           new_v = ssi->best_gl_visual;
1619           if (!new_v && p->verbose_p)
1620             fprintf (stderr, "%s: no GL visuals.\n", progname);
1621         }
1622
1623       if (!new_v)
1624         new_v = get_visual (ssi->screen, visual_name, True, False);
1625     }
1626   else
1627     {
1628       new_v = ssi->default_visual;
1629     }
1630
1631   got_it = !!new_v;
1632
1633   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1634     /* It's not the default visual, so we have no choice but to install. */
1635     install_cmap_p = True;
1636
1637   ssi->install_cmap_p = install_cmap_p;
1638
1639   if (new_v &&
1640       ((ssi->current_visual != new_v) ||
1641        (install_cmap_p != was_installed_p)))
1642     {
1643       Colormap old_c = ssi->cmap;
1644       Window old_w = ssi->screensaver_window;
1645
1646       if (p->verbose_p)
1647         {
1648           fprintf (stderr, "%s: switching to visual ", blurb());
1649           describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
1650 #if 0
1651           fprintf (stderr, "%s:                from ", blurb());
1652           describe_visual (stderr, ssi->screen, ssi->current_visual,
1653                            was_installed_p);
1654 #endif
1655         }
1656
1657       reset_stderr (ssi);
1658       ssi->current_visual = new_v;
1659       ssi->current_depth = visual_depth(ssi->screen, new_v);
1660       ssi->cmap = 0;
1661       ssi->screensaver_window = 0;
1662
1663       initialize_screensaver_window_1 (ssi);
1664
1665       /* stderr_overlay_window is a child of screensaver_window, so we need
1666          to destroy that as well (actually, we just need to invalidate and
1667          drop our pointers to it, but this will destroy it, which is ok so
1668          long as it happens before old_w itself is destroyed.) */
1669       reset_stderr (ssi);
1670
1671       raise_window (si, True, True, False);
1672       store_vroot_property (si->dpy,
1673                             ssi->screensaver_window, ssi->screensaver_window);
1674
1675       /* Transfer any grabs from the old window to the new. */
1676       maybe_transfer_grabs (ssi, old_w, ssi->screensaver_window);
1677
1678       /* Now we can destroy the old window without horking our grabs. */
1679       XDestroyWindow (si->dpy, old_w);
1680
1681       if (p->verbose_p)
1682         fprintf (stderr, "%s: destroyed old saver window 0x%lx.\n",
1683                  blurb(), (unsigned long) old_w);
1684
1685       if (old_c &&
1686           old_c != DefaultColormapOfScreen (ssi->screen) &&
1687           old_c != ssi->demo_cmap)
1688         XFreeColormap (si->dpy, old_c);
1689     }
1690
1691   return got_it;
1692 }