ftp://ftp.smr.ru/pub/0/FreeBSD/releases/distfiles/xscreensaver-3.16.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 event, error;
807   int dot;
808   XF86VidModeModeLine ml;
809   int x, y;
810
811   if (XF86VidModeQueryExtension (si->dpy, &event, &error) &&
812       XF86VidModeGetModeLine (si->dpy, screen_no, &dot, &ml) &&
813       XF86VidModeGetViewPort (si->dpy, screen_no, &x, &y))
814     {
815       char msg[512];
816       *x_ret = x;
817       *y_ret = y;
818       *w_ret = ml.hdisplay;
819       *h_ret = ml.vdisplay;
820
821       if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
822         /* There is no viewport -- the screen does not scroll. */
823         return;
824
825       sprintf (msg, "%s: vp is %dx%d+%d+%d",
826                blurb(), *w_ret, *h_ret, *x_ret, *y_ret);
827
828
829       /* Apparently, though the server stores the X position in increments of
830          1 pixel, it will only make changes to the *display* in some other
831          increment.  With XF86_SVGA on a Thinkpad, the display only updates
832          in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
833          pixels in 16-bit mode.  I don't know what it does in 24- and 32-bit
834          mode, because I don't have enough video memory to find out.
835
836          I consider it a bug that XF86VidModeGetViewPort() is telling me the
837          server's *target* scroll position rather than the server's *actual*
838          scroll position.  David Dawes agrees, and says they may fix this in
839          XFree86 4.0, but it's notrivial.
840
841          He also confirms that this behavior is server-dependent, so the
842          actual scroll position cannot be reliably determined by the client.
843          So... that means the only solution is to provide a ``sandbox''
844          around the blackout window -- we make the window be up to N pixels
845          larger than the viewport on both the left and right sides.  That
846          means some part of the outer edges of each hack might not be
847          visible, but screw it.
848
849          I'm going to guess that 16 pixels is enough, and that the Y dimension
850          doesn't have this problem.
851
852          The drawback of doing this, of course, is that some of the screenhacks
853          will still look pretty stupid -- for example, "slidescreen" will cut
854          off the left and right edges of the grid, etc.
855       */
856 # define FUDGE 16
857       if (x > 0 && x < w - ml.hdisplay)  /* not at left edge or right edge */
858         {
859           /* Round X position down to next lower multiple of FUDGE.
860              Increase width by 2*FUDGE in case some server rounds up.
861            */
862           *x_ret = ((x - 1) / FUDGE) * FUDGE;
863           *w_ret += (FUDGE * 2);
864         }
865 # undef FUDGE
866
867       if (*x_ret != x ||
868           *y_ret != y ||
869           *w_ret != ml.hdisplay ||
870           *h_ret != ml.vdisplay)
871         sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
872                  *w_ret, *h_ret, *x_ret, *y_ret);
873
874       if (verbose_p)
875         fprintf (stderr, "%s.\n", msg);
876
877       return;
878     }
879
880 #endif /* HAVE_XF86VMODE */
881
882   *x_ret = 0;
883   *y_ret = 0;
884   *w_ret = w;
885   *h_ret = h;
886 }
887
888
889 static void
890 initialize_screensaver_window_1 (saver_screen_info *ssi)
891 {
892   saver_info *si = ssi->global;
893   saver_preferences *p = &si->prefs;
894   Bool install_cmap_p = ssi->install_cmap_p;   /* not p->install_cmap_p */
895
896   /* This resets the screensaver window as fully as possible, since there's
897      no way of knowing what some random client may have done to us in the
898      meantime.  We could just destroy and recreate the window, but that has
899      its own set of problems...
900    */
901   XColor black;
902   XSetWindowAttributes attrs;
903   unsigned long attrmask;
904   int x, y, width, height;
905   static Bool printed_visual_info = False;  /* only print the message once. */
906
907   get_screen_viewport (ssi, &x, &y, &width, &height,
908                        (p->verbose_p && !si->screen_blanked_p));
909
910   black.red = black.green = black.blue = 0;
911
912   if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
913     ssi->cmap = 0;
914
915   if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
916     /* It's not the default visual, so we have no choice but to install. */
917     install_cmap_p = True;
918
919   if (install_cmap_p)
920     {
921       if (! ssi->cmap)
922         {
923           ssi->cmap = XCreateColormap (si->dpy,
924                                        RootWindowOfScreen (ssi->screen),
925                                       ssi->current_visual, AllocNone);
926           if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
927           ssi->black_pixel = black.pixel;
928         }
929     }
930   else
931     {
932       Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
933       if (ssi->cmap)
934         {
935           XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
936           if (ssi->cmap != ssi->demo_cmap &&
937               ssi->cmap != def_cmap)
938             XFreeColormap (si->dpy, ssi->cmap);
939         }
940       ssi->cmap = def_cmap;
941       ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
942     }
943
944   attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
945               CWBackPixel | CWBackingPixel | CWBorderPixel);
946   attrs.override_redirect = True;
947
948   /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
949      actually be reading these events during normal operation; but we still
950      need to see Button events for demo-mode to work properly.
951    */
952   attrs.event_mask = (KeyPressMask | KeyReleaseMask |
953                       ButtonPressMask | ButtonReleaseMask |
954                       PointerMotionMask);
955
956   attrs.backing_store = NotUseful;
957   attrs.colormap = ssi->cmap;
958   attrs.background_pixel = ssi->black_pixel;
959   attrs.backing_pixel = ssi->black_pixel;
960   attrs.border_pixel = ssi->black_pixel;
961
962   if (p->debug_p) width = width / 2;
963
964   if (!p->verbose_p || printed_visual_info)
965     ;
966   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
967     {
968       fprintf (stderr, "%s: using default visual ", blurb());
969       describe_visual (stderr, ssi->screen, ssi->current_visual,
970                        install_cmap_p);
971     }
972   else
973     {
974       fprintf (stderr, "%s: using visual:   ", blurb());
975       describe_visual (stderr, ssi->screen, ssi->current_visual,
976                        install_cmap_p);
977       fprintf (stderr, "%s: default visual: ", blurb());
978       describe_visual (stderr, ssi->screen,
979                        DefaultVisualOfScreen (ssi->screen),
980                        ssi->install_cmap_p);
981     }
982   printed_visual_info = True;
983
984 #ifdef HAVE_MIT_SAVER_EXTENSION
985   if (si->using_mit_saver_extension)
986     {
987       XScreenSaverInfo *info;
988       Window root = RootWindowOfScreen (ssi->screen);
989
990 #if 0
991       /* This call sets the server screensaver timeouts to what we think
992          they should be (based on the resources and args xscreensaver was
993          started with.)  It's important that we do this to sync back up
994          with the server - if we have turned on prematurely, as by an
995          ACTIVATE ClientMessage, then the server may decide to activate
996          the screensaver while it's already active.  That's ok for us,
997          since we would know to ignore that ScreenSaverActivate event,
998          but a side effect of this would be that the server would map its
999          saver window (which we then hide again right away) meaning that
1000          the bits currently on the screen get blown away.  Ugly. */
1001
1002       /* #### Ok, that doesn't work - when we tell the server that the
1003          screensaver is "off" it sends us a Deactivate event, which is
1004          sensible... but causes the saver to never come on.  Hmm. */
1005       disable_builtin_screensaver (si, True);
1006 #endif /* 0 */
1007
1008 #if 0
1009       /* #### The MIT-SCREEN-SAVER extension gives us access to the
1010          window that the server itself uses for saving the screen.
1011          However, using this window in any way, in particular, calling
1012          XScreenSaverSetAttributes() as below, tends to make the X server
1013          crash.  So fuck it, let's try and get along without using it...
1014
1015          It's also inconvenient to use this window because it doesn't
1016          always exist (though the ID is constant.)  So to use this
1017          window, we'd have to reimplement the ACTIVATE ClientMessage to
1018          tell the *server* to tell *us* to turn on, to cause the window
1019          to get created at the right time.  Gag.  */
1020       XScreenSaverSetAttributes (si->dpy, root,
1021                                  0, 0, width, height, 0,
1022                                  current_depth, InputOutput, visual,
1023                                  attrmask, &attrs);
1024       XSync (si->dpy, False);
1025 #endif /* 0 */
1026
1027       info = XScreenSaverAllocInfo ();
1028       XScreenSaverQueryInfo (si->dpy, root, info);
1029       ssi->server_mit_saver_window = info->window;
1030       if (! ssi->server_mit_saver_window) abort ();
1031       XFree (info);
1032     }
1033 #endif /* HAVE_MIT_SAVER_EXTENSION */
1034
1035   if (ssi->screensaver_window)
1036     {
1037       XWindowChanges changes;
1038       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1039       changes.x = x;
1040       changes.y = y;
1041       changes.width = width;
1042       changes.height = height;
1043       changes.border_width = 0;
1044
1045       XConfigureWindow (si->dpy, ssi->screensaver_window,
1046                         changesmask, &changes);
1047       XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
1048                                attrmask, &attrs);
1049     }
1050   else
1051     {
1052       ssi->screensaver_window =
1053         XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
1054                        x, y, width, height,
1055                        0, ssi->current_depth, InputOutput,
1056                        ssi->current_visual, attrmask, &attrs);
1057
1058       reset_stderr (ssi);
1059       store_activate_time(si, True);
1060       if (p->verbose_p)
1061         fprintf (stderr, "%s: saver window is 0x%lx.\n",
1062                  blurb(), (unsigned long) ssi->screensaver_window);
1063     }
1064
1065
1066   store_saver_id (ssi);
1067
1068   if (!ssi->cursor)
1069     {
1070       Pixmap bit;
1071       bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
1072                                          "\000", 1, 1,
1073                                          BlackPixelOfScreen (ssi->screen),
1074                                          BlackPixelOfScreen (ssi->screen),
1075                                          1);
1076       ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
1077                                          0, 0);
1078       XFreePixmap (si->dpy, bit);
1079     }
1080
1081   XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
1082
1083   if (si->demoing_p)
1084     XUndefineCursor (si->dpy, ssi->screensaver_window);
1085   else
1086     XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1087 }
1088
1089 void
1090 initialize_screensaver_window (saver_info *si)
1091 {
1092   int i;
1093   for (i = 0; i < si->nscreens; i++)
1094     initialize_screensaver_window_1 (&si->screens[i]);
1095 }
1096
1097
1098 void 
1099 raise_window (saver_info *si,
1100               Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
1101 {
1102   saver_preferences *p = &si->prefs;
1103   int i;
1104
1105   if (si->demoing_p)
1106     inhibit_fade = True;
1107
1108   if (si->emergency_lock_p)
1109     inhibit_fade = True;
1110
1111   if (!dont_clear)
1112     initialize_screensaver_window (si);
1113
1114   reset_watchdog_timer (si, True);
1115
1116   if (p->fade_p && si->fading_possible_p && !inhibit_fade)
1117     {
1118       Window *current_windows = (Window *)
1119         calloc(sizeof(Window), si->nscreens);
1120       Colormap *current_maps = (Colormap *)
1121         calloc(sizeof(Colormap), si->nscreens);
1122
1123       for (i = 0; i < si->nscreens; i++)
1124         {
1125           saver_screen_info *ssi = &si->screens[i];
1126           current_windows[i] = ssi->screensaver_window;
1127           current_maps[i] = (between_hacks_p
1128                              ? ssi->cmap
1129                              : DefaultColormapOfScreen (ssi->screen));
1130           /* Ensure that the default background of the window is really black,
1131              not a pixmap or something.  (This does not clear the window.) */
1132           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1133                                 ssi->black_pixel);
1134         }
1135
1136       if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1137
1138       XGrabServer (si->dpy);                    /* ############ DANGER! */
1139
1140       /* Clear the stderr layer on each screen.
1141        */
1142       if (!dont_clear)
1143         for (i = 0; i < si->nscreens; i++)
1144           {
1145             saver_screen_info *ssi = &si->screens[i];
1146             if (ssi->stderr_overlay_window)
1147               /* Do this before the fade, since the stderr cmap won't fade
1148                  even if we uninstall it (beats me...) */
1149               clear_stderr (ssi);
1150           }
1151
1152       /* Note!  The server is grabbed, and this will take several seconds
1153          to complete! */
1154       fade_screens (si->dpy, current_maps, current_windows,
1155                     p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1156
1157       free(current_maps);
1158       free(current_windows);
1159       current_maps = 0;
1160       current_windows = 0;
1161
1162       if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1163
1164 #ifdef HAVE_MIT_SAVER_EXTENSION
1165       for (i = 0; i < si->nscreens; i++)
1166         {
1167           saver_screen_info *ssi = &si->screens[i];
1168           if (ssi->server_mit_saver_window &&
1169               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1170             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1171         }
1172 #endif /* HAVE_MIT_SAVER_EXTENSION */
1173
1174       XUngrabServer (si->dpy);
1175       XSync (si->dpy, False);                   /* ###### (danger over) */
1176     }
1177   else
1178     {
1179       for (i = 0; i < si->nscreens; i++)
1180         {
1181           saver_screen_info *ssi = &si->screens[i];
1182           if (!dont_clear)
1183             XClearWindow (si->dpy, ssi->screensaver_window);
1184           if (!dont_clear || ssi->stderr_overlay_window)
1185             clear_stderr (ssi);
1186           XMapRaised (si->dpy, ssi->screensaver_window);
1187 #ifdef HAVE_MIT_SAVER_EXTENSION
1188           if (ssi->server_mit_saver_window &&
1189               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1190             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1191 #endif /* HAVE_MIT_SAVER_EXTENSION */
1192         }
1193     }
1194
1195   for (i = 0; i < si->nscreens; i++)
1196     {
1197       saver_screen_info *ssi = &si->screens[i];
1198       if (ssi->cmap)
1199         XInstallColormap (si->dpy, ssi->cmap);
1200     }
1201 }
1202
1203 Bool
1204 blank_screen (saver_info *si)
1205 {
1206   int i;
1207   Bool ok;
1208
1209   /* Note: we do our grabs on the root window, not on the screensaver window.
1210      If we grabbed on the saver window, then the demo mode and lock dialog
1211      boxes wouldn't get any events.
1212    */
1213   ok = grab_keyboard_and_mouse (si,
1214                                 /*si->screens[0].screensaver_window,*/
1215                                 RootWindowOfScreen(si->screens[0].screen),
1216                                 (si->demoing_p
1217                                  ? 0
1218                                  : si->screens[0].cursor));
1219
1220
1221   if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1222     /* If we're using a server extension, then failure to get a grab is
1223        not a big deal -- even without the grab, we will still be able
1224        to un-blank when there is user activity, since the server will
1225        tell us. */
1226     ok = True;
1227
1228   if (!ok)
1229     return False;
1230
1231   for (i = 0; i < si->nscreens; i++)
1232     {
1233       saver_screen_info *ssi = &si->screens[i];
1234
1235       save_real_vroot (ssi);
1236       store_vroot_property (si->dpy,
1237                             ssi->screensaver_window,
1238                             ssi->screensaver_window);
1239
1240 #ifdef HAVE_XF86VMODE
1241       {
1242         int ev, er;
1243         if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
1244             !XF86VidModeGetViewPort (si->dpy, i,
1245                                      &ssi->blank_vp_x,
1246                                      &ssi->blank_vp_y))
1247           ssi->blank_vp_x = ssi->blank_vp_y = -1;
1248       }
1249 #endif /* HAVE_XF86VMODE */
1250     }
1251   store_activate_time (si, si->screen_blanked_p);
1252   raise_window (si, False, False, False);
1253
1254   si->screen_blanked_p = True;
1255   si->last_wall_clock_time = 0;
1256
1257   return True;
1258 }
1259
1260 void
1261 unblank_screen (saver_info *si)
1262 {
1263   saver_preferences *p = &si->prefs;
1264   Bool unfade_p = (si->fading_possible_p && p->unfade_p);
1265   int i;
1266
1267   monitor_power_on (si);
1268
1269   store_activate_time (si, True);
1270   reset_watchdog_timer (si, False);
1271
1272   if (si->demoing_p)
1273     unfade_p = False;
1274
1275   if (unfade_p)
1276     {
1277       Window *current_windows = (Window *)
1278         calloc(sizeof(Window), si->nscreens);
1279
1280       for (i = 0; i < si->nscreens; i++)
1281         {
1282           saver_screen_info *ssi = &si->screens[i];
1283           current_windows[i] = ssi->screensaver_window;
1284           /* Ensure that the default background of the window is really black,
1285              not a pixmap or something.  (This does not clear the window.) */
1286           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1287                                 ssi->black_pixel);
1288         }
1289
1290       if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1291
1292
1293       XSync (si->dpy, False);
1294       XGrabServer (si->dpy);                    /* ############ DANGER! */
1295       XSync (si->dpy, False);
1296
1297       /* Clear the stderr layer on each screen.
1298        */
1299       for (i = 0; i < si->nscreens; i++)
1300         {
1301           saver_screen_info *ssi = &si->screens[i];
1302           clear_stderr (ssi);
1303         }
1304
1305       XUngrabServer (si->dpy);
1306       XSync (si->dpy, False);                   /* ###### (danger over) */
1307
1308
1309       fade_screens (si->dpy, 0, current_windows,
1310                     p->fade_seconds/1000, p->fade_ticks,
1311                     False, False);
1312
1313       free(current_windows);
1314       current_windows = 0;
1315
1316       if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1317     }
1318   else
1319     {
1320       for (i = 0; i < si->nscreens; i++)
1321         {
1322           saver_screen_info *ssi = &si->screens[i];
1323           if (ssi->cmap)
1324             {
1325               Colormap c = DefaultColormapOfScreen (ssi->screen);
1326               /* avoid technicolor */
1327               XClearWindow (si->dpy, ssi->screensaver_window);
1328               if (c) XInstallColormap (si->dpy, c);
1329             }
1330           XUnmapWindow (si->dpy, ssi->screensaver_window);
1331         }
1332     }
1333
1334
1335   /* If the focus window does has a non-default colormap, then install
1336      that colormap as well.  (On SGIs, this will cause both the root map
1337      and the focus map to be installed simultaniously.  It'd be nice to
1338      pick up the other colormaps that had been installed, too; perhaps
1339      XListInstalledColormaps could be used for that?)
1340    */
1341   {
1342     Window focus = 0;
1343     int revert_to;
1344     XGetInputFocus (si->dpy, &focus, &revert_to);
1345     if (focus && focus != PointerRoot && focus != None)
1346       {
1347         XWindowAttributes xgwa;
1348         xgwa.colormap = 0;
1349         XGetWindowAttributes (si->dpy, focus, &xgwa);
1350         if (xgwa.colormap &&
1351             xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1352           XInstallColormap (si->dpy, xgwa.colormap);
1353       }
1354   }
1355
1356
1357   for (i = 0; i < si->nscreens; i++)
1358     {
1359       saver_screen_info *ssi = &si->screens[i];
1360       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1361     }
1362
1363   store_activate_time(si, False);  /* store unblank time */
1364
1365   ungrab_keyboard_and_mouse (si);
1366   restore_real_vroot (si);
1367
1368   /* Unmap the windows a second time, dammit -- just to avoid a race
1369      with the screen-grabbing hacks.  (I'm not sure if this is really
1370      necessary; I'm stabbing in the dark now.)
1371   */
1372   for (i = 0; i < si->nscreens; i++)
1373     XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1374
1375   si->screen_blanked_p = False;
1376   si->last_wall_clock_time = 0;
1377 }
1378
1379
1380 static void
1381 store_activate_time (saver_info *si, Bool use_last_p)
1382 {
1383   static time_t last_time = 0;
1384   time_t now = ((use_last_p && last_time) ? last_time : time ((time_t) 0));
1385   CARD32 now32 = (CARD32) now;
1386   int i;
1387   last_time = now;
1388
1389   for (i = 0; i < si->nscreens; i++)
1390     {
1391       saver_screen_info *ssi = &si->screens[i];
1392       if (!ssi->screensaver_window) continue;
1393       XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_TIME,
1394                        XA_INTEGER, 32, PropModeReplace,
1395                        (unsigned char *) &now32, 1);
1396     }
1397 }
1398
1399
1400 Bool
1401 select_visual (saver_screen_info *ssi, const char *visual_name)
1402 {
1403   saver_info *si = ssi->global;
1404   saver_preferences *p = &si->prefs;
1405   Bool install_cmap_p = p->install_cmap_p;
1406   Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1407   Visual *new_v = 0;
1408   Bool got_it;
1409
1410   if (visual_name && *visual_name)
1411     {
1412       if (!strcmp(visual_name, "default-i"))
1413         {
1414           visual_name = "default";
1415           install_cmap_p = True;
1416         }
1417       else if (!strcmp(visual_name, "default-n"))
1418         {
1419           visual_name = "default";
1420           install_cmap_p = False;
1421         }
1422 #ifdef DAEMON_USE_GL
1423       else if (!strcmp(visual_name, "gl") ||
1424                !strcmp(visual_name, "GL"))
1425         {
1426           new_v = get_gl_visual (ssi->screen);
1427           if (!new_v && p->verbose_p)
1428             fprintf (stderr, "%s: no GL visuals.\n", progname);
1429         }
1430 #endif /* DAEMON_USE_GL */
1431
1432       if (!new_v)
1433         new_v = get_visual (ssi->screen, visual_name, True, False);
1434     }
1435   else
1436     {
1437       new_v = ssi->default_visual;
1438     }
1439
1440   got_it = !!new_v;
1441
1442   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1443     /* It's not the default visual, so we have no choice but to install. */
1444     install_cmap_p = True;
1445
1446   ssi->install_cmap_p = install_cmap_p;
1447
1448   if (new_v &&
1449       ((ssi->current_visual != new_v) ||
1450        (install_cmap_p != was_installed_p)))
1451     {
1452       Colormap old_c = ssi->cmap;
1453       Window old_w = ssi->screensaver_window;
1454
1455       if (p->verbose_p)
1456         {
1457           fprintf (stderr, "%s: switching to visual ", blurb());
1458           describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
1459 #if 0
1460           fprintf (stderr, "%s:                from ", blurb());
1461           describe_visual (stderr, ssi->screen, ssi->current_visual,
1462                            was_installed_p);
1463 #endif
1464         }
1465
1466       reset_stderr (ssi);
1467       ssi->current_visual = new_v;
1468       ssi->current_depth = visual_depth(ssi->screen, new_v);
1469       ssi->cmap = 0;
1470       ssi->screensaver_window = 0;
1471
1472       initialize_screensaver_window_1 (ssi);
1473
1474       /* stderr_overlay_window is a child of screensaver_window, so we need
1475          to destroy that as well (actually, we just need to invalidate and
1476          drop our pointers to it, but this will destroy it, which is ok so
1477          long as it happens before old_w itself is destroyed.) */
1478       reset_stderr (ssi);
1479
1480       raise_window (si, True, True, False);
1481       store_vroot_property (si->dpy,
1482                             ssi->screensaver_window, ssi->screensaver_window);
1483       store_activate_time (si, True);
1484
1485
1486
1487       /* Transfer the grabs from the old window to the new.
1488          Actually I think none of this is necessary, since we always
1489          hold our grabs on the root window, but I wrote this before
1490          re-discovering that...
1491        */
1492
1493
1494       /* If we're destroying the window that holds our mouse grab,
1495          transfer the grab to the new window.  (Grab the server while
1496          so doing, to avoid a race condition.)
1497        */
1498       if (old_w == si->mouse_grab_window)
1499         {
1500           XGrabServer (si->dpy);                /* ############ DANGER! */
1501           ungrab_mouse (si);
1502           grab_mouse (si, ssi->screensaver_window,
1503                       (si->demoing_p
1504                        ? 0
1505                        : ssi->cursor));
1506           XUngrabServer (si->dpy);
1507           XSync (si->dpy, False);               /* ###### (danger over) */
1508         }
1509
1510       /* If we're destroying the window that holds our keyboard grab,
1511          transfer the grab to the new window.  (Grab the server while
1512          so doing, to avoid a race condition.)
1513        */
1514       if (old_w == si->keyboard_grab_window)
1515         {
1516           XGrabServer (si->dpy);                /* ############ DANGER! */
1517           ungrab_kbd(si);
1518           grab_kbd(si, ssi->screensaver_window);
1519           XUngrabServer (si->dpy);
1520           XSync (si->dpy, False);               /* ###### (danger over) */
1521         }
1522
1523       /* Now we can destroy this window without horking our grabs. */
1524
1525       XDestroyWindow (si->dpy, old_w);
1526
1527       if (p->verbose_p)
1528         fprintf (stderr, "%s: destroyed old saver window 0x%lx.\n",
1529                  blurb(), (unsigned long) old_w);
1530
1531       if (old_c &&
1532           old_c != DefaultColormapOfScreen (ssi->screen) &&
1533           old_c != ssi->demo_cmap)
1534         XFreeColormap (si->dpy, old_c);
1535     }
1536
1537   return got_it;
1538 }