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