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

Reply via email to