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

Daniel Stone daniel at fooishbar.org
Wed Jan 19 15:11:55 PST 2011


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
+
 # 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
+
 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;
+        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;
+    }
+#endif
+
+    if (num_axes < 1 && num_mt_axes < 1) {
+        xf86Msg(X_WARNING, "%s: no absolute or touch axes found.\n",
+                device->name);
+        return !Success;
+    }
+
     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;
+        int mode = pEvdev->flags & EVDEV_TOUCHPAD ?
+            XIDependentTouch : XIDirectTouch;
+
+        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);
+#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;
+    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