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