[PATCH libinput 3/3] touchpad: add support for per-finger hovering information

Benjamin Tissoires benjamin.tissoires at gmail.com
Thu Apr 30 13:30:25 PDT 2015


From: Benjamin Tissoires <benjamin.tissoires at redhat.com>

When the device supports true hovering, it reports this
information through ABS_MT_DISTANCE.
When this axis is available, we should rely on it to
(un)hover the touches as BTN_TOUCH is most of the time
unreliable (generated by the mouse emulation in the kernel).

Signed-off-by: Benjamin Tissoires <benjamin.tissoires at gmail.com>
---
 src/evdev-mt-touchpad.c   |   40 +++++++++++-
 src/evdev-mt-touchpad.h   |    2 +
 test/Makefile.am          |    1 +
 test/litest-atmel-hover.c |  149 ++++++++++++++++++++++++++++++++++++++++
 test/litest.c             |  153 +++++++++++++++++++++++++++++++++++++-----
 test/litest.h             |   26 +++++++-
 test/touchpad.c           |  166 +++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 519 insertions(+), 18 deletions(-)
 create mode 100644 test/litest-atmel-hover.c

diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index d5ce880..5b7f291 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -300,6 +300,9 @@ tp_process_absolute(struct tp_dispatch *tp,
 	case ABS_MT_SLOT:
 		tp->slot = e->value;
 		break;
+	case ABS_MT_DISTANCE:
+		t->distance = e->value;
+		break;
 	case ABS_MT_TRACKING_ID:
 		if (e->value != -1)
 			tp_new_touch(tp, t, time);
@@ -520,7 +523,29 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 }
 
 static void
-tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
+tp_unhover_abs_distance(struct tp_dispatch *tp, uint64_t time)
+{
+	struct tp_touch *t;
+	int i;
+
+	for (i = 0; i < (int)tp->ntouches; i++) {
+		t = tp_get_touch(tp, i);
+
+		if (t->state == TOUCH_HOVERING) {
+			if (t->distance == 0) {
+				/* avoid jumps when landing a finger */
+				tp_motion_history_reset(t);
+				tp_begin_touch(tp, t, time);
+			}
+		} else {
+			if (t->distance > 0)
+				tp_end_touch(tp, t, time);
+		}
+	}
+}
+
+static void
+tp_unhover_fake_touches(struct tp_dispatch *tp, uint64_t time)
 {
 	struct tp_touch *t;
 	unsigned int nfake_touches;
@@ -579,6 +604,16 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
 }
 
 static void
+tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
+{
+	if (tp->reports_distance)
+		tp_unhover_abs_distance(tp, time);
+	else
+		tp_unhover_fake_touches(tp, time);
+
+}
+
+static void
 tp_process_state(struct tp_dispatch *tp, uint64_t time)
 {
 	struct tp_touch *t;
@@ -1184,6 +1219,9 @@ tp_init(struct tp_dispatch *tp,
 		     device->abs.absinfo_y->minimum);
 	diagonal = sqrt(width*width + height*height);
 
+	tp->reports_distance = libevdev_has_event_code(device->evdev, EV_ABS,
+						       ABS_MT_DISTANCE);
+
 	tp->hysteresis_margin.x =
 		diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
 	tp->hysteresis_margin.y =
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 97b17cd..ebba9df 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -129,6 +129,7 @@ struct tp_touch {
 	bool dirty;
 	struct device_coords point;
 	uint64_t millis;
+	int distance;				/* distance == 0 means touch */
 
 	struct {
 		struct device_coords samples[TOUCHPAD_HISTORY_LENGTH];
@@ -183,6 +184,7 @@ struct tp_dispatch {
 	unsigned int slot;			/* current slot */
 	bool has_mt;
 	bool semi_mt;
+	bool reports_distance;			/* does the device supports true hovering */
 	enum touchpad_model model;
 
 	unsigned int num_slots;			/* number of slots */
diff --git a/test/Makefile.am b/test/Makefile.am
index be0c5d9..a90fa78 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -14,6 +14,7 @@ liblitest_la_SOURCES = \
 	litest.h \
 	litest-int.h \
 	litest-alps-semi-mt.c \
+	litest-atmel-hover.c \
 	litest-bcm5974.c \
 	litest-generic-singletouch.c \
 	litest-keyboard.c \
diff --git a/test/litest-atmel-hover.c b/test/litest-atmel-hover.c
new file mode 100644
index 0000000..7da0901
--- /dev/null
+++ b/test/litest-atmel-hover.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+
+#include "libinput-util.h"
+
+#include "litest.h"
+#include "litest-int.h"
+
+static void
+atmel_hover_create(struct litest_device *d);
+
+static void
+litest_atmel_hover_setup(void)
+{
+	struct litest_device *d = litest_create_device(LITEST_ATMEL_HOVER);
+	litest_set_current_device(d);
+}
+
+static struct input_event down[] = {
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+	{ .type = -1, .code = -1 },
+};
+
+static struct input_event move[] = {
+	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+	{ .type = -1, .code = -1 },
+};
+
+static struct input_event up[] = {
+	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = -1 },
+	{ .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = 1 },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = 0  },
+	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+	{ .type = -1, .code = -1 },
+};
+
+static struct litest_device_interface interface = {
+	.touch_down_events = down,
+	.touch_move_events = move,
+	.touch_up_events = up,
+};
+
+static struct input_id input_id = {
+	.bustype = 0x18,
+	.vendor = 0x0,
+	.product = 0x0,
+};
+
+static int events[] = {
+	EV_KEY, BTN_LEFT,
+	EV_KEY, BTN_TOOL_FINGER,
+	EV_KEY, BTN_TOUCH,
+	EV_KEY, BTN_TOOL_DOUBLETAP,
+	EV_KEY, BTN_TOOL_TRIPLETAP,
+	EV_KEY, BTN_TOOL_QUADTAP,
+	EV_KEY, BTN_TOOL_QUINTTAP,
+	INPUT_PROP_MAX, INPUT_PROP_POINTER,
+	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
+	-1, -1,
+};
+
+static struct input_absinfo absinfo[] = {
+	{ ABS_X, 0, 960, 0, 0, 10 },
+	{ ABS_Y, 0, 540, 0, 0, 10 },
+	{ ABS_PRESSURE, 0, 255, 0, 0, 0 },
+	{ ABS_MT_SLOT, 0, 9, 0, 0, 0 },
+	{ ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0, 0 },
+	{ ABS_MT_ORIENTATION, 0, 255, 0, 0, 0 },
+	{ ABS_MT_POSITION_X, 0, 960, 0, 0, 10 },
+	{ ABS_MT_POSITION_Y, 0, 540, 0, 0, 10 },
+	{ ABS_MT_TOOL_TYPE, 0, 2, 0, 0, 0 },
+	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
+	{ ABS_MT_PRESSURE, 0, 255, 0, 0, 0 },
+	{ ABS_MT_DISTANCE, 0, 1, 0, 0, 0 },
+	{ .value = -1 }
+};
+
+struct litest_test_device litest_atmel_hover_device = {
+	.type = LITEST_ATMEL_HOVER,
+	.features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_CLICKPAD | LITEST_HOVER,
+	.shortname = "atmel hover",
+	.setup = litest_atmel_hover_setup,
+	.interface = &interface,
+	.create = atmel_hover_create,
+
+	.name = "Atmel maXTouch Touchpad",
+	.id = &input_id,
+	.events = events,
+	.absinfo = absinfo,
+};
+
+static void
+atmel_hover_create(struct litest_device *d)
+{
+	struct litest_semi_mt *semi_mt = zalloc(sizeof(*semi_mt));
+	assert(semi_mt);
+
+	d->private = semi_mt;
+
+	d->uinput = litest_create_uinput_device_from_description(
+			litest_atmel_hover_device.name,
+			litest_atmel_hover_device.id,
+			absinfo,
+			events);
+	d->interface = &interface;
+}
diff --git a/test/litest.c b/test/litest.c
index 15fc87e..fa5095b 100644
--- a/test/litest.c
+++ b/test/litest.c
@@ -103,6 +103,7 @@ extern struct litest_test_device litest_wheel_only_device;
 extern struct litest_test_device litest_mouse_roccat_device;
 extern struct litest_test_device litest_ms_surface_cover_device;
 extern struct litest_test_device litest_logitech_trackball_device;
+extern struct litest_test_device litest_atmel_hover_device;
 
 struct litest_test_device* devices[] = {
 	&litest_synaptics_clickpad_device,
@@ -127,6 +128,7 @@ struct litest_test_device* devices[] = {
 	&litest_mouse_roccat_device,
 	&litest_ms_surface_cover_device,
 	&litest_logitech_trackball_device,
+	&litest_atmel_hover_device,
 	NULL,
 };
 
@@ -794,7 +796,8 @@ litest_event(struct litest_device *d, unsigned int type,
 int
 litest_auto_assign_value(struct litest_device *d,
 			 const struct input_event *ev,
-			 int slot, double x, double y)
+			 int slot, double x, double y,
+			 int touching)
 {
 	static int tracking_id;
 	int value = ev->value;
@@ -817,6 +820,9 @@ litest_auto_assign_value(struct litest_device *d,
 	case ABS_MT_SLOT:
 		value = slot;
 		break;
+	case ABS_MT_DISTANCE:
+		value = touching ? 0 : 1;
+		break;
 	}
 
 	return value;
@@ -833,9 +839,17 @@ send_btntool(struct litest_device *d)
 	litest_event(d, EV_KEY, BTN_TOOL_QUINTTAP, d->ntouches_down == 5);
 }
 
-void
-litest_touch_down(struct litest_device *d, unsigned int slot,
-		  double x, double y)
+static inline int
+litest_validate_event(struct input_event *ev, int send_ev_syn)
+{
+	return !((int16_t)ev->type == EV_SYN &&
+		(int16_t)ev->code == SYN_REPORT &&
+		!send_ev_syn);
+}
+
+static void
+litest_slot_start(struct litest_device *d, unsigned int slot,
+		   double x, double y, int touching, int sync)
 {
 	struct input_event *ev;
 
@@ -851,13 +865,22 @@ litest_touch_down(struct litest_device *d, unsigned int slot,
 
 	ev = d->interface->touch_down_events;
 	while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
-		int value = litest_auto_assign_value(d, ev, slot, x, y);
-		litest_event(d, ev->type, ev->code, value);
+		int value = litest_auto_assign_value(d, ev, slot, x, y, touching);
+
+		if (litest_validate_event(ev, sync))
+			litest_event(d, ev->type, ev->code, value);
 		ev++;
 	}
 }
 
 void
+litest_touch_down(struct litest_device *d, unsigned int slot,
+		  double x, double y)
+{
+	litest_slot_start(d, slot, x, y, 1, 1);
+}
+
+void
 litest_touch_up(struct litest_device *d, unsigned int slot)
 {
 	struct input_event *ev;
@@ -882,15 +905,15 @@ litest_touch_up(struct litest_device *d, unsigned int slot)
 		ev = up;
 
 	while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
-		int value = litest_auto_assign_value(d, ev, slot, 0, 0);
+		int value = litest_auto_assign_value(d, ev, slot, 0, 0, 0);
 		litest_event(d, ev->type, ev->code, value);
 		ev++;
 	}
 }
 
-void
-litest_touch_move(struct litest_device *d, unsigned int slot,
-		  double x, double y)
+static void
+litest_slot_move(struct litest_device *d, unsigned int slot,
+		   double x, double y, int touching, int sync)
 {
 	struct input_event *ev;
 
@@ -901,13 +924,21 @@ litest_touch_move(struct litest_device *d, unsigned int slot,
 
 	ev = d->interface->touch_move_events;
 	while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
-		int value = litest_auto_assign_value(d, ev, slot, x, y);
-		litest_event(d, ev->type, ev->code, value);
+		int value = litest_auto_assign_value(d, ev, slot, x, y, touching);
+		if (litest_validate_event(ev, sync))
+			litest_event(d, ev->type, ev->code, value);
 		ev++;
 	}
 }
 
 void
+litest_touch_move(struct litest_device *d, unsigned int slot,
+		  double x, double y)
+{
+	litest_slot_move(d, slot, x, y, 1, 1);
+}
+
+void
 litest_touch_move_to(struct litest_device *d,
 		     unsigned int slot,
 		     double x_from, double y_from,
@@ -950,6 +981,96 @@ litest_touch_move_two_touches(struct litest_device *d,
 }
 
 void
+litest_hover_start(struct litest_device *d, unsigned int slot,
+		   double x, double y, int sync)
+{
+	litest_slot_start(d, slot, x, y, 0, sync);
+}
+
+void
+litest_hover_end(struct litest_device *d, unsigned int slot, int sync)
+{
+	struct input_event *ev;
+	struct input_event up[] = {
+		{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
+		{ .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = 1 },
+		{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = -1 },
+		{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+		{ .type = -1, .code = -1 }
+	};
+
+	assert(d->ntouches_down > 0);
+	d->ntouches_down--;
+
+	send_btntool(d);
+
+	if (d->interface->touch_up) {
+		d->interface->touch_up(d, slot);
+		return;
+	} else if (d->interface->touch_up_events) {
+		ev = d->interface->touch_up_events;
+	} else
+		ev = up;
+
+	while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
+		int value = litest_auto_assign_value(d, ev, slot, 0, 0, 0);
+		if (litest_validate_event(ev, sync))
+			litest_event(d, ev->type, ev->code, value);
+		ev++;
+	}
+}
+
+void
+litest_hover_move(struct litest_device *d, unsigned int slot,
+		  double x, double y, int sync)
+{
+	litest_slot_move(d, slot, x, y, 0, sync);
+}
+
+void
+litest_hover_move_to(struct litest_device *d,
+		     unsigned int slot,
+		     double x_from, double y_from,
+		     double x_to, double y_to,
+		     int steps, int sleep_ms, int sync)
+{
+	for (int i = 0; i < steps - 1; i++) {
+		litest_hover_move(d, slot,
+				  x_from + (x_to - x_from)/steps * i,
+				  y_from + (y_to - y_from)/steps * i,
+				  sync);
+		if (sleep_ms) {
+			libinput_dispatch(d->libinput);
+			msleep(sleep_ms);
+			libinput_dispatch(d->libinput);
+		}
+	}
+	litest_hover_move(d, slot, x_to, y_to, sync);
+}
+
+void
+litest_hover_move_two_touches(struct litest_device *d,
+			      double x0, double y0,
+			      double x1, double y1,
+			      double dx, double dy,
+			      int steps, int sleep_ms)
+{
+	for (int i = 0; i < steps - 1; i++) {
+		litest_hover_move(d, 0, x0 + dx / steps * i,
+					y0 + dy / steps * i, 0);
+		litest_hover_move(d, 1, x1 + dx / steps * i,
+					y1 + dy / steps * i, 1);
+		if (sleep_ms) {
+			libinput_dispatch(d->libinput);
+			msleep(sleep_ms);
+			libinput_dispatch(d->libinput);
+		}
+	}
+	litest_hover_move(d, 0, x0 + dx, y0 + dy, 0);
+	litest_hover_move(d, 1, x1 + dx, y1 + dy, 1);
+}
+
+void
 litest_button_click(struct litest_device *d, unsigned int button, bool is_press)
 {
 
@@ -1484,11 +1605,11 @@ send_abs_xy(struct litest_device *d, double x, double y)
 	e.type = EV_ABS;
 	e.code = ABS_X;
 	e.value = LITEST_AUTO_ASSIGN;
-	val = litest_auto_assign_value(d, &e, 0, x, y);
+	val = litest_auto_assign_value(d, &e, 0, x, y, 1);
 	litest_event(d, EV_ABS, ABS_X, val);
 
 	e.code = ABS_Y;
-	val = litest_auto_assign_value(d, &e, 0, x, y);
+	val = litest_auto_assign_value(d, &e, 0, x, y, 1);
 	litest_event(d, EV_ABS, ABS_Y, val);
 }
 
@@ -1501,12 +1622,12 @@ send_abs_mt_xy(struct litest_device *d, double x, double y)
 	e.type = EV_ABS;
 	e.code = ABS_MT_POSITION_X;
 	e.value = LITEST_AUTO_ASSIGN;
-	val = litest_auto_assign_value(d, &e, 0, x, y);
+	val = litest_auto_assign_value(d, &e, 0, x, y, 1);
 	litest_event(d, EV_ABS, ABS_MT_POSITION_X, val);
 
 	e.code = ABS_MT_POSITION_Y;
 	e.value = LITEST_AUTO_ASSIGN;
-	val = litest_auto_assign_value(d, &e, 0, x, y);
+	val = litest_auto_assign_value(d, &e, 0, x, y, 1);
 	litest_event(d, EV_ABS, ABS_MT_POSITION_Y, val);
 }
 
diff --git a/test/litest.h b/test/litest.h
index 611cdf7..bb9ba03 100644
--- a/test/litest.h
+++ b/test/litest.h
@@ -57,6 +57,7 @@ enum litest_device_type {
 	LITEST_WHEEL_ONLY = -21,
 	LITEST_MOUSE_ROCCAT = -22,
 	LITEST_LOGITECH_TRACKBALL = -23,
+	LITEST_ATMEL_HOVER = -24,
 };
 
 enum litest_device_feature {
@@ -77,6 +78,7 @@ enum litest_device_feature {
 	LITEST_FAKE_MT = 1 << 12,
 	LITEST_ABSOLUTE = 1 << 13,
 	LITEST_PROTOCOL_A = 1 << 14,
+	LITEST_HOVER = 1 << 15,
 };
 
 struct litest_device {
@@ -141,7 +143,8 @@ void litest_event(struct litest_device *t,
 		  int value);
 int litest_auto_assign_value(struct litest_device *d,
 			     const struct input_event *ev,
-			     int slot, double x, double y);
+			     int slot, double x, double y,
+			     int touching);
 void litest_touch_up(struct litest_device *d, unsigned int slot);
 void litest_touch_move(struct litest_device *d,
 		       unsigned int slot,
@@ -161,6 +164,27 @@ void litest_touch_move_two_touches(struct litest_device *d,
 				   double x1, double y1,
 				   double dx, double dy,
 				   int steps, int sleep_ms);
+void litest_hover_start(struct litest_device *d,
+			unsigned int slot,
+			double x,
+			double y,
+			int sync);
+void litest_hover_end(struct litest_device *d, unsigned int slot, int sync);
+void litest_hover_move(struct litest_device *d,
+		       unsigned int slot,
+		       double x,
+		       double y,
+		       int sync);
+void litest_hover_move_to(struct litest_device *d,
+			  unsigned int slot,
+			  double x_from, double y_from,
+			  double x_to, double y_to,
+			  int steps, int sleep_ms, int sync);
+void litest_hover_move_two_touches(struct litest_device *d,
+				   double x0, double y0,
+				   double x1, double y1,
+				   double dx, double dy,
+				   int steps, int sleep_ms);
 void litest_button_click(struct litest_device *d,
 			 unsigned int button,
 			 bool is_press);
diff --git a/test/touchpad.c b/test/touchpad.c
index 49f5023..f0be6d9 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -3834,6 +3834,165 @@ START_TEST(touchpad_semi_mt_hover_2fg_1fg_down)
 }
 END_TEST
 
+START_TEST(touchpad_hover_noevent)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+
+	litest_drain_events(li);
+
+	litest_hover_start(dev, 0, 50, 50, 1);
+	litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10, 1);
+	litest_hover_end(dev, 0, 1);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_hover_down)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+
+	litest_drain_events(li);
+
+	/* hover the finger */
+	litest_hover_start(dev, 0, 50, 50, 1);
+
+	litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10, 1);
+
+	litest_assert_empty_queue(li);
+
+	/* touch the finger on the sensor */
+	litest_touch_move_to(dev, 0, 70, 70, 50, 50, 10, 10);
+
+	libinput_dispatch(li);
+
+	litest_assert_only_typed_events(li,
+					LIBINPUT_EVENT_POINTER_MOTION);
+
+	/* go back to hover */
+	litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10, 1);
+	litest_hover_end(dev, 0, 1);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_hover_down_hover_down)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	int i;
+
+	litest_drain_events(li);
+
+	litest_hover_start(dev, 0, 50, 50, 1);
+
+	for (i = 0; i < 3; i++) {
+
+		/* hover the finger */
+		litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10, 1);
+
+		litest_assert_empty_queue(li);
+
+		/* touch the finger */
+		litest_touch_move_to(dev, 0, 70, 70, 50, 50, 10, 10);
+
+		libinput_dispatch(li);
+
+		litest_assert_only_typed_events(li,
+						LIBINPUT_EVENT_POINTER_MOTION);
+	}
+
+	litest_hover_end(dev, 0, 1);
+
+	/* start a new touch to be sure */
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_move_to(dev, 0, 50, 50, 70, 70, 10, 10);
+	litest_touch_up(dev, 0);
+
+	litest_assert_only_typed_events(li,
+					LIBINPUT_EVENT_POINTER_MOTION);
+}
+END_TEST
+
+START_TEST(touchpad_hover_down_up)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+
+	litest_drain_events(li);
+
+	/* hover two fingers, and a touch */
+	litest_hover_start(dev, 0, 50, 50, 0);
+	litest_hover_start(dev, 1, 50, 50, 0);
+	litest_touch_down(dev, 2, 50, 50);
+
+	litest_assert_empty_queue(li);
+
+	/* hover first finger, end second and third in same frame */
+	litest_hover_move(dev, 0, 70, 70, 0);
+	litest_hover_end(dev, 1, 0);
+	litest_touch_up(dev, 2);
+
+	litest_assert_empty_queue(li);
+
+	/* now move the finger */
+	litest_touch_move_to(dev, 0, 50, 50, 70, 70, 10, 10);
+
+	litest_touch_up(dev, 0);
+
+	litest_assert_only_typed_events(li,
+					LIBINPUT_EVENT_POINTER_MOTION);
+}
+END_TEST
+
+START_TEST(touchpad_hover_2fg_noevent)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+
+	litest_drain_events(li);
+
+	/* hover two fingers */
+	litest_hover_start(dev, 0, 25, 25, 0);
+	litest_hover_start(dev, 1, 50, 50, 1);
+
+	litest_hover_move_two_touches(dev, 25, 25, 50, 50, 50, 50, 10, 0);
+
+	litest_hover_end(dev, 0, 0);
+	litest_hover_end(dev, 1, 1);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_hover_2fg_1fg_down)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	int i;
+
+	litest_drain_events(li);
+
+	/* hover two fingers */
+	litest_hover_start(dev, 0, 25, 25, 0);
+	litest_touch_down(dev, 1, 50, 50);
+
+	for (i = 0; i < 10; i++) {
+		litest_hover_move(dev, 0, 25 + 5 * i, 25 + 5 * i, 0);
+		litest_touch_move(dev, 1, 50 + 5 * i, 50 - 5 * i);
+	}
+
+	litest_hover_end(dev, 0, 0);
+	litest_touch_up(dev, 1);
+
+	litest_assert_only_typed_events(li,
+					LIBINPUT_EVENT_POINTER_MOTION);
+}
+END_TEST
+
 static void
 assert_btnevent_from_device(struct litest_device *device,
 			    unsigned int button,
@@ -4241,6 +4400,13 @@ int main(int argc, char **argv) {
 	litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_2fg_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT);
 	litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_2fg_1fg_down, LITEST_SYNAPTICS_HOVER_SEMI_MT);
 
+	litest_add("touchpad:hover", touchpad_hover_noevent, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
+	litest_add("touchpad:hover", touchpad_hover_down, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
+	litest_add("touchpad:hover", touchpad_hover_down_up, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
+	litest_add("touchpad:hover", touchpad_hover_down_hover_down, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
+	litest_add("touchpad:hover", touchpad_hover_2fg_noevent, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
+	litest_add("touchpad:hover", touchpad_hover_2fg_1fg_down, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
+
 	litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_buttons, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS);
 	litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_mb_scroll, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS);
 	litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_mb_click, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS);
-- 
1.7.1



More information about the wayland-devel mailing list