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