[PATCHv3] dix: add 3x3 transformation matrix xinput property for multi-head handling
Peter Hutterer
peter.hutterer at who-t.net
Thu May 27 18:15:48 PDT 2010
On Tue, May 25, 2010 at 11:03:28AM +0200, Peter Korsgaard wrote:
> 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 ]
^ typo, fixed this up
>
> Which can be done using xinput:
>
> xinput set-prop <left> --type=float "Coordinate Transformation Matrix" \
> 0.5 0 0 0 1 0 0 0 1
>
> xinput set-prop <right> --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 <peter.korsgaard at barco.com>
thanks, I've finally had time to merge and test this. It's in my tree now,
I'll get it pulled by keith asap.
Cheers,
Peter
> ---
> 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.
>
> Changes since V2:
> - Renamed TransformAbsolute to transformAbsolute as requested by Peter
> - Check for invalid float values (nan/inifite) in DeviceSetProperty
> as requested by Peter and Simon
>
> Changes since V1:
> - Implement using pixman as requested by Keith
>
> Xi/xiproperty.c | 4 ++-
> dix/devices.c | 71 ++++++++++++++++++++++++++++++++++++++++++
> dix/getevents.c | 19 +++++++++++
> include/inputstr.h | 4 ++
> include/xserver-properties.h | 7 ++++
> 5 files changed, 104 insertions(+), 1 deletions(-)
>
> diff --git a/Xi/xiproperty.c b/Xi/xiproperty.c
> index 8bb1962..b4d939f 100644
> --- a/Xi/xiproperty.c
> +++ b/Xi/xiproperty.c
> @@ -177,7 +177,9 @@ static struct dev_properties
> {0, BTN_LABEL_PROP_BTN_TOOL_TRIPLETAP},
>
> {0, BTN_LABEL_PROP_BTN_GEAR_DOWN},
> - {0, BTN_LABEL_PROP_BTN_GEAR_UP}
> + {0, BTN_LABEL_PROP_BTN_GEAR_UP},
> +
> + {0, XI_PROP_TRANSFORM}
> };
>
> static long XIPropHandlerID = 1;
> diff --git a/dix/devices.c b/dix/devices.c
> index cf23bc6..b0b4653 100644
> --- a/dix/devices.c
> +++ b/dix/devices.c
> @@ -77,6 +77,8 @@ SOFTWARE.
> #include <X11/extensions/XI.h>
> #include <X11/extensions/XI2.h>
> #include <X11/extensions/XIproto.h>
> +#include <math.h>
> +#include <pixman.h>
> #include "exglobals.h"
> #include "exevents.h"
> #include "xiquerydevice.h" /* for SizeDeviceClasses */
> @@ -91,6 +93,48 @@ SOFTWARE.
>
> static void RecalculateMasterButtons(DeviceIntPtr slave);
>
> +static void
> +DeviceSetTransform(DeviceIntPtr dev, float *transform)
> +{
> + struct pixman_f_transform scale;
> + double sx, sy;
> + int x, y;
> +
> + /**
> + * 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;
> +
> + /* invscale */
> + pixman_f_transform_init_scale(&scale, sx, sy);
> + scale.m[0][2] = dev->valuator->axes[0].min_value;
> + scale.m[1][2] = dev->valuator->axes[1].min_value;
> +
> + /* transform */
> + for (y=0; y<3; y++)
> + for (x=0; x<3; x++)
> + dev->transform.m[y][x] = *transform++;
> +
> + pixman_f_transform_multiply(&dev->transform, &scale, &dev->transform);
> +
> + /* 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;
> +
> + pixman_f_transform_multiply(&dev->transform, &dev->transform, &scale);
> +}
> +
> /**
> * DIX property handler.
> */
> @@ -115,6 +159,21 @@ DeviceSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
> else if (!(*((CARD8*)prop->data)) && dev->enabled)
> DisableDevice(dev, TRUE);
> }
> + } else if (property == XIGetKnownProperty(XI_PROP_TRANSFORM))
> + {
> + float *f = (float*)prop->data;
> + int i;
> +
> + if (prop->format != 32 || prop->size != 9 ||
> + prop->type != XIGetKnownProperty(XATOM_FLOAT))
> + return BadValue;
> +
> + for (i=0; i<9; i++)
> + if (!isfinite(f[i]))
> + return BadValue;
> +
> + if (!checkonly)
> + DeviceSetTransform(dev, f);
> }
>
> return Success;
> @@ -183,6 +242,7 @@ AddInputDevice(ClientPtr client, DeviceProc deviceProc, Bool autoStart)
> int devid;
> char devind[MAXDEVICES];
> BOOL enabled;
> + float transform[9];
>
> /* Find next available id, 0 and 1 are reserved */
> memset(devind, 0, sizeof(char)*MAXDEVICES);
> @@ -234,6 +294,17 @@ AddInputDevice(ClientPtr client, DeviceProc deviceProc, Bool autoStart)
> XA_INTEGER, 8, PropModeReplace, 1, &enabled,
> FALSE);
> XISetDevicePropertyDeletable(dev, XIGetKnownProperty(XI_PROP_ENABLED), FALSE);
> +
> + /* unity matrix */
> + memset(transform, 0, sizeof(transform));
> + transform[0] = transform[4] = transform[8] = 1.0f;
> +
> + XIChangeDeviceProperty(dev, XIGetKnownProperty(XI_PROP_TRANSFORM),
> + XIGetKnownProperty(XATOM_FLOAT), 32,
> + PropModeReplace, 9, 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 62aaff9..eeef414 100644
> --- a/dix/getevents.c
> +++ b/dix/getevents.c
> @@ -33,6 +33,7 @@
> #include <X11/X.h>
> #include <X11/keysym.h>
> #include <X11/Xproto.h>
> +#include <math.h>
>
> #include "misc.h"
> #include "resource.h"
> @@ -56,6 +57,7 @@
>
> #include <X11/extensions/XI.h>
> #include <X11/extensions/XIproto.h>
> +#include <pixman.h>
> #include "exglobals.h"
> #include "exevents.h"
> #include "exglobals.h"
> @@ -997,6 +999,22 @@ FreeEventList(EventListPtr list, int num_events)
> free(list);
> }
>
> +static void
> +transformAbsolute(DeviceIntPtr dev, int v[MAX_VALUATORS])
> +{
> + struct pixman_f_vector p;
> +
> + /* p' = M * p in homogeneous coordinates */
> + p.v[0] = v[0];
> + p.v[1] = v[1];
> + p.v[2] = 1.0;
> +
> + pixman_f_transform_point(&dev->transform, &p);
> +
> + v[0] = lround(p.v[0]);
> + v[1] = lround(p.v[1]);
> +}
> +
> /**
> * Generate a series of xEvents (filled into the EventList) representing
> * pointer motion, or button presses. Xi and XKB-aware.
> @@ -1068,6 +1086,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 c5b9ff7..a83e863 100644
> --- a/include/inputstr.h
> +++ b/include/inputstr.h
> @@ -49,6 +49,7 @@ SOFTWARE.
> #ifndef INPUTSTRUCT_H
> #define INPUTSTRUCT_H
>
> +#include <pixman.h>
> #include "input.h"
> #include "window.h"
> #include "dixstruct.h"
> @@ -550,6 +551,9 @@ typedef struct _DeviceIntRec {
> XIPropertyPtr properties;
> XIPropertyHandlerPtr handlers; /* NULL-terminated */
> } properties;
> +
> + /* coordinate transformation matrix for absolute input devices */
> + struct pixman_f_transform transform;
> } DeviceIntRec;
>
> typedef struct {
> diff --git a/include/xserver-properties.h b/include/xserver-properties.h
> index 30e8efb..c6259ae 100644
> --- a/include/xserver-properties.h
> +++ b/include/xserver-properties.h
> @@ -35,6 +35,13 @@
> /* BOOL. If present, device is a virtual XTEST device */
> #define XI_PROP_XTEST_DEVICE "XTEST Device"
>
> +/* 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
>
More information about the xorg-devel
mailing list