http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.02.tar.gz
[xscreensaver] / driver / windows.c
1 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
2  * xscreensaver, Copyright (c) 1991-2001 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 #endif /* VMS */
21
22 #ifndef VMS
23 # include <pwd.h>               /* for getpwuid() */
24 #else /* VMS */
25 # include "vms-pwd.h"
26 #endif /* VMS */
27
28 #ifdef HAVE_UNAME
29 # include <sys/utsname.h>       /* for uname() */
30 #endif /* HAVE_UNAME */
31
32 #include <stdio.h>
33 #include <X11/Xproto.h>         /* for CARD32 */
34 #include <X11/Xlib.h>
35 #include <X11/Xutil.h>          /* for XSetClassHint() */
36 #include <X11/Xatom.h>
37 #include <X11/Xos.h>            /* for time() */
38 #include <signal.h>             /* for the signal names */
39 #include <time.h>
40 #include <sys/time.h>
41
42 #ifdef HAVE_MIT_SAVER_EXTENSION
43 # include <X11/extensions/scrnsaver.h>
44 #endif /* HAVE_MIT_SAVER_EXTENSION */
45
46 #ifdef HAVE_XF86VMODE
47 # include <X11/extensions/xf86vmode.h>
48 #endif /* HAVE_XF86VMODE */
49
50 #ifdef HAVE_XINERAMA
51 # include <X11/extensions/Xinerama.h>
52 #endif /* HAVE_XINERAMA */
53
54
55 /* This file doesn't need the Xt headers, so stub these types out... */
56 #undef XtPointer
57 #define XtAppContext void*
58 #define XrmDatabase  void*
59 #define XtIntervalId void*
60 #define XtPointer    void*
61 #define Widget       void*
62
63 #include "xscreensaver.h"
64 #include "visual.h"
65 #include "fade.h"
66
67
68 extern int kill (pid_t, int);           /* signal() is in sys/signal.h... */
69
70 Atom XA_VROOT, XA_XSETROOT_ID, XA_ESETROOT_PMAP_ID, XA_XROOTPMAP_ID;
71 Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
72 Atom XA_SCREENSAVER_STATUS;
73
74
75 extern saver_info *global_si_kludge;    /* I hate C so much... */
76
77 static void maybe_transfer_grabs (saver_screen_info *ssi,
78                                   Window old_w, Window new_w, int new_screen);
79
80 #define ALL_POINTER_EVENTS \
81         (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
82          LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
83          Button1MotionMask | Button2MotionMask | Button3MotionMask | \
84          Button4MotionMask | Button5MotionMask | ButtonMotionMask)
85
86
87 static const char *
88 grab_string(int status)
89 {
90   switch (status)
91     {
92     case GrabSuccess:     return "GrabSuccess";
93     case AlreadyGrabbed:  return "AlreadyGrabbed";
94     case GrabInvalidTime: return "GrabInvalidTime";
95     case GrabNotViewable: return "GrabNotViewable";
96     case GrabFrozen:      return "GrabFrozen";
97     default:
98       {
99         static char foo[255];
100         sprintf(foo, "unknown status: %d", status);
101         return foo;
102       }
103     }
104 }
105
106 static int
107 grab_kbd(saver_info *si, Window w, int screen_no)
108 {
109   saver_preferences *p = &si->prefs;
110   int status = XGrabKeyboard (si->dpy, w, True,
111                               /* I don't really understand Sync vs Async,
112                                  but these seem to work... */
113                               GrabModeSync, GrabModeAsync,
114                               CurrentTime);
115   if (status == GrabSuccess)
116     {
117       si->keyboard_grab_window = w;
118       si->keyboard_grab_screen = screen_no;
119     }
120
121   if (p->verbose_p)
122     fprintf(stderr, "%s: %d: grabbing keyboard on 0x%x... %s.\n",
123             blurb(), screen_no, (unsigned long) w, grab_string(status));
124   return status;
125 }
126
127
128 static int
129 grab_mouse (saver_info *si, Window w, Cursor cursor, int screen_no)
130 {
131   saver_preferences *p = &si->prefs;
132   int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS,
133                              GrabModeAsync, GrabModeAsync, w,
134                              cursor, CurrentTime);
135   if (status == GrabSuccess)
136     {
137       si->mouse_grab_window = w;
138       si->mouse_grab_screen = screen_no;
139     }
140
141   if (p->verbose_p)
142     fprintf(stderr, "%s: %d: grabbing mouse on 0x%x... %s.\n",
143             blurb(), screen_no, (unsigned long) w, grab_string(status));
144   return status;
145 }
146
147
148 static void
149 ungrab_kbd(saver_info *si)
150 {
151   saver_preferences *p = &si->prefs;
152   XUngrabKeyboard(si->dpy, CurrentTime);
153   if (p->verbose_p)
154     fprintf(stderr, "%s: %d: ungrabbing keyboard (was 0x%x).\n",
155             blurb(), si->keyboard_grab_screen,
156             (unsigned long) si->keyboard_grab_window);
157   si->keyboard_grab_window = 0;
158 }
159
160
161 static void
162 ungrab_mouse(saver_info *si)
163 {
164   saver_preferences *p = &si->prefs;
165   XUngrabPointer(si->dpy, CurrentTime);
166   if (p->verbose_p)
167     fprintf(stderr, "%s: %d: ungrabbing mouse (was 0x%x).\n",
168             blurb(), si->mouse_grab_screen,
169             (unsigned long) si->mouse_grab_window);
170   si->mouse_grab_window = 0;
171 }
172
173
174 static Bool
175 grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor,
176                          int screen_no)
177 {
178   Status mstatus, kstatus;
179   int i;
180   int retries = 4;
181
182   for (i = 0; i < retries; i++)
183     {
184       XSync (si->dpy, False);
185       kstatus = grab_kbd (si, window, screen_no);
186       if (kstatus == GrabSuccess)
187         break;
188
189       /* else, wait a second and try to grab again. */
190       sleep (1);
191     }
192
193   if (kstatus != GrabSuccess)
194     fprintf (stderr, "%s: couldn't grab keyboard!  (%s)\n",
195              blurb(), grab_string(kstatus));
196
197   for (i = 0; i < retries; i++)
198     {
199       XSync (si->dpy, False);
200       mstatus = grab_mouse (si, window, cursor, screen_no);
201       if (mstatus == GrabSuccess)
202         break;
203
204       /* else, wait a second and try to grab again. */
205       sleep (1);
206     }
207
208   if (mstatus != GrabSuccess)
209     fprintf (stderr, "%s: couldn't grab pointer!  (%s)\n",
210              blurb(), grab_string(mstatus));
211
212   return (kstatus == GrabSuccess ||
213           mstatus == GrabSuccess);
214 }
215
216 static void
217 ungrab_keyboard_and_mouse (saver_info *si)
218 {
219   ungrab_mouse (si);
220   ungrab_kbd (si);
221 }
222
223
224 int
225 move_mouse_grab (saver_info *si, Window to, Cursor cursor, int to_screen_no)
226 {
227   Window old = si->mouse_grab_window;
228
229   if (old == 0)
230     return grab_mouse (si, to, cursor, to_screen_no);
231   else
232     {
233       saver_preferences *p = &si->prefs;
234       int status;
235
236       XSync (si->dpy, False);
237       XGrabServer (si->dpy);                    /* ############ DANGER! */
238       XSync (si->dpy, False);
239
240       if (p->verbose_p)
241         fprintf(stderr, "%s: grabbing server...\n", blurb());
242
243       ungrab_mouse (si);
244       status = grab_mouse (si, to, cursor, to_screen_no);
245
246       if (status != GrabSuccess)   /* Augh! */
247         {
248           sleep (1);               /* Note dramatic evil of sleeping
249                                       with server grabbed. */
250           XSync (si->dpy, False);
251           status = grab_mouse (si, to, cursor, to_screen_no);
252         }
253
254       if (status != GrabSuccess)   /* Augh!  Try to get the old one back... */
255         grab_mouse (si, old, cursor, to_screen_no);
256
257       XUngrabServer (si->dpy);
258       XSync (si->dpy, False);                   /* ###### (danger over) */
259
260       if (p->verbose_p)
261         fprintf(stderr, "%s: ungrabbing server.\n", blurb());
262
263       return status;
264     }
265 }
266
267
268 /* Prints an error message to stderr and returns True if there is another
269    xscreensaver running already.  Silently returns False otherwise. */
270 Bool
271 ensure_no_screensaver_running (Display *dpy, Screen *screen)
272 {
273   Bool status = 0;
274   int i;
275   Window root = RootWindowOfScreen (screen);
276   Window root2, parent, *kids;
277   unsigned int nkids;
278   XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
279
280   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
281     abort ();
282   if (root != root2)
283     abort ();
284   if (parent)
285     abort ();
286   for (i = 0; i < nkids; i++)
287     {
288       Atom type;
289       int format;
290       unsigned long nitems, bytesafter;
291       char *version;
292
293       if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
294                               False, XA_STRING, &type, &format, &nitems,
295                               &bytesafter, (unsigned char **) &version)
296           == Success
297           && type != None)
298         {
299           char *id;
300           if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
301                                    False, XA_STRING, &type, &format, &nitems,
302                                    &bytesafter, (unsigned char **) &id)
303               == Success
304               || type == None)
305             id = "???";
306
307           fprintf (stderr,
308       "%s: already running on display %s (window 0x%x)\n from process %s.\n",
309                    blurb(), DisplayString (dpy), (int) kids [i], id);
310           status = True;
311         }
312     }
313
314   if (kids) XFree ((char *) kids);
315   XSync (dpy, False);
316   XSetErrorHandler (old_handler);
317   return status;
318 }
319
320
321 \f
322 /* Virtual-root hackery */
323
324 #ifdef _VROOT_H_
325 ERROR!  You must not include vroot.h in this file.
326 #endif
327
328 static void
329 store_vroot_property (Display *dpy, Window win, Window value)
330 {
331 #if 0
332   if (p->verbose_p)
333     fprintf (stderr,
334              "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(), 
335              win,
336              (win == screensaver_window ? "ScreenSaver" :
337               (win == real_vroot ? "VRoot" :
338                (win == real_vroot_value ? "Vroot_value" : "???"))),
339              value,
340              (value == screensaver_window ? "ScreenSaver" :
341               (value == real_vroot ? "VRoot" :
342                (value == real_vroot_value ? "Vroot_value" : "???"))));
343 #endif
344   XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
345                    (unsigned char *) &value, 1);
346 }
347
348 static void
349 remove_vroot_property (Display *dpy, Window win)
350 {
351 #if 0
352   if (p->verbose_p)
353     fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win, 
354              (win == screensaver_window ? "ScreenSaver" :
355               (win == real_vroot ? "VRoot" :
356                (win == real_vroot_value ? "Vroot_value" : "???"))));
357 #endif
358   XDeleteProperty (dpy, win, XA_VROOT);
359 }
360
361
362 static Bool safe_XKillClient (Display *dpy, XID id);
363
364 static void
365 kill_xsetroot_data_1 (Display *dpy, Window window,
366                       Atom prop, const char *atom_name,
367                       Bool verbose_p)
368 {
369   Atom type;
370   int format;
371   unsigned long nitems, bytesafter;
372   Pixmap *dataP = 0;
373
374   /* If the user has been using xv or xsetroot as a screensaver (to display
375      an image on the screensaver window, as a kind of slideshow) then the
376      pixmap and its associated color cells have been put in RetainPermanent
377      CloseDown mode.  Since we're not destroying the xscreensaver window,
378      but merely unmapping it, we need to free these resources or those
379      colormap cells will stay allocated while the screensaver is off.  (We
380      could just delete the screensaver window and recreate it later, but
381      that could cause other problems.)  This code does an atomic read-and-
382      delete of the _XSETROOT_ID property, and if it held a pixmap, then we
383      cause the RetainPermanent resources of the client which created it
384      (and which no longer exists) to be freed.
385
386      Update: it seems that Gnome and KDE do this same trick, but with the
387      properties "ESETROOT_PMAP_ID" and/or "_XROOTPMAP_ID" instead of
388      "_XSETROOT_ID".  So, we'll kill those too.
389    */
390   if (XGetWindowProperty (dpy, window, prop, 0, 1,
391                           True, AnyPropertyType, &type, &format, &nitems, 
392                           &bytesafter, (unsigned char **) &dataP)
393       == Success
394       && type != None)
395     {
396       if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
397           nitems == 1 && bytesafter == 0)
398         {
399           if (verbose_p)
400             fprintf (stderr, "%s: destroying %s data (0x%lX).\n",
401                      blurb(), atom_name, *dataP);
402           safe_XKillClient (dpy, *dataP);
403         }
404       else
405         fprintf (stderr,
406                  "%s: deleted unrecognised %s property: \n"
407                  "\t%lu, %lu; type: %lu, format: %d, "
408                  "nitems: %lu, bytesafter %ld\n",
409                  blurb(), atom_name,
410                  (unsigned long) dataP, (dataP ? *dataP : 0), type,
411                  format, nitems, bytesafter);
412     }
413 }
414
415
416 static void
417 kill_xsetroot_data (Display *dpy, Window w, Bool verbose_p)
418 {
419   kill_xsetroot_data_1 (dpy, w, XA_XSETROOT_ID, "_XSETROOT_ID", verbose_p);
420   kill_xsetroot_data_1 (dpy, w, XA_ESETROOT_PMAP_ID, "ESETROOT_PMAP_ID",
421                         verbose_p);
422   kill_xsetroot_data_1 (dpy, w, XA_XROOTPMAP_ID, "_XROOTPMAP_ID", verbose_p);
423 }
424
425
426 static void
427 save_real_vroot (saver_screen_info *ssi)
428 {
429   saver_info *si = ssi->global;
430   Display *dpy = si->dpy;
431   Screen *screen = ssi->screen;
432   int i;
433   Window root = RootWindowOfScreen (screen);
434   Window root2, parent, *kids;
435   unsigned int nkids;
436   XErrorHandler old_handler;
437
438   /* It's possible that a window might be deleted between our call to
439      XQueryTree() and our call to XGetWindowProperty().  Don't die if
440      that happens (but just ignore that window, it's not the one we're
441      interested in anyway.)
442    */
443   XSync (dpy, False);
444   old_handler = XSetErrorHandler (BadWindow_ehandler);
445   XSync (dpy, False);
446
447   ssi->real_vroot = 0;
448   ssi->real_vroot_value = 0;
449   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
450     abort ();
451   if (root != root2)
452     abort ();
453   if (parent)
454     abort ();
455   for (i = 0; i < nkids; i++)
456     {
457       Atom type;
458       int format;
459       unsigned long nitems, bytesafter;
460       Window *vrootP = 0;
461
462       if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
463                               &type, &format, &nitems, &bytesafter,
464                               (unsigned char **) &vrootP)
465           != Success)
466         continue;
467       if (! vrootP)
468         continue;
469       if (ssi->real_vroot)
470         {
471           if (*vrootP == ssi->screensaver_window) abort ();
472           fprintf (stderr,
473             "%s: more than one virtual root window found (0x%x and 0x%x).\n",
474                    blurb(), (int) ssi->real_vroot, (int) kids [i]);
475           exit (1);
476         }
477       ssi->real_vroot = kids [i];
478       ssi->real_vroot_value = *vrootP;
479     }
480
481   XSync (dpy, False);
482   XSetErrorHandler (old_handler);
483   XSync (dpy, False);
484
485   if (ssi->real_vroot)
486     {
487       remove_vroot_property (si->dpy, ssi->real_vroot);
488       XSync (dpy, False);
489     }
490
491   XFree ((char *) kids);
492 }
493
494
495 static Bool
496 restore_real_vroot_1 (saver_screen_info *ssi)
497 {
498   saver_info *si = ssi->global;
499   saver_preferences *p = &si->prefs;
500   if (p->verbose_p && ssi->real_vroot)
501     fprintf (stderr,
502              "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
503              blurb(), (unsigned long) ssi->real_vroot);
504   remove_vroot_property (si->dpy, ssi->screensaver_window);
505   if (ssi->real_vroot)
506     {
507       store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value);
508       ssi->real_vroot = 0;
509       ssi->real_vroot_value = 0;
510       /* make sure the property change gets there before this process
511          terminates!  We might be doing this because we have intercepted
512          SIGTERM or something. */
513       XSync (si->dpy, False);
514       return True;
515     }
516   return False;
517 }
518
519 Bool
520 restore_real_vroot (saver_info *si)
521 {
522   int i;
523   Bool did_any = False;
524   for (i = 0; i < si->nscreens; i++)
525     {
526       saver_screen_info *ssi = &si->screens[i];
527       if (restore_real_vroot_1 (ssi))
528         did_any = True;
529     }
530   return did_any;
531 }
532
533 \f
534 /* Signal hackery to ensure that the vroot doesn't get left in an 
535    inconsistent state
536  */
537
538 const char *
539 signal_name(int signal)
540 {
541   switch (signal) {
542   case SIGHUP:    return "SIGHUP";
543   case SIGINT:    return "SIGINT";
544   case SIGQUIT:   return "SIGQUIT";
545   case SIGILL:    return "SIGILL";
546   case SIGTRAP:   return "SIGTRAP";
547 #ifdef SIGABRT
548   case SIGABRT:   return "SIGABRT";
549 #endif
550   case SIGFPE:    return "SIGFPE";
551   case SIGKILL:   return "SIGKILL";
552   case SIGBUS:    return "SIGBUS";
553   case SIGSEGV:   return "SIGSEGV";
554   case SIGPIPE:   return "SIGPIPE";
555   case SIGALRM:   return "SIGALRM";
556   case SIGTERM:   return "SIGTERM";
557 #ifdef SIGSTOP
558   case SIGSTOP:   return "SIGSTOP";
559 #endif
560 #ifdef SIGCONT
561   case SIGCONT:   return "SIGCONT";
562 #endif
563 #ifdef SIGUSR1
564   case SIGUSR1:   return "SIGUSR1";
565 #endif
566 #ifdef SIGUSR2
567   case SIGUSR2:   return "SIGUSR2";
568 #endif
569 #ifdef SIGEMT
570   case SIGEMT:    return "SIGEMT";
571 #endif
572 #ifdef SIGSYS
573   case SIGSYS:    return "SIGSYS";
574 #endif
575 #ifdef SIGCHLD
576   case SIGCHLD:   return "SIGCHLD";
577 #endif
578 #ifdef SIGPWR
579   case SIGPWR:    return "SIGPWR";
580 #endif
581 #ifdef SIGWINCH
582   case SIGWINCH:  return "SIGWINCH";
583 #endif
584 #ifdef SIGURG
585   case SIGURG:    return "SIGURG";
586 #endif
587 #ifdef SIGIO
588   case SIGIO:     return "SIGIO";
589 #endif
590 #ifdef SIGVTALRM
591   case SIGVTALRM: return "SIGVTALRM";
592 #endif
593 #ifdef SIGXCPU
594   case SIGXCPU:   return "SIGXCPU";
595 #endif
596 #ifdef SIGXFSZ
597   case SIGXFSZ:   return "SIGXFSZ";
598 #endif
599 #ifdef SIGDANGER
600   case SIGDANGER: return "SIGDANGER";
601 #endif
602   default:
603     {
604       static char buf[50];
605       sprintf(buf, "signal %d\n", signal);
606       return buf;
607     }
608   }
609 }
610
611
612
613 static RETSIGTYPE
614 restore_real_vroot_handler (int sig)
615 {
616   saver_info *si = global_si_kludge;    /* I hate C so much... */
617
618   signal (sig, SIG_DFL);
619   if (restore_real_vroot (si))
620     fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
621              blurb(), signal_name(sig));
622   kill (getpid (), sig);
623 }
624
625 static void
626 catch_signal (saver_info *si, int sig, RETSIGTYPE (*handler) (int))
627 {
628 # ifdef HAVE_SIGACTION
629
630   struct sigaction a;
631   a.sa_handler = handler;
632   sigemptyset (&a.sa_mask);
633   a.sa_flags = 0;
634
635   /* On Linux 2.4.9 (at least) we need to tell the kernel to not mask delivery
636      of this signal from inside its handler, or else when we execvp() the
637      process again, it starts up with SIGHUP blocked, meaning that killing
638      it with -HUP only works *once*.  You'd think that execvp() would reset
639      all the signal masks, but it doesn't.
640    */
641 #  if defined(SA_NOMASK)
642   a.sa_flags |= SA_NOMASK;
643 #  elif defined(SA_NODEFER)
644   a.sa_flags |= SA_NODEFER;
645 #  endif
646
647   if (sigaction (sig, &a, 0) < 0)
648 # else  /* !HAVE_SIGACTION */
649   if (((long) signal (sig, handler)) == -1L)
650 # endif /* !HAVE_SIGACTION */
651     {
652       char buf [255];
653       sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
654       perror (buf);
655       saver_exit (si, 1, 0);
656     }
657 }
658
659 static RETSIGTYPE saver_sighup_handler (int sig);
660
661 void
662 handle_signals (saver_info *si)
663 {
664   catch_signal (si, SIGHUP, saver_sighup_handler);
665
666   catch_signal (si, SIGINT,  restore_real_vroot_handler);
667   catch_signal (si, SIGQUIT, restore_real_vroot_handler);
668   catch_signal (si, SIGILL,  restore_real_vroot_handler);
669   catch_signal (si, SIGTRAP, restore_real_vroot_handler);
670 #ifdef SIGIOT
671   catch_signal (si, SIGIOT,  restore_real_vroot_handler);
672 #endif
673   catch_signal (si, SIGABRT, restore_real_vroot_handler);
674 #ifdef SIGEMT
675   catch_signal (si, SIGEMT,  restore_real_vroot_handler);
676 #endif
677   catch_signal (si, SIGFPE,  restore_real_vroot_handler);
678   catch_signal (si, SIGBUS,  restore_real_vroot_handler);
679   catch_signal (si, SIGSEGV, restore_real_vroot_handler);
680 #ifdef SIGSYS
681   catch_signal (si, SIGSYS,  restore_real_vroot_handler);
682 #endif
683   catch_signal (si, SIGTERM, restore_real_vroot_handler);
684 #ifdef SIGXCPU
685   catch_signal (si, SIGXCPU, restore_real_vroot_handler);
686 #endif
687 #ifdef SIGXFSZ
688   catch_signal (si, SIGXFSZ, restore_real_vroot_handler);
689 #endif
690 #ifdef SIGDANGER
691   catch_signal (si, SIGDANGER, restore_real_vroot_handler);
692 #endif
693 }
694
695
696 static RETSIGTYPE
697 saver_sighup_handler (int sig)
698 {
699   saver_info *si = global_si_kludge;    /* I hate C so much... */
700
701   /* Re-establish SIGHUP handler */
702   catch_signal (si, SIGHUP, saver_sighup_handler);
703
704   fprintf (stderr, "%s: %s received: restarting...\n",
705            blurb(), signal_name(sig));
706   unblank_screen (si);
707   kill_screenhack (si);
708   XSync (si->dpy, False);
709   restart_process (si);   /* Does not return */
710   abort ();
711 }
712
713
714
715 void
716 saver_exit (saver_info *si, int status, const char *dump_core_reason)
717 {
718   saver_preferences *p = &si->prefs;
719   static Bool exiting = False;
720   Bool bugp;
721   Bool vrs;
722
723   if (exiting)
724     exit(status);
725
726   exiting = True;
727   
728   vrs = restore_real_vroot (si);
729   emergency_kill_subproc (si);
730   shutdown_stderr (si);
731
732   if (p->verbose_p && vrs)
733     fprintf (real_stderr, "%s: old vroot restored.\n", blurb());
734
735   fflush(real_stdout);
736
737 #ifdef VMS      /* on VMS, 1 is the "normal" exit code instead of 0. */
738   if (status == 0) status = 1;
739   else if (status == 1) status = -1;
740 #endif
741
742   bugp = !!dump_core_reason;
743
744   if (si->prefs.debug_p && !dump_core_reason)
745     dump_core_reason = "because of -debug";
746
747   if (dump_core_reason)
748     {
749       /* Note that the Linux man page for setuid() says If uid is
750          different from the old effective uid, the process will be
751          forbidden from leaving core dumps.
752       */
753       char cwd[4096]; /* should really be PATH_MAX, but who cares. */
754       cwd[0] = 0;
755       fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
756               dump_core_reason);
757
758       if (bugp)
759         fprintf(real_stderr,
760                 "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
761                 "\t\t\tfor bug reporting information.\n\n",
762                 blurb());
763
764 # if defined(HAVE_GETCWD)
765       if (!getcwd (cwd, sizeof(cwd)))
766 # elif defined(HAVE_GETWD)
767       if (!getwd (cwd))
768 # endif
769         strcpy(cwd, "unknown.");
770
771       fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
772       describe_uids (si, real_stderr);
773
774       /* Do this to drop a core file, so that we can get a stack trace. */
775       abort();
776     }
777
778   exit (status);
779 }
780
781 \f
782 /* Managing the actual screensaver window */
783
784 Bool
785 window_exists_p (Display *dpy, Window window)
786 {
787   XErrorHandler old_handler;
788   XWindowAttributes xgwa;
789   xgwa.screen = 0;
790   old_handler = XSetErrorHandler (BadWindow_ehandler);
791   XGetWindowAttributes (dpy, window, &xgwa);
792   XSync (dpy, False);
793   XSetErrorHandler (old_handler);
794   return (xgwa.screen != 0);
795 }
796
797 static void
798 store_saver_id (saver_screen_info *ssi)
799 {
800   XClassHint class_hints;
801   saver_info *si = ssi->global;
802   unsigned long pid = (unsigned long) getpid ();
803   char buf[20];
804   struct passwd *p = getpwuid (getuid ());
805   const char *name, *host;
806   char *id;
807
808   /* First store the name and class on the window.
809    */
810   class_hints.res_name = progname;
811   class_hints.res_class = progclass;
812   XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
813   XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
814
815   /* Then store the xscreensaver version number.
816    */
817   XChangeProperty (si->dpy, ssi->screensaver_window,
818                    XA_SCREENSAVER_VERSION,
819                    XA_STRING, 8, PropModeReplace,
820                    (unsigned char *) si->version,
821                    strlen (si->version));
822
823   /* Now store the XSCREENSAVER_ID property, that says what user and host
824      xscreensaver is running as.
825    */
826
827   if (p && p->pw_name && *p->pw_name)
828     name = p->pw_name;
829   else if (p)
830     {
831       sprintf (buf, "%lu", (unsigned long) p->pw_uid);
832       name = buf;
833     }
834   else
835     name = "???";
836
837 # if defined(HAVE_UNAME)
838   {
839     struct utsname uts;
840     if (uname (&uts) < 0)
841       host = "???";
842     else
843       host = uts.nodename;
844   }
845 # elif defined(VMS)
846   host = getenv("SYS$NODE");
847 # else  /* !HAVE_UNAME && !VMS */
848   host = "???";
849 # endif /* !HAVE_UNAME && !VMS */
850
851   id = (char *) malloc (strlen(name) + strlen(host) + 50);
852   sprintf (id, "%lu (%s@%s)", pid, name, host);
853
854   XChangeProperty (si->dpy, ssi->screensaver_window,
855                    XA_SCREENSAVER_ID, XA_STRING,
856                    8, PropModeReplace,
857                    (unsigned char *) id, strlen (id));
858   free (id);
859 }
860
861
862 void
863 store_saver_status (saver_info *si)
864 {
865   CARD32 *status;
866   int size = si->nscreens + 2;
867   int i;
868
869   status = (CARD32 *) calloc (size, sizeof(CARD32));
870
871   status[0] = (CARD32) (si->screen_blanked_p
872                         ? (si->locked_p ? XA_LOCK : XA_BLANK)
873                         : 0);
874   status[1] = (CARD32) si->blank_time;
875
876   for (i = 0; i < si->nscreens; i++)
877     {
878       saver_screen_info *ssi = &si->screens[i];
879       status [2 + i] = ssi->current_hack + 1;
880     }
881
882   XChangeProperty (si->dpy,
883                    RootWindow (si->dpy, 0),  /* always screen #0 */
884                    XA_SCREENSAVER_STATUS,
885                    XA_INTEGER, 32, PropModeReplace,
886                    (unsigned char *) status, size);
887   free (status);
888 }
889
890
891
892 /* Returns the area of the screen which the xscreensaver window should cover.
893    Normally this is the whole screen, but if the X server's root window is
894    actually larger than the monitor's displayable area, then we want to
895    operate in the currently-visible portion of the desktop instead.
896  */
897 void
898 get_screen_viewport (saver_screen_info *ssi,
899                      int *x_ret, int *y_ret,
900                      int *w_ret, int *h_ret,
901                      int target_x, int target_y,
902                      Bool verbose_p)
903 {
904   int w = WidthOfScreen (ssi->screen);
905   int h = HeightOfScreen (ssi->screen);
906
907 #ifdef HAVE_XF86VMODE
908   saver_info *si = ssi->global;
909   int event, error;
910   int dot;
911   XF86VidModeModeLine ml;
912   int x, y;
913   Bool xinerama_p;
914   Bool placement_only_p = (target_x != -1 && target_y != -1);
915
916 #ifdef HAVE_XINERAMA
917   xinerama_p = (XineramaQueryExtension (si->dpy, &event, &error) &&
918                 XineramaIsActive (si->dpy));
919 #else  /* !HAVE_XINERAMA */
920   /* Even if we don't have the client-side Xinerama lib, check to see if
921      the server supports Xinerama, so that we know to ignore the VidMode
922      extension -- otherwise a server crash could result.  Yay. */
923   xinerama_p = XQueryExtension (si->dpy, "XINERAMA", &error, &event, &error);
924   
925 #endif /* !HAVE_XINERAMA */
926
927 #ifdef HAVE_XINERAMA
928   if (xinerama_p && placement_only_p)
929     {
930       int nscreens = 0;
931       XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &nscreens);
932       if (xsi)
933         {
934           /* Find the screen that contains the mouse. */
935           int which = -1;
936           int i;
937           for (i = 0; i < nscreens; i++)
938             {
939               if (target_x >= xsi[i].x_org &&
940                   target_y >= xsi[i].y_org &&
941                   target_x < xsi[i].x_org + xsi[i].width &&
942                   target_y < xsi[i].y_org + xsi[i].height)
943                 which = i;
944               if (verbose_p)
945                 {
946                   fprintf (stderr, "%s: %d: xinerama vp: %dx%d+%d+%d",
947                            blurb(), i,
948                            xsi[which].width, xsi[which].height,
949                            xsi[i].x_org, xsi[i].y_org);
950                   if (which == i)
951                     fprintf (stderr, "; mouse at %d,%d",
952                              target_x, target_y);
953                   fprintf (stderr, ".\n");
954                 }
955             }
956           if (which == -1) which = 0;  /* didn't find it?  Use the first. */
957           *x_ret = xsi[which].x_org;
958           *y_ret = xsi[which].y_org;
959           *w_ret = xsi[which].width;
960           *h_ret = xsi[which].height;
961           XFree (xsi);
962           return;
963         }
964     }
965 #endif /* HAVE_XINERAMA */
966
967   if (!xinerama_p &&  /* Xinerama + VidMode = broken. */
968       XF86VidModeQueryExtension (si->dpy, &event, &error) &&
969       XF86VidModeGetModeLine (si->dpy, ssi->number, &dot, &ml) &&
970       XF86VidModeGetViewPort (si->dpy, ssi->number, &x, &y))
971     {
972       char msg[512];
973       *x_ret = x;
974       *y_ret = y;
975       *w_ret = ml.hdisplay;
976       *h_ret = ml.vdisplay;
977
978       if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
979         /* There is no viewport -- the screen does not scroll. */
980         return;
981
982
983       /* Apparently some versions of XFree86 return nonsense here!
984          I've had reports of 1024x768 viewports at -1936862040, -1953705044.
985          So, sanity-check the values and give up if they are out of range.
986        */
987       if (*x_ret <  0 || *x_ret >= w ||
988           *y_ret <  0 || *y_ret >= h ||
989           *w_ret <= 0 || *w_ret >  w ||
990           *h_ret <= 0 || *h_ret >  h)
991         {
992           static int warned_once = 0;
993           if (!warned_once)
994             {
995               fprintf (stderr, "\n"
996                   "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
997                   "%s: The XVidMode server extension is returning nonsense.\n"
998                   "%s: Please report this bug to your X server vendor.\n\n",
999                        blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
1000                        blurb(), blurb());
1001               warned_once = 1;
1002             }
1003           *x_ret = 0;
1004           *y_ret = 0;
1005           *w_ret = w;
1006           *h_ret = h;
1007           return;
1008         }
1009
1010       sprintf (msg, "%s: %d: vp is %dx%d+%d+%d",
1011                blurb(), ssi->number,
1012                *w_ret, *h_ret, *x_ret, *y_ret);
1013
1014
1015       /* Apparently, though the server stores the X position in increments of
1016          1 pixel, it will only make changes to the *display* in some other
1017          increment.  With XF86_SVGA on a Thinkpad, the display only updates
1018          in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
1019          pixels in 16-bit mode.  I don't know what it does in 24- and 32-bit
1020          mode, because I don't have enough video memory to find out.
1021
1022          I consider it a bug that XF86VidModeGetViewPort() is telling me the
1023          server's *target* scroll position rather than the server's *actual*
1024          scroll position.  David Dawes agrees, and says they may fix this in
1025          XFree86 4.0, but it's notrivial.
1026
1027          He also confirms that this behavior is server-dependent, so the
1028          actual scroll position cannot be reliably determined by the client.
1029          So... that means the only solution is to provide a ``sandbox''
1030          around the blackout window -- we make the window be up to N pixels
1031          larger than the viewport on both the left and right sides.  That
1032          means some part of the outer edges of each hack might not be
1033          visible, but screw it.
1034
1035          I'm going to guess that 16 pixels is enough, and that the Y dimension
1036          doesn't have this problem.
1037
1038          The drawback of doing this, of course, is that some of the screenhacks
1039          will still look pretty stupid -- for example, "slidescreen" will cut
1040          off the left and right edges of the grid, etc.
1041       */
1042 # define FUDGE 16
1043       if (x > 0 && x < w - ml.hdisplay)  /* not at left edge or right edge */
1044         {
1045           /* Round X position down to next lower multiple of FUDGE.
1046              Increase width by 2*FUDGE in case some server rounds up.
1047            */
1048           *x_ret = ((x - 1) / FUDGE) * FUDGE;
1049           *w_ret += (FUDGE * 2);
1050         }
1051 # undef FUDGE
1052
1053       if (*x_ret != x ||
1054           *y_ret != y ||
1055           *w_ret != ml.hdisplay ||
1056           *h_ret != ml.vdisplay)
1057         sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
1058                  *w_ret, *h_ret, *x_ret, *y_ret);
1059
1060       if (verbose_p)
1061         fprintf (stderr, "%s.\n", msg);
1062
1063       return;
1064     }
1065
1066 #endif /* HAVE_XF86VMODE */
1067
1068   *x_ret = 0;
1069   *y_ret = 0;
1070   *w_ret = w;
1071   *h_ret = h;
1072 }
1073
1074
1075 static Bool error_handler_hit_p = False;
1076
1077 static int
1078 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1079 {
1080   error_handler_hit_p = True;
1081   return 0;
1082 }
1083
1084
1085 /* Returns True if successful, False if an X error occurred.
1086    We need this because other programs might have done things to
1087    our window that will cause XChangeWindowAttributes() to fail:
1088    if that happens, we give up, destroy the window, and re-create
1089    it.
1090  */
1091 static Bool
1092 safe_XChangeWindowAttributes (Display *dpy, Window window,
1093                               unsigned long mask,
1094                               XSetWindowAttributes *attrs)
1095 {
1096   XErrorHandler old_handler;
1097   XSync (dpy, False);
1098   error_handler_hit_p = False;
1099   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1100
1101   XChangeWindowAttributes (dpy, window, mask, attrs);
1102
1103   XSync (dpy, False);
1104   XSetErrorHandler (old_handler);
1105   XSync (dpy, False);
1106
1107   return (!error_handler_hit_p);
1108 }
1109
1110
1111 /* This might not be necessary, but just in case. */
1112 static Bool
1113 safe_XConfigureWindow (Display *dpy, Window window,
1114                        unsigned long mask, XWindowChanges *changes)
1115 {
1116   XErrorHandler old_handler;
1117   XSync (dpy, False);
1118   error_handler_hit_p = False;
1119   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1120
1121   XConfigureWindow (dpy, window, mask, changes);
1122
1123   XSync (dpy, False);
1124   XSetErrorHandler (old_handler);
1125   XSync (dpy, False);
1126
1127   return (!error_handler_hit_p);
1128 }
1129
1130 /* This might not be necessary, but just in case. */
1131 static Bool
1132 safe_XDestroyWindow (Display *dpy, Window window)
1133 {
1134   XErrorHandler old_handler;
1135   XSync (dpy, False);
1136   error_handler_hit_p = False;
1137   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1138
1139   XDestroyWindow (dpy, window);
1140
1141   XSync (dpy, False);
1142   XSetErrorHandler (old_handler);
1143   XSync (dpy, False);
1144
1145   return (!error_handler_hit_p);
1146 }
1147
1148
1149 static Bool
1150 safe_XKillClient (Display *dpy, XID id)
1151 {
1152   XErrorHandler old_handler;
1153   XSync (dpy, False);
1154   error_handler_hit_p = False;
1155   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1156
1157   XKillClient (dpy, id);
1158
1159   XSync (dpy, False);
1160   XSetErrorHandler (old_handler);
1161   XSync (dpy, False);
1162
1163   return (!error_handler_hit_p);
1164 }
1165
1166
1167 static void
1168 initialize_screensaver_window_1 (saver_screen_info *ssi)
1169 {
1170   saver_info *si = ssi->global;
1171   saver_preferences *p = &si->prefs;
1172   Bool install_cmap_p = ssi->install_cmap_p;   /* not p->install_cmap_p */
1173
1174   /* This resets the screensaver window as fully as possible, since there's
1175      no way of knowing what some random client may have done to us in the
1176      meantime.  We could just destroy and recreate the window, but that has
1177      its own set of problems...
1178    */
1179   XColor black;
1180   XSetWindowAttributes attrs;
1181   unsigned long attrmask;
1182   int x, y, width, height;
1183   static Bool printed_visual_info = False;  /* only print the message once. */
1184   Window horked_window = 0;
1185
1186   get_screen_viewport (ssi, &x, &y, &width, &height, -1, -1,
1187                        (p->verbose_p && !si->screen_blanked_p));
1188
1189   black.red = black.green = black.blue = 0;
1190
1191   if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
1192     ssi->cmap = 0;
1193
1194   if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
1195     /* It's not the default visual, so we have no choice but to install. */
1196     install_cmap_p = True;
1197
1198   if (install_cmap_p)
1199     {
1200       if (! ssi->cmap)
1201         {
1202           ssi->cmap = XCreateColormap (si->dpy,
1203                                        RootWindowOfScreen (ssi->screen),
1204                                       ssi->current_visual, AllocNone);
1205           if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
1206           ssi->black_pixel = black.pixel;
1207         }
1208     }
1209   else
1210     {
1211       Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
1212       if (ssi->cmap)
1213         {
1214           XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
1215           if (ssi->cmap != ssi->demo_cmap &&
1216               ssi->cmap != def_cmap)
1217             XFreeColormap (si->dpy, ssi->cmap);
1218         }
1219       ssi->cmap = def_cmap;
1220       ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
1221     }
1222
1223   attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
1224               CWBackPixel | CWBackingPixel | CWBorderPixel);
1225   attrs.override_redirect = True;
1226
1227   /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
1228      actually be reading these events during normal operation; but we still
1229      need to see Button events for demo-mode to work properly.
1230    */
1231   attrs.event_mask = (KeyPressMask | KeyReleaseMask |
1232                       ButtonPressMask | ButtonReleaseMask |
1233                       PointerMotionMask);
1234
1235   attrs.backing_store = NotUseful;
1236   attrs.colormap = ssi->cmap;
1237   attrs.background_pixel = ssi->black_pixel;
1238   attrs.backing_pixel = ssi->black_pixel;
1239   attrs.border_pixel = ssi->black_pixel;
1240
1241   if (p->debug_p) width = width / 2;
1242
1243   if (!p->verbose_p || printed_visual_info)
1244     ;
1245   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
1246     {
1247       fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number);
1248       describe_visual (stderr, ssi->screen, ssi->current_visual,
1249                        install_cmap_p);
1250     }
1251   else
1252     {
1253       fprintf (stderr, "%s: using visual:   ", blurb());
1254       describe_visual (stderr, ssi->screen, ssi->current_visual,
1255                        install_cmap_p);
1256       fprintf (stderr, "%s: default visual: ", blurb());
1257       describe_visual (stderr, ssi->screen,
1258                        DefaultVisualOfScreen (ssi->screen),
1259                        ssi->install_cmap_p);
1260     }
1261   printed_visual_info = True;
1262
1263 #ifdef HAVE_MIT_SAVER_EXTENSION
1264   if (si->using_mit_saver_extension)
1265     {
1266       XScreenSaverInfo *info;
1267       Window root = RootWindowOfScreen (ssi->screen);
1268
1269 #if 0
1270       /* This call sets the server screensaver timeouts to what we think
1271          they should be (based on the resources and args xscreensaver was
1272          started with.)  It's important that we do this to sync back up
1273          with the server - if we have turned on prematurely, as by an
1274          ACTIVATE ClientMessage, then the server may decide to activate
1275          the screensaver while it's already active.  That's ok for us,
1276          since we would know to ignore that ScreenSaverActivate event,
1277          but a side effect of this would be that the server would map its
1278          saver window (which we then hide again right away) meaning that
1279          the bits currently on the screen get blown away.  Ugly. */
1280
1281       /* #### Ok, that doesn't work - when we tell the server that the
1282          screensaver is "off" it sends us a Deactivate event, which is
1283          sensible... but causes the saver to never come on.  Hmm. */
1284       disable_builtin_screensaver (si, True);
1285 #endif /* 0 */
1286
1287 #if 0
1288       /* #### The MIT-SCREEN-SAVER extension gives us access to the
1289          window that the server itself uses for saving the screen.
1290          However, using this window in any way, in particular, calling
1291          XScreenSaverSetAttributes() as below, tends to make the X server
1292          crash.  So fuck it, let's try and get along without using it...
1293
1294          It's also inconvenient to use this window because it doesn't
1295          always exist (though the ID is constant.)  So to use this
1296          window, we'd have to reimplement the ACTIVATE ClientMessage to
1297          tell the *server* to tell *us* to turn on, to cause the window
1298          to get created at the right time.  Gag.  */
1299       XScreenSaverSetAttributes (si->dpy, root,
1300                                  0, 0, width, height, 0,
1301                                  current_depth, InputOutput, visual,
1302                                  attrmask, &attrs);
1303       XSync (si->dpy, False);
1304 #endif /* 0 */
1305
1306       info = XScreenSaverAllocInfo ();
1307       XScreenSaverQueryInfo (si->dpy, root, info);
1308       ssi->server_mit_saver_window = info->window;
1309       if (! ssi->server_mit_saver_window) abort ();
1310       XFree (info);
1311     }
1312 #endif /* HAVE_MIT_SAVER_EXTENSION */
1313
1314   if (ssi->screensaver_window)
1315     {
1316       XWindowChanges changes;
1317       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1318       changes.x = x;
1319       changes.y = y;
1320       changes.width = width;
1321       changes.height = height;
1322       changes.border_width = 0;
1323
1324       if (! (safe_XConfigureWindow (si->dpy, ssi->screensaver_window,
1325                                   changesmask, &changes) &&
1326              safe_XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
1327                                            attrmask, &attrs)))
1328         {
1329           horked_window = ssi->screensaver_window;
1330           ssi->screensaver_window = 0;
1331         }
1332     }
1333
1334   if (!ssi->screensaver_window)
1335     {
1336       ssi->screensaver_window =
1337         XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
1338                        x, y, width, height,
1339                        0, ssi->current_depth, InputOutput,
1340                        ssi->current_visual, attrmask, &attrs);
1341
1342       reset_stderr (ssi);
1343
1344       if (horked_window)
1345         {
1346           fprintf (stderr,
1347             "%s: someone horked our saver window (0x%lx)!  Recreating it...\n",
1348                    blurb(), (unsigned long) horked_window);
1349           maybe_transfer_grabs (ssi, horked_window, ssi->screensaver_window,
1350                                 ssi->number);
1351           safe_XDestroyWindow (si->dpy, horked_window);
1352           horked_window = 0;
1353         }
1354
1355       if (p->verbose_p)
1356         fprintf (stderr, "%s: %d: saver window is 0x%lx.\n",
1357                  blurb(), ssi->number,
1358                  (unsigned long) ssi->screensaver_window);
1359     }
1360
1361   store_saver_id (ssi);       /* store window name and IDs */
1362
1363   if (!ssi->cursor)
1364     {
1365       Pixmap bit;
1366       bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
1367                                          "\000", 1, 1,
1368                                          BlackPixelOfScreen (ssi->screen),
1369                                          BlackPixelOfScreen (ssi->screen),
1370                                          1);
1371       ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
1372                                          0, 0);
1373       XFreePixmap (si->dpy, bit);
1374     }
1375
1376   XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
1377
1378   if (si->demoing_p)
1379     XUndefineCursor (si->dpy, ssi->screensaver_window);
1380   else
1381     XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1382 }
1383
1384 void
1385 initialize_screensaver_window (saver_info *si)
1386 {
1387   int i;
1388   for (i = 0; i < si->nscreens; i++)
1389     initialize_screensaver_window_1 (&si->screens[i]);
1390 }
1391
1392
1393 void 
1394 raise_window (saver_info *si,
1395               Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
1396 {
1397   saver_preferences *p = &si->prefs;
1398   int i;
1399
1400   if (si->demoing_p)
1401     inhibit_fade = True;
1402
1403   if (si->emergency_lock_p)
1404     inhibit_fade = True;
1405
1406   if (!dont_clear)
1407     initialize_screensaver_window (si);
1408
1409   reset_watchdog_timer (si, True);
1410
1411   if (p->fade_p && si->fading_possible_p && !inhibit_fade)
1412     {
1413       Window *current_windows = (Window *)
1414         calloc(sizeof(Window), si->nscreens);
1415       Colormap *current_maps = (Colormap *)
1416         calloc(sizeof(Colormap), si->nscreens);
1417
1418       for (i = 0; i < si->nscreens; i++)
1419         {
1420           saver_screen_info *ssi = &si->screens[i];
1421           current_windows[i] = ssi->screensaver_window;
1422           current_maps[i] = (between_hacks_p
1423                              ? ssi->cmap
1424                              : DefaultColormapOfScreen (ssi->screen));
1425           /* Ensure that the default background of the window is really black,
1426              not a pixmap or something.  (This does not clear the window.) */
1427           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1428                                 ssi->black_pixel);
1429         }
1430
1431       if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1432
1433       XGrabServer (si->dpy);                    /* ############ DANGER! */
1434
1435       /* Clear the stderr layer on each screen.
1436        */
1437       if (!dont_clear)
1438         for (i = 0; i < si->nscreens; i++)
1439           {
1440             saver_screen_info *ssi = &si->screens[i];
1441             if (ssi->stderr_overlay_window)
1442               /* Do this before the fade, since the stderr cmap won't fade
1443                  even if we uninstall it (beats me...) */
1444               clear_stderr (ssi);
1445           }
1446
1447       /* Note!  The server is grabbed, and this will take several seconds
1448          to complete! */
1449       fade_screens (si->dpy, current_maps, current_windows,
1450                     p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1451
1452       free(current_maps);
1453       free(current_windows);
1454       current_maps = 0;
1455       current_windows = 0;
1456
1457       if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1458
1459 #ifdef HAVE_MIT_SAVER_EXTENSION
1460       for (i = 0; i < si->nscreens; i++)
1461         {
1462           saver_screen_info *ssi = &si->screens[i];
1463           if (ssi->server_mit_saver_window &&
1464               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1465             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1466         }
1467 #endif /* HAVE_MIT_SAVER_EXTENSION */
1468
1469       XUngrabServer (si->dpy);
1470       XSync (si->dpy, False);                   /* ###### (danger over) */
1471     }
1472   else
1473     {
1474       for (i = 0; i < si->nscreens; i++)
1475         {
1476           saver_screen_info *ssi = &si->screens[i];
1477           if (!dont_clear)
1478             XClearWindow (si->dpy, ssi->screensaver_window);
1479           if (!dont_clear || ssi->stderr_overlay_window)
1480             clear_stderr (ssi);
1481           XMapRaised (si->dpy, ssi->screensaver_window);
1482 #ifdef HAVE_MIT_SAVER_EXTENSION
1483           if (ssi->server_mit_saver_window &&
1484               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1485             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1486 #endif /* HAVE_MIT_SAVER_EXTENSION */
1487         }
1488     }
1489
1490   for (i = 0; i < si->nscreens; i++)
1491     {
1492       saver_screen_info *ssi = &si->screens[i];
1493       if (ssi->cmap)
1494         XInstallColormap (si->dpy, ssi->cmap);
1495     }
1496 }
1497
1498
1499 int
1500 mouse_screen (saver_info *si)
1501 {
1502   saver_preferences *p = &si->prefs;
1503
1504   if (si->nscreens == 1)
1505     return 0;
1506   else
1507     {
1508       int i;
1509       for (i = 0; i < si->nscreens; i++)
1510         {
1511           saver_screen_info *ssi = &si->screens[i];
1512           Window pointer_root, pointer_child;
1513           int root_x, root_y, win_x, win_y;
1514           unsigned int mask;
1515           if (XQueryPointer (si->dpy,
1516                              RootWindowOfScreen (ssi->screen),
1517                              &pointer_root, &pointer_child,
1518                              &root_x, &root_y, &win_x, &win_y, &mask))
1519             {
1520               if (p->verbose_p)
1521                 fprintf (stderr, "%s: mouse is on screen %d\n",
1522                          blurb(), i, si->nscreens);
1523               return i;
1524             }
1525         }
1526
1527       /* couldn't figure out where the mouse is?  Oh well. */
1528       return 0;
1529     }
1530 }
1531
1532
1533 Bool
1534 blank_screen (saver_info *si)
1535 {
1536   int i;
1537   Bool ok;
1538   Window w;
1539   int mscreen;
1540
1541   /* Note: we do our grabs on the root window, not on the screensaver window.
1542      If we grabbed on the saver window, then the demo mode and lock dialog
1543      boxes wouldn't get any events.
1544
1545      By "the root window", we mean "the root window that contains the mouse."
1546      We use to always grab the mouse on screen 0, but that has the effect of
1547      moving the mouse to screen 0 from whichever screen it was on, on
1548      multi-head systems.
1549    */
1550   mscreen = mouse_screen (si);
1551   w = RootWindowOfScreen(si->screens[mscreen].screen);
1552   ok = grab_keyboard_and_mouse (si, w,
1553                                 (si->demoing_p ? 0 : si->screens[0].cursor),
1554                                 mscreen);
1555
1556
1557   if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1558     /* If we're using a server extension, then failure to get a grab is
1559        not a big deal -- even without the grab, we will still be able
1560        to un-blank when there is user activity, since the server will
1561        tell us. */
1562     ok = True;
1563
1564   if (!ok)
1565     return False;
1566
1567   for (i = 0; i < si->nscreens; i++)
1568     {
1569       saver_screen_info *ssi = &si->screens[i];
1570
1571       save_real_vroot (ssi);
1572       store_vroot_property (si->dpy,
1573                             ssi->screensaver_window,
1574                             ssi->screensaver_window);
1575
1576 #ifdef HAVE_XF86VMODE
1577       {
1578         int ev, er;
1579         if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
1580             !XF86VidModeGetViewPort (si->dpy, i,
1581                                      &ssi->blank_vp_x,
1582                                      &ssi->blank_vp_y))
1583           ssi->blank_vp_x = ssi->blank_vp_y = -1;
1584       }
1585 #endif /* HAVE_XF86VMODE */
1586     }
1587
1588   raise_window (si, False, False, False);
1589
1590   si->screen_blanked_p = True;
1591   si->blank_time = time ((time_t) 0);
1592   si->last_wall_clock_time = 0;
1593
1594   store_saver_status (si);  /* store blank time */
1595
1596   return True;
1597 }
1598
1599
1600 void
1601 unblank_screen (saver_info *si)
1602 {
1603   saver_preferences *p = &si->prefs;
1604   Bool unfade_p = (si->fading_possible_p && p->unfade_p);
1605   int i;
1606
1607   monitor_power_on (si);
1608   reset_watchdog_timer (si, False);
1609
1610   if (si->demoing_p)
1611     unfade_p = False;
1612
1613   if (unfade_p)
1614     {
1615       Window *current_windows = (Window *)
1616         calloc(sizeof(Window), si->nscreens);
1617
1618       for (i = 0; i < si->nscreens; i++)
1619         {
1620           saver_screen_info *ssi = &si->screens[i];
1621           current_windows[i] = ssi->screensaver_window;
1622           /* Ensure that the default background of the window is really black,
1623              not a pixmap or something.  (This does not clear the window.) */
1624           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1625                                 ssi->black_pixel);
1626         }
1627
1628       if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1629
1630
1631       XSync (si->dpy, False);
1632       XGrabServer (si->dpy);                    /* ############ DANGER! */
1633       XSync (si->dpy, False);
1634
1635       /* Clear the stderr layer on each screen.
1636        */
1637       for (i = 0; i < si->nscreens; i++)
1638         {
1639           saver_screen_info *ssi = &si->screens[i];
1640           clear_stderr (ssi);
1641         }
1642
1643       XUngrabServer (si->dpy);
1644       XSync (si->dpy, False);                   /* ###### (danger over) */
1645
1646
1647       fade_screens (si->dpy, 0, current_windows,
1648                     p->fade_seconds/1000, p->fade_ticks,
1649                     False, False);
1650
1651       free(current_windows);
1652       current_windows = 0;
1653
1654       if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1655     }
1656   else
1657     {
1658       for (i = 0; i < si->nscreens; i++)
1659         {
1660           saver_screen_info *ssi = &si->screens[i];
1661           if (ssi->cmap)
1662             {
1663               Colormap c = DefaultColormapOfScreen (ssi->screen);
1664               /* avoid technicolor */
1665               XClearWindow (si->dpy, ssi->screensaver_window);
1666               if (c) XInstallColormap (si->dpy, c);
1667             }
1668           XUnmapWindow (si->dpy, ssi->screensaver_window);
1669         }
1670     }
1671
1672
1673   /* If the focus window does has a non-default colormap, then install
1674      that colormap as well.  (On SGIs, this will cause both the root map
1675      and the focus map to be installed simultaniously.  It'd be nice to
1676      pick up the other colormaps that had been installed, too; perhaps
1677      XListInstalledColormaps could be used for that?)
1678    */
1679   {
1680     Window focus = 0;
1681     int revert_to;
1682     XGetInputFocus (si->dpy, &focus, &revert_to);
1683     if (focus && focus != PointerRoot && focus != None)
1684       {
1685         XWindowAttributes xgwa;
1686         xgwa.colormap = 0;
1687         XGetWindowAttributes (si->dpy, focus, &xgwa);
1688         if (xgwa.colormap &&
1689             xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1690           XInstallColormap (si->dpy, xgwa.colormap);
1691       }
1692   }
1693
1694
1695   for (i = 0; i < si->nscreens; i++)
1696     {
1697       saver_screen_info *ssi = &si->screens[i];
1698       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1699     }
1700
1701   store_saver_status (si);  /* store unblank time */
1702   ungrab_keyboard_and_mouse (si);
1703   restore_real_vroot (si);
1704
1705   /* Unmap the windows a second time, dammit -- just to avoid a race
1706      with the screen-grabbing hacks.  (I'm not sure if this is really
1707      necessary; I'm stabbing in the dark now.)
1708   */
1709   for (i = 0; i < si->nscreens; i++)
1710     XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1711
1712   si->screen_blanked_p = False;
1713   si->blank_time = time ((time_t) 0);
1714   si->last_wall_clock_time = 0;
1715
1716   store_saver_status (si);  /* store unblank time */
1717 }
1718
1719
1720 /* Transfer any grabs from the old window to the new.
1721    Actually I think none of this is necessary, since we always
1722    hold our grabs on the root window, but I wrote this before
1723    re-discovering that...
1724  */
1725 static void
1726 maybe_transfer_grabs (saver_screen_info *ssi,
1727                       Window old_w, Window new_w,
1728                       int new_screen_no)
1729 {
1730   saver_info *si = ssi->global;
1731
1732   /* If the old window held our mouse grab, transfer the grab to the new
1733      window.  (Grab the server while so doing, to avoid a race condition.)
1734    */
1735   if (old_w == si->mouse_grab_window)
1736     {
1737       XGrabServer (si->dpy);            /* ############ DANGER! */
1738       ungrab_mouse (si);
1739       grab_mouse (si, ssi->screensaver_window,
1740                   (si->demoing_p ? 0 : ssi->cursor),
1741                   new_screen_no);
1742       XUngrabServer (si->dpy);
1743       XSync (si->dpy, False);           /* ###### (danger over) */
1744     }
1745
1746   /* If the old window held our keyboard grab, transfer the grab to the new
1747      window.  (Grab the server while so doing, to avoid a race condition.)
1748    */
1749   if (old_w == si->keyboard_grab_window)
1750     {
1751       XGrabServer (si->dpy);            /* ############ DANGER! */
1752       ungrab_kbd(si);
1753       grab_kbd(si, ssi->screensaver_window, ssi->number);
1754       XUngrabServer (si->dpy);
1755       XSync (si->dpy, False);           /* ###### (danger over) */
1756     }
1757 }
1758
1759
1760
1761 Bool
1762 select_visual (saver_screen_info *ssi, const char *visual_name)
1763 {
1764   saver_info *si = ssi->global;
1765   saver_preferences *p = &si->prefs;
1766   Bool install_cmap_p = p->install_cmap_p;
1767   Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1768   Visual *new_v = 0;
1769   Bool got_it;
1770
1771   if (visual_name && *visual_name)
1772     {
1773       if (!strcmp(visual_name, "default-i") ||
1774           !strcmp(visual_name, "Default-i") ||
1775           !strcmp(visual_name, "Default-I")
1776           )
1777         {
1778           visual_name = "default";
1779           install_cmap_p = True;
1780         }
1781       else if (!strcmp(visual_name, "default-n") ||
1782                !strcmp(visual_name, "Default-n") ||
1783                !strcmp(visual_name, "Default-N"))
1784         {
1785           visual_name = "default";
1786           install_cmap_p = False;
1787         }
1788       else if (!strcmp(visual_name, "gl") ||
1789                !strcmp(visual_name, "Gl") ||
1790                !strcmp(visual_name, "GL"))
1791         {
1792           new_v = ssi->best_gl_visual;
1793           if (!new_v && p->verbose_p)
1794             fprintf (stderr, "%s: no GL visuals.\n", progname);
1795         }
1796
1797       if (!new_v)
1798         new_v = get_visual (ssi->screen, visual_name, True, False);
1799     }
1800   else
1801     {
1802       new_v = ssi->default_visual;
1803     }
1804
1805   got_it = !!new_v;
1806
1807   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1808     /* It's not the default visual, so we have no choice but to install. */
1809     install_cmap_p = True;
1810
1811   ssi->install_cmap_p = install_cmap_p;
1812
1813   if (new_v &&
1814       ((ssi->current_visual != new_v) ||
1815        (install_cmap_p != was_installed_p)))
1816     {
1817       Colormap old_c = ssi->cmap;
1818       Window old_w = ssi->screensaver_window;
1819
1820       if (p->verbose_p)
1821         {
1822           fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number);
1823           describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
1824 #if 0
1825           fprintf (stderr, "%s:                  from ", blurb());
1826           describe_visual (stderr, ssi->screen, ssi->current_visual,
1827                            was_installed_p);
1828 #endif
1829         }
1830
1831       reset_stderr (ssi);
1832       ssi->current_visual = new_v;
1833       ssi->current_depth = visual_depth(ssi->screen, new_v);
1834       ssi->cmap = 0;
1835       ssi->screensaver_window = 0;
1836
1837       initialize_screensaver_window_1 (ssi);
1838
1839       /* stderr_overlay_window is a child of screensaver_window, so we need
1840          to destroy that as well (actually, we just need to invalidate and
1841          drop our pointers to it, but this will destroy it, which is ok so
1842          long as it happens before old_w itself is destroyed.) */
1843       reset_stderr (ssi);
1844
1845       raise_window (si, True, True, False);
1846       store_vroot_property (si->dpy,
1847                             ssi->screensaver_window, ssi->screensaver_window);
1848
1849       /* Transfer any grabs from the old window to the new. */
1850       maybe_transfer_grabs (ssi, old_w, ssi->screensaver_window, ssi->number);
1851
1852       /* Now we can destroy the old window without horking our grabs. */
1853       XDestroyWindow (si->dpy, old_w);
1854
1855       if (p->verbose_p)
1856         fprintf (stderr, "%s: %d: destroyed old saver window 0x%lx.\n",
1857                  blurb(), ssi->number, (unsigned long) old_w);
1858
1859       if (old_c &&
1860           old_c != DefaultColormapOfScreen (ssi->screen) &&
1861           old_c != ssi->demo_cmap)
1862         XFreeColormap (si->dpy, old_c);
1863     }
1864
1865   return got_it;
1866 }