diff options
author | Daniel Stone <daniel@fooishbar.org> | 2010-09-20 15:03:07 +1000 |
---|---|---|
committer | Chase Douglas <chase.douglas@ubuntu.com> | 2010-10-15 16:19:45 +0200 |
commit | c04b7ae0074ea7b4ae6955c01b370914a3d48492 (patch) | |
tree | 702c12109bfa45fa0137a80354da58230f850613 | |
parent | 185ad42078a3bcb423b83e41ebeddbb7541fe26c (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.c | 210 | ||||
-rw-r--r-- | src/evdev.h | 9 |
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 */ |