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