http://ftp.x.org/contrib/applications/xscreensaver-3.20.tar.gz
[xscreensaver] / driver / windows.c
1 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
2  * xscreensaver, Copyright (c) 1991-1998 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
655   if (p->verbose_p)    /* nobody cares about this */
656     {
657       if (vrs)
658         fprintf (real_stderr, "%s: vroot restored, exiting.\n", blurb());
659       else if (p->verbose_p)
660         fprintf (real_stderr, "%s: no vroot to restore; exiting.\n", blurb());
661     }
662
663   fflush(real_stdout);
664
665 #ifdef VMS      /* on VMS, 1 is the "normal" exit code instead of 0. */
666   if (status == 0) status = 1;
667   else if (status == 1) status = -1;
668 #endif
669
670   bugp = !!dump_core_reason;
671
672   if (si->prefs.debug_p && !dump_core_reason)
673     dump_core_reason = "because of -debug";
674
675   if (dump_core_reason)
676     {
677       /* Note that the Linux man page for setuid() says If uid is
678          different from the old effective uid, the process will be
679          forbidden from leaving core dumps.
680       */
681       char cwd[4096]; /* should really be PATH_MAX, but who cares. */
682       cwd[0] = 0;
683       fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
684               dump_core_reason);
685
686       if (bugp)
687         fprintf(real_stderr,
688                 "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
689                 "\t\tfor bug reporting information.\n\n",
690                 blurb());
691
692 # if defined(HAVE_GETCWD)
693       if (!getcwd (cwd, sizeof(cwd)))
694 # elif defined(HAVE_GETWD)
695       if (!getwd (cwd))
696 # endif
697         strcpy(cwd, "unknown.");
698
699       fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
700       describe_uids (si, real_stderr);
701
702       /* Do this to drop a core file, so that we can get a stack trace. */
703       abort();
704     }
705
706   exit (status);
707 }
708
709 \f
710 /* Managing the actual screensaver window */
711
712 Bool
713 window_exists_p (Display *dpy, Window window)
714 {
715   XErrorHandler old_handler;
716   XWindowAttributes xgwa;
717   xgwa.screen = 0;
718   old_handler = XSetErrorHandler (BadWindow_ehandler);
719   XGetWindowAttributes (dpy, window, &xgwa);
720   XSync (dpy, False);
721   XSetErrorHandler (old_handler);
722   return (xgwa.screen != 0);
723 }
724
725 static void
726 store_saver_id (saver_screen_info *ssi)
727 {
728   XClassHint class_hints;
729   saver_info *si = ssi->global;
730   unsigned long pid = (unsigned long) getpid ();
731   char buf[20];
732   struct passwd *p = getpwuid (getuid ());
733   const char *name, *host;
734   char *id;
735
736   /* First store the name and class on the window.
737    */
738   class_hints.res_name = progname;
739   class_hints.res_class = progclass;
740   XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
741   XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
742
743   /* Then store the xscreensaver version number.
744    */
745   XChangeProperty (si->dpy, ssi->screensaver_window,
746                    XA_SCREENSAVER_VERSION,
747                    XA_STRING, 8, PropModeReplace,
748                    (unsigned char *) si->version,
749                    strlen (si->version));
750
751   /* Now store the XSCREENSAVER_ID property, that says what user and host
752      xscreensaver is running as.
753    */
754
755   if (p && p->pw_name && *p->pw_name)
756     name = p->pw_name;
757   else if (p)
758     {
759       sprintf (buf, "%lu", (unsigned long) p->pw_uid);
760       name = buf;
761     }
762   else
763     name = "???";
764
765 # if defined(HAVE_UNAME)
766   {
767     struct utsname uts;
768     if (uname (&uts) < 0)
769       host = "???";
770     else
771       host = uts.nodename;
772   }
773 # elif defined(VMS)
774   host = getenv("SYS$NODE");
775 # else  /* !HAVE_UNAME && !VMS */
776   host = "???";
777 # endif /* !HAVE_UNAME && !VMS */
778
779   id = (char *) malloc (strlen(name) + strlen(host) + 50);
780   sprintf (id, "%lu (%s@%s)", pid, name, host);
781
782   XChangeProperty (si->dpy, ssi->screensaver_window,
783                    XA_SCREENSAVER_ID, XA_STRING,
784                    8, PropModeReplace,
785                    (unsigned char *) id, strlen (id));
786   free (id);
787 }
788
789
790 void
791 store_saver_status (saver_info *si)
792 {
793   CARD32 *status;
794   int size = si->nscreens + 2;
795   int i;
796
797   status = (CARD32 *) calloc (size, sizeof(CARD32));
798
799   status[0] = (CARD32) (si->screen_blanked_p
800                         ? (si->locked_p ? XA_LOCK : XA_BLANK)
801                         : 0);
802   status[1] = (CARD32) si->blank_time;
803
804   for (i = 0; i < si->nscreens; i++)
805     {
806       saver_screen_info *ssi = &si->screens[i];
807       status [2 + i] = ssi->current_hack + 1;
808     }
809
810   XChangeProperty (si->dpy,
811                    RootWindow (si->dpy, 0),  /* always screen #0 */
812                    XA_SCREENSAVER_STATUS,
813                    XA_INTEGER, 32, PropModeReplace,
814                    (unsigned char *) status, size);
815 }
816
817
818
819 /* Returns the area of the screen which the xscreensaver window should cover.
820    Normally this is the whole screen, but if the X server's root window is
821    actually larger than the monitor's displayable area, then we want to
822    operate in the currently-visible portion of the desktop instead.
823  */
824 void
825 get_screen_viewport (saver_screen_info *ssi,
826                      int *x_ret, int *y_ret,
827                      int *w_ret, int *h_ret,
828                      Bool verbose_p)
829 {
830   int w = WidthOfScreen (ssi->screen);
831   int h = HeightOfScreen (ssi->screen);
832
833 #ifdef HAVE_XF86VMODE
834   saver_info *si = ssi->global;
835   int screen_no = screen_number (ssi->screen);
836   int op, event, error;
837   int dot;
838   XF86VidModeModeLine ml;
839   int x, y;
840
841   /* Check for Xinerama first, because the VidModeExtension is broken
842      when Xinerama is present.  Wheee!
843    */
844
845   if (!XQueryExtension (si->dpy, "XINERAMA", &op, &event, &error) &&
846       XF86VidModeQueryExtension (si->dpy, &event, &error) &&
847       XF86VidModeGetModeLine (si->dpy, screen_no, &dot, &ml) &&
848       XF86VidModeGetViewPort (si->dpy, screen_no, &x, &y))
849     {
850       char msg[512];
851       *x_ret = x;
852       *y_ret = y;
853       *w_ret = ml.hdisplay;
854       *h_ret = ml.vdisplay;
855
856       if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
857         /* There is no viewport -- the screen does not scroll. */
858         return;
859
860
861       /* Apparently some versions of XFree86 return nonsense here!
862          I've had reports of 1024x768 viewports at -1936862040, -1953705044.
863          So, sanity-check the values and give up if they are out of range.
864        */
865       if (*x_ret <  0 || *x_ret >= w ||
866           *y_ret <  0 || *y_ret >= h ||
867           *w_ret <= 0 || *w_ret >  w ||
868           *h_ret <= 0 || *h_ret >  h)
869         {
870           static int warned_once = 0;
871           if (!warned_once)
872             {
873               fprintf (stderr, "\n"
874                   "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
875                   "%s: The XVidMode server extension is returning nonsense.\n"
876                   "%s: Please report this bug to your X server vendor.\n\n",
877                        blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
878                        blurb(), blurb());
879               warned_once = 1;
880             }
881           *x_ret = 0;
882           *y_ret = 0;
883           *w_ret = w;
884           *h_ret = h;
885           return;
886         }
887           
888
889       sprintf (msg, "%s: vp is %dx%d+%d+%d",
890                blurb(), *w_ret, *h_ret, *x_ret, *y_ret);
891
892
893       /* Apparently, though the server stores the X position in increments of
894          1 pixel, it will only make changes to the *display* in some other
895          increment.  With XF86_SVGA on a Thinkpad, the display only updates
896          in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
897          pixels in 16-bit mode.  I don't know what it does in 24- and 32-bit
898          mode, because I don't have enough video memory to find out.
899
900          I consider it a bug that XF86VidModeGetViewPort() is telling me the
901          server's *target* scroll position rather than the server's *actual*
902          scroll position.  David Dawes agrees, and says they may fix this in
903          XFree86 4.0, but it's notrivial.
904
905          He also confirms that this behavior is server-dependent, so the
906          actual scroll position cannot be reliably determined by the client.
907          So... that means the only solution is to provide a ``sandbox''
908          around the blackout window -- we make the window be up to N pixels
909          larger than the viewport on both the left and right sides.  That
910          means some part of the outer edges of each hack might not be
911          visible, but screw it.
912
913          I'm going to guess that 16 pixels is enough, and that the Y dimension
914          doesn't have this problem.
915
916          The drawback of doing this, of course, is that some of the screenhacks
917          will still look pretty stupid -- for example, "slidescreen" will cut
918          off the left and right edges of the grid, etc.
919       */
920 # define FUDGE 16
921       if (x > 0 && x < w - ml.hdisplay)  /* not at left edge or right edge */
922         {
923           /* Round X position down to next lower multiple of FUDGE.
924              Increase width by 2*FUDGE in case some server rounds up.
925            */
926           *x_ret = ((x - 1) / FUDGE) * FUDGE;
927           *w_ret += (FUDGE * 2);
928         }
929 # undef FUDGE
930
931       if (*x_ret != x ||
932           *y_ret != y ||
933           *w_ret != ml.hdisplay ||
934           *h_ret != ml.vdisplay)
935         sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
936                  *w_ret, *h_ret, *x_ret, *y_ret);
937
938       if (verbose_p)
939         fprintf (stderr, "%s.\n", msg);
940
941       return;
942     }
943
944 #endif /* HAVE_XF86VMODE */
945
946   *x_ret = 0;
947   *y_ret = 0;
948   *w_ret = w;
949   *h_ret = h;
950 }
951
952
953 static void
954 initialize_screensaver_window_1 (saver_screen_info *ssi)
955 {
956   saver_info *si = ssi->global;
957   saver_preferences *p = &si->prefs;
958   Bool install_cmap_p = ssi->install_cmap_p;   /* not p->install_cmap_p */
959
960   /* This resets the screensaver window as fully as possible, since there's
961      no way of knowing what some random client may have done to us in the
962      meantime.  We could just destroy and recreate the window, but that has
963      its own set of problems...
964    */
965   XColor black;
966   XSetWindowAttributes attrs;
967   unsigned long attrmask;
968   int x, y, width, height;
969   static Bool printed_visual_info = False;  /* only print the message once. */
970
971   get_screen_viewport (ssi, &x, &y, &width, &height,
972                        (p->verbose_p && !si->screen_blanked_p));
973
974   black.red = black.green = black.blue = 0;
975
976   if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
977     ssi->cmap = 0;
978
979   if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
980     /* It's not the default visual, so we have no choice but to install. */
981     install_cmap_p = True;
982
983   if (install_cmap_p)
984     {
985       if (! ssi->cmap)
986         {
987           ssi->cmap = XCreateColormap (si->dpy,
988                                        RootWindowOfScreen (ssi->screen),
989                                       ssi->current_visual, AllocNone);
990           if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
991           ssi->black_pixel = black.pixel;
992         }
993     }
994   else
995     {
996       Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
997       if (ssi->cmap)
998         {
999           XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
1000           if (ssi->cmap != ssi->demo_cmap &&
1001               ssi->cmap != def_cmap)
1002             XFreeColormap (si->dpy, ssi->cmap);
1003         }
1004       ssi->cmap = def_cmap;
1005       ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
1006     }
1007
1008   attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
1009               CWBackPixel | CWBackingPixel | CWBorderPixel);
1010   attrs.override_redirect = True;
1011
1012   /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
1013      actually be reading these events during normal operation; but we still
1014      need to see Button events for demo-mode to work properly.
1015    */
1016   attrs.event_mask = (KeyPressMask | KeyReleaseMask |
1017                       ButtonPressMask | ButtonReleaseMask |
1018                       PointerMotionMask);
1019
1020   attrs.backing_store = NotUseful;
1021   attrs.colormap = ssi->cmap;
1022   attrs.background_pixel = ssi->black_pixel;
1023   attrs.backing_pixel = ssi->black_pixel;
1024   attrs.border_pixel = ssi->black_pixel;
1025
1026   if (p->debug_p) width = width / 2;
1027
1028   if (!p->verbose_p || printed_visual_info)
1029     ;
1030   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
1031     {
1032       fprintf (stderr, "%s: using default visual ", blurb());
1033       describe_visual (stderr, ssi->screen, ssi->current_visual,
1034                        install_cmap_p);
1035     }
1036   else
1037     {
1038       fprintf (stderr, "%s: using visual:   ", blurb());
1039       describe_visual (stderr, ssi->screen, ssi->current_visual,
1040                        install_cmap_p);
1041       fprintf (stderr, "%s: default visual: ", blurb());
1042       describe_visual (stderr, ssi->screen,
1043                        DefaultVisualOfScreen (ssi->screen),
1044                        ssi->install_cmap_p);
1045     }
1046   printed_visual_info = True;
1047
1048 #ifdef HAVE_MIT_SAVER_EXTENSION
1049   if (si->using_mit_saver_extension)
1050     {
1051       XScreenSaverInfo *info;
1052       Window root = RootWindowOfScreen (ssi->screen);
1053
1054 #if 0
1055       /* This call sets the server screensaver timeouts to what we think
1056          they should be (based on the resources and args xscreensaver was
1057          started with.)  It's important that we do this to sync back up
1058          with the server - if we have turned on prematurely, as by an
1059          ACTIVATE ClientMessage, then the server may decide to activate
1060          the screensaver while it's already active.  That's ok for us,
1061          since we would know to ignore that ScreenSaverActivate event,
1062          but a side effect of this would be that the server would map its
1063          saver window (which we then hide again right away) meaning that
1064          the bits currently on the screen get blown away.  Ugly. */
1065
1066       /* #### Ok, that doesn't work - when we tell the server that the
1067          screensaver is "off" it sends us a Deactivate event, which is
1068          sensible... but causes the saver to never come on.  Hmm. */
1069       disable_builtin_screensaver (si, True);
1070 #endif /* 0 */
1071
1072 #if 0
1073       /* #### The MIT-SCREEN-SAVER extension gives us access to the
1074          window that the server itself uses for saving the screen.
1075          However, using this window in any way, in particular, calling
1076          XScreenSaverSetAttributes() as below, tends to make the X server
1077          crash.  So fuck it, let's try and get along without using it...
1078
1079          It's also inconvenient to use this window because it doesn't
1080          always exist (though the ID is constant.)  So to use this
1081          window, we'd have to reimplement the ACTIVATE ClientMessage to
1082          tell the *server* to tell *us* to turn on, to cause the window
1083          to get created at the right time.  Gag.  */
1084       XScreenSaverSetAttributes (si->dpy, root,
1085                                  0, 0, width, height, 0,
1086                                  current_depth, InputOutput, visual,
1087                                  attrmask, &attrs);
1088       XSync (si->dpy, False);
1089 #endif /* 0 */
1090
1091       info = XScreenSaverAllocInfo ();
1092       XScreenSaverQueryInfo (si->dpy, root, info);
1093       ssi->server_mit_saver_window = info->window;
1094       if (! ssi->server_mit_saver_window) abort ();
1095       XFree (info);
1096     }
1097 #endif /* HAVE_MIT_SAVER_EXTENSION */
1098
1099   if (ssi->screensaver_window)
1100     {
1101       XWindowChanges changes;
1102       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1103       changes.x = x;
1104       changes.y = y;
1105       changes.width = width;
1106       changes.height = height;
1107       changes.border_width = 0;
1108
1109       XConfigureWindow (si->dpy, ssi->screensaver_window,
1110                         changesmask, &changes);
1111       XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
1112                                attrmask, &attrs);
1113     }
1114   else
1115     {
1116       ssi->screensaver_window =
1117         XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
1118                        x, y, width, height,
1119                        0, ssi->current_depth, InputOutput,
1120                        ssi->current_visual, attrmask, &attrs);
1121
1122       reset_stderr (ssi);
1123       if (p->verbose_p)
1124         fprintf (stderr, "%s: saver window is 0x%lx.\n",
1125                  blurb(), (unsigned long) ssi->screensaver_window);
1126     }
1127
1128   store_saver_id (ssi);       /* store window name and IDs */
1129
1130   if (!ssi->cursor)
1131     {
1132       Pixmap bit;
1133       bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
1134                                          "\000", 1, 1,
1135                                          BlackPixelOfScreen (ssi->screen),
1136                                          BlackPixelOfScreen (ssi->screen),
1137                                          1);
1138       ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
1139                                          0, 0);
1140       XFreePixmap (si->dpy, bit);
1141     }
1142
1143   XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
1144
1145   if (si->demoing_p)
1146     XUndefineCursor (si->dpy, ssi->screensaver_window);
1147   else
1148     XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1149 }
1150
1151 void
1152 initialize_screensaver_window (saver_info *si)
1153 {
1154   int i;
1155   for (i = 0; i < si->nscreens; i++)
1156     initialize_screensaver_window_1 (&si->screens[i]);
1157 }
1158
1159
1160 void 
1161 raise_window (saver_info *si,
1162               Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
1163 {
1164   saver_preferences *p = &si->prefs;
1165   int i;
1166
1167   if (si->demoing_p)
1168     inhibit_fade = True;
1169
1170   if (si->emergency_lock_p)
1171     inhibit_fade = True;
1172
1173   if (!dont_clear)
1174     initialize_screensaver_window (si);
1175
1176   reset_watchdog_timer (si, True);
1177
1178   if (p->fade_p && si->fading_possible_p && !inhibit_fade)
1179     {
1180       Window *current_windows = (Window *)
1181         calloc(sizeof(Window), si->nscreens);
1182       Colormap *current_maps = (Colormap *)
1183         calloc(sizeof(Colormap), si->nscreens);
1184
1185       for (i = 0; i < si->nscreens; i++)
1186         {
1187           saver_screen_info *ssi = &si->screens[i];
1188           current_windows[i] = ssi->screensaver_window;
1189           current_maps[i] = (between_hacks_p
1190                              ? ssi->cmap
1191                              : DefaultColormapOfScreen (ssi->screen));
1192           /* Ensure that the default background of the window is really black,
1193              not a pixmap or something.  (This does not clear the window.) */
1194           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1195                                 ssi->black_pixel);
1196         }
1197
1198       if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1199
1200       XGrabServer (si->dpy);                    /* ############ DANGER! */
1201
1202       /* Clear the stderr layer on each screen.
1203        */
1204       if (!dont_clear)
1205         for (i = 0; i < si->nscreens; i++)
1206           {
1207             saver_screen_info *ssi = &si->screens[i];
1208             if (ssi->stderr_overlay_window)
1209               /* Do this before the fade, since the stderr cmap won't fade
1210                  even if we uninstall it (beats me...) */
1211               clear_stderr (ssi);
1212           }
1213
1214       /* Note!  The server is grabbed, and this will take several seconds
1215          to complete! */
1216       fade_screens (si->dpy, current_maps, current_windows,
1217                     p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1218
1219       free(current_maps);
1220       free(current_windows);
1221       current_maps = 0;
1222       current_windows = 0;
1223
1224       if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1225
1226 #ifdef HAVE_MIT_SAVER_EXTENSION
1227       for (i = 0; i < si->nscreens; i++)
1228         {
1229           saver_screen_info *ssi = &si->screens[i];
1230           if (ssi->server_mit_saver_window &&
1231               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1232             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1233         }
1234 #endif /* HAVE_MIT_SAVER_EXTENSION */
1235
1236       XUngrabServer (si->dpy);
1237       XSync (si->dpy, False);                   /* ###### (danger over) */
1238     }
1239   else
1240     {
1241       for (i = 0; i < si->nscreens; i++)
1242         {
1243           saver_screen_info *ssi = &si->screens[i];
1244           if (!dont_clear)
1245             XClearWindow (si->dpy, ssi->screensaver_window);
1246           if (!dont_clear || ssi->stderr_overlay_window)
1247             clear_stderr (ssi);
1248           XMapRaised (si->dpy, ssi->screensaver_window);
1249 #ifdef HAVE_MIT_SAVER_EXTENSION
1250           if (ssi->server_mit_saver_window &&
1251               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1252             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1253 #endif /* HAVE_MIT_SAVER_EXTENSION */
1254         }
1255     }
1256
1257   for (i = 0; i < si->nscreens; i++)
1258     {
1259       saver_screen_info *ssi = &si->screens[i];
1260       if (ssi->cmap)
1261         XInstallColormap (si->dpy, ssi->cmap);
1262     }
1263 }
1264
1265 Bool
1266 blank_screen (saver_info *si)
1267 {
1268   int i;
1269   Bool ok;
1270
1271   /* Note: we do our grabs on the root window, not on the screensaver window.
1272      If we grabbed on the saver window, then the demo mode and lock dialog
1273      boxes wouldn't get any events.
1274    */
1275   ok = grab_keyboard_and_mouse (si,
1276                                 /*si->screens[0].screensaver_window,*/
1277                                 RootWindowOfScreen(si->screens[0].screen),
1278                                 (si->demoing_p
1279                                  ? 0
1280                                  : si->screens[0].cursor));
1281
1282
1283   if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1284     /* If we're using a server extension, then failure to get a grab is
1285        not a big deal -- even without the grab, we will still be able
1286        to un-blank when there is user activity, since the server will
1287        tell us. */
1288     ok = True;
1289
1290   if (!ok)
1291     return False;
1292
1293   for (i = 0; i < si->nscreens; i++)
1294     {
1295       saver_screen_info *ssi = &si->screens[i];
1296
1297       save_real_vroot (ssi);
1298       store_vroot_property (si->dpy,
1299                             ssi->screensaver_window,
1300                             ssi->screensaver_window);
1301
1302 #ifdef HAVE_XF86VMODE
1303       {
1304         int ev, er;
1305         if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
1306             !XF86VidModeGetViewPort (si->dpy, i,
1307                                      &ssi->blank_vp_x,
1308                                      &ssi->blank_vp_y))
1309           ssi->blank_vp_x = ssi->blank_vp_y = -1;
1310       }
1311 #endif /* HAVE_XF86VMODE */
1312     }
1313
1314   raise_window (si, False, False, False);
1315
1316   si->screen_blanked_p = True;
1317   si->blank_time = time ((time_t) 0);
1318   si->last_wall_clock_time = 0;
1319
1320   store_saver_status (si);  /* store blank time */
1321
1322   return True;
1323 }
1324
1325
1326 void
1327 unblank_screen (saver_info *si)
1328 {
1329   saver_preferences *p = &si->prefs;
1330   Bool unfade_p = (si->fading_possible_p && p->unfade_p);
1331   int i;
1332
1333   monitor_power_on (si);
1334   reset_watchdog_timer (si, False);
1335
1336   if (si->demoing_p)
1337     unfade_p = False;
1338
1339   if (unfade_p)
1340     {
1341       Window *current_windows = (Window *)
1342         calloc(sizeof(Window), si->nscreens);
1343
1344       for (i = 0; i < si->nscreens; i++)
1345         {
1346           saver_screen_info *ssi = &si->screens[i];
1347           current_windows[i] = ssi->screensaver_window;
1348           /* Ensure that the default background of the window is really black,
1349              not a pixmap or something.  (This does not clear the window.) */
1350           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1351                                 ssi->black_pixel);
1352         }
1353
1354       if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1355
1356
1357       XSync (si->dpy, False);
1358       XGrabServer (si->dpy);                    /* ############ DANGER! */
1359       XSync (si->dpy, False);
1360
1361       /* Clear the stderr layer on each screen.
1362        */
1363       for (i = 0; i < si->nscreens; i++)
1364         {
1365           saver_screen_info *ssi = &si->screens[i];
1366           clear_stderr (ssi);
1367         }
1368
1369       XUngrabServer (si->dpy);
1370       XSync (si->dpy, False);                   /* ###### (danger over) */
1371
1372
1373       fade_screens (si->dpy, 0, current_windows,
1374                     p->fade_seconds/1000, p->fade_ticks,
1375                     False, False);
1376
1377       free(current_windows);
1378       current_windows = 0;
1379
1380       if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1381     }
1382   else
1383     {
1384       for (i = 0; i < si->nscreens; i++)
1385         {
1386           saver_screen_info *ssi = &si->screens[i];
1387           if (ssi->cmap)
1388             {
1389               Colormap c = DefaultColormapOfScreen (ssi->screen);
1390               /* avoid technicolor */
1391               XClearWindow (si->dpy, ssi->screensaver_window);
1392               if (c) XInstallColormap (si->dpy, c);
1393             }
1394           XUnmapWindow (si->dpy, ssi->screensaver_window);
1395         }
1396     }
1397
1398
1399   /* If the focus window does has a non-default colormap, then install
1400      that colormap as well.  (On SGIs, this will cause both the root map
1401      and the focus map to be installed simultaniously.  It'd be nice to
1402      pick up the other colormaps that had been installed, too; perhaps
1403      XListInstalledColormaps could be used for that?)
1404    */
1405   {
1406     Window focus = 0;
1407     int revert_to;
1408     XGetInputFocus (si->dpy, &focus, &revert_to);
1409     if (focus && focus != PointerRoot && focus != None)
1410       {
1411         XWindowAttributes xgwa;
1412         xgwa.colormap = 0;
1413         XGetWindowAttributes (si->dpy, focus, &xgwa);
1414         if (xgwa.colormap &&
1415             xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1416           XInstallColormap (si->dpy, xgwa.colormap);
1417       }
1418   }
1419
1420
1421   for (i = 0; i < si->nscreens; i++)
1422     {
1423       saver_screen_info *ssi = &si->screens[i];
1424       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1425     }
1426
1427   store_saver_status (si);  /* store unblank time */
1428   ungrab_keyboard_and_mouse (si);
1429   restore_real_vroot (si);
1430
1431   /* Unmap the windows a second time, dammit -- just to avoid a race
1432      with the screen-grabbing hacks.  (I'm not sure if this is really
1433      necessary; I'm stabbing in the dark now.)
1434   */
1435   for (i = 0; i < si->nscreens; i++)
1436     XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1437
1438   si->screen_blanked_p = False;
1439   si->blank_time = time ((time_t) 0);
1440   si->last_wall_clock_time = 0;
1441
1442   store_saver_status (si);  /* store unblank time */
1443 }
1444
1445
1446 Bool
1447 select_visual (saver_screen_info *ssi, const char *visual_name)
1448 {
1449   saver_info *si = ssi->global;
1450   saver_preferences *p = &si->prefs;
1451   Bool install_cmap_p = p->install_cmap_p;
1452   Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1453   Visual *new_v = 0;
1454   Bool got_it;
1455
1456   if (visual_name && *visual_name)
1457     {
1458       if (!strcmp(visual_name, "default-i") ||
1459           !strcmp(visual_name, "Default-i") ||
1460           !strcmp(visual_name, "Default-I")
1461           )
1462         {
1463           visual_name = "default";
1464           install_cmap_p = True;
1465         }
1466       else if (!strcmp(visual_name, "default-n") ||
1467                !strcmp(visual_name, "Default-n") ||
1468                !strcmp(visual_name, "Default-N"))
1469         {
1470           visual_name = "default";
1471           install_cmap_p = False;
1472         }
1473 #ifdef DAEMON_USE_GL
1474       else if (!strcmp(visual_name, "gl") ||
1475                !strcmp(visual_name, "Gl") ||
1476                !strcmp(visual_name, "GL"))
1477         {
1478           new_v = get_gl_visual (ssi->screen);
1479           if (!new_v && p->verbose_p)
1480             fprintf (stderr, "%s: no GL visuals.\n", progname);
1481         }
1482 #endif /* DAEMON_USE_GL */
1483
1484       if (!new_v)
1485         new_v = get_visual (ssi->screen, visual_name, True, False);
1486     }
1487   else
1488     {
1489       new_v = ssi->default_visual;
1490     }
1491
1492   got_it = !!new_v;
1493
1494   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1495     /* It's not the default visual, so we have no choice but to install. */
1496     install_cmap_p = True;
1497
1498   ssi->install_cmap_p = install_cmap_p;
1499
1500   if (new_v &&
1501       ((ssi->current_visual != new_v) ||
1502        (install_cmap_p != was_installed_p)))
1503     {
1504       Colormap old_c = ssi->cmap;
1505       Window old_w = ssi->screensaver_window;
1506
1507       if (p->verbose_p)
1508         {
1509           fprintf (stderr, "%s: switching to visual ", blurb());
1510           describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
1511 #if 0
1512           fprintf (stderr, "%s:                from ", blurb());
1513           describe_visual (stderr, ssi->screen, ssi->current_visual,
1514                            was_installed_p);
1515 #endif
1516         }
1517
1518       reset_stderr (ssi);
1519       ssi->current_visual = new_v;
1520       ssi->current_depth = visual_depth(ssi->screen, new_v);
1521       ssi->cmap = 0;
1522       ssi->screensaver_window = 0;
1523
1524       initialize_screensaver_window_1 (ssi);
1525
1526       /* stderr_overlay_window is a child of screensaver_window, so we need
1527          to destroy that as well (actually, we just need to invalidate and
1528          drop our pointers to it, but this will destroy it, which is ok so
1529          long as it happens before old_w itself is destroyed.) */
1530       reset_stderr (ssi);
1531
1532       raise_window (si, True, True, False);
1533       store_vroot_property (si->dpy,
1534                             ssi->screensaver_window, ssi->screensaver_window);
1535
1536
1537       /* Transfer the grabs from the old window to the new.
1538          Actually I think none of this is necessary, since we always
1539          hold our grabs on the root window, but I wrote this before
1540          re-discovering that...
1541        */
1542
1543
1544       /* If we're destroying the window that holds our mouse grab,
1545          transfer the grab to the new window.  (Grab the server while
1546          so doing, to avoid a race condition.)
1547        */
1548       if (old_w == si->mouse_grab_window)
1549         {
1550           XGrabServer (si->dpy);                /* ############ DANGER! */
1551           ungrab_mouse (si);
1552           grab_mouse (si, ssi->screensaver_window,
1553                       (si->demoing_p
1554                        ? 0
1555                        : ssi->cursor));
1556           XUngrabServer (si->dpy);
1557           XSync (si->dpy, False);               /* ###### (danger over) */
1558         }
1559
1560       /* If we're destroying the window that holds our keyboard grab,
1561          transfer the grab to the new window.  (Grab the server while
1562          so doing, to avoid a race condition.)
1563        */
1564       if (old_w == si->keyboard_grab_window)
1565         {
1566           XGrabServer (si->dpy);                /* ############ DANGER! */
1567           ungrab_kbd(si);
1568           grab_kbd(si, ssi->screensaver_window);
1569           XUngrabServer (si->dpy);
1570           XSync (si->dpy, False);               /* ###### (danger over) */
1571         }
1572
1573       /* Now we can destroy this window without horking our grabs. */
1574
1575       XDestroyWindow (si->dpy, old_w);
1576
1577       if (p->verbose_p)
1578         fprintf (stderr, "%s: destroyed old saver window 0x%lx.\n",
1579                  blurb(), (unsigned long) old_w);
1580
1581       if (old_c &&
1582           old_c != DefaultColormapOfScreen (ssi->screen) &&
1583           old_c != ssi->demo_cmap)
1584         XFreeColormap (si->dpy, old_c);
1585     }
1586
1587   return got_it;
1588 }