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