[RFC XI 2.1 - xf86-input-evdev 2/3] Add experimental XI 2.1 multitouch support

Peter Hutterer peter.hutterer at who-t.net
Thu Nov 18 22:29:11 PST 2010


On Fri, Nov 12, 2010 at 05:35:12PM -0500, Chase Douglas wrote:
> From: Chase Douglas <chase.douglas at ubuntu.com>
> 
> This multitouch addition only supports slotted MT evdev protocol
> devices. Support must be enabled at configure time using
> --enable-multitouch. It is built on the masked valuator support in
> XInput ABI 12, so do not attempt to build it for an earlier ABI.
> 
> Signed-off-by: Chase Douglas <chase.douglas at canonical.com>
> ---
>  configure.ac |   11 ++++
>  src/evdev.c  |  159 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
>  src/evdev.h  |    5 ++
>  3 files changed, 167 insertions(+), 8 deletions(-)
> 
> diff --git a/configure.ac b/configure.ac
> index 07cd64e..cc9e721 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -47,6 +47,17 @@ XORG_DEFAULT_OPTIONS
>  # Obtain compiler/linker options from server and required extensions
>  PKG_CHECK_MODULES(XORG, xorg-server xproto inputproto)
>  
> +# Whether to include support for experimental XI 2.1 multitouch
> +AC_ARG_ENABLE(multitouch,
> +              AC_HELP_STRING([--enable-multitouch],
> +                             [Enable experimental XI 2.1 multitouch support [[default: disabled]]]),
> +              [MULTITOUCH=$enableval],
> +              [MULTITOUCH=no])
> +
> +if test "x$MULTITOUCH" = xyes; then
> +        AC_DEFINE(MULTITOUCH, 1, [Enable experimental multitouch code])
> +fi
> +

keep it on a branch and skip this bit.

I'll give a high-level review of these bits only because with the
requirements for masked valuators the ifdef stuff goes away and the next
patch will look quite different anyway.

the touch event processing skips the event queue completely. I don't think
that's a good idea and we've had problems with this in the past. In the past
this was because we'd post button events before motion events if they were
within the same sync window. I think this here has the danger to suffer from
the same issue, even though the current MT protocol makes this less of an
issue.

so I'd say queue them up like other events, then send them when the EV_SYN
comes around. this way the driver keeps the clean split between
queueing/processing.

on-the-side question that I just thought of: will having
XIDirectTouch/XIDependentTouch on a device be equally limiting as having a
single axis mode in XI 1.x?

Cheers,
  Peter

>  # Define a configure option for an alternate input module directory
>  AC_ARG_WITH(xorg-module-dir,
>              AC_HELP_STRING([--with-xorg-module-dir=DIR],
> diff --git a/src/evdev.c b/src/evdev.c
> index 4984019..8b13e9f 100644
> --- a/src/evdev.c
> +++ b/src/evdev.c
> @@ -87,6 +87,14 @@
>  #define MODEFLAG	8
>  #define COMPOSEFLAG	16
>  
> +#ifndef ABS_MT_SLOT
> +#define ABS_MT_SLOT 0x2f
> +#endif
> +
> +#ifndef ABS_MT_TRACKING_ID
> +#define ABS_MT_TRACKING_ID 0x39
> +#endif
> +
>  static char *evdevDefaults[] = {
>      "XkbRules",     "evdev",
>      "XkbModel",     "evdev",
> @@ -743,6 +751,60 @@ EvdevProcessRelativeMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
>      }
>  }
>  
> +#ifdef MULTITOUCH
> +static void
> +EvdevProcessTouch(InputInfoPtr pInfo)
> +{
> +    EvdevPtr pEvdev = pInfo->private;
> +    int map = pEvdev->axis_map[ABS_MT_TRACKING_ID] - pEvdev->num_vals;
> +
> +    if (pEvdev->cur_slot < 0 || !pEvdev->mtMask)
> +        return;
> +
> +    if (valuator_mask_isset(pEvdev->mtMask, map) &&
> +        valuator_mask_get(pEvdev->mtMask, map) < 0) {
> +        valuator_mask_set(pEvdev->mtMask, map,
> +                          pEvdev->mt_slot_map[pEvdev->cur_slot]);
> +        xf86PostTouchEvent(pInfo->dev, pEvdev->mtMask, 1);
> +        pEvdev->mt_slot_map[pEvdev->cur_slot] = -1;
> +    } else {
> +        if (pEvdev->mt_slot_map[pEvdev->cur_slot] < 0)
> +            if (!valuator_mask_isset(pEvdev->mtMask, map))
> +                xf86Msg(X_WARNING, "%s: New touch without tracking ID.\n",
> +                        pInfo->dev->name);
> +            else {
> +                int tracking_id = valuator_mask_get(pEvdev->mtMask, map);
> +                pEvdev->mt_slot_map[pEvdev->cur_slot] = tracking_id;
> +            }
> +        else if (!valuator_mask_isset(pEvdev->mtMask, map))
> +            valuator_mask_set(pEvdev->mtMask, map,
> +                              pEvdev->mt_slot_map[pEvdev->cur_slot]);
> +
> +        xf86PostTouchEvent(pInfo->dev, pEvdev->mtMask, 0);
> +    }
> +
> +    valuator_mask_zero(pEvdev->mtMask);
> +}
> +
> +static void
> +EvdevProcessTouchEvent(InputInfoPtr pInfo, struct input_event *ev)
> +{
> +    EvdevPtr pEvdev = pInfo->private;
> +    int map;
> +
> +    if (ev->code == ABS_MT_SLOT) {
> +        EvdevProcessTouch(pInfo);
> +        pEvdev->cur_slot = ev->value;
> +    } else {
> +        map = pEvdev->axis_map[ev->code] - pEvdev->num_vals;
> +        valuator_mask_set(pEvdev->mtMask, map, ev->value);
> +    }
> +}
> +#else
> +#define EvdevProcessTouch(pInfo)
> +#define EvdevProcessTouchEvent(pInfo, ev)
> +#endif /* MULTITOUCH */
> +
>  /**
>   * Take the absolute motion input event and process it accordingly.
>   */
> @@ -766,11 +828,16 @@ EvdevProcessAbsoluteMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
>      if (EvdevWheelEmuFilterMotion(pInfo, ev))
>          return;
>  
> -    map = pEvdev->axis_map[ev->code];
>  #ifdef HAVE_MASKED_VALUATORS
> -    valuator_mask_set(pEvdev->mask, map, value);
> -    pEvdev->abs_queued = 1;
> +    if (ev->code >= ABS_MT_SLOT)
> +        EvdevProcessTouchEvent(pInfo, ev);
> +    else {
> +        map = pEvdev->axis_map[ev->code];
> +        valuator_mask_set(pEvdev->mask, map, value);
> +        pEvdev->abs_queued = 1;
> +    }
>  #else
> +    map = pEvdev->axis_map[ev->code];
>      pEvdev->vals[map] = value;
>      if (ev->code == ABS_X)
>          pEvdev->abs_queued |= ABS_X_VALUE;
> @@ -936,6 +1003,7 @@ EvdevProcessSyncEvent(InputInfoPtr pInfo, struct input_event *ev)
>  
>  #ifdef HAVE_MASKED_VALUATORS
>      EvdevProcessValuators(pInfo);
> +    EvdevProcessTouch(pInfo);
>  #else
>      EvdevProcessValuators(pInfo, v, &num_v, &first_v);
>  #endif
> @@ -1416,7 +1484,7 @@ EvdevAddAbsClass(DeviceIntPtr device)
>  {
>      InputInfoPtr pInfo;
>      EvdevPtr pEvdev;
> -    int num_axes, axis, i = 0;
> +    int num_axes, num_mt_axes, axis, i = 0;
>      Atom *atoms;
>  
>      pInfo = device->public.devicePrivate;
> @@ -1425,7 +1493,13 @@ EvdevAddAbsClass(DeviceIntPtr device)
>      if (!TestBit(EV_ABS, pEvdev->bitmask))
>              return !Success;
>  
> +#ifdef HAVE_MASKED_VALUATORS
> +    num_axes = CountBits((uint8_t *)pEvdev->abs_bitmask, ABS_MT_SLOT);
> +    num_mt_axes = CountBits((uint8_t *)pEvdev->abs_bitmask, ABS_MAX) - num_axes;
> +#else
>      num_axes = EvdevCountBits(pEvdev->abs_bitmask, NLONGS(ABS_MAX));
> +    num_mt_axes = 0;
> +#endif
>      if (num_axes < 1)
>          return !Success;
>  
> @@ -1434,6 +1508,13 @@ EvdevAddAbsClass(DeviceIntPtr device)
>          num_axes = MAX_VALUATORS;
>      }
>  
> +#ifdef MULTITOUCH
> +    if (num_mt_axes > MAX_VALUATORS) {
> +        xf86Msg(X_WARNING, "%s: found %d MT axes, limiting to %d.\n", device->name, num_axes, MAX_VALUATORS);
> +        num_mt_axes = MAX_VALUATORS;
> +    }
> +#endif
> +
>      pEvdev->num_vals = num_axes;
>  #ifdef HAVE_MASKED_VALUATORS
>      if (num_axes > 0) {
> @@ -1444,21 +1525,28 @@ EvdevAddAbsClass(DeviceIntPtr device)
>          if (!pEvdev->oldMask)
>              goto out;
>      }
> +#ifdef MULTITOUCH
> +    if (num_mt_axes > 0) {
> +        pEvdev->mtMask = valuator_mask_new(num_mt_axes);
> +        if (!pEvdev->mtMask)
> +            goto out;
> +    }
> +#endif
>  #else
>      memset(pEvdev->vals, 0, num_axes * sizeof(int));
>      memset(pEvdev->old_vals, -1, num_axes * sizeof(int));
>  #endif
> -    atoms = malloc(pEvdev->num_vals * sizeof(Atom));
> +    atoms = malloc((pEvdev->num_vals + num_mt_axes) * sizeof(Atom));
>  
>      for (axis = ABS_X; i < MAX_VALUATORS && axis <= ABS_MAX; axis++) {
>          pEvdev->axis_map[axis] = -1;
> -        if (!TestBit(axis, pEvdev->abs_bitmask))
> +        if (!TestBit(axis, pEvdev->abs_bitmask) || axis == ABS_MT_SLOT)
>              continue;
>          pEvdev->axis_map[axis] = i;
>          i++;
>      }
>  
> -    EvdevInitAxesLabels(pEvdev, pEvdev->num_vals, atoms);
> +    EvdevInitAxesLabels(pEvdev, pEvdev->num_vals + num_mt_axes, atoms);
>  
>      if (!InitValuatorClassDeviceStruct(device, num_axes,
>  #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
> @@ -1470,7 +1558,29 @@ EvdevAddAbsClass(DeviceIntPtr device)
>                                         GetMotionHistorySize(), Absolute))
>          goto out;
>  
> -    for (axis = ABS_X; axis <= ABS_MAX; axis++) {
> +#ifdef MULTITOUCH
> +    if (num_mt_axes > 0)
> +    {
> +        int num_touches = 10;
> +        int mode = pEvdev->flags & EVDEV_TOUCHPAD ?
> +            XIDependentTouch : XIDirectTouch;
> +
> +        if (pEvdev->absinfo[ABS_MT_SLOT].maximum > 0)
> +            num_touches = pEvdev->absinfo[ABS_MT_SLOT].maximum;
> +
> +        pEvdev->mt_slot_map = malloc(num_touches * sizeof(int));
> +        if (!pEvdev->mt_slot_map)
> +            goto out;
> +
> +        for (i = 0; i < num_touches; i++)
> +            pEvdev->mt_slot_map[i] = -1;
> +
> +        if (!InitTouchClassDeviceStruct(device, num_touches, mode, num_mt_axes))
> +            goto out;
> +    }
> +#endif
> +
> +    for (axis = ABS_X; axis < ABS_MT_SLOT; axis++) {
>          int axnum = pEvdev->axis_map[axis];
>          int resolution = 10000;
>  
> @@ -1500,6 +1610,25 @@ EvdevAddAbsClass(DeviceIntPtr device)
>  #endif
>      }
>  
> +#ifdef MULTITOUCH
> +    for (axis = ABS_MT_SLOT; axis <= ABS_MAX; axis++) {
> +        int axnum = pEvdev->axis_map[axis] - pEvdev->num_vals;
> +        int resolution = 10000;
> +
> +        if (axnum < 0)
> +            continue;
> +
> +        if (pEvdev->absinfo[axis].resolution)
> +            resolution = pEvdev->absinfo[axis].resolution * 1000;
> +
> +        xf86InitTouchValuatorAxisStruct(device, axnum,
> +                                        atoms[axnum + pEvdev->num_vals],
> +                                        pEvdev->absinfo[axis].minimum,
> +                                        pEvdev->absinfo[axis].maximum,
> +                                        pEvdev->absinfo[axis].resolution);
> +    }
> +#endif
> +
>      free(atoms);
>  
>      for (i = 0; i < ArrayLength(proximity_bits); i++)
> @@ -1539,6 +1668,12 @@ EvdevAddAbsClass(DeviceIntPtr device)
>  
>  out:
>  #ifdef HAVE_MASKED_VALUATORS
> +#ifdef MULTITOUCH
> +    free(pEvdev->mtMask);
> +    pEvdev->mtMask = NULL;
> +    free(pEvdev->mt_slot_map);
> +    pEvdev->mt_slot_map = NULL;
> +#endif
>      free(pEvdev->mask);
>      pEvdev->mask = NULL;
>      free(pEvdev->oldMask);
> @@ -1923,6 +2058,10 @@ EvdevProc(DeviceIntPtr device, int what)
>          free(pEvdev->mask);
>          free(pEvdev->oldMask);
>          free(pEvdev->proxMask);
> +#ifdef MULTITOUCH
> +        free(pEvdev->mt_slot_map);
> +        free(pEvdev->mtMask);
> +#endif
>          EvdevRemoveDevice(pInfo);
>          pEvdev->min_maj = 0;
>  	break;
> @@ -2439,6 +2578,10 @@ EvdevPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
>      if (!EvdevOpenDevice(pInfo))
>          goto error;
>  
> +#ifdef MULTITOUCH
> +    pEvdev->cur_slot = -1;
> +#endif
> +
>      /*
>       * We initialize pEvdev->proximity to 1 so that device that doesn't use
>       * proximity will still report events.
> diff --git a/src/evdev.h b/src/evdev.h
> index bd5cb16..63c75a3 100644
> --- a/src/evdev.h
> +++ b/src/evdev.h
> @@ -129,6 +129,11 @@ typedef struct {
>      ValuatorMask *mask;
>      ValuatorMask *oldMask;
>      ValuatorMask *proxMask;
> +#ifdef MULTITOUCH
> +    ValuatorMask *mtMask;
> +    int *mt_slot_map;
> +    int cur_slot;
> +#endif
>  #else
>      int vals[MAX_VALUATORS];
>      int old_vals[MAX_VALUATORS]; /* Translate absolute inputs to relative */
> -- 
> 1.7.1
> 


More information about the xorg-devel mailing list