1003
/* Returns the area of the screen which the xscreensaver window should cover.
1004
Normally this is the whole screen, but if the X server's root window is
1005
actually larger than the monitor's displayable area, then we want to
1006
operate in the currently-visible portion of the desktop instead.
1009
get_screen_viewport (saver_screen_info *ssi,
1010
int *x_ret, int *y_ret,
1011
int *w_ret, int *h_ret,
1012
int target_x, int target_y,
1015
int w = WidthOfScreen (ssi->screen);
1016
int h = HeightOfScreen (ssi->screen);
1018
# ifdef HAVE_XF86VMODE
1019
saver_info *si = ssi->global;
1020
saver_preferences *p = &si->prefs;
1023
XF86VidModeModeLine ml;
1025
Bool xinerama_p = si->xinerama_p;
1027
# ifndef HAVE_XINERAMA
1028
/* Even if we don't have the client-side Xinerama lib, check to see if
1029
the server supports Xinerama, so that we know to ignore the VidMode
1030
extension -- otherwise a server crash could result. Yay. */
1031
xinerama_p = XQueryExtension (si->dpy, "XINERAMA", &error, &event, &error);
1032
# endif /* !HAVE_XINERAMA */
1034
# ifdef HAVE_XINERAMA
1037
int mouse_p = (target_x != -1 && target_y != -1);
1041
/* If a mouse position wasn't passed in, assume we're talking about
1047
which = ssi->number;
1050
/* Find the Xinerama rectangle that contains the mouse position. */
1051
for (i = 0; i < si->nscreens; i++)
1054
target_x >= si->screens[i].x &&
1055
target_y >= si->screens[i].y &&
1056
target_x < si->screens[i].x + si->screens[i].width &&
1057
target_y < si->screens[i].y + si->screens[i].height)
1060
if (which == -1) which = 0; /* didn't find it? Use the first. */
1061
*x_ret = si->screens[which].x;
1062
*y_ret = si->screens[which].y;
1063
*w_ret = si->screens[which].width;
1064
*h_ret = si->screens[which].height;
1068
fprintf (stderr, "%s: %d: xinerama vp: %dx%d+%d+%d",
1070
si->screens[which].width, si->screens[which].height,
1071
si->screens[which].x, si->screens[which].y);
1073
fprintf (stderr, "; mouse at %d,%d", target_x, target_y);
1074
fprintf (stderr, ".\n");
1079
# endif /* HAVE_XINERAMA */
1081
if (!xinerama_p && /* Xinerama + VidMode = broken. */
1082
XF86VidModeQueryExtension (si->dpy, &event, &error) &&
1083
safe_XF86VidModeGetViewPort (si->dpy, ssi->number, &x, &y) &&
1084
XF86VidModeGetModeLine (si->dpy, ssi->number, &dot, &ml))
1089
*w_ret = ml.hdisplay;
1090
*h_ret = ml.vdisplay;
1092
if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
1093
/* There is no viewport -- the screen does not scroll. */
1097
/* Apparently some versions of XFree86 return nonsense here!
1098
I've had reports of 1024x768 viewports at -1936862040, -1953705044.
1099
So, sanity-check the values and give up if they are out of range.
1101
if (*x_ret < 0 || *x_ret >= w ||
1102
*y_ret < 0 || *y_ret >= h ||
1103
*w_ret <= 0 || *w_ret > w ||
1104
*h_ret <= 0 || *h_ret > h)
1106
static int warned_once = 0;
1109
fprintf (stderr, "\n"
1110
"%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
1111
"%s: The XVidMode server extension is returning nonsense.\n"
1112
"%s: Please report this bug to your X server vendor.\n\n",
1113
blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
1124
sprintf (msg, "%s: %d: vp is %dx%d+%d+%d",
1125
blurb(), ssi->number,
1126
*w_ret, *h_ret, *x_ret, *y_ret);
1129
if (p->getviewport_full_of_lies_p)
1131
/* XF86VidModeGetViewPort() tends to be full of lies on laptops
1132
that have a docking station or external monitor that runs in
1133
a different resolution than the laptop's screen:
1135
http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=81593
1136
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=208417
1137
http://bugs.xfree86.org/show_bug.cgi?id=421
1139
The XFree86 developers have closed the bug. As far as I can
1140
tell, their reason for this was, "this is an X server bug,
1141
but it's pretty hard to fix. Therefore, we are closing it."
1143
So, now there's a preference item for those unfortunate users to
1144
tell us not to trust a word that XF86VidModeGetViewPort() says.
1146
static int warned_once = 0;
1147
if (!warned_once && verbose_p)
1151
"%s: %d: XF86VidModeGetViewPort() says vp is %dx%d+%d+%d;\n"
1152
"%s: %d: assuming that is a pack of lies;\n"
1153
"%s: %d: using %dx%d+0+0 instead.\n",
1154
blurb(), ssi->number,
1155
*w_ret, *h_ret, *x_ret, *y_ret,
1156
blurb(), ssi->number,
1157
blurb(), ssi->number, w, h);
1168
/* Apparently, though the server stores the X position in increments of
1169
1 pixel, it will only make changes to the *display* in some other
1170
increment. With XF86_SVGA on a Thinkpad, the display only updates
1171
in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
1172
pixels in 16-bit mode. I don't know what it does in 24- and 32-bit
1173
mode, because I don't have enough video memory to find out.
1175
I consider it a bug that XF86VidModeGetViewPort() is telling me the
1176
server's *target* scroll position rather than the server's *actual*
1177
scroll position. David Dawes agrees, and says they may fix this in
1178
XFree86 4.0, but it's notrivial.
1180
He also confirms that this behavior is server-dependent, so the
1181
actual scroll position cannot be reliably determined by the client.
1182
So... that means the only solution is to provide a ``sandbox''
1183
around the blackout window -- we make the window be up to N pixels
1184
larger than the viewport on both the left and right sides. That
1185
means some part of the outer edges of each hack might not be
1186
visible, but screw it.
1188
I'm going to guess that 16 pixels is enough, and that the Y dimension
1189
doesn't have this problem.
1191
The drawback of doing this, of course, is that some of the screenhacks
1192
will still look pretty stupid -- for example, "slidescreen" will cut
1193
off the left and right edges of the grid, etc.
1196
if (x > 0 && x < w - ml.hdisplay) /* not at left edge or right edge */
1198
/* Round X position down to next lower multiple of FUDGE.
1199
Increase width by 2*FUDGE in case some server rounds up.
1201
*x_ret = ((x - 1) / FUDGE) * FUDGE;
1202
*w_ret += (FUDGE * 2);
1208
*w_ret != ml.hdisplay ||
1209
*h_ret != ml.vdisplay)
1210
sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
1211
*w_ret, *h_ret, *x_ret, *y_ret);
1214
fprintf (stderr, "%s.\n", msg);
1219
# endif /* HAVE_XF86VMODE */
1228
1020
static Bool error_handler_hit_p = False;
1589
1369
saver_preferences *p = &si->prefs;
1592
/* First update the size info in the saver_screen_info structs.
1595
# ifdef HAVE_XINERAMA
1598
/* As of XFree86 4.3.0, the RANDR and XINERAMA extensions cannot coexist.
1599
However, maybe they will someday, so I'm guessing that the right thing
1600
to do in that case will be to re-query the Xinerama rectangles after
1601
a RANDR size change is received: presumably, if the resolution of one
1602
or more of the monitors has changed, then the Xinerama rectangle
1603
corresponding to that monitor will also have been updated.
1606
XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &nscreens);
1608
if (nscreens != si->nscreens) {
1609
/* Apparently some Xinerama implementations let you use a hot-key
1610
to change the number of screens in use! This is, of course,
1611
documented nowhere. Let's try to do something marginally less
1614
fprintf (stderr, "%s: bad craziness: xinerama screen count changed "
1615
"from %d to %d!\n", blurb(), si->nscreens, nscreens);
1616
if (nscreens > si->nscreens)
1617
nscreens = si->nscreens;
1621
for (i = 0; i < nscreens; i++)
1623
saver_screen_info *ssi = &si->screens[i];
1625
(ssi->x != xsi[i].x_org ||
1626
ssi->y != xsi[i].y_org ||
1627
ssi->width != xsi[i].width ||
1628
ssi->height != xsi[i].height))
1630
"%s: %d: resize xinerama from %dx%d+%d+%d to %dx%d+%d+%d\n",
1632
ssi->width, ssi->height, ssi->x, ssi->y,
1633
xsi[i].width, xsi[i].height, xsi[i].x_org, xsi[i].y_org);
1635
ssi->x = xsi[i].x_org;
1636
ssi->y = xsi[i].y_org;
1637
ssi->width = xsi[i].width;
1638
ssi->height = xsi[i].height;
1643
# endif /* HAVE_XINERAMA */
1645
/* Not Xinerama -- get the real sizes of the root windows. */
1646
for (i = 0; i < si->nscreens; i++)
1648
saver_screen_info *ssi = &si->screens[i];
1649
XWindowAttributes xgwa;
1650
XGetWindowAttributes (si->dpy, RootWindowOfScreen (ssi->screen),
1654
(ssi->x != xgwa.x ||
1656
ssi->width != xgwa.width ||
1657
ssi->height != xgwa.height))
1659
"%s: %d: resize screen from %dx%d+%d+%d to %dx%d+%d+%d\n",
1661
ssi->width, ssi->height, ssi->x, ssi->y,
1662
xgwa.width, xgwa.height, xgwa.x, xgwa.y);
1666
ssi->width = xgwa.width;
1667
ssi->height = xgwa.height;
1671
/* Next, ensure that the screensaver windows are the right size, taking
1672
into account both the new size of the screen in question's root window,
1673
and any viewport within that.
1676
1372
for (i = 0; i < si->nscreens; i++)
1678
1374
saver_screen_info *ssi = &si->screens[i];
1679
1375
XWindowAttributes xgwa;
1680
XWindowChanges changes;
1681
int x, y, width, height;
1682
unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1377
/* Make sure a window exists -- it might not if a monitor was just
1378
added for the first time.
1380
if (! ssi->screensaver_window)
1382
initialize_screensaver_window_1 (ssi);
1385
"%s: %d: newly added window 0x%lx %dx%d+%d+%d\n",
1386
blurb(), i, (unsigned long) ssi->screensaver_window,
1387
ssi->width, ssi->height, ssi->x, ssi->y);
1390
/* Make sure the window is the right size -- it might not be if
1391
the monitor changed resolution, or if a badly-behaved hack
1684
1394
XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa);
1685
get_screen_viewport (ssi, &x, &y, &width, &height, -1, -1,
1686
(p->verbose_p && !si->screen_blanked_p));
1689
xgwa.width == width &&
1690
xgwa.height == height)
1691
continue; /* no change! */
1695
changes.width = width;
1696
changes.height = height;
1697
changes.border_width = 0;
1704
changes.width = changes.width / 2;
1708
"%s: %d: resize 0x%lx from %dx%d+%d+%d to %dx%d+%d+%d\n",
1709
blurb(), i, (unsigned long) ssi->screensaver_window,
1710
xgwa.width, xgwa.height, xgwa.x, xgwa.y,
1711
width, height, x, y);
1712
if (! safe_XConfigureWindow (si->dpy, ssi->screensaver_window,
1713
changesmask, &changes))
1716
"%s: %d: someone horked our saver window (0x%lx)! Unable to resize it!\n",
1717
blurb(), i, (unsigned long) ssi->screensaver_window);
1395
if (xgwa.x != ssi->x ||
1397
xgwa.width != ssi->width ||
1398
xgwa.height != ssi->height)
1400
XWindowChanges changes;
1401
unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
1404
changes.width = ssi->width;
1405
changes.height = ssi->height;
1406
changes.border_width = 0;
1410
"%s: %d: resize 0x%lx from %dx%d+%d+%d to %dx%d+%d+%d\n",
1411
blurb(), i, (unsigned long) ssi->screensaver_window,
1412
xgwa.width, xgwa.height, xgwa.x, xgwa.y,
1413
ssi->width, ssi->height, ssi->x, ssi->y);
1415
if (! safe_XConfigureWindow (si->dpy, ssi->screensaver_window,
1416
changesmask, &changes))
1417
fprintf (stderr, "%s: %d: someone horked our saver window"
1418
" (0x%lx)! Unable to resize it!\n",
1419
blurb(), i, (unsigned long) ssi->screensaver_window);
1422
/* Now (if blanked) make sure that it's mapped and running a hack --
1423
it might not be if we just added it. (We also might be re-using
1424
an old window that existed for a previous monitor that was
1425
removed and re-added.)
1427
Note that spawn_screenhack() calls select_visual() which may destroy
1428
and re-create the window via initialize_screensaver_window_1().
1430
if (si->screen_blanked_p)
1433
XInstallColormap (si->dpy, ssi->cmap);
1434
XMapRaised (si->dpy, ssi->screensaver_window);
1436
spawn_screenhack (ssi);
1438
/* Make sure the act of adding a screen doesn't present as
1439
pointer motion (and thus cause an unblank). */
1444
XQueryPointer (si->dpy, ssi->screensaver_window, &root, &child,
1445
&ssi->poll_mouse_last_root_x,
1446
&ssi->poll_mouse_last_root_y,
1452
/* Kill off any savers running on no-longer-extant monitors.
1454
for (; i < si->ssi_count; i++)
1456
saver_screen_info *ssi = &si->screens[i];
1458
kill_screenhack (ssi);
1459
if (ssi->screensaver_window)
1461
XUnmapWindow (si->dpy, ssi->screensaver_window);
1462
restore_real_vroot_1 (ssi);