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