* to put seperate savers on those duplicated-or-overlapping
* monitors, xscreensaver just ignores them (which allows them to
* display duplicates or overlaps).
+ *
+ * 5a) Nvidia fucks it up:
+ *
+ * Nvidia drivers as of Aug 2008 running in "TwinView" mode
+ * apparently report correct screen geometry via Xinerama, but
+ * report one giant screen via RANDR. The response from the
+ * nvidia developers is, "we don't support RANDR, use Xinerama
+ * instead." Which is a seriously lame answer. So, xscreensaver
+ * has to query *both* extensions, and make a guess as to which
+ * is to be believed.
+ *
+ * 5b) Also sometimes RANDR says stupid shit like, "You have one
+ * screen, and it has no available orientations or sizes."
+ *
*/
#ifdef HAVE_CONFIG_H
int x, y, width, height;
monitor_sanity sanity; /* I'm not crazy you're the one who's crazy */
int enemy; /* which monitor it overlaps or duplicates */
+ char *err; /* msg to print at appropriate later time;
+ exists only on monitor #0. */
};
+static Bool layouts_differ_p (monitor **a, monitor **b);
+
+
static void
free_monitors (monitor **monitors)
{
while (*m2)
{
if ((*m2)->desc) free ((*m2)->desc);
+ if ((*m2)->err) free ((*m2)->err);
free (*m2);
m2++;
}
}
+static char *
+append (char *s1, const char *s2)
+{
+ char *s = (char *) malloc ((s1 ? strlen(s1) : 0) +
+ (s2 ? strlen(s2) : 0) + 3);
+ *s = 0;
+ if (s1) strcat (s, s1);
+ if (s1 && s2) strcat (s, "\n");
+ if (s2) strcat (s, s2);
+ if (s1) free (s1);
+ return s;
+}
+
+
#ifdef HAVE_XINERAMA
static monitor **
-xinerama_scan_monitors (Display *dpy)
+xinerama_scan_monitors (Display *dpy, char **errP)
{
Screen *screen = DefaultScreenOfDisplay (dpy);
int event, error, nscreens, i;
#ifdef HAVE_XF86VMODE
static monitor **
-vidmode_scan_monitors (Display *dpy)
+vidmode_scan_monitors (Display *dpy, char **errP)
{
int event, error, nscreens, i;
monitor **monitors;
m->height = ml.vdisplay;
}
+ /* On a system that has VidMode but does not have RANDR, and that has
+ "Option Rotate" set, WidthOfScreen/HeightOfScreen are the rotated
+ size, but XF86VidModeModeLine contains the unrotated size.
+ Maybe there's something in 'flags' that indicates this?
+ Or, we can just notice that the aspect ratios are inverted:
+ */
+ if (m->width > 0 &&
+ m->height > 0 &&
+ ((m->width > m->height) !=
+ (WidthOfScreen(screen) > HeightOfScreen(screen))))
+ {
+ int swap = m->width;
+ m->width = m->height;
+ m->height = swap;
+ }
+
+
/* 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
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.
+ XFree86 4.0, but it's nontrivial.
He also confirms that this behavior is server-dependent, so the
actual scroll position cannot be reliably determined by the client.
#ifdef HAVE_RANDR
static monitor **
-randr_scan_monitors (Display *dpy)
+randr_scan_monitors (Display *dpy, char **errP)
{
int event, error, major, minor, nscreens, i, j;
monitor **monitors;
# endif /* HAVE_RANDR_12 */
}
+ if (nscreens <= 0)
+ {
+ *errP = append (*errP,
+ "WARNING: RANDR reported no screens! Ignoring it.");
+ return 0;
+ }
+
monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors));
if (!monitors) return 0;
for (i = 0, j = 0; i < ScreenCount (dpy); i++)
{
- Screen *screen = ScreenOfDisplay (dpy, j);
+ Screen *screen = ScreenOfDisplay (dpy, i);
if (! new_randr_p) /* RANDR 1.0 */
{
SizeID size = -1;
Rotation rot = ~0;
XRRScreenSize *rrsizes;
- int nsizes;
+ int nsizes = 0;
size = XRRConfigCurrentConfiguration (rrc, &rot);
rrsizes = XRRConfigSizes (rrc, &nsizes);
- if (rot & (RR_Rotate_90|RR_Rotate_270))
+ if (nsizes <= 0) /* WTF? Shouldn't happen but does. */
+ {
+ m->width = DisplayWidth (dpy, i);
+ m->height = DisplayHeight (dpy, i);
+ }
+ else if (rot & (RR_Rotate_90|RR_Rotate_270))
{
m->width = rrsizes[size].height;
m->height = rrsizes[size].width;
int k;
XRRScreenResources *res =
XRRGetScreenResources (dpy, RootWindowOfScreen (screen));
- for (k = 0; k < res->noutput; k++)
+ for (k = 0; k < res->noutput; k++, j++)
{
monitor *m = (monitor *) calloc (1, sizeof (monitor));
XRROutputInfo *rroi = XRRGetOutputInfo (dpy, res,
res->outputs[k]);
- RRCrtc crtc = (rroi->crtc ? rroi->crtc : rroi->crtcs[0]);
- XRRCrtcInfo *crtci = XRRGetCrtcInfo (dpy, res, crtc);
+ RRCrtc crtc = (rroi->crtc ? rroi->crtc :
+ rroi->ncrtc ? rroi->crtcs[0] : 0);
+ XRRCrtcInfo *crtci = (crtc ? XRRGetCrtcInfo(dpy, res, crtc) : 0);
monitors[j] = m;
m->screen = screen;
m->id = (i * 1000) + j;
m->desc = (rroi->name ? strdup (rroi->name) : 0);
- m->x = crtci->x;
- m->y = crtci->y;
- if (crtci->rotation & (RR_Rotate_90|RR_Rotate_270))
- {
- m->width = crtci->height;
- m->height = crtci->width;
- }
- else
+ if (crtci)
{
+ /* Note: if the screen is rotated, XRRConfigSizes contains
+ the unrotated WxH, but XRRCrtcInfo contains rotated HxW.
+ */
+ m->x = crtci->x;
+ m->y = crtci->y;
m->width = crtci->width;
m->height = crtci->height;
}
- j++;
-
if (rroi->connection == RR_Disconnected)
m->sanity = S_DISABLED;
/* #### do the same for RR_UnknownConnection? */
- XRRFreeCrtcInfo (crtci);
+ if (crtci)
+ XRRFreeCrtcInfo (crtci);
XRRFreeOutputInfo (rroi);
}
XRRFreeScreenResources (res);
}
}
+ /* Work around more fucking brain damage. */
+ {
+ int ok = 0;
+ int i = 0;
+ while (monitors[i])
+ {
+ if (monitors[i]->width != 0 && monitors[i]->height != 0)
+ ok++;
+ i++;
+ }
+ if (! ok)
+ {
+ *errP = append (*errP,
+ "WARNING: RANDR says all screens are 0x0! Ignoring it.");
+ free_monitors (monitors);
+ monitors = 0;
+ }
+ }
+
return monitors;
}
static monitor **
-basic_scan_monitors (Display *dpy)
+basic_scan_monitors (Display *dpy, char **errP)
{
int nscreens = ScreenCount (dpy);
int i;
}
+#if defined(HAVE_RANDR) && defined(HAVE_XINERAMA)
+
+/* From: Aaron Plattner <aplattner@nvidia.com>
+ Date: August 7, 2008 10:21:25 AM PDT
+ To: linux-bugs@nvidia.com
+
+ The NVIDIA X driver does not yet support RandR 1.2. The X server has
+ a compatibility layer in it that allows RandR 1.2 clients to talk to
+ RandR 1.1 drivers through an RandR 1.2 pseudo-output called "default".
+ This reports the total combined resolution of the TwinView display,
+ since it doesn't have any visibility into TwinView metamodes. There
+ is no way for the driver to prevent the server from turning on this
+ compatibility layer.
+
+ The intention is for X client applications to continue to use the
+ Xinerama extension to query the screen geometry. RandR 1.2 reports
+ its own Xinerama info for this purpose. I would recommend against
+ modifying xscreensaver to try to get this information from RandR.
+ */
+static monitor **
+randr_versus_xinerama_fight (Display *dpy, monitor **randr_monitors,
+ char **errP)
+{
+ monitor **xinerama_monitors;
+
+ if (!randr_monitors)
+ return 0;
+
+ xinerama_monitors = xinerama_scan_monitors (dpy, errP);
+ if (!xinerama_monitors)
+ return randr_monitors;
+
+ if (! layouts_differ_p (randr_monitors, xinerama_monitors))
+ {
+ free_monitors (xinerama_monitors);
+ return randr_monitors;
+ }
+ else if ( randr_monitors[0] && !randr_monitors[1] && /* 1 monitor */
+ xinerama_monitors[0] && xinerama_monitors[1]) /* >1 monitor */
+ {
+ *errP = append (*errP,
+ "WARNING: RANDR reports 1 screen but Xinerama\n"
+ "\t\treports multiple. Believing Xinerama.");
+ free_monitors (randr_monitors);
+ return xinerama_monitors;
+ }
+ else
+ {
+ *errP = append (*errP,
+ "WARNING: RANDR and Xinerama report different\n"
+ "\t\tscreen layouts! Believing RANDR.");
+ free_monitors (xinerama_monitors);
+ return randr_monitors;
+ }
+}
+
+#endif /* HAVE_RANDR && HAVE_XINERAMA */
+
+
#ifdef DEBUG_MULTISCREEN
/* If DEBUG_MULTISCREEN is defined, then in "-debug" mode, xscreensaver
for stress-testing purposes.
*/
static monitor **
-debug_scan_monitors (Display *dpy)
+debug_scan_monitors (Display *dpy, char **errP)
{
static const char * const geoms[] = {
"1600x1028+0+22",
#ifdef QUAD_MODE
static monitor **
-quadruple (monitor **monitors, Bool debug_p)
+quadruple (monitor **monitors, Bool debug_p, char **errP)
{
int i, j, count = 0;
monitor **monitors2;
{
saver_preferences *p = &si->prefs;
monitor **monitors = 0;
+ char *err = 0;
# ifdef DEBUG_MULTISCREEN
- if (! monitors) monitors = debug_scan_monitors (si->dpy);
+ if (! monitors) monitors = debug_scan_monitors (si->dpy, &err);
# endif
# ifdef HAVE_RANDR
if (! p->getviewport_full_of_lies_p)
- if (! monitors) monitors = randr_scan_monitors (si->dpy);
-# endif
+ if (! monitors) monitors = randr_scan_monitors (si->dpy, &err);
+
+# ifdef HAVE_XINERAMA
+ monitors = randr_versus_xinerama_fight (si->dpy, monitors, &err);
+# endif
+# endif /* HAVE_RANDR */
# ifdef HAVE_XF86VMODE
- if (! monitors) monitors = vidmode_scan_monitors (si->dpy);
+ if (! monitors) monitors = vidmode_scan_monitors (si->dpy, &err);
# endif
-# ifdef HAVE_XF86VMODE
- if (! monitors) monitors = xinerama_scan_monitors (si->dpy);
+# ifdef HAVE_XINERAMA
+ if (! monitors) monitors = xinerama_scan_monitors (si->dpy, &err);
# endif
- if (! monitors) monitors = basic_scan_monitors (si->dpy);
+ if (! monitors) monitors = basic_scan_monitors (si->dpy, &err);
# ifdef QUAD_MODE
if (p->quad_p)
- monitors = quadruple (monitors, p->debug_p);
+ monitors = quadruple (monitors, p->debug_p, &err);
# endif
+ if (monitors && err) monitors[0]->err = err;
+
return monitors;
}
}
+static Bool
+plausible_aspect_ratio_p (monitor **monitors)
+{
+ /* Modern wide-screen monitors come in the following aspect ratios:
+
+ One monitor: If you tack a 640x480 monitor
+ onto the right, the ratio is:
+ 16 x 9 --> 1.78
+ 852 x 480 --> 1.77 852+640 x 480 --> 3.11 "SD 480p"
+ 1280 x 720 --> 1.78 1280+640 x 720 --> 2.67 "HD 720p"
+ 1280 x 920 --> 1.39 1280+640 x 920 --> 2.09
+ 1366 x 768 --> 1.78 1366+640 x 768 --> 2.61 "HD 768p"
+ 1440 x 900 --> 1.60 1440+640 x 900 --> 2.31
+ 1680 x 1050 --> 1.60 1680+640 x 1050 --> 2.21
+ 1690 x 1050 --> 1.61 1690+640 x 1050 --> 2.22
+ 1920 x 1080 --> 1.78 1920+640 x 1080 --> 2.37 "HD 1080p"
+ 1920 x 1200 --> 1.60 1920+640 x 1200 --> 2.13
+ 2560 x 1600 --> 1.60 2560+640 x 1600 --> 2.00
+
+ So that implies that if we ever see an aspect ratio >= 2.0,
+ we can be pretty sure that the X server is lying to us, and
+ that's actually two monitors, not one.
+ */
+ if (monitors[0] && !monitors[1] && /* exactly 1 monitor */
+ monitors[0]->height &&
+ monitors[0]->width / (double) monitors[0]->height >= 1.9)
+ return False;
+ else
+ return True;
+}
+
+
/* Mark the ones that overlap, etc.
*/
static void
if (i != j &&
monitors[i]->sanity == S_SANE &&
monitors[j]->sanity == S_SANE &&
+ monitors[i]->screen == monitors[j]->screen &&
X2 >= X1 &&
Y2 >= Y1 &&
(X2+W2) <= (X1+W1) &&
{
if (monitors[i]->sanity != S_SANE) continue; /* already marked */
if (monitors[j]->sanity != S_SANE) continue;
+ if (monitors[i]->screen != monitors[j]->screen) continue;
if (monitors_overlap_p (monitors[i], monitors[j]))
{
}
}
- /* Finally, make sure all monitors are enclosed by their X screen.
+ /* Finally, make sure all monitors have sane positions and sizes.
Xinerama sometimes reports 1024x768 VPs at -1936862040, -1953705044.
*/
for (i = 0; i < count; i++)
{
- int sw = WidthOfScreen (monitors[i]->screen) * 2;
- int sh = HeightOfScreen (monitors[i]->screen) * 2;
if (monitors[i]->sanity != S_SANE) continue; /* already marked */
- if (X1 < 0 || Y1 < 0 ||
- W1 <= 0 || H1 <= 0 ||
- X1+W1 > sw || Y1+H1 > sh)
+ if (X1 < 0 || Y1 < 0 ||
+ W1 <= 0 || H1 <= 0 ||
+ X1+W1 >= 0x7FFF || Y1+H1 >= 0x7FFF)
{
monitors[i]->sanity = S_OFFSCREEN;
monitors[i]->enemy = 0;
int count = 0;
int good_count = 0;
int bad_count = 0;
+ int implausible_p = !plausible_aspect_ratio_p (monitors);
+
while (monitors[count])
{
if (monitors[count]->sanity == S_SANE)
count++;
}
+ if (monitors[0]->err) /* deferred error msg */
+ {
+ char *token = strtok (monitors[0]->err, "\n");
+ while (token)
+ {
+ fprintf (stderr, "%s: %s\n", blurb(), token);
+ token = strtok (0, "\n");
+ }
+ free (monitors[0]->err);
+ monitors[0]->err = 0;
+ }
+
if (count == 0)
fprintf (stderr, "%s: no screens!\n", blurb());
else
}
}
}
+
+ if (implausible_p)
+ fprintf (stderr,
+ "%s: WARNING: single screen aspect ratio is %dx%d = %.2f\n"
+ "%s: probable X server bug in Xinerama/RANDR!\n",
+ blurb(), monitors[0]->width, monitors[0]->height,
+ monitors[0]->width / (double) monitors[0]->height,
+ blurb());
}
}
if (ssi->screen != old_screen)
initialize_screen_root_widget (ssi);
- ssi->poll_mouse_last_root_x = -1;
- ssi->poll_mouse_last_root_y = -1;
+ ssi->last_poll_mouse.root_x = -1;
+ ssi->last_poll_mouse.root_y = -1;
ssi->x = m->x;
ssi->y = m->y;