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 <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@barco.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Korsgaard
2010-05-25 11:03:28 +02:00
committed by Peter Hutterer
parent e4582d9e5c
commit 6cccf0131c
5 changed files with 104 additions and 1 deletions

View File

@@ -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;