[PATCH] dix: the input matrix must work on pixels, not device ranges

Peter Hutterer peter.hutterer at who-t.net
Thu Apr 11 23:34:01 PDT 2013


The screen width is for most devices smaller than the device range.
Previously, we applied the matrix to the device coordinate range, then
converting into screen (subpixel) coordinates. The half-way point of a
device is likely somewhere inside a subpixel, just to the left of the pixel
we want (in a 50:50 configuration). Thus, the mapping is off by one pixel.
In a 50/50 mapping with a 50% offset this means that we hit the right-most
pixel of the left screen, even when we should only be bound to the right
screen.

Of course, since we've now moved the effective position of the cursor
compared to the input we need to re-scale into device coordinates so that
the valuators have the right values.

And the matrix now needs to be updated every time the screen configuration
changes. This could be fixed without the property hooks, but I'd rather keep
the ABI for backport-ability.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 dix/devices.c    | 10 +++++-----
 dix/getevents.c  | 34 +++++++++++++++++++++++++++++-----
 dix/inpututils.c | 23 +++++++++++++++++++++++
 include/input.h  |  1 +
 4 files changed, 58 insertions(+), 10 deletions(-)

diff --git a/dix/devices.c b/dix/devices.c
index be236dd..c397ff3 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -92,7 +92,7 @@ SOFTWARE.
 
 static void RecalculateMasterButtons(DeviceIntPtr slave);
 
-static void
+void
 DeviceSetTransform(DeviceIntPtr dev, float *transform_data)
 {
     struct pixman_f_transform scale;
@@ -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 = screenInfo.width - screenInfo.x;
+    sy = screenInfo.height - screenInfo.y;
 
     /* invscale */
     pixman_f_transform_init_scale(&scale, sx, sy);
@@ -129,8 +129,8 @@ DeviceSetTransform(DeviceIntPtr dev, float *transform_data)
 
     /* scale */
     pixman_f_transform_init_scale(&scale, 1.0 / sx, 1.0 / sy);
-    scale.m[0][2] = -dev->valuator->axes[0].min_value / sx;
-    scale.m[1][2] = -dev->valuator->axes[1].min_value / sy;
+    scale.m[0][2] = -screenInfo.x / sx;
+    scale.m[1][2] = -screenInfo.y / sy;
 
     pixman_f_transform_multiply(&dev->scale_and_transform, &dev->scale_and_transform, &scale);
 
diff --git a/dix/getevents.c b/dix/getevents.c
index 0ffc61a..a1a0400 100644
--- a/dix/getevents.c
+++ b/dix/getevents.c
@@ -1273,6 +1273,12 @@ transformAbsolute(DeviceIntPtr dev, ValuatorMask *mask)
 }
 
 static void
+transformAbsoluteScreen(DeviceIntPtr dev, double *x, double *y)
+{
+    transform(&dev->scale_and_transform, x, y);
+}
+
+static void
 storeLastValuators(DeviceIntPtr dev, ValuatorMask *mask,
                    int xaxis, int yaxis, double devx, double devy)
 {
@@ -1417,11 +1423,6 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type,
             sy = valuator_mask_get(&mask, 1);
             scale_from_screen(pDev, &mask, flags);
         }
-
-        transformAbsolute(pDev, &mask);
-        clipAbsolute(pDev, &mask);
-        if ((flags & POINTER_NORAW) == 0)
-            set_raw_valuators(raw, &mask, raw->valuators.data);
     }
     else {
         transformRelative(pDev, &mask);
@@ -1437,6 +1438,29 @@ 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);
 
+    /* The matrix is a screen-based matrix, so we need to apply it _after_
+       we had the coordinates scaled to screen coordinates. Otherwise,
+       applying the matrix on the input data only may land us inside a
+       subpixel, causing a off-by-one pixel pointer position */
+    if (flags & POINTER_ABSOLUTE) {
+        transformAbsoluteScreen(pDev, &screenx, &screeny);
+
+        devx = rescaleValuatorAxis(screenx, NULL, pDev->valuator->axes + 0,
+                                   screenInfo.x, screenInfo.width - 1);
+        devy = rescaleValuatorAxis(screenx, NULL, pDev->valuator->axes + 1,
+                                   screenInfo.y, screenInfo.height - 1);
+
+        if (valuator_mask_isset(&mask, 0))
+            valuator_mask_set_double(&mask, 0, devx);
+
+        if (valuator_mask_isset(&mask, 1))
+            valuator_mask_set_double(&mask, 1, devy);
+
+        clipAbsolute(pDev, &mask); /* set to axis boundaries */
+        if ((flags & POINTER_NORAW) == 0)
+            set_raw_valuators(raw, &mask, raw->valuators.data);
+    }
+
     /* #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.
diff --git a/dix/inpututils.c b/dix/inpututils.c
index 9e38e17..ef1bccf 100644
--- a/dix/inpututils.c
+++ b/dix/inpututils.c
@@ -39,6 +39,7 @@
 #include "eventstr.h"
 #include "scrnintstr.h"
 #include "optionstr.h"
+#include "xserver-properties.h"
 
 /* Check if a button map change is okay with the device.
  * Returns -1 for BadValue, as it collides with MappingBusy. */
@@ -738,6 +739,26 @@ point_on_screen(ScreenPtr pScreen, int x, int y)
 }
 
 /**
+ * Update all device's transformation matrix based.
+ */
+static void
+update_transformation_matrices(void)
+{
+    Atom prop = XIGetKnownProperty(XI_PROP_TRANSFORM);
+    DeviceIntPtr dev;
+
+    nt_list_for_each_entry(dev, inputInfo.devices, next) {
+        XIPropertyValuePtr val;
+
+        if (IsMaster(dev) || !dev->valuator)
+            continue;
+
+        if (XIGetDeviceProperty(dev, prop, &val) == Success)
+            DeviceSetTransform(dev, (float*)val->data);
+    }
+}
+
+/**
  * Update desktop dimensions on the screenInfo struct.
  */
 void
@@ -760,6 +781,8 @@ update_desktop_dimensions(void)
     screenInfo.y = y1;
     screenInfo.width = x2 - x1;
     screenInfo.height = y2 - y1;
+
+    update_transformation_matrices();
 }
 
 /*
diff --git a/include/input.h b/include/input.h
index 304895f..985dcba 100644
--- a/include/input.h
+++ b/include/input.h
@@ -503,6 +503,7 @@ extern int AttachDevice(ClientPtr client,
 
 extern _X_EXPORT DeviceIntPtr GetPairedDevice(DeviceIntPtr kbd);
 extern DeviceIntPtr GetMaster(DeviceIntPtr dev, int type);
+extern void DeviceSetTransform(DeviceIntPtr dev, float *data);
 
 extern _X_EXPORT int AllocDevicePair(ClientPtr client,
                                      const char *name,
-- 
1.8.1.4



More information about the xorg-devel mailing list