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 */
49 /* This file doesn't need the Xt headers, so stub these types out... */
51 #define XtAppContext void*
52 #define XrmDatabase void*
53 #define XtIntervalId void*
54 #define XtPointer void*
57 #include "xscreensaver.h"
62 extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
64 Atom XA_VROOT, XA_XSETROOT_ID;
65 Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
66 Atom XA_SCREENSAVER_TIME;
69 extern saver_info *global_si_kludge; /* I hate C so much... */
72 static void store_activate_time (saver_info *si, Bool use_last_p);
74 #define ALL_POINTER_EVENTS \
75 (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
76 LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
77 Button1MotionMask | Button2MotionMask | Button3MotionMask | \
78 Button4MotionMask | Button5MotionMask | ButtonMotionMask)
82 grab_string(int status)
86 case GrabSuccess: return "GrabSuccess";
87 case AlreadyGrabbed: return "AlreadyGrabbed";
88 case GrabInvalidTime: return "GrabInvalidTime";
89 case GrabNotViewable: return "GrabNotViewable";
90 case GrabFrozen: return "GrabFrozen";
94 sprintf(foo, "unknown status: %d", status);
101 grab_kbd(saver_info *si, Window w)
103 saver_preferences *p = &si->prefs;
104 int status = XGrabKeyboard (si->dpy, w, True,
105 /* I don't really understand Sync vs Async,
106 but these seem to work... */
107 GrabModeSync, GrabModeAsync,
109 if (status == GrabSuccess)
110 si->keyboard_grab_window = w;
113 fprintf(stderr, "%s: grabbing keyboard on 0x%x... %s.\n",
114 blurb(), (unsigned long) w, grab_string(status));
120 grab_mouse (saver_info *si, Window w, Cursor cursor)
122 saver_preferences *p = &si->prefs;
123 int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS,
124 GrabModeAsync, GrabModeAsync, w,
125 cursor, CurrentTime);
126 if (status == GrabSuccess)
127 si->mouse_grab_window = w;
130 fprintf(stderr, "%s: grabbing mouse on 0x%x... %s.\n",
131 blurb(), (unsigned long) w, grab_string(status));
137 ungrab_kbd(saver_info *si)
139 saver_preferences *p = &si->prefs;
140 XUngrabKeyboard(si->dpy, CurrentTime);
142 fprintf(stderr, "%s: ungrabbing keyboard (was 0x%x).\n", blurb(),
143 (unsigned long) si->keyboard_grab_window);
144 si->keyboard_grab_window = 0;
149 ungrab_mouse(saver_info *si)
151 saver_preferences *p = &si->prefs;
152 XUngrabPointer(si->dpy, CurrentTime);
154 fprintf(stderr, "%s: ungrabbing mouse (was 0x%x).\n", blurb(),
155 (unsigned long) si->mouse_grab_window);
156 si->mouse_grab_window = 0;
161 grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
163 Status mstatus, kstatus;
164 XSync (si->dpy, False);
166 kstatus = grab_kbd (si, window);
167 if (kstatus != GrabSuccess)
168 { /* try again in a second */
170 kstatus = grab_kbd (si, window);
171 if (kstatus != GrabSuccess)
172 fprintf (stderr, "%s: couldn't grab keyboard! (%s)\n",
173 blurb(), grab_string(kstatus));
176 mstatus = grab_mouse (si, window, cursor);
177 if (mstatus != GrabSuccess)
178 { /* try again in a second */
180 mstatus = grab_mouse (si, window, cursor);
181 if (mstatus != GrabSuccess)
182 fprintf (stderr, "%s: couldn't grab pointer! (%s)\n",
183 blurb(), grab_string(mstatus));
186 return (kstatus == GrabSuccess ||
187 mstatus == GrabSuccess);
191 ungrab_keyboard_and_mouse (saver_info *si)
199 move_mouse_grab (saver_info *si, Window to, Cursor cursor)
201 Window old = si->mouse_grab_window;
204 return grab_mouse (si, to, cursor);
207 saver_preferences *p = &si->prefs;
210 XSync (si->dpy, False);
211 XGrabServer (si->dpy); /* ############ DANGER! */
212 XSync (si->dpy, False);
215 fprintf(stderr, "%s: grabbing server...\n", blurb());
218 status = grab_mouse (si, to, cursor);
220 if (status != GrabSuccess) /* Augh! */
222 sleep (1); /* Note dramatic evil of sleeping
223 with server grabbed. */
224 XSync (si->dpy, False);
225 status = grab_mouse (si, to, cursor);
228 if (status != GrabSuccess) /* Augh! Try to get the old one back... */
229 grab_mouse (si, to, cursor);
231 XUngrabServer (si->dpy);
232 XSync (si->dpy, False); /* ###### (danger over) */
235 fprintf(stderr, "%s: ungrabbing server.\n", blurb());
242 /* Prints an error message to stderr and returns True if there is another
243 xscreensaver running already. Silently returns False otherwise. */
245 ensure_no_screensaver_running (Display *dpy, Screen *screen)
249 Window root = RootWindowOfScreen (screen);
250 Window root2, parent, *kids;
252 XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
254 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
260 for (i = 0; i < nkids; i++)
264 unsigned long nitems, bytesafter;
267 if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
268 False, XA_STRING, &type, &format, &nitems,
269 &bytesafter, (unsigned char **) &version)
274 if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
275 False, XA_STRING, &type, &format, &nitems,
276 &bytesafter, (unsigned char **) &id)
282 "%s: already running on display %s (window 0x%x)\n from process %s.\n",
283 blurb(), DisplayString (dpy), (int) kids [i], id);
288 if (kids) XFree ((char *) kids);
290 XSetErrorHandler (old_handler);
296 /* Virtual-root hackery */
299 ERROR! You must not include vroot.h in this file.
303 store_vroot_property (Display *dpy, Window win, Window value)
308 "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(),
310 (win == screensaver_window ? "ScreenSaver" :
311 (win == real_vroot ? "VRoot" :
312 (win == real_vroot_value ? "Vroot_value" : "???"))),
314 (value == screensaver_window ? "ScreenSaver" :
315 (value == real_vroot ? "VRoot" :
316 (value == real_vroot_value ? "Vroot_value" : "???"))));
318 XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
319 (unsigned char *) &value, 1);
323 remove_vroot_property (Display *dpy, Window win)
327 fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win,
328 (win == screensaver_window ? "ScreenSaver" :
329 (win == real_vroot ? "VRoot" :
330 (win == real_vroot_value ? "Vroot_value" : "???"))));
332 XDeleteProperty (dpy, win, XA_VROOT);
337 kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
341 unsigned long nitems, bytesafter;
344 /* If the user has been using xv or xsetroot as a screensaver (to display
345 an image on the screensaver window, as a kind of slideshow) then the
346 pixmap and its associated color cells have been put in RetainPermanent
347 CloseDown mode. Since we're not destroying the xscreensaver window,
348 but merely unmapping it, we need to free these resources or those
349 colormap cells will stay allocated while the screensaver is off. (We
350 could just delete the screensaver window and recreate it later, but
351 that could cause other problems.) This code does an atomic read-and-
352 delete of the _XSETROOT_ID property, and if it held a pixmap, then we
353 cause the RetainPermanent resources of the client which created it
354 (and which no longer exists) to be freed.
356 if (XGetWindowProperty (dpy, window, XA_XSETROOT_ID, 0, 1,
357 True, AnyPropertyType, &type, &format, &nitems,
358 &bytesafter, (unsigned char **) &dataP)
362 if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
363 nitems == 1 && bytesafter == 0)
366 fprintf (stderr, "%s: destroying xsetroot data (0x%lX).\n",
368 XKillClient (dpy, *dataP);
371 fprintf (stderr, "%s: deleted unrecognised _XSETROOT_ID property: \n\
372 %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
373 blurb(), (unsigned long) dataP, (dataP ? *dataP : 0), type,
374 format, nitems, bytesafter);
379 static void handle_signals (saver_info *si, Bool on_p);
382 save_real_vroot (saver_screen_info *ssi)
384 saver_info *si = ssi->global;
385 Display *dpy = si->dpy;
386 Screen *screen = ssi->screen;
388 Window root = RootWindowOfScreen (screen);
389 Window root2, parent, *kids;
391 XErrorHandler old_handler;
393 /* It's possible that a window might be deleted between our call to
394 XQueryTree() and our call to XGetWindowProperty(). Don't die if
395 that happens (but just ignore that window, it's not the one we're
396 interested in anyway.)
399 old_handler = XSetErrorHandler (BadWindow_ehandler);
403 ssi->real_vroot_value = 0;
404 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
410 for (i = 0; i < nkids; i++)
414 unsigned long nitems, bytesafter;
417 if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
418 &type, &format, &nitems, &bytesafter,
419 (unsigned char **) &vrootP)
426 if (*vrootP == ssi->screensaver_window) abort ();
428 "%s: more than one virtual root window found (0x%x and 0x%x).\n",
429 blurb(), (int) ssi->real_vroot, (int) kids [i]);
432 ssi->real_vroot = kids [i];
433 ssi->real_vroot_value = *vrootP;
437 XSetErrorHandler (old_handler);
442 handle_signals (si, True);
443 remove_vroot_property (si->dpy, ssi->real_vroot);
447 XFree ((char *) kids);
452 restore_real_vroot_2 (saver_screen_info *ssi)
454 saver_info *si = ssi->global;
455 saver_preferences *p = &si->prefs;
456 if (p->verbose_p && ssi->real_vroot)
458 "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
459 blurb(), (unsigned long) ssi->real_vroot);
460 remove_vroot_property (si->dpy, ssi->screensaver_window);
463 store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value);
465 ssi->real_vroot_value = 0;
466 /* make sure the property change gets there before this process
467 terminates! We might be doing this because we have intercepted
468 SIGTERM or something. */
469 XSync (si->dpy, False);
476 restore_real_vroot_1 (saver_info *si)
479 Bool did_any = False;
480 for (i = 0; i < si->nscreens; i++)
482 saver_screen_info *ssi = &si->screens[i];
483 if (restore_real_vroot_2 (ssi))
490 restore_real_vroot (saver_info *si)
492 if (restore_real_vroot_1 (si))
493 handle_signals (si, False);
497 /* Signal hackery to ensure that the vroot doesn't get left in an
502 signal_name(int signal)
505 case SIGHUP: return "SIGHUP";
506 case SIGINT: return "SIGINT";
507 case SIGQUIT: return "SIGQUIT";
508 case SIGILL: return "SIGILL";
509 case SIGTRAP: return "SIGTRAP";
511 case SIGABRT: return "SIGABRT";
513 case SIGFPE: return "SIGFPE";
514 case SIGKILL: return "SIGKILL";
515 case SIGBUS: return "SIGBUS";
516 case SIGSEGV: return "SIGSEGV";
517 case SIGPIPE: return "SIGPIPE";
518 case SIGALRM: return "SIGALRM";
519 case SIGTERM: return "SIGTERM";
521 case SIGSTOP: return "SIGSTOP";
524 case SIGCONT: return "SIGCONT";
527 case SIGUSR1: return "SIGUSR1";
530 case SIGUSR2: return "SIGUSR2";
533 case SIGEMT: return "SIGEMT";
536 case SIGSYS: return "SIGSYS";
539 case SIGCHLD: return "SIGCHLD";
542 case SIGPWR: return "SIGPWR";
545 case SIGWINCH: return "SIGWINCH";
548 case SIGURG: return "SIGURG";
551 case SIGIO: return "SIGIO";
554 case SIGVTALRM: return "SIGVTALRM";
557 case SIGXCPU: return "SIGXCPU";
560 case SIGXFSZ: return "SIGXFSZ";
563 case SIGDANGER: return "SIGDANGER";
568 sprintf(buf, "signal %d\n", signal);
577 restore_real_vroot_handler (int sig)
579 saver_info *si = global_si_kludge; /* I hate C so much... */
581 signal (sig, SIG_DFL);
582 if (restore_real_vroot_1 (si))
583 fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
584 blurb(), signal_name(sig));
585 kill (getpid (), sig);
589 catch_signal (saver_info *si, int sig, Bool on_p)
592 signal (sig, SIG_DFL);
595 if (((long) signal (sig, restore_real_vroot_handler)) == -1L)
598 sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
600 saver_exit (si, 1, 0);
606 handle_signals (saver_info *si, Bool on_p)
609 if (on_p) fprintf (stderr, "handling signals\n");
610 else fprintf (stderr, "unhandling signals\n");
613 catch_signal (si, SIGHUP, on_p);
614 catch_signal (si, SIGINT, on_p);
615 catch_signal (si, SIGQUIT, on_p);
616 catch_signal (si, SIGILL, on_p);
617 catch_signal (si, SIGTRAP, on_p);
618 catch_signal (si, SIGIOT, on_p);
619 catch_signal (si, SIGABRT, on_p);
621 catch_signal (si, SIGEMT, on_p);
623 catch_signal (si, SIGFPE, on_p);
624 catch_signal (si, SIGBUS, on_p);
625 catch_signal (si, SIGSEGV, on_p);
627 catch_signal (si, SIGSYS, on_p);
629 catch_signal (si, SIGTERM, on_p);
631 catch_signal (si, SIGXCPU, on_p);
634 catch_signal (si, SIGXFSZ, on_p);
637 catch_signal (si, SIGDANGER, on_p);
642 saver_exit (saver_info *si, int status, const char *dump_core_reason)
644 saver_preferences *p = &si->prefs;
645 static Bool exiting = False;
654 vrs = restore_real_vroot_1 (si);
655 emergency_kill_subproc (si);
657 if (vrs && (p->verbose_p || status != 0))
658 fprintf (real_stderr, "%s: vroot restored, exiting.\n", blurb());
659 else if (p->verbose_p)
660 fprintf (real_stderr, "%s: no vroot to restore; exiting.\n", blurb());
664 #ifdef VMS /* on VMS, 1 is the "normal" exit code instead of 0. */
665 if (status == 0) status = 1;
666 else if (status == 1) status = -1;
669 bugp = !!dump_core_reason;
671 if (si->prefs.debug_p && !dump_core_reason)
672 dump_core_reason = "because of -debug";
674 if (dump_core_reason)
676 /* Note that the Linux man page for setuid() says If uid is
677 different from the old effective uid, the process will be
678 forbidden from leaving core dumps.
680 char cwd[4096]; /* should really be PATH_MAX, but who cares. */
682 fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
687 "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
688 "\t\tfor bug reporting information.\n\n",
691 # if defined(HAVE_GETCWD)
692 if (!getcwd (cwd, sizeof(cwd)))
693 # elif defined(HAVE_GETWD)
696 strcpy(cwd, "unknown.");
698 fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
699 describe_uids (si, real_stderr);
701 /* Do this to drop a core file, so that we can get a stack trace. */
709 /* Managing the actual screensaver window */
712 window_exists_p (Display *dpy, Window window)
714 XErrorHandler old_handler;
715 XWindowAttributes xgwa;
717 old_handler = XSetErrorHandler (BadWindow_ehandler);
718 XGetWindowAttributes (dpy, window, &xgwa);
720 XSetErrorHandler (old_handler);
721 return (xgwa.screen != 0);
725 store_saver_id (saver_screen_info *ssi)
727 XClassHint class_hints;
728 saver_info *si = ssi->global;
729 unsigned long pid = (unsigned long) getpid ();
731 struct passwd *p = getpwuid (getuid ());
732 const char *name, *host;
735 /* First store the name and class on the window.
737 class_hints.res_name = progname;
738 class_hints.res_class = progclass;
739 XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
740 XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
742 /* Then store the xscreensaver version number.
744 XChangeProperty (si->dpy, ssi->screensaver_window,
745 XA_SCREENSAVER_VERSION,
746 XA_STRING, 8, PropModeReplace,
747 (unsigned char *) si->version,
748 strlen (si->version));
750 /* Now store the XSCREENSAVER_ID property, that says what user and host
751 xscreensaver is running as.
754 if (p && p->pw_name && *p->pw_name)
758 sprintf (buf, "%lu", (unsigned long) p->pw_uid);
764 # if defined(HAVE_UNAME)
767 if (uname (&uts) < 0)
773 host = getenv("SYS$NODE");
774 # else /* !HAVE_UNAME && !VMS */
776 # endif /* !HAVE_UNAME && !VMS */
778 id = (char *) malloc (strlen(name) + strlen(host) + 50);
779 sprintf (id, "%lu (%s@%s)", pid, name, host);
781 XChangeProperty (si->dpy, ssi->screensaver_window,
782 XA_SCREENSAVER_ID, XA_STRING,
784 (unsigned char *) id, strlen (id));
789 /* Returns the area of the screen which the xscreensaver window should cover.
790 Normally this is the whole screen, but if the X server's root window is
791 actually larger than the monitor's displayable area, then we want to
792 operate in the currently-visible portion of the desktop instead.
795 get_screen_viewport (saver_screen_info *ssi,
796 int *x_ret, int *y_ret,
797 int *w_ret, int *h_ret,
800 int w = WidthOfScreen (ssi->screen);
801 int h = HeightOfScreen (ssi->screen);
803 #ifdef HAVE_XF86VMODE
804 saver_info *si = ssi->global;
805 int screen_no = screen_number (ssi->screen);
806 int op, event, error;
808 XF86VidModeModeLine ml;
811 /* Check for Xinerama first, because the VidModeExtension is broken
812 when Xinerama is present. Wheee!
815 if (!XQueryExtension (si->dpy, "XINERAMA", &op, &event, &error) &&
816 XF86VidModeQueryExtension (si->dpy, &event, &error) &&
817 XF86VidModeGetModeLine (si->dpy, screen_no, &dot, &ml) &&
818 XF86VidModeGetViewPort (si->dpy, screen_no, &x, &y))
823 *w_ret = ml.hdisplay;
824 *h_ret = ml.vdisplay;
826 if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
827 /* There is no viewport -- the screen does not scroll. */
831 /* Apparently some versions of XFree86 return nonsense here!
832 I've had reports of 1024x768 viewports at -1936862040, -1953705044.
833 So, sanity-check the values and give up if they are out of range.
835 if (*x_ret < 0 || *x_ret >= w ||
836 *y_ret < 0 || *y_ret >= h ||
837 *w_ret <= 0 || *w_ret > w ||
838 *h_ret <= 0 || *h_ret > h)
840 static int warned_once = 0;
843 fprintf (stderr, "\n"
844 "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
845 "%s: The XVidMode server extension is returning nonsense.\n"
846 "%s: Please report this bug to your X server vendor.\n\n",
847 blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
859 sprintf (msg, "%s: vp is %dx%d+%d+%d",
860 blurb(), *w_ret, *h_ret, *x_ret, *y_ret);
863 /* Apparently, though the server stores the X position in increments of
864 1 pixel, it will only make changes to the *display* in some other
865 increment. With XF86_SVGA on a Thinkpad, the display only updates
866 in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
867 pixels in 16-bit mode. I don't know what it does in 24- and 32-bit
868 mode, because I don't have enough video memory to find out.
870 I consider it a bug that XF86VidModeGetViewPort() is telling me the
871 server's *target* scroll position rather than the server's *actual*
872 scroll position. David Dawes agrees, and says they may fix this in
873 XFree86 4.0, but it's notrivial.
875 He also confirms that this behavior is server-dependent, so the
876 actual scroll position cannot be reliably determined by the client.
877 So... that means the only solution is to provide a ``sandbox''
878 around the blackout window -- we make the window be up to N pixels
879 larger than the viewport on both the left and right sides. That
880 means some part of the outer edges of each hack might not be
881 visible, but screw it.
883 I'm going to guess that 16 pixels is enough, and that the Y dimension
884 doesn't have this problem.
886 The drawback of doing this, of course, is that some of the screenhacks
887 will still look pretty stupid -- for example, "slidescreen" will cut
888 off the left and right edges of the grid, etc.
891 if (x > 0 && x < w - ml.hdisplay) /* not at left edge or right edge */
893 /* Round X position down to next lower multiple of FUDGE.
894 Increase width by 2*FUDGE in case some server rounds up.
896 *x_ret = ((x - 1) / FUDGE) * FUDGE;
897 *w_ret += (FUDGE * 2);
903 *w_ret != ml.hdisplay ||
904 *h_ret != ml.vdisplay)
905 sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
906 *w_ret, *h_ret, *x_ret, *y_ret);
909 fprintf (stderr, "%s.\n", msg);
914 #endif /* HAVE_XF86VMODE */
924 initialize_screensaver_window_1 (saver_screen_info *ssi)
926 saver_info *si = ssi->global;
927 saver_preferences *p = &si->prefs;
928 Bool install_cmap_p = ssi->install_cmap_p; /* not p->install_cmap_p */
930 /* This resets the screensaver window as fully as possible, since there's
931 no way of knowing what some random client may have done to us in the
932 meantime. We could just destroy and recreate the window, but that has
933 its own set of problems...
936 XSetWindowAttributes attrs;
937 unsigned long attrmask;
938 int x, y, width, height;
939 static Bool printed_visual_info = False; /* only print the message once. */
941 get_screen_viewport (ssi, &x, &y, &width, &height,
942 (p->verbose_p && !si->screen_blanked_p));
944 black.red = black.green = black.blue = 0;
946 if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
949 if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
950 /* It's not the default visual, so we have no choice but to install. */
951 install_cmap_p = True;
957 ssi->cmap = XCreateColormap (si->dpy,
958 RootWindowOfScreen (ssi->screen),
959 ssi->current_visual, AllocNone);
960 if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
961 ssi->black_pixel = black.pixel;
966 Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
969 XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
970 if (ssi->cmap != ssi->demo_cmap &&
971 ssi->cmap != def_cmap)
972 XFreeColormap (si->dpy, ssi->cmap);
974 ssi->cmap = def_cmap;
975 ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
978 attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
979 CWBackPixel | CWBackingPixel | CWBorderPixel);
980 attrs.override_redirect = True;
982 /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
983 actually be reading these events during normal operation; but we still
984 need to see Button events for demo-mode to work properly.
986 attrs.event_mask = (KeyPressMask | KeyReleaseMask |
987 ButtonPressMask | ButtonReleaseMask |
990 attrs.backing_store = NotUseful;
991 attrs.colormap = ssi->cmap;
992 attrs.background_pixel = ssi->black_pixel;
993 attrs.backing_pixel = ssi->black_pixel;
994 attrs.border_pixel = ssi->black_pixel;
996 if (p->debug_p) width = width / 2;
998 if (!p->verbose_p || printed_visual_info)
1000 else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
1002 fprintf (stderr, "%s: using default visual ", blurb());
1003 describe_visual (stderr, ssi->screen, ssi->current_visual,
1008 fprintf (stderr, "%s: using visual: ", blurb());
1009 describe_visual (stderr, ssi->screen, ssi->current_visual,
1011 fprintf (stderr, "%s: default visual: ", blurb());
1012 describe_visual (stderr, ssi->screen,
1013 DefaultVisualOfScreen (ssi->screen),
1014 ssi->install_cmap_p);
1016 printed_visual_info = True;
1018 #ifdef HAVE_MIT_SAVER_EXTENSION
1019 if (si->using_mit_saver_extension)
1021 XScreenSaverInfo *info;
1022 Window root = RootWindowOfScreen (ssi->screen);
1025 /* This call sets the server screensaver timeouts to what we think
1026 they should be (based on the resources and args xscreensaver was
1027 started with.) It's important that we do this to sync back up
1028 with the server - if we have turned on prematurely, as by an
1029 ACTIVATE ClientMessage, then the server may decide to activate
1030 the screensaver while it's already active. That's ok for us,
1031 since we would know to ignore that ScreenSaverActivate event,
1032 but a side effect of this would be that the server would map its
1033 saver window (which we then hide again right away) meaning that
1034 the bits currently on the screen get blown away. Ugly. */
1036 /* #### Ok, that doesn't work - when we tell the server that the
1037 screensaver is "off" it sends us a Deactivate event, which is
1038 sensible... but causes the saver to never come on. Hmm. */
1039 disable_builtin_screensaver (si, True);
1043 /* #### The MIT-SCREEN-SAVER extension gives us access to the
1044 window that the server itself uses for saving the screen.
1045 However, using this window in any way, in particular, calling
1046 XScreenSaverSetAttributes() as below, tends to make the X server
1047 crash. So fuck it, let's try and get along without using it...
1049 It's also inconvenient to use this window because it doesn't
1050 always exist (though the ID is constant.) So to use this
1051 window, we'd have to reimplement the ACTIVATE ClientMessage to
1052 tell the *server* to tell *us* to turn on, to cause the window
1053 to get created at the right time. Gag. */
1054 XScreenSaverSetAttributes (si->dpy, root,
1055 0, 0, width, height, 0,
1056 current_depth, InputOutput, visual,
1058 XSync (si->dpy, False);
1061 info = XScreenSaverAllocInfo ();
1062 XScreenSaverQueryInfo (si->dpy, root, info);
1063 ssi->server_mit_saver_window = info->window;
1064 if (! ssi->server_mit_saver_window) abort ();
1067 #endif /* HAVE_MIT_SAVER_EXTENSION */
1069 if (ssi->screensaver_window)
1071 XWindowChanges changes;
1072 unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1075 changes.width = width;
1076 changes.height = height;
1077 changes.border_width = 0;
1079 XConfigureWindow (si->dpy, ssi->screensaver_window,
1080 changesmask, &changes);
1081 XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
1086 ssi->screensaver_window =
1087 XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
1088 x, y, width, height,
1089 0, ssi->current_depth, InputOutput,
1090 ssi->current_visual, attrmask, &attrs);
1093 store_activate_time(si, True);
1095 fprintf (stderr, "%s: saver window is 0x%lx.\n",
1096 blurb(), (unsigned long) ssi->screensaver_window);
1100 store_saver_id (ssi);
1105 bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
1107 BlackPixelOfScreen (ssi->screen),
1108 BlackPixelOfScreen (ssi->screen),
1110 ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
1112 XFreePixmap (si->dpy, bit);
1115 XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
1118 XUndefineCursor (si->dpy, ssi->screensaver_window);
1120 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1124 initialize_screensaver_window (saver_info *si)
1127 for (i = 0; i < si->nscreens; i++)
1128 initialize_screensaver_window_1 (&si->screens[i]);
1133 raise_window (saver_info *si,
1134 Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
1136 saver_preferences *p = &si->prefs;
1140 inhibit_fade = True;
1142 if (si->emergency_lock_p)
1143 inhibit_fade = True;
1146 initialize_screensaver_window (si);
1148 reset_watchdog_timer (si, True);
1150 if (p->fade_p && si->fading_possible_p && !inhibit_fade)
1152 Window *current_windows = (Window *)
1153 calloc(sizeof(Window), si->nscreens);
1154 Colormap *current_maps = (Colormap *)
1155 calloc(sizeof(Colormap), si->nscreens);
1157 for (i = 0; i < si->nscreens; i++)
1159 saver_screen_info *ssi = &si->screens[i];
1160 current_windows[i] = ssi->screensaver_window;
1161 current_maps[i] = (between_hacks_p
1163 : DefaultColormapOfScreen (ssi->screen));
1164 /* Ensure that the default background of the window is really black,
1165 not a pixmap or something. (This does not clear the window.) */
1166 XSetWindowBackground (si->dpy, ssi->screensaver_window,
1170 if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1172 XGrabServer (si->dpy); /* ############ DANGER! */
1174 /* Clear the stderr layer on each screen.
1177 for (i = 0; i < si->nscreens; i++)
1179 saver_screen_info *ssi = &si->screens[i];
1180 if (ssi->stderr_overlay_window)
1181 /* Do this before the fade, since the stderr cmap won't fade
1182 even if we uninstall it (beats me...) */
1186 /* Note! The server is grabbed, and this will take several seconds
1188 fade_screens (si->dpy, current_maps, current_windows,
1189 p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1192 free(current_windows);
1194 current_windows = 0;
1196 if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1198 #ifdef HAVE_MIT_SAVER_EXTENSION
1199 for (i = 0; i < si->nscreens; i++)
1201 saver_screen_info *ssi = &si->screens[i];
1202 if (ssi->server_mit_saver_window &&
1203 window_exists_p (si->dpy, ssi->server_mit_saver_window))
1204 XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1206 #endif /* HAVE_MIT_SAVER_EXTENSION */
1208 XUngrabServer (si->dpy);
1209 XSync (si->dpy, False); /* ###### (danger over) */
1213 for (i = 0; i < si->nscreens; i++)
1215 saver_screen_info *ssi = &si->screens[i];
1217 XClearWindow (si->dpy, ssi->screensaver_window);
1218 if (!dont_clear || ssi->stderr_overlay_window)
1220 XMapRaised (si->dpy, ssi->screensaver_window);
1221 #ifdef HAVE_MIT_SAVER_EXTENSION
1222 if (ssi->server_mit_saver_window &&
1223 window_exists_p (si->dpy, ssi->server_mit_saver_window))
1224 XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1225 #endif /* HAVE_MIT_SAVER_EXTENSION */
1229 for (i = 0; i < si->nscreens; i++)
1231 saver_screen_info *ssi = &si->screens[i];
1233 XInstallColormap (si->dpy, ssi->cmap);
1238 blank_screen (saver_info *si)
1243 /* Note: we do our grabs on the root window, not on the screensaver window.
1244 If we grabbed on the saver window, then the demo mode and lock dialog
1245 boxes wouldn't get any events.
1247 ok = grab_keyboard_and_mouse (si,
1248 /*si->screens[0].screensaver_window,*/
1249 RootWindowOfScreen(si->screens[0].screen),
1252 : si->screens[0].cursor));
1255 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1256 /* If we're using a server extension, then failure to get a grab is
1257 not a big deal -- even without the grab, we will still be able
1258 to un-blank when there is user activity, since the server will
1265 for (i = 0; i < si->nscreens; i++)
1267 saver_screen_info *ssi = &si->screens[i];
1269 save_real_vroot (ssi);
1270 store_vroot_property (si->dpy,
1271 ssi->screensaver_window,
1272 ssi->screensaver_window);
1274 #ifdef HAVE_XF86VMODE
1277 if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
1278 !XF86VidModeGetViewPort (si->dpy, i,
1281 ssi->blank_vp_x = ssi->blank_vp_y = -1;
1283 #endif /* HAVE_XF86VMODE */
1285 store_activate_time (si, si->screen_blanked_p);
1286 raise_window (si, False, False, False);
1288 si->screen_blanked_p = True;
1289 si->last_wall_clock_time = 0;
1295 unblank_screen (saver_info *si)
1297 saver_preferences *p = &si->prefs;
1298 Bool unfade_p = (si->fading_possible_p && p->unfade_p);
1301 monitor_power_on (si);
1303 store_activate_time (si, True);
1304 reset_watchdog_timer (si, False);
1311 Window *current_windows = (Window *)
1312 calloc(sizeof(Window), si->nscreens);
1314 for (i = 0; i < si->nscreens; i++)
1316 saver_screen_info *ssi = &si->screens[i];
1317 current_windows[i] = ssi->screensaver_window;
1318 /* Ensure that the default background of the window is really black,
1319 not a pixmap or something. (This does not clear the window.) */
1320 XSetWindowBackground (si->dpy, ssi->screensaver_window,
1324 if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1327 XSync (si->dpy, False);
1328 XGrabServer (si->dpy); /* ############ DANGER! */
1329 XSync (si->dpy, False);
1331 /* Clear the stderr layer on each screen.
1333 for (i = 0; i < si->nscreens; i++)
1335 saver_screen_info *ssi = &si->screens[i];
1339 XUngrabServer (si->dpy);
1340 XSync (si->dpy, False); /* ###### (danger over) */
1343 fade_screens (si->dpy, 0, current_windows,
1344 p->fade_seconds/1000, p->fade_ticks,
1347 free(current_windows);
1348 current_windows = 0;
1350 if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1354 for (i = 0; i < si->nscreens; i++)
1356 saver_screen_info *ssi = &si->screens[i];
1359 Colormap c = DefaultColormapOfScreen (ssi->screen);
1360 /* avoid technicolor */
1361 XClearWindow (si->dpy, ssi->screensaver_window);
1362 if (c) XInstallColormap (si->dpy, c);
1364 XUnmapWindow (si->dpy, ssi->screensaver_window);
1369 /* If the focus window does has a non-default colormap, then install
1370 that colormap as well. (On SGIs, this will cause both the root map
1371 and the focus map to be installed simultaniously. It'd be nice to
1372 pick up the other colormaps that had been installed, too; perhaps
1373 XListInstalledColormaps could be used for that?)
1378 XGetInputFocus (si->dpy, &focus, &revert_to);
1379 if (focus && focus != PointerRoot && focus != None)
1381 XWindowAttributes xgwa;
1383 XGetWindowAttributes (si->dpy, focus, &xgwa);
1384 if (xgwa.colormap &&
1385 xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1386 XInstallColormap (si->dpy, xgwa.colormap);
1391 for (i = 0; i < si->nscreens; i++)
1393 saver_screen_info *ssi = &si->screens[i];
1394 kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1397 store_activate_time(si, False); /* store unblank time */
1399 ungrab_keyboard_and_mouse (si);
1400 restore_real_vroot (si);
1402 /* Unmap the windows a second time, dammit -- just to avoid a race
1403 with the screen-grabbing hacks. (I'm not sure if this is really
1404 necessary; I'm stabbing in the dark now.)
1406 for (i = 0; i < si->nscreens; i++)
1407 XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1409 si->screen_blanked_p = False;
1410 si->last_wall_clock_time = 0;
1415 store_activate_time (saver_info *si, Bool use_last_p)
1417 static time_t last_time = 0;
1418 time_t now = ((use_last_p && last_time) ? last_time : time ((time_t) 0));
1419 CARD32 now32 = (CARD32) now;
1423 for (i = 0; i < si->nscreens; i++)
1425 saver_screen_info *ssi = &si->screens[i];
1426 if (!ssi->screensaver_window) continue;
1427 XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_TIME,
1428 XA_INTEGER, 32, PropModeReplace,
1429 (unsigned char *) &now32, 1);
1435 select_visual (saver_screen_info *ssi, const char *visual_name)
1437 saver_info *si = ssi->global;
1438 saver_preferences *p = &si->prefs;
1439 Bool install_cmap_p = p->install_cmap_p;
1440 Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1444 if (visual_name && *visual_name)
1446 if (!strcmp(visual_name, "default-i"))
1448 visual_name = "default";
1449 install_cmap_p = True;
1451 else if (!strcmp(visual_name, "default-n"))
1453 visual_name = "default";
1454 install_cmap_p = False;
1456 #ifdef DAEMON_USE_GL
1457 else if (!strcmp(visual_name, "gl") ||
1458 !strcmp(visual_name, "GL"))
1460 new_v = get_gl_visual (ssi->screen);
1461 if (!new_v && p->verbose_p)
1462 fprintf (stderr, "%s: no GL visuals.\n", progname);
1464 #endif /* DAEMON_USE_GL */
1467 new_v = get_visual (ssi->screen, visual_name, True, False);
1471 new_v = ssi->default_visual;
1476 if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1477 /* It's not the default visual, so we have no choice but to install. */
1478 install_cmap_p = True;
1480 ssi->install_cmap_p = install_cmap_p;
1483 ((ssi->current_visual != new_v) ||
1484 (install_cmap_p != was_installed_p)))
1486 Colormap old_c = ssi->cmap;
1487 Window old_w = ssi->screensaver_window;
1491 fprintf (stderr, "%s: switching to visual ", blurb());
1492 describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
1494 fprintf (stderr, "%s: from ", blurb());
1495 describe_visual (stderr, ssi->screen, ssi->current_visual,
1501 ssi->current_visual = new_v;
1502 ssi->current_depth = visual_depth(ssi->screen, new_v);
1504 ssi->screensaver_window = 0;
1506 initialize_screensaver_window_1 (ssi);
1508 /* stderr_overlay_window is a child of screensaver_window, so we need
1509 to destroy that as well (actually, we just need to invalidate and
1510 drop our pointers to it, but this will destroy it, which is ok so
1511 long as it happens before old_w itself is destroyed.) */
1514 raise_window (si, True, True, False);
1515 store_vroot_property (si->dpy,
1516 ssi->screensaver_window, ssi->screensaver_window);
1517 store_activate_time (si, True);
1521 /* Transfer the grabs from the old window to the new.
1522 Actually I think none of this is necessary, since we always
1523 hold our grabs on the root window, but I wrote this before
1524 re-discovering that...
1528 /* If we're destroying the window that holds our mouse grab,
1529 transfer the grab to the new window. (Grab the server while
1530 so doing, to avoid a race condition.)
1532 if (old_w == si->mouse_grab_window)
1534 XGrabServer (si->dpy); /* ############ DANGER! */
1536 grab_mouse (si, ssi->screensaver_window,
1540 XUngrabServer (si->dpy);
1541 XSync (si->dpy, False); /* ###### (danger over) */
1544 /* If we're destroying the window that holds our keyboard grab,
1545 transfer the grab to the new window. (Grab the server while
1546 so doing, to avoid a race condition.)
1548 if (old_w == si->keyboard_grab_window)
1550 XGrabServer (si->dpy); /* ############ DANGER! */
1552 grab_kbd(si, ssi->screensaver_window);
1553 XUngrabServer (si->dpy);
1554 XSync (si->dpy, False); /* ###### (danger over) */
1557 /* Now we can destroy this window without horking our grabs. */
1559 XDestroyWindow (si->dpy, old_w);
1562 fprintf (stderr, "%s: destroyed old saver window 0x%lx.\n",
1563 blurb(), (unsigned long) old_w);
1566 old_c != DefaultColormapOfScreen (ssi->screen) &&
1567 old_c != ssi->demo_cmap)
1568 XFreeColormap (si->dpy, old_c);