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