[PATCHv2] dix: add 3x3 transformation matrix xinput property for multi-head handling

Peter Hutterer peter.hutterer at who-t.net
Tue May 11 20:41:11 PDT 2010


On Thu, May 06, 2010 at 04:34:42PM +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   ]
> 
> 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>
> ---
> 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 V1:
> - Implement using pixman as requested by Keith
> 
>  Xi/xiproperty.c              |    3 +-
>  dix/devices.c                |   63 ++++++++++++++++++++++++++++++++++++++++++
>  dix/getevents.c              |   19 ++++++++++++
>  include/inputstr.h           |    4 ++
>  include/xserver-properties.h |    7 ++++
>  5 files changed, 95 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..cb0c1a6 100644
> --- a/dix/devices.c
> +++ b/dix/devices.c
> @@ -77,6 +77,7 @@ SOFTWARE.
>  #include <X11/extensions/XI.h>
>  #include <X11/extensions/XI2.h>
>  #include <X11/extensions/XIproto.h>
> +#include <pixman.h>
>  #include "exglobals.h"
>  #include "exevents.h"
>  #include "xiquerydevice.h" /* for SizeDeviceClasses */
> @@ -91,6 +92,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 +158,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);
>      }

are we going to allow values > 1? If not, there should be BadValue checks
here.
  
>      return Success;
> @@ -183,6 +234,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 +286,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 197deb4..41cd55f 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)
>      xfree(list);
>  }
>  
> +static void
> +TransformAbsolute(DeviceIntPtr dev, int v[MAX_VALUATORS])

minor nitpick, the other static ones have lowercase first letters, so
transformAbsolute is preferable here.

Cheers,
  Peter

> +{
> +    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 6da3f38..315cc93 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"
> @@ -542,6 +543,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 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
> 


More information about the xorg-devel mailing list