Hi all, > 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 evdev to allow us to do so.
I like the idea, but just if you haven't considered it: more or less the same thing could be done in dix (GetPointerEvents), where it would stand a remote chance to sync with randr, if desired. Also, in dix one would have more control when creating synthetic events, properly do Xi2 raw/screen coords etc. Not striking, but it may be better in the long run. Cheers, Simon Am 21.04.2010 08:47, schrieb Peter Korsgaard: > > The X server scales input coordinates to the dimensions of the shared > (total) 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 the X server. > > 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 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 "Evdev Coordinate Transformation Matrix" \ > 0.5 0 0 0 1 0 0 0 1 > > xinput set-prop <right> --type=float "Evdev Coordinate Transformation Matrix" > \ > 0.5 0 0.5 0 1 0 0 0 1 > > This is general enough to replace the various tweaks we have in evdev > (InvertX/Y, Calibration, SwapAxes), but I have left them for now. > > Signed-off-by: Peter Korsgaard <peter.korsga...@barco.com> > Acked-by: Oliver McFadden <oliver.mcfad...@nokia.com> > --- > Changes since V1: > - Add missing xfree() noticed by Oliver > > include/evdev-properties.h | 7 ++ > man/evdev.man | 13 ++++ > src/evdev.c | 139 > +++++++++++++++++++++++++++++++++++++++++++- > src/evdev.h | 2 + > 4 files changed, 160 insertions(+), 1 deletions(-) > > diff --git a/include/evdev-properties.h b/include/evdev-properties.h > index 7df2876..7417a5b 100644 > --- a/include/evdev-properties.h > +++ b/include/evdev-properties.h > @@ -66,4 +66,11 @@ > /* BOOL */ > #define EVDEV_PROP_SWAP_AXES "Evdev Axes Swap" > > +/* Coordinate transformation matrix */ > +/* FLOAT, 9 values in row-major order, coordinates in 0..1 range: */ > +/* [c0 c1 c2] [x] */ > +/* [c5 c4 c5] * [y] */ > +/* [c6 c7 c8] [1] */ > +#define EVDEV_PROP_TRANSFORM "Evdev Coordinate Transformation Matrix" > + > #endif > diff --git a/man/evdev.man b/man/evdev.man > index 49ab12c..21d7aa4 100644 > --- a/man/evdev.man > +++ b/man/evdev.man > @@ -161,6 +161,15 @@ originally reported by the kernel (e.g. touchscreens). > The scaling to the > custom coordinate system is done in-driver and the X server is unaware of > the transformation. Property: "Evdev Axis Calibration". > .TP 7 > +.BI "Option \*qTransformationMatrix\*q \*q" "c0 c1 c2 c3 c4 c5 c6 c7 c8" \*q > +Applies the 3x3 transformation matrix defined by c0..c8 in row-major > +order to the device coordinates before passing them on to the X > +server. The coefficients are floating point values, and the > +coordinates are scaled to the 0..1 range before transformed. This > +feature can be used to restrict absolute devices (e.g. touchscreens) > +to specific outputs in multi-head setups. Property: "Evdev Coordinate > +Transformation Matrix". > +.TP 7 > .B Option \*qMode\*q \*qRelative\*q\fP|\fP\*qAbsolute\*q > Sets the mode of the device if device has absolute axes. > The default value for touchpads is relative, for other absolute. > @@ -196,6 +205,10 @@ driver. > 4 32-bit values, order min-x, max-x, min-y, max-y or 0 values to disable > in-driver axis calibration. > .TP 7 > +.BI "Evdev Coordinate Transformation Matrix" > +9 floating point values, defining the coordinate transformation matrix > +in row-major order. > +.TP 7 > .BI "Evdev Axis Inversion" > 2 boolean values (8 bit, 0 or 1), order X, Y. 1 inverts the axis. > .TP 7 > diff --git a/src/evdev.c b/src/evdev.c > index 0678edf..31da4bf 100644 > --- a/src/evdev.c > +++ b/src/evdev.c > @@ -32,6 +32,8 @@ > #endif > #include <xorg-server.h> > > +#include <math.h> > + > #include <X11/keysym.h> > #include <X11/extensions/XI.h> > > @@ -119,6 +121,7 @@ static Atom prop_calibration = 0; > static Atom prop_swap = 0; > static Atom prop_axis_label = 0; > static Atom prop_btn_label = 0; > +static Atom prop_transform = 0; > #endif > > /* All devices the evdev driver has allocated and knows about. > @@ -347,6 +350,35 @@ EvdevQueueButtonClicks(InputInfoPtr pInfo, int button, > int count) > } > } > > +static int > +EvdevClip(InputInfoPtr pInfo, float f, int axis) > +{ > + EvdevPtr pEvdev = pInfo->private; > + int v; > + > + v = lroundf(f); > + > + if (v > pEvdev->absinfo[axis].maximum) > + v = pEvdev->absinfo[axis].maximum; > + else if (v < pEvdev->absinfo[axis].minimum) > + v = pEvdev->absinfo[axis].minimum; > + > + return v; > +} > + > +static void > +EvdevTransformAbsolute(InputInfoPtr pInfo, int v[MAX_VALUATORS]) > +{ > + EvdevPtr pEvdev = pInfo->private; > + float x, y, *t = pEvdev->transform; > + > + x = t[0]*v[0] + t[1]*v[1] + t[2]; > + y = t[3]*v[0] + t[4]*v[1] + t[5]; > + > + v[0] = EvdevClip(pInfo, x, ABS_X); > + v[1] = EvdevClip(pInfo, y, ABS_Y); > +} > + > #define ABS_X_VALUE 0x1 > #define ABS_Y_VALUE 0x2 > #define ABS_VALUE 0x4 > @@ -447,6 +479,8 @@ EvdevProcessValuators(InputInfoPtr pInfo, int > v[MAX_VALUATORS], int *num_v, > v[1] = (pEvdev->absinfo[ABS_Y].maximum - v[1] + > pEvdev->absinfo[ABS_Y].minimum); > > + EvdevTransformAbsolute(pInfo, v); > + > *num_v = pEvdev->num_vals; > *first_v = 0; > } > @@ -1993,12 +2027,77 @@ EvdevSetCalibration(InputInfoPtr pInfo, int > num_calibration, int calibration[4]) > } > } > > +/* a *= b */ > +static void > +EvdevMatrixMul(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 > +EvdevSetTransform(InputInfoPtr pInfo, float *transform) > +{ > + EvdevPtr pEvdev = pInfo->private; > + 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 = pEvdev->absinfo[ABS_X].maximum - pEvdev->absinfo[ABS_X].minimum; > + sy = pEvdev->absinfo[ABS_Y].maximum - pEvdev->absinfo[ABS_Y].minimum; > + > + t = pEvdev->transform; > + memset(t, 0, sizeof(pEvdev->transform)); > + > + t[0] = sx; > + t[2] = pEvdev->absinfo[ABS_X].minimum; > + t[4] = sy; > + t[5] = pEvdev->absinfo[ABS_Y].minimum; > + t[8] = 1.0; > + > + EvdevMatrixMul(t, transform); > + > + memset(scale, 0, sizeof(scale)); > + scale[0] = 1.0 / sx; > + scale[2] = -pEvdev->absinfo[ABS_X].minimum / sx; > + scale[4] = 1.0 / sy; > + scale[5] = -pEvdev->absinfo[ABS_Y].minimum / sy; > + scale[8] = 1.0; > + > + EvdevMatrixMul(t, scale); > +} > + > static InputInfoPtr > EvdevPreInit(InputDriverPtr drv, IDevPtr dev, int flags) > { > InputInfoPtr pInfo; > const char *device, *str; > - int num_calibration = 0, calibration[4] = { 0, 0, 0, 0 }; > + int num_transform, num_calibration = 0, calibration[4] = { 0, 0, 0, 0 }; > + float transform[9]; > EvdevPtr pEvdev; > > if (!(pInfo = xf86AllocateInput(drv, 0))) > @@ -2035,6 +2134,9 @@ EvdevPreInit(InputDriverPtr drv, IDevPtr dev, int flags) > */ > pEvdev->tool = 1; > > + /* unity matrix */ > + pEvdev->transform[0] = pEvdev->transform[4] = pEvdev->transform[8] = > 1.0f; > + > device = xf86CheckStrOption(dev->commonOptions, "Device", NULL); > if (!device) { > xf86Msg(X_ERROR, "%s: No device specified.\n", pInfo->name); > @@ -2084,6 +2186,21 @@ EvdevPreInit(InputDriverPtr drv, IDevPtr dev, int > flags) > pInfo->name, num_calibration); > } > > + str = xf86CheckStrOption(pInfo->options, "TransformationMatrix", NULL); > + if (str) { > + num_transform = sscanf(str, "%f %f %f %f %f %f %f %f %f", > + &transform[0], &transform[1], &transform[2], > + &transform[3], &transform[4], &transform[5], > + &transform[6], &transform[7], &transform[8]); > + xfree(str); > + if (num_transform == 9) > + EvdevSetTransform(pInfo, transform); > + else > + xf86Msg(X_ERROR, > + "%s: Insufficient transformation factors (%d). Ignoring > transformation\n", > + pInfo->name, num_transform); > + } > + > /* Grabbing the event device stops in-kernel event forwarding. In other > words, it disables rfkill and the "Macintosh mouse button emulation". > Note that this needs a server that sets the console to RAW mode. */ > @@ -2523,6 +2640,18 @@ EvdevInitProperty(DeviceIntPtr dev) > XISetDevicePropertyDeletable(dev, prop_btn_label, FALSE); > } > #endif /* HAVE_LABELS */ > + > + prop_transform = MakeAtom(EVDEV_PROP_TRANSFORM, > + strlen(EVDEV_PROP_TRANSFORM), TRUE); > + > + rc = XIChangeDeviceProperty(dev, prop_transform, > + XIGetKnownProperty(XATOM_FLOAT), 32, > + PropModeReplace, 9, pEvdev->transform, > + FALSE); > + if (rc != Success) > + return; > + > + XISetDevicePropertyDeletable(dev, prop_transform, FALSE); > } > > } > @@ -2564,6 +2693,14 @@ EvdevSetProperty(DeviceIntPtr dev, Atom atom, > XIPropertyValuePtr val, > pEvdev->swap_axes = *((BOOL*)val->data); > } else if (atom == prop_axis_label || atom == prop_btn_label) > return BadAccess; /* Axis/Button labels can't be changed */ > + else if (atom == prop_transform) { > + if (val->format != 32 || val->size != 9 || > + val->type != XIGetKnownProperty(XATOM_FLOAT)) > + return BadMatch; > + > + if (!checkonly) > + EvdevSetTransform(pInfo, val->data); > + } > > return Success; > } > diff --git a/src/evdev.h b/src/evdev.h > index 1133985..258c302 100644 > --- a/src/evdev.h > +++ b/src/evdev.h > @@ -176,6 +176,8 @@ typedef struct { > int reopen_attempts; /* max attempts to re-open after read failure */ > int reopen_left; /* number of attempts left to re-open the device */ > OsTimerPtr reopen_timer; > + float transform[9]; /* 3x3 coordinate transformation matrix in row major > + order */ > > /* Cached info from device. */ > char name[1024]; _______________________________________________ xorg-devel@lists.x.org: X.Org development Archives: http://lists.x.org/archives/xorg-devel Info: http://lists.x.org/mailman/listinfo/xorg-devel