[PATCH 2/4] Add the new valuators for multitouch

Chase Douglas chase.douglas at canonical.com
Wed Jun 23 07:04:38 PDT 2010


From: Benjamin Tissoires <tissoire at cena.fr>

The step one in implementing multitouch in evdev is to report all the
touches in different valuators.
This patch detects multitouch devices and creates the extra valuators
required for multitouch.

Bonus point: this patch also sort the multitouch valuators to have
ABS_MT_POSITION_X and ABS_MT_POSITION_Y at their first position.

Note that I currently assume that all definitions between
ABS_MT_TOUCH_MAJOR and below ABS_MT_PRESSURE are MT-related.
This can be changed by re-defining EVDEV_FIRST_MT_AXIS and
EVDEV_LAST_MT_AXIS.

Signed-off-by: Benjamin Tissoires <tissoire at cena.fr>
---
 src/evdev.c |  201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/evdev.h |    6 ++-
 2 files changed, 205 insertions(+), 2 deletions(-)

diff --git a/src/evdev.c b/src/evdev.c
index 0cf1d3e..68d8946 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -66,6 +66,10 @@
 #define MAXDEVICES MAX_DEVICES
 #endif
 
+#ifndef ABS_MT_SLOT
+#define ABS_MT_SLOT 0x2f
+#endif
+
 #define ArrayLength(a) (sizeof(a) / (sizeof((a)[0])))
 
 #define MIN_KEYCODE 8
@@ -82,6 +86,11 @@
 #define MODEFLAG	8
 #define COMPOSEFLAG	16
 
+#define EVDEV_FIRST_MT_AXIS ABS_MT_TOUCH_MAJOR
+#define EVDEV_LAST_MT_AXIS 0x3a /* ABS_MT_PRESSURE in 2.6.33 */
+
+#define EVDEV_XI_MULTITOUCHDEVICE "MULTITOUCHDEVICE"
+
 static const char *evdevDefaults[] = {
     "XkbRules",     "evdev",
     "XkbModel",     "evdev",
@@ -339,6 +348,8 @@ EvdevQueueButtonClicks(InputInfoPtr pInfo, int button, int count)
 #define ABS_X_VALUE 0x1
 #define ABS_Y_VALUE 0x2
 #define ABS_VALUE   0x4
+#define ABS_MT_X_VALUE   0x8
+#define ABS_MT_Y_VALUE   0x10
 /**
  * Take the valuators and process them accordingly.
  */
@@ -543,6 +554,10 @@ EvdevProcessAbsoluteMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
         pEvdev->abs |= ABS_X_VALUE;
     else if (ev->code == ABS_Y)
         pEvdev->abs |= ABS_Y_VALUE;
+    else if (ev->code == ABS_MT_POSITION_X)
+        pEvdev->abs |= ABS_MT_X_VALUE;
+    else if (ev->code == ABS_MT_POSITION_Y)
+        pEvdev->abs |= ABS_MT_Y_VALUE;
     else
         pEvdev->abs |= ABS_VALUE;
 }
@@ -704,6 +719,8 @@ EvdevProcessEvent(InputInfoPtr pInfo, struct input_event *ev)
 #undef ABS_X_VALUE
 #undef ABS_Y_VALUE
 #undef ABS_VALUE
+#undef ABS_MT_X_VALUE
+#undef ABS_MT_Y_VALUE
 
 /* just a magic number to reduce the number of reads */
 #define NUM_EVENTS 16
@@ -1132,6 +1149,168 @@ EvdevAddKeyClass(DeviceIntPtr device)
     return Success;
 }
 
+/**
+ * Count the number of mt-related valuators.
+ * assume that mt values are in the range
+ * EVDEV_FIRST_MT_AXIS..EVDEV_LAST_MT_AXIS
+ */
+static int
+EvdevMTCountValuators(EvdevPtr pEvdev)
+{
+    int axis;
+
+    pEvdev->mt_num_valuators = 0;
+
+    if (!(pEvdev->flags & EVDEV_MULTITOUCH))
+        return 0;
+
+    for (axis = EVDEV_FIRST_MT_AXIS; axis <= EVDEV_LAST_MT_AXIS; axis++) {
+        if (TestBit(axis, pEvdev->abs_bitmask))
+            pEvdev->mt_num_valuators++;
+    }
+
+    return pEvdev->mt_num_valuators;
+}
+
+/**
+ * This function counts the number of valuators the device has.
+ * In case of multitouch device, it adds to this number a set of
+ * valuators to handle more than one touch.
+ *
+ * For instance, two touches can be received as:
+ *
+ * Valuator 0: sth_absolute
+ * Valuator 1: other_absolute
+ * Valuator 2: another_absolute
+ * Valuator 3: mt_x0
+ * Valuator 4: mt_y0
+ * Valuator 5: mt_pressure0
+ * Valuator 6: mt_x1
+ * Valuator 7: mt_y1
+ * Valuator 8: mt_pressure1
+ *
+ * Here, in this example total_num_axes to 6, and  mt_num_valuators
+ * is equal to 3.
+ */
+static int
+EvdevMTAddExtraValuators(EvdevPtr pEvdev, int total_num_axes)
+{
+    int num_axes = 0;
+    int mt_num_valuators = 0;
+
+    if (!(pEvdev->flags & EVDEV_MULTITOUCH))
+        return total_num_axes;
+
+    mt_num_valuators = EvdevMTCountValuators(pEvdev);
+
+    /* substract the mt-valuators to total_num_axes
+     * to have only the non-mt valuators */
+    num_axes = total_num_axes - mt_num_valuators;
+
+    /* count the maximum number of touchpoints the device can support */
+    pEvdev->mt_max_touchpoints = (MAX_VALUATORS - num_axes)
+                                                / mt_num_valuators;
+
+    /* check if the device tells the number of touchpoints
+     * it can support. */
+    if (TestBit(ABS_MT_SLOT, pEvdev->abs_bitmask)) {
+        int max_id = pEvdev->absinfo[ABS_MT_SLOT].maximum;
+        if (max_id < pEvdev->mt_max_touchpoints)
+            pEvdev->mt_max_touchpoints = max_id;
+    }
+
+    num_axes += pEvdev->mt_max_touchpoints * mt_num_valuators;
+
+    return num_axes;
+}
+
+/**
+ * As the first declared mt valuator is ABS_MT_TOUCH_MAJOR, it is then
+ * the first mt-related valuator.
+ * This function puts ABS_MT_POSITION_X and ABS_MT_POSITION_Y at places
+ * 0 and 1 in the set of multitouch valuators.
+ */
+static void
+EvdevMTSortValuators(EvdevPtr pEvdev)
+{
+    int axis, tmp_axis_value;
+    int first_axis = 0;
+    int second_axis = 0;
+
+    if (!(pEvdev->flags & EVDEV_MULTITOUCH))
+        return;
+
+    /* find the first and second mt axes */
+    for (axis = EVDEV_FIRST_MT_AXIS; axis <= EVDEV_LAST_MT_AXIS; axis++) {
+        if (pEvdev->axis_map[axis] == -1)
+            continue;
+
+        if (!first_axis)
+            first_axis = axis;
+        else if (!second_axis) {
+            second_axis = axis;
+            break;
+        }
+    }
+
+    /* do the actual swap */
+    tmp_axis_value = pEvdev->axis_map[first_axis];
+    pEvdev->axis_map[first_axis] = pEvdev->axis_map[ABS_MT_POSITION_X];
+    pEvdev->axis_map[ABS_MT_POSITION_X] = tmp_axis_value;
+
+    tmp_axis_value = pEvdev->axis_map[second_axis];
+    pEvdev->axis_map[second_axis] = pEvdev->axis_map[ABS_MT_POSITION_Y];
+    pEvdev->axis_map[ABS_MT_POSITION_Y] = tmp_axis_value;
+}
+
+/**
+ * As multitouch devices contains extra axes to enable multitouch,
+ * they are not initialized in the first init pass. This function
+ * does the init for those extra valuators.
+ */
+static void
+EvdevMTInitValuators(DeviceIntPtr device, Atom *atoms)
+{
+    InputInfoPtr pInfo;
+    EvdevPtr pEvdev;
+    int axis, j;
+
+    pInfo = device->public.devicePrivate;
+    pEvdev = pInfo->private;
+
+    if (!(pEvdev->flags & EVDEV_MULTITOUCH))
+        return;
+
+    /* Here j starts at 1 as one set of mt-valuators has already been
+     * registered */
+    for (j = 1; j < pEvdev->mt_max_touchpoints; j++) {
+        for (axis = EVDEV_FIRST_MT_AXIS; axis <= EVDEV_LAST_MT_AXIS; axis++) {
+            int axnum = pEvdev->axis_map[axis];
+            int real_axnum = axnum + j * pEvdev->mt_num_valuators;
+            int resolution = 10000;
+
+            if (axnum == -1)
+                continue;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 30)
+            /* Kernel provides units/mm, X wants units/m */
+            if (pEvdev->absinfo[axis].resolution)
+                resolution = pEvdev->absinfo[axis].resolution * 1000;
+#endif
+
+            xf86InitValuatorAxisStruct(device, real_axnum,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
+                                       atoms[axnum],
+#endif
+                                       pEvdev->absinfo[axis].minimum,
+                                       pEvdev->absinfo[axis].maximum,
+                                       resolution, 0, resolution);
+            xf86InitValuatorDefaults(device, real_axnum);
+            pEvdev->old_vals[real_axnum] = -1;
+        }
+    }
+}
+
 static int
 EvdevAddAbsClass(DeviceIntPtr device)
 {
@@ -1149,6 +1328,9 @@ EvdevAddAbsClass(DeviceIntPtr device)
     num_axes = CountBits(pEvdev->abs_bitmask, NLONGS(ABS_MAX));
     if (num_axes < 1)
         return !Success;
+
+    num_axes = EvdevMTAddExtraValuators(pEvdev, num_axes);
+
     pEvdev->num_vals = num_axes;
     memset(pEvdev->vals, 0, num_axes * sizeof(int));
     memset(pEvdev->old_vals, -1, num_axes * sizeof(int));
@@ -1162,6 +1344,8 @@ EvdevAddAbsClass(DeviceIntPtr device)
         i++;
     }
 
+    EvdevMTSortValuators(pEvdev);
+
     EvdevInitAxesLabels(pEvdev, pEvdev->num_vals, atoms);
 
     if (!InitValuatorClassDeviceStruct(device, num_axes,
@@ -1198,6 +1382,8 @@ EvdevAddAbsClass(DeviceIntPtr device)
         pEvdev->old_vals[axnum] = -1;
     }
 
+    EvdevMTInitValuators(device, atoms);
+
     free(atoms);
 
     if (!InitPtrFeedbackClassDeviceStruct(device, EvdevPtrCtrlProc))
@@ -1503,7 +1689,7 @@ EvdevInit(DeviceIntPtr device)
 
     if (pEvdev->flags & (EVDEV_UNIGNORE_RELATIVE | EVDEV_UNIGNORE_ABSOLUTE))
         EvdevInitAnyClass(device, pEvdev);
-    else if (pEvdev->flags & (EVDEV_TOUCHPAD | EVDEV_TOUCHSCREEN | EVDEV_TABLET))
+    else if (pEvdev->flags & (EVDEV_TOUCHPAD | EVDEV_TOUCHSCREEN | EVDEV_TABLET | EVDEV_MULTITOUCH))
         EvdevInitTouchDevice(device, pEvdev);
     else if (pEvdev->flags & EVDEV_RELATIVE_EVENTS)
         EvdevInitRelClass(device, pEvdev);
@@ -1875,6 +2061,16 @@ EvdevProbe(InputInfoPtr pInfo)
         xf86Msg(X_PROBED, "%s: Found absolute axes\n", pInfo->name);
         pEvdev->flags |= EVDEV_ABSOLUTE_EVENTS;
 
+        if (TestBit(ABS_MT_POSITION_X, pEvdev->abs_bitmask) &&
+            TestBit(ABS_MT_POSITION_Y, pEvdev->abs_bitmask) &&
+            TestBit(ABS_MT_SLOT, pEvdev->abs_bitmask)) {
+            xf86Msg(X_INFO, "%s: Found absolute multitouch device.\n", pInfo->name);
+            pEvdev->flags |= EVDEV_MULTITOUCH;
+            if (!pEvdev->num_buttons) {
+                pEvdev->num_buttons = 7; /* LMR + scroll wheels */
+                pEvdev->flags |= EVDEV_BUTTON_EVENTS;
+            }
+        }
         if ((TestBit(ABS_X, pEvdev->abs_bitmask) &&
              TestBit(ABS_Y, pEvdev->abs_bitmask))) {
             xf86Msg(X_PROBED, "%s: Found x and y absolute axes\n", pInfo->name);
@@ -1947,6 +2143,9 @@ EvdevProbe(InputInfoPtr pInfo)
         } else if (pEvdev->flags & EVDEV_TOUCHSCREEN) {
             xf86Msg(X_INFO, "%s: Configuring as touchscreen\n", pInfo->name);
             pInfo->type_name = XI_TOUCHSCREEN;
+	} else if (pEvdev->flags & EVDEV_MULTITOUCH) {
+	    xf86Msg(X_INFO, "%s: Configuring as multitouch device\n", pInfo->name);
+	    pInfo->type_name = EVDEV_XI_MULTITOUCHDEVICE;
 	} else {
 	    xf86Msg(X_INFO, "%s: Configuring as mouse\n", pInfo->name);
 	    pInfo->type_name = XI_MOUSE;
diff --git a/src/evdev.h b/src/evdev.h
index 4945140..7f9fc47 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -71,6 +71,7 @@
 #define EVDEV_UNIGNORE_ABSOLUTE (1 << 9) /* explicitly unignore abs axes */
 #define EVDEV_UNIGNORE_RELATIVE (1 << 10) /* explicitly unignore rel axes */
 #define EVDEV_RELATIVE_MODE	(1 << 11) /* Force relative events for devices with absolute axes */
+#define EVDEV_MULTITOUCH	(1 << 12) /* device looks like a multi-touch screen? */
 
 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
 #define HAVE_PROPERTIES 1
@@ -80,7 +81,6 @@
 #define MAX_VALUATORS 36
 #endif
 
-
 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5
 typedef struct {
     char *rules;
@@ -193,6 +193,10 @@ typedef struct {
     /* Event queue used to defer keyboard/button events until EV_SYN time. */
     int                     num_queue;
     EventQueueRec           queue[EVDEV_MAXQUEUE];
+
+    unsigned int mt_num_valuators;
+    unsigned int mt_max_touchpoints; /* the number of simultaneous touchpoints
+                                      * the device can support */
 } EvdevRec, *EvdevPtr;
 
 /* Event posting functions */
-- 
1.7.0.4



More information about the xorg-devel mailing list