This patch is a mixture of an experimental project (libtouchpad) and evdev-touchpad.c. It adds a new touchpad driver for multi-touch touchpads that tracks each touchpoint separately. This makes it a lot easier to handle multi-finger tapping, software button areas, etc.
libtouchpad used a slightly different coding style, this is the attempt to get closer to the one used in libinput. Currently sends motion events for single-finger motion, button events only for physical buttons. Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> --- src/evdev-mt-touchpad.c | 300 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 281 insertions(+), 19 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index a360651..856d54f 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -22,38 +22,300 @@ #include "config.h" +#include <assert.h> +#include <stdbool.h> + #include "evdev.h" -struct touchpad_dispatch { +#define TOUCHPAD_HISTORY_LENGTH 4 + +#define tp_for_each_touch(_tp, _t) \ + for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++) + +enum touch_state { + TOUCH_NONE = 0, + TOUCH_BEGIN, + TOUCH_UPDATE, + TOUCH_END +}; + +struct tp_motion { + int32_t x; + int32_t y; +}; + +struct tp_touch { + enum touch_state state; + bool dirty; + int32_t x; + int32_t y; + uint32_t millis; + + struct { + struct tp_motion samples[TOUCHPAD_HISTORY_LENGTH]; + unsigned int index; + unsigned int count; + } history; +}; + +struct tp_dispatch { struct evdev_dispatch base; struct evdev_device *device; + unsigned int nfingers_down; /* number of fingers down */ + unsigned int slot; /* current slot */ + + unsigned int ntouches; /* number of slots */ + struct tp_touch *touches; /* len == ntouches */ }; +static inline struct tp_motion * +tp_motion_history_offset(struct tp_touch *t, int offset) +{ + int offset_index = + (t->history.index - offset + TOUCHPAD_HISTORY_LENGTH) % + TOUCHPAD_HISTORY_LENGTH; + + return &t->history.samples[offset_index]; +} + +static inline void +tp_motion_history_push(struct tp_touch *t) +{ + int motion_index = (t->history.index + 1) % TOUCHPAD_HISTORY_LENGTH; + + if (t->history.count < TOUCHPAD_HISTORY_LENGTH) + t->history.count++; + + t->history.samples[motion_index].x = t->x; + t->history.samples[motion_index].y = t->y; + t->history.index = motion_index; +} + +static inline void +tp_motion_history_reset(struct tp_touch *t) +{ + t->history.count = 0; +} + +static inline struct tp_touch * +tp_current_touch(struct tp_dispatch *tp) +{ + return &tp->touches[min(tp->slot, tp->ntouches)]; +} + +static inline void +tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t) +{ + if (t->state != TOUCH_UPDATE) { + tp_motion_history_reset(t); + t->dirty = true; + t->state = TOUCH_BEGIN; + tp->nfingers_down++; + assert(tp->nfingers_down >= 1); + } +} + +static inline void +tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t) +{ + if (t->state == TOUCH_NONE) + return; + + t->dirty = true; + t->state = TOUCH_END; + assert(tp->nfingers_down >= 1); + tp->nfingers_down--; +} + +static double +tp_estimate_delta(int x0, int x1, int x2, int x3) +{ + return (x0 + x1 - x2 - x3) / 4; +} + static void -touchpad_process(struct evdev_dispatch *dispatch, - struct evdev_device *device, - struct input_event *e, - uint32_t time) +tp_get_delta(struct tp_touch *t, double *dx, double *dy) { + if (t->history.count < 4) { + *dx = 0; + *dy = 0; + return; + } + + *dx = tp_estimate_delta(tp_motion_history_offset(t, 0)->x, + tp_motion_history_offset(t, 1)->x, + tp_motion_history_offset(t, 2)->x, + tp_motion_history_offset(t, 3)->x); + *dy = tp_estimate_delta(tp_motion_history_offset(t, 0)->y, + tp_motion_history_offset(t, 1)->y, + tp_motion_history_offset(t, 2)->y, + tp_motion_history_offset(t, 3)->y); +} + +static void +tp_process_absolute(struct tp_dispatch *tp, + const struct input_event *e, + uint32_t time) +{ + struct tp_touch *t = tp_current_touch(tp); + + switch(e->code) { + case ABS_MT_POSITION_X: + t->x = e->value; + t->millis = time; + t->dirty = true; + break; + case ABS_MT_POSITION_Y: + t->y = e->value; + t->millis = time; + t->dirty = true; + break; + case ABS_MT_SLOT: + tp->slot = e->value; + break; + case ABS_MT_TRACKING_ID: + t->millis = time; + if (e->value != -1) + tp_begin_touch(tp, t); + else + tp_end_touch(tp, t); + } } static void -touchpad_destroy(struct evdev_dispatch *dispatch) +tp_process_key(struct tp_dispatch *tp, + const struct input_event *e, + uint32_t time) { - free(dispatch); + switch (e->code) { + case BTN_LEFT: + case BTN_MIDDLE: + case BTN_RIGHT: + pointer_notify_button( + &tp->device->base, + time, + e->code, + e->value ? LIBINPUT_POINTER_BUTTON_STATE_PRESSED : + LIBINPUT_POINTER_BUTTON_STATE_RELEASED); + break; + } +} + +static void +tp_process_state(struct tp_dispatch *tp, uint32_t time) +{ + struct tp_touch *t; + + tp_for_each_touch(tp, t) { + if (!t->dirty) + continue; + + tp_motion_history_push(t); + } +} + +static void +tp_post_process_state(struct tp_dispatch *tp, uint32_t time) +{ + struct tp_touch *t; + + tp_for_each_touch(tp, t) { + if (!t->dirty) + continue; + + if (t->state == TOUCH_END) + t->state = TOUCH_NONE; + else if (t->state == TOUCH_BEGIN) + t->state = TOUCH_UPDATE; + + t->dirty = false; + } +} + +static void +tp_post_events(struct tp_dispatch *tp, uint32_t time) +{ + struct tp_touch *t = tp_current_touch(tp); + double dx, dy; + + if (tp->nfingers_down != 1) + return; + + tp_get_delta(t, &dx, &dy); + + if (dx != 0 || dy != 0) + pointer_notify_motion( + &tp->device->base, + time, + li_fixed_from_double(dx), + li_fixed_from_double(dy)); +} + +static void +tp_process(struct evdev_dispatch *dispatch, + struct evdev_device *device, + struct input_event *e, + uint32_t time) +{ + struct tp_dispatch *tp = + (struct tp_dispatch *)dispatch; + + switch (e->type) { + case EV_ABS: + tp_process_absolute(tp, e, time); + break; + case EV_KEY: + tp_process_key(tp, e, time); + break; + case EV_SYN: + tp_process_state(tp, time); + tp_post_events(tp, time); + tp_post_process_state(tp, time); + break; + } +} + +static void +tp_destroy(struct evdev_dispatch *dispatch) +{ + struct tp_dispatch *tp = + (struct tp_dispatch*)dispatch; + + free(tp->touches); + free(tp); } -static struct evdev_dispatch_interface touchpad_interface = { - touchpad_process, - touchpad_destroy +static struct evdev_dispatch_interface tp_interface = { + tp_process, + tp_destroy }; static int -touchpad_init(struct touchpad_dispatch *touchpad, +tp_init_slots(struct tp_dispatch *tp, struct evdev_device *device) { - touchpad->base.interface = &touchpad_interface; - touchpad->device = device; + struct input_absinfo absinfo = {0}; + + ioctl(device->fd, EVIOCGABS(ABS_MT_SLOT), &absinfo); + + tp->ntouches = absinfo.maximum + 1; + tp->touches = calloc(tp->ntouches, + sizeof(struct tp_touch)); + tp->slot = absinfo.value; + + return 0; +} + + +static int +tp_init(struct tp_dispatch *tp, + struct evdev_device *device) +{ + tp->base.interface = &tp_interface; + tp->device = device; + + if (tp_init_slots(tp, device) != 0) + return -1; return 0; } @@ -61,16 +323,16 @@ touchpad_init(struct touchpad_dispatch *touchpad, struct evdev_dispatch * evdev_mt_touchpad_create(struct evdev_device *device) { - struct touchpad_dispatch *touchpad; + struct tp_dispatch *tp; - touchpad = zalloc(sizeof *touchpad); - if (!touchpad) + tp = zalloc(sizeof *tp); + if (!tp) return NULL; - if (touchpad_init(touchpad, device) != 0) { - free(touchpad); + if (tp_init(tp, device) != 0) { + tp_destroy(&tp->base); return NULL; } - return &touchpad->base; + return &tp->base; } -- 1.8.4.2 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel