http://ftp.x.org/contrib/applications/xscreensaver-3.03.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 || 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     install_cmap_p = True;
793
794   if (install_cmap_p)
795     {
796       if (! ssi->cmap)
797         {
798           ssi->cmap = XCreateColormap (si->dpy,
799                                        RootWindowOfScreen (ssi->screen),
800                                       ssi->current_visual, AllocNone);
801           if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
802           ssi->black_pixel = black.pixel;
803         }
804     }
805   else
806     {
807       Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
808       if (ssi->cmap)
809         {
810           XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
811           if (ssi->cmap != ssi->demo_cmap &&
812               ssi->cmap != def_cmap)
813             XFreeColormap (si->dpy, ssi->cmap);
814         }
815       ssi->cmap = def_cmap;
816       ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
817     }
818
819   attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
820               CWBackPixel | CWBackingPixel | CWBorderPixel);
821   attrs.override_redirect = True;
822
823   /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
824      actually be reading these events during normal operation; but we still
825      need to see Button events for demo-mode to work properly.
826    */
827   attrs.event_mask = (KeyPressMask | KeyReleaseMask |
828                       ButtonPressMask | ButtonReleaseMask |
829                       PointerMotionMask);
830
831   attrs.backing_store = NotUseful;
832   attrs.colormap = ssi->cmap;
833   attrs.background_pixel = ssi->black_pixel;
834   attrs.backing_pixel = ssi->black_pixel;
835   attrs.border_pixel = ssi->black_pixel;
836
837   if (p->debug_p) width = width / 2;
838
839   if (!p->verbose_p || printed_visual_info)
840     ;
841   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
842     {
843       fprintf (stderr, "%s: using default visual ", blurb());
844       describe_visual (stderr, ssi->screen, ssi->current_visual,
845                        install_cmap_p);
846     }
847   else
848     {
849       fprintf (stderr, "%s: using visual:   ", blurb());
850       describe_visual (stderr, ssi->screen, ssi->current_visual,
851                        install_cmap_p);
852       fprintf (stderr, "%s: default visual: ", blurb());
853       describe_visual (stderr, ssi->screen,
854                        DefaultVisualOfScreen (ssi->screen),
855                        ssi->install_cmap_p);
856     }
857   printed_visual_info = True;
858
859 #ifdef HAVE_MIT_SAVER_EXTENSION
860   if (si->using_mit_saver_extension)
861     {
862       XScreenSaverInfo *info;
863       Window root = RootWindowOfScreen (ssi->screen);
864
865 #if 0
866       /* This call sets the server screensaver timeouts to what we think
867          they should be (based on the resources and args xscreensaver was
868          started with.)  It's important that we do this to sync back up
869          with the server - if we have turned on prematurely, as by an
870          ACTIVATE ClientMessage, then the server may decide to activate
871          the screensaver while it's already active.  That's ok for us,
872          since we would know to ignore that ScreenSaverActivate event,
873          but a side effect of this would be that the server would map its
874          saver window (which we then hide again right away) meaning that
875          the bits currently on the screen get blown away.  Ugly. */
876
877       /* #### Ok, that doesn't work - when we tell the server that the
878          screensaver is "off" it sends us a Deactivate event, which is
879          sensible... but causes the saver to never come on.  Hmm. */
880       disable_builtin_screensaver (si, True);
881 #endif /* 0 */
882
883 #if 0
884       /* #### The MIT-SCREEN-SAVER extension gives us access to the
885          window that the server itself uses for saving the screen.
886          However, using this window in any way, in particular, calling
887          XScreenSaverSetAttributes() as below, tends to make the X server
888          crash.  So fuck it, let's try and get along without using it...
889
890          It's also inconvenient to use this window because it doesn't
891          always exist (though the ID is constant.)  So to use this
892          window, we'd have to reimplement the ACTIVATE ClientMessage to
893          tell the *server* to tell *us* to turn on, to cause the window
894          to get created at the right time.  Gag.  */
895       XScreenSaverSetAttributes (si->dpy, root,
896                                  0, 0, width, height, 0,
897                                  current_depth, InputOutput, visual,
898                                  attrmask, &attrs);
899       XSync (si->dpy, False);
900 #endif /* 0 */
901
902       info = XScreenSaverAllocInfo ();
903       XScreenSaverQueryInfo (si->dpy, root, info);
904       ssi->server_mit_saver_window = info->window;
905       if (! ssi->server_mit_saver_window) abort ();
906       XFree (info);
907     }
908 #endif /* HAVE_MIT_SAVER_EXTENSION */
909
910   if (ssi->screensaver_window)
911     {
912       XWindowChanges changes;
913       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
914       changes.x = 0;
915       changes.y = 0;
916       changes.width = width;
917       changes.height = height;
918       changes.border_width = 0;
919
920       XConfigureWindow (si->dpy, ssi->screensaver_window,
921                         changesmask, &changes);
922       XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
923                                attrmask, &attrs);
924     }
925   else
926     {
927       ssi->screensaver_window =
928         XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen), 0, 0,
929                        width, height, 0, ssi->current_depth, InputOutput,
930                        ssi->current_visual, attrmask, &attrs);
931       reset_stderr (ssi);
932       store_activate_time(si, True);
933       if (p->verbose_p)
934         fprintf (stderr, "%s: saver window is 0x%lx.\n",
935                  blurb(), (unsigned long) ssi->screensaver_window);
936     }
937
938
939   store_saver_id (ssi);
940
941   if (!ssi->cursor)
942     {
943       Pixmap bit;
944       bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
945                                          "\000", 1, 1,
946                                          BlackPixelOfScreen (ssi->screen),
947                                          BlackPixelOfScreen (ssi->screen),
948                                          1);
949       ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
950                                          0, 0);
951       XFreePixmap (si->dpy, bit);
952     }
953
954   XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
955
956   if (si->demoing_p)
957     XUndefineCursor (si->dpy, ssi->screensaver_window);
958   else
959     XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
960 }
961
962 void
963 initialize_screensaver_window (saver_info *si)
964 {
965   int i;
966   for (i = 0; i < si->nscreens; i++)
967     initialize_screensaver_window_1 (&si->screens[i]);
968 }
969
970
971 void 
972 raise_window (saver_info *si,
973               Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
974 {
975   saver_preferences *p = &si->prefs;
976   int i;
977
978   if (si->demoing_p)
979     inhibit_fade = True;
980
981   initialize_screensaver_window (si);
982   reset_watchdog_timer (si, True);
983
984   if (p->fade_p && p->fading_possible_p && !inhibit_fade)
985     {
986       Window *current_windows = (Window *)
987         calloc(sizeof(Window), si->nscreens);
988       Colormap *current_maps = (Colormap *)
989         calloc(sizeof(Colormap), si->nscreens);
990
991       for (i = 0; i < si->nscreens; i++)
992         {
993           saver_screen_info *ssi = &si->screens[i];
994           current_windows[i] = ssi->screensaver_window;
995           current_maps[i] = (between_hacks_p
996                              ? ssi->cmap
997                              : DefaultColormapOfScreen (ssi->screen));
998           /* Ensure that the default background of the window is really black,
999              not a pixmap or something.  (This does not clear the window.) */
1000           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1001                                 ssi->black_pixel);
1002         }
1003
1004       if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1005
1006       XGrabServer (si->dpy);                    /* ############ DANGER! */
1007
1008       /* Clear the stderr layer on each screen.
1009        */
1010       if (!dont_clear)
1011         for (i = 0; i < si->nscreens; i++)
1012           {
1013             saver_screen_info *ssi = &si->screens[i];
1014             if (ssi->stderr_overlay_window)
1015               /* Do this before the fade, since the stderr cmap won't fade
1016                  even if we uninstall it (beats me...) */
1017               clear_stderr (ssi);
1018           }
1019
1020       /* Note!  The server is grabbed, and this will take several seconds
1021          to complete! */
1022       fade_screens (si->dpy, current_maps, current_windows,
1023                     p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1024
1025       free(current_maps);
1026       free(current_windows);
1027       current_maps = 0;
1028       current_windows = 0;
1029
1030       if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1031
1032 #ifdef HAVE_MIT_SAVER_EXTENSION
1033       for (i = 0; i < si->nscreens; i++)
1034         {
1035           saver_screen_info *ssi = &si->screens[i];
1036           if (ssi->server_mit_saver_window &&
1037               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1038             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1039         }
1040 #endif /* HAVE_MIT_SAVER_EXTENSION */
1041
1042       XUngrabServer (si->dpy);
1043       XSync (si->dpy, False);                   /* ###### (danger over) */
1044     }
1045   else
1046     {
1047       for (i = 0; i < si->nscreens; i++)
1048         {
1049           saver_screen_info *ssi = &si->screens[i];
1050           if (!dont_clear)
1051             XClearWindow (si->dpy, ssi->screensaver_window);
1052           if (!dont_clear || ssi->stderr_overlay_window)
1053             clear_stderr (ssi);
1054           XMapRaised (si->dpy, ssi->screensaver_window);
1055 #ifdef HAVE_MIT_SAVER_EXTENSION
1056           if (ssi->server_mit_saver_window &&
1057               window_exists_p (si->dpy, ssi->server_mit_saver_window))
1058             XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1059 #endif /* HAVE_MIT_SAVER_EXTENSION */
1060         }
1061     }
1062
1063   for (i = 0; i < si->nscreens; i++)
1064     {
1065       saver_screen_info *ssi = &si->screens[i];
1066       if (ssi->cmap)
1067         XInstallColormap (si->dpy, ssi->cmap);
1068     }
1069 }
1070
1071 void
1072 blank_screen (saver_info *si)
1073 {
1074   int i;
1075
1076   /* Note: we do our grabs on the root window, not on the screensaver window.
1077      If we grabbed on the saver window, then the demo mode and lock dialog
1078      boxes wouldn't get any events.
1079    */
1080   grab_keyboard_and_mouse (si,
1081                            /*si->screens[0].screensaver_window,*/
1082                            RootWindowOfScreen(si->screens[0].screen),
1083                            (si->demoing_p
1084                             ? 0
1085                             : si->screens[0].cursor));
1086
1087   for (i = 0; i < si->nscreens; i++)
1088     {
1089       saver_screen_info *ssi = &si->screens[i];
1090
1091       save_real_vroot (ssi);
1092       store_vroot_property (si->dpy,
1093                             ssi->screensaver_window,
1094                             ssi->screensaver_window);
1095     }
1096   store_activate_time (si, si->screen_blanked_p);
1097   raise_window (si, False, False, False);
1098
1099 #ifdef HAVE_XHPDISABLERESET
1100   if (si->locked_p && !hp_locked_p)
1101     {
1102       XHPDisableReset (si->dpy);        /* turn off C-Sh-Reset */
1103       hp_locked_p = True;
1104     }
1105 #endif
1106
1107 #ifdef HAVE_VT_LOCKSWITCH
1108   if (si->locked_p)
1109       lock_vt (si, True);               /* turn off C-Alt-Fn */
1110 #endif
1111
1112   si->screen_blanked_p = True;
1113 }
1114
1115 void
1116 unblank_screen (saver_info *si)
1117 {
1118   saver_preferences *p = &si->prefs;
1119   Bool unfade_p = (p->fading_possible_p && p->unfade_p);
1120   int i;
1121
1122   monitor_power_on (si);
1123
1124   store_activate_time (si, True);
1125   reset_watchdog_timer (si, False);
1126
1127   if (si->demoing_p)
1128     unfade_p = False;
1129
1130   if (unfade_p)
1131     {
1132       Window *current_windows = (Window *)
1133         calloc(sizeof(Window), si->nscreens);
1134
1135       for (i = 0; i < si->nscreens; i++)
1136         {
1137           saver_screen_info *ssi = &si->screens[i];
1138           current_windows[i] = ssi->screensaver_window;
1139           /* Ensure that the default background of the window is really black,
1140              not a pixmap or something.  (This does not clear the window.) */
1141           XSetWindowBackground (si->dpy, ssi->screensaver_window,
1142                                 ssi->black_pixel);
1143         }
1144
1145       if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1146
1147
1148       XSync (si->dpy, False);
1149       XGrabServer (si->dpy);                    /* ############ DANGER! */
1150       XSync (si->dpy, False);
1151
1152       /* Clear the stderr layer on each screen.
1153        */
1154       for (i = 0; i < si->nscreens; i++)
1155         {
1156           saver_screen_info *ssi = &si->screens[i];
1157           clear_stderr (ssi);
1158         }
1159
1160       XUngrabServer (si->dpy);
1161       XSync (si->dpy, False);                   /* ###### (danger over) */
1162
1163
1164       fade_screens (si->dpy, 0, current_windows,
1165                     p->fade_seconds/1000, p->fade_ticks,
1166                     False, False);
1167
1168       free(current_windows);
1169       current_windows = 0;
1170
1171       if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1172     }
1173   else
1174     {
1175       for (i = 0; i < si->nscreens; i++)
1176         {
1177           saver_screen_info *ssi = &si->screens[i];
1178           if (ssi->cmap)
1179             {
1180               Colormap c = DefaultColormapOfScreen (ssi->screen);
1181               /* avoid technicolor */
1182               XClearWindow (si->dpy, ssi->screensaver_window);
1183               if (c) XInstallColormap (si->dpy, c);
1184             }
1185           XUnmapWindow (si->dpy, ssi->screensaver_window);
1186         }
1187     }
1188
1189
1190   /* If the focus window does has a non-default colormap, then install
1191      that colormap as well.  (On SGIs, this will cause both the root map
1192      and the focus map to be installed simultaniously.  It'd be nice to
1193      pick up the other colormaps that had been installed, too; perhaps
1194      XListInstalledColormaps could be used for that?)
1195    */
1196   {
1197     Window focus = 0;
1198     int revert_to;
1199     XGetInputFocus (si->dpy, &focus, &revert_to);
1200     if (focus && focus != PointerRoot && focus != None)
1201       {
1202         XWindowAttributes xgwa;
1203         xgwa.colormap = 0;
1204         XGetWindowAttributes (si->dpy, focus, &xgwa);
1205         if (xgwa.colormap &&
1206             xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1207           XInstallColormap (si->dpy, xgwa.colormap);
1208       }
1209   }
1210
1211
1212   for (i = 0; i < si->nscreens; i++)
1213     {
1214       saver_screen_info *ssi = &si->screens[i];
1215       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1216     }
1217
1218   store_activate_time(si, False);  /* store unblank time */
1219
1220   ungrab_keyboard_and_mouse (si);
1221   restore_real_vroot (si);
1222
1223 #ifdef HAVE_XHPDISABLERESET
1224   if (hp_locked_p)
1225     {
1226       XHPEnableReset (si->dpy); /* turn C-Sh-Reset back on */
1227       hp_locked_p = False;
1228     }
1229 #endif
1230
1231 #ifdef HAVE_VT_LOCKSWITCH
1232   lock_vt (si, False);          /* turn C-Alt-Fn back on */
1233 #endif
1234
1235   /* Unmap the windows a second time, dammit -- just to avoid a race
1236      with the screen-grabbing hacks.  (I'm not sure if this is really
1237      necessary; I'm stabbing in the dark now.)
1238   */
1239   for (i = 0; i < si->nscreens; i++)
1240     XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1241
1242   si->screen_blanked_p = False;
1243 }
1244
1245
1246 static void
1247 store_activate_time (saver_info *si, Bool use_last_p)
1248 {
1249   static time_t last_time = 0;
1250   time_t now = ((use_last_p && last_time) ? last_time : time ((time_t) 0));
1251   CARD32 now32 = (CARD32) now;
1252   int i;
1253   last_time = now;
1254
1255   for (i = 0; i < si->nscreens; i++)
1256     {
1257       saver_screen_info *ssi = &si->screens[i];
1258       if (!ssi->screensaver_window) continue;
1259       XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_TIME,
1260                        XA_INTEGER, 32, PropModeReplace,
1261                        (unsigned char *) &now32, 1);
1262     }
1263 }
1264
1265
1266 Bool
1267 select_visual (saver_screen_info *ssi, const char *visual_name)
1268 {
1269   saver_info *si = ssi->global;
1270   saver_preferences *p = &si->prefs;
1271   Bool install_cmap_p = p->install_cmap_p;
1272   Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1273   Visual *new_v;
1274   Bool got_it;
1275
1276   if (visual_name && *visual_name)
1277     {
1278       if (!strcmp(visual_name, "default-i"))
1279         {
1280           visual_name = "default";
1281           install_cmap_p = True;
1282         }
1283       else if (!strcmp(visual_name, "default-n"))
1284         {
1285           visual_name = "default";
1286           install_cmap_p = False;
1287         }
1288       new_v = get_visual (ssi->screen, visual_name, True, False);
1289     }
1290   else
1291     {
1292       new_v = ssi->default_visual;
1293     }
1294
1295   got_it = !!new_v;
1296
1297   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1298     install_cmap_p = True;
1299
1300   ssi->install_cmap_p = install_cmap_p;
1301
1302   if (new_v &&
1303       ((ssi->current_visual != new_v) ||
1304        (install_cmap_p != was_installed_p)))
1305     {
1306       Colormap old_c = ssi->cmap;
1307       Window old_w = ssi->screensaver_window;
1308
1309       if (p->verbose_p)
1310         {
1311           fprintf (stderr, "%s: switching to visual ", blurb());
1312           describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
1313 #if 0
1314           fprintf (stderr, "%s:                from ", blurb());
1315           describe_visual (stderr, ssi->screen, ssi->current_visual,
1316                            was_installed_p);
1317 #endif
1318         }
1319
1320       reset_stderr (ssi);
1321       ssi->current_visual = new_v;
1322       ssi->current_depth = visual_depth(ssi->screen, new_v);
1323       ssi->cmap = 0;
1324       ssi->screensaver_window = 0;
1325
1326       initialize_screensaver_window_1 (ssi);
1327
1328       /* stderr_overlay_window is a child of screensaver_window, so we need
1329          to destroy that as well (actually, we just need to invalidate and
1330          drop our pointers to it, but this will destroy it, which is ok so
1331          long as it happens before old_w itself is destroyed.) */
1332       reset_stderr (ssi);
1333
1334       raise_window (si, True, True, False);
1335       store_vroot_property (si->dpy,
1336                             ssi->screensaver_window, ssi->screensaver_window);
1337       store_activate_time (si, True);
1338
1339
1340
1341       /* Transfer the grabs from the old window to the new.
1342          Actually I think none of this is necessary, since we always
1343          hold our grabs on the root window, but I wrote this before
1344          re-discovering that...
1345        */
1346
1347
1348       /* If we're destroying the window that holds our mouse grab,
1349          transfer the grab to the new window.  (Grab the server while
1350          so doing, to avoid a race condition.)
1351        */
1352       if (old_w == si->mouse_grab_window)
1353         {
1354           XGrabServer (si->dpy);                /* ############ DANGER! */
1355           ungrab_mouse (si);
1356           grab_mouse (si, ssi->screensaver_window,
1357                       (si->demoing_p
1358                        ? 0
1359                        : ssi->cursor));
1360           XUngrabServer (si->dpy);
1361           XSync (si->dpy, False);               /* ###### (danger over) */
1362         }
1363
1364       /* If we're destroying the window that holds our keyboard grab,
1365          transfer the grab to the new window.  (Grab the server while
1366          so doing, to avoid a race condition.)
1367        */
1368       if (old_w == si->keyboard_grab_window)
1369         {
1370           XGrabServer (si->dpy);                /* ############ DANGER! */
1371           ungrab_kbd(si);
1372           grab_kbd(si, ssi->screensaver_window);
1373           XUngrabServer (si->dpy);
1374           XSync (si->dpy, False);               /* ###### (danger over) */
1375         }
1376
1377       /* Now we can destroy this window without horking our grabs. */
1378
1379       XDestroyWindow (si->dpy, old_w);
1380
1381       if (p->verbose_p)
1382         fprintf (stderr, "%s: destroyed old saver window 0x%lx.\n",
1383                  blurb(), (unsigned long) old_w);
1384
1385       if (old_c &&
1386           old_c != DefaultColormapOfScreen (ssi->screen) &&
1387           old_c != ssi->demo_cmap)
1388         XFreeColormap (si->dpy, old_c);
1389     }
1390
1391   return got_it;
1392 }
1393
1394 \f
1395 /* VT locking */
1396
1397 #ifdef HAVE_VT_LOCKSWITCH
1398 static void
1399 lock_vt (saver_info *si, Bool lock_p)
1400 {
1401   saver_preferences *p = &si->prefs;
1402   static Bool locked_p = False;
1403   const char *dev_console = "/dev/console";
1404   int fd;
1405
1406   if (lock_p == locked_p)
1407     return;
1408
1409   if (lock_p && !p->lock_vt_p)
1410     return;
1411
1412   fd = open (dev_console, O_RDWR);
1413   if (fd < 0)
1414     {
1415       char buf [255];
1416       sprintf (buf, "%s: couldn't %s VTs: %s", blurb(),
1417                (lock_p ? "lock" : "unlock"),
1418                dev_console);
1419 #if 0 /* #### doesn't work yet, so don't bother complaining */
1420       perror (buf);
1421 #endif
1422       return;
1423     }
1424
1425   if (ioctl (fd, (lock_p ? VT_LOCKSWITCH : VT_UNLOCKSWITCH)) == 0)
1426     {
1427       locked_p = lock_p;
1428
1429       if (p->verbose_p)
1430         fprintf (stderr, "%s: %s VTs\n", blurb(),
1431                  (lock_p ? "locked" : "unlocked"));
1432     }
1433   else
1434     {
1435       char buf [255];
1436       sprintf (buf, "%s: couldn't %s VTs: ioctl", blurb(),
1437                (lock_p ? "lock" : "unlock"));
1438 #if 0 /* #### doesn't work yet, so don't bother complaining */
1439       perror (buf);
1440 #endif
1441     }
1442
1443   close (fd);
1444 }
1445 #endif /* HAVE_VT_LOCKSWITCH */