From bec07c41986b7e8c2a054b2336ba5b6d500e165f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Jul 2015 14:37:37 +1000 Subject: [PATCH 01/12] Move CASE_RETURN_STRING to libinput-util.h Signed-off-by: Peter Hutterer --- src/evdev-middle-button.c | 2 -- src/evdev-mt-touchpad-buttons.c | 2 -- src/evdev-mt-touchpad-edge-scroll.c | 2 -- src/evdev-mt-touchpad-gestures.c | 2 -- src/evdev-mt-touchpad-tap.c | 3 --- src/libinput-util.h | 2 ++ 6 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/evdev-middle-button.c b/src/evdev-middle-button.c index 98ac8872..48330d39 100644 --- a/src/evdev-middle-button.c +++ b/src/evdev-middle-button.c @@ -43,8 +43,6 @@ * as-is. */ -#define CASE_RETURN_STRING(a) case a: return #a; - static inline const char* middlebutton_state_to_str(enum evdev_middlebutton_state state) { diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 6aa74c31..9cfa491c 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -46,8 +46,6 @@ * The state machine only affects the soft button area code. */ -#define CASE_RETURN_STRING(a) case a: return #a; - static inline const char* button_state_to_str(enum button_state state) { switch(state) { diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index ade55e03..aa9197f0 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -32,8 +32,6 @@ #include "evdev-mt-touchpad.h" -#define CASE_RETURN_STRING(a) case a: return #a - /* Use a reasonably large threshold until locked into scrolling mode, to avoid accidentally locking in scrolling mode when trying to use the entire touchpad to move the pointer. The user can wait for the timeout to trigger diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index a23b5ce1..5c10dc35 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -32,8 +32,6 @@ #define DEFAULT_GESTURE_SWITCH_TIMEOUT 100 /* ms */ #define DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT 1000 /* ms */ -#define CASE_RETURN_STRING(a) case a: return #a - static inline const char* gesture_state_to_str(enum tp_gesture_2fg_state state) { diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 9bbf6b88..1354e899 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -35,8 +35,6 @@ #include "evdev-mt-touchpad.h" -#define CASE_RETURN_STRING(a) case a: return #a - #define DEFAULT_TAP_TIMEOUT_PERIOD 180 #define DEFAULT_DRAG_TIMEOUT_PERIOD 300 #define DEFAULT_TAP_MOVE_THRESHOLD TP_MM_TO_DPI_NORMALIZED(3) @@ -98,7 +96,6 @@ tap_event_to_str(enum tap_event event) } return NULL; } -#undef CASE_RETURN_STRING static void tp_tap_notify(struct tp_dispatch *tp, diff --git a/src/libinput-util.h b/src/libinput-util.h index b18aece5..12f6d981 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -41,6 +41,8 @@ /* The HW DPI rate we normalize to before calculating pointer acceleration */ #define DEFAULT_MOUSE_DPI 1000 +#define CASE_RETURN_STRING(a) case a: return #a; + void set_logging_enabled(int enabled); From c06a173e586c3ab432d8d5cb7e7538e3df556a1c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 17 Jul 2015 11:29:40 +1000 Subject: [PATCH 02/12] touchpad: skip thumb detection for touchpads smaller than 50mm Gets a bit cramped if you're trying to rest the thumb on a touchpad that small. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 8 ++++++++ test/touchpad.c | 10 +++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 35d0d853..19669fef 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1497,6 +1497,7 @@ tp_init_thumb(struct tp_dispatch *tp) { struct evdev_device *device = tp->device; const struct input_absinfo *abs; + double w = 0.0, h = 0.0; abs = libevdev_get_abs_info(device->evdev, ABS_MT_PRESSURE); if (!abs) @@ -1505,6 +1506,13 @@ tp_init_thumb(struct tp_dispatch *tp) if (abs->maximum - abs->minimum < 255) return 0; + /* if the touchpad is less than 50mm high, skip thumb detection. + * it's too small to meaningfully interact with a thumb on the + * touchpad */ + evdev_device_get_size(device, &w, &h); + if (h < 50) + return 0; + /* The touchpads we looked at so far have a clear thumb threshold of * ~100, you don't reach that with a normal finger interaction. * Note: "thumb" means massive touch that should not interact, not diff --git a/test/touchpad.c b/test/touchpad.c index 64bdd440..eb083c9d 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -3904,7 +3904,15 @@ END_TEST static int has_thumb_detect(struct litest_device *dev) { - return libevdev_has_event_code(dev->evdev, EV_ABS, ABS_MT_PRESSURE); + double w, h; + + if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_MT_PRESSURE)) + return 0; + + if (libinput_device_get_size(dev->libinput_device, &w, &h) != 0) + return 0; + + return h >= 50.0; } START_TEST(touchpad_thumb_begin_no_motion) From 058d05e5895062ef7a83ebff58b0f0aa00f2a9a9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Jul 2015 16:01:32 +1000 Subject: [PATCH 03/12] touchpad: only enable thumb detection on clickpads The use-case we have thumb detection for is to let a user rest a thumb on the touchpad before clicking. On a touchpad with physical buttons, the thumb won't be resting on the touchpad. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 3 +++ test/touchpad.c | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 19669fef..8629e40c 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1499,6 +1499,9 @@ tp_init_thumb(struct tp_dispatch *tp) const struct input_absinfo *abs; double w = 0.0, h = 0.0; + if (!tp->buttons.is_clickpad) + return 0; + abs = libevdev_get_abs_info(device->evdev, ABS_MT_PRESSURE); if (!abs) return 0; diff --git a/test/touchpad.c b/test/touchpad.c index eb083c9d..3e053eea 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4411,14 +4411,14 @@ litest_setup_tests(void) 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); - litest_add("touchpad:thumb", touchpad_thumb_begin_no_motion, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_update_no_motion, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_begin_no_motion, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_update_no_motion, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_clickfinger, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_btnarea, LITEST_CLICKPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_edgescroll, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_tap_begin, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_tap_touch, LITEST_TOUCHPAD, LITEST_ANY); - 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("touchpad:thumb", touchpad_thumb_edgescroll, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_tap_begin, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_tap_touch, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_tap_hold, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_tap_hold_2ndfg, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH); + litest_add("touchpad:thumb", touchpad_thumb_tap_hold_2ndfg_tap, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH); } From 6bb4b26f301d9ac2770270ae8f7cbda5b3db2b25 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Jul 2015 08:48:03 +1000 Subject: [PATCH 04/12] touchpad: 4-finger clickfingers are middle button clicks If a thumb is resting with a three-finger click, that must be a middle-click. And the odd case where we have a real four-finger click doesn't need worrying about. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 9cfa491c..b8dcb519 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -902,9 +902,8 @@ out: case 0: case 1: button = BTN_LEFT; break; case 2: button = BTN_RIGHT; break; - case 3: button = BTN_MIDDLE; break; default: - button = 0; + button = BTN_MIDDLE; break; break; } From f6bef12bfa9f47c18c30260151a798e49f5b33b1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 17 Jul 2015 16:38:16 +1000 Subject: [PATCH 05/12] touchpad: ignore thumbs when counting clickfingers We may have four fingers on the touchpad - three real ones + a thumb. Count it as three-finger click then. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 3 +++ test/touchpad.c | 39 ++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index b8dcb519..687a613f 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -872,6 +872,9 @@ tp_clickfinger_set_button(struct tp_dispatch *tp) if (t->state != TOUCH_BEGIN && t->state != TOUCH_UPDATE) continue; + if (t->is_thumb) + continue; + if (!first) first = t; else if (!second) diff --git a/test/touchpad.c b/test/touchpad.c index 3e053eea..40d9c33a 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -364,6 +364,7 @@ START_TEST(touchpad_4fg_clickfinger) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + struct libinput_event *event; if (libevdev_get_num_slots(dev->evdev) < 4) return; @@ -387,6 +388,18 @@ START_TEST(touchpad_4fg_clickfinger) libinput_dispatch(li); + litest_wait_for_event(li); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + libinput_event_destroy(event); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + libinput_event_destroy(event); + litest_assert_empty_queue(li); } END_TEST @@ -395,6 +408,7 @@ START_TEST(touchpad_4fg_clickfinger_btntool_2slots) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + struct libinput_event *event; if (libevdev_get_num_slots(dev->evdev) >= 3 || !libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_QUADTAP)) @@ -419,7 +433,17 @@ START_TEST(touchpad_4fg_clickfinger_btntool_2slots) litest_touch_up(dev, 0); litest_touch_up(dev, 1); - libinput_dispatch(li); + litest_wait_for_event(li); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + libinput_event_destroy(event); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + libinput_event_destroy(event); litest_assert_empty_queue(li); } @@ -429,6 +453,7 @@ START_TEST(touchpad_4fg_clickfinger_btntool_3slots) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + struct libinput_event *event; if (libevdev_get_num_slots(dev->evdev) >= 4 || !libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_TRIPLETAP)) @@ -457,6 +482,18 @@ START_TEST(touchpad_4fg_clickfinger_btntool_3slots) libinput_dispatch(li); + litest_wait_for_event(li); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + libinput_event_destroy(event); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + libinput_event_destroy(event); + litest_assert_empty_queue(li); } END_TEST From 7a6026104be70b443ca6bf2d4b54a1e27ba6d7bb Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 17 Jul 2015 16:40:50 +1000 Subject: [PATCH 06/12] touchpad: use the top-most touch for fake finger positions The average human hand has four fingers but only one thumb, i.e. the chance of a fake finger being close to the top-most touch is higher than to whatever the first touch was (which may be a thumb at the bottom of the touchpad). So search for the top-most real touch and copy its position into the fake touches. This also fixes another bug with the previous code - the first slot may not be active but we still used its position for the fake touches. Whether that was really triggerable is questionable though. The test is only run for the T440 touchpad - we know it's big enough to enable thumb detection and that way we don't have to double-check in the how big the touchpad is, etc. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 50 +++++++++++++++++++++++++++----- test/touchpad.c | 64 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 7 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 8629e40c..b4399d2e 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -722,16 +722,58 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time) } +static inline void +tp_position_fake_touches(struct tp_dispatch *tp) +{ + struct tp_touch *t; + struct tp_touch *topmost = NULL; + unsigned int start, i; + + if (tp_fake_finger_count(tp) <= tp->num_slots) + return; + + /* We have at least one fake touch down. Find the top-most real + * touch and copy its coordinates over to to all fake touches. + * This is more reliable than just taking the first touch. + */ + for (i = 0; i < tp->num_slots; i++) { + t = tp_get_touch(tp, i); + if (t->state == TOUCH_END || + t->state == TOUCH_NONE) + continue; + + if (topmost == NULL || t->point.y < topmost->point.y) + topmost = t; + } + + if (!topmost) { + log_bug_libinput(tp_libinput_context(tp), + "Unable to find topmost touch\n"); + return; + } + + start = tp->has_mt ? tp->num_slots : 1; + for (i = start; i < tp->ntouches; i++) { + t = tp_get_touch(tp, i); + if (t->state == TOUCH_NONE) + continue; + + t->point = topmost->point; + if (!t->dirty) + t->dirty = topmost->dirty; + } +} + static void tp_process_state(struct tp_dispatch *tp, uint64_t time) { struct tp_touch *t; - struct tp_touch *first = tp_get_touch(tp, 0); unsigned int i; bool restart_filter = false; tp_process_fake_touches(tp, time); tp_unhover_touches(tp, time); + tp_position_fake_touches(tp); for (i = 0; i < tp->ntouches; i++) { t = tp_get_touch(tp, i); @@ -740,12 +782,6 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down) tp_motion_history_reset(t); - if (i >= tp->num_slots && t->state != TOUCH_NONE) { - t->point = first->point; - if (!t->dirty) - t->dirty = first->dirty; - } - if (!t->dirty) continue; diff --git a/test/touchpad.c b/test/touchpad.c index 40d9c33a..7b8324cc 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -795,6 +795,66 @@ START_TEST(touchpad_area_to_clickfinger_method_while_down) } END_TEST +START_TEST(touchpad_clickfinger_3fg_tool_position) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + enable_clickfinger(dev); + litest_drain_events(li); + + /* one in thumb area, one in normal area. spread is wide so the two + * real fingers don't count together. we expect a 2-finger click */ + litest_touch_down(dev, 0, 5, 99); + litest_touch_down(dev, 1, 90, 15); + 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); + + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); +} +END_TEST + +START_TEST(touchpad_clickfinger_4fg_tool_position) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + enable_clickfinger(dev); + litest_drain_events(li); + + litest_touch_down(dev, 0, 5, 99); + litest_touch_down(dev, 1, 90, 15); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0); + litest_event(dev, EV_KEY, BTN_TOOL_QUADTAP, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); +} +END_TEST + START_TEST(touchpad_btn_left) { struct litest_device *dev = litest_current_device(); @@ -4348,6 +4408,10 @@ litest_setup_tests(void) litest_add("touchpad:clickfinger", touchpad_area_to_clickfinger_method, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_area_to_clickfinger_method_while_down, LITEST_CLICKPAD, LITEST_ANY); + /* run those two for the T440 one only so we don't have to worry + * about small touchpads messing with thumb detection expectations */ + litest_add_for_device("touchpad:clickfinger", touchpad_clickfinger_3fg_tool_position, LITEST_SYNAPTICS_TOPBUTTONPAD); + litest_add_for_device("touchpad:clickfinger", touchpad_clickfinger_4fg_tool_position, LITEST_SYNAPTICS_TOPBUTTONPAD); litest_add("touchpad:click", touchpad_click_defaults_clickfinger, LITEST_APPLE_CLICKPAD, LITEST_ANY); litest_add("touchpad:click", touchpad_click_defaults_btnarea, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); From 7e9ef68dd66168903cc8a379c8cd412a1116001d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Jul 2015 10:27:29 +1000 Subject: [PATCH 07/12] touchpad: scale thumb pressure threshold with the resolution On touchpads with a higher resolution we also see higher pressure values. Scale accordingly, but use the T440s as reference and don't go below that device's threshold. A false positive is worse than a false negative when it comes to thumb detection. https://bugs.freedesktop.org/show_bug.cgi?id=91362 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 13 ++++++++++--- test/touchpad.c | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index b4399d2e..c7f55c38 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1534,6 +1534,8 @@ tp_init_thumb(struct tp_dispatch *tp) struct evdev_device *device = tp->device; const struct input_absinfo *abs; double w = 0.0, h = 0.0; + int xres, yres; + double threshold; if (!tp->buttons.is_clickpad) return 0; @@ -1552,12 +1554,17 @@ tp_init_thumb(struct tp_dispatch *tp) if (h < 50) return 0; - /* The touchpads we looked at so far have a clear thumb threshold of - * ~100, you don't reach that with a normal finger interaction. + /* Our reference touchpad is the T440s with 42x42 resolution. + * Higher-res touchpads exhibit higher pressure for the same + * interaction. On the T440s, the threshold value is 100, you don't + * reach that with a normal finger interaction. * Note: "thumb" means massive touch that should not interact, not * "using the tip of my thumb for a pinch gestures". */ - tp->thumb.threshold = 100; + xres = tp->device->abs.absinfo_x->resolution; + yres = tp->device->abs.absinfo_y->resolution; + threshold = 100.0 * hypot(xres, yres)/hypot(42, 42); + tp->thumb.threshold = max(100, threshold); tp->thumb.detect_thumbs = true; return 0; diff --git a/test/touchpad.c b/test/touchpad.c index 7b8324cc..27485ae7 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4017,7 +4017,7 @@ START_TEST(touchpad_thumb_begin_no_motion) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -4041,7 +4041,7 @@ START_TEST(touchpad_thumb_update_no_motion) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -4071,7 +4071,7 @@ START_TEST(touchpad_thumb_clickfinger) struct libinput_event *event; struct libinput_event_pointer *ptrev; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -4128,7 +4128,7 @@ START_TEST(touchpad_thumb_btnarea) struct libinput_event *event; struct libinput_event_pointer *ptrev; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -4164,7 +4164,7 @@ START_TEST(touchpad_thumb_edgescroll) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -4195,7 +4195,7 @@ START_TEST(touchpad_thumb_tap_begin) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -4225,7 +4225,7 @@ START_TEST(touchpad_thumb_tap_touch) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -4255,7 +4255,7 @@ START_TEST(touchpad_thumb_tap_hold) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -4286,7 +4286,7 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -4333,7 +4333,7 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg_tap) struct libinput_event *event; struct libinput_event_pointer *ptrev; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; From 689632cd0a664d338460883025ab4c7cfe45175a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 17 Jul 2015 11:17:22 +1000 Subject: [PATCH 08/12] touchpad: only try thumb detection in the lowest 15/8mm That's the most likely area it will be resting in, if it's sitting anywhere above that it's likely part of an interaction. A thumb in the lowest 15mm needs to trigger the pressure threshold before it's labelled a thumb. A thumb in the lowest 8mm is considered a thumb if it remains there for 300ms. Regardless of the pressure, since we can't reliably get pressure here. If a thumb moves out of the area, or starts outside of that area it is never a thumb. If edge scrolling is enabled, the 8mm threshold is ineffective since we'll have normal interaction in that zone for horizontal scrolling. The thumb tests now require all touchpads to be switched to clickfinger, if we test for thumb detection on the bottom of the pad we won't get expected motion events due to the software button area. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- doc/Makefile.am | 1 + doc/palm-detection.dox | 28 ++++++++ doc/svg/thumb-detection.svg | 116 ++++++++++++++++++++++++++++++++ src/evdev-mt-touchpad-buttons.c | 5 +- src/evdev-mt-touchpad-tap.c | 4 +- src/evdev-mt-touchpad.c | 66 ++++++++++++++---- src/evdev-mt-touchpad.h | 14 +++- test/touchpad.c | 59 ++++++++-------- 8 files changed, 249 insertions(+), 44 deletions(-) create mode 100644 doc/svg/thumb-detection.svg diff --git a/doc/Makefile.am b/doc/Makefile.am index 95a96e45..ddda230b 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -39,6 +39,7 @@ diagram_files = \ $(srcdir)/svg/pinch-gestures.svg \ $(srcdir)/svg/swipe-gestures.svg \ $(srcdir)/svg/tap-n-drag.svg \ + $(srcdir)/svg/thumb-detection.svg \ $(srcdir)/svg/top-software-buttons.svg \ $(srcdir)/svg/touchscreen-gestures.svg \ $(srcdir)/svg/twofinger-scrolling.svg diff --git a/doc/palm-detection.dox b/doc/palm-detection.dox index d787455e..1a5e6572 100644 --- a/doc/palm-detection.dox +++ b/doc/palm-detection.dox @@ -80,4 +80,32 @@ Notable behaviors of libinput's disable-while-typing feature: - Physical buttons work even while the touchpad is disabled. This includes software-emulated buttons. +@section thumb-detection Thumb detection + +Many users rest their thumb on the touchpad while using the index finger to +move the finger around. For clicks, often the thumb is used rather than the +finger. The thumb should otherwise be ignored as a touch, i.e. it should not +count towards @ref clickfinger and it should not cause a single-finger +movement to trigger @ref twofinger_scrolling. + +libinput uses two triggers for thumb detection: pressure and +location. A touch exceeding a pressure threshold is considered a thumb if it +is within the thumb detection zone. + +@note "Pressure" on touchpads is synonymous with "contact area", a large +touch surface area has a higher pressure and thus hints at a thumb or palm +touching the surface. + +Pressure readings are unreliable at the far bottom of the touchpad as a +thumb hanging mostly off the touchpad will have a small surface area. +libinput has a definitive thumb zone where any touch is considered a resting +thumb. + +@image html thumb-detection.svg + +The picture above shows the two detection areas. In the larger (light red) +area, a touch is labelled as thumb when it exceeds a device-specific +pressure threshold. In the lower (dark red) area, a touch is labelled as +thumb if it remains in that area for a time without moving outside. + */ diff --git a/doc/svg/thumb-detection.svg b/doc/svg/thumb-detection.svg new file mode 100644 index 00000000..bc746981 --- /dev/null +++ b/doc/svg/thumb-detection.svg @@ -0,0 +1,116 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 687a613f..77abc55f 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -810,7 +810,8 @@ tp_check_clickfinger_distance(struct tp_dispatch *tp, if (!t1 || !t2) return 0; - if (t1->is_thumb || t2->is_thumb) + if (t1->thumb.state == THUMB_STATE_YES || + t2->thumb.state == THUMB_STATE_YES) return 0; x = abs(t1->point.x - t2->point.x); @@ -872,7 +873,7 @@ tp_clickfinger_set_button(struct tp_dispatch *tp) if (t->state != TOUCH_BEGIN && t->state != TOUCH_UPDATE) continue; - if (t->is_thumb) + if (t->thumb.state == THUMB_STATE_YES) continue; if (!first) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 1354e899..b2977545 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -740,7 +740,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) /* The simple version: if a touch is a thumb on * begin we ignore it. All other thumb touches * follow the normal tap state for now */ - if (t->is_thumb) { + if (t->thumb.state == THUMB_STATE_YES) { t->tap.is_thumb = true; continue; } @@ -772,7 +772,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time); } else if (tp->tap.state != TAP_STATE_IDLE && - t->is_thumb && + t->thumb.state == THUMB_STATE_YES && !t->tap.is_thumb) { tp_tap_handle_event(tp, t, TAP_EVENT_THUMB, time); } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index c7f55c38..c06b5177 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -208,7 +208,8 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) t->millis = time; tp->nfingers_down++; t->palm.time = time; - t->is_thumb = false; + t->thumb.state = THUMB_STATE_MAYBE; + t->thumb.first_touch_time = time; t->tap.is_thumb = false; assert(tp->nfingers_down >= 1); } @@ -462,7 +463,7 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t) return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) && t->palm.state == PALM_NONE && !t->pinned.is_pinned && - !t->is_thumb && + t->thumb.state != THUMB_STATE_YES && tp_button_touch_active(tp, t) && tp_edge_scroll_touch_active(tp, t); } @@ -604,20 +605,47 @@ out: t->palm.state == PALM_TYPING ? "typing" : "trackpoint"); } -static void -tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t) +static inline const char* +thumb_state_to_str(enum tp_thumb_state state) { - /* once a thumb, always a thumb */ - if (!tp->thumb.detect_thumbs || t->is_thumb) + switch(state){ + CASE_RETURN_STRING(THUMB_STATE_NO); + CASE_RETURN_STRING(THUMB_STATE_YES); + CASE_RETURN_STRING(THUMB_STATE_MAYBE); + } + + return NULL; +} + +static void +tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) +{ + enum tp_thumb_state state = t->thumb.state; + + /* once a thumb, always a thumb, once ruled out always ruled out */ + if (!tp->thumb.detect_thumbs || + t->thumb.state != THUMB_STATE_MAYBE) return; + if (t->point.y < tp->thumb.upper_thumb_line) { + /* if a potential thumb is above the line, it won't ever + * label as thumb */ + t->thumb.state = THUMB_STATE_NO; + goto out; + } + /* Note: a thumb at the edge of the touchpad won't trigger the - * threshold, the surface area is usually too small. + * threshold, the surface area is usually too small. So we have a + * two-stage detection: pressure and time within the area. + * A finger that remains at the very bottom of the touchpad becomes + * a thumb. */ - if (t->pressure < tp->thumb.threshold) - return; - - t->is_thumb = true; + if (t->pressure > tp->thumb.threshold) + t->thumb.state = THUMB_STATE_YES; + else if (t->point.y > tp->thumb.lower_thumb_line && + tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE && + t->thumb.first_touch_time + 300 < time) + t->thumb.state = THUMB_STATE_YES; /* now what? we marked it as thumb, so: * @@ -629,6 +657,12 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t) * - tapping: honour thumb on begin, ignore it otherwise for now, * this gets a tad complicated otherwise */ +out: + if (t->thumb.state != state) + log_debug(tp_libinput_context(tp), + "thumb state: %s → %s\n", + thumb_state_to_str(state), + thumb_state_to_str(t->thumb.state)); } static void @@ -785,7 +819,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) if (!t->dirty) continue; - tp_thumb_detect(tp, t); + tp_thumb_detect(tp, t, time); tp_palm_detect(tp, t, time); tp_motion_hysteresis(tp, t); @@ -1535,6 +1569,7 @@ tp_init_thumb(struct tp_dispatch *tp) const struct input_absinfo *abs; double w = 0.0, h = 0.0; int xres, yres; + int ymax; double threshold; if (!tp->buttons.is_clickpad) @@ -1567,6 +1602,13 @@ tp_init_thumb(struct tp_dispatch *tp) tp->thumb.threshold = max(100, threshold); tp->thumb.detect_thumbs = true; + /* detect thumbs by pressure in the bottom 15mm, detect thumbs by + * lingering in the bottom 8mm */ + ymax = tp->device->abs.absinfo_y->maximum; + yres = tp->device->abs.absinfo_y->resolution; + tp->thumb.upper_thumb_line = ymax - yres * 15; + tp->thumb.lower_thumb_line = ymax - yres * 8; + return 0; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 61c41660..89801d6d 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -136,12 +136,17 @@ enum tp_gesture_2fg_state { GESTURE_2FG_STATE_PINCH, }; +enum tp_thumb_state { + THUMB_STATE_NO, + THUMB_STATE_YES, + THUMB_STATE_MAYBE, +}; + struct tp_touch { struct tp_dispatch *tp; enum touch_state state; bool has_ended; /* TRACKING_ID == -1 */ bool dirty; - bool is_thumb; struct device_coords point; uint64_t millis; int distance; /* distance == 0 means touch */ @@ -195,6 +200,11 @@ struct tp_touch { struct { struct device_coords initial; } gesture; + + struct { + enum tp_thumb_state state; + uint64_t first_touch_time; + } thumb; }; struct tp_dispatch { @@ -324,6 +334,8 @@ struct tp_dispatch { struct { bool detect_thumbs; int threshold; + int upper_thumb_line; + int lower_thumb_line; } thumb; }; diff --git a/test/touchpad.c b/test/touchpad.c index 27485ae7..329eeb38 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4028,8 +4028,8 @@ START_TEST(touchpad_thumb_begin_no_motion) litest_drain_events(li); - litest_touch_down_extended(dev, 0, 50, 50, axes); - litest_touch_move_to(dev, 0, 50, 50, 80, 50, 10, 0); + litest_touch_down_extended(dev, 0, 50, 99, axes); + litest_touch_move_to(dev, 0, 50, 99, 80, 99, 10, 0); litest_touch_up(dev, 0); litest_assert_empty_queue(li); @@ -4046,18 +4046,19 @@ START_TEST(touchpad_thumb_update_no_motion) }; litest_disable_tap(dev->libinput_device); + enable_clickfinger(dev); if (!has_thumb_detect(dev)) return; litest_drain_events(li); - litest_touch_down(dev, 0, 50, 50); - litest_touch_move_to(dev, 0, 50, 50, 60, 50, 10, 0); + litest_touch_down(dev, 0, 50, 99); + litest_touch_move_to(dev, 0, 50, 99, 60, 99, 10, 0); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); - litest_touch_move_extended(dev, 0, 65, 50, axes); - litest_touch_move_to(dev, 0, 65, 50, 80, 50, 10, 0); + litest_touch_move_extended(dev, 0, 65, 99, axes); + litest_touch_move_to(dev, 0, 65, 99, 80, 99, 10, 0); litest_touch_up(dev, 0); litest_assert_empty_queue(li); @@ -4085,9 +4086,9 @@ START_TEST(touchpad_thumb_clickfinger) litest_drain_events(li); - litest_touch_down(dev, 0, 50, 50); - litest_touch_down(dev, 1, 60, 50); - litest_touch_move_extended(dev, 0, 55, 50, axes); + litest_touch_down(dev, 0, 50, 99); + litest_touch_down(dev, 1, 60, 99); + litest_touch_move_extended(dev, 0, 55, 99, axes); litest_button_click(dev, BTN_LEFT, true); libinput_dispatch(li); @@ -4105,9 +4106,9 @@ START_TEST(touchpad_thumb_clickfinger) litest_drain_events(li); - litest_touch_down(dev, 0, 50, 50); - litest_touch_down(dev, 1, 60, 50); - litest_touch_move_extended(dev, 1, 65, 50, axes); + litest_touch_down(dev, 0, 50, 99); + litest_touch_down(dev, 1, 60, 99); + litest_touch_move_extended(dev, 1, 65, 99, axes); litest_button_click(dev, BTN_LEFT, true); libinput_dispatch(li); @@ -4142,8 +4143,8 @@ START_TEST(touchpad_thumb_btnarea) litest_drain_events(li); - litest_touch_down(dev, 0, 90, 95); - litest_touch_move_extended(dev, 0, 95, 95, axes); + litest_touch_down(dev, 0, 90, 99); + litest_touch_move_extended(dev, 0, 95, 99, axes); litest_button_click(dev, BTN_LEFT, true); /* button areas work as usual with a thumb */ @@ -4203,17 +4204,18 @@ START_TEST(touchpad_thumb_tap_begin) return; litest_enable_tap(dev->libinput_device); + enable_clickfinger(dev); litest_drain_events(li); /* touch down is a thumb */ - litest_touch_down_extended(dev, 0, 50, 50, axes); + litest_touch_down_extended(dev, 0, 50, 99, axes); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_empty_queue(li); /* make sure normal tap still works */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -4233,17 +4235,18 @@ START_TEST(touchpad_thumb_tap_touch) return; litest_enable_tap(dev->libinput_device); + enable_clickfinger(dev); litest_drain_events(li); /* event after touch down is thumb */ litest_touch_down(dev, 0, 50, 50); - litest_touch_move_extended(dev, 0, 51, 50, axes); + litest_touch_move_extended(dev, 0, 51, 99, axes); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_empty_queue(li); /* make sure normal tap still works */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -4263,18 +4266,19 @@ START_TEST(touchpad_thumb_tap_hold) return; litest_enable_tap(dev->libinput_device); + enable_clickfinger(dev); litest_drain_events(li); /* event in state HOLD is thumb */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_timeout_tap(); libinput_dispatch(li); - litest_touch_move_extended(dev, 0, 51, 50, axes); + litest_touch_move_extended(dev, 0, 51, 99, axes); litest_touch_up(dev, 0); litest_assert_empty_queue(li); /* make sure normal tap still works */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -4294,13 +4298,14 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg) return; litest_enable_tap(dev->libinput_device); + enable_clickfinger(dev); litest_drain_events(li); /* event in state HOLD is thumb */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_timeout_tap(); libinput_dispatch(li); - litest_touch_move_extended(dev, 0, 51, 50, axes); + litest_touch_move_extended(dev, 0, 51, 99, axes); litest_assert_empty_queue(li); @@ -4319,7 +4324,7 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg) litest_assert_empty_queue(li); /* make sure normal tap still works */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -4344,10 +4349,10 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg_tap) litest_drain_events(li); /* event in state HOLD is thumb */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_timeout_tap(); libinput_dispatch(li); - litest_touch_move_extended(dev, 0, 51, 50, axes); + litest_touch_move_extended(dev, 0, 51, 99, axes); litest_assert_empty_queue(li); @@ -4377,7 +4382,7 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg_tap) libinput_event_destroy(libinput_event_pointer_get_base_event(ptrev)); /* make sure normal tap still works */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); From 8ba5b9cbf95fdb597fd6df5cd85e715285b11556 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Jul 2015 09:21:13 +1000 Subject: [PATCH 09/12] test: only run 3-slot test for touchpads with three slots Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/touchpad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/touchpad.c b/test/touchpad.c index 329eeb38..875bc580 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -456,6 +456,7 @@ START_TEST(touchpad_4fg_clickfinger_btntool_3slots) struct libinput_event *event; if (libevdev_get_num_slots(dev->evdev) >= 4 || + libevdev_get_num_slots(dev->evdev) < 3 || !libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_TRIPLETAP)) return; From 303565c329733b0639d7e89320adbea5915a5725 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Jul 2015 12:22:33 +1000 Subject: [PATCH 10/12] test: fix default axis value assignment Was assigned to -1 if no custom axes were provided. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/litest.c b/test/litest.c index db93bbe8..5ab95f1f 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1223,6 +1223,9 @@ axis_replacement_value(struct axis_replacement *axes, { struct axis_replacement *axis = axes; + if (!axes) + return false; + while (axis->evcode != -1) { if (axis->evcode == evcode) { *value = axis->value; @@ -1267,9 +1270,6 @@ litest_auto_assign_value(struct litest_device *d, break; default: value = -1; - if (!axes) - break; - if (!axis_replacement_value(axes, ev->code, &value) && d->interface->get_axis_default) d->interface->get_axis_default(d, ev->code, &value); From 2dbf998aa82f8052df1eba1c5b4cfcb24e6521cf Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Jul 2015 12:24:47 +1000 Subject: [PATCH 11/12] touchpad: a pressure change alone needs touch processing, mark as dirty A thumb may not move, but may change pressure so we need to process accordingly. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index c06b5177..0b39bf60 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -315,6 +315,8 @@ tp_process_absolute(struct tp_dispatch *tp, break; case ABS_MT_PRESSURE: t->pressure = e->value; + t->dirty = true; + tp->queued |= TOUCHPAD_EVENT_MOTION; break; } } From 2c78149844166a5943873ad3d234d42119f2efe1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Jul 2015 11:53:22 +1000 Subject: [PATCH 12/12] touchpad: put a movement threshold on thumb detection If a thumb moves around, it's not resting and we should consider it a normal touch. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 16 ++++++++++++++++ src/evdev-mt-touchpad.h | 1 + test/touchpad.c | 31 ++++++++++++++++++++++++++++--- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 0b39bf60..63ce4880 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -636,6 +636,22 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) goto out; } + /* If the thumb moves by more than 7mm, it's not a resting thumb */ + if (t->state == TOUCH_BEGIN) + t->thumb.initial = t->point; + else if (t->state == TOUCH_UPDATE) { + struct device_float_coords delta; + struct normalized_coords normalized; + + delta = device_delta(t->point, t->thumb.initial); + normalized = tp_normalize_delta(tp, delta); + if (normalized_length(normalized) > + TP_MM_TO_DPI_NORMALIZED(7)) { + t->thumb.state = THUMB_STATE_NO; + goto out; + } + } + /* Note: a thumb at the edge of the touchpad won't trigger the * threshold, the surface area is usually too small. So we have a * two-stage detection: pressure and time within the area. diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 89801d6d..0dfc6ac2 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -204,6 +204,7 @@ struct tp_touch { struct { enum tp_thumb_state state; uint64_t first_touch_time; + struct device_coords initial; } thumb; }; diff --git a/test/touchpad.c b/test/touchpad.c index 875bc580..25db43f5 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4054,15 +4054,39 @@ START_TEST(touchpad_thumb_update_no_motion) litest_drain_events(li); + litest_touch_down(dev, 0, 59, 99); + litest_touch_move_extended(dev, 0, 59, 99, axes); + litest_touch_move_to(dev, 0, 60, 99, 80, 99, 10, 0); + litest_touch_up(dev, 0); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_thumb_moving) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 190 }, + { -1, 0 } + }; + + litest_disable_tap(dev->libinput_device); + enable_clickfinger(dev); + + if (!has_thumb_detect(dev)) + return; + + litest_drain_events(li); + litest_touch_down(dev, 0, 50, 99); litest_touch_move_to(dev, 0, 50, 99, 60, 99, 10, 0); - litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); - litest_touch_move_extended(dev, 0, 65, 99, axes); litest_touch_move_to(dev, 0, 65, 99, 80, 99, 10, 0); litest_touch_up(dev, 0); - litest_assert_empty_queue(li); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); } END_TEST @@ -4520,6 +4544,7 @@ litest_setup_tests(void) litest_add("touchpad:thumb", touchpad_thumb_begin_no_motion, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_update_no_motion, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_moving, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_clickfinger, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_btnarea, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_edgescroll, LITEST_CLICKPAD, LITEST_ANY);