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