[PATCH libinput 02/19] touchpad: add a touchpad driver based on per-finger tracking

Peter Hutterer peter.hutterer at who-t.net
Sun Feb 16 22:48:21 PST 2014


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.hutterer at 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



More information about the wayland-devel mailing list