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