dix: work around scaling issues during WarpPointer (#53037)

In WarpPointer calls, we get input in screen coordinates. They must be
scaled to device coordinates, and then back to screen coordinates for screen
crossing and root coordinates in events.

The rounding errors introduced (and clipping in core/XI 1.x events) can lead
to the actual position being different to the requested input coordinates.
e.g. 200 scales to 199.9999, truncated to 199 in the event.

Avoid this by simply overwriting the scaled screen coordinates with the
input coordinates for the POINTER_SCREEN case.

X.Org Bug 53037 <http://bugs.freedesktop.org/show_bug.cgi?id=53037>

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Keith Packard <keithp@keithp.com>
This commit is contained in:
Peter Hutterer 2012-08-08 11:34:32 +10:00
parent 1ebba43052
commit bafbd99080

View File

@ -1327,6 +1327,7 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type,
RawDeviceEvent *raw;
double screenx = 0.0, screeny = 0.0; /* desktop coordinate system */
double devx = 0.0, devy = 0.0; /* desktop-wide in device coords */
int sx, sy; /* for POINTER_SCREEN */
ValuatorMask mask;
ScreenPtr scr;
@ -1369,8 +1370,11 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type,
/* valuators are in driver-native format (rel or abs) */
if (flags & POINTER_ABSOLUTE) {
if (flags & POINTER_SCREEN) /* valuators are in screen coords */
if (flags & POINTER_SCREEN) { /* valuators are in screen coords */
sx = valuator_mask_get(&mask, 0);
sy = valuator_mask_get(&mask, 1);
scale_from_screen(pDev, &mask);
}
transformAbsolute(pDev, &mask);
clipAbsolute(pDev, &mask);
@ -1388,6 +1392,18 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type,
/* valuators are in device coordinate system in absolute coordinates */
scale_to_desktop(pDev, &mask, &devx, &devy, &screenx, &screeny);
/* #53037 XWarpPointer's scaling back and forth between screen and
device may leave us with rounding errors. End result is that the
pointer doesn't end up on the pixel it should.
Avoid this by forcing screenx/screeny back to what the input
coordinates were.
*/
if (flags & POINTER_SCREEN) {
screenx = sx;
screeny = sy;
}
scr = positionSprite(pDev, (flags & POINTER_ABSOLUTE) ? Absolute : Relative,
&mask, &devx, &devy, &screenx, &screeny);