From 5cc2c96f824dbb28b9f8da61efc41596f8bd0561 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Sat, 9 Mar 2013 20:43:51 +1000 Subject: [PATCH 1/3] dix: pre-scale x by the screen:device:resolution ratio commit 61a99aff9d33728a0b67920254d2d4d79f80cf39 dix: pre-scale relative events from abs devices to desktop ratio (#31636) added pre-scaling of relative coordinates coming from absolute devices to undo uneven scaling based on the screen dimensions. Devices have their own device width/height ratio as well (in a specific resolution) and this must be applied for relative devices as well to avoid scaling of the relative events into the device's ratio. e.g. a Wacom Intuos4 6x9 is in 16:10 format with equal horiz/vert resolution (dpi). A movement by 1000/1000 coordinates is a perfect diagonal on the tablet and must be reflected as such on the screen. However, we map the relative device-coordinate events to absolute screen coordinates based on the axis ranges. This results in an effective scaling of 1000/(1000 * 1.6) and thus an uneven x/y axis movement - the y axis is always faster. So we need to pre-scale not only by the desktop dimenstions but also by the device width/height ratio _and_ the resolution ratio. Signed-off-by: Peter Hutterer --- dix/getevents.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/dix/getevents.c b/dix/getevents.c index 241c7ec0d..ac0ccb28f 100644 --- a/dix/getevents.c +++ b/dix/getevents.c @@ -770,6 +770,29 @@ add_to_scroll_valuator(DeviceIntPtr dev, ValuatorMask *mask, int valuator, doubl } +static void +scale_for_device_resolution(DeviceIntPtr dev, ValuatorMask *mask) +{ + double x; + ValuatorClassPtr v = dev->valuator; + int xrange = v->axes[0].max_value - v->axes[0].min_value + 1; + int yrange = v->axes[1].max_value - v->axes[1].min_value + 1; + + double screen_ratio = 1.0 * screenInfo.width/screenInfo.height; + double device_ratio = 1.0 * xrange/yrange; + double resolution_ratio = 1.0; + double ratio; + + if (!valuator_mask_fetch_double(mask, 0, &x)) + return; + + if (v->axes[0].resolution != 0 && v->axes[1].resolution != 0) + resolution_ratio = 1.0 * v->axes[0].resolution/v->axes[1].resolution; + + ratio = device_ratio/resolution_ratio/screen_ratio; + valuator_mask_set_double(mask, 0, x * ratio); +} + /** * Move the device's pointer by the values given in @valuators. * @@ -781,27 +804,14 @@ moveRelative(DeviceIntPtr dev, int flags, ValuatorMask *mask) { int i; Bool clip_xy = IsMaster(dev) || !IsFloating(dev); + ValuatorClassPtr v = dev->valuator; /* for abs devices in relative mode, we've just scaled wrong, since we mapped the device's shape into the screen shape. Undo this. */ - if ((flags & POINTER_ABSOLUTE) == 0 && dev->valuator && - dev->valuator->axes[0].min_value < dev->valuator->axes[0].max_value) { - - double ratio = 1.0 * screenInfo.width/screenInfo.height; - - if (ratio > 1.0) { - double y; - if (valuator_mask_fetch_double(mask, 1, &y)) { - y *= ratio; - valuator_mask_set_double(mask, 1, y); - } - } else { - double x; - if (valuator_mask_fetch_double(mask, 0, &x)) { - x *= ratio; - valuator_mask_set_double(mask, 0, x); - } - } + if ((flags & POINTER_ABSOLUTE) == 0 && v && v->numAxes > 1 && + v->axes[0].min_value < v->axes[0].max_value && + v->axes[1].min_value < v->axes[1].max_value) { + scale_for_device_resolution(dev, mask); } /* calc other axes, clip, drop back into valuators */ From 756ab88d93542f0589c9bf46f40ccc57df64f0fd Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 26 Apr 2013 15:10:08 +1000 Subject: [PATCH 2/3] dix: fix device scaling to use a [min,max[ range. defmin/defmax are screen coords and thus use a min-inclusive, max-exclusive range. device axes ranges are inclusive, so bump the max up by one to get the scaling right. This fixes off-by-one coordinate errors if the coordinate matrix is used to bind the device to a fraction of the screen. It introduces an off-by-one scaling error in the device coordinate range, but since most devices have a higher resolution than the screen (e.g. a Wacom I4 has 5080 dpi) the effect of this should be limited. This error manifests when we have numScreens > 1, as the scaling from desktop size back to screen size drops one device unit. Signed-off-by: Peter Hutterer --- dix/devices.c | 4 ++-- dix/getevents.c | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/dix/devices.c b/dix/devices.c index 9b6faee23..291b76773 100644 --- a/dix/devices.c +++ b/dix/devices.c @@ -112,8 +112,8 @@ DeviceSetTransform(DeviceIntPtr dev, float *transform_data) * Transform is the user supplied (affine) transform * InvScale scales coordinates back up into their native range */ - sx = dev->valuator->axes[0].max_value - dev->valuator->axes[0].min_value; - sy = dev->valuator->axes[1].max_value - dev->valuator->axes[1].min_value; + sx = dev->valuator->axes[0].max_value - dev->valuator->axes[0].min_value + 1; + sy = dev->valuator->axes[1].max_value - dev->valuator->axes[1].min_value + 1; /* invscale */ pixman_f_transform_init_scale(&scale, sx, sy); diff --git a/dix/getevents.c b/dix/getevents.c index ac0ccb28f..51d4fd4da 100644 --- a/dix/getevents.c +++ b/dix/getevents.c @@ -298,11 +298,11 @@ rescaleValuatorAxis(double coord, AxisInfoPtr from, AxisInfoPtr to, if (from && from->min_value < from->max_value) { fmin = from->min_value; - fmax = from->max_value; + fmax = from->max_value + 1; } if (to && to->min_value < to->max_value) { tmin = to->min_value; - tmax = to->max_value; + tmax = to->max_value + 1; } if (fmin == tmin && fmax == tmax) @@ -924,9 +924,9 @@ scale_to_desktop(DeviceIntPtr dev, ValuatorMask *mask, /* scale x&y to desktop coordinates */ *screenx = rescaleValuatorAxis(x, dev->valuator->axes + 0, NULL, - screenInfo.x, screenInfo.width - 1); + screenInfo.x, screenInfo.width); *screeny = rescaleValuatorAxis(y, dev->valuator->axes + 1, NULL, - screenInfo.y, screenInfo.height - 1); + screenInfo.y, screenInfo.height); *devx = x; *devy = y; @@ -1366,6 +1366,12 @@ QueuePointerEvents(DeviceIntPtr device, int type, * is the last coordinate on the first screen and must be rescaled for the * event to be m. XI2 clients that do their own coordinate mapping would * otherwise interpret the position of the device elsewere to the cursor. + * However, this scaling leads to losses: + * if we have two ScreenRecs we scale from e.g. [0..44704] (Wacom I4) to + * [0..2048[. that gives us 2047.954 as desktop coord, or the per-screen + * coordinate 1023.954. Scaling that back into the device coordinate range + * gives us 44703. So off by one device unit. It's a bug, but we'll have to + * live with it because with all this scaling, we just cannot win. * * @return the number of events written into events. */ From 6589f3b55e335eef6c658c8ba1fe15a062f7e31c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 14 May 2013 08:31:19 +1000 Subject: [PATCH 3/3] dix: devices must have valuators before touch is initialized Signed-off-by: Peter Hutterer --- dix/devices.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dix/devices.c b/dix/devices.c index 291b76773..527eea028 100644 --- a/dix/devices.c +++ b/dix/devices.c @@ -1647,6 +1647,7 @@ InitTouchClassDeviceStruct(DeviceIntPtr device, unsigned int max_touches, BUG_RETURN_VAL(device == NULL, FALSE); BUG_RETURN_VAL(device->touch != NULL, FALSE); + BUG_RETURN_VAL(device->valuator == NULL, FALSE); /* Check the mode is valid, and at least X and Y axes. */ BUG_RETURN_VAL(mode != XIDirectTouch && mode != XIDependentTouch, FALSE);