1 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
2 * xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@jwz.org>
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
18 # include <unixlib.h> /* for getpid() */
19 # include "vms-gtod.h" /* for gettimeofday() */
23 # include <pwd.h> /* for getpwuid() */
29 # include <sys/utsname.h> /* for uname() */
30 #endif /* HAVE_UNAME */
33 #include <X11/Xproto.h> /* for CARD32 */
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 */
40 #ifdef HAVE_MIT_SAVER_EXTENSION
41 # include <X11/extensions/scrnsaver.h>
42 #endif /* HAVE_MIT_SAVER_EXTENSION */
45 # include <X11/extensions/xf86vmode.h>
46 #endif /* HAVE_XF86VMODE */
48 #ifdef HAVE_XHPDISABLERESET
49 # include <X11/XHPlib.h>
51 /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
52 or BadAccess errors occur. (Ok for this to be global, since it
53 affects the whole machine, not just the current screen.) */
54 Bool hp_locked_p = False;
56 #endif /* HAVE_XHPDISABLERESET */
59 /* This file doesn't need the Xt headers, so stub these types out... */
61 #define XtAppContext void*
62 #define XrmDatabase void*
63 #define XtIntervalId void*
64 #define XtPointer void*
67 #include "xscreensaver.h"
72 #ifdef HAVE_VT_LOCKSWITCH
74 # include <sys/ioctl.h>
76 static void lock_vt (saver_info *si, Bool lock_p);
77 #endif /* HAVE_VT_LOCKSWITCH */
80 extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
82 Atom XA_VROOT, XA_XSETROOT_ID;
83 Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
84 Atom XA_SCREENSAVER_TIME;
87 extern saver_info *global_si_kludge; /* I hate C so much... */
90 static void store_activate_time (saver_info *si, Bool use_last_p);
92 #define ALL_POINTER_EVENTS \
93 (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
94 LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
95 Button1MotionMask | Button2MotionMask | Button3MotionMask | \
96 Button4MotionMask | Button5MotionMask | ButtonMotionMask)
100 grab_string(int status)
104 case GrabSuccess: return "GrabSuccess";
105 case AlreadyGrabbed: return "AlreadyGrabbed";
106 case GrabInvalidTime: return "GrabInvalidTime";
107 case GrabNotViewable: return "GrabNotViewable";
108 case GrabFrozen: return "GrabFrozen";
111 static char foo[255];
112 sprintf(foo, "unknown status: %d", status);
119 grab_kbd(saver_info *si, Window w)
121 saver_preferences *p = &si->prefs;
122 int status = XGrabKeyboard (si->dpy, w, True,
123 /* I don't really understand Sync vs Async,
124 but these seem to work... */
125 GrabModeSync, GrabModeAsync,
127 if (status == GrabSuccess)
128 si->keyboard_grab_window = w;
131 fprintf(stderr, "%s: grabbing keyboard on 0x%x... %s.\n",
132 blurb(), (unsigned long) w, grab_string(status));
138 grab_mouse (saver_info *si, Window w, Cursor cursor)
140 saver_preferences *p = &si->prefs;
141 int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS,
142 GrabModeAsync, GrabModeAsync, w,
143 cursor, CurrentTime);
144 if (status == GrabSuccess)
145 si->mouse_grab_window = w;
148 fprintf(stderr, "%s: grabbing mouse on 0x%x... %s.\n",
149 blurb(), (unsigned long) w, grab_string(status));
155 ungrab_kbd(saver_info *si)
157 saver_preferences *p = &si->prefs;
158 XUngrabKeyboard(si->dpy, CurrentTime);
160 fprintf(stderr, "%s: ungrabbing keyboard (was 0x%x).\n", blurb(),
161 (unsigned long) si->keyboard_grab_window);
162 si->keyboard_grab_window = 0;
167 ungrab_mouse(saver_info *si)
169 saver_preferences *p = &si->prefs;
170 XUngrabPointer(si->dpy, CurrentTime);
172 fprintf(stderr, "%s: ungrabbing mouse (was 0x%x).\n", blurb(),
173 (unsigned long) si->mouse_grab_window);
174 si->mouse_grab_window = 0;
179 grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
181 Status mstatus, kstatus;
182 XSync (si->dpy, False);
184 kstatus = grab_kbd (si, window);
185 if (kstatus != GrabSuccess)
186 { /* try again in a second */
188 kstatus = grab_kbd (si, window);
189 if (kstatus != GrabSuccess)
190 fprintf (stderr, "%s: couldn't grab keyboard! (%s)\n",
191 blurb(), grab_string(kstatus));
194 mstatus = grab_mouse (si, window, cursor);
195 if (mstatus != GrabSuccess)
196 { /* try again in a second */
198 mstatus = grab_mouse (si, window, cursor);
199 if (mstatus != GrabSuccess)
200 fprintf (stderr, "%s: couldn't grab pointer! (%s)\n",
201 blurb(), grab_string(mstatus));
204 return (kstatus == GrabSuccess ||
205 mstatus == GrabSuccess);
209 ungrab_keyboard_and_mouse (saver_info *si)
217 move_mouse_grab (saver_info *si, Window to, Cursor cursor)
219 Window old = si->mouse_grab_window;
222 return grab_mouse (si, to, cursor);
225 saver_preferences *p = &si->prefs;
228 XSync (si->dpy, False);
229 XGrabServer (si->dpy); /* ############ DANGER! */
230 XSync (si->dpy, False);
233 fprintf(stderr, "%s: grabbing server...\n", blurb());
236 status = grab_mouse (si, to, cursor);
238 if (status != GrabSuccess) /* Augh! */
240 sleep (1); /* Note dramatic evil of sleeping
241 with server grabbed. */
242 XSync (si->dpy, False);
243 status = grab_mouse (si, to, cursor);
246 if (status != GrabSuccess) /* Augh! Try to get the old one back... */
247 grab_mouse (si, to, cursor);
249 XUngrabServer (si->dpy);
250 XSync (si->dpy, False); /* ###### (danger over) */
253 fprintf(stderr, "%s: ungrabbing server.\n", blurb());
260 /* Prints an error message to stderr and returns True if there is another
261 xscreensaver running already. Silently returns False otherwise. */
263 ensure_no_screensaver_running (Display *dpy, Screen *screen)
267 Window root = RootWindowOfScreen (screen);
268 Window root2, parent, *kids;
270 XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
272 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
278 for (i = 0; i < nkids; i++)
282 unsigned long nitems, bytesafter;
285 if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
286 False, XA_STRING, &type, &format, &nitems,
287 &bytesafter, (unsigned char **) &version)
292 if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
293 False, XA_STRING, &type, &format, &nitems,
294 &bytesafter, (unsigned char **) &id)
300 "%s: already running on display %s (window 0x%x)\n from process %s.\n",
301 blurb(), DisplayString (dpy), (int) kids [i], id);
306 if (kids) XFree ((char *) kids);
308 XSetErrorHandler (old_handler);
314 /* Virtual-root hackery */
317 ERROR! You must not include vroot.h in this file.
321 store_vroot_property (Display *dpy, Window win, Window value)
326 "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(),
328 (win == screensaver_window ? "ScreenSaver" :
329 (win == real_vroot ? "VRoot" :
330 (win == real_vroot_value ? "Vroot_value" : "???"))),
332 (value == screensaver_window ? "ScreenSaver" :
333 (value == real_vroot ? "VRoot" :
334 (value == real_vroot_value ? "Vroot_value" : "???"))));
336 XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
337 (unsigned char *) &value, 1);
341 remove_vroot_property (Display *dpy, Window win)
345 fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win,
346 (win == screensaver_window ? "ScreenSaver" :
347 (win == real_vroot ? "VRoot" :
348 (win == real_vroot_value ? "Vroot_value" : "???"))));
350 XDeleteProperty (dpy, win, XA_VROOT);
355 kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
359 unsigned long nitems, bytesafter;
362 /* If the user has been using xv or xsetroot as a screensaver (to display
363 an image on the screensaver window, as a kind of slideshow) then the
364 pixmap and its associated color cells have been put in RetainPermanent
365 CloseDown mode. Since we're not destroying the xscreensaver window,
366 but merely unmapping it, we need to free these resources or those
367 colormap cells will stay allocated while the screensaver is off. (We
368 could just delete the screensaver window and recreate it later, but
369 that could cause other problems.) This code does an atomic read-and-
370 delete of the _XSETROOT_ID property, and if it held a pixmap, then we
371 cause the RetainPermanent resources of the client which created it
372 (and which no longer exists) to be freed.
374 if (XGetWindowProperty (dpy, window, XA_XSETROOT_ID, 0, 1,
375 True, AnyPropertyType, &type, &format, &nitems,
376 &bytesafter, (unsigned char **) &dataP)
380 if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
381 nitems == 1 && bytesafter == 0)
384 fprintf (stderr, "%s: destroying xsetroot data (0x%lX).\n",
386 XKillClient (dpy, *dataP);
389 fprintf (stderr, "%s: deleted unrecognised _XSETROOT_ID property: \n\
390 %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
391 blurb(), (unsigned long) dataP, (dataP ? *dataP : 0), type,
392 format, nitems, bytesafter);
397 static void handle_signals (saver_info *si, Bool on_p);
400 save_real_vroot (saver_screen_info *ssi)
402 saver_info *si = ssi->global;
403 Display *dpy = si->dpy;
404 Screen *screen = ssi->screen;
406 Window root = RootWindowOfScreen (screen);
407 Window root2, parent, *kids;
409 XErrorHandler old_handler;
411 /* It's possible that a window might be deleted between our call to
412 XQueryTree() and our call to XGetWindowProperty(). Don't die if
413 that happens (but just ignore that window, it's not the one we're
414 interested in anyway.)
417 old_handler = XSetErrorHandler (BadWindow_ehandler);
421 ssi->real_vroot_value = 0;
422 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
428 for (i = 0; i < nkids; i++)
432 unsigned long nitems, bytesafter;
435 if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
436 &type, &format, &nitems, &bytesafter,
437 (unsigned char **) &vrootP)
444 if (*vrootP == ssi->screensaver_window) abort ();
446 "%s: more than one virtual root window found (0x%x and 0x%x).\n",
447 blurb(), (int) ssi->real_vroot, (int) kids [i]);
450 ssi->real_vroot = kids [i];
451 ssi->real_vroot_value = *vrootP;
455 XSetErrorHandler (old_handler);
460 handle_signals (si, True);
461 remove_vroot_property (si->dpy, ssi->real_vroot);
465 XFree ((char *) kids);
470 restore_real_vroot_2 (saver_screen_info *ssi)
472 saver_info *si = ssi->global;
473 saver_preferences *p = &si->prefs;
474 if (p->verbose_p && ssi->real_vroot)
476 "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
477 blurb(), (unsigned long) ssi->real_vroot);
478 remove_vroot_property (si->dpy, ssi->screensaver_window);
481 store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value);
483 ssi->real_vroot_value = 0;
484 /* make sure the property change gets there before this process
485 terminates! We might be doing this because we have intercepted
486 SIGTERM or something. */
487 XSync (si->dpy, False);
494 restore_real_vroot_1 (saver_info *si)
497 Bool did_any = False;
498 for (i = 0; i < si->nscreens; i++)
500 saver_screen_info *ssi = &si->screens[i];
501 if (restore_real_vroot_2 (ssi))
508 restore_real_vroot (saver_info *si)
510 if (restore_real_vroot_1 (si))
511 handle_signals (si, False);
515 /* Signal hackery to ensure that the vroot doesn't get left in an
520 signal_name(int signal)
523 case SIGHUP: return "SIGHUP";
524 case SIGINT: return "SIGINT";
525 case SIGQUIT: return "SIGQUIT";
526 case SIGILL: return "SIGILL";
527 case SIGTRAP: return "SIGTRAP";
529 case SIGABRT: return "SIGABRT";
531 case SIGFPE: return "SIGFPE";
532 case SIGKILL: return "SIGKILL";
533 case SIGBUS: return "SIGBUS";
534 case SIGSEGV: return "SIGSEGV";
535 case SIGPIPE: return "SIGPIPE";
536 case SIGALRM: return "SIGALRM";
537 case SIGTERM: return "SIGTERM";
539 case SIGSTOP: return "SIGSTOP";
542 case SIGCONT: return "SIGCONT";
545 case SIGUSR1: return "SIGUSR1";
548 case SIGUSR2: return "SIGUSR2";
551 case SIGEMT: return "SIGEMT";
554 case SIGSYS: return "SIGSYS";
557 case SIGCHLD: return "SIGCHLD";
560 case SIGPWR: return "SIGPWR";
563 case SIGWINCH: return "SIGWINCH";
566 case SIGURG: return "SIGURG";
569 case SIGIO: return "SIGIO";
572 case SIGVTALRM: return "SIGVTALRM";
575 case SIGXCPU: return "SIGXCPU";
578 case SIGXFSZ: return "SIGXFSZ";
581 case SIGDANGER: return "SIGDANGER";
586 sprintf(buf, "signal %d\n", signal);
595 restore_real_vroot_handler (int sig)
597 saver_info *si = global_si_kludge; /* I hate C so much... */
599 signal (sig, SIG_DFL);
600 if (restore_real_vroot_1 (si))
601 fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
602 blurb(), signal_name(sig));
603 kill (getpid (), sig);
607 catch_signal (saver_info *si, int sig, Bool on_p)
610 signal (sig, SIG_DFL);
613 if (((long) signal (sig, restore_real_vroot_handler)) == -1L)
616 sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
618 saver_exit (si, 1, 0);
624 handle_signals (saver_info *si, Bool on_p)
627 if (on_p) fprintf (stderr, "handling signals\n");
628 else fprintf (stderr, "unhandling signals\n");
631 catch_signal (si, SIGHUP, on_p);
632 catch_signal (si, SIGINT, on_p);
633 catch_signal (si, SIGQUIT, on_p);
634 catch_signal (si, SIGILL, on_p);
635 catch_signal (si, SIGTRAP, on_p);
636 catch_signal (si, SIGIOT, on_p);
637 catch_signal (si, SIGABRT, on_p);
639 catch_signal (si, SIGEMT, on_p);
641 catch_signal (si, SIGFPE, on_p);
642 catch_signal (si, SIGBUS, on_p);
643 catch_signal (si, SIGSEGV, on_p);
645 catch_signal (si, SIGSYS, on_p);
647 catch_signal (si, SIGTERM, on_p);
649 catch_signal (si, SIGXCPU, on_p);
652 catch_signal (si, SIGXFSZ, on_p);
655 catch_signal (si, SIGDANGER, on_p);
660 saver_exit (saver_info *si, int status, const char *dump_core_reason)
662 saver_preferences *p = &si->prefs;
663 static Bool exiting = False;
672 vrs = restore_real_vroot_1 (si);
673 emergency_kill_subproc (si);
675 if (vrs && (p->verbose_p || status != 0))
676 fprintf (real_stderr, "%s: vroot restored, exiting.\n", blurb());
677 else if (p->verbose_p)
678 fprintf (real_stderr, "%s: no vroot to restore; exiting.\n", blurb());
682 #ifdef VMS /* on VMS, 1 is the "normal" exit code instead of 0. */
683 if (status == 0) status = 1;
684 else if (status == 1) status = -1;
687 bugp = !!dump_core_reason;
689 if (si->prefs.debug_p && !dump_core_reason)
690 dump_core_reason = "because of -debug";
692 if (dump_core_reason)
694 /* Note that the Linux man page for setuid() says If uid is
695 different from the old effective uid, the process will be
696 forbidden from leaving core dumps.
698 char cwd[4096]; /* should really be PATH_MAX, but who cares. */
700 fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
705 "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
706 "\t\tfor bug reporting information.\n\n",
709 # if defined(HAVE_GETCWD)
710 if (!getcwd (cwd, sizeof(cwd)))
711 # elif defined(HAVE_GETWD)
714 strcpy(cwd, "unknown.");
716 fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
717 describe_uids (si, real_stderr);
719 /* Do this to drop a core file, so that we can get a stack trace. */
727 /* Managing the actual screensaver window */
730 window_exists_p (Display *dpy, Window window)
732 XErrorHandler old_handler;
733 XWindowAttributes xgwa;
735 old_handler = XSetErrorHandler (BadWindow_ehandler);
736 XGetWindowAttributes (dpy, window, &xgwa);
738 XSetErrorHandler (old_handler);
739 return (xgwa.screen != 0);
743 store_saver_id (saver_screen_info *ssi)
745 XClassHint class_hints;
746 saver_info *si = ssi->global;
747 unsigned long pid = (unsigned long) getpid ();
749 struct passwd *p = getpwuid (getuid ());
750 const char *name, *host;
753 /* First store the name and class on the window.
755 class_hints.res_name = progname;
756 class_hints.res_class = progclass;
757 XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
758 XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
760 /* Then store the xscreensaver version number.
762 XChangeProperty (si->dpy, ssi->screensaver_window,
763 XA_SCREENSAVER_VERSION,
764 XA_STRING, 8, PropModeReplace,
765 (unsigned char *) si->version,
766 strlen (si->version));
768 /* Now store the XSCREENSAVER_ID property, that says what user and host
769 xscreensaver is running as.
772 if (p && p->pw_name && *p->pw_name)
776 sprintf (buf, "%lu", (unsigned long) p->pw_uid);
782 # if defined(HAVE_UNAME)
785 if (uname (&uts) < 0)
791 host = getenv("SYS$NODE");
792 # else /* !HAVE_UNAME && !VMS */
794 # endif /* !HAVE_UNAME && !VMS */
796 id = (char *) malloc (strlen(name) + strlen(host) + 50);
797 sprintf (id, "%lu (%s@%s)", pid, name, host);
799 XChangeProperty (si->dpy, ssi->screensaver_window,
800 XA_SCREENSAVER_ID, XA_STRING,
802 (unsigned char *) id, strlen (id));
807 /* Returns the area of the screen which the xscreensaver window should cover.
808 Normally this is the whole screen, but if the X server's root window is
809 actually larger than the monitor's displayable area, then we want to
810 operate in the currently-visible portion of the desktop instead.
813 get_screen_viewport (saver_screen_info *ssi,
814 int *x_ret, int *y_ret,
815 int *w_ret, int *h_ret,
818 int w = WidthOfScreen (ssi->screen);
819 int h = HeightOfScreen (ssi->screen);
821 #ifdef HAVE_XF86VMODE
822 saver_info *si = ssi->global;
823 int screen_no = screen_number (ssi->screen);
826 XF86VidModeModeLine ml;
829 if (XF86VidModeQueryExtension (si->dpy, &event, &error) &&
830 XF86VidModeGetModeLine (si->dpy, screen_no, &dot, &ml) &&
831 XF86VidModeGetViewPort (si->dpy, screen_no, &x, &y))
836 *w_ret = ml.hdisplay;
837 *h_ret = ml.vdisplay;
839 if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
840 /* There is no viewport -- the screen does not scroll. */
843 sprintf (msg, "%s: vp is %dx%d+%d+%d",
844 blurb(), *w_ret, *h_ret, *x_ret, *y_ret);
847 /* Apparently, though the server stores the X position in increments of
848 1 pixel, it will only make changes to the *display* in some other
849 increment. With XF86_SVGA on a Thinkpad, the display only updates
850 in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
851 pixels in 16-bit mode. I don't know what it does in 24- and 32-bit
852 mode, because I don't have enough video memory to find out.
854 I consider it a bug that XF86VidModeGetViewPort() is telling me the
855 server's *target* scroll position rather than the server's *actual*
856 scroll position. David Dawes agrees, and says they may fix this in
857 XFree86 4.0, but it's notrivial.
859 He also confirms that this behavior is server-dependent, so the
860 actual scroll position cannot be reliably determined by the client.
861 So... that means the only solution is to provide a ``sandbox''
862 around the blackout window -- we make the window be up to N pixels
863 larger than the viewport on both the left and right sides. That
864 means some part of the outer edges of each hack might not be
865 visible, but screw it.
867 I'm going to guess that 16 pixels is enough, and that the Y dimension
868 doesn't have this problem.
870 The drawback of doing this, of course, is that some of the screenhacks
871 will still look pretty stupid -- for example, "slidescreen" will cut
872 off the left and right edges of the grid, etc.
875 if (x > 0 && x < w - ml.hdisplay) /* not at left edge or right edge */
877 /* Round X position down to next lower multiple of FUDGE.
878 Increase width by 2*FUDGE in case some server rounds up.
880 *x_ret = ((x - 1) / FUDGE) * FUDGE;
881 *w_ret += (FUDGE * 2);
887 *w_ret != ml.hdisplay ||
888 *h_ret != ml.vdisplay)
889 sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
890 *w_ret, *h_ret, *x_ret, *y_ret);
893 fprintf (stderr, "%s.\n", msg);
898 #endif /* HAVE_XF86VMODE */
908 initialize_screensaver_window_1 (saver_screen_info *ssi)
910 saver_info *si = ssi->global;
911 saver_preferences *p = &si->prefs;
912 Bool install_cmap_p = ssi->install_cmap_p; /* not p->install_cmap_p */
914 /* This resets the screensaver window as fully as possible, since there's
915 no way of knowing what some random client may have done to us in the
916 meantime. We could just destroy and recreate the window, but that has
917 its own set of problems...
920 XSetWindowAttributes attrs;
921 unsigned long attrmask;
922 int x, y, width, height;
923 static Bool printed_visual_info = False; /* only print the message once. */
925 get_screen_viewport (si->default_screen, &x, &y, &width, &height,
926 (p->verbose_p && !si->screen_blanked_p));
928 black.red = black.green = black.blue = 0;
930 if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
933 if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
934 /* It's not the default visual, so we have no choice but to install. */
935 install_cmap_p = True;
941 ssi->cmap = XCreateColormap (si->dpy,
942 RootWindowOfScreen (ssi->screen),
943 ssi->current_visual, AllocNone);
944 if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
945 ssi->black_pixel = black.pixel;
950 Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
953 XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
954 if (ssi->cmap != ssi->demo_cmap &&
955 ssi->cmap != def_cmap)
956 XFreeColormap (si->dpy, ssi->cmap);
958 ssi->cmap = def_cmap;
959 ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
962 attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
963 CWBackPixel | CWBackingPixel | CWBorderPixel);
964 attrs.override_redirect = True;
966 /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
967 actually be reading these events during normal operation; but we still
968 need to see Button events for demo-mode to work properly.
970 attrs.event_mask = (KeyPressMask | KeyReleaseMask |
971 ButtonPressMask | ButtonReleaseMask |
974 attrs.backing_store = NotUseful;
975 attrs.colormap = ssi->cmap;
976 attrs.background_pixel = ssi->black_pixel;
977 attrs.backing_pixel = ssi->black_pixel;
978 attrs.border_pixel = ssi->black_pixel;
980 if (p->debug_p) width = width / 2;
982 if (!p->verbose_p || printed_visual_info)
984 else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
986 fprintf (stderr, "%s: using default visual ", blurb());
987 describe_visual (stderr, ssi->screen, ssi->current_visual,
992 fprintf (stderr, "%s: using visual: ", blurb());
993 describe_visual (stderr, ssi->screen, ssi->current_visual,
995 fprintf (stderr, "%s: default visual: ", blurb());
996 describe_visual (stderr, ssi->screen,
997 DefaultVisualOfScreen (ssi->screen),
998 ssi->install_cmap_p);
1000 printed_visual_info = True;
1002 #ifdef HAVE_MIT_SAVER_EXTENSION
1003 if (si->using_mit_saver_extension)
1005 XScreenSaverInfo *info;
1006 Window root = RootWindowOfScreen (ssi->screen);
1009 /* This call sets the server screensaver timeouts to what we think
1010 they should be (based on the resources and args xscreensaver was
1011 started with.) It's important that we do this to sync back up
1012 with the server - if we have turned on prematurely, as by an
1013 ACTIVATE ClientMessage, then the server may decide to activate
1014 the screensaver while it's already active. That's ok for us,
1015 since we would know to ignore that ScreenSaverActivate event,
1016 but a side effect of this would be that the server would map its
1017 saver window (which we then hide again right away) meaning that
1018 the bits currently on the screen get blown away. Ugly. */
1020 /* #### Ok, that doesn't work - when we tell the server that the
1021 screensaver is "off" it sends us a Deactivate event, which is
1022 sensible... but causes the saver to never come on. Hmm. */
1023 disable_builtin_screensaver (si, True);
1027 /* #### The MIT-SCREEN-SAVER extension gives us access to the
1028 window that the server itself uses for saving the screen.
1029 However, using this window in any way, in particular, calling
1030 XScreenSaverSetAttributes() as below, tends to make the X server
1031 crash. So fuck it, let's try and get along without using it...
1033 It's also inconvenient to use this window because it doesn't
1034 always exist (though the ID is constant.) So to use this
1035 window, we'd have to reimplement the ACTIVATE ClientMessage to
1036 tell the *server* to tell *us* to turn on, to cause the window
1037 to get created at the right time. Gag. */
1038 XScreenSaverSetAttributes (si->dpy, root,
1039 0, 0, width, height, 0,
1040 current_depth, InputOutput, visual,
1042 XSync (si->dpy, False);
1045 info = XScreenSaverAllocInfo ();
1046 XScreenSaverQueryInfo (si->dpy, root, info);
1047 ssi->server_mit_saver_window = info->window;
1048 if (! ssi->server_mit_saver_window) abort ();
1051 #endif /* HAVE_MIT_SAVER_EXTENSION */
1053 if (ssi->screensaver_window)
1055 XWindowChanges changes;
1056 unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1059 changes.width = width;
1060 changes.height = height;
1061 changes.border_width = 0;
1063 XConfigureWindow (si->dpy, ssi->screensaver_window,
1064 changesmask, &changes);
1065 XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
1070 ssi->screensaver_window =
1071 XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
1072 x, y, width, height,
1073 0, ssi->current_depth, InputOutput,
1074 ssi->current_visual, attrmask, &attrs);
1077 store_activate_time(si, True);
1079 fprintf (stderr, "%s: saver window is 0x%lx.\n",
1080 blurb(), (unsigned long) ssi->screensaver_window);
1084 store_saver_id (ssi);
1089 bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
1091 BlackPixelOfScreen (ssi->screen),
1092 BlackPixelOfScreen (ssi->screen),
1094 ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
1096 XFreePixmap (si->dpy, bit);
1099 XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
1102 XUndefineCursor (si->dpy, ssi->screensaver_window);
1104 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1108 initialize_screensaver_window (saver_info *si)
1111 for (i = 0; i < si->nscreens; i++)
1112 initialize_screensaver_window_1 (&si->screens[i]);
1117 raise_window (saver_info *si,
1118 Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
1120 saver_preferences *p = &si->prefs;
1124 inhibit_fade = True;
1126 if (si->emergency_lock_p)
1127 inhibit_fade = True;
1130 initialize_screensaver_window (si);
1132 reset_watchdog_timer (si, True);
1134 if (p->fade_p && si->fading_possible_p && !inhibit_fade)
1136 Window *current_windows = (Window *)
1137 calloc(sizeof(Window), si->nscreens);
1138 Colormap *current_maps = (Colormap *)
1139 calloc(sizeof(Colormap), si->nscreens);
1141 for (i = 0; i < si->nscreens; i++)
1143 saver_screen_info *ssi = &si->screens[i];
1144 current_windows[i] = ssi->screensaver_window;
1145 current_maps[i] = (between_hacks_p
1147 : DefaultColormapOfScreen (ssi->screen));
1148 /* Ensure that the default background of the window is really black,
1149 not a pixmap or something. (This does not clear the window.) */
1150 XSetWindowBackground (si->dpy, ssi->screensaver_window,
1154 if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1156 XGrabServer (si->dpy); /* ############ DANGER! */
1158 /* Clear the stderr layer on each screen.
1161 for (i = 0; i < si->nscreens; i++)
1163 saver_screen_info *ssi = &si->screens[i];
1164 if (ssi->stderr_overlay_window)
1165 /* Do this before the fade, since the stderr cmap won't fade
1166 even if we uninstall it (beats me...) */
1170 /* Note! The server is grabbed, and this will take several seconds
1172 fade_screens (si->dpy, current_maps, current_windows,
1173 p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1176 free(current_windows);
1178 current_windows = 0;
1180 if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1182 #ifdef HAVE_MIT_SAVER_EXTENSION
1183 for (i = 0; i < si->nscreens; i++)
1185 saver_screen_info *ssi = &si->screens[i];
1186 if (ssi->server_mit_saver_window &&
1187 window_exists_p (si->dpy, ssi->server_mit_saver_window))
1188 XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1190 #endif /* HAVE_MIT_SAVER_EXTENSION */
1192 XUngrabServer (si->dpy);
1193 XSync (si->dpy, False); /* ###### (danger over) */
1197 for (i = 0; i < si->nscreens; i++)
1199 saver_screen_info *ssi = &si->screens[i];
1201 XClearWindow (si->dpy, ssi->screensaver_window);
1202 if (!dont_clear || ssi->stderr_overlay_window)
1204 XMapRaised (si->dpy, ssi->screensaver_window);
1205 #ifdef HAVE_MIT_SAVER_EXTENSION
1206 if (ssi->server_mit_saver_window &&
1207 window_exists_p (si->dpy, ssi->server_mit_saver_window))
1208 XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1209 #endif /* HAVE_MIT_SAVER_EXTENSION */
1213 for (i = 0; i < si->nscreens; i++)
1215 saver_screen_info *ssi = &si->screens[i];
1217 XInstallColormap (si->dpy, ssi->cmap);
1222 blank_screen (saver_info *si)
1227 /* Note: we do our grabs on the root window, not on the screensaver window.
1228 If we grabbed on the saver window, then the demo mode and lock dialog
1229 boxes wouldn't get any events.
1231 ok = grab_keyboard_and_mouse (si,
1232 /*si->screens[0].screensaver_window,*/
1233 RootWindowOfScreen(si->screens[0].screen),
1236 : si->screens[0].cursor));
1239 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1240 /* If we're using a server extension, then failure to get a grab is
1241 not a big deal -- even without the grab, we will still be able
1242 to un-blank when there is user activity, since the server will
1249 for (i = 0; i < si->nscreens; i++)
1251 saver_screen_info *ssi = &si->screens[i];
1253 save_real_vroot (ssi);
1254 store_vroot_property (si->dpy,
1255 ssi->screensaver_window,
1256 ssi->screensaver_window);
1258 #ifdef HAVE_XF86VMODE
1261 if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
1262 !XF86VidModeGetViewPort (si->dpy, i,
1265 ssi->blank_vp_x = ssi->blank_vp_y = -1;
1267 #endif /* HAVE_XF86VMODE */
1269 store_activate_time (si, si->screen_blanked_p);
1270 raise_window (si, False, False, False);
1272 #ifdef HAVE_XHPDISABLERESET
1273 if (si->locked_p && !hp_locked_p)
1275 XHPDisableReset (si->dpy); /* turn off C-Sh-Reset */
1280 #ifdef HAVE_VT_LOCKSWITCH
1282 lock_vt (si, True); /* turn off C-Alt-Fn */
1285 si->screen_blanked_p = True;
1286 si->last_wall_clock_time = 0;
1292 unblank_screen (saver_info *si)
1294 saver_preferences *p = &si->prefs;
1295 Bool unfade_p = (si->fading_possible_p && p->unfade_p);
1298 monitor_power_on (si);
1300 store_activate_time (si, True);
1301 reset_watchdog_timer (si, False);
1308 Window *current_windows = (Window *)
1309 calloc(sizeof(Window), si->nscreens);
1311 for (i = 0; i < si->nscreens; i++)
1313 saver_screen_info *ssi = &si->screens[i];
1314 current_windows[i] = ssi->screensaver_window;
1315 /* Ensure that the default background of the window is really black,
1316 not a pixmap or something. (This does not clear the window.) */
1317 XSetWindowBackground (si->dpy, ssi->screensaver_window,
1321 if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1324 XSync (si->dpy, False);
1325 XGrabServer (si->dpy); /* ############ DANGER! */
1326 XSync (si->dpy, False);
1328 /* Clear the stderr layer on each screen.
1330 for (i = 0; i < si->nscreens; i++)
1332 saver_screen_info *ssi = &si->screens[i];
1336 XUngrabServer (si->dpy);
1337 XSync (si->dpy, False); /* ###### (danger over) */
1340 fade_screens (si->dpy, 0, current_windows,
1341 p->fade_seconds/1000, p->fade_ticks,
1344 free(current_windows);
1345 current_windows = 0;
1347 if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1351 for (i = 0; i < si->nscreens; i++)
1353 saver_screen_info *ssi = &si->screens[i];
1356 Colormap c = DefaultColormapOfScreen (ssi->screen);
1357 /* avoid technicolor */
1358 XClearWindow (si->dpy, ssi->screensaver_window);
1359 if (c) XInstallColormap (si->dpy, c);
1361 XUnmapWindow (si->dpy, ssi->screensaver_window);
1366 /* If the focus window does has a non-default colormap, then install
1367 that colormap as well. (On SGIs, this will cause both the root map
1368 and the focus map to be installed simultaniously. It'd be nice to
1369 pick up the other colormaps that had been installed, too; perhaps
1370 XListInstalledColormaps could be used for that?)
1375 XGetInputFocus (si->dpy, &focus, &revert_to);
1376 if (focus && focus != PointerRoot && focus != None)
1378 XWindowAttributes xgwa;
1380 XGetWindowAttributes (si->dpy, focus, &xgwa);
1381 if (xgwa.colormap &&
1382 xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1383 XInstallColormap (si->dpy, xgwa.colormap);
1388 for (i = 0; i < si->nscreens; i++)
1390 saver_screen_info *ssi = &si->screens[i];
1391 kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1394 store_activate_time(si, False); /* store unblank time */
1396 ungrab_keyboard_and_mouse (si);
1397 restore_real_vroot (si);
1399 #ifdef HAVE_XHPDISABLERESET
1402 XHPEnableReset (si->dpy); /* turn C-Sh-Reset back on */
1403 hp_locked_p = False;
1407 #ifdef HAVE_VT_LOCKSWITCH
1408 lock_vt (si, False); /* turn C-Alt-Fn back on */
1411 /* Unmap the windows a second time, dammit -- just to avoid a race
1412 with the screen-grabbing hacks. (I'm not sure if this is really
1413 necessary; I'm stabbing in the dark now.)
1415 for (i = 0; i < si->nscreens; i++)
1416 XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1418 si->screen_blanked_p = False;
1419 si->last_wall_clock_time = 0;
1424 store_activate_time (saver_info *si, Bool use_last_p)
1426 static time_t last_time = 0;
1427 time_t now = ((use_last_p && last_time) ? last_time : time ((time_t) 0));
1428 CARD32 now32 = (CARD32) now;
1432 for (i = 0; i < si->nscreens; i++)
1434 saver_screen_info *ssi = &si->screens[i];
1435 if (!ssi->screensaver_window) continue;
1436 XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_TIME,
1437 XA_INTEGER, 32, PropModeReplace,
1438 (unsigned char *) &now32, 1);
1444 select_visual (saver_screen_info *ssi, const char *visual_name)
1446 saver_info *si = ssi->global;
1447 saver_preferences *p = &si->prefs;
1448 Bool install_cmap_p = p->install_cmap_p;
1449 Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1453 if (visual_name && *visual_name)
1455 if (!strcmp(visual_name, "default-i"))
1457 visual_name = "default";
1458 install_cmap_p = True;
1460 else if (!strcmp(visual_name, "default-n"))
1462 visual_name = "default";
1463 install_cmap_p = False;
1465 #ifdef DAEMON_USE_GL
1466 else if (!strcmp(visual_name, "gl") ||
1467 !strcmp(visual_name, "GL"))
1469 new_v = get_gl_visual (ssi->screen);
1470 if (!new_v && p->verbose_p)
1471 fprintf (stderr, "%s: no GL visuals.\n", progname);
1473 #endif /* DAEMON_USE_GL */
1476 new_v = get_visual (ssi->screen, visual_name, True, False);
1480 new_v = ssi->default_visual;
1485 if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1486 /* It's not the default visual, so we have no choice but to install. */
1487 install_cmap_p = True;
1489 ssi->install_cmap_p = install_cmap_p;
1492 ((ssi->current_visual != new_v) ||
1493 (install_cmap_p != was_installed_p)))
1495 Colormap old_c = ssi->cmap;
1496 Window old_w = ssi->screensaver_window;
1500 fprintf (stderr, "%s: switching to visual ", blurb());
1501 describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
1503 fprintf (stderr, "%s: from ", blurb());
1504 describe_visual (stderr, ssi->screen, ssi->current_visual,
1510 ssi->current_visual = new_v;
1511 ssi->current_depth = visual_depth(ssi->screen, new_v);
1513 ssi->screensaver_window = 0;
1515 initialize_screensaver_window_1 (ssi);
1517 /* stderr_overlay_window is a child of screensaver_window, so we need
1518 to destroy that as well (actually, we just need to invalidate and
1519 drop our pointers to it, but this will destroy it, which is ok so
1520 long as it happens before old_w itself is destroyed.) */
1523 raise_window (si, True, True, False);
1524 store_vroot_property (si->dpy,
1525 ssi->screensaver_window, ssi->screensaver_window);
1526 store_activate_time (si, True);
1530 /* Transfer the grabs from the old window to the new.
1531 Actually I think none of this is necessary, since we always
1532 hold our grabs on the root window, but I wrote this before
1533 re-discovering that...
1537 /* If we're destroying the window that holds our mouse grab,
1538 transfer the grab to the new window. (Grab the server while
1539 so doing, to avoid a race condition.)
1541 if (old_w == si->mouse_grab_window)
1543 XGrabServer (si->dpy); /* ############ DANGER! */
1545 grab_mouse (si, ssi->screensaver_window,
1549 XUngrabServer (si->dpy);
1550 XSync (si->dpy, False); /* ###### (danger over) */
1553 /* If we're destroying the window that holds our keyboard grab,
1554 transfer the grab to the new window. (Grab the server while
1555 so doing, to avoid a race condition.)
1557 if (old_w == si->keyboard_grab_window)
1559 XGrabServer (si->dpy); /* ############ DANGER! */
1561 grab_kbd(si, ssi->screensaver_window);
1562 XUngrabServer (si->dpy);
1563 XSync (si->dpy, False); /* ###### (danger over) */
1566 /* Now we can destroy this window without horking our grabs. */
1568 XDestroyWindow (si->dpy, old_w);
1571 fprintf (stderr, "%s: destroyed old saver window 0x%lx.\n",
1572 blurb(), (unsigned long) old_w);
1575 old_c != DefaultColormapOfScreen (ssi->screen) &&
1576 old_c != ssi->demo_cmap)
1577 XFreeColormap (si->dpy, old_c);
1586 #ifdef HAVE_VT_LOCKSWITCH
1588 lock_vt (saver_info *si, Bool lock_p)
1590 saver_preferences *p = &si->prefs;
1591 static Bool locked_p = False;
1592 const char *dev_console = "/dev/console";
1595 if (lock_p == locked_p)
1598 if (lock_p && !p->lock_vt_p)
1601 fd = open (dev_console, O_RDWR);
1605 sprintf (buf, "%s: couldn't %s VTs: %s", blurb(),
1606 (lock_p ? "lock" : "unlock"),
1608 #if 0 /* #### doesn't work yet, so don't bother complaining */
1614 if (ioctl (fd, (lock_p ? VT_LOCKSWITCH : VT_UNLOCKSWITCH)) == 0)
1619 fprintf (stderr, "%s: %s VTs\n", blurb(),
1620 (lock_p ? "locked" : "unlocked"));
1625 sprintf (buf, "%s: couldn't %s VTs: ioctl", blurb(),
1626 (lock_p ? "lock" : "unlock"));
1627 #if 0 /* #### doesn't work yet, so don't bother complaining */
1634 #endif /* HAVE_VT_LOCKSWITCH */