From c2d6d501d8faf40c3e0033c5219382fb8df93b62 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Wed, 5 May 2010 17:22:54 +0200 Subject: [PATCH] dix: add 3x3 transformation matrix xinput property for multi-head handling For absolute input devices (E.G. touchscreens) in multi-head setups, we need a way to bind the device to an randr output. This adds the infrastructure to the server to allow us to do so. positionSprite() scales input coordinates to the dimensions of the shared (total) screen frame buffer, so to restrict motion to an output we need to scale/rotate/translate device coordinates to a subset of the frame buffer before passing them on to positionSprite. This is done here using a 3x3 transformation matrix, which is applied to the device coordinates using homogeneous coordinates, E.G.: [ c0 c1 c2 ] [ x ] [ c3 c4 c5 ] * [ y ] [ c6 c7 c8 ] [ 1 ] Notice: As input devices have varying input ranges, the coordinates are first scaled to the [0..1] range for generality, and afterwards scaled back up. E.G. for a dual head setup (using same resolution) next to each other, you would want to scale the X coordinates of the touchscreen connected to the both heads by 50%, and translate (offset) the coordinates of the rightmost head by 50%, or in matrix form: left: right: [ 0.5 0 0 ] [ 0.5 0 0.5 ] [ 0 1 0 ] [ 0 1 0 ] [ 0 0 1 ] [ 0 0 0 ] Which can be done using xinput: xinput set-prop --type=float "Coordinate Transformation Matrix" \ 0.5 0 0 0 1 0 0 0 1 xinput set-prop --type=float "Coordinate Transformation Matrix" \ 0.5 0 0.5 0 1 0 0 0 1 Likewise more complication setups involving more heads, rotation or different resolution can be handled. Signed-off-by: Peter Korsgaard --- This is equivalent to the evdev patch sent earlier: http://thread.gmane.org/gmane.comp.freedesktop.xorg.devel/7336 But moved into the server as requested as requested by Simon and Peter, so it's available for all input drivers. Xi/xiproperty.c | 3 +- dix/devices.c | 82 ++++++++++++++++++++++++++++++++++++++++++ dix/getevents.c | 33 +++++++++++++++++ include/inputstr.h | 3 ++ include/xserver-properties.h | 7 ++++ 5 files changed, 127 insertions(+), 1 deletions(-) diff --git a/Xi/xiproperty.c b/Xi/xiproperty.c index d265b67..b9bc102 100644 --- a/Xi/xiproperty.c +++ b/Xi/xiproperty.c @@ -179,7 +179,8 @@ static struct dev_properties {0, BTN_LABEL_PROP_BTN_GEAR_DOWN}, {0, BTN_LABEL_PROP_BTN_GEAR_UP}, - {0, XI_PROP_DEVICE_NODE} + {0, XI_PROP_DEVICE_NODE}, + {0, XI_PROP_TRANSFORM} }; static long XIPropHandlerID = 1; diff --git a/dix/devices.c b/dix/devices.c index a33df4d..8596ae4 100644 --- a/dix/devices.c +++ b/dix/devices.c @@ -91,6 +91,70 @@ SOFTWARE. static void RecalculateMasterButtons(DeviceIntPtr slave); +/* a *= b */ +static void +DeviceMatrixMul(float *a, float *b) +{ + int i, j, k; + float t[3]; + + for (i=0; i<3; i++) + { + for (j=0; j<3; j++) + { + t[j] = 0; + + for (k=0; k<3; k++) + { + t[j] += a[i*3 + k]*b[k*3 + j]; + } + } + memcpy(&a[i*3], t, sizeof(t)); + } +} + +static void +DeviceSetTransform(DeviceIntPtr dev, float *transform) +{ + float scale[9], *t; + float sx, sy; + + /** + * calculate combined transformation matrix: + * + * M = InvScale * Transform * Scale + * + * So we can later transform points using M * p + * + * Where: + * Scale scales coordinates into 0..1 range + * 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; + + t = dev->transform; + memset(t, 0, sizeof(dev->transform)); + + t[0] = sx; + t[2] = dev->valuator->axes[0].min_value; + t[4] = sy; + t[5] = dev->valuator->axes[1].min_value; + t[8] = 1.0; + + DeviceMatrixMul(t, transform); + + memset(scale, 0, sizeof(scale)); + scale[0] = 1.0 / sx; + scale[2] = -dev->valuator->axes[0].min_value / sx; + scale[4] = 1.0 / sy; + scale[5] = -dev->valuator->axes[1].min_value / sy; + scale[8] = 1.0; + + DeviceMatrixMul(t, scale); +} + /** * DIX property handler. */ @@ -115,6 +179,14 @@ DeviceSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, else if (!(*((CARD8*)prop->data)) && dev->enabled) DisableDevice(dev, TRUE); } + } else if (property == XIGetKnownProperty(XI_PROP_TRANSFORM)) + { + if (prop->format != 32 || prop->size != 9 || + prop->type != XIGetKnownProperty(XATOM_FLOAT)) + return BadValue; + + if (!checkonly) + DeviceSetTransform(dev, prop->data); } return Success; @@ -215,6 +287,9 @@ AddInputDevice(ClientPtr client, DeviceProc deviceProc, Bool autoStart) /* sprite defaults */ dev->spriteInfo = (SpriteInfoPtr)&dev[1]; + /* unity matrix */ + dev->transform[0] = dev->transform[4] = dev->transform[8] = 1.0f; + /* security creation/labeling check */ if (XaceHook(XACE_DEVICE_ACCESS, client, dev, DixCreateAccess)) { @@ -234,6 +309,13 @@ AddInputDevice(ClientPtr client, DeviceProc deviceProc, Bool autoStart) XA_INTEGER, 8, PropModeReplace, 1, &enabled, FALSE); XISetDevicePropertyDeletable(dev, XIGetKnownProperty(XI_PROP_ENABLED), FALSE); + + XIChangeDeviceProperty(dev, XIGetKnownProperty(XI_PROP_TRANSFORM), + XIGetKnownProperty(XATOM_FLOAT), 32, + PropModeReplace, 9, dev->transform, FALSE); + XISetDevicePropertyDeletable(dev, XIGetKnownProperty(XI_PROP_TRANSFORM), + FALSE); + XIRegisterPropertyHandler(dev, DeviceSetProperty, NULL, NULL); return dev; diff --git a/dix/getevents.c b/dix/getevents.c index 197deb4..b45762c 100644 --- a/dix/getevents.c +++ b/dix/getevents.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "misc.h" #include "resource.h" @@ -997,6 +998,37 @@ FreeEventList(EventListPtr list, int num_events) xfree(list); } +static int +DeviceClip(DeviceIntPtr dev, float f, AxisInfoPtr axis) +{ + int v; + + v = lroundf(f); + + if (v > axis->max_value) + v = axis->max_value; + else if (v < axis->min_value) + v = axis->min_value; + + return v; +} + +static void +TransformAbsolute(DeviceIntPtr dev, int v[MAX_VALUATORS]) +{ + float x, y, *m = dev->transform; + + /** + * v' = M * v in homogeneous coordinates, + * lowest row of matrix is assumed to be 0 0 1 + */ + x = m[0]*v[0] + m[1]*v[1] + m[2]; + y = m[3]*v[0] + m[4]*v[1] + m[5]; + + v[0] = DeviceClip(dev, x, &dev->valuator->axes[0]); + v[1] = DeviceClip(dev, y, &dev->valuator->axes[1]); +} + /** * Generate a series of xEvents (filled into the EventList) representing * pointer motion, or button presses. Xi and XKB-aware. @@ -1068,6 +1100,7 @@ GetPointerEvents(EventList *events, DeviceIntPtr pDev, int type, int buttons, scr->height); } + TransformAbsolute(pDev, valuators); moveAbsolute(pDev, &x, &y, first_valuator, num_valuators, valuators); } else { if (flags & POINTER_ACCELERATE) { diff --git a/include/inputstr.h b/include/inputstr.h index 6da3f38..db75ace 100644 --- a/include/inputstr.h +++ b/include/inputstr.h @@ -542,6 +542,9 @@ typedef struct _DeviceIntRec { XIPropertyPtr properties; XIPropertyHandlerPtr handlers; /* NULL-terminated */ } properties; + + /* 3x3 coordinate transformation matrix for abs devs in row major order */ + float transform[9]; } DeviceIntRec; typedef struct { diff --git a/include/xserver-properties.h b/include/xserver-properties.h index 6a05178..5a645f3 100644 --- a/include/xserver-properties.h +++ b/include/xserver-properties.h @@ -38,6 +38,13 @@ /* STRING. Device node path of device */ #define XI_PROP_DEVICE_NODE "Device Node" +/* Coordinate transformation matrix for absolute input devices + * FLOAT, 9 values in row-major order, coordinates in 0..1 range: + * [c0 c1 c2] [x] + * [c3 c4 c5] * [y] + * [c6 c7 c8] [1] */ +#define XI_PROP_TRANSFORM "Coordinate Transformation Matrix" + /* Pointer acceleration properties */ /* INTEGER of any format */ #define ACCEL_PROP_PROFILE_NUMBER "Device Accel Profile" -- 1.7.0