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