[PATCH evdev] Add initial multitouch support using Xi 2.1
Daniel Stone
daniel at fooishbar.org
Sun Sep 19 22:03:07 PDT 2010
This uses the new xserver touch event API to provide support for
multitouch in evdev, for devices that provide a hardware tracking ID.
All ABS_MT_* events are sent as independent touch events, rather than
valuators.
Signed-off-by: Daniel Stone <daniel at fooishbar.org>
---
src/evdev.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
src/evdev.h | 9 +++
2 files changed, 205 insertions(+), 14 deletions(-)
diff --git a/src/evdev.c b/src/evdev.c
index ae20de3..54bc9cb 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -25,6 +25,7 @@
* Adam Jackson (ajax at redhat.com)
* Peter Hutterer (peter.hutterer at redhat.com)
* Oliver McFadden (oliver.mcfadden at nokia.com)
+ * Daniel Stone (daniel at fooishbar.org)
*/
#ifdef HAVE_CONFIG_H
@@ -73,6 +74,8 @@
#endif
#define ArrayLength(a) (sizeof(a) / (sizeof((a)[0])))
+#define TestBit(bit, array) ((array[(bit) / LONG_BITS]) & (1L << ((bit) % LONG_BITS)))
+#define SetBitLong(bit, array) ((array[(bit) / LONG_BITS]) |= (1L << ((bit) % LONG_BITS)))
#define MIN_KEYCODE 8
#define GLYPHS_PER_KEY 2
@@ -95,6 +98,30 @@ static char *evdevDefaults[] = {
NULL
};
+static int abs_mt_axis_map[] = {
+ ABS_MT_POSITION_X,
+ ABS_MT_POSITION_Y,
+ ABS_MT_TOUCH_MAJOR,
+ ABS_MT_TOUCH_MINOR,
+ ABS_MT_WIDTH_MAJOR,
+ ABS_MT_WIDTH_MINOR,
+ ABS_MT_ORIENTATION,
+ ABS_MT_TRACKING_ID,
+ ABS_MT_TOOL_TYPE,
+};
+static inline int mt_axis_to_evdev(int axis)
+{
+ int i;
+
+ for (i = 0; i < ArrayLength(abs_mt_axis_map); i++)
+ if (abs_mt_axis_map[i] == axis)
+ return i;
+
+ return -1;
+}
+#define mt_cached_axis(_evdev, _axis) \
+ (_evdev->mt_tp_axes[mt_axis_to_evdev(_axis)])
+
static int EvdevOn(DeviceIntPtr);
static int EvdevCacheCompare(InputInfoPtr pInfo, BOOL compare);
static void EvdevKbdCtrl(DeviceIntPtr device, KeybdCtrl *ctrl);
@@ -534,11 +561,22 @@ EvdevProcessAbsoluteMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
/* Get the signed value, earlier kernels had this as unsigned */
value = ev->value;
- /* Ignore EV_ABS events if we never set up for them. */
- if (!(pEvdev->flags & EVDEV_ABSOLUTE_EVENTS))
+ /* Process multitouch events first, since they don't interfere
+ * with abs/rel. */
+ if (ev->code >= ABS_MT_TOUCH_MAJOR && ev->code <= ABS_MT_TRACKING_ID)
+ {
+ int axis = mt_axis_to_evdev(ev->code);
+
+ if (axis == -1 || !(pEvdev->flags & EVDEV_MULTITOUCH))
+ return;
+
+ pEvdev->mt_tp_axes[axis] = value;
+ SetBitLong(ev->code, pEvdev->mt_tp_axismask);
return;
+ }
- if (ev->code > ABS_MAX)
+ /* Ignore EV_ABS events if we never set up for them. */
+ if (!(pEvdev->flags & EVDEV_ABSOLUTE_EVENTS))
return;
if (EvdevWheelEmuFilterMotion(pInfo, ev))
@@ -660,6 +698,40 @@ static void EvdevPostQueuedEvents(InputInfoPtr pInfo, int *num_v, int *first_v,
}
/**
+ * If a touchpoint hasn't appeared between EV_SYN::SYN_REPORT pairs, it means
+ * that it's gone; ABS_MT_TRACKING_ID is always guaranteed to appear
+ * if a touchpoint is live, even if it hasn't moved.
+ */
+static void
+EvdevGCTouchPoints(InputInfoPtr pInfo)
+{
+ EvdevPtr pEvdev = pInfo->private;
+ int i, j;
+
+ if (!(pEvdev->flags & EVDEV_MULTITOUCH))
+ return;
+
+ for (i = 0; i < pEvdev->mt_max_tps; i++)
+ {
+ if (!pEvdev->mt_all_tps[i])
+ continue;
+
+ for (j = 0; j < pEvdev->mt_max_tps; j++)
+ if (pEvdev->mt_active_tps[j] == pEvdev->mt_all_tps[i])
+ break;
+ if (j == pEvdev->mt_max_tps)
+ {
+ xf86FiniTouchPoint(pInfo->dev, pEvdev->mt_all_tps[i]);
+ pEvdev->mt_all_tps[i] = 0;
+ }
+ }
+
+ memcpy(pEvdev->mt_all_tps, pEvdev->mt_active_tps,
+ pEvdev->mt_max_tps * sizeof(int));
+ memset(pEvdev->mt_active_tps, 0, pEvdev->mt_max_tps * sizeof(int));
+}
+
+/**
* Take the synchronization input event and process it accordingly; the motion
* notify events are sent first, then any button/key press/release events.
*/
@@ -668,19 +740,66 @@ EvdevProcessSyncEvent(InputInfoPtr pInfo, struct input_event *ev)
{
int num_v = 0, first_v = 0;
int v[MAX_VALUATORS] = {};
+ int i;
EvdevPtr pEvdev = pInfo->private;
- EvdevProcessValuators(pInfo, v, &num_v, &first_v);
+ /* This means we've finished receiving all the events for a given
+ * touchpoint; collate them, update the valuators, and bail. */
+ if (ev->code == SYN_MT_REPORT) {
+ int id = mt_cached_axis(pEvdev, ABS_MT_TRACKING_ID);
+ int mask = 0;
- EvdevPostRelativeMotionEvents(pInfo, &num_v, &first_v, v);
- EvdevPostAbsoluteMotionEvents(pInfo, &num_v, &first_v, v);
- EvdevPostQueuedEvents(pInfo, &num_v, &first_v, v);
+ if (id == -1 || !(pEvdev->flags & EVDEV_MULTITOUCH))
+ return;
- memset(pEvdev->delta, 0, sizeof(pEvdev->delta));
- memset(pEvdev->queue, 0, sizeof(pEvdev->queue));
- pEvdev->num_queue = 0;
- pEvdev->abs = 0;
- pEvdev->rel = 0;
+ for (i = 0; i < pEvdev->mt_max_tps; i++) {
+ if (pEvdev->mt_active_tps[i] == 0) {
+ pEvdev->mt_active_tps[i] = id;
+ break;
+ }
+ }
+
+ if (TestBit(ABS_MT_POSITION_X, pEvdev->mt_tp_axismask))
+ mask |= XITouchXMask;
+ if (TestBit(ABS_MT_POSITION_Y, pEvdev->mt_tp_axismask))
+ mask |= XITouchYMask;
+ if (TestBit(ABS_MT_TOUCH_MAJOR, pEvdev->mt_tp_axismask) ||
+ TestBit(ABS_MT_TOUCH_MINOR, pEvdev->mt_tp_axismask))
+ mask |= XITouchTouchSizeMask;
+ if (TestBit(ABS_MT_WIDTH_MAJOR, pEvdev->mt_tp_axismask) ||
+ TestBit(ABS_MT_WIDTH_MINOR, pEvdev->mt_tp_axismask))
+ mask |= XITouchToolSizeMask;
+ if (TestBit(ABS_MT_ORIENTATION, pEvdev->mt_tp_axismask))
+ mask |= XITouchOrientationMask;
+
+ xf86PostTouchMotion(pInfo->dev, id,
+ mt_cached_axis(pEvdev, ABS_MT_TOOL_TYPE),
+ mask,
+ mt_cached_axis(pEvdev, ABS_MT_POSITION_X),
+ mt_cached_axis(pEvdev, ABS_MT_POSITION_Y),
+ mt_cached_axis(pEvdev, ABS_MT_TOUCH_MAJOR),
+ mt_cached_axis(pEvdev, ABS_MT_TOUCH_MINOR),
+ mt_cached_axis(pEvdev, ABS_MT_WIDTH_MAJOR),
+ mt_cached_axis(pEvdev, ABS_MT_WIDTH_MINOR),
+ mt_cached_axis(pEvdev, ABS_MT_ORIENTATION));
+
+ memset(pEvdev->mt_tp_axes, 0, pEvdev->mt_max_tps * sizeof(int));
+ memset(pEvdev->mt_tp_axismask, 0, sizeof(pEvdev->mt_tp_axismask));
+ } else if (ev->code == SYN_REPORT) {
+ EvdevGCTouchPoints(pInfo);
+
+ EvdevProcessValuators(pInfo, v, &num_v, &first_v);
+
+ EvdevPostRelativeMotionEvents(pInfo, &num_v, &first_v, v);
+ EvdevPostAbsoluteMotionEvents(pInfo, &num_v, &first_v, v);
+ EvdevPostQueuedEvents(pInfo, &num_v, &first_v, v);
+
+ memset(pEvdev->delta, 0, sizeof(pEvdev->delta));
+ memset(pEvdev->queue, 0, sizeof(pEvdev->queue));
+ pEvdev->num_queue = 0;
+ pEvdev->abs = 0;
+ pEvdev->rel = 0;
+ }
}
/**
@@ -722,6 +841,7 @@ EvdevReadInput(InputInfoPtr pInfo)
while (len == sizeof(ev))
{
len = read(pInfo->fd, &ev, sizeof(ev));
+
if (len <= 0)
{
if (errno == ENODEV) /* May happen after resume */
@@ -752,8 +872,6 @@ EvdevReadInput(InputInfoPtr pInfo)
}
}
-#define TestBit(bit, array) ((array[(bit) / LONG_BITS]) & (1L << ((bit) % LONG_BITS)))
-
static void
EvdevPtrCtrlProc(DeviceIntPtr device, PtrCtrl *ctrl)
{
@@ -1165,8 +1283,12 @@ EvdevAddAbsClass(DeviceIntPtr device)
for (axis = ABS_X; i < MAX_VALUATORS && axis <= ABS_MAX; axis++) {
pEvdev->axis_map[axis] = -1;
+ /* Ignore axes the device doesn't expose, or are stolen for MT. */
if (!TestBit(axis, pEvdev->abs_bitmask))
continue;
+ if ((pEvdev->flags & EVDEV_MULTITOUCH) &&
+ axis >= ABS_MT_TOUCH_MAJOR && axis <= ABS_MT_TRACKING_ID)
+ continue;
pEvdev->axis_map[axis] = i;
i++;
}
@@ -1424,6 +1546,39 @@ EvdevInitAbsClass(DeviceIntPtr device, EvdevPtr pEvdev)
}
static void
+EvdevInitMTClass(DeviceIntPtr device, EvdevPtr pEvdev)
+{
+ Bool ret;
+ double min_x, max_x, min_y, max_y, min_touch_width, max_touch_width;
+
+ min_x = pEvdev->absinfo[ABS_MT_POSITION_X].minimum;
+ max_x = pEvdev->absinfo[ABS_MT_POSITION_X].maximum;
+ min_y = pEvdev->absinfo[ABS_MT_POSITION_Y].minimum;
+ max_y = pEvdev->absinfo[ABS_MT_POSITION_Y].maximum;
+
+ if (TestBit(ABS_MT_TOUCH_MAJOR, pEvdev->abs_bitmask)) {
+ min_touch_width = pEvdev->absinfo[ABS_MT_TOUCH_MAJOR].minimum;
+ max_touch_width = pEvdev->absinfo[ABS_MT_TOUCH_MAJOR].maximum;
+ } else {
+ min_touch_width = -1;
+ max_touch_width = -1;
+ }
+
+ ret = InitTouchClassDeviceStruct(device, pEvdev->mt_max_tps, Absolute,
+ Relative, min_x, max_x, min_y, max_y,
+ min_touch_width, max_touch_width);
+ if (ret == TRUE) {
+ xf86Msg(X_INFO, "%s: initialised multitouch support\n", device->name);
+ } else {
+ xf86Msg(X_ERROR, "%s: failed to initialise multitouch support\n",
+ device->name);
+ }
+
+ memset(pEvdev->mt_tp_axes, 0, pEvdev->mt_max_tps * sizeof(int));
+ memset(pEvdev->mt_tp_axismask, 0, sizeof(pEvdev->mt_tp_axismask));
+}
+
+static void
EvdevInitRelClass(DeviceIntPtr device, EvdevPtr pEvdev)
{
int has_abs_axes = pEvdev->flags & EVDEV_ABSOLUTE_EVENTS;
@@ -1506,6 +1661,9 @@ EvdevInit(DeviceIntPtr device)
else if (pEvdev->flags & EVDEV_ABSOLUTE_EVENTS)
EvdevInitAbsClass(device, pEvdev);
+ if (pEvdev->flags & EVDEV_MULTITOUCH)
+ EvdevInitMTClass(device, pEvdev);
+
#ifdef HAVE_PROPERTIES
/* We drop the return value, the only time we ever want the handlers to
* unregister is when the device dies. In which case we don't have to
@@ -1766,6 +1924,7 @@ EvdevProbe(InputInfoPtr pInfo)
{
int i, has_rel_axes, has_abs_axes, has_keys, num_buttons, has_scroll;
int has_lmr; /* left middle right */
+ int has_mt;
int ignore_abs = 0, ignore_rel = 0;
EvdevPtr pEvdev = pInfo->private;
@@ -1795,6 +1954,7 @@ EvdevProbe(InputInfoPtr pInfo)
has_keys = FALSE;
has_scroll = FALSE;
has_lmr = FALSE;
+ has_mt = FALSE;
num_buttons = 0;
/* count all buttons */
@@ -1898,6 +2058,10 @@ EvdevProbe(InputInfoPtr pInfo)
}
}
}
+
+ if (TestBit(ABS_MT_POSITION_X, pEvdev->abs_bitmask) &&
+ TestBit(ABS_MT_POSITION_Y, pEvdev->abs_bitmask))
+ has_mt = TRUE;
}
for (i = 0; i < BTN_MISC; i++) {
@@ -1964,6 +2128,24 @@ EvdevProbe(InputInfoPtr pInfo)
pEvdev->flags |= EVDEV_RELATIVE_EVENTS;
}
+ if (has_mt && (has_abs_axes || has_rel_axes) &&
+ (pInfo->flags & XI86_CONFIGURED))
+ {
+ pEvdev->flags |= EVDEV_MULTITOUCH;
+ pEvdev->mt_max_tps = xf86SetIntOption(pInfo->options,
+ "MaxMTTouchPoints", 10);
+ xf86Msg(X_INFO, "%s: Adding multitouch support (%d touchpoints)\n",
+ pInfo->name, pEvdev->mt_max_tps);
+ pEvdev->mt_active_tps = calloc(pEvdev->mt_max_tps, sizeof(int));
+ pEvdev->mt_all_tps = calloc(pEvdev->mt_max_tps, sizeof(int));
+ if (!pEvdev->mt_active_tps || !pEvdev->mt_all_tps)
+ {
+ xf86Msg(X_ERROR, "%s: Couldn't allocate MT axis values\n",
+ pInfo->name);
+ return BadAlloc;
+ }
+ }
+
if ((pInfo->flags & XI86_CONFIGURED) == 0) {
xf86Msg(X_WARNING, "%s: Don't know how to use device\n",
pInfo->name);
diff --git a/src/evdev.h b/src/evdev.h
index 4945140..d6c747c 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -55,6 +55,8 @@
#define LED_CNT (LED_MAX+1)
#endif
+#define MT_NUM_AXES (ABS_MT_TRACKING_ID - ABS_MT_TOUCH_MAJOR)
+
#define EVDEV_MAXBUTTONS 32
#define EVDEV_MAXQUEUE 32
@@ -71,6 +73,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) /* Multitouch type B device */
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
#define HAVE_PROPERTIES 1
@@ -123,6 +126,12 @@ typedef struct {
int vals[MAX_VALUATORS];
int old_vals[MAX_VALUATORS]; /* Translate absolute inputs to relative */
+ int *mt_active_tps; /* Active touchpoints in this event stream only */
+ int *mt_all_tps; /* All currently active touchpoints */
+ int mt_max_tps; /* Maximum number of active touchpoints */
+ int mt_tp_axes[MT_NUM_AXES]; /* Valuators for the current touchpoint */
+ unsigned long mt_tp_axismask[NLONGS(ABS_CNT)];
+
int flags;
int tool;
int num_buttons; /* number of buttons */
--
1.7.1
More information about the xorg-devel
mailing list