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