[PATCH libinput 11/11] touchpad: touches after the last key press can be released

Peter Hutterer peter.hutterer at who-t.net
Mon May 25 15:52:58 PDT 2015


The current code labels a touch as palm if it started within the typing
timeouts. To move the pointer even after the timeout expires, a user has to
lift the finger which is quite annoying and different to the old synaptics
driver behaviour (which had a simple on/off toggle on whether to let events
through or not).

Be smarter about this: if a touch starts _after_ the last key press event,
release it for pointer motion once the timeout expires. Touches started before
the last key press remain labelled as palms. This makes it possible to rest
the palm on the touchpad while typing without getting interference but also
provides a more responsive UI when moving from typing to using the touchpad
normally.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 doc/palm-detection.dox              |   5 +-
 src/evdev-mt-touchpad-edge-scroll.c |   3 +
 src/evdev-mt-touchpad.c             |  24 +++++-
 src/evdev-mt-touchpad.h             |   2 +
 test/touchpad.c                     | 144 +++++++++++++++++++++++++++++++++++-
 5 files changed, 169 insertions(+), 9 deletions(-)

diff --git a/doc/palm-detection.dox b/doc/palm-detection.dox
index a03f9c1..d787455 100644
--- a/doc/palm-detection.dox
+++ b/doc/palm-detection.dox
@@ -74,8 +74,9 @@ Notable behaviors of libinput's disable-while-typing feature:
 - Some keys do not trigger the timeout, specifically some modifier keys 
   (Ctrl, Alt, Shift, and Fn). Actions such as Ctrl + click thus stay
   responsive.
-- Touches started while the touchpad is disabled do not control the cursor,
-  it is thus possible to rest the palm on the touchpad while typing.
+- Touches started while typing do not control the cursor even after typing
+  has stopped, it is thus possible to rest the palm on the touchpad while
+  typing.
 - Physical buttons work even while the touchpad is disabled. This includes
   software-emulated buttons.
 
diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c
index f7eae9e..8a4d892 100644
--- a/src/evdev-mt-touchpad-edge-scroll.c
+++ b/src/evdev-mt-touchpad-edge-scroll.c
@@ -361,6 +361,9 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
 		if (!t->dirty)
 			continue;
 
+		if (t->palm.state != PALM_NONE)
+			continue;
+
 		switch (t->scroll.edge) {
 			case EDGE_NONE:
 				if (t->scroll.direction != -1) {
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index db330ec..e130363 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -237,6 +237,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 	t->state = TOUCH_END;
 	t->pinned.is_pinned = false;
 	t->millis = time;
+	t->palm.time = 0;
 	assert(tp->nfingers_down >= 1);
 	tp->nfingers_down--;
 	tp->queued |= TOUCHPAD_EVENT_MOTION;
@@ -487,14 +488,28 @@ tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t)
 static int
 tp_palm_detect_dwt(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
-	if (!tp->dwt.keyboard_active)
-		return 0;
-
-	if (t->state == TOUCH_BEGIN) {
+	if (tp->dwt.keyboard_active &&
+	    t->state == TOUCH_BEGIN) {
 		t->palm.state = PALM_TYPING;
 		t->palm.time = time;
 		t->palm.first = t->point;
 		return 1;
+	} else if (!tp->dwt.keyboard_active &&
+		   t->state == TOUCH_UPDATE &&
+		   t->palm.state == PALM_TYPING)
+	{
+		/* If a touch has started before the first or after the last
+		   key press, release it on timeout. Benefit: a palm rested
+		   while typing on the touchpad will be ignored, but a touch
+		   started once we stop typing will be able to control the
+		   pointer (alas not tap, etc.).
+		   */
+		if (t->palm.time == 0 ||
+		    t->palm.time > tp->dwt.keyboard_last_press_time) {
+			t->palm.state = PALM_NONE;
+			log_debug(tp_libinput_context(tp),
+				  "palm: touch released, timeout after typing\n");
+		}
 	}
 
 	return 0;
@@ -1002,6 +1017,7 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
 		timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2;
 	}
 
+	tp->dwt.keyboard_last_press_time = time;
 	libinput_timer_set(&tp->dwt.keyboard_timer,
 			   time + timeout);
 }
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 9eeb5fb..bced2b1 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -287,6 +287,8 @@ struct tp_dispatch {
 		struct libinput_event_listener keyboard_listener;
 		struct libinput_timer keyboard_timer;
 		struct evdev_device *keyboard;
+
+		uint64_t keyboard_last_press_time;
 	} dwt;
 };
 
diff --git a/test/touchpad.c b/test/touchpad.c
index fc77439..5579c04 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -4646,6 +4646,45 @@ START_TEST(touchpad_dwt)
 }
 END_TEST
 
+START_TEST(touchpad_dwt_enable_touch)
+{
+	struct litest_device *touchpad = litest_current_device();
+	struct litest_device *keyboard;
+	struct libinput *li = touchpad->libinput;
+
+	if (!has_disable_while_typing(touchpad))
+		return;
+
+	keyboard = litest_add_device(li, LITEST_KEYBOARD);
+	libinput_device_config_tap_set_enabled(touchpad->libinput_device,
+					       LIBINPUT_CONFIG_TAP_DISABLED);
+	litest_drain_events(li);
+
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+	libinput_dispatch(li);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
+
+	/* finger down after last key event, but
+	   we're still within timeout - no events */
+	msleep(10);
+	litest_touch_down(touchpad, 0, 50, 50);
+	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1);
+	litest_assert_empty_queue(li);
+
+	litest_timeout_dwt_short();
+	libinput_dispatch(li);
+
+	/* same touch after timeout  - motion events*/
+	litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 10, 1);
+	litest_touch_up(touchpad, 0);
+
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+	litest_delete_device(keyboard);
+}
+END_TEST
+
 START_TEST(touchpad_dwt_touch_hold)
 {
 	struct litest_device *touchpad = litest_current_device();
@@ -4661,7 +4700,7 @@ START_TEST(touchpad_dwt_touch_hold)
 	litest_drain_events(li);
 
 	litest_keyboard_key(keyboard, KEY_A, true);
-	libinput_dispatch(li);
+	msleep(1); /* make sure touch starts after key press */
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1);
 
@@ -4678,7 +4717,7 @@ START_TEST(touchpad_dwt_touch_hold)
 	libinput_dispatch(li);
 	litest_touch_move_to(touchpad, 0, 30, 50, 50, 50, 5, 1);
 	litest_touch_up(touchpad, 0);
-	litest_assert_empty_queue(li);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_delete_device(keyboard);
 }
@@ -4837,6 +4876,7 @@ START_TEST(touchpad_dwt_tap_drag)
 
 	litest_keyboard_key(keyboard, KEY_A, true);
 	libinput_dispatch(li);
+	msleep(1); /* make sure touch starts after key press */
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_up(touchpad, 0);
 	litest_touch_down(touchpad, 0, 50, 50);
@@ -4849,7 +4889,7 @@ START_TEST(touchpad_dwt_tap_drag)
 	libinput_dispatch(li);
 	litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 5, 1);
 	litest_touch_up(touchpad, 0);
-	litest_assert_empty_queue(li);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_delete_device(keyboard);
 }
@@ -4887,6 +4927,101 @@ START_TEST(touchpad_dwt_click)
 }
 END_TEST
 
+START_TEST(touchpad_dwt_edge_scroll)
+{
+	struct litest_device *touchpad = litest_current_device();
+	struct litest_device *keyboard;
+	struct libinput *li = touchpad->libinput;
+
+	if (!has_disable_while_typing(touchpad))
+		return;
+
+	enable_edge_scroll(touchpad);
+
+	keyboard = litest_add_device(li, LITEST_KEYBOARD);
+	litest_drain_events(li);
+
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
+
+	litest_touch_down(touchpad, 0, 99, 20);
+	libinput_dispatch(li);
+	litest_timeout_edgescroll();
+	libinput_dispatch(li);
+	litest_assert_empty_queue(li);
+
+	/* edge scroll timeout is 300ms atm, make sure we don't accidentally
+	   exit the DWT timeout */
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+	libinput_dispatch(li);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
+
+	litest_touch_move_to(touchpad, 0, 99, 20, 99, 80, 60, 10);
+	libinput_dispatch(li);
+	litest_assert_empty_queue(li);
+
+	litest_touch_move_to(touchpad, 0, 99, 80, 99, 20, 60, 10);
+	litest_touch_up(touchpad, 0);
+	libinput_dispatch(li);
+	litest_assert_empty_queue(li);
+
+	litest_delete_device(keyboard);
+}
+END_TEST
+
+START_TEST(touchpad_dwt_edge_scroll_interrupt)
+{
+	struct litest_device *touchpad = litest_current_device();
+	struct litest_device *keyboard;
+	struct libinput *li = touchpad->libinput;
+	struct libinput_event_pointer *stop_event;
+
+	if (!has_disable_while_typing(touchpad))
+		return;
+
+	enable_edge_scroll(touchpad);
+
+	keyboard = litest_add_device(li, LITEST_KEYBOARD);
+	litest_drain_events(li);
+
+	litest_touch_down(touchpad, 0, 99, 20);
+	libinput_dispatch(li);
+	litest_timeout_edgescroll();
+	litest_touch_move_to(touchpad, 0, 99, 20, 99, 30, 10, 10);
+	libinput_dispatch(li);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS);
+
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+
+	/* scroll stop event */
+	litest_wait_for_event(li);
+	stop_event = litest_is_axis_event(libinput_get_event(li),
+					  LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
+					  LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
+	libinput_event_destroy(libinput_event_pointer_get_base_event(stop_event));
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
+
+	litest_timeout_dwt_long();
+
+	/* Known bad behavior: a touch starting to edge-scroll before dwt
+	 * kicks in will stop to scroll but be recognized as normal
+	 * pointer-moving touch once the timeout expires. We'll fix that
+	 * when we need to.
+	 */
+	litest_touch_move_to(touchpad, 0, 99, 30, 99, 80, 10, 5);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+	litest_delete_device(keyboard);
+}
+END_TEST
+
 void
 litest_setup_tests(void)
 {
@@ -5037,6 +5172,7 @@ litest_setup_tests(void)
 	litest_add_ranged("touchpad:state", touchpad_initial_state, LITEST_TOUCHPAD, LITEST_ANY, &axis_range);
 
 	litest_add("touchpad:dwt", touchpad_dwt, LITEST_TOUCHPAD, LITEST_ANY);
+	litest_add("touchpad:dwt", touchpad_dwt_enable_touch, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add("touchpad:dwt", touchpad_dwt_touch_hold, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add("touchpad:dwt", touchpad_dwt_key_hold, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add("touchpad:dwt", touchpad_dwt_type, LITEST_TOUCHPAD, LITEST_ANY);
@@ -5044,4 +5180,6 @@ litest_setup_tests(void)
 	litest_add("touchpad:dwt", touchpad_dwt_tap, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add("touchpad:dwt", touchpad_dwt_tap_drag, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add("touchpad:dwt", touchpad_dwt_click, LITEST_TOUCHPAD, LITEST_ANY);
+	litest_add("touchpad:dwt", touchpad_dwt_edge_scroll, LITEST_TOUCHPAD, LITEST_CLICKPAD);
+	litest_add("touchpad:dwt", touchpad_dwt_edge_scroll_interrupt, LITEST_TOUCHPAD, LITEST_CLICKPAD);
 }
-- 
2.4.1



More information about the wayland-devel mailing list