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