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