[PATCH libinput 3/4] touchpad: handle serial synaptics slot confusion on TRIPLETAP

Peter Hutterer peter.hutterer at who-t.net
Mon Jul 20 22:51:03 PDT 2015


Synatics touchpads only have 2 slots, but support TRIPLETAP and above. When
the third finger touches, the kernel may end the second slot and re-start it
with the coordinates of the third touch in the next frame. The event sequence
is something like:

ABS_MT_SLOT          0
ABS_MT_POSITION_X    4000
ABS_MT_POSITION_Y    4000
ABS_MT_PRESSURE      78
ABS_MT_SLOT          1
ABS_MT_TRACKING_ID   -1

ABS_X                4000
ABS_Y                4000
ABS_PRESSURE         78
BTN_TOOL_DOUBLETAP   0
BTN_TOOL_TRIPLETAP   1
--- SYN_REPORT (0) ----------
ABS_MT_SLOT          0
ABS_MT_POSITION_X    4000
ABS_MT_POSITION_Y    4000
ABS_MT_PRESSURE      78
ABS_MT_SLOT          1
ABS_MT_TRACKING_ID   55
ABS_MT_POSITION_X    2000
ABS_MT_POSITION_Y    2000
ABS_MT_PRESSURE      72

ABS_X                4000
ABS_Y                4000
ABS_PRESSURE         78
--- SYN_REPORT (0) ----------

libinput usually ignores any BTN_TOOL_* <= num_slots since we expect
that the slot values are valid. Make an exception for the serial synaptics
touchpads. If a touch has ended when the fake touch goes above active-slots
(but still within num-slots), move that touch back to UPDATE. This ensures the
right number of nfingers_down. When the touch restarts again in the next
frame, tp_begin_touch() will skip over re-initializing it because it's already
in UPDATE anyway.

Note that at this point this only handles the transition _to_ TRIPLETAP, not
from TRIPLETAP to DOUBLETAP. Need to wait for this to be seen in the wild
first.

https://bugs.freedesktop.org/show_bug.cgi?id=91352

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 src/evdev-mt-touchpad.c |  38 ++++++++++++++++
 test/touchpad.c         | 116 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 154 insertions(+)

diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index b78618b..1bf206d 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -341,6 +341,40 @@ tp_process_absolute_st(struct tp_dispatch *tp,
 	}
 }
 
+static inline void
+tp_restore_synaptics_touches(struct tp_dispatch *tp,
+			     uint64_t time)
+{
+	unsigned int i;
+	unsigned int nfake_touches;
+
+	nfake_touches = tp_fake_finger_count(tp);
+	if (nfake_touches < 3)
+		return;
+
+	if (tp->nfingers_down >= nfake_touches ||
+	    tp->nfingers_down == tp->num_slots)
+		return;
+
+	/* Synaptics devices may end touch 2 on BTN_TOOL_TRIPLETAP
+	 * and start it again on the next frame with different coordinates
+	 * (#91352). We search the touches we have, if there is one that has
+	 * just ended despite us being on tripletap, we move it back to
+	 * update.
+	 */
+	for (i = 0; i < tp->num_slots; i++) {
+		struct tp_touch *t = tp_get_touch(tp, i);
+
+		if (t->state != TOUCH_END)
+			continue;
+
+		/* new touch, move it through begin to update immediately */
+		tp_new_touch(tp, t, time);
+		tp_begin_touch(tp, t, time);
+		t->state = TOUCH_UPDATE;
+	}
+}
+
 static void
 tp_process_fake_touches(struct tp_dispatch *tp,
 			uint64_t time)
@@ -353,6 +387,10 @@ tp_process_fake_touches(struct tp_dispatch *tp,
 	if (nfake_touches == FAKE_FINGER_OVERFLOW)
 		return;
 
+	if (tp->device->model_flags &
+	    EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD)
+		tp_restore_synaptics_touches(tp, time);
+
 	start = tp->has_mt ? tp->num_slots : 0;
 	for (i = start; i < tp->ntouches; i++) {
 		t = tp_get_touch(tp, i);
diff --git a/test/touchpad.c b/test/touchpad.c
index 64bdd44..bc10aba 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -4279,6 +4279,120 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg_tap)
 }
 END_TEST
 
+START_TEST(touchpad_tool_tripletap_touch_count)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct libinput_event *event;
+	struct libinput_event_pointer *ptrev;
+
+	/* Synaptics touchpads sometimes end one touch point while
+	 * simultaneously setting BTN_TOOL_TRIPLETAP.
+	 * https://bugs.freedesktop.org/show_bug.cgi?id=91352
+	 */
+	litest_drain_events(li);
+	enable_clickfinger(dev);
+
+	/* touch 1 down */
+	litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
+	litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, 1);
+	litest_event(dev, EV_ABS, ABS_MT_POSITION_X, 1200);
+	litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 3200);
+	litest_event(dev, EV_ABS, ABS_MT_PRESSURE, 78);
+	litest_event(dev, EV_ABS, ABS_X, 1200);
+	litest_event(dev, EV_ABS, ABS_Y, 3200);
+	litest_event(dev, EV_ABS, ABS_PRESSURE, 78);
+	litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1);
+	litest_event(dev, EV_KEY, BTN_TOUCH, 1);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	libinput_dispatch(li);
+	msleep(2);
+
+	/* touch 2 down */
+	litest_event(dev, EV_ABS, ABS_MT_SLOT, 1);
+	litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, 1);
+	litest_event(dev, EV_ABS, ABS_MT_POSITION_X, 2200);
+	litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 3200);
+	litest_event(dev, EV_ABS, ABS_MT_PRESSURE, 73);
+	litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0);
+	litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	libinput_dispatch(li);
+	msleep(2);
+
+	/* touch 3 down, coordinate jump + ends slot 1 */
+	litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
+	litest_event(dev, EV_ABS, ABS_MT_POSITION_X, 4000);
+	litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 4000);
+	litest_event(dev, EV_ABS, ABS_MT_PRESSURE, 78);
+	litest_event(dev, EV_ABS, ABS_MT_SLOT, 1);
+	litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+	litest_event(dev, EV_ABS, ABS_X, 4000);
+	litest_event(dev, EV_ABS, ABS_Y, 4000);
+	litest_event(dev, EV_ABS, ABS_PRESSURE, 78);
+	litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
+	litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 1);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	libinput_dispatch(li);
+	msleep(2);
+
+	/* slot 2 reactivated:
+	 * Note, slot is activated close enough that we don't accidentally
+	 * trigger the clickfinger distance check, remains to be seen if
+	 * that is true for real-world interaction.
+	 */
+	litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
+	litest_event(dev, EV_ABS, ABS_MT_POSITION_X, 4000);
+	litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 4000);
+	litest_event(dev, EV_ABS, ABS_MT_PRESSURE, 78);
+	litest_event(dev, EV_ABS, ABS_MT_SLOT, 1);
+	litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, 3);
+	litest_event(dev, EV_ABS, ABS_MT_POSITION_X, 3500);
+	litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 3500);
+	litest_event(dev, EV_ABS, ABS_MT_PRESSURE, 73);
+	litest_event(dev, EV_ABS, ABS_X, 4000);
+	litest_event(dev, EV_ABS, ABS_Y, 4000);
+	litest_event(dev, EV_ABS, ABS_PRESSURE, 78);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	libinput_dispatch(li);
+	msleep(2);
+
+	/* now a click should trigger middle click */
+	litest_event(dev, EV_KEY, BTN_LEFT, 1);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	libinput_dispatch(li);
+	litest_event(dev, EV_KEY, BTN_LEFT, 0);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	libinput_dispatch(li);
+
+	litest_wait_for_event(li);
+	event = libinput_get_event(li);
+	ptrev = litest_is_button_event(event,
+				       BTN_MIDDLE,
+				       LIBINPUT_BUTTON_STATE_PRESSED);
+	libinput_event_destroy(event);
+	event = libinput_get_event(li);
+	ptrev = litest_is_button_event(event,
+				       BTN_MIDDLE,
+				       LIBINPUT_BUTTON_STATE_RELEASED);
+	/* silence gcc set-but-not-used warning, litest_is_button_event
+	 * checks what we care about */
+	event = libinput_event_pointer_get_base_event(ptrev);
+	libinput_event_destroy(event);
+
+	/* release everything */
+	litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
+	litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+	litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
+	litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+	litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0);
+	litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
+	litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 0);
+	litest_event(dev, EV_KEY, BTN_TOUCH, 0);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+}
+END_TEST
+
 void
 litest_setup_tests(void)
 {
@@ -4413,4 +4527,6 @@ litest_setup_tests(void)
 	litest_add("touchpad:thumb", touchpad_thumb_tap_hold, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add("touchpad:thumb", touchpad_thumb_tap_hold_2ndfg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
 	litest_add("touchpad:thumb", touchpad_thumb_tap_hold_2ndfg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+
+	litest_add_for_device("touchpad:bugs", touchpad_tool_tripletap_touch_count, LITEST_SYNAPTICS_TOPBUTTONPAD);
 }
-- 
2.4.3



More information about the wayland-devel mailing list