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