1 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
2 * xscreensaver, Copyright (c) 1991-2001 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_STATUS;
69 extern saver_info *global_si_kludge; /* I hate C so much... */
72 #define ALL_POINTER_EVENTS \
73 (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
74 LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | \
75 Button1MotionMask | Button2MotionMask | Button3MotionMask | \
76 Button4MotionMask | Button5MotionMask | ButtonMotionMask)
80 grab_string(int status)
84 case GrabSuccess: return "GrabSuccess";
85 case AlreadyGrabbed: return "AlreadyGrabbed";
86 case GrabInvalidTime: return "GrabInvalidTime";
87 case GrabNotViewable: return "GrabNotViewable";
88 case GrabFrozen: return "GrabFrozen";
92 sprintf(foo, "unknown status: %d", status);
99 grab_kbd(saver_info *si, Window w)
101 saver_preferences *p = &si->prefs;
102 int status = XGrabKeyboard (si->dpy, w, True,
103 /* I don't really understand Sync vs Async,
104 but these seem to work... */
105 GrabModeSync, GrabModeAsync,
107 if (status == GrabSuccess)
108 si->keyboard_grab_window = w;
111 fprintf(stderr, "%s: grabbing keyboard on 0x%x... %s.\n",
112 blurb(), (unsigned long) w, grab_string(status));
118 grab_mouse (saver_info *si, Window w, Cursor cursor)
120 saver_preferences *p = &si->prefs;
121 int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS,
122 GrabModeAsync, GrabModeAsync, w,
123 cursor, CurrentTime);
124 if (status == GrabSuccess)
125 si->mouse_grab_window = w;
128 fprintf(stderr, "%s: grabbing mouse on 0x%x... %s.\n",
129 blurb(), (unsigned long) w, grab_string(status));
135 ungrab_kbd(saver_info *si)
137 saver_preferences *p = &si->prefs;
138 XUngrabKeyboard(si->dpy, CurrentTime);
140 fprintf(stderr, "%s: ungrabbing keyboard (was 0x%x).\n", blurb(),
141 (unsigned long) si->keyboard_grab_window);
142 si->keyboard_grab_window = 0;
147 ungrab_mouse(saver_info *si)
149 saver_preferences *p = &si->prefs;
150 XUngrabPointer(si->dpy, CurrentTime);
152 fprintf(stderr, "%s: ungrabbing mouse (was 0x%x).\n", blurb(),
153 (unsigned long) si->mouse_grab_window);
154 si->mouse_grab_window = 0;
159 grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
161 Status mstatus, kstatus;
165 for (i = 0; i < retries; i++)
167 XSync (si->dpy, False);
168 kstatus = grab_kbd (si, window);
169 if (kstatus == GrabSuccess)
172 /* else, wait a second and try to grab again. */
176 if (kstatus != GrabSuccess)
177 fprintf (stderr, "%s: couldn't grab keyboard! (%s)\n",
178 blurb(), grab_string(kstatus));
180 for (i = 0; i < retries; i++)
182 XSync (si->dpy, False);
183 mstatus = grab_mouse (si, window, cursor);
184 if (mstatus == GrabSuccess)
187 /* else, wait a second and try to grab again. */
191 if (mstatus != GrabSuccess)
192 fprintf (stderr, "%s: couldn't grab pointer! (%s)\n",
193 blurb(), grab_string(mstatus));
195 return (kstatus == GrabSuccess ||
196 mstatus == GrabSuccess);
200 ungrab_keyboard_and_mouse (saver_info *si)
208 move_mouse_grab (saver_info *si, Window to, Cursor cursor)
210 Window old = si->mouse_grab_window;
213 return grab_mouse (si, to, cursor);
216 saver_preferences *p = &si->prefs;
219 XSync (si->dpy, False);
220 XGrabServer (si->dpy); /* ############ DANGER! */
221 XSync (si->dpy, False);
224 fprintf(stderr, "%s: grabbing server...\n", blurb());
227 status = grab_mouse (si, to, cursor);
229 if (status != GrabSuccess) /* Augh! */
231 sleep (1); /* Note dramatic evil of sleeping
232 with server grabbed. */
233 XSync (si->dpy, False);
234 status = grab_mouse (si, to, cursor);
237 if (status != GrabSuccess) /* Augh! Try to get the old one back... */
238 grab_mouse (si, to, cursor);
240 XUngrabServer (si->dpy);
241 XSync (si->dpy, False); /* ###### (danger over) */
244 fprintf(stderr, "%s: ungrabbing server.\n", blurb());
251 /* Prints an error message to stderr and returns True if there is another
252 xscreensaver running already. Silently returns False otherwise. */
254 ensure_no_screensaver_running (Display *dpy, Screen *screen)
258 Window root = RootWindowOfScreen (screen);
259 Window root2, parent, *kids;
261 XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
263 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
269 for (i = 0; i < nkids; i++)
273 unsigned long nitems, bytesafter;
276 if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
277 False, XA_STRING, &type, &format, &nitems,
278 &bytesafter, (unsigned char **) &version)
283 if (!XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
284 False, XA_STRING, &type, &format, &nitems,
285 &bytesafter, (unsigned char **) &id)
291 "%s: already running on display %s (window 0x%x)\n from process %s.\n",
292 blurb(), DisplayString (dpy), (int) kids [i], id);
297 if (kids) XFree ((char *) kids);
299 XSetErrorHandler (old_handler);
305 /* Virtual-root hackery */
308 ERROR! You must not include vroot.h in this file.
312 store_vroot_property (Display *dpy, Window win, Window value)
317 "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(),
319 (win == screensaver_window ? "ScreenSaver" :
320 (win == real_vroot ? "VRoot" :
321 (win == real_vroot_value ? "Vroot_value" : "???"))),
323 (value == screensaver_window ? "ScreenSaver" :
324 (value == real_vroot ? "VRoot" :
325 (value == real_vroot_value ? "Vroot_value" : "???"))));
327 XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
328 (unsigned char *) &value, 1);
332 remove_vroot_property (Display *dpy, Window win)
336 fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win,
337 (win == screensaver_window ? "ScreenSaver" :
338 (win == real_vroot ? "VRoot" :
339 (win == real_vroot_value ? "Vroot_value" : "???"))));
341 XDeleteProperty (dpy, win, XA_VROOT);
346 kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
350 unsigned long nitems, bytesafter;
353 /* If the user has been using xv or xsetroot as a screensaver (to display
354 an image on the screensaver window, as a kind of slideshow) then the
355 pixmap and its associated color cells have been put in RetainPermanent
356 CloseDown mode. Since we're not destroying the xscreensaver window,
357 but merely unmapping it, we need to free these resources or those
358 colormap cells will stay allocated while the screensaver is off. (We
359 could just delete the screensaver window and recreate it later, but
360 that could cause other problems.) This code does an atomic read-and-
361 delete of the _XSETROOT_ID property, and if it held a pixmap, then we
362 cause the RetainPermanent resources of the client which created it
363 (and which no longer exists) to be freed.
365 if (XGetWindowProperty (dpy, window, XA_XSETROOT_ID, 0, 1,
366 True, AnyPropertyType, &type, &format, &nitems,
367 &bytesafter, (unsigned char **) &dataP)
371 if (dataP && *dataP && type == XA_PIXMAP && format == 32 &&
372 nitems == 1 && bytesafter == 0)
375 fprintf (stderr, "%s: destroying xsetroot data (0x%lX).\n",
377 XKillClient (dpy, *dataP);
380 fprintf (stderr, "%s: deleted unrecognised _XSETROOT_ID property: \n\
381 %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
382 blurb(), (unsigned long) dataP, (dataP ? *dataP : 0), type,
383 format, nitems, bytesafter);
388 static void handle_signals (saver_info *si, Bool on_p);
391 save_real_vroot (saver_screen_info *ssi)
393 saver_info *si = ssi->global;
394 Display *dpy = si->dpy;
395 Screen *screen = ssi->screen;
397 Window root = RootWindowOfScreen (screen);
398 Window root2, parent, *kids;
400 XErrorHandler old_handler;
402 /* It's possible that a window might be deleted between our call to
403 XQueryTree() and our call to XGetWindowProperty(). Don't die if
404 that happens (but just ignore that window, it's not the one we're
405 interested in anyway.)
408 old_handler = XSetErrorHandler (BadWindow_ehandler);
412 ssi->real_vroot_value = 0;
413 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
419 for (i = 0; i < nkids; i++)
423 unsigned long nitems, bytesafter;
426 if (XGetWindowProperty (dpy, kids[i], XA_VROOT, 0, 1, False, XA_WINDOW,
427 &type, &format, &nitems, &bytesafter,
428 (unsigned char **) &vrootP)
435 if (*vrootP == ssi->screensaver_window) abort ();
437 "%s: more than one virtual root window found (0x%x and 0x%x).\n",
438 blurb(), (int) ssi->real_vroot, (int) kids [i]);
441 ssi->real_vroot = kids [i];
442 ssi->real_vroot_value = *vrootP;
446 XSetErrorHandler (old_handler);
451 handle_signals (si, True);
452 remove_vroot_property (si->dpy, ssi->real_vroot);
456 XFree ((char *) kids);
461 restore_real_vroot_2 (saver_screen_info *ssi)
463 saver_info *si = ssi->global;
464 saver_preferences *p = &si->prefs;
465 if (p->verbose_p && ssi->real_vroot)
467 "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
468 blurb(), (unsigned long) ssi->real_vroot);
469 remove_vroot_property (si->dpy, ssi->screensaver_window);
472 store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value);
474 ssi->real_vroot_value = 0;
475 /* make sure the property change gets there before this process
476 terminates! We might be doing this because we have intercepted
477 SIGTERM or something. */
478 XSync (si->dpy, False);
485 restore_real_vroot_1 (saver_info *si)
488 Bool did_any = False;
489 for (i = 0; i < si->nscreens; i++)
491 saver_screen_info *ssi = &si->screens[i];
492 if (restore_real_vroot_2 (ssi))
499 restore_real_vroot (saver_info *si)
501 if (restore_real_vroot_1 (si))
502 handle_signals (si, False);
506 /* Signal hackery to ensure that the vroot doesn't get left in an
511 signal_name(int signal)
514 case SIGHUP: return "SIGHUP";
515 case SIGINT: return "SIGINT";
516 case SIGQUIT: return "SIGQUIT";
517 case SIGILL: return "SIGILL";
518 case SIGTRAP: return "SIGTRAP";
520 case SIGABRT: return "SIGABRT";
522 case SIGFPE: return "SIGFPE";
523 case SIGKILL: return "SIGKILL";
524 case SIGBUS: return "SIGBUS";
525 case SIGSEGV: return "SIGSEGV";
526 case SIGPIPE: return "SIGPIPE";
527 case SIGALRM: return "SIGALRM";
528 case SIGTERM: return "SIGTERM";
530 case SIGSTOP: return "SIGSTOP";
533 case SIGCONT: return "SIGCONT";
536 case SIGUSR1: return "SIGUSR1";
539 case SIGUSR2: return "SIGUSR2";
542 case SIGEMT: return "SIGEMT";
545 case SIGSYS: return "SIGSYS";
548 case SIGCHLD: return "SIGCHLD";
551 case SIGPWR: return "SIGPWR";
554 case SIGWINCH: return "SIGWINCH";
557 case SIGURG: return "SIGURG";
560 case SIGIO: return "SIGIO";
563 case SIGVTALRM: return "SIGVTALRM";
566 case SIGXCPU: return "SIGXCPU";
569 case SIGXFSZ: return "SIGXFSZ";
572 case SIGDANGER: return "SIGDANGER";
577 sprintf(buf, "signal %d\n", signal);
586 restore_real_vroot_handler (int sig)
588 saver_info *si = global_si_kludge; /* I hate C so much... */
590 signal (sig, SIG_DFL);
591 if (restore_real_vroot_1 (si))
592 fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
593 blurb(), signal_name(sig));
594 kill (getpid (), sig);
598 catch_signal (saver_info *si, int sig, Bool on_p)
601 signal (sig, SIG_DFL);
604 if (((long) signal (sig, restore_real_vroot_handler)) == -1L)
607 sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
609 saver_exit (si, 1, 0);
615 handle_signals (saver_info *si, Bool on_p)
618 if (on_p) fprintf (stderr, "handling signals\n");
619 else fprintf (stderr, "unhandling signals\n");
622 catch_signal (si, SIGHUP, on_p);
623 catch_signal (si, SIGINT, on_p);
624 catch_signal (si, SIGQUIT, on_p);
625 catch_signal (si, SIGILL, on_p);
626 catch_signal (si, SIGTRAP, on_p);
627 catch_signal (si, SIGIOT, on_p);
628 catch_signal (si, SIGABRT, on_p);
630 catch_signal (si, SIGEMT, on_p);
632 catch_signal (si, SIGFPE, on_p);
633 catch_signal (si, SIGBUS, on_p);
634 catch_signal (si, SIGSEGV, on_p);
636 catch_signal (si, SIGSYS, on_p);
638 catch_signal (si, SIGTERM, on_p);
640 catch_signal (si, SIGXCPU, on_p);
643 catch_signal (si, SIGXFSZ, on_p);
646 catch_signal (si, SIGDANGER, on_p);
651 saver_exit (saver_info *si, int status, const char *dump_core_reason)
653 saver_preferences *p = &si->prefs;
654 static Bool exiting = False;
663 vrs = restore_real_vroot_1 (si);
664 emergency_kill_subproc (si);
665 shutdown_stderr (si);
667 if (p->verbose_p && vrs)
668 fprintf (real_stderr, "%s: old vroot restored.\n", blurb());
672 #ifdef VMS /* on VMS, 1 is the "normal" exit code instead of 0. */
673 if (status == 0) status = 1;
674 else if (status == 1) status = -1;
677 bugp = !!dump_core_reason;
679 if (si->prefs.debug_p && !dump_core_reason)
680 dump_core_reason = "because of -debug";
682 if (dump_core_reason)
684 /* Note that the Linux man page for setuid() says If uid is
685 different from the old effective uid, the process will be
686 forbidden from leaving core dumps.
688 char cwd[4096]; /* should really be PATH_MAX, but who cares. */
690 fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
695 "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
696 "\t\tfor bug reporting information.\n\n",
699 # if defined(HAVE_GETCWD)
700 if (!getcwd (cwd, sizeof(cwd)))
701 # elif defined(HAVE_GETWD)
704 strcpy(cwd, "unknown.");
706 fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
707 describe_uids (si, real_stderr);
709 /* Do this to drop a core file, so that we can get a stack trace. */
717 /* Managing the actual screensaver window */
720 window_exists_p (Display *dpy, Window window)
722 XErrorHandler old_handler;
723 XWindowAttributes xgwa;
725 old_handler = XSetErrorHandler (BadWindow_ehandler);
726 XGetWindowAttributes (dpy, window, &xgwa);
728 XSetErrorHandler (old_handler);
729 return (xgwa.screen != 0);
733 store_saver_id (saver_screen_info *ssi)
735 XClassHint class_hints;
736 saver_info *si = ssi->global;
737 unsigned long pid = (unsigned long) getpid ();
739 struct passwd *p = getpwuid (getuid ());
740 const char *name, *host;
743 /* First store the name and class on the window.
745 class_hints.res_name = progname;
746 class_hints.res_class = progclass;
747 XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
748 XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
750 /* Then store the xscreensaver version number.
752 XChangeProperty (si->dpy, ssi->screensaver_window,
753 XA_SCREENSAVER_VERSION,
754 XA_STRING, 8, PropModeReplace,
755 (unsigned char *) si->version,
756 strlen (si->version));
758 /* Now store the XSCREENSAVER_ID property, that says what user and host
759 xscreensaver is running as.
762 if (p && p->pw_name && *p->pw_name)
766 sprintf (buf, "%lu", (unsigned long) p->pw_uid);
772 # if defined(HAVE_UNAME)
775 if (uname (&uts) < 0)
781 host = getenv("SYS$NODE");
782 # else /* !HAVE_UNAME && !VMS */
784 # endif /* !HAVE_UNAME && !VMS */
786 id = (char *) malloc (strlen(name) + strlen(host) + 50);
787 sprintf (id, "%lu (%s@%s)", pid, name, host);
789 XChangeProperty (si->dpy, ssi->screensaver_window,
790 XA_SCREENSAVER_ID, XA_STRING,
792 (unsigned char *) id, strlen (id));
798 store_saver_status (saver_info *si)
801 int size = si->nscreens + 2;
804 status = (CARD32 *) calloc (size, sizeof(CARD32));
806 status[0] = (CARD32) (si->screen_blanked_p
807 ? (si->locked_p ? XA_LOCK : XA_BLANK)
809 status[1] = (CARD32) si->blank_time;
811 for (i = 0; i < si->nscreens; i++)
813 saver_screen_info *ssi = &si->screens[i];
814 status [2 + i] = ssi->current_hack + 1;
817 XChangeProperty (si->dpy,
818 RootWindow (si->dpy, 0), /* always screen #0 */
819 XA_SCREENSAVER_STATUS,
820 XA_INTEGER, 32, PropModeReplace,
821 (unsigned char *) status, size);
826 /* Returns the area of the screen which the xscreensaver window should cover.
827 Normally this is the whole screen, but if the X server's root window is
828 actually larger than the monitor's displayable area, then we want to
829 operate in the currently-visible portion of the desktop instead.
832 get_screen_viewport (saver_screen_info *ssi,
833 int *x_ret, int *y_ret,
834 int *w_ret, int *h_ret,
837 int w = WidthOfScreen (ssi->screen);
838 int h = HeightOfScreen (ssi->screen);
840 #ifdef HAVE_XF86VMODE
841 saver_info *si = ssi->global;
842 int screen_no = screen_number (ssi->screen);
843 int op, event, error;
845 XF86VidModeModeLine ml;
848 /* Check for Xinerama first, because the VidModeExtension is broken
849 when Xinerama is present. Wheee!
852 if (!XQueryExtension (si->dpy, "XINERAMA", &op, &event, &error) &&
853 XF86VidModeQueryExtension (si->dpy, &event, &error) &&
854 XF86VidModeGetModeLine (si->dpy, screen_no, &dot, &ml) &&
855 XF86VidModeGetViewPort (si->dpy, screen_no, &x, &y))
860 *w_ret = ml.hdisplay;
861 *h_ret = ml.vdisplay;
863 if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
864 /* There is no viewport -- the screen does not scroll. */
868 /* Apparently some versions of XFree86 return nonsense here!
869 I've had reports of 1024x768 viewports at -1936862040, -1953705044.
870 So, sanity-check the values and give up if they are out of range.
872 if (*x_ret < 0 || *x_ret >= w ||
873 *y_ret < 0 || *y_ret >= h ||
874 *w_ret <= 0 || *w_ret > w ||
875 *h_ret <= 0 || *h_ret > h)
877 static int warned_once = 0;
880 fprintf (stderr, "\n"
881 "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
882 "%s: The XVidMode server extension is returning nonsense.\n"
883 "%s: Please report this bug to your X server vendor.\n\n",
884 blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
896 sprintf (msg, "%s: vp is %dx%d+%d+%d",
897 blurb(), *w_ret, *h_ret, *x_ret, *y_ret);
900 /* Apparently, though the server stores the X position in increments of
901 1 pixel, it will only make changes to the *display* in some other
902 increment. With XF86_SVGA on a Thinkpad, the display only updates
903 in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
904 pixels in 16-bit mode. I don't know what it does in 24- and 32-bit
905 mode, because I don't have enough video memory to find out.
907 I consider it a bug that XF86VidModeGetViewPort() is telling me the
908 server's *target* scroll position rather than the server's *actual*
909 scroll position. David Dawes agrees, and says they may fix this in
910 XFree86 4.0, but it's notrivial.
912 He also confirms that this behavior is server-dependent, so the
913 actual scroll position cannot be reliably determined by the client.
914 So... that means the only solution is to provide a ``sandbox''
915 around the blackout window -- we make the window be up to N pixels
916 larger than the viewport on both the left and right sides. That
917 means some part of the outer edges of each hack might not be
918 visible, but screw it.
920 I'm going to guess that 16 pixels is enough, and that the Y dimension
921 doesn't have this problem.
923 The drawback of doing this, of course, is that some of the screenhacks
924 will still look pretty stupid -- for example, "slidescreen" will cut
925 off the left and right edges of the grid, etc.
928 if (x > 0 && x < w - ml.hdisplay) /* not at left edge or right edge */
930 /* Round X position down to next lower multiple of FUDGE.
931 Increase width by 2*FUDGE in case some server rounds up.
933 *x_ret = ((x - 1) / FUDGE) * FUDGE;
934 *w_ret += (FUDGE * 2);
940 *w_ret != ml.hdisplay ||
941 *h_ret != ml.vdisplay)
942 sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
943 *w_ret, *h_ret, *x_ret, *y_ret);
946 fprintf (stderr, "%s.\n", msg);
951 #endif /* HAVE_XF86VMODE */
961 initialize_screensaver_window_1 (saver_screen_info *ssi)
963 saver_info *si = ssi->global;
964 saver_preferences *p = &si->prefs;
965 Bool install_cmap_p = ssi->install_cmap_p; /* not p->install_cmap_p */
967 /* This resets the screensaver window as fully as possible, since there's
968 no way of knowing what some random client may have done to us in the
969 meantime. We could just destroy and recreate the window, but that has
970 its own set of problems...
973 XSetWindowAttributes attrs;
974 unsigned long attrmask;
975 int x, y, width, height;
976 static Bool printed_visual_info = False; /* only print the message once. */
978 get_screen_viewport (ssi, &x, &y, &width, &height,
979 (p->verbose_p && !si->screen_blanked_p));
981 black.red = black.green = black.blue = 0;
983 if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
986 if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
987 /* It's not the default visual, so we have no choice but to install. */
988 install_cmap_p = True;
994 ssi->cmap = XCreateColormap (si->dpy,
995 RootWindowOfScreen (ssi->screen),
996 ssi->current_visual, AllocNone);
997 if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
998 ssi->black_pixel = black.pixel;
1003 Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
1006 XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
1007 if (ssi->cmap != ssi->demo_cmap &&
1008 ssi->cmap != def_cmap)
1009 XFreeColormap (si->dpy, ssi->cmap);
1011 ssi->cmap = def_cmap;
1012 ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
1015 attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
1016 CWBackPixel | CWBackingPixel | CWBorderPixel);
1017 attrs.override_redirect = True;
1019 /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
1020 actually be reading these events during normal operation; but we still
1021 need to see Button events for demo-mode to work properly.
1023 attrs.event_mask = (KeyPressMask | KeyReleaseMask |
1024 ButtonPressMask | ButtonReleaseMask |
1027 attrs.backing_store = NotUseful;
1028 attrs.colormap = ssi->cmap;
1029 attrs.background_pixel = ssi->black_pixel;
1030 attrs.backing_pixel = ssi->black_pixel;
1031 attrs.border_pixel = ssi->black_pixel;
1033 if (p->debug_p) width = width / 2;
1035 if (!p->verbose_p || printed_visual_info)
1037 else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
1039 fprintf (stderr, "%s: using default visual ", blurb());
1040 describe_visual (stderr, ssi->screen, ssi->current_visual,
1045 fprintf (stderr, "%s: using visual: ", blurb());
1046 describe_visual (stderr, ssi->screen, ssi->current_visual,
1048 fprintf (stderr, "%s: default visual: ", blurb());
1049 describe_visual (stderr, ssi->screen,
1050 DefaultVisualOfScreen (ssi->screen),
1051 ssi->install_cmap_p);
1053 printed_visual_info = True;
1055 #ifdef HAVE_MIT_SAVER_EXTENSION
1056 if (si->using_mit_saver_extension)
1058 XScreenSaverInfo *info;
1059 Window root = RootWindowOfScreen (ssi->screen);
1062 /* This call sets the server screensaver timeouts to what we think
1063 they should be (based on the resources and args xscreensaver was
1064 started with.) It's important that we do this to sync back up
1065 with the server - if we have turned on prematurely, as by an
1066 ACTIVATE ClientMessage, then the server may decide to activate
1067 the screensaver while it's already active. That's ok for us,
1068 since we would know to ignore that ScreenSaverActivate event,
1069 but a side effect of this would be that the server would map its
1070 saver window (which we then hide again right away) meaning that
1071 the bits currently on the screen get blown away. Ugly. */
1073 /* #### Ok, that doesn't work - when we tell the server that the
1074 screensaver is "off" it sends us a Deactivate event, which is
1075 sensible... but causes the saver to never come on. Hmm. */
1076 disable_builtin_screensaver (si, True);
1080 /* #### The MIT-SCREEN-SAVER extension gives us access to the
1081 window that the server itself uses for saving the screen.
1082 However, using this window in any way, in particular, calling
1083 XScreenSaverSetAttributes() as below, tends to make the X server
1084 crash. So fuck it, let's try and get along without using it...
1086 It's also inconvenient to use this window because it doesn't
1087 always exist (though the ID is constant.) So to use this
1088 window, we'd have to reimplement the ACTIVATE ClientMessage to
1089 tell the *server* to tell *us* to turn on, to cause the window
1090 to get created at the right time. Gag. */
1091 XScreenSaverSetAttributes (si->dpy, root,
1092 0, 0, width, height, 0,
1093 current_depth, InputOutput, visual,
1095 XSync (si->dpy, False);
1098 info = XScreenSaverAllocInfo ();
1099 XScreenSaverQueryInfo (si->dpy, root, info);
1100 ssi->server_mit_saver_window = info->window;
1101 if (! ssi->server_mit_saver_window) abort ();
1104 #endif /* HAVE_MIT_SAVER_EXTENSION */
1106 if (ssi->screensaver_window)
1108 XWindowChanges changes;
1109 unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1112 changes.width = width;
1113 changes.height = height;
1114 changes.border_width = 0;
1116 XConfigureWindow (si->dpy, ssi->screensaver_window,
1117 changesmask, &changes);
1118 XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
1123 ssi->screensaver_window =
1124 XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
1125 x, y, width, height,
1126 0, ssi->current_depth, InputOutput,
1127 ssi->current_visual, attrmask, &attrs);
1131 fprintf (stderr, "%s: saver window is 0x%lx.\n",
1132 blurb(), (unsigned long) ssi->screensaver_window);
1135 store_saver_id (ssi); /* store window name and IDs */
1140 bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
1142 BlackPixelOfScreen (ssi->screen),
1143 BlackPixelOfScreen (ssi->screen),
1145 ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
1147 XFreePixmap (si->dpy, bit);
1150 XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
1153 XUndefineCursor (si->dpy, ssi->screensaver_window);
1155 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1159 initialize_screensaver_window (saver_info *si)
1162 for (i = 0; i < si->nscreens; i++)
1163 initialize_screensaver_window_1 (&si->screens[i]);
1168 raise_window (saver_info *si,
1169 Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
1171 saver_preferences *p = &si->prefs;
1175 inhibit_fade = True;
1177 if (si->emergency_lock_p)
1178 inhibit_fade = True;
1181 initialize_screensaver_window (si);
1183 reset_watchdog_timer (si, True);
1185 if (p->fade_p && si->fading_possible_p && !inhibit_fade)
1187 Window *current_windows = (Window *)
1188 calloc(sizeof(Window), si->nscreens);
1189 Colormap *current_maps = (Colormap *)
1190 calloc(sizeof(Colormap), si->nscreens);
1192 for (i = 0; i < si->nscreens; i++)
1194 saver_screen_info *ssi = &si->screens[i];
1195 current_windows[i] = ssi->screensaver_window;
1196 current_maps[i] = (between_hacks_p
1198 : DefaultColormapOfScreen (ssi->screen));
1199 /* Ensure that the default background of the window is really black,
1200 not a pixmap or something. (This does not clear the window.) */
1201 XSetWindowBackground (si->dpy, ssi->screensaver_window,
1205 if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
1207 XGrabServer (si->dpy); /* ############ DANGER! */
1209 /* Clear the stderr layer on each screen.
1212 for (i = 0; i < si->nscreens; i++)
1214 saver_screen_info *ssi = &si->screens[i];
1215 if (ssi->stderr_overlay_window)
1216 /* Do this before the fade, since the stderr cmap won't fade
1217 even if we uninstall it (beats me...) */
1221 /* Note! The server is grabbed, and this will take several seconds
1223 fade_screens (si->dpy, current_maps, current_windows,
1224 p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
1227 free(current_windows);
1229 current_windows = 0;
1231 if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
1233 #ifdef HAVE_MIT_SAVER_EXTENSION
1234 for (i = 0; i < si->nscreens; i++)
1236 saver_screen_info *ssi = &si->screens[i];
1237 if (ssi->server_mit_saver_window &&
1238 window_exists_p (si->dpy, ssi->server_mit_saver_window))
1239 XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1241 #endif /* HAVE_MIT_SAVER_EXTENSION */
1243 XUngrabServer (si->dpy);
1244 XSync (si->dpy, False); /* ###### (danger over) */
1248 for (i = 0; i < si->nscreens; i++)
1250 saver_screen_info *ssi = &si->screens[i];
1252 XClearWindow (si->dpy, ssi->screensaver_window);
1253 if (!dont_clear || ssi->stderr_overlay_window)
1255 XMapRaised (si->dpy, ssi->screensaver_window);
1256 #ifdef HAVE_MIT_SAVER_EXTENSION
1257 if (ssi->server_mit_saver_window &&
1258 window_exists_p (si->dpy, ssi->server_mit_saver_window))
1259 XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
1260 #endif /* HAVE_MIT_SAVER_EXTENSION */
1264 for (i = 0; i < si->nscreens; i++)
1266 saver_screen_info *ssi = &si->screens[i];
1268 XInstallColormap (si->dpy, ssi->cmap);
1273 blank_screen (saver_info *si)
1278 /* Note: we do our grabs on the root window, not on the screensaver window.
1279 If we grabbed on the saver window, then the demo mode and lock dialog
1280 boxes wouldn't get any events.
1282 ok = grab_keyboard_and_mouse (si,
1283 /*si->screens[0].screensaver_window,*/
1284 RootWindowOfScreen(si->screens[0].screen),
1287 : si->screens[0].cursor));
1290 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1291 /* If we're using a server extension, then failure to get a grab is
1292 not a big deal -- even without the grab, we will still be able
1293 to un-blank when there is user activity, since the server will
1300 for (i = 0; i < si->nscreens; i++)
1302 saver_screen_info *ssi = &si->screens[i];
1304 save_real_vroot (ssi);
1305 store_vroot_property (si->dpy,
1306 ssi->screensaver_window,
1307 ssi->screensaver_window);
1309 #ifdef HAVE_XF86VMODE
1312 if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
1313 !XF86VidModeGetViewPort (si->dpy, i,
1316 ssi->blank_vp_x = ssi->blank_vp_y = -1;
1318 #endif /* HAVE_XF86VMODE */
1321 raise_window (si, False, False, False);
1323 si->screen_blanked_p = True;
1324 si->blank_time = time ((time_t) 0);
1325 si->last_wall_clock_time = 0;
1327 store_saver_status (si); /* store blank time */
1334 unblank_screen (saver_info *si)
1336 saver_preferences *p = &si->prefs;
1337 Bool unfade_p = (si->fading_possible_p && p->unfade_p);
1340 monitor_power_on (si);
1341 reset_watchdog_timer (si, False);
1348 Window *current_windows = (Window *)
1349 calloc(sizeof(Window), si->nscreens);
1351 for (i = 0; i < si->nscreens; i++)
1353 saver_screen_info *ssi = &si->screens[i];
1354 current_windows[i] = ssi->screensaver_window;
1355 /* Ensure that the default background of the window is really black,
1356 not a pixmap or something. (This does not clear the window.) */
1357 XSetWindowBackground (si->dpy, ssi->screensaver_window,
1361 if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
1364 XSync (si->dpy, False);
1365 XGrabServer (si->dpy); /* ############ DANGER! */
1366 XSync (si->dpy, False);
1368 /* Clear the stderr layer on each screen.
1370 for (i = 0; i < si->nscreens; i++)
1372 saver_screen_info *ssi = &si->screens[i];
1376 XUngrabServer (si->dpy);
1377 XSync (si->dpy, False); /* ###### (danger over) */
1380 fade_screens (si->dpy, 0, current_windows,
1381 p->fade_seconds/1000, p->fade_ticks,
1384 free(current_windows);
1385 current_windows = 0;
1387 if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
1391 for (i = 0; i < si->nscreens; i++)
1393 saver_screen_info *ssi = &si->screens[i];
1396 Colormap c = DefaultColormapOfScreen (ssi->screen);
1397 /* avoid technicolor */
1398 XClearWindow (si->dpy, ssi->screensaver_window);
1399 if (c) XInstallColormap (si->dpy, c);
1401 XUnmapWindow (si->dpy, ssi->screensaver_window);
1406 /* If the focus window does has a non-default colormap, then install
1407 that colormap as well. (On SGIs, this will cause both the root map
1408 and the focus map to be installed simultaniously. It'd be nice to
1409 pick up the other colormaps that had been installed, too; perhaps
1410 XListInstalledColormaps could be used for that?)
1415 XGetInputFocus (si->dpy, &focus, &revert_to);
1416 if (focus && focus != PointerRoot && focus != None)
1418 XWindowAttributes xgwa;
1420 XGetWindowAttributes (si->dpy, focus, &xgwa);
1421 if (xgwa.colormap &&
1422 xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
1423 XInstallColormap (si->dpy, xgwa.colormap);
1428 for (i = 0; i < si->nscreens; i++)
1430 saver_screen_info *ssi = &si->screens[i];
1431 kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
1434 store_saver_status (si); /* store unblank time */
1435 ungrab_keyboard_and_mouse (si);
1436 restore_real_vroot (si);
1438 /* Unmap the windows a second time, dammit -- just to avoid a race
1439 with the screen-grabbing hacks. (I'm not sure if this is really
1440 necessary; I'm stabbing in the dark now.)
1442 for (i = 0; i < si->nscreens; i++)
1443 XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
1445 si->screen_blanked_p = False;
1446 si->blank_time = time ((time_t) 0);
1447 si->last_wall_clock_time = 0;
1449 store_saver_status (si); /* store unblank time */
1454 select_visual (saver_screen_info *ssi, const char *visual_name)
1456 saver_info *si = ssi->global;
1457 saver_preferences *p = &si->prefs;
1458 Bool install_cmap_p = p->install_cmap_p;
1459 Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
1463 if (visual_name && *visual_name)
1465 if (!strcmp(visual_name, "default-i") ||
1466 !strcmp(visual_name, "Default-i") ||
1467 !strcmp(visual_name, "Default-I")
1470 visual_name = "default";
1471 install_cmap_p = True;
1473 else if (!strcmp(visual_name, "default-n") ||
1474 !strcmp(visual_name, "Default-n") ||
1475 !strcmp(visual_name, "Default-N"))
1477 visual_name = "default";
1478 install_cmap_p = False;
1480 else if (!strcmp(visual_name, "gl") ||
1481 !strcmp(visual_name, "Gl") ||
1482 !strcmp(visual_name, "GL"))
1484 new_v = ssi->best_gl_visual;
1485 if (!new_v && p->verbose_p)
1486 fprintf (stderr, "%s: no GL visuals.\n", progname);
1490 new_v = get_visual (ssi->screen, visual_name, True, False);
1494 new_v = ssi->default_visual;
1499 if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
1500 /* It's not the default visual, so we have no choice but to install. */
1501 install_cmap_p = True;
1503 ssi->install_cmap_p = install_cmap_p;
1506 ((ssi->current_visual != new_v) ||
1507 (install_cmap_p != was_installed_p)))
1509 Colormap old_c = ssi->cmap;
1510 Window old_w = ssi->screensaver_window;
1514 fprintf (stderr, "%s: switching to visual ", blurb());
1515 describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
1517 fprintf (stderr, "%s: from ", blurb());
1518 describe_visual (stderr, ssi->screen, ssi->current_visual,
1524 ssi->current_visual = new_v;
1525 ssi->current_depth = visual_depth(ssi->screen, new_v);
1527 ssi->screensaver_window = 0;
1529 initialize_screensaver_window_1 (ssi);
1531 /* stderr_overlay_window is a child of screensaver_window, so we need
1532 to destroy that as well (actually, we just need to invalidate and
1533 drop our pointers to it, but this will destroy it, which is ok so
1534 long as it happens before old_w itself is destroyed.) */
1537 raise_window (si, True, True, False);
1538 store_vroot_property (si->dpy,
1539 ssi->screensaver_window, ssi->screensaver_window);
1542 /* Transfer the grabs from the old window to the new.
1543 Actually I think none of this is necessary, since we always
1544 hold our grabs on the root window, but I wrote this before
1545 re-discovering that...
1549 /* If we're destroying the window that holds our mouse grab,
1550 transfer the grab to the new window. (Grab the server while
1551 so doing, to avoid a race condition.)
1553 if (old_w == si->mouse_grab_window)
1555 XGrabServer (si->dpy); /* ############ DANGER! */
1557 grab_mouse (si, ssi->screensaver_window,
1561 XUngrabServer (si->dpy);
1562 XSync (si->dpy, False); /* ###### (danger over) */
1565 /* If we're destroying the window that holds our keyboard grab,
1566 transfer the grab to the new window. (Grab the server while
1567 so doing, to avoid a race condition.)
1569 if (old_w == si->keyboard_grab_window)
1571 XGrabServer (si->dpy); /* ############ DANGER! */
1573 grab_kbd(si, ssi->screensaver_window);
1574 XUngrabServer (si->dpy);
1575 XSync (si->dpy, False); /* ###### (danger over) */
1578 /* Now we can destroy this window without horking our grabs. */
1580 XDestroyWindow (si->dpy, old_w);
1583 fprintf (stderr, "%s: destroyed old saver window 0x%lx.\n",
1584 blurb(), (unsigned long) old_w);
1587 old_c != DefaultColormapOfScreen (ssi->screen) &&
1588 old_c != ssi->demo_cmap)
1589 XFreeColormap (si->dpy, old_c);