summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stone <daniel@fooishbar.org>2010-09-20 15:03:07 +1000
committerChase Douglas <chase.douglas@ubuntu.com>2010-10-15 16:19:45 +0200
commitc04b7ae0074ea7b4ae6955c01b370914a3d48492 (patch)
tree702c12109bfa45fa0137a80354da58230f850613
parent185ad42078a3bcb423b83e41ebeddbb7541fe26c (diff)
Add initial multitouch support using Xi 2.1
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@fooishbar.org>
-rw-r--r--src/evdev.c210
-rw-r--r--src/evdev.h9
2 files changed, 205 insertions, 14 deletions
diff --git a/src/evdev.c b/src/evdev.c
index 9e1fb10..7a72de8 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -25,6 +25,7 @@
* Adam Jackson (ajax@redhat.com)
* Peter Hutterer (peter.hutterer@redhat.com)
* Oliver McFadden (oliver.mcfadden@nokia.com)
+ * Daniel Stone (daniel@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);
@@ -546,11 +573,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))
@@ -677,6 +715,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.
*/
@@ -685,19 +757,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;
+ }
}
/**
@@ -739,6 +858,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 */
@@ -769,8 +889,6 @@ EvdevReadInput(InputInfoPtr pInfo)
}
}
-#define TestBit(bit, array) ((array[(bit) / LONG_BITS]) & (1L << ((bit) % LONG_BITS)))
-
static void
EvdevPtrCtrlProc(DeviceIntPtr device, PtrCtrl *ctrl)
{
@@ -1182,8 +1300,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++;
}
@@ -1441,6 +1563,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;
@@ -1523,6 +1678,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
@@ -1783,6 +1941,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;
@@ -1812,6 +1971,7 @@ EvdevProbe(InputInfoPtr pInfo)
has_keys = FALSE;
has_scroll = FALSE;
has_lmr = FALSE;
+ has_mt = FALSE;
num_buttons = 0;
/* count all buttons */
@@ -1915,6 +2075,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++) {
@@ -1981,6 +2145,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 b382670..2f874a2 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 proximity;
int num_buttons; /* number of buttons */