X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Fscreens.c;h=52320d65c359f110cde58a38be32e23d79ac25be;hb=f8cf5ac7b2f53510f80a0eaf286a25298be17bfe;hp=cef66b782850ac9dcbef843c3eccbb4df488d6b2;hpb=de460e831dc8578acfa8b72251ab9346c99c1f96;p=xscreensaver diff --git a/driver/screens.c b/driver/screens.c index cef66b78..52320d65 100644 --- a/driver/screens.c +++ b/driver/screens.c @@ -98,6 +98,20 @@ * 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 @@ -141,8 +155,13 @@ struct _monitor { 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) { @@ -151,6 +170,7 @@ free_monitors (monitor **monitors) while (*m2) { if ((*m2)->desc) free ((*m2)->desc); + if ((*m2)->err) free ((*m2)->err); free (*m2); m2++; } @@ -158,10 +178,24 @@ free_monitors (monitor **monitors) } +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; @@ -200,7 +234,7 @@ xinerama_scan_monitors (Display *dpy) #ifdef HAVE_XF86VMODE static monitor ** -vidmode_scan_monitors (Display *dpy) +vidmode_scan_monitors (Display *dpy, char **errP) { int event, error, nscreens, i; monitor **monitors; @@ -299,7 +333,7 @@ vidmode_scan_monitors (Display *dpy) #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; @@ -337,12 +371,19 @@ randr_scan_monitors (Display *dpy) # 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 */ { @@ -358,12 +399,17 @@ randr_scan_monitors (Display *dpy) 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; @@ -384,39 +430,37 @@ randr_scan_monitors (Display *dpy) 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); @@ -424,6 +468,25 @@ randr_scan_monitors (Display *dpy) } } + /* 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; } @@ -431,7 +494,7 @@ randr_scan_monitors (Display *dpy) static monitor ** -basic_scan_monitors (Display *dpy) +basic_scan_monitors (Display *dpy, char **errP) { int nscreens = ScreenCount (dpy); int i; @@ -454,6 +517,65 @@ basic_scan_monitors (Display *dpy) } +#if defined(HAVE_RANDR) && defined(HAVE_XINERAMA) + +/* From: Aaron Plattner + 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 @@ -462,7 +584,7 @@ basic_scan_monitors (Display *dpy) 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", @@ -509,7 +631,7 @@ debug_scan_monitors (Display *dpy) #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; @@ -549,31 +671,38 @@ scan_monitors (saver_info *si) { 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; } @@ -598,6 +727,38 @@ monitors_overlap_p (monitor *a, monitor *b) } +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 @@ -624,6 +785,7 @@ check_monitor_sanity (monitor **monitors) 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) && @@ -648,6 +810,7 @@ check_monitor_sanity (monitor **monitors) { 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])) { @@ -656,17 +819,15 @@ check_monitor_sanity (monitor **monitors) } } - /* 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; @@ -715,6 +876,8 @@ describe_monitor_layout (saver_info *si) 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) @@ -724,6 +887,18 @@ describe_monitor_layout (saver_info *si) 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 @@ -778,6 +953,14 @@ describe_monitor_layout (saver_info *si) } } } + + 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()); } } @@ -866,8 +1049,8 @@ update_screen_layout (saver_info *si) 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;