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