7a938c5a53a7f394bc4c605c0aed99c0238041c6
[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_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           /* Ensure that the default background of the window is really black,
826              not a pixmap or something.  (This does not clear the window.) */
827           XSetWindowBackground (si->dpy, ssi->screensaver_window,
828                                 ssi->black_pixel);
829         }
830
831       if (p->verbose_p) fprintf (stderr, "%s: fading... ", progname);
832
833       XGrabServer (si->dpy);
834
835       for (i = 0; i < si->nscreens; i++)
836         {
837           saver_screen_info *ssi = &si->screens[i];
838
839           /* grab and blacken mouse on the root window (saver not mapped yet)
840            */
841           if (grabbed != GrabSuccess)
842             grabbed = grab_mouse (si->dpy, ssi->screensaver_window,
843                                   (si->demo_mode_p ? 0 : ssi->cursor));
844
845           if (!dont_clear || ssi->stderr_overlay_window)
846             /* Do this before the fade, since the stderr cmap won't fade
847                even if we uninstall it (beats me...) */
848             clear_stderr (ssi);
849         }
850
851       fade_screens (si->dpy, current_maps, current_windows,
852                     p->fade_seconds, p->fade_ticks, True, !dont_clear);
853       free(current_maps);
854       free(current_windows);
855       current_maps = 0;
856       current_windows = 0;
857
858       if (p->verbose_p) fprintf (stderr, "fading done.\n");
859
860 #ifdef HAVE_MIT_SAVER_EXTENSION
861       for (i = 0; i < si->nscreens; i++)
862         {
863           saver_screen_info *ssi = &si->screens[i];
864           if (ssi->server_mit_saver_window &&
865               window_exists_p (si->dpy, ssi->server_mit_saver_window))
866             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
867         }
868 #endif /* HAVE_MIT_SAVER_EXTENSION */
869
870       if (grabbed == GrabSuccess)
871         XUngrabPointer (si->dpy, CurrentTime);
872       XUngrabServer (si->dpy);
873     }
874   else
875     {
876       for (i = 0; i < si->nscreens; i++)
877         {
878           saver_screen_info *ssi = &si->screens[i];
879           if (!dont_clear)
880             XClearWindow (si->dpy, ssi->screensaver_window);
881           if (!dont_clear || ssi->stderr_overlay_window)
882             clear_stderr (ssi);
883           XMapRaised (si->dpy, ssi->screensaver_window);
884 #ifdef HAVE_MIT_SAVER_EXTENSION
885           if (ssi->server_mit_saver_window &&
886               window_exists_p (si->dpy, ssi->server_mit_saver_window))
887             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
888 #endif /* HAVE_MIT_SAVER_EXTENSION */
889         }
890     }
891
892   for (i = 0; i < si->nscreens; i++)
893     {
894       saver_screen_info *ssi = &si->screens[i];
895       if (ssi->cmap)
896         XInstallColormap (si->dpy, ssi->cmap);
897     }
898 }
899
900 void
901 blank_screen (saver_info *si)
902 {
903   int i;
904   for (i = 0; i < si->nscreens; i++)
905     {
906       saver_screen_info *ssi = &si->screens[i];
907
908       save_real_vroot (ssi);
909       store_vroot_property (si->dpy,
910                             ssi->screensaver_window,
911                             ssi->screensaver_window);
912     }
913   store_activate_time (si, si->screen_blanked_p);
914   raise_window (si, False, False, False);
915   /* #### */
916   grab_keyboard_and_mouse (si->dpy, si->screens[0].screensaver_window,
917                            (si->demo_mode_p ? 0 : si->screens[0].cursor));
918 #ifdef HAVE_XHPDISABLERESET
919   if (si->locked_p && !hp_locked_p)
920     {
921       XHPDisableReset (si->dpy);        /* turn off C-Sh-Reset */
922       hp_locked_p = True;
923     }
924 #endif
925
926   si->screen_blanked_p = True;
927 }
928
929 void
930 unblank_screen (saver_info *si)
931 {
932   saver_preferences *p = &si->prefs;
933   int i;
934
935   store_activate_time (si, True);
936   reset_watchdog_timer (si, False);
937
938   if (p->unfade_p && !si->demo_mode_p)
939     {
940       int grabbed = -1;
941       Window *current_windows = (Window *)
942         calloc(sizeof(Window), si->nscreens);
943
944       for (i = 0; i < si->nscreens; i++)
945         {
946           saver_screen_info *ssi = &si->screens[i];
947           current_windows[i] = ssi->screensaver_window;
948           /* Ensure that the default background of the window is really black,
949              not a pixmap or something.  (This does not clear the window.) */
950           XSetWindowBackground (si->dpy, ssi->screensaver_window,
951                                 ssi->black_pixel);
952         }
953
954       if (p->verbose_p) fprintf (stderr, "%s: unfading... ", progname);
955
956       XSync (si->dpy, False);
957       XGrabServer (si->dpy);
958       XSync (si->dpy, False);
959       for (i = 0; i < si->nscreens; i++)
960         {
961           saver_screen_info *ssi = &si->screens[i];
962           if (grabbed != GrabSuccess)
963             grabbed = grab_mouse (si->dpy, RootWindowOfScreen (ssi->screen),
964                                   0);
965           clear_stderr (ssi);
966         }
967       XUngrabServer (si->dpy);
968       XSync (si->dpy, False);
969
970       fade_screens (si->dpy, 0, current_windows,
971                     p->fade_seconds, p->fade_ticks,
972                     False, False);
973
974       free(current_windows);
975       current_windows = 0;
976
977       if (p->verbose_p) fprintf (stderr, "unfading done.\n");
978       if (grabbed == GrabSuccess)
979         XUngrabPointer (si->dpy, CurrentTime);
980     }
981   else
982     {
983       for (i = 0; i < si->nscreens; i++)
984         {
985           saver_screen_info *ssi = &si->screens[i];
986           if (ssi->cmap)
987             {
988               Colormap c = DefaultColormapOfScreen (ssi->screen);
989               /* avoid technicolor */
990               XClearWindow (si->dpy, ssi->screensaver_window);
991               if (c) XInstallColormap (si->dpy, c);
992             }
993           XUnmapWindow (si->dpy, ssi->screensaver_window);
994         }
995     }
996
997
998   /* If the focus window does has a non-default colormap, then install
999      that colormap as well.  (On SGIs, this will cause both the root map
1000      and the focus map to be installed simultaniously.  It'd be nice to
1001      pick up the other colormaps that had been installed, too; perhaps
1002      XListInstalledColormaps could be used for that?)
1003    */
1004   {
1005     Window focus = 0;
1006     int revert_to;
1007     XGetInputFocus (si->dpy, &focus, &revert_to);
1008     if (focus && focus != PointerRoot && focus != None)
1009       {
1010         XWindowAttributes xgwa;
1011         xgwa.colormap = 0;
1012         XGetWindowAttributes (si->dpy, focus, &xgwa);
1013         if (xgwa.colormap &&
1014             xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1015           XInstallColormap (si->dpy, xgwa.colormap);
1016       }
1017   }
1018
1019
1020   for (i = 0; i < si->nscreens; i++)
1021     {
1022       saver_screen_info *ssi = &si->screens[i];
1023       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1024     }
1025
1026   store_activate_time(si, False);  /* store unblank time */
1027
1028   ungrab_keyboard_and_mouse (si->dpy);
1029   restore_real_vroot (si);
1030
1031 #ifdef HAVE_XHPDISABLERESET
1032   if (hp_locked_p)
1033     {
1034       XHPEnableReset (si->dpy); /* turn C-Sh-Reset back on */
1035       hp_locked_p = False;
1036     }
1037 #endif
1038
1039   /* Unmap the windows a second time, dammit -- just to avoid a race
1040      with the screen-grabbing hacks.  (I'm not sure if this is really
1041      necessary; I'm stabbing in the dark now.)
1042   */
1043   for (i = 0; i < si->nscreens; i++)
1044     XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1045
1046   si->screen_blanked_p = False;
1047 }
1048
1049
1050 static void
1051 store_activate_time (saver_info *si, Bool use_last_p)
1052 {
1053   static time_t last_time = 0;
1054   time_t now = ((use_last_p && last_time) ? last_time : time ((time_t) 0));
1055   CARD32 now32 = (CARD32) now;
1056   int i;
1057   last_time = now;
1058
1059   for (i = 0; i < si->nscreens; i++)
1060     {
1061       saver_screen_info *ssi = &si->screens[i];
1062       if (!ssi->screensaver_window) continue;
1063       XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_TIME,
1064                        XA_INTEGER, 32, PropModeReplace,
1065                        (unsigned char *) &now32, 1);
1066     }
1067 }
1068
1069
1070 Bool
1071 select_visual (saver_screen_info *ssi, const char *visual_name)
1072 {
1073   saver_info *si = ssi->global;
1074   saver_preferences *p = &si->prefs;
1075   Bool install_cmap_p = p->install_cmap_p;
1076   Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1077   Visual *new_v;
1078   Bool got_it;
1079
1080   if (visual_name && *visual_name)
1081     {
1082       if (!strcmp(visual_name, "default-i"))
1083         {
1084           visual_name = "default";
1085           install_cmap_p = True;
1086         }
1087       else if (!strcmp(visual_name, "default-n"))
1088         {
1089           visual_name = "default";
1090           install_cmap_p = False;
1091         }
1092       new_v = get_visual (ssi->screen, visual_name, True, False);
1093     }
1094   else
1095     {
1096       new_v = ssi->default_visual;
1097     }
1098
1099   got_it = !!new_v;
1100
1101   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1102     install_cmap_p = True;
1103
1104   ssi->install_cmap_p = install_cmap_p;
1105
1106   if (new_v &&
1107       ((ssi->current_visual != new_v) ||
1108        (install_cmap_p != was_installed_p)))
1109     {
1110       Colormap old_c = ssi->cmap;
1111       Window old_w = ssi->screensaver_window;
1112
1113       if (p->verbose_p)
1114         {
1115 #if 0
1116           fprintf (stderr, "%s: switching visuals\tfrom: ", progname);
1117           describe_visual (stderr, ssi->screen, ssi->current_visual);
1118           fprintf (stderr, "\t\t\t\tto:   ");
1119           describe_visual (stderr, ssi->screen, new_v);
1120           fprintf (stderr, "\t\t\t\t install cmap:   %s\n",
1121                    (install_cmap_p ? "yes" : "no"));
1122 #else
1123           fprintf (stderr, "%s: switching to visual ", progname);
1124           describe_visual (stderr, ssi->screen, new_v);
1125 #endif
1126         }
1127
1128       reset_stderr (ssi);
1129       ssi->current_visual = new_v;
1130       ssi->current_depth = visual_depth(ssi->screen, new_v);
1131       ssi->cmap = 0;
1132       ssi->screensaver_window = 0;
1133
1134       initialize_screensaver_window_1 (ssi);
1135       raise_window (si, True, True, False);
1136       store_vroot_property (si->dpy,
1137                             ssi->screensaver_window, ssi->screensaver_window);
1138       store_activate_time (si, True);
1139
1140       XDestroyWindow (si->dpy, old_w);
1141       if (old_c &&
1142           old_c != DefaultColormapOfScreen (ssi->screen) &&
1143           old_c != ssi->demo_cmap)
1144         XFreeColormap (si->dpy, old_c);
1145     }
1146
1147   return got_it;
1148 }