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