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