+void
+store_saver_status (saver_info *si)
+{
+ CARD32 *status;
+ int size = si->nscreens + 2;
+ int i;
+
+ status = (CARD32 *) calloc (size, sizeof(CARD32));
+
+ status[0] = (CARD32) (si->screen_blanked_p
+ ? (si->locked_p ? XA_LOCK : XA_BLANK)
+ : 0);
+ status[1] = (CARD32) si->blank_time;
+
+ for (i = 0; i < si->nscreens; i++)
+ {
+ saver_screen_info *ssi = &si->screens[i];
+ status [2 + i] = ssi->current_hack + 1;
+ }
+
+ XChangeProperty (si->dpy,
+ RootWindow (si->dpy, 0), /* always screen #0 */
+ XA_SCREENSAVER_STATUS,
+ XA_INTEGER, 32, PropModeReplace,
+ (unsigned char *) status, size);
+}
+
+
+
+/* Returns the area of the screen which the xscreensaver window should cover.
+ Normally this is the whole screen, but if the X server's root window is
+ actually larger than the monitor's displayable area, then we want to
+ operate in the currently-visible portion of the desktop instead.
+ */
+void
+get_screen_viewport (saver_screen_info *ssi,
+ int *x_ret, int *y_ret,
+ int *w_ret, int *h_ret,
+ int target_x, int target_y,
+ Bool verbose_p)
+{
+ int w = WidthOfScreen (ssi->screen);
+ int h = HeightOfScreen (ssi->screen);
+
+#ifdef HAVE_XF86VMODE
+ saver_info *si = ssi->global;
+ int event, error;
+ int dot;
+ XF86VidModeModeLine ml;
+ int x, y;
+ Bool xinerama_p;
+ Bool placement_only_p = (target_x != -1 && target_y != -1);
+
+#ifdef HAVE_XINERAMA
+ xinerama_p = (XineramaQueryExtension (si->dpy, &event, &error) &&
+ XineramaIsActive (si->dpy));
+#else /* !HAVE_XINERAMA */
+ /* Even if we don't have the client-side Xinerama lib, check to see if
+ the server supports Xinerama, so that we know to ignore the VidMode
+ extension -- otherwise a server crash could result. Yay. */
+ xinerama_p = XQueryExtension (si->dpy, "XINERAMA", &error, &event, &error);
+
+#endif /* !HAVE_XINERAMA */
+
+#ifdef HAVE_XINERAMA
+ if (xinerama_p && placement_only_p)
+ {
+ int nscreens = 0;
+ XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &nscreens);
+ if (xsi)
+ {
+ /* Find the screen that contains the mouse. */
+ int which = -1;
+ int i;
+ for (i = 0; i < nscreens; i++)
+ {
+ if (target_x >= xsi[i].x_org &&
+ target_y >= xsi[i].y_org &&
+ target_x < xsi[i].x_org + xsi[i].width &&
+ target_y < xsi[i].y_org + xsi[i].height)
+ which = i;
+ if (verbose_p)
+ {
+ fprintf (stderr, "%s: %d: xinerama vp: %dx%d+%d+%d",
+ blurb(), i,
+ xsi[which].width, xsi[which].height,
+ xsi[i].x_org, xsi[i].y_org);
+ if (which == i)
+ fprintf (stderr, "; mouse at %d,%d",
+ target_x, target_y);
+ fprintf (stderr, ".\n");
+ }
+ }
+ if (which == -1) which = 0; /* didn't find it? Use the first. */
+ *x_ret = xsi[which].x_org;
+ *y_ret = xsi[which].y_org;
+ *w_ret = xsi[which].width;
+ *h_ret = xsi[which].height;
+ XFree (xsi);
+ return;
+ }
+ }
+#endif /* HAVE_XINERAMA */
+
+ if (!xinerama_p && /* Xinerama + VidMode = broken. */
+ XF86VidModeQueryExtension (si->dpy, &event, &error) &&
+ XF86VidModeGetModeLine (si->dpy, ssi->number, &dot, &ml) &&
+ XF86VidModeGetViewPort (si->dpy, ssi->number, &x, &y))
+ {
+ char msg[512];
+ *x_ret = x;
+ *y_ret = y;
+ *w_ret = ml.hdisplay;
+ *h_ret = ml.vdisplay;
+
+ if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
+ /* There is no viewport -- the screen does not scroll. */
+ return;
+
+
+ /* Apparently some versions of XFree86 return nonsense here!
+ I've had reports of 1024x768 viewports at -1936862040, -1953705044.
+ So, sanity-check the values and give up if they are out of range.
+ */
+ if (*x_ret < 0 || *x_ret >= w ||
+ *y_ret < 0 || *y_ret >= h ||
+ *w_ret <= 0 || *w_ret > w ||
+ *h_ret <= 0 || *h_ret > h)
+ {
+ static int warned_once = 0;
+ if (!warned_once)
+ {
+ fprintf (stderr, "\n"
+ "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
+ "%s: The XVidMode server extension is returning nonsense.\n"
+ "%s: Please report this bug to your X server vendor.\n\n",
+ blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
+ blurb(), blurb());
+ warned_once = 1;
+ }
+ *x_ret = 0;
+ *y_ret = 0;
+ *w_ret = w;
+ *h_ret = h;
+ return;
+ }
+
+ sprintf (msg, "%s: %d: vp is %dx%d+%d+%d",
+ blurb(), ssi->number,
+ *w_ret, *h_ret, *x_ret, *y_ret);
+
+
+ /* Apparently, though the server stores the X position in increments of
+ 1 pixel, it will only make changes to the *display* in some other
+ increment. With XF86_SVGA on a Thinkpad, the display only updates
+ in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
+ pixels in 16-bit mode. I don't know what it does in 24- and 32-bit
+ mode, because I don't have enough video memory to find out.
+
+ I consider it a bug that XF86VidModeGetViewPort() is telling me the
+ server's *target* scroll position rather than the server's *actual*
+ scroll position. David Dawes agrees, and says they may fix this in
+ XFree86 4.0, but it's notrivial.
+
+ He also confirms that this behavior is server-dependent, so the
+ actual scroll position cannot be reliably determined by the client.
+ So... that means the only solution is to provide a ``sandbox''
+ around the blackout window -- we make the window be up to N pixels
+ larger than the viewport on both the left and right sides. That
+ means some part of the outer edges of each hack might not be
+ visible, but screw it.
+
+ I'm going to guess that 16 pixels is enough, and that the Y dimension
+ doesn't have this problem.
+
+ The drawback of doing this, of course, is that some of the screenhacks
+ will still look pretty stupid -- for example, "slidescreen" will cut
+ off the left and right edges of the grid, etc.
+ */
+# define FUDGE 16
+ if (x > 0 && x < w - ml.hdisplay) /* not at left edge or right edge */
+ {
+ /* Round X position down to next lower multiple of FUDGE.
+ Increase width by 2*FUDGE in case some server rounds up.
+ */
+ *x_ret = ((x - 1) / FUDGE) * FUDGE;
+ *w_ret += (FUDGE * 2);
+ }
+# undef FUDGE
+
+ if (*x_ret != x ||
+ *y_ret != y ||
+ *w_ret != ml.hdisplay ||
+ *h_ret != ml.vdisplay)
+ sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
+ *w_ret, *h_ret, *x_ret, *y_ret);
+
+ if (verbose_p)
+ fprintf (stderr, "%s.\n", msg);
+
+ return;
+ }
+
+#endif /* HAVE_XF86VMODE */
+
+ *x_ret = 0;
+ *y_ret = 0;
+ *w_ret = w;
+ *h_ret = h;
+}
+
+
+static Bool error_handler_hit_p = False;
+
+static int
+ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
+{
+ error_handler_hit_p = True;
+ return 0;
+}
+
+
+/* Returns True if successful, False if an X error occurred.
+ We need this because other programs might have done things to
+ our window that will cause XChangeWindowAttributes() to fail:
+ if that happens, we give up, destroy the window, and re-create
+ it.
+ */
+static Bool
+safe_XChangeWindowAttributes (Display *dpy, Window window,
+ unsigned long mask,
+ XSetWindowAttributes *attrs)
+{
+ XErrorHandler old_handler;
+ XSync (dpy, False);
+ error_handler_hit_p = False;
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+ XChangeWindowAttributes (dpy, window, mask, attrs);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XSync (dpy, False);
+
+ return (!error_handler_hit_p);
+}
+
+
+/* This might not be necessary, but just in case. */
+static Bool
+safe_XConfigureWindow (Display *dpy, Window window,
+ unsigned long mask, XWindowChanges *changes)
+{
+ XErrorHandler old_handler;
+ XSync (dpy, False);
+ error_handler_hit_p = False;
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+ XConfigureWindow (dpy, window, mask, changes);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XSync (dpy, False);
+
+ return (!error_handler_hit_p);
+}
+
+/* This might not be necessary, but just in case. */
+static Bool
+safe_XDestroyWindow (Display *dpy, Window window)
+{
+ XErrorHandler old_handler;
+ XSync (dpy, False);
+ error_handler_hit_p = False;
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+ XDestroyWindow (dpy, window);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XSync (dpy, False);
+
+ return (!error_handler_hit_p);
+}
+
+