1ec24e4e4af2fecdad427bf8f7900d172163edeb
[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@netscape.com>
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 # if !defined(HAVE_UNAME) && (__VMS_VER >= 70000000)
21 #  define HAVE_UNAME 1
22 # endif /* !HAVE_UNAME */
23 #endif /* VMS */
24
25 # ifdef HAVE_UNAME
26 #  include <sys/utsname.h>      /* for uname() */
27 # endif /* HAVE_UNAME */
28
29 #include <stdio.h>
30 #include <X11/Xproto.h>         /* for CARD32 */
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>          /* for XSetClassHint() */
33 #include <X11/Xatom.h>
34 #include <X11/Xos.h>            /* for time() */
35 #include <signal.h>             /* for the signal names */
36
37 #ifdef HAVE_MIT_SAVER_EXTENSION
38 # include <X11/extensions/scrnsaver.h>
39 #endif /* HAVE_MIT_SAVER_EXTENSION */
40
41
42 #ifdef HAVE_XHPDISABLERESET
43 # include <X11/XHPlib.h>
44
45  /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
46     or BadAccess errors occur.  (Ok for this to be global, since it
47     affects the whole machine, not just the current screen.) */
48   Bool hp_locked_p = False;
49
50 #endif /* HAVE_XHPDISABLERESET */
51
52
53 /* This file doesn't need the Xt headers, so stub these types out... */
54 #undef XtPointer
55 #define XtAppContext void*
56 #define XrmDatabase  void*
57 #define XtIntervalId void*
58 #define XtPointer    void*
59 #define Widget       void*
60
61 #include "xscreensaver.h"
62 #include "visual.h"
63 #include "fade.h"
64
65 extern int kill (pid_t, int);           /* signal() is in sys/signal.h... */
66
67 Atom XA_VROOT, XA_XSETROOT_ID;
68 Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
69 Atom XA_SCREENSAVER_TIME;
70
71
72 extern saver_info *global_si_kludge;    /* I hate C so much... */
73
74
75 static void store_activate_time (saver_info *si, Bool use_last_p);
76
77 #define ALL_POINTER_EVENTS \
78         (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
79          LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
80          Button1MotionMask | Button2MotionMask | Button3MotionMask | \
81          Button4MotionMask | Button5MotionMask | ButtonMotionMask)
82
83
84 static int
85 grab_kbd(saver_info *si, Window w)
86 {
87   saver_preferences *p = &si->prefs;
88   int status = XGrabKeyboard (si->dpy, w, True,
89                               /* I don't really understand Sync vs Async,
90                                  but these seem to work... */
91                               GrabModeSync, GrabModeAsync,
92                               CurrentTime);
93   if (status == GrabSuccess)
94     si->keyboard_grab_window = w;
95
96   if (p->debug_p)
97     fprintf(stderr, "%s: XGrabKeyboard(... 0x%x ...) ==> %s\n",
98             progname, (unsigned long) w,
99             (status == GrabSuccess ? "GrabSuccess" :
100              status == AlreadyGrabbed ? "AlreadyGrabbed" :
101              status == GrabInvalidTime ? "GrabInvalidTime" :
102              status == GrabNotViewable ? "GrabNotViewable" :
103              status == GrabFrozen ? "GrabFrozen" :
104              "???"));
105
106   return status;
107 }
108
109 static const char *
110 grab_string(int status)
111 {
112   switch (status)
113     {
114     case GrabSuccess:     return "GrabSuccess";     break;
115     case AlreadyGrabbed:  return "AlreadyGrabbed";  break;
116     case GrabInvalidTime: return "GrabInvalidTime"; break;
117     case GrabNotViewable: return "GrabNotViewable"; break;
118     case GrabFrozen:      return "GrabFrozen";      break;
119     default:
120       {
121         static char foo[255];
122         sprintf(foo, "unknown status: %d", status);
123         return foo;
124       }
125     }
126 }
127
128
129 static int
130 grab_mouse (saver_info *si, Window w, Cursor cursor)
131 {
132   saver_preferences *p = &si->prefs;
133   int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS,
134                              GrabModeAsync, GrabModeAsync, None,
135                              cursor, CurrentTime);
136   if (status == GrabSuccess)
137     si->mouse_grab_window = w;
138
139   if (p->debug_p)
140     fprintf(stderr, "%s: XGrabPointer(... 0x%x, 0x%x ...) ==> %s\n",
141             progname, (unsigned long) w, (unsigned long) cursor,
142             grab_string(status));
143   return status;
144 }
145
146
147 static void
148 ungrab_kbd(saver_info *si)
149 {
150   saver_preferences *p = &si->prefs;
151   XUngrabKeyboard(si->dpy, CurrentTime);
152   if (p->debug_p)
153     fprintf(stderr, "%s: XUngrabKeyboard (was 0x%x)\n", progname,
154             (unsigned long) si->keyboard_grab_window);
155   si->keyboard_grab_window = 0;
156 }
157
158
159 static void
160 ungrab_mouse(saver_info *si)
161 {
162   saver_preferences *p = &si->prefs;
163   XUngrabPointer(si->dpy, CurrentTime);
164   if (p->debug_p)
165     fprintf(stderr, "%s: XUngrabPointer (was 0x%x)\n", progname,
166             (unsigned long) si->mouse_grab_window);
167   si->mouse_grab_window = 0;
168 }
169
170
171 void
172 grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
173 {
174   Status status;
175   XSync (si->dpy, False);
176
177   status = grab_kbd (si, window);
178   if (status != GrabSuccess)
179     {   /* try again in a second */
180       sleep (1);
181       status = grab_kbd (si, window);
182       if (status != GrabSuccess)
183         fprintf (stderr, "%s: couldn't grab keyboard!  (%s)\n",
184                  progname, grab_string(status));
185     }
186
187   status = grab_mouse (si, window, cursor);
188   if (status != GrabSuccess)
189     {   /* try again in a second */
190       sleep (1);
191       status = grab_mouse (si, window, cursor);
192       if (status != GrabSuccess)
193         fprintf (stderr, "%s: couldn't grab pointer!  (%s)\n",
194                  progname, grab_string(status));
195     }
196 }
197
198 void
199 ungrab_keyboard_and_mouse (saver_info *si)
200 {
201   ungrab_mouse (si);
202   ungrab_kbd (si);
203 }
204
205
206 /* Prints an error message to stderr and returns True if there is another
207    xscreensaver running already.  Silently returns False otherwise. */
208 Bool
209 ensure_no_screensaver_running (Display *dpy, Screen *screen)
210 {
211   Bool status = 0;
212   int i;
213   Window root = RootWindowOfScreen (screen);
214   Window root2, parent, *kids;
215   unsigned int nkids;
216   XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
217
218   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
219     abort ();
220   if (root != root2)
221     abort ();
222   if (parent)
223     abort ();
224   for (i = 0; i < nkids; i++)
225     {
226       Atom type;
227       int format;
228       unsigned long nitems, bytesafter;
229       char *version;
230
231       if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
232                               False, XA_STRING, &type, &format, &nitems,
233                               &bytesafter, (unsigned char **) &version)
234           == Success
235           && type != None)
236         {
237           char *id;
238           if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
239                                    False, XA_STRING, &type, &format, &nitems,
240                                    &bytesafter, (unsigned char **) &id)
241               == Success
242               || type == None)
243             id = "???";
244
245           fprintf (stderr,
246       "%s: already running on display %s (window 0x%x)\n from process %s.\n",
247                    progname, DisplayString (dpy), (int) kids [i], id);
248           status = True;
249         }
250     }
251
252   if (kids) XFree ((char *) kids);
253   XSync (dpy, False);
254   XSetErrorHandler (old_handler);
255   return status;
256 }
257
258
259 \f
260 /* Virtual-root hackery */
261
262 #ifdef _VROOT_H_
263 ERROR!  You must not include vroot.h in this file.
264 #endif
265
266 static void
267 store_vroot_property (Display *dpy, Window win, Window value)
268 {
269 #if 0
270   if (p->verbose_p)
271     fprintf (stderr,
272              "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", progname, 
273              win,
274              (win == screensaver_window ? "ScreenSaver" :
275               (win == real_vroot ? "VRoot" :
276                (win == real_vroot_value ? "Vroot_value" : "???"))),
277              value,
278              (value == screensaver_window ? "ScreenSaver" :
279               (value == real_vroot ? "VRoot" :
280                (value == real_vroot_value ? "Vroot_value" : "???"))));
281 #endif
282   XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
283                    (unsigned char *) &value, 1);
284 }
285
286 static void
287 remove_vroot_property (Display *dpy, Window win)
288 {
289 #if 0
290   if (p->verbose_p)
291     fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", progname, win, 
292              (win == screensaver_window ? "ScreenSaver" :
293               (win == real_vroot ? "VRoot" :
294                (win == real_vroot_value ? "Vroot_value" : "???"))));
295 #endif
296   XDeleteProperty (dpy, win, XA_VROOT);
297 }
298
299
300 static void
301 kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
302 {
303   Atom type;
304   int format;
305   unsigned long nitems, bytesafter;
306   Pixmap *dataP = 0;
307
308   /* If the user has been using xv or xsetroot as a screensaver (to display
309      an image on the screensaver window, as a kind of slideshow) then the
310      pixmap and its associated color cells have been put in RetainPermanent
311      CloseDown mode.  Since we're not destroying the xscreensaver window,
312      but merely unmapping it, we need to free these resources or those
313      colormap cells will stay allocated while the screensaver is off.  (We
314      could just delete the screensaver window and recreate it later, but
315      that could cause other problems.)  This code does an atomic read-and-
316      delete of the _XSETROOT_ID property, and if it held a pixmap, then we
317      cause the RetainPermanent resources of the client which created it
318      (and which no longer exists) to be freed.
319    */
320   if (XGetWindowProperty (dpy, window, XA_XSETROOT_ID, 0, 1,
321                           True, AnyPropertyType, &type, &format, &nitems, 
322                           &bytesafter, (unsigned char **) &dataP)
323       == Success
324       && type != None)
325     {
326       if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
327           nitems == 1 && bytesafter == 0)
328         {
329           if (verbose_p)
330             printf ("%s: destroying xsetroot data (0x%lX).\n",
331                     progname, *dataP);
332           XKillClient (dpy, *dataP);
333         }
334       else
335         fprintf (stderr, "%s: deleted unrecognised _XSETROOT_ID property: \n\
336         %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
337                  progname, (unsigned long) dataP, (dataP ? *dataP : 0), type,
338                  format, nitems, bytesafter);
339     }
340 }
341
342
343 static void handle_signals (saver_info *si, Bool on_p);
344
345 static void
346 save_real_vroot (saver_screen_info *ssi)
347 {
348   saver_info *si = ssi->global;
349   Display *dpy = si->dpy;
350   Screen *screen = ssi->screen;
351   int i;
352   Window root = RootWindowOfScreen (screen);
353   Window root2, parent, *kids;
354   unsigned int nkids;
355
356   ssi->real_vroot = 0;
357   ssi->real_vroot_value = 0;
358   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
359     abort ();
360   if (root != root2)
361     abort ();
362   if (parent)
363     abort ();
364   for (i = 0; i < nkids; i++)
365     {
366       Atom type;
367       int format;
368       unsigned long nitems, bytesafter;
369       Window *vrootP = 0;
370
371       if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
372                               &type, &format, &nitems, &bytesafter,
373                               (unsigned char **) &vrootP)
374           != Success)
375         continue;
376       if (! vrootP)
377         continue;
378       if (ssi->real_vroot)
379         {
380           if (*vrootP == ssi->screensaver_window) abort ();
381           fprintf (stderr,
382             "%s: more than one virtual root window found (0x%x and 0x%x).\n",
383                    progname, (int) ssi->real_vroot, (int) kids [i]);
384           exit (1);
385         }
386       ssi->real_vroot = kids [i];
387       ssi->real_vroot_value = *vrootP;
388     }
389
390   if (ssi->real_vroot)
391     {
392       handle_signals (si, True);
393       remove_vroot_property (si->dpy, ssi->real_vroot);
394       XSync (dpy, False);
395     }
396
397   XFree ((char *) kids);
398 }
399
400
401 static Bool
402 restore_real_vroot_2 (saver_screen_info *ssi)
403 {
404   saver_info *si = ssi->global;
405   saver_preferences *p = &si->prefs;
406   if (p->verbose_p && ssi->real_vroot)
407     printf ("%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
408             progname, (unsigned long) ssi->real_vroot);
409   remove_vroot_property (si->dpy, ssi->screensaver_window);
410   if (ssi->real_vroot)
411     {
412       store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value);
413       ssi->real_vroot = 0;
414       ssi->real_vroot_value = 0;
415       /* make sure the property change gets there before this process
416          terminates!  We might be doing this because we have intercepted
417          SIGTERM or something. */
418       XSync (si->dpy, False);
419       return True;
420     }
421   return False;
422 }
423
424 static Bool
425 restore_real_vroot_1 (saver_info *si)
426 {
427   int i;
428   Bool did_any = False;
429   for (i = 0; i < si->nscreens; i++)
430     {
431       saver_screen_info *ssi = &si->screens[i];
432       if (restore_real_vroot_2 (ssi))
433         did_any = True;
434     }
435   return did_any;
436 }
437
438 void
439 restore_real_vroot (saver_info *si)
440 {
441   if (restore_real_vroot_1 (si))
442     handle_signals (si, False);
443 }
444
445 \f
446 /* Signal hackery to ensure that the vroot doesn't get left in an 
447    inconsistent state
448  */
449
450 const char *
451 signal_name(int signal)
452 {
453   switch (signal) {
454   case SIGHUP:    return "SIGHUP";
455   case SIGINT:    return "SIGINT";
456   case SIGQUIT:   return "SIGQUIT";
457   case SIGILL:    return "SIGILL";
458   case SIGTRAP:   return "SIGTRAP";
459 #ifdef SIGABRT
460   case SIGABRT:   return "SIGABRT";
461 #endif
462   case SIGFPE:    return "SIGFPE";
463   case SIGKILL:   return "SIGKILL";
464   case SIGBUS:    return "SIGBUS";
465   case SIGSEGV:   return "SIGSEGV";
466   case SIGPIPE:   return "SIGPIPE";
467   case SIGALRM:   return "SIGALRM";
468   case SIGTERM:   return "SIGTERM";
469 #ifdef SIGSTOP
470   case SIGSTOP:   return "SIGSTOP";
471 #endif
472 #ifdef SIGCONT
473   case SIGCONT:   return "SIGCONT";
474 #endif
475 #ifdef SIGUSR1
476   case SIGUSR1:   return "SIGUSR1";
477 #endif
478 #ifdef SIGUSR2
479   case SIGUSR2:   return "SIGUSR2";
480 #endif
481 #ifdef SIGEMT
482   case SIGEMT:    return "SIGEMT";
483 #endif
484 #ifdef SIGSYS
485   case SIGSYS:    return "SIGSYS";
486 #endif
487 #ifdef SIGCHLD
488   case SIGCHLD:   return "SIGCHLD";
489 #endif
490 #ifdef SIGPWR
491   case SIGPWR:    return "SIGPWR";
492 #endif
493 #ifdef SIGWINCH
494   case SIGWINCH:  return "SIGWINCH";
495 #endif
496 #ifdef SIGURG
497   case SIGURG:    return "SIGURG";
498 #endif
499 #ifdef SIGIO
500   case SIGIO:     return "SIGIO";
501 #endif
502 #ifdef SIGVTALRM
503   case SIGVTALRM: return "SIGVTALRM";
504 #endif
505 #ifdef SIGXCPU
506   case SIGXCPU:   return "SIGXCPU";
507 #endif
508 #ifdef SIGXFSZ
509   case SIGXFSZ:   return "SIGXFSZ";
510 #endif
511 #ifdef SIGDANGER
512   case SIGDANGER: return "SIGDANGER";
513 #endif
514   default:
515     {
516       static char buf[50];
517       sprintf(buf, "signal %d\n", signal);
518       return buf;
519     }
520   }
521 }
522
523
524
525 static RETSIGTYPE
526 restore_real_vroot_handler (int sig)
527 {
528   saver_info *si = global_si_kludge;    /* I hate C so much... */
529
530   signal (sig, SIG_DFL);
531   if (restore_real_vroot_1 (si))
532     fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
533              progname, signal_name(sig));
534   kill (getpid (), sig);
535 }
536
537 static void
538 catch_signal (saver_info *si, int sig, Bool on_p)
539 {
540   if (! on_p)
541     signal (sig, SIG_DFL);
542   else
543     {
544       if (((long) signal (sig, restore_real_vroot_handler)) == -1L)
545         {
546           char buf [255];
547           sprintf (buf, "%s: couldn't catch %s", progname, signal_name(sig));
548           perror (buf);
549           saver_exit (si, 1);
550         }
551     }
552 }
553
554 static void
555 handle_signals (saver_info *si, Bool on_p)
556 {
557 #if 0
558   if (on_p) printf ("handling signals\n");
559   else printf ("unhandling signals\n");
560 #endif
561
562   catch_signal (si, SIGHUP,  on_p);
563   catch_signal (si, SIGINT,  on_p);
564   catch_signal (si, SIGQUIT, on_p);
565   catch_signal (si, SIGILL,  on_p);
566   catch_signal (si, SIGTRAP, on_p);
567   catch_signal (si, SIGIOT,  on_p);
568   catch_signal (si, SIGABRT, on_p);
569 #ifdef SIGEMT
570   catch_signal (si, SIGEMT,  on_p);
571 #endif
572   catch_signal (si, SIGFPE,  on_p);
573   catch_signal (si, SIGBUS,  on_p);
574   catch_signal (si, SIGSEGV, on_p);
575 #ifdef SIGSYS
576   catch_signal (si, SIGSYS,  on_p);
577 #endif
578   catch_signal (si, SIGTERM, on_p);
579 #ifdef SIGXCPU
580   catch_signal (si, SIGXCPU, on_p);
581 #endif
582 #ifdef SIGXFSZ
583   catch_signal (si, SIGXFSZ, on_p);
584 #endif
585 #ifdef SIGDANGER
586   catch_signal (si, SIGDANGER, on_p);
587 #endif
588 }
589
590 void
591 saver_exit (saver_info *si, int status)
592 {
593   saver_preferences *p = &si->prefs;
594   static Bool exiting = False;
595   Bool vrs;
596
597   if (exiting)
598     exit(status);
599
600   exiting = True;
601   
602   vrs = restore_real_vroot_1 (si);
603   emergency_kill_subproc (si);
604
605   if (vrs && (p->verbose_p || status != 0))
606     fprintf (real_stderr, "%s: vroot restored, exiting.\n", progname);
607   else if (p->verbose_p)
608     fprintf (real_stderr, "%s: no vroot to restore; exiting.\n", progname);
609
610   fflush(real_stdout);
611
612 #ifdef VMS      /* on VMS, 1 is the "normal" exit code instead of 0. */
613   if (status == 0) status = 1;
614   else if (status == 1) status = -1;
615 #endif
616
617   if (si->prefs.debug_p)
618     {
619       fprintf(real_stderr, "%s: dumping core (because of -debug)\n", progname);
620       /* Do this to drop a core file, so that we can get a stack trace. */
621       abort();
622     }
623
624   exit (status);
625 }
626
627 \f
628 /* Managing the actual screensaver window */
629
630 Bool
631 window_exists_p (Display *dpy, Window window)
632 {
633   XErrorHandler old_handler;
634   XWindowAttributes xgwa;
635   xgwa.screen = 0;
636   old_handler = XSetErrorHandler (BadWindow_ehandler);
637   XGetWindowAttributes (dpy, window, &xgwa);
638   XSync (dpy, False);
639   XSetErrorHandler (old_handler);
640   return (xgwa.screen != 0);
641 }
642
643 static void
644 initialize_screensaver_window_1 (saver_screen_info *ssi)
645 {
646   saver_info *si = ssi->global;
647   saver_preferences *p = &si->prefs;
648   Bool install_cmap_p = ssi->install_cmap_p;
649
650   /* This resets the screensaver window as fully as possible, since there's
651      no way of knowing what some random client may have done to us in the
652      meantime.  We could just destroy and recreate the window, but that has
653      its own set of problems...
654    */
655   XColor black;
656   XClassHint class_hints;
657   XSetWindowAttributes attrs;
658   unsigned long attrmask;
659   int width = WidthOfScreen (ssi->screen);
660   int height = HeightOfScreen (ssi->screen);
661   char id [2048];
662   static Bool printed_visual_info = False;  /* only print the message once. */
663
664   black.red = black.green = black.blue = 0;
665
666   if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
667     ssi->cmap = 0;
668
669   if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
670     install_cmap_p = True;
671
672   if (install_cmap_p)
673     {
674       if (! ssi->cmap)
675         {
676           ssi->cmap = XCreateColormap (si->dpy, RootWindowOfScreen (ssi->screen),
677                                       ssi->current_visual, AllocNone);
678           if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
679           ssi->black_pixel = black.pixel;
680         }
681     }
682   else
683     {
684       Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
685       if (ssi->cmap)
686         {
687           XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
688           if (ssi->cmap != ssi->demo_cmap &&
689               ssi->cmap != def_cmap)
690             XFreeColormap (si->dpy, ssi->cmap);
691         }
692       ssi->cmap = def_cmap;
693       ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
694     }
695
696   attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
697               CWBackPixel | CWBackingPixel | CWBorderPixel);
698   attrs.override_redirect = True;
699
700   /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
701      actually be reading these events during normal operation; but we still
702      need to see Button events for demo-mode to work properly.
703    */
704   attrs.event_mask = (KeyPressMask | KeyReleaseMask |
705                       ButtonPressMask | ButtonReleaseMask |
706                       PointerMotionMask);
707
708   attrs.backing_store = NotUseful;
709   attrs.colormap = ssi->cmap;
710   attrs.background_pixel = ssi->black_pixel;
711   attrs.backing_pixel = ssi->black_pixel;
712   attrs.border_pixel = ssi->black_pixel;
713
714   if (p->debug_p) width = width / 2;
715
716   if (!p->verbose_p || printed_visual_info)
717     ;
718   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
719     {
720       fprintf (stderr, "%s: using default visual ", progname);
721       describe_visual (stderr, ssi->screen, ssi->current_visual);
722     }
723   else
724     {
725       fprintf (stderr, "%s: using visual:   ", progname);
726       describe_visual (stderr, ssi->screen, ssi->current_visual);
727       fprintf (stderr, "%s: default visual: ", progname);
728       describe_visual (stderr, ssi->screen,
729                        DefaultVisualOfScreen (ssi->screen));
730     }
731   printed_visual_info = True;
732
733 #ifdef HAVE_MIT_SAVER_EXTENSION
734   if (p->use_mit_saver_extension)
735     {
736       XScreenSaverInfo *info;
737       Window root = RootWindowOfScreen (ssi->screen);
738
739 #if 0
740       /* This call sets the server screensaver timeouts to what we think
741          they should be (based on the resources and args xscreensaver was
742          started with.)  It's important that we do this to sync back up
743          with the server - if we have turned on prematurely, as by an
744          ACTIVATE ClientMessage, then the server may decide to activate
745          the screensaver while it's already active.  That's ok for us,
746          since we would know to ignore that ScreenSaverActivate event,
747          but a side effect of this would be that the server would map its
748          saver window (which we then hide again right away) meaning that
749          the bits currently on the screen get blown away.  Ugly. */
750
751       /* #### Ok, that doesn't work - when we tell the server that the
752          screensaver is "off" it sends us a Deactivate event, which is
753          sensible... but causes the saver to never come on.  Hmm. */
754       disable_builtin_screensaver (si, True);
755 #endif /* 0 */
756
757 #if 0
758       /* #### The MIT-SCREEN-SAVER extension gives us access to the
759          window that the server itself uses for saving the screen.
760          However, using this window in any way, in particular, calling
761          XScreenSaverSetAttributes() as below, tends to make the X server
762          crash.  So fuck it, let's try and get along without using it...
763
764          It's also inconvenient to use this window because it doesn't
765          always exist (though the ID is constant.)  So to use this
766          window, we'd have to reimplement the ACTIVATE ClientMessage to
767          tell the *server* to tell *us* to turn on, to cause the window
768          to get created at the right time.  Gag.  */
769       XScreenSaverSetAttributes (si->dpy, root,
770                                  0, 0, width, height, 0,
771                                  current_depth, InputOutput, visual,
772                                  attrmask, &attrs);
773       XSync (si->dpy, False);
774 #endif /* 0 */
775
776       info = XScreenSaverAllocInfo ();
777       XScreenSaverQueryInfo (si->dpy, root, info);
778       ssi->server_mit_saver_window = info->window;
779       if (! ssi->server_mit_saver_window) abort ();
780       XFree (info);
781     }
782 #endif /* HAVE_MIT_SAVER_EXTENSION */
783
784   if (ssi->screensaver_window)
785     {
786       XWindowChanges changes;
787       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
788       changes.x = 0;
789       changes.y = 0;
790       changes.width = width;
791       changes.height = height;
792       changes.border_width = 0;
793
794       XConfigureWindow (si->dpy, ssi->screensaver_window,
795                         changesmask, &changes);
796       XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
797                                attrmask, &attrs);
798     }
799   else
800     {
801       ssi->screensaver_window =
802         XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen), 0, 0,
803                        width, height, 0, ssi->current_depth, InputOutput,
804                        ssi->current_visual, attrmask, &attrs);
805       reset_stderr (ssi);
806       store_activate_time(si, True);
807       if (p->verbose_p)
808         fprintf (stderr, "%s: saver window is 0x%lx.\n",
809                  progname, (unsigned long) ssi->screensaver_window);
810     }
811
812 #ifdef HAVE_MIT_SAVER_EXTENSION
813   if (!p->use_mit_saver_extension ||
814       window_exists_p (si->dpy, ssi->screensaver_window))
815     /* When using the MIT-SCREEN-SAVER extension, the window pointed to
816        by screensaver_window only exists while the saver is active.
817        So we must be careful to only try and manipulate it while it
818        exists...
819        (#### The above comment would be true if the MIT extension actually
820        worked, but it's not true today -- see `server_mit_saver_window'.)
821      */
822 #endif /* HAVE_MIT_SAVER_EXTENSION */
823     {
824       class_hints.res_name = progname;
825       class_hints.res_class = progclass;
826       XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
827       XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
828       XChangeProperty (si->dpy, ssi->screensaver_window,
829                        XA_SCREENSAVER_VERSION,
830                        XA_STRING, 8, PropModeReplace,
831                        (unsigned char *) si->version,
832                        strlen (si->version));
833
834       sprintf (id, "%lu on host ", (unsigned long) getpid ());
835
836 # if defined(HAVE_UNAME)
837       {
838         struct utsname uts;
839         if (uname (&uts) < 0)
840           strcat (id, "???");
841         else
842           strcat (id, uts.nodename);
843       }
844 # elif defined(VMS)
845       strcat (id, getenv("SYS$NODE"));
846 # else  /* !HAVE_UNAME && !VMS */
847       strcat (id, "???");
848 # endif /* !HAVE_UNAME && !VMS */
849
850       XChangeProperty (si->dpy, ssi->screensaver_window,
851                        XA_SCREENSAVER_ID, XA_STRING,
852                        8, PropModeReplace, (unsigned char *) id, strlen (id));
853
854       if (!ssi->cursor)
855         {
856           Pixmap bit;
857           bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
858                                              "\000", 1, 1,
859                                              BlackPixelOfScreen (ssi->screen),
860                                              BlackPixelOfScreen (ssi->screen),
861                                              1);
862           ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
863                                              0, 0);
864           XFreePixmap (si->dpy, bit);
865         }
866
867       XSetWindowBackground (si->dpy, ssi->screensaver_window,
868                             ssi->black_pixel);
869       if (si->demo_mode_p)
870         XUndefineCursor (si->dpy, ssi->screensaver_window);
871       else
872         XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
873     }
874 }
875
876 void
877 initialize_screensaver_window (saver_info *si)
878 {
879   int i;
880   for (i = 0; i < si->nscreens; i++)
881     initialize_screensaver_window_1 (&si->screens[i]);
882 }
883
884
885 void 
886 raise_window (saver_info *si,
887               Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
888 {
889   saver_preferences *p = &si->prefs;
890   int i;
891
892   initialize_screensaver_window (si);
893   reset_watchdog_timer (si, True);
894
895   if (p->fade_p && !inhibit_fade && !si->demo_mode_p)
896     {
897       int grabbed = -1;
898       Window *current_windows = (Window *)
899         calloc(sizeof(Window), si->nscreens);
900       Colormap *current_maps = (Colormap *)
901         calloc(sizeof(Colormap), si->nscreens);
902
903       for (i = 0; i < si->nscreens; i++)
904         {
905           saver_screen_info *ssi = &si->screens[i];
906           current_windows[i] = ssi->screensaver_window;
907           current_maps[i] = (between_hacks_p
908                              ? ssi->cmap
909                              : DefaultColormapOfScreen (ssi->screen));
910           /* Ensure that the default background of the window is really black,
911              not a pixmap or something.  (This does not clear the window.) */
912           XSetWindowBackground (si->dpy, ssi->screensaver_window,
913                                 ssi->black_pixel);
914         }
915
916       if (p->verbose_p) fprintf (stderr, "%s: fading... ", progname);
917
918       XGrabServer (si->dpy);                    /* ############ DANGER! */
919
920       /* Clear the stderr layer on each screen.
921          Grab the mouse on the first screen on which the mouse is grabbable
922          (if there are multiple heads, I think you might only be able to
923          grab the mouse on the screen that currently has the mouse?  Anyway,
924          we only grab the mouse once, and don't try again after the grab
925          has succeeded.)  We grab the mouse on the root window of the screen,
926          not on the screensaver window, since the screensaver window is not
927          yet mapped.
928        */
929       for (i = 0; i < si->nscreens; i++)
930         {
931           saver_screen_info *ssi = &si->screens[i];
932
933           /* grab and blacken mouse on the root window (saver not mapped yet)
934            */
935           if (grabbed != GrabSuccess)
936             {
937               Window root = RootWindowOfScreen(ssi->screen);
938               grabbed = grab_mouse (si, root,
939                                     (si->demo_mode_p ? 0 : ssi->cursor));
940             }
941
942           if (!dont_clear || ssi->stderr_overlay_window)
943             /* Do this before the fade, since the stderr cmap won't fade
944                even if we uninstall it (beats me...) */
945             clear_stderr (ssi);
946         }
947
948       /* Note!  The server is grabbed, and this will take several seconds
949          to complete! */
950       fade_screens (si->dpy, current_maps, current_windows,
951                     p->fade_seconds, p->fade_ticks, True, !dont_clear);
952
953       free(current_maps);
954       free(current_windows);
955       current_maps = 0;
956       current_windows = 0;
957
958       if (p->verbose_p) fprintf (stderr, "fading done.\n");
959
960 #ifdef HAVE_MIT_SAVER_EXTENSION
961       for (i = 0; i < si->nscreens; i++)
962         {
963           saver_screen_info *ssi = &si->screens[i];
964           if (ssi->server_mit_saver_window &&
965               window_exists_p (si->dpy, ssi->server_mit_saver_window))
966             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
967         }
968 #endif /* HAVE_MIT_SAVER_EXTENSION */
969
970       /* If we had successfully grabbed the mouse, let it go now. */
971       if (grabbed == GrabSuccess)
972         ungrab_mouse (si);
973
974       XUngrabServer (si->dpy);
975       XSync (si->dpy, False);                   /* ###### (danger over) */
976     }
977   else
978     {
979       for (i = 0; i < si->nscreens; i++)
980         {
981           saver_screen_info *ssi = &si->screens[i];
982           if (!dont_clear)
983             XClearWindow (si->dpy, ssi->screensaver_window);
984           if (!dont_clear || ssi->stderr_overlay_window)
985             clear_stderr (ssi);
986           XMapRaised (si->dpy, ssi->screensaver_window);
987 #ifdef HAVE_MIT_SAVER_EXTENSION
988           if (ssi->server_mit_saver_window &&
989               window_exists_p (si->dpy, ssi->server_mit_saver_window))
990             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
991 #endif /* HAVE_MIT_SAVER_EXTENSION */
992         }
993     }
994
995   for (i = 0; i < si->nscreens; i++)
996     {
997       saver_screen_info *ssi = &si->screens[i];
998       if (ssi->cmap)
999         XInstallColormap (si->dpy, ssi->cmap);
1000     }
1001 }
1002
1003 void
1004 blank_screen (saver_info *si)
1005 {
1006   int i;
1007   for (i = 0; i < si->nscreens; i++)
1008     {
1009       saver_screen_info *ssi = &si->screens[i];
1010
1011       save_real_vroot (ssi);
1012       store_vroot_property (si->dpy,
1013                             ssi->screensaver_window,
1014                             ssi->screensaver_window);
1015     }
1016   store_activate_time (si, si->screen_blanked_p);
1017   raise_window (si, False, False, False);
1018
1019   /* Note: we do our grabs on the root window, not on the screensaver window.
1020      If we grabbed on the saver window, then the demo mode and lock dialog
1021      boxes wouldn't get any events.
1022    */
1023   grab_keyboard_and_mouse (si,
1024                            /*si->screens[0].screensaver_window,*/
1025                            RootWindowOfScreen(si->screens[0].screen),
1026                            (si->demo_mode_p ? 0 : si->screens[0].cursor));
1027
1028 #ifdef HAVE_XHPDISABLERESET
1029   if (si->locked_p && !hp_locked_p)
1030     {
1031       XHPDisableReset (si->dpy);        /* turn off C-Sh-Reset */
1032       hp_locked_p = True;
1033     }
1034 #endif
1035
1036   si->screen_blanked_p = True;
1037 }
1038
1039 void
1040 unblank_screen (saver_info *si)
1041 {
1042   saver_preferences *p = &si->prefs;
1043   int i;
1044
1045   monitor_power_on (si);
1046
1047   store_activate_time (si, True);
1048   reset_watchdog_timer (si, False);
1049
1050   if (p->unfade_p && !si->demo_mode_p)
1051     {
1052       int grabbed = -1;
1053       Window *current_windows = (Window *)
1054         calloc(sizeof(Window), si->nscreens);
1055
1056       for (i = 0; i < si->nscreens; i++)
1057         {
1058           saver_screen_info *ssi = &si->screens[i];
1059           current_windows[i] = ssi->screensaver_window;
1060           /* Ensure that the default background of the window is really black,
1061              not a pixmap or something.  (This does not clear the window.) */
1062           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1063                                 ssi->black_pixel);
1064         }
1065
1066       if (p->verbose_p) fprintf (stderr, "%s: unfading... ", progname);
1067
1068
1069       XSync (si->dpy, False);
1070       XGrabServer (si->dpy);                    /* ############ DANGER! */
1071       XSync (si->dpy, False);
1072
1073       /* Clear the stderr layer on each screen.
1074          Grab the mouse on the first screen on which the mouse is grabbable
1075          (if there are multiple heads, I think you might only be able to
1076          grab the mouse on the screen that currently has the mouse?  Anyway,
1077          we only grab the mouse once, and don't try again after the grab
1078          has succeeded.)
1079        */
1080       for (i = 0; i < si->nscreens; i++)
1081         {
1082           saver_screen_info *ssi = &si->screens[i];
1083           if (grabbed != GrabSuccess)
1084             grabbed = grab_mouse (si, RootWindowOfScreen (ssi->screen),
1085                                   0);
1086           clear_stderr (ssi);
1087         }
1088
1089       XUngrabServer (si->dpy);
1090       XSync (si->dpy, False);                   /* ###### (danger over) */
1091
1092
1093       fade_screens (si->dpy, 0, current_windows,
1094                     p->fade_seconds, p->fade_ticks,
1095                     False, False);
1096
1097       free(current_windows);
1098       current_windows = 0;
1099
1100       if (p->verbose_p) fprintf (stderr, "unfading done.\n");
1101
1102       /* If we had successfully grabbed the mouse, let it go now. */
1103       if (grabbed == GrabSuccess)
1104         ungrab_mouse (si);
1105     }
1106   else
1107     {
1108       for (i = 0; i < si->nscreens; i++)
1109         {
1110           saver_screen_info *ssi = &si->screens[i];
1111           if (ssi->cmap)
1112             {
1113               Colormap c = DefaultColormapOfScreen (ssi->screen);
1114               /* avoid technicolor */
1115               XClearWindow (si->dpy, ssi->screensaver_window);
1116               if (c) XInstallColormap (si->dpy, c);
1117             }
1118           XUnmapWindow (si->dpy, ssi->screensaver_window);
1119         }
1120     }
1121
1122
1123   /* If the focus window does has a non-default colormap, then install
1124      that colormap as well.  (On SGIs, this will cause both the root map
1125      and the focus map to be installed simultaniously.  It'd be nice to
1126      pick up the other colormaps that had been installed, too; perhaps
1127      XListInstalledColormaps could be used for that?)
1128    */
1129   {
1130     Window focus = 0;
1131     int revert_to;
1132     XGetInputFocus (si->dpy, &focus, &revert_to);
1133     if (focus && focus != PointerRoot && focus != None)
1134       {
1135         XWindowAttributes xgwa;
1136         xgwa.colormap = 0;
1137         XGetWindowAttributes (si->dpy, focus, &xgwa);
1138         if (xgwa.colormap &&
1139             xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1140           XInstallColormap (si->dpy, xgwa.colormap);
1141       }
1142   }
1143
1144
1145   for (i = 0; i < si->nscreens; i++)
1146     {
1147       saver_screen_info *ssi = &si->screens[i];
1148       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1149     }
1150
1151   store_activate_time(si, False);  /* store unblank time */
1152
1153   ungrab_keyboard_and_mouse (si);
1154   restore_real_vroot (si);
1155
1156 #ifdef HAVE_XHPDISABLERESET
1157   if (hp_locked_p)
1158     {
1159       XHPEnableReset (si->dpy); /* turn C-Sh-Reset back on */
1160       hp_locked_p = False;
1161     }
1162 #endif
1163
1164   /* Unmap the windows a second time, dammit -- just to avoid a race
1165      with the screen-grabbing hacks.  (I'm not sure if this is really
1166      necessary; I'm stabbing in the dark now.)
1167   */
1168   for (i = 0; i < si->nscreens; i++)
1169     XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1170
1171   si->screen_blanked_p = False;
1172 }
1173
1174
1175 static void
1176 store_activate_time (saver_info *si, Bool use_last_p)
1177 {
1178   static time_t last_time = 0;
1179   time_t now = ((use_last_p && last_time) ? last_time : time ((time_t) 0));
1180   CARD32 now32 = (CARD32) now;
1181   int i;
1182   last_time = now;
1183
1184   for (i = 0; i < si->nscreens; i++)
1185     {
1186       saver_screen_info *ssi = &si->screens[i];
1187       if (!ssi->screensaver_window) continue;
1188       XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_TIME,
1189                        XA_INTEGER, 32, PropModeReplace,
1190                        (unsigned char *) &now32, 1);
1191     }
1192 }
1193
1194
1195 Bool
1196 select_visual (saver_screen_info *ssi, const char *visual_name)
1197 {
1198   saver_info *si = ssi->global;
1199   saver_preferences *p = &si->prefs;
1200   Bool install_cmap_p = p->install_cmap_p;
1201   Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1202   Visual *new_v;
1203   Bool got_it;
1204
1205   if (visual_name && *visual_name)
1206     {
1207       if (!strcmp(visual_name, "default-i"))
1208         {
1209           visual_name = "default";
1210           install_cmap_p = True;
1211         }
1212       else if (!strcmp(visual_name, "default-n"))
1213         {
1214           visual_name = "default";
1215           install_cmap_p = False;
1216         }
1217       new_v = get_visual (ssi->screen, visual_name, True, False);
1218     }
1219   else
1220     {
1221       new_v = ssi->default_visual;
1222     }
1223
1224   got_it = !!new_v;
1225
1226   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1227     install_cmap_p = True;
1228
1229   ssi->install_cmap_p = install_cmap_p;
1230
1231   if (new_v &&
1232       ((ssi->current_visual != new_v) ||
1233        (install_cmap_p != was_installed_p)))
1234     {
1235       Colormap old_c = ssi->cmap;
1236       Window old_w = ssi->screensaver_window;
1237
1238       if (p->verbose_p)
1239         {
1240 #if 0
1241           fprintf (stderr, "%s: switching visuals\tfrom: ", progname);
1242           describe_visual (stderr, ssi->screen, ssi->current_visual);
1243           fprintf (stderr, "\t\t\t\tto:   ");
1244           describe_visual (stderr, ssi->screen, new_v);
1245           fprintf (stderr, "\t\t\t\t install cmap:   %s\n",
1246                    (install_cmap_p ? "yes" : "no"));
1247 #else
1248           fprintf (stderr, "%s: switching to visual ", progname);
1249           describe_visual (stderr, ssi->screen, new_v);
1250 #endif
1251         }
1252
1253       reset_stderr (ssi);
1254       ssi->current_visual = new_v;
1255       ssi->current_depth = visual_depth(ssi->screen, new_v);
1256       ssi->cmap = 0;
1257       ssi->screensaver_window = 0;
1258
1259       initialize_screensaver_window_1 (ssi);
1260       raise_window (si, True, True, False);
1261       store_vroot_property (si->dpy,
1262                             ssi->screensaver_window, ssi->screensaver_window);
1263       store_activate_time (si, True);
1264
1265
1266
1267       /* Transfer the grabs from the old window to the new.
1268          Actually I think none of this is necessary, since we always
1269          hold our grabs on the root window, but I wrote this before
1270          re-discovering that...
1271        */
1272
1273
1274       /* If we're destroying the window that holds our mouse grab,
1275          transfer the grab to the new window.  (Grab the server while
1276          so doing, to avoid a race condition.)
1277        */
1278       if (old_w == si->mouse_grab_window)
1279         {
1280           XGrabServer (si->dpy);                /* ############ DANGER! */
1281           ungrab_mouse(si);
1282           grab_mouse(si, ssi->screensaver_window,
1283                      (si->demo_mode_p ? 0 : ssi->cursor));
1284           XUngrabServer (si->dpy);
1285           XSync (si->dpy, False);               /* ###### (danger over) */
1286         }
1287
1288       /* If we're destroying the window that holds our keyboard grab,
1289          transfer the grab to the new window.  (Grab the server while
1290          so doing, to avoid a race condition.)
1291        */
1292       if (old_w == si->keyboard_grab_window)
1293         {
1294           XGrabServer (si->dpy);                /* ############ DANGER! */
1295           ungrab_kbd(si);
1296           grab_kbd(si, ssi->screensaver_window);
1297           XUngrabServer (si->dpy);
1298           XSync (si->dpy, False);               /* ###### (danger over) */
1299         }
1300
1301       /* Now we can destroy this window without horking our grabs. */
1302
1303       XDestroyWindow (si->dpy, old_w);
1304
1305       if (p->verbose_p)
1306         fprintf (stderr, "%s: destroyed old saver window 0x%lx.\n",
1307                  progname, (unsigned long) old_w);
1308
1309       if (old_c &&
1310           old_c != DefaultColormapOfScreen (ssi->screen) &&
1311           old_c != ssi->demo_cmap)
1312         XFreeColormap (si->dpy, old_c);
1313     }
1314
1315   return got_it;
1316 }