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