[PATCH (v5) evdev 2/4] Add experimental XI 2.1 multitouch support

Peter Hutterer peter.hutterer at who-t.net
Thu Jan 20 21:34:59 PST 2011


On Wed, Jan 19, 2011 at 11:11:55PM +0000, Daniel Stone wrote:
> From: Chase Douglas <chase.douglas at canonical.com>
> 
> This multitouch addition only supports slotted MT evdev protocol
> devices. Support must be enabled at configure time using
> --enable-multitouch.
> 
> Signed-off-by: Chase Douglas <chase.douglas at canonical.com>
> ---
> 
> v5: Fixed merge conflicts.
>     Updated to new xf86PostTouchEvent API, including explicitly tracking
>     touchpoint creation and destruction, rather than relying on the server to
>     do it for us.
>     Removed mt_slot_map, as we no longer need to hand the server monotonic
>     touch IDs: it does that for us.
> 
>  configure.ac |   11 +++
>  src/evdev.c  |  217 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
>  src/evdev.h  |   25 ++++++-
>  3 files changed, 233 insertions(+), 20 deletions(-)
> 
> diff --git a/configure.ac b/configure.ac
> index 887021c..02ab67a 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
> +

default enabled if the server version is high enough please.
come to think of it, I don't see the need for this at all, might as well
build it in unconditionally. there's nothing controversial in here either,
it's pretty much the already existing framework extended.

>  # 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 393f443..32d9109 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

if you define those two, you might want to also define ABS_MT_TOUCH_MAJOR

> +
>  static char *evdevDefaults[] = {
>      "XkbRules",     "evdev",
>      "XkbModel",     "evdev",
> @@ -317,7 +325,7 @@ EvdevQueueKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value)
>      if ((pQueue = EvdevNextInQueue(pInfo)))
>      {
>          pQueue->type = EV_QUEUE_KEY;
> -        pQueue->key = code;
> +        pQueue->detail.key = code;
>          pQueue->val = value;
>      }
>  }
> @@ -330,7 +338,7 @@ EvdevQueueButtonEvent(InputInfoPtr pInfo, int button, int value)
>      if ((pQueue = EvdevNextInQueue(pInfo)))
>      {
>          pQueue->type = EV_QUEUE_BTN;
> -        pQueue->key = button;
> +        pQueue->detail.key = button;
>          pQueue->val = value;
>      }
>  }
> @@ -342,11 +350,27 @@ EvdevQueueProximityEvent(InputInfoPtr pInfo, int value)
>      if ((pQueue = EvdevNextInQueue(pInfo)))
>      {
>          pQueue->type = EV_QUEUE_PROXIMITY;
> -        pQueue->key = 0;
> +        pQueue->detail.key = 0;
>          pQueue->val = value;
>      }
>  }
>  
> +#ifdef MULTITOUCH
> +void
> +EvdevQueueTouchEvent(InputInfoPtr pInfo, unsigned int touch, ValuatorMask *mask,
> +                     uint16_t evtype)
> +{
> +    EventQueuePtr pQueue;
> +    if ((pQueue = EvdevNextInQueue(pInfo)))
> +    {
> +        pQueue->type = EV_QUEUE_TOUCH;
> +        pQueue->detail.touch = touch;
> +        valuator_mask_copy(pQueue->touchMask, mask);
> +        pQueue->val = evtype;
> +    }
> +}
> +#endif
> +
>  /**
>   * Post button event right here, right now.
>   * Interface for MB emulation since these need to post immediately.
> @@ -615,6 +639,53 @@ EvdevProcessRelativeMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
>      }
>  }
>  
> +#ifdef MULTITOUCH
> +static void
> +EvdevProcessTouch(InputInfoPtr pInfo)
> +{
> +    EvdevPtr pEvdev = pInfo->private;
> +
> +    if (pEvdev->cur_slot < 0 || !pEvdev->mtMask)
> +        return;
> +
> +    if (pEvdev->close_slot) {
> +        EvdevQueueTouchEvent(pInfo, pEvdev->cur_slot, pEvdev->mtMask,
> +                             XI_TouchEnd);
> +        pEvdev->close_slot = 0;
> +    } else {
> +        EvdevQueueTouchEvent(pInfo, pEvdev->cur_slot, pEvdev->mtMask,
> +                             pEvdev->open_slot ? XI_TouchBegin :
> +                                                 XI_TouchMotion);
> +        pEvdev->open_slot = 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 if (ev->code == ABS_MT_TRACKING_ID) {
> +        if (ev->value >= 0)
> +            pEvdev->open_slot = 1;
> +        else
> +            pEvdev->close_slot = 1;
> +    } else {
> +        map = pEvdev->axis_map[ev->code] - pEvdev->num_vals;

not sure if i've asked this before, but what's the deal with - num_vals
here?

> +        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.
>   */
> @@ -638,9 +709,13 @@ EvdevProcessAbsoluteMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
>      if (EvdevWheelEmuFilterMotion(pInfo, ev))
>          return;
>  
> -    map = pEvdev->axis_map[ev->code];
> -    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;
> +    }
>  }
>  
>  /**
> @@ -736,6 +811,9 @@ EvdevPostProximityEvents(InputInfoPtr pInfo, int which, int num_v, int first_v,
>          switch (pEvdev->queue[i].type) {
>              case EV_QUEUE_KEY:
>              case EV_QUEUE_BTN:
> +#ifdef MULTITOUCH
> +            case EV_QUEUE_TOUCH:
> +#endif
>                  break;
>              case EV_QUEUE_PROXIMITY:
>                  if (pEvdev->queue[i].val == which)
> @@ -758,23 +836,30 @@ static void EvdevPostQueuedEvents(InputInfoPtr pInfo, int num_v, int first_v,
>      for (i = 0; i < pEvdev->num_queue; i++) {
>          switch (pEvdev->queue[i].type) {
>          case EV_QUEUE_KEY:
> -            xf86PostKeyboardEvent(pInfo->dev, pEvdev->queue[i].key,
> +            xf86PostKeyboardEvent(pInfo->dev, pEvdev->queue[i].detail.key,
>                                    pEvdev->queue[i].val);
>              break;
>          case EV_QUEUE_BTN:
>  #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
>              if (pEvdev->abs_queued && pEvdev->in_proximity) {
> -                xf86PostButtonEventP(pInfo->dev, 1, pEvdev->queue[i].key,
> +                xf86PostButtonEventP(pInfo->dev, 1, pEvdev->queue[i].detail.key,
>                                       pEvdev->queue[i].val, first_v, num_v,
>                                       v + first_v);
>  
>              } else
>  #endif
> -                xf86PostButtonEvent(pInfo->dev, 0, pEvdev->queue[i].key,
> +                xf86PostButtonEvent(pInfo->dev, 0, pEvdev->queue[i].detail.key,
>                                      pEvdev->queue[i].val, 0, 0);
>              break;
>          case EV_QUEUE_PROXIMITY:
>              break;
> +#ifdef MULTITOUCH
> +        case EV_QUEUE_TOUCH:
> +            xf86PostTouchEvent(pInfo->dev, pEvdev->queue[i].detail.touch,
> +                               pEvdev->queue[i].val, 0,
> +                               pEvdev->queue[i].touchMask);
> +            break;
> +#endif
>          }
>      }
>  }
> @@ -793,6 +878,7 @@ EvdevProcessSyncEvent(InputInfoPtr pInfo, struct input_event *ev)
>      EvdevProcessProximityState(pInfo);
>  
>      EvdevProcessValuators(pInfo);
> +    EvdevProcessTouch(pInfo);
>  
>      EvdevPostProximityEvents(pInfo, TRUE, num_v, first_v, v);
>      EvdevPostRelativeMotionEvents(pInfo, num_v, first_v, v);
> @@ -801,7 +887,6 @@ EvdevProcessSyncEvent(InputInfoPtr pInfo, struct input_event *ev)
>      EvdevPostProximityEvents(pInfo, FALSE, num_v, first_v, v);
>  
>      memset(pEvdev->delta, 0, sizeof(pEvdev->delta));
> -    memset(pEvdev->queue, 0, sizeof(pEvdev->queue));
>      if (pEvdev->mask)
>          valuator_mask_zero(pEvdev->mask);
>      pEvdev->num_queue = 0;
> @@ -1268,7 +1353,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;
> @@ -1277,15 +1362,32 @@ EvdevAddAbsClass(DeviceIntPtr device)
>      if (!TestBit(EV_ABS, pEvdev->bitmask))
>              return !Success;
>  
> -    num_axes = EvdevCountBits(pEvdev->abs_bitmask, NLONGS(ABS_MAX));
> -    if (num_axes < 1)
> -        return !Success;
> +    num_axes = CountBits((uint8_t *)pEvdev->abs_bitmask, ABS_MT_SLOT);
> +    num_mt_axes = CountBits((uint8_t *)pEvdev->abs_bitmask, ABS_MAX) - num_axes;
>  
>      if (num_axes > MAX_VALUATORS) {
>          xf86Msg(X_WARNING, "%s: found %d axes, limiting to %d.\n", device->name, num_axes, MAX_VALUATORS);
>          num_axes = MAX_VALUATORS;
>      }
>  
> +#ifdef MULTITOUCH
> +    if (TestBit(ABS_MT_SLOT, pEvdev->abs_bitmask))
> +        num_mt_axes--;
> +    if (TestBit(ABS_MT_TRACKING_ID, pEvdev->abs_bitmask))
> +        num_mt_axes--;
> +
> +    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;

shouldn't this be num_mt_axes + num_axes?

> +    }
> +#endif
> +
> +    if (num_axes < 1 && num_mt_axes < 1) {
> +        xf86Msg(X_WARNING, "%s: no absolute or touch axes found.\n",
> +                device->name);
> +        return !Success;

can we mkae this a goto out; as well?

> +    }
> +
>      pEvdev->num_vals = num_axes;
>      if (num_axes > 0) {
>          pEvdev->mask = valuator_mask_new(num_axes);
> @@ -1301,17 +1403,39 @@ EvdevAddAbsClass(DeviceIntPtr device)
>              goto out;
>          }
>      }
> -    atoms = malloc(pEvdev->num_vals * sizeof(Atom));
> +#ifdef MULTITOUCH
> +    if (num_mt_axes > 0) {
> +        pEvdev->mtMask = valuator_mask_new(num_mt_axes);
> +        if (!pEvdev->mtMask) {
> +            xf86Msg(X_ERROR, "%s: failed to allocate MT valuator mask.\n",
> +                    device->name);
> +            goto out;
> +        }
>  
> +        for (i = 0; i < EVDEV_MAXQUEUE; i++) {
> +            pEvdev->queue[i].touchMask =
> +                valuator_mask_new(num_mt_axes);
> +            if (!pEvdev->queue[i].touchMask) {
> +                xf86Msg(X_ERROR, "%s: failed to allocate MT valuator masks for "
> +                        "evdev event queue.\n", device->name);
> +                goto out;
> +            }
> +        }
> +    }
> +#endif
> +    atoms = malloc((pEvdev->num_vals + num_mt_axes) * sizeof(Atom));
> +
> +    i = 0;
>      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 ||
> +            axis == ABS_MT_TRACKING_ID)
>              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
> @@ -1326,7 +1450,26 @@ EvdevAddAbsClass(DeviceIntPtr device)
>          goto out;
>      }
>  
> -    for (axis = ABS_X; axis <= ABS_MAX; axis++) {
> +#ifdef MULTITOUCH
> +    if (num_mt_axes > 0)
> +    {
> +        int num_touches = 10;

a #define for this please

> +        int mode = pEvdev->flags & EVDEV_TOUCHPAD ?
> +            XIDependentTouch : XIDirectTouch;

some extra () would be nice, plus some more indentation on the second line.

> +
> +        if (pEvdev->absinfo[ABS_MT_SLOT].maximum > 0)
> +            num_touches = pEvdev->absinfo[ABS_MT_SLOT].maximum;
> +
> +        if (!InitTouchClassDeviceStruct(device, num_touches, mode,
> +                                        num_mt_axes)) {
> +            xf86Msg(X_ERROR, "%s: failed to initialize touch class device.\n",
> +                    device->name);
> +            goto out;
> +        }
> +    }
> +#endif
> +
> +    for (axis = ABS_X; axis < ABS_MT_SLOT; axis++) {
>          int axnum = pEvdev->axis_map[axis];
>          int resolution = 10000;
>  
> @@ -1353,6 +1496,25 @@ EvdevAddAbsClass(DeviceIntPtr device)
>          xf86InitValuatorDefaults(device, axnum);
>      }
>  
> +#ifdef MULTITOUCH
> +    for (axis = ABS_MT_TOUCH_MAJOR; 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++)
> @@ -1400,6 +1562,13 @@ EvdevAddAbsClass(DeviceIntPtr device)
>      return Success;
>  
>  out:
> +#ifdef MULTITOUCH
> +    free(pEvdev->mtMask);
> +    pEvdev->mtMask = NULL;
> +    for (i = 0; i < EVDEV_MAXQUEUE; i++)
> +        free(pEvdev->queue[i].touchMask);
> +        pEvdev->queue[i].touchMask = NULL;
> +#endif
>      free(pEvdev->mask);
>      pEvdev->mask = NULL;
>      free(pEvdev->oldMask);
> @@ -1749,6 +1918,9 @@ EvdevProc(DeviceIntPtr device, int what)
>  {
>      InputInfoPtr pInfo;
>      EvdevPtr pEvdev;
> +#ifdef MULTITOUCH
> +    int i;
> +#endif
>  
>      pInfo = device->public.devicePrivate;
>      pEvdev = pInfo->private;
> @@ -1785,6 +1957,11 @@ EvdevProc(DeviceIntPtr device, int what)
>          free(pEvdev->mask);
>          free(pEvdev->oldMask);
>          free(pEvdev->proxMask);
> +#ifdef MULTITOUCH
> +        free(pEvdev->mtMask);
> +        for (i = 0; i < EVDEV_MAXQUEUE; i++)
> +            free(pEvdev->queue[i].touchMask);

valuator_mask_free() here as well

> +#endif
>          EvdevRemoveDevice(pInfo);
>          pEvdev->min_maj = 0;
>  	break;
> @@ -2250,6 +2427,10 @@ EvdevPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
>      if (rc != Success)
>          goto error;
>  
> +#ifdef MULTITOUCH
> +    pEvdev->cur_slot = -1;
> +#endif
> +
>      /*
>       * We initialize pEvdev->in_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 6af145f..62619dd 100644
> --- a/src/evdev.h
> +++ b/src/evdev.h
> @@ -110,9 +110,20 @@ typedef struct {
>          EV_QUEUE_KEY,	/* xf86PostKeyboardEvent() */
>          EV_QUEUE_BTN,	/* xf86PostButtonEvent() */
>          EV_QUEUE_PROXIMITY, /* xf86PostProximityEvent() */
> +#ifdef MULTITOUCH
> +        EV_QUEUE_TOUCH,	/*xf86PostTouchEvent() */
> +#endif
>      } type;
> -    int key;		/* May be either a key code or button number. */
> -    int val;		/* State of the key/button; pressed or released. */
> +    union {
> +        int key;	/* May be either a key code or button number. */
> +#ifdef MULTITOUCH
> +        unsigned int touch; /* Touch ID */
> +#endif
> +    } detail;

we hardly ever see negative button numbers or keys, so you can just leave
that as detail and make it uint.

Cheers,
  Peter

> +    int val;	/* State of the key/button/touch; pressed or released. */
> +#ifdef MULTITOUCH
> +    ValuatorMask *touchMask;
> +#endif
>  } EventQueueRec, *EventQueuePtr;
>  
>  typedef struct {
> @@ -124,6 +135,12 @@ typedef struct {
>      ValuatorMask *mask;
>      ValuatorMask *oldMask;
>      ValuatorMask *proxMask;
> +#ifdef MULTITOUCH
> +    ValuatorMask *mtMask;
> +    int cur_slot;
> +    BOOL close_slot;
> +    BOOL open_slot;
> +#endif
>  
>      int flags;
>      int in_proximity;           /* device in proximity */
> @@ -202,6 +219,10 @@ typedef struct {
>  void EvdevQueueKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value);
>  void EvdevQueueButtonEvent(InputInfoPtr pInfo, int button, int value);
>  void EvdevQueueProximityEvent(InputInfoPtr pInfo, int value);
> +#ifdef MULTITOUCH
> +void EvdevQueueTouchEvent(InputInfoPtr pInfo, unsigned int touch,
> +                          ValuatorMask *mask, uint16_t type);
> +#endif
>  void EvdevPostButtonEvent(InputInfoPtr pInfo, int button, int value);
>  void EvdevQueueButtonClicks(InputInfoPtr pInfo, int button, int count);
>  void EvdevPostRelativeMotionEvents(InputInfoPtr pInfo, int num_v, int first_v,
> -- 
> 1.7.2.3



More information about the xorg-devel mailing list