ftp://ftp.demon.nl/disk1/redhat-contrib/libc5/SRPMS/xscreensaver-2.14-1.src.rpm
[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   Bool install_cmap_p = ssi->install_cmap_p;
562
563   /* This resets the screensaver window as fully as possible, since there's
564      no way of knowing what some random client may have done to us in the
565      meantime.  We could just destroy and recreate the window, but that has
566      its own set of problems...
567    */
568   XColor black;
569   XClassHint class_hints;
570   XSetWindowAttributes attrs;
571   unsigned long attrmask;
572   int width = WidthOfScreen (ssi->screen);
573   int height = HeightOfScreen (ssi->screen);
574   char id [2048];
575   static Bool printed_visual_info = False;  /* only print the message once. */
576
577   black.red = black.green = black.blue = 0;
578
579   if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
580     ssi->cmap = 0;
581
582   if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
583     install_cmap_p = True;
584
585   if (install_cmap_p)
586     {
587       if (! ssi->cmap)
588         {
589           ssi->cmap = XCreateColormap (si->dpy, RootWindowOfScreen (ssi->screen),
590                                       ssi->current_visual, AllocNone);
591           if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
592           ssi->black_pixel = black.pixel;
593         }
594     }
595   else
596     {
597       Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
598       if (ssi->cmap)
599         {
600           XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
601           if (ssi->cmap != ssi->demo_cmap &&
602               ssi->cmap != def_cmap)
603             XFreeColormap (si->dpy, ssi->cmap);
604         }
605       ssi->cmap = def_cmap;
606       ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
607     }
608
609   attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
610               CWBackPixel | CWBackingPixel | CWBorderPixel);
611   attrs.override_redirect = True;
612
613   /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
614      actually be reading these events during normal operation; but we still
615      need to see Button events for demo-mode to work properly.
616    */
617   attrs.event_mask = (KeyPressMask | KeyReleaseMask |
618                       ButtonPressMask | ButtonReleaseMask |
619                       PointerMotionMask);
620
621   attrs.backing_store = NotUseful;
622   attrs.colormap = ssi->cmap;
623   attrs.background_pixel = ssi->black_pixel;
624   attrs.backing_pixel = ssi->black_pixel;
625   attrs.border_pixel = ssi->black_pixel;
626
627 #ifdef DEBUG
628   if (p->debug_p) width = width / 2;
629 #endif /* DEBUG */
630
631   if (!p->verbose_p || printed_visual_info)
632     ;
633   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
634     {
635       fprintf (stderr, "%s: using default visual ", progname);
636       describe_visual (stderr, ssi->screen, ssi->current_visual);
637     }
638   else
639     {
640       fprintf (stderr, "%s: using visual:   ", progname);
641       describe_visual (stderr, ssi->screen, ssi->current_visual);
642       fprintf (stderr, "%s: default visual: ", progname);
643       describe_visual (stderr, ssi->screen,
644                        DefaultVisualOfScreen (ssi->screen));
645     }
646   printed_visual_info = True;
647
648 #ifdef HAVE_MIT_SAVER_EXTENSION
649   if (p->use_mit_saver_extension)
650     {
651       XScreenSaverInfo *info;
652       Window root = RootWindowOfScreen (ssi->screen);
653
654 #if 0
655       /* This call sets the server screensaver timeouts to what we think
656          they should be (based on the resources and args xscreensaver was
657          started with.)  It's important that we do this to sync back up
658          with the server - if we have turned on prematurely, as by an
659          ACTIVATE ClientMessage, then the server may decide to activate
660          the screensaver while it's already active.  That's ok for us,
661          since we would know to ignore that ScreenSaverActivate event,
662          but a side effect of this would be that the server would map its
663          saver window (which we then hide again right away) meaning that
664          the bits currently on the screen get blown away.  Ugly. */
665
666       /* #### Ok, that doesn't work - when we tell the server that the
667          screensaver is "off" it sends us a Deactivate event, which is
668          sensible... but causes the saver to never come on.  Hmm. */
669       disable_builtin_screensaver (si, True);
670 #endif /* 0 */
671
672 #if 0
673       /* #### The MIT-SCREEN-SAVER extension gives us access to the
674          window that the server itself uses for saving the screen.
675          However, using this window in any way, in particular, calling
676          XScreenSaverSetAttributes() as below, tends to make the X server
677          crash.  So fuck it, let's try and get along without using it...
678
679          It's also inconvenient to use this window because it doesn't
680          always exist (though the ID is constant.)  So to use this
681          window, we'd have to reimplement the ACTIVATE ClientMessage to
682          tell the *server* to tell *us* to turn on, to cause the window
683          to get created at the right time.  Gag.  */
684       XScreenSaverSetAttributes (si->dpy, root,
685                                  0, 0, width, height, 0,
686                                  current_depth, InputOutput, visual,
687                                  attrmask, &attrs);
688       XSync (si->dpy, False);
689 #endif /* 0 */
690
691       info = XScreenSaverAllocInfo ();
692       XScreenSaverQueryInfo (si->dpy, root, info);
693       ssi->server_mit_saver_window = info->window;
694       if (! ssi->server_mit_saver_window) abort ();
695       XFree (info);
696     }
697 #endif /* HAVE_MIT_SAVER_EXTENSION */
698
699   if (ssi->screensaver_window)
700     {
701       XWindowChanges changes;
702       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
703       changes.x = 0;
704       changes.y = 0;
705       changes.width = width;
706       changes.height = height;
707       changes.border_width = 0;
708
709       XConfigureWindow (si->dpy, ssi->screensaver_window,
710                         changesmask, &changes);
711       XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
712                                attrmask, &attrs);
713     }
714   else
715     {
716       ssi->screensaver_window =
717         XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen), 0, 0,
718                        width, height, 0, ssi->current_depth, InputOutput,
719                        ssi->current_visual, attrmask, &attrs);
720       reset_stderr (ssi);
721       store_activate_time(si, True);
722       if (p->verbose_p)
723         fprintf (stderr, "%s: saver window is 0x%lx.\n",
724                  progname, (unsigned long) ssi->screensaver_window);
725     }
726
727 #ifdef HAVE_MIT_SAVER_EXTENSION
728   if (!p->use_mit_saver_extension ||
729       window_exists_p (si->dpy, ssi->screensaver_window))
730     /* When using the MIT-SCREEN-SAVER extension, the window pointed to
731        by screensaver_window only exists while the saver is active.
732        So we must be careful to only try and manipulate it while it
733        exists...
734        (#### The above comment would be true if the MIT extension actually
735        worked, but it's not true today -- see `server_mit_saver_window'.)
736      */
737 #endif /* HAVE_MIT_SAVER_EXTENSION */
738     {
739       class_hints.res_name = progname;
740       class_hints.res_class = progclass;
741       XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
742       XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
743       XChangeProperty (si->dpy, ssi->screensaver_window,
744                        XA_SCREENSAVER_VERSION,
745                        XA_STRING, 8, PropModeReplace,
746                        (unsigned char *) si->version,
747                        strlen (si->version));
748
749       sprintf (id, "%lu on host ", (unsigned long) getpid ());
750
751 # if defined(HAVE_UNAME)
752       {
753         struct utsname uts;
754         if (uname (&uts) < 0)
755           strcat (id, "???");
756         else
757           strcat (id, uts.nodename);
758       }
759 # elif defined(VMS)
760       strcat (id, getenv("SYS$NODE"));
761 # else  /* !HAVE_UNAME && !VMS */
762       strcat (id, "???");
763 # endif /* !HAVE_UNAME && !VMS */
764
765       XChangeProperty (si->dpy, ssi->screensaver_window,
766                        XA_SCREENSAVER_ID, XA_STRING,
767                        8, PropModeReplace, (unsigned char *) id, strlen (id));
768
769       if (!ssi->cursor)
770         {
771           Pixmap bit;
772           bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
773                                              "\000", 1, 1,
774                                              BlackPixelOfScreen (ssi->screen),
775                                              BlackPixelOfScreen (ssi->screen),
776                                              1);
777           ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
778                                              0, 0);
779           XFreePixmap (si->dpy, bit);
780         }
781
782       XSetWindowBackground (si->dpy, ssi->screensaver_window,
783                             ssi->black_pixel);
784       if (si->demo_mode_p)
785         XUndefineCursor (si->dpy, ssi->screensaver_window);
786       else
787         XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
788     }
789 }
790
791 void
792 initialize_screensaver_window (saver_info *si)
793 {
794   int i;
795   for (i = 0; i < si->nscreens; i++)
796     initialize_screensaver_window_1 (&si->screens[i]);
797 }
798
799
800 void 
801 raise_window (saver_info *si,
802               Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
803 {
804   saver_preferences *p = &si->prefs;
805   int i;
806
807   initialize_screensaver_window (si);
808   reset_watchdog_timer (si, True);
809
810   if (p->fade_p && !inhibit_fade && !si->demo_mode_p)
811     {
812       int grabbed = -1;
813       Window *current_windows = (Window *)
814         calloc(sizeof(Window), si->nscreens);
815       Colormap *current_maps = (Colormap *)
816         calloc(sizeof(Colormap), si->nscreens);
817
818       for (i = 0; i < si->nscreens; i++)
819         {
820           saver_screen_info *ssi = &si->screens[i];
821           current_windows[i] = ssi->screensaver_window;
822           current_maps[i] = (between_hacks_p
823                              ? ssi->cmap
824                              : DefaultColormapOfScreen (ssi->screen));
825         }
826
827       if (p->verbose_p) fprintf (stderr, "%s: fading... ", progname);
828
829       XGrabServer (si->dpy);
830
831       for (i = 0; i < si->nscreens; i++)
832         {
833           saver_screen_info *ssi = &si->screens[i];
834
835           /* grab and blacken mouse on the root window (saver not mapped yet)
836            */
837           if (grabbed != GrabSuccess)
838             grabbed = grab_mouse (si->dpy, ssi->screensaver_window,
839                                   (si->demo_mode_p ? 0 : ssi->cursor));
840
841           if (!dont_clear || ssi->stderr_overlay_window)
842             /* Do this before the fade, since the stderr cmap won't fade
843                even if we uninstall it (beats me...) */
844             clear_stderr (ssi);
845         }
846
847       fade_screens (si->dpy, current_maps, current_windows,
848                     p->fade_seconds, p->fade_ticks, True, !dont_clear);
849       free(current_maps);
850       free(current_windows);
851       current_maps = 0;
852       current_windows = 0;
853
854       if (p->verbose_p) fprintf (stderr, "fading done.\n");
855
856 #ifdef HAVE_MIT_SAVER_EXTENSION
857       for (i = 0; i < si->nscreens; i++)
858         {
859           saver_screen_info *ssi = &si->screens[i];
860           if (ssi->server_mit_saver_window &&
861               window_exists_p (si->dpy, ssi->server_mit_saver_window))
862             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
863         }
864 #endif /* HAVE_MIT_SAVER_EXTENSION */
865
866       if (grabbed == GrabSuccess)
867         XUngrabPointer (si->dpy, CurrentTime);
868       XUngrabServer (si->dpy);
869     }
870   else
871     {
872       for (i = 0; i < si->nscreens; i++)
873         {
874           saver_screen_info *ssi = &si->screens[i];
875           if (!dont_clear)
876             XClearWindow (si->dpy, ssi->screensaver_window);
877           if (!dont_clear || ssi->stderr_overlay_window)
878             clear_stderr (ssi);
879           XMapRaised (si->dpy, ssi->screensaver_window);
880 #ifdef HAVE_MIT_SAVER_EXTENSION
881           if (ssi->server_mit_saver_window &&
882               window_exists_p (si->dpy, ssi->server_mit_saver_window))
883             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
884 #endif /* HAVE_MIT_SAVER_EXTENSION */
885         }
886     }
887
888   for (i = 0; i < si->nscreens; i++)
889     {
890       saver_screen_info *ssi = &si->screens[i];
891       if (ssi->cmap)
892         XInstallColormap (si->dpy, ssi->cmap);
893     }
894 }
895
896 void
897 blank_screen (saver_info *si)
898 {
899   int i;
900   for (i = 0; i < si->nscreens; i++)
901     {
902       saver_screen_info *ssi = &si->screens[i];
903
904       save_real_vroot (ssi);
905       store_vroot_property (si->dpy,
906                             ssi->screensaver_window,
907                             ssi->screensaver_window);
908     }
909   store_activate_time (si, si->screen_blanked_p);
910   raise_window (si, False, False, False);
911   /* #### */
912   grab_keyboard_and_mouse (si->dpy, si->screens[0].screensaver_window,
913                            (si->demo_mode_p ? 0 : si->screens[0].cursor));
914 #ifdef HAVE_XHPDISABLERESET
915   if (si->locked_p && !hp_locked_p)
916     {
917       XHPDisableReset (si->dpy);        /* turn off C-Sh-Reset */
918       hp_locked_p = True;
919     }
920 #endif
921
922   si->screen_blanked_p = True;
923 }
924
925 void
926 unblank_screen (saver_info *si)
927 {
928   saver_preferences *p = &si->prefs;
929   int i;
930
931   store_activate_time (si, True);
932   reset_watchdog_timer (si, False);
933
934   if (p->unfade_p && !si->demo_mode_p)
935     {
936       int grabbed = -1;
937       Window *current_windows = (Window *)
938         calloc(sizeof(Window), si->nscreens);
939
940       for (i = 0; i < si->nscreens; i++)
941         {
942           saver_screen_info *ssi = &si->screens[i];
943           current_windows[i] = ssi->screensaver_window;
944         }
945
946       if (p->verbose_p) fprintf (stderr, "%s: unfading... ", progname);
947
948       XGrabServer (si->dpy);
949       for (i = 0; i < si->nscreens; i++)
950         {
951           saver_screen_info *ssi = &si->screens[i];
952           if (grabbed != GrabSuccess)
953             grabbed = grab_mouse (si->dpy, RootWindowOfScreen (ssi->screen),
954                                   0);
955           clear_stderr (ssi);
956         }
957       XUngrabServer (si->dpy);
958
959       fade_screens (si->dpy, 0, current_windows,
960                     p->fade_seconds, p->fade_ticks,
961                     False, False);
962
963       free(current_windows);
964       current_windows = 0;
965
966       if (p->verbose_p) fprintf (stderr, "unfading done.\n");
967       if (grabbed == GrabSuccess)
968         XUngrabPointer (si->dpy, CurrentTime);
969     }
970   else
971     {
972       for (i = 0; i < si->nscreens; i++)
973         {
974           saver_screen_info *ssi = &si->screens[i];
975           if (ssi->cmap)
976             {
977               Colormap c = DefaultColormapOfScreen (ssi->screen);
978               /* avoid technicolor */
979               XClearWindow (si->dpy, ssi->screensaver_window);
980               if (c) XInstallColormap (si->dpy, c);
981             }
982           XUnmapWindow (si->dpy, ssi->screensaver_window);
983         }
984     }
985
986
987   /* If the focus window does has a non-default colormap, then install
988      that colormap as well.  (On SGIs, this will cause both the root map
989      and the focus map to be installed simultaniously.  It'd be nice to
990      pick up the other colormaps that had been installed, too; perhaps
991      XListInstalledColormaps could be used for that?)
992    */
993   {
994     Window focus = 0;
995     int revert_to;
996     XGetInputFocus (si->dpy, &focus, &revert_to);
997     if (focus && focus != PointerRoot && focus != None)
998       {
999         XWindowAttributes xgwa;
1000         xgwa.colormap = 0;
1001         XGetWindowAttributes (si->dpy, focus, &xgwa);
1002         if (xgwa.colormap &&
1003             xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1004           XInstallColormap (si->dpy, xgwa.colormap);
1005       }
1006   }
1007
1008
1009   for (i = 0; i < si->nscreens; i++)
1010     {
1011       saver_screen_info *ssi = &si->screens[i];
1012       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1013     }
1014
1015   store_activate_time(si, False);  /* store unblank time */
1016
1017   ungrab_keyboard_and_mouse (si->dpy);
1018   restore_real_vroot (si);
1019
1020 #ifdef HAVE_XHPDISABLERESET
1021   if (hp_locked_p)
1022     {
1023       XHPEnableReset (si->dpy); /* turn C-Sh-Reset back on */
1024       hp_locked_p = False;
1025     }
1026 #endif
1027
1028   si->screen_blanked_p = False;
1029 }
1030
1031
1032 static void
1033 store_activate_time (saver_info *si, Bool use_last_p)
1034 {
1035   static time_t last_time = 0;
1036   time_t now = ((use_last_p && last_time) ? last_time : time ((time_t) 0));
1037   CARD32 now32 = (CARD32) now;
1038   int i;
1039   last_time = now;
1040
1041   for (i = 0; i < si->nscreens; i++)
1042     {
1043       saver_screen_info *ssi = &si->screens[i];
1044       if (!ssi->screensaver_window) continue;
1045       XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_TIME,
1046                        XA_INTEGER, 32, PropModeReplace,
1047                        (unsigned char *) &now32, 1);
1048     }
1049 }
1050
1051
1052 Bool
1053 select_visual (saver_screen_info *ssi, const char *visual_name)
1054 {
1055   saver_info *si = ssi->global;
1056   saver_preferences *p = &si->prefs;
1057   Bool install_cmap_p = p->install_cmap_p;
1058   Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1059   Visual *new_v;
1060   Bool got_it;
1061
1062   if (visual_name && *visual_name)
1063     {
1064       if (!strcmp(visual_name, "default-i"))
1065         {
1066           visual_name = "default";
1067           install_cmap_p = True;
1068         }
1069       else if (!strcmp(visual_name, "default-n"))
1070         {
1071           visual_name = "default";
1072           install_cmap_p = False;
1073         }
1074       new_v = get_visual (ssi->screen, visual_name, True, False);
1075     }
1076   else
1077     {
1078       new_v = ssi->default_visual;
1079     }
1080
1081   got_it = !!new_v;
1082
1083   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1084     install_cmap_p = True;
1085
1086   ssi->install_cmap_p = install_cmap_p;
1087
1088   if (new_v &&
1089       ((ssi->current_visual != new_v) ||
1090        (install_cmap_p != was_installed_p)))
1091     {
1092       Colormap old_c = ssi->cmap;
1093       Window old_w = ssi->screensaver_window;
1094
1095       if (p->verbose_p)
1096         {
1097 #if 0
1098           fprintf (stderr, "%s: switching visuals\tfrom: ", progname);
1099           describe_visual (stderr, ssi->screen, ssi->current_visual);
1100           fprintf (stderr, "\t\t\t\tto:   ");
1101           describe_visual (stderr, ssi->screen, new_v);
1102           fprintf (stderr, "\t\t\t\t install cmap:   %s\n",
1103                    (install_cmap_p ? "yes" : "no"));
1104 #else
1105           fprintf (stderr, "%s: switching to visual ", progname);
1106           describe_visual (stderr, ssi->screen, new_v);
1107 #endif
1108         }
1109
1110       reset_stderr (ssi);
1111       ssi->current_visual = new_v;
1112       ssi->current_depth = visual_depth(ssi->screen, new_v);
1113       ssi->cmap = 0;
1114       ssi->screensaver_window = 0;
1115
1116       initialize_screensaver_window_1 (ssi);
1117       raise_window (si, True, True, False);
1118       store_vroot_property (si->dpy,
1119                             ssi->screensaver_window, ssi->screensaver_window);
1120       store_activate_time (si, True);
1121
1122       XDestroyWindow (si->dpy, old_w);
1123       if (old_c &&
1124           old_c != DefaultColormapOfScreen (ssi->screen) &&
1125           old_c != ssi->demo_cmap)
1126         XFreeColormap (si->dpy, old_c);
1127     }
1128
1129   return got_it;
1130 }