From 43b910b1dfcddb73e241e17cd73f5b1e7699fe8d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 18 Jun 2019 18:24:49 +1000 Subject: [PATCH 01/25] touchpad: reduce state debugging output by only logging changed states Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-gestures.c | 9 +++++---- src/evdev-mt-touchpad-tap.c | 13 +++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 1a3518e5..eeb3b4cb 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -642,10 +642,11 @@ tp_gesture_post_gesture(struct tp_dispatch *tp, uint64_t time) tp->gesture.state = tp_gesture_handle_state_pinch(tp, time); - evdev_log_debug(tp->device, - "gesture state: %s → %s\n", - gesture_state_to_str(oldstate), - gesture_state_to_str(tp->gesture.state)); + if (oldstate != tp->gesture.state) + evdev_log_debug(tp->device, + "gesture state: %s → %s\n", + gesture_state_to_str(oldstate), + gesture_state_to_str(tp->gesture.state)); } void diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 5255469e..37853b30 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -912,12 +912,13 @@ tp_tap_handle_event(struct tp_dispatch *tp, if (tp->tap.state == TAP_STATE_IDLE || tp->tap.state == TAP_STATE_DEAD) tp_tap_clear_timer(tp); - evdev_log_debug(tp->device, - "tap: touch %d state %s → %s → %s\n", - t ? (int)t->index : -1, - tap_state_to_str(current), - tap_event_to_str(event), - tap_state_to_str(tp->tap.state)); + if (current != tp->tap.state) + evdev_log_debug(tp->device, + "tap: touch %d state %s → %s → %s\n", + t ? (int)t->index : -1, + tap_state_to_str(current), + tap_event_to_str(event), + tap_state_to_str(tp->tap.state)); } static bool From 7e89e43c74ade30cff7cc013f88b1877188c578d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 18 Jun 2019 10:27:59 +1000 Subject: [PATCH 02/25] touchpad: rename the scroll timeout define, drop the pinch one The previously 'scroll'-named timeout is also used for swipe, so let's rename it. And the pinch one isn't used at all. Extracted from Matt Mayfield's thumb detection patches. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-gestures.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index eeb3b4cb..75a54f88 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -30,8 +30,7 @@ #include "evdev-mt-touchpad.h" #define DEFAULT_GESTURE_SWITCH_TIMEOUT ms2us(100) -#define DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT ms2us(150) -#define DEFAULT_GESTURE_2FG_PINCH_TIMEOUT ms2us(75) +#define DEFAULT_GESTURE_SWIPE_TIMEOUT ms2us(150) static inline const char* gesture_state_to_str(enum tp_gesture_state state) @@ -475,7 +474,7 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) vert_distance = abs(first->point.y - second->point.y); horiz_distance = abs(first->point.x - second->point.x); - if (time > (tp->gesture.initial_time + DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT)) { + if (time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) { /* for two-finger gestures, if the fingers stay unmoving for a * while, assume (slow) scroll */ if (tp->gesture.finger_count == 2) { @@ -500,7 +499,7 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) } } - if (time > (tp->gesture.initial_time + DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT)) { + if (time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) { mm = evdev_convert_xy_to_mm(tp->device, horiz_distance, vert_distance); if (tp->gesture.finger_count == 2 && mm.x > 40 && mm.y > 40) return GESTURE_STATE_PINCH; From 6e27a100b58588e3fc58ae8f6594eb4a1487d867 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 18 Jun 2019 10:16:33 +1000 Subject: [PATCH 03/25] touchpad: add a helper function for checking thumb state No functional changes Extracted from Matt Mayfield's thumb detection patches. Signed-off-by: Peter Hutterer --- meson.build | 1 + src/evdev-mt-touchpad-buttons.c | 5 ++--- src/evdev-mt-touchpad-edge-scroll.c | 3 +-- src/evdev-mt-touchpad-tap.c | 4 ++-- src/evdev-mt-touchpad-thumb.c | 32 +++++++++++++++++++++++++++++ src/evdev-mt-touchpad.c | 2 +- src/evdev-mt-touchpad.h | 3 +++ 7 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 src/evdev-mt-touchpad-thumb.c diff --git a/meson.build b/meson.build index ce0c4fc2..d43828d1 100644 --- a/meson.build +++ b/meson.build @@ -325,6 +325,7 @@ src_libinput = src_libfilter + [ 'src/evdev-mt-touchpad.c', 'src/evdev-mt-touchpad.h', 'src/evdev-mt-touchpad-tap.c', + 'src/evdev-mt-touchpad-thumb.c', 'src/evdev-mt-touchpad-buttons.c', 'src/evdev-mt-touchpad-edge-scroll.c', 'src/evdev-mt-touchpad-gestures.c', diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 11d1d6a5..6e721935 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -1042,8 +1042,7 @@ tp_clickfinger_within_distance(struct tp_dispatch *tp, if (!t1 || !t2) return 0; - if (t1->thumb.state == THUMB_STATE_YES || - t2->thumb.state == THUMB_STATE_YES) + if (tp_thumb_ignored(tp, t1) || tp_thumb_ignored(tp, t2)) return 0; x = abs(t1->point.x - t2->point.x); @@ -1098,7 +1097,7 @@ tp_clickfinger_set_button(struct tp_dispatch *tp) if (t->state != TOUCH_BEGIN && t->state != TOUCH_UPDATE) continue; - if (t->thumb.state == THUMB_STATE_YES) + if (tp_thumb_ignored(tp, t)) continue; if (t->palm.state != PALM_NONE) diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index 497e4bd6..4675b4ff 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -398,8 +398,7 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time) if (!t->dirty) continue; - if (t->palm.state != PALM_NONE || - t->thumb.state == THUMB_STATE_YES) + if (t->palm.state != PALM_NONE || tp_thumb_ignored(tp, t)) continue; /* only scroll with the finger in the previous edge */ diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 37853b30..551c70f2 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -1015,7 +1015,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->thumb.state == THUMB_STATE_YES) { + if (tp_thumb_ignored(tp, t)) { t->tap.is_thumb = true; continue; } @@ -1040,7 +1040,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) } t->tap.state = TAP_TOUCH_STATE_IDLE; } else if (tp->tap.state != TAP_STATE_IDLE && - t->thumb.state == THUMB_STATE_YES) { + tp_thumb_ignored(tp, t)) { tp_tap_handle_event(tp, t, TAP_EVENT_THUMB, time); } else if (tp->tap.state != TAP_STATE_IDLE && tp_tap_exceeds_motion_threshold(tp, t)) { diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c new file mode 100644 index 00000000..b207f02f --- /dev/null +++ b/src/evdev-mt-touchpad-thumb.c @@ -0,0 +1,32 @@ +/* + * Copyright © 2019 Matt Mayfield + * Copyright © 2019 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" +#include "evdev-mt-touchpad.h" + +bool +tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t) +{ + return t->thumb.state == THUMB_STATE_YES; +} diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 6619501d..ca7e74fb 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -774,7 +774,7 @@ tp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t) return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) && t->palm.state == PALM_NONE && !t->pinned.is_pinned && - t->thumb.state != THUMB_STATE_YES && + !tp_thumb_ignored(tp, t) && tp_button_touch_active(tp, t) && tp_edge_scroll_touch_active(tp, t); } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 204fc25a..aac47a78 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -677,4 +677,7 @@ tp_palm_tap_is_palm(const struct tp_dispatch *tp, const struct tp_touch *t); void tp_clickpad_middlebutton_apply_config(struct evdev_device *device); +bool +tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t); + #endif From 7c9ed03c4235197cd38a760e7d207ff35ce517ca Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 18 Jun 2019 10:21:13 +1000 Subject: [PATCH 04/25] touchpad: add a helper function for counting touches for gestures Currently the same as tp_touch_active() but this will change. No functional changes. Extracted from Matt Mayfield's thumb detection patches. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-gestures.c | 6 +++--- src/evdev-mt-touchpad.c | 11 +++++++++++ src/evdev-mt-touchpad.h | 4 ++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 75a54f88..47e4ed85 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -55,7 +55,7 @@ tp_get_touches_delta(struct tp_dispatch *tp, bool average) for (i = 0; i < tp->num_slots; i++) { t = &tp->touches[i]; - if (!tp_touch_active(tp, t)) + if (!tp_touch_active_for_gesture(tp, t)) continue; nactive++; @@ -174,7 +174,7 @@ tp_gesture_get_active_touches(const struct tp_dispatch *tp, memset(touches, 0, count * sizeof(struct tp_touch *)); tp_for_each_touch(tp, t) { - if (tp_touch_active(tp, t)) { + if (tp_touch_active_for_gesture(tp, t)) { touches[n++] = t; if (n == count) return count; @@ -758,7 +758,7 @@ tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time) struct tp_touch *t; tp_for_each_touch(tp, t) { - if (tp_touch_active(tp, t)) + if (tp_touch_active_for_gesture(tp, t)) active_touches++; } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index ca7e74fb..51b7ef71 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -779,6 +779,17 @@ tp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t) tp_edge_scroll_touch_active(tp, t); } +bool +tp_touch_active_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t) +{ + return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) && + t->palm.state == PALM_NONE && + !t->pinned.is_pinned && + !tp_thumb_ignored(tp, t) && + tp_button_touch_active(tp, t) && + tp_edge_scroll_touch_active(tp, t); +} + static inline bool tp_palm_was_in_side_edge(const struct tp_dispatch *tp, const struct tp_touch *t) { diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index aac47a78..9c2d1f44 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -569,6 +569,10 @@ tp_filter_motion_unaccelerated(struct tp_dispatch *tp, bool tp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t); +bool +tp_touch_active_for_gesture(const struct tp_dispatch *tp, + const struct tp_touch *t); + int tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time); From 9e27244e6c3b052824f3b66e81b5c64e02648099 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 18 Jun 2019 10:53:39 +1000 Subject: [PATCH 05/25] touchpad: move tp_init_thumb and tp_thumb_detect to the thumb file Extracted from Matt Mayfield's thumb detection patches. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-thumb.c | 170 ++++++++++++++++++++++++++++++++++ src/evdev-mt-touchpad.c | 168 --------------------------------- src/evdev-mt-touchpad.h | 6 ++ 3 files changed, 176 insertions(+), 168 deletions(-) diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index b207f02f..3c9354c4 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -25,8 +25,178 @@ #include "config.h" #include "evdev-mt-touchpad.h" +#define THUMB_MOVE_TIMEOUT ms2us(300) + bool tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t) { return t->thumb.state == THUMB_STATE_YES; } + +static inline const char* +thumb_state_to_str(enum tp_thumb_state state) +{ + switch(state){ + CASE_RETURN_STRING(THUMB_STATE_NO); + CASE_RETURN_STRING(THUMB_STATE_YES); + CASE_RETURN_STRING(THUMB_STATE_MAYBE); + } + + return NULL; +} + +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; + } + + /* 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 phys_coords mm; + + delta = device_delta(t->point, t->thumb.initial); + mm = tp_phys_delta(tp, delta); + if (length_in_mm(mm) > 7) { + t->thumb.state = THUMB_STATE_NO; + goto out; + } + } + + /* If the finger is below the upper thumb line and we have another + * finger in the same area, neither finger is a thumb (unless we've + * already labeled it as such). + */ + if (t->point.y > tp->thumb.upper_thumb_line && + tp->nfingers_down > 1) { + struct tp_touch *other; + + tp_for_each_touch(tp, other) { + if (other->state != TOUCH_BEGIN && + other->state != TOUCH_UPDATE) + continue; + + if (other->point.y > tp->thumb.upper_thumb_line) { + t->thumb.state = THUMB_STATE_NO; + if (other->thumb.state == THUMB_STATE_MAYBE) + other->thumb.state = THUMB_STATE_NO; + break; + } + } + } + + /* 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. + * A finger that remains at the very bottom of the touchpad becomes + * a thumb. + */ + if (tp->thumb.use_pressure && + t->pressure > tp->thumb.pressure_threshold) { + t->thumb.state = THUMB_STATE_YES; + } else if (tp->thumb.use_size && + (t->major > tp->thumb.size_threshold) && + (t->minor < (tp->thumb.size_threshold * 0.6))) { + 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 + THUMB_MOVE_TIMEOUT < time) { + t->thumb.state = THUMB_STATE_YES; + } + + /* now what? we marked it as thumb, so: + * + * - pointer motion must ignore this touch + * - clickfinger must ignore this touch for finger count + * - software buttons are unaffected + * - edge scrolling unaffected + * - gestures: unaffected + * - tapping: honour thumb on begin, ignore it otherwise for now, + * this gets a tad complicated otherwise + */ +out: + if (t->thumb.state != state) + evdev_log_debug(tp->device, + "thumb state: touch %d, %s → %s\n", + t->index, + thumb_state_to_str(state), + thumb_state_to_str(t->thumb.state)); +} + +void +tp_init_thumb(struct tp_dispatch *tp) +{ + struct evdev_device *device = tp->device; + double w = 0.0, h = 0.0; + struct device_coords edges; + struct phys_coords mm = { 0.0, 0.0 }; + uint32_t threshold; + struct quirks_context *quirks; + struct quirks *q; + + if (!tp->buttons.is_clickpad) + return; + + /* 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; + + tp->thumb.detect_thumbs = true; + tp->thumb.use_pressure = false; + tp->thumb.pressure_threshold = INT_MAX; + + /* detect thumbs by pressure in the bottom 15mm, detect thumbs by + * lingering in the bottom 8mm */ + mm.y = h * 0.85; + edges = evdev_device_mm_to_units(device, &mm); + tp->thumb.upper_thumb_line = edges.y; + + mm.y = h * 0.92; + edges = evdev_device_mm_to_units(device, &mm); + tp->thumb.lower_thumb_line = edges.y; + + quirks = evdev_libinput_context(device)->quirks; + q = quirks_fetch_for_device(quirks, device->udev_device); + + if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE)) { + if (quirks_get_uint32(q, + QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD, + &threshold)) { + tp->thumb.use_pressure = true; + tp->thumb.pressure_threshold = threshold; + } + } + + if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_TOUCH_MAJOR)) { + if (quirks_get_uint32(q, + QUIRK_ATTR_THUMB_SIZE_THRESHOLD, + &threshold)) { + tp->thumb.use_size = true; + tp->thumb.size_threshold = threshold; + } + } + + quirks_unref(q); + + evdev_log_debug(device, + "thumb: enabled thumb detection (area%s%s)\n", + tp->thumb.use_pressure ? ", pressure" : "", + tp->thumb.use_size ? ", size" : ""); +} diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 51b7ef71..929de21c 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -39,7 +39,6 @@ #define DEFAULT_TRACKPOINT_EVENT_TIMEOUT ms2us(40) #define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 ms2us(200) #define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 ms2us(500) -#define THUMB_MOVE_TIMEOUT ms2us(300) #define FAKE_FINGER_OVERFLOW (1 << 7) #define THUMB_IGNORE_SPEED_THRESHOLD 20 /* mm/s */ @@ -1156,110 +1155,6 @@ out: palm_state); } -static inline const char* -thumb_state_to_str(enum tp_thumb_state state) -{ - 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; - } - - /* 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 phys_coords mm; - - delta = device_delta(t->point, t->thumb.initial); - mm = tp_phys_delta(tp, delta); - if (length_in_mm(mm) > 7) { - t->thumb.state = THUMB_STATE_NO; - goto out; - } - } - - /* If the finger is below the upper thumb line and we have another - * finger in the same area, neither finger is a thumb (unless we've - * already labeled it as such). - */ - if (t->point.y > tp->thumb.upper_thumb_line && - tp->nfingers_down > 1) { - struct tp_touch *other; - - tp_for_each_touch(tp, other) { - if (other->state != TOUCH_BEGIN && - other->state != TOUCH_UPDATE) - continue; - - if (other->point.y > tp->thumb.upper_thumb_line) { - t->thumb.state = THUMB_STATE_NO; - if (other->thumb.state == THUMB_STATE_MAYBE) - other->thumb.state = THUMB_STATE_NO; - break; - } - } - } - - /* 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. - * A finger that remains at the very bottom of the touchpad becomes - * a thumb. - */ - if (tp->thumb.use_pressure && - t->pressure > tp->thumb.pressure_threshold) { - t->thumb.state = THUMB_STATE_YES; - } else if (tp->thumb.use_size && - (t->major > tp->thumb.size_threshold) && - (t->minor < (tp->thumb.size_threshold * 0.6))) { - 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 + THUMB_MOVE_TIMEOUT < time) { - t->thumb.state = THUMB_STATE_YES; - } - - /* now what? we marked it as thumb, so: - * - * - pointer motion must ignore this touch - * - clickfinger must ignore this touch for finger count - * - software buttons are unaffected - * - edge scrolling unaffected - * - gestures: unaffected - * - tapping: honour thumb on begin, ignore it otherwise for now, - * this gets a tad complicated otherwise - */ -out: - if (t->thumb.state != state) - evdev_log_debug(tp->device, - "thumb state: touch %d, %s → %s\n", - t->index, - thumb_state_to_str(state), - thumb_state_to_str(t->thumb.state)); -} - static void tp_unhover_pressure(struct tp_dispatch *tp, uint64_t time) { @@ -3452,69 +3347,6 @@ tp_init_sendevents(struct tp_dispatch *tp, tp_keyboard_timeout, tp); } -static void -tp_init_thumb(struct tp_dispatch *tp) -{ - struct evdev_device *device = tp->device; - double w = 0.0, h = 0.0; - struct device_coords edges; - struct phys_coords mm = { 0.0, 0.0 }; - uint32_t threshold; - struct quirks_context *quirks; - struct quirks *q; - - if (!tp->buttons.is_clickpad) - return; - - /* 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; - - tp->thumb.detect_thumbs = true; - tp->thumb.use_pressure = false; - tp->thumb.pressure_threshold = INT_MAX; - - /* detect thumbs by pressure in the bottom 15mm, detect thumbs by - * lingering in the bottom 8mm */ - mm.y = h * 0.85; - edges = evdev_device_mm_to_units(device, &mm); - tp->thumb.upper_thumb_line = edges.y; - - mm.y = h * 0.92; - edges = evdev_device_mm_to_units(device, &mm); - tp->thumb.lower_thumb_line = edges.y; - - quirks = evdev_libinput_context(device)->quirks; - q = quirks_fetch_for_device(quirks, device->udev_device); - - if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE)) { - if (quirks_get_uint32(q, - QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD, - &threshold)) { - tp->thumb.use_pressure = true; - tp->thumb.pressure_threshold = threshold; - } - } - - if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_TOUCH_MAJOR)) { - if (quirks_get_uint32(q, - QUIRK_ATTR_THUMB_SIZE_THRESHOLD, - &threshold)) { - tp->thumb.use_size = true; - tp->thumb.size_threshold = threshold; - } - } - - quirks_unref(q); - - evdev_log_debug(device, - "thumb: enabled thumb detection (area%s%s)\n", - tp->thumb.use_pressure ? ", pressure" : "", - tp->thumb.use_size ? ", size" : ""); -} static bool tp_pass_sanity_check(struct tp_dispatch *tp, diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 9c2d1f44..74006b4e 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -684,4 +684,10 @@ tp_clickpad_middlebutton_apply_config(struct evdev_device *device); bool tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t); +void +tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time); + +void +tp_init_thumb(struct tp_dispatch *tp); + #endif From d232e44c9fefbb3f54c63edb2cc61868725a92e2 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 19 Jun 2019 11:57:45 +1000 Subject: [PATCH 06/25] touchpad: add helper function for setting the thumb state This moves the thumb state logging directly into that helper function too. Extracted from Matt Mayfield's thumb detection patches. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-thumb.c | 46 +++++++++++++++++++++-------------- src/evdev-mt-touchpad.c | 2 +- src/evdev-mt-touchpad.h | 5 ++++ 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index 3c9354c4..b5ce2998 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -45,11 +45,26 @@ thumb_state_to_str(enum tp_thumb_state state) return NULL; } +void +tp_thumb_set_state(struct tp_dispatch *tp, + struct tp_touch *t, + enum tp_thumb_state state) +{ + if (t->thumb.state == state) + return; + + evdev_log_debug(tp->device, + "thumb: touch %d, %s → %s\n", + t->index, + thumb_state_to_str(t->thumb.state), + thumb_state_to_str(state)); + + t->thumb.state = state; +} + 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) @@ -58,8 +73,8 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) 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; + tp_thumb_set_state(tp, t, THUMB_STATE_NO); + return; } /* If the thumb moves by more than 7mm, it's not a resting thumb */ @@ -72,8 +87,8 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) delta = device_delta(t->point, t->thumb.initial); mm = tp_phys_delta(tp, delta); if (length_in_mm(mm) > 7) { - t->thumb.state = THUMB_STATE_NO; - goto out; + tp_thumb_set_state(tp, t, THUMB_STATE_NO); + return; } } @@ -91,9 +106,11 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) continue; if (other->point.y > tp->thumb.upper_thumb_line) { - t->thumb.state = THUMB_STATE_NO; + tp_thumb_set_state(tp, t, THUMB_STATE_NO); if (other->thumb.state == THUMB_STATE_MAYBE) - other->thumb.state = THUMB_STATE_NO; + tp_thumb_set_state(tp, + other, + THUMB_STATE_NO); break; } } @@ -107,15 +124,15 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) */ if (tp->thumb.use_pressure && t->pressure > tp->thumb.pressure_threshold) { - t->thumb.state = THUMB_STATE_YES; + tp_thumb_set_state(tp, t, THUMB_STATE_YES); } else if (tp->thumb.use_size && (t->major > tp->thumb.size_threshold) && (t->minor < (tp->thumb.size_threshold * 0.6))) { - t->thumb.state = THUMB_STATE_YES; + tp_thumb_set_state(tp, t, 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 + THUMB_MOVE_TIMEOUT < time) { - t->thumb.state = THUMB_STATE_YES; + tp_thumb_set_state(tp, t, THUMB_STATE_YES); } /* now what? we marked it as thumb, so: @@ -128,13 +145,6 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) * - tapping: honour thumb on begin, ignore it otherwise for now, * this gets a tad complicated otherwise */ -out: - if (t->thumb.state != state) - evdev_log_debug(tp->device, - "thumb state: touch %d, %s → %s\n", - t->index, - thumb_state_to_str(state), - thumb_state_to_str(t->thumb.state)); } void diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 929de21c..349f730d 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1537,7 +1537,7 @@ tp_detect_thumb_while_moving(struct tp_dispatch *tp) evdev_log_debug(tp->device, "touch %d is speed-based thumb\n", second->index); - second->thumb.state = THUMB_STATE_YES; + tp_thumb_set_state(tp, second, THUMB_STATE_YES); } /** diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 74006b4e..12a6b03f 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -690,4 +690,9 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time); void tp_init_thumb(struct tp_dispatch *tp); +void +tp_thumb_set_state(struct tp_dispatch *tp, + struct tp_touch *t, + enum tp_thumb_state state); + #endif From c2331ae11ebc4ef19186374126d54e3bb95a9e4e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 19 Jun 2019 12:01:09 +1000 Subject: [PATCH 07/25] touchpad: explicitly start with detect_thumbs as false Not needed because we zalloc() anyway, but this makes it grep-able. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-thumb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index b5ce2998..4d9b6f26 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -158,6 +158,8 @@ tp_init_thumb(struct tp_dispatch *tp) struct quirks_context *quirks; struct quirks *q; + tp->thumb.detect_thumbs = false; + if (!tp->buttons.is_clickpad) return; From 117ef6508710b98f89d7072a63f0daca6899495b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 19 Jun 2019 12:04:23 +1000 Subject: [PATCH 08/25] touchpad: add helper function to reset a thumb's state Extracted from Matt Mayfield's thumb detection patches. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-thumb.c | 6 ++++++ src/evdev-mt-touchpad.c | 2 +- src/evdev-mt-touchpad.h | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index 4d9b6f26..bfa5256c 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -62,6 +62,12 @@ tp_thumb_set_state(struct tp_dispatch *tp, t->thumb.state = state; } +void +tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t) +{ + t->thumb.state = THUMB_STATE_MAYBE; +} + void tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 349f730d..e9716381 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -353,7 +353,7 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) t->was_down = true; tp->nfingers_down++; t->palm.time = time; - t->thumb.state = THUMB_STATE_MAYBE; + tp_thumb_reset(tp, t); t->thumb.first_touch_time = time; t->tap.is_thumb = false; t->tap.is_palm = false; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 12a6b03f..61422a5e 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -684,6 +684,9 @@ tp_clickpad_middlebutton_apply_config(struct evdev_device *device); bool tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t); +void +tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t); + void tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time); From 93a754c41cd0c49056250a1133b56b8f1202634a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 19 Jun 2019 12:05:25 +1000 Subject: [PATCH 09/25] touchpad: move the speed-thumb detection code to the thumb helper file Extracted from Matt Mayfield's thumb detection patches. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-thumb.c | 46 +++++++++++++++++++++++++++++++++++ src/evdev-mt-touchpad.c | 46 ----------------------------------- src/evdev-mt-touchpad.h | 3 +++ 3 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index bfa5256c..eb9dc554 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -153,6 +153,52 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) */ } +void +tp_detect_thumb_while_moving(struct tp_dispatch *tp) +{ + struct tp_touch *t; + struct tp_touch *first = NULL, + *second = NULL; + struct device_coords distance; + struct phys_coords mm; + + tp_for_each_touch(tp, t) { + if (t->state == TOUCH_NONE || + t->state == TOUCH_HOVERING) + continue; + + if (t->state != TOUCH_BEGIN) + first = t; + else + second = t; + + if (first && second) + break; + } + + assert(first); + assert(second); + + if (tp->scroll.method == LIBINPUT_CONFIG_SCROLL_2FG) { + /* If the second finger comes down next to the other one, we + * assume this is a scroll motion. + */ + distance.x = abs(first->point.x - second->point.x); + distance.y = abs(first->point.y - second->point.y); + mm = evdev_device_unit_delta_to_mm(tp->device, &distance); + + if (mm.x <= 25 && mm.y <= 15) + return; + } + + /* Finger are too far apart or 2fg scrolling is disabled, mark + * second finger as thumb */ + evdev_log_debug(tp->device, + "touch %d is speed-based thumb\n", + second->index); + tp_thumb_set_state(tp, second, THUMB_STATE_YES); +} + void tp_init_thumb(struct tp_dispatch *tp) { diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index e9716381..a9d9d6a1 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1494,52 +1494,6 @@ tp_detect_jumps(const struct tp_dispatch *tp, return is_jump; } -static void -tp_detect_thumb_while_moving(struct tp_dispatch *tp) -{ - struct tp_touch *t; - struct tp_touch *first = NULL, - *second = NULL; - struct device_coords distance; - struct phys_coords mm; - - tp_for_each_touch(tp, t) { - if (t->state == TOUCH_NONE || - t->state == TOUCH_HOVERING) - continue; - - if (t->state != TOUCH_BEGIN) - first = t; - else - second = t; - - if (first && second) - break; - } - - assert(first); - assert(second); - - if (tp->scroll.method == LIBINPUT_CONFIG_SCROLL_2FG) { - /* If the second finger comes down next to the other one, we - * assume this is a scroll motion. - */ - distance.x = abs(first->point.x - second->point.x); - distance.y = abs(first->point.y - second->point.y); - mm = evdev_device_unit_delta_to_mm(tp->device, &distance); - - if (mm.x <= 25 && mm.y <= 15) - return; - } - - /* Finger are too far apart or 2fg scrolling is disabled, mark - * second finger as thumb */ - evdev_log_debug(tp->device, - "touch %d is speed-based thumb\n", - second->index); - tp_thumb_set_state(tp, second, THUMB_STATE_YES); -} - /** * Rewrite the motion history so that previous points' timestamps are the * current point's timestamp minus whatever MSC_TIMESTAMP gives us. diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 61422a5e..24447402 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -690,6 +690,9 @@ tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t); void tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time); +void +tp_detect_thumb_while_moving(struct tp_dispatch *tp); + void tp_init_thumb(struct tp_dispatch *tp); From 47d8f05d3bf7d3281affc79d7255983ff783e961 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 20 Jun 2019 10:37:37 +1000 Subject: [PATCH 10/25] touchpad: extract some bits of thumb detection into helper functions No functional changes Extracted from Matt Mayfield's thumb detection patches. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-thumb.c | 46 ++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index eb9dc554..89d715e4 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -68,6 +68,39 @@ tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t) t->thumb.state = THUMB_STATE_MAYBE; } +static bool +tp_thumb_in_exclusion_area(const struct tp_dispatch *tp, + const struct tp_touch *t, + uint64_t time) +{ + return (t->point.y > tp->thumb.lower_thumb_line && + tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE && + t->thumb.first_touch_time + THUMB_MOVE_TIMEOUT < time); + +} + +static bool +tp_thumb_detect_pressure_size(const struct tp_dispatch *tp, + const struct tp_touch *t, + uint64_t time) +{ + bool is_thumb = false; + + if (tp->thumb.use_pressure && + t->pressure > tp->thumb.pressure_threshold && + tp_thumb_in_exclusion_area(tp, t, time)) { + is_thumb = true; + } + + if (tp->thumb.use_size && + (t->major > tp->thumb.size_threshold) && + (t->minor < (tp->thumb.size_threshold * 0.6))) { + is_thumb = true; + } + + return is_thumb; +} + void tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { @@ -128,18 +161,9 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) * A finger that remains at the very bottom of the touchpad becomes * a thumb. */ - if (tp->thumb.use_pressure && - t->pressure > tp->thumb.pressure_threshold) { + if (tp_thumb_detect_pressure_size(tp, t, time) || + tp_thumb_in_exclusion_area(tp, t, time)) tp_thumb_set_state(tp, t, THUMB_STATE_YES); - } else if (tp->thumb.use_size && - (t->major > tp->thumb.size_threshold) && - (t->minor < (tp->thumb.size_threshold * 0.6))) { - tp_thumb_set_state(tp, t, 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 + THUMB_MOVE_TIMEOUT < time) { - tp_thumb_set_state(tp, t, THUMB_STATE_YES); - } /* now what? we marked it as thumb, so: * From 7fbfb14419143d75466a68408942d9d460860d04 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 20 Jun 2019 10:56:17 +1000 Subject: [PATCH 11/25] touchpad: rename the thumb detection methods No functional changes, just prep work for a later patch where the thumbs will dynamically update their state (instead of just using yes/no/maybe). Extracted from Matt Mayfield's thumb detection patches. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-thumb.c | 6 ++++-- src/evdev-mt-touchpad.c | 4 ++-- src/evdev-mt-touchpad.h | 7 ++++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index 89d715e4..c45a0670 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -102,7 +102,9 @@ tp_thumb_detect_pressure_size(const struct tp_dispatch *tp, } void -tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) +tp_thumb_update_touch(struct tp_dispatch *tp, + struct tp_touch *t, + uint64_t time) { /* once a thumb, always a thumb, once ruled out always ruled out */ if (!tp->thumb.detect_thumbs || @@ -178,7 +180,7 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) } void -tp_detect_thumb_while_moving(struct tp_dispatch *tp) +tp_thumb_update_multifinger(struct tp_dispatch *tp) { struct tp_touch *t; struct tp_touch *first = NULL, diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index a9d9d6a1..9b7d30ca 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1679,7 +1679,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) tp_motion_history_reset(t); } - tp_thumb_detect(tp, t, time); + tp_thumb_update_touch(tp, t, time); tp_palm_detect(tp, t, time); tp_detect_wobbling(tp, t, time); tp_motion_hysteresis(tp, t); @@ -1722,7 +1722,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) if (have_new_touch && tp->nfingers_down == 2 && speed_exceeded_count > 5) - tp_detect_thumb_while_moving(tp); + tp_thumb_update_multifinger(tp); if (restart_filter) filter_restart(tp->device->pointer.filter, tp, time); diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 24447402..8845c8cd 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -688,11 +688,16 @@ void tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t); void -tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time); +tp_thumb_update_touch(struct tp_dispatch *tp, + struct tp_touch *t, + uint64_t time); void tp_detect_thumb_while_moving(struct tp_dispatch *tp); +void +tp_thumb_update_multifinger(struct tp_dispatch *tp); + void tp_init_thumb(struct tp_dispatch *tp); From 7b9a6a94a37cc659d3888c4de25deaca9bd4bed6 Mon Sep 17 00:00:00 2001 From: Matt Mayfield Date: Sat, 18 Aug 2018 01:10:34 -0500 Subject: [PATCH 12/25] touchpad: basic thumb detection within gestures When a touchpad has thumb detection enabled, avoid false-positive gestures involving a resting thumb by using two thresholds: inner and outer. While both touches remain inside their inner thresholds, remain in UNKNOWN state to allow for accurate gesture detection even with no timeout. If both touches move outside their inner thresholds, start a pinch or swipe/scroll gesture according to direction, as usual. If one touch moves outside its outer threshold while the other has not yet exceeded its inner threshold, and thumb detection is enabled, then if one touch is >20mm lower, mark it as a thumb and cancel the gesture. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-gestures.c | 127 +++++++++++++++++++------------ test/test-gestures.c | 56 +------------- 2 files changed, 78 insertions(+), 105 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 47e4ed85..d4133b52 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -195,21 +195,14 @@ tp_gesture_get_active_touches(const struct tp_dispatch *tp, } static uint32_t -tp_gesture_get_direction(struct tp_dispatch *tp, struct tp_touch *touch, - unsigned int nfingers) +tp_gesture_get_direction(struct tp_dispatch *tp, struct tp_touch *touch) { struct phys_coords mm; struct device_float_coords delta; - double move_threshold = 1.0; /* mm */ - - move_threshold *= (nfingers - 1); delta = device_delta(touch->point, touch->gesture.initial); mm = tp_phys_delta(tp, delta); - if (length_in_mm(mm) < move_threshold) - return UNDEFINED_DIRECTION; - return phys_get_direction(mm); } @@ -462,57 +455,88 @@ tp_gesture_init_pinch(struct tp_dispatch *tp) tp->gesture.prev_scale = 1.0; } +static struct phys_coords +tp_gesture_mm_moved(struct tp_dispatch *tp, struct tp_touch *t) +{ + struct device_coords delta; + + delta.x = abs(t->point.x - t->gesture.initial.x); + delta.y = abs(t->point.y - t->gesture.initial.y); + + return evdev_device_unit_delta_to_mm(tp->device, &delta); +} + static enum tp_gesture_state tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) { struct tp_touch *first = tp->gesture.touches[0], *second = tp->gesture.touches[1]; uint32_t dir1, dir2; - struct phys_coords mm; - int vert_distance, horiz_distance; + struct device_coords delta; + struct phys_coords first_moved, second_moved, distance_mm; + double first_mm, second_mm; /* movement since gesture start in mm */ + double inner = 1.5; /* inner threshold in mm - count this touch */ + double outer = 4.0; /* outer threshold in mm - ignore other touch */ - vert_distance = abs(first->point.y - second->point.y); - horiz_distance = abs(first->point.x - second->point.x); + /* Need more margin for error when there are more fingers */ + outer += 2.0 * (tp->gesture.finger_count - 2); + inner += 0.5 * (tp->gesture.finger_count - 2); - if (time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) { - /* for two-finger gestures, if the fingers stay unmoving for a - * while, assume (slow) scroll */ - if (tp->gesture.finger_count == 2) { - tp_gesture_set_scroll_buildup(tp); - return GESTURE_STATE_SCROLL; - /* more fingers than slots, don't bother with pinch, always - * assume swipe */ - } else if (tp->gesture.finger_count > tp->num_slots) { - return GESTURE_STATE_SWIPE; - } + first_moved = tp_gesture_mm_moved(tp, first); + first_mm = hypot(first_moved.x, first_moved.y); - /* for 3+ finger gestures, check if one finger is > 20mm - below the others */ - mm = evdev_convert_xy_to_mm(tp->device, - horiz_distance, - vert_distance); - if (mm.y > 20 && tp->gesture.enabled) { - tp_gesture_init_pinch(tp); - return GESTURE_STATE_PINCH; - } else { - return GESTURE_STATE_SWIPE; - } - } + second_moved = tp_gesture_mm_moved(tp, second); + second_mm = hypot(second_moved.x, second_moved.y); - if (time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) { - mm = evdev_convert_xy_to_mm(tp->device, horiz_distance, vert_distance); - if (tp->gesture.finger_count == 2 && mm.x > 40 && mm.y > 40) - return GESTURE_STATE_PINCH; - } + delta.x = abs(first->point.x - second->point.x); + delta.y = abs(first->point.y - second->point.y); + distance_mm = evdev_device_unit_delta_to_mm(tp->device, &delta); - /* Else wait for both fingers to have moved */ - dir1 = tp_gesture_get_direction(tp, first, tp->gesture.finger_count); - dir2 = tp_gesture_get_direction(tp, second, tp->gesture.finger_count); - if (dir1 == UNDEFINED_DIRECTION || dir2 == UNDEFINED_DIRECTION) + /* If both touches moved less than a mm, we cannot decide yet */ + if (first_mm < 1 && second_mm < 1) return GESTURE_STATE_UNKNOWN; - /* If both touches are moving in the same direction assume - * scroll or swipe */ + /* If one touch exceeds the outer threshold while the other has not + * yet passed the inner threshold, this is not a valid gesture. + * If thumb detection is enabled, and one of the touches is >20mm + * below the other, cancel the gesture and mark the thumb. + * + * Give the thumb a larger effective outer threshold for more reliable + * detection of pinch vs. resting thumb. + */ + if (tp->thumb.detect_thumbs && distance_mm.y > 20.0 && + time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) { + if ((first->point.y >= second->point.y) && + ((first_mm >= outer * 2.0) || + (second_mm >= outer))) { + tp_gesture_cancel(tp, time); + first->thumb.state = THUMB_STATE_YES; + return GESTURE_STATE_NONE; + } + if ((second->point.y >= first->point.y) && + ((second_mm >= outer * 2.0) || + (first_mm >= outer))) { + tp_gesture_cancel(tp, time); + second->thumb.state = THUMB_STATE_YES; + return GESTURE_STATE_NONE; + } + } + + /* If either touch is still inside the inner threshold, we can't + * tell what kind of gesture this is. + */ + if ((first_mm < inner) || (second_mm < inner)) + return GESTURE_STATE_UNKNOWN; + + /* Both touches have exceeded the inner threshold; get their directions + * gesture. G directions so we know if it's a pinch or swipe/scroll. + */ + dir1 = tp_gesture_get_direction(tp, first); + dir2 = tp_gesture_get_direction(tp, second); + + /* If we can't accurately detect pinches, or if the touches are moving + * the same way, this is a scroll or swipe. + */ if (tp->gesture.finger_count > tp->num_slots || tp_gesture_same_directions(dir1, dir2)) { if (tp->gesture.finger_count == 2) { @@ -521,12 +545,11 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) } else if (tp->gesture.enabled) { return GESTURE_STATE_SWIPE; } - } else { - tp_gesture_init_pinch(tp); - return GESTURE_STATE_PINCH; } - return GESTURE_STATE_UNKNOWN; + /* If the touches are moving away from each other, this is a pinch */ + tp_gesture_init_pinch(tp); + return GESTURE_STATE_PINCH; } static enum tp_gesture_state @@ -772,6 +795,10 @@ tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time) } else if (!tp->gesture.started) { tp->gesture.finger_count = active_touches; tp->gesture.finger_count_pending = 0; + /* If in UNKNOWN state, go back to NONE to + * re-evaluate leftmost and rightmost touches + */ + tp->gesture.state = GESTURE_STATE_NONE; /* Else debounce finger changes */ } else if (active_touches != tp->gesture.finger_count_pending) { tp->gesture.finger_count_pending = active_touches; diff --git a/test/test-gestures.c b/test/test-gestures.c index 4a9596d3..76f80f37 100644 --- a/test/test-gestures.c +++ b/test/test-gestures.c @@ -489,58 +489,6 @@ START_TEST(gestures_swipe_4fg_btntool) } END_TEST -START_TEST(gestures_pinch_vertical_position) -{ - struct litest_device *dev = litest_current_device(); - struct libinput *li = dev->libinput; - struct libinput_event *event; - int nfingers = _i; /* ranged test */ - - if (libevdev_get_num_slots(dev->evdev) < nfingers || - !libinput_device_has_capability(dev->libinput_device, - LIBINPUT_DEVICE_CAP_GESTURE)) - return; - - litest_disable_tap(dev->libinput_device); - litest_drain_events(li); - - litest_touch_down(dev, 0, 40, 30); - litest_touch_down(dev, 1, 50, 70); - litest_touch_down(dev, 2, 60, 70); - if (nfingers > 3) - litest_touch_down(dev, 3, 70, 70); - libinput_dispatch(li); - litest_timeout_gesture_scroll(); - libinput_dispatch(li); - - /* This is actually a small swipe gesture, all three fingers moving - * down but we're checking for the code that triggers based on - * finger position. */ - litest_touch_move(dev, 0, 40, 30.5); - litest_touch_move(dev, 1, 50, 70.5); - litest_touch_move(dev, 2, 60, 70.5); - if (nfingers > 3) - litest_touch_move(dev, 3, 70, 70.5); - libinput_dispatch(li); - - event = libinput_get_event(li); - litest_is_gesture_event(event, - LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, - nfingers); - libinput_event_destroy(event); - - litest_touch_move_to(dev, 0, 40, 30.5, 40, 36, 5); - litest_touch_move_to(dev, 1, 50, 70.5, 50, 76, 5); - litest_touch_move_to(dev, 2, 60, 70.5, 60, 76, 5); - if (nfingers > 3) - litest_touch_move_to(dev, 3, 70, 70.5, 60, 76, 5); - libinput_dispatch(li); - - litest_assert_only_typed_events(li, - LIBINPUT_EVENT_GESTURE_PINCH_UPDATE); -} -END_TEST - START_TEST(gestures_pinch) { struct litest_device *dev = litest_current_device(); @@ -784,7 +732,7 @@ START_TEST(gestures_pinch_4fg) litest_touch_down(dev, 3, 52 - dir_x, 52 - dir_y); libinput_dispatch(li); - for (i = 0; i < 8; i++) { + for (i = 0; i < 7; i++) { litest_push_event_frame(dev); if (dir_x > 0.0) dir_x -= 2; @@ -1045,7 +993,6 @@ END_TEST TEST_COLLECTION(gestures) { struct range cardinals = { N, N + NCARDINALS }; - struct range fingers = { 3, 5 }; litest_add("gestures:cap", gestures_cap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("gestures:cap", gestures_nocap, LITEST_ANY, LITEST_TOUCHPAD); @@ -1058,7 +1005,6 @@ TEST_COLLECTION(gestures) litest_add_ranged("gestures:pinch", gestures_pinch_3fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals); litest_add_ranged("gestures:pinch", gestures_pinch_4fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals); litest_add_ranged("gestures:pinch", gestures_spread, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals); - litest_add_ranged("gestures:pinch", gestures_pinch_vertical_position, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &fingers); litest_add("gestures:swipe", gestures_3fg_buttonarea_scroll, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH); litest_add("gestures:swipe", gestures_3fg_buttonarea_scroll_btntool, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH); From e97f0549173a07aa6ebd19ca19f842b8d3ac994c Mon Sep 17 00:00:00 2001 From: Matt Mayfield Date: Tue, 21 Aug 2018 21:12:33 -0500 Subject: [PATCH 13/25] gestures: improve scroll responsiveness for vertically aligned touches Put some basic location checks in, if the fingers are next to each other and vertically close, assume scroll over swipe. Signed-off-by: Peter Hutterer : --- src/evdev-mt-touchpad-gestures.c | 11 +++++++++++ test/test-gestures.c | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index d4133b52..12e00a3f 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -496,6 +496,17 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) if (first_mm < 1 && second_mm < 1) return GESTURE_STATE_UNKNOWN; + /* If both touches are within 7mm vertically and 40mm horizontally, + * assume scroll/swipe */ + if (distance_mm.x < 40 && distance_mm.y < 7.0) { + if (tp->gesture.finger_count == 2) { + tp_gesture_set_scroll_buildup(tp); + return GESTURE_STATE_SCROLL; + } else if (tp->gesture.enabled) { + return GESTURE_STATE_SWIPE; + } + } + /* If one touch exceeds the outer threshold while the other has not * yet passed the inner threshold, this is not a valid gesture. * If thumb detection is enabled, and one of the touches is >20mm diff --git a/test/test-gestures.c b/test/test-gestures.c index 76f80f37..ae2da795 100644 --- a/test/test-gestures.c +++ b/test/test-gestures.c @@ -517,6 +517,17 @@ START_TEST(gestures_pinch) LIBINPUT_DEVICE_CAP_GESTURE)) return; + /* If the device is too small to provide a finger spread wide enough + * to avoid the scroll bias, skip the test */ + if (cardinal == E || cardinal == W) { + double w = 0, h = 0; + libinput_device_get_size(dev->libinput_device, &w, &h); + /* 0.6 because the code below gives us points like 20/y and + * 80/y. 45 because the threshold in the code is 40mm */ + if (w * 0.6 < 45) + return; + } + dir_x = cardinals[cardinal][0]; dir_y = cardinals[cardinal][1]; @@ -834,6 +845,17 @@ START_TEST(gestures_spread) LIBINPUT_DEVICE_CAP_GESTURE)) return; + /* If the device is too small to provide a finger spread wide enough + * to avoid the scroll bias, skip the test */ + if (cardinal == E || cardinal == W) { + double w = 0, h = 0; + libinput_device_get_size(dev->libinput_device, &w, &h); + /* 0.6 because the code below gives us points like 20/y and + * 80/y. 45 because the threshold in the code is 40mm */ + if (w * 0.6 < 45) + return; + } + dir_x = cardinals[cardinal][0]; dir_y = cardinals[cardinal][1]; From 93ab2f964a29965b36dd0467ab8ffb44e75b6637 Mon Sep 17 00:00:00 2001 From: Matt Mayfield Date: Fri, 24 Aug 2018 23:17:03 -0500 Subject: [PATCH 14/25] gestures: improvements to pinch detection Only bias towards scrolling if the fingers are in the position past the timeout. --- src/evdev-mt-touchpad-gestures.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 12e00a3f..e13fb3da 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -31,6 +31,7 @@ #define DEFAULT_GESTURE_SWITCH_TIMEOUT ms2us(100) #define DEFAULT_GESTURE_SWIPE_TIMEOUT ms2us(150) +#define DEFAULT_GESTURE_PINCH_TIMEOUT ms2us(150) static inline const char* gesture_state_to_str(enum tp_gesture_state state) @@ -496,9 +497,10 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) if (first_mm < 1 && second_mm < 1) return GESTURE_STATE_UNKNOWN; - /* If both touches are within 7mm vertically and 40mm horizontally, - * assume scroll/swipe */ - if (distance_mm.x < 40 && distance_mm.y < 7.0) { + /* If both touches are within 7mm vertically and 40mm horizontally + * past the timeout, assume scroll/swipe */ + if (distance_mm.x < 40.0 && distance_mm.y < 7.0 && + time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) { if (tp->gesture.finger_count == 2) { tp_gesture_set_scroll_buildup(tp); return GESTURE_STATE_SCROLL; @@ -539,8 +541,9 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) if ((first_mm < inner) || (second_mm < inner)) return GESTURE_STATE_UNKNOWN; - /* Both touches have exceeded the inner threshold; get their directions - * gesture. G directions so we know if it's a pinch or swipe/scroll. + /* Both touches have exceeded the inner threshold, so we have a valid + * gesture. Update gesture initial time and get directions so we know + * if it's a pinch or swipe/scroll. */ dir1 = tp_gesture_get_direction(tp, first); dir2 = tp_gesture_get_direction(tp, second); From 1dae79c833c202d7b6782b63fe295027450b0ebe Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 20 Jun 2019 21:25:25 +1000 Subject: [PATCH 15/25] gestures: fast-track scroll/swipe detection when gestures are off We can't detect pinch when gestures are off anyway, so we don't need to check the finger distances. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-gestures.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index e13fb3da..a3f18dcd 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -499,12 +499,13 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) /* If both touches are within 7mm vertically and 40mm horizontally * past the timeout, assume scroll/swipe */ - if (distance_mm.x < 40.0 && distance_mm.y < 7.0 && + if ((!tp->gesture.enabled || + (distance_mm.x < 40.0 && distance_mm.y < 7.0)) && time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) { if (tp->gesture.finger_count == 2) { tp_gesture_set_scroll_buildup(tp); return GESTURE_STATE_SCROLL; - } else if (tp->gesture.enabled) { + } else { return GESTURE_STATE_SWIPE; } } From 15c8613a653526db17af713e18f6a90f6cae3b80 Mon Sep 17 00:00:00 2001 From: Matt Mayfield Date: Thu, 30 Aug 2018 21:29:46 -0500 Subject: [PATCH 16/25] gestures: Improve thumb detection, allow one finger scroll Check if there's a thumb if we have two touches. If one finger moves but the thumb remains still, we assume that one is really a thumb. But if the thumb moves while the finger is still, let's assume this is a 2-finger scroll. Extracted from Matt Mayfield's thumb detection patchset --- src/evdev-mt-touchpad-gestures.c | 59 +++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index a3f18dcd..47d14177 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -471,11 +471,13 @@ static enum tp_gesture_state tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) { struct tp_touch *first = tp->gesture.touches[0], - *second = tp->gesture.touches[1]; + *second = tp->gesture.touches[1], + *thumb; uint32_t dir1, dir2; struct device_coords delta; struct phys_coords first_moved, second_moved, distance_mm; double first_mm, second_mm; /* movement since gesture start in mm */ + double thumb_mm, finger_mm; double inner = 1.5; /* inner threshold in mm - count this touch */ double outer = 4.0; /* outer threshold in mm - ignore other touch */ @@ -497,6 +499,17 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) if (first_mm < 1 && second_mm < 1) return GESTURE_STATE_UNKNOWN; + /* Pick the thumb as the lowest point on the touchpad */ + if (first->point.y > second->point.y) { + thumb = first; + thumb_mm = first_mm; + finger_mm = second_mm; + } else { + thumb = second; + thumb_mm = second_mm; + finger_mm = first_mm; + } + /* If both touches are within 7mm vertically and 40mm horizontally * past the timeout, assume scroll/swipe */ if ((!tp->gesture.enabled || @@ -511,29 +524,35 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) } /* If one touch exceeds the outer threshold while the other has not - * yet passed the inner threshold, this is not a valid gesture. - * If thumb detection is enabled, and one of the touches is >20mm - * below the other, cancel the gesture and mark the thumb. - * - * Give the thumb a larger effective outer threshold for more reliable - * detection of pinch vs. resting thumb. + * yet passed the inner threshold, there is either a resting thumb, + * or the user is doing "one-finger-scroll," where one touch stays in + * place while the other moves. */ - if (tp->thumb.detect_thumbs && distance_mm.y > 20.0 && - time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) { - if ((first->point.y >= second->point.y) && - ((first_mm >= outer * 2.0) || - (second_mm >= outer))) { - tp_gesture_cancel(tp, time); - first->thumb.state = THUMB_STATE_YES; + if (first_mm >= outer || second_mm >= outer) { + /* If thumb detection is enabled, and thumb is still while + * finger moves, cancel gestures and mark lower as thumb. + * This applies to all gestures (2, 3, 4+ fingers), but allows + * more thumb motion on >2 finger gestures during detection. + */ + if (tp->thumb.detect_thumbs && thumb_mm < inner) { + tp_thumb_set_state(tp, thumb, THUMB_STATE_YES); return GESTURE_STATE_NONE; } - if ((second->point.y >= first->point.y) && - ((second_mm >= outer * 2.0) || - (first_mm >= outer))) { - tp_gesture_cancel(tp, time); - second->thumb.state = THUMB_STATE_YES; - return GESTURE_STATE_NONE; + + /* If gestures detection is disabled, or if finger is still + * while thumb moves, assume this is "one-finger scrolling." + * This applies only to 2-finger gestures. + */ + if ((!tp->gesture.enabled || finger_mm < inner) && + tp->gesture.finger_count == 2) { + tp_gesture_set_scroll_buildup(tp); + return GESTURE_STATE_SCROLL; } + + /* If we get here, either both fingers have passed the inner + * threshold (handled below), or >2 fingers are involved + * (handled in a future event when both have moved enough). + */ } /* If either touch is still inside the inner threshold, we can't From 282979558eecd83c2a6c50d6a744b789d5f5fdff Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 21 Jun 2019 11:26:11 +1000 Subject: [PATCH 17/25] touchpad: add a helper function for supressing a thumb Only sets the state to YES at the moment, will do more in the future. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-gestures.c | 2 +- src/evdev-mt-touchpad-thumb.c | 8 +++++++- src/evdev-mt-touchpad.h | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 47d14177..7d4a13f9 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -535,7 +535,7 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) * more thumb motion on >2 finger gestures during detection. */ if (tp->thumb.detect_thumbs && thumb_mm < inner) { - tp_thumb_set_state(tp, thumb, THUMB_STATE_YES); + tp_thumb_suppress(tp, thumb); return GESTURE_STATE_NONE; } diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index c45a0670..41e6f41c 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -101,6 +101,12 @@ tp_thumb_detect_pressure_size(const struct tp_dispatch *tp, return is_thumb; } +void +tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t) +{ + tp_thumb_set_state(tp, t, THUMB_STATE_YES); +} + void tp_thumb_update_touch(struct tp_dispatch *tp, struct tp_touch *t, @@ -222,7 +228,7 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp) evdev_log_debug(tp->device, "touch %d is speed-based thumb\n", second->index); - tp_thumb_set_state(tp, second, THUMB_STATE_YES); + tp_thumb_suppress(tp, second); } void diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 8845c8cd..ef1265b1 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -687,6 +687,9 @@ tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t); void tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t); +void +tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t); + void tp_thumb_update_touch(struct tp_dispatch *tp, struct tp_touch *t, From f140826c860623b03d8d10ae651a08c7278c0e17 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Jul 2019 11:56:52 +1000 Subject: [PATCH 18/25] touchpad: only log edge scroll state changes when the state actually changes Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-edge-scroll.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index 4675b4ff..25e92a62 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -260,12 +260,13 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp, break; } - evdev_log_debug(tp->device, - "edge-scroll: touch %d state %s → %s → %s\n", - t->index, - edge_state_to_str(current), - edge_event_to_str(event), - edge_state_to_str(t->scroll.edge_state)); + if (current != t->scroll.edge_state) + evdev_log_debug(tp->device, + "edge-scroll: touch %d state %s → %s → %s\n", + t->index, + edge_state_to_str(current), + edge_event_to_str(event), + edge_state_to_str(t->scroll.edge_state)); } static void From 181f8d2ab58095319a43b48e351dfaf8fca30372 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Jul 2019 12:07:09 +1000 Subject: [PATCH 19/25] test: only run the speed tests for clickpads We don't need speed detection for non-clickpads - the only reason to ever drop a second finger on those is to either scroll or trigger a gesture. Unlike clickpads, where a dropped finger may be a thumb to click. Signed-off-by: Peter Hutterer --- test/test-touchpad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test-touchpad.c b/test/test-touchpad.c index 3b934145..1f349b83 100644 --- a/test/test-touchpad.c +++ b/test/test-touchpad.c @@ -7165,9 +7165,9 @@ TEST_COLLECTION(touchpad) litest_add("touchpad:touch-size", touchpad_touch_size, LITEST_APPLE_CLICKPAD, LITEST_ANY); litest_add("touchpad:touch-size", touchpad_touch_size_2fg, LITEST_APPLE_CLICKPAD, LITEST_ANY); - litest_add("touchpad:speed", touchpad_speed_ignore_finger, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); - litest_add("touchpad:speed", touchpad_speed_allow_nearby_finger, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); - litest_add("touchpad:speed", touchpad_speed_ignore_finger_edgescroll, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); + litest_add("touchpad:speed", touchpad_speed_ignore_finger, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); + litest_add("touchpad:speed", touchpad_speed_allow_nearby_finger, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); + litest_add("touchpad:speed", touchpad_speed_ignore_finger_edgescroll, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); litest_add_for_device("touchpad:speed", touchpad_speed_ignore_hovering_finger, LITEST_BCM5974); litest_add_ranged("touchpad:suspend", touchpad_suspend_abba, LITEST_TOUCHPAD, LITEST_ANY, &suspends); From 8e4d820efd604d927372f55ca9c02856cacbd6ff Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Jul 2019 12:10:42 +1000 Subject: [PATCH 20/25] test: only run the speed finger tests when the touchpad has thumb detection --- src/evdev-mt-touchpad.c | 2 +- test/test-touchpad.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 9b7d30ca..8f43b4b8 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1698,7 +1698,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) * never count down. Let's see how far we get with that. */ if (t->speed.last_speed > THUMB_IGNORE_SPEED_THRESHOLD) { - if (t->speed.exceeded_count < 10) + if (t->speed.exceeded_count < 15) t->speed.exceeded_count++; } else if (t->speed.exceeded_count > 0) { t->speed.exceeded_count--; diff --git a/test/test-touchpad.c b/test/test-touchpad.c index 1f349b83..9ce94568 100644 --- a/test/test-touchpad.c +++ b/test/test-touchpad.c @@ -6498,6 +6498,9 @@ START_TEST(touchpad_speed_ignore_finger) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + if (!has_thumb_detect(dev)) + return; + if (litest_has_clickfinger(dev)) litest_enable_clickfinger(dev); @@ -6521,6 +6524,9 @@ START_TEST(touchpad_speed_allow_nearby_finger) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + if (!has_thumb_detect(dev)) + return; + if (!litest_has_2fg_scroll(dev)) return; @@ -6550,6 +6556,9 @@ START_TEST(touchpad_speed_ignore_finger_edgescroll) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + if (!has_thumb_detect(dev)) + return; + litest_enable_edge_scroll(dev); if (litest_has_clickfinger(dev)) litest_enable_clickfinger(dev); @@ -6581,6 +6590,9 @@ START_TEST(touchpad_speed_ignore_hovering_finger) { -1, 0 } }; + if (!has_thumb_detect(dev)) + return; + litest_drain_events(li); /* first finger down but below touch size. we use slot 2 because From dda6f733af4960b15233909afff77a9f22ad92c5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Jul 2019 12:45:46 +1000 Subject: [PATCH 21/25] test: make the touchpad size the only check for thumb detection Signed-off-by: Peter Hutterer --- test/test-touchpad.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/test-touchpad.c b/test/test-touchpad.c index 9ce94568..32d4fb30 100644 --- a/test/test-touchpad.c +++ b/test/test-touchpad.c @@ -5000,9 +5000,6 @@ has_thumb_detect(struct litest_device *dev) { 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; From 4536b5b38faea0e43168f2349dd4c13b6489ff6e Mon Sep 17 00:00:00 2001 From: Matt Mayfield Date: Fri, 21 Jun 2019 13:19:22 +1000 Subject: [PATCH 22/25] touchpad: revamp thumb detection Instead of a simple yes/no/maybe for thumbs, have a more extensive state machine that keeps track of the thumb. Since we only support one thumb anyway, the tracking moves to the tp_dispatch struct. Test case changes: touchpad_clickfinger_3fg_tool_position: with better thumb detection we can now handle this properly and expect a right button (2fg) press for the test case touchpad_thumb_no_doublethumb_with_timeout: two thumbs are now always two fingers, so let's switch to axis events here Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-gestures.c | 16 +- src/evdev-mt-touchpad-tap.c | 2 +- src/evdev-mt-touchpad-thumb.c | 330 +++++++++++++++++++++---------- src/evdev-mt-touchpad.c | 20 +- src/evdev-mt-touchpad.h | 34 ++-- test/litest.c | 6 - test/litest.h | 3 - test/test-touchpad-buttons.c | 4 +- test/test-touchpad.c | 164 +++------------ 9 files changed, 294 insertions(+), 285 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 7d4a13f9..4a0287c7 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -549,10 +549,16 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) return GESTURE_STATE_SCROLL; } - /* If we get here, either both fingers have passed the inner - * threshold (handled below), or >2 fingers are involved - * (handled in a future event when both have moved enough). + /* If more than 2 fingers are involved, and the thumb moves + * while the fingers stay still, assume a pinch if eligible. */ + if (finger_mm < inner && + tp->gesture.finger_count > 2 && + tp->gesture.enabled && + tp->thumb.pinch_eligible) { + tp_gesture_init_pinch(tp); + return GESTURE_STATE_PINCH; + } } /* If either touch is still inside the inner threshold, we can't @@ -711,8 +717,8 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time) if (tp->gesture.finger_count == 0) return; - /* When tap-and-dragging, or a clickpad is clicked force 1fg mode */ - if (tp_tap_dragging(tp) || (tp->buttons.is_clickpad && tp->buttons.state)) { + /* When tap-and-dragging, force 1fg mode. */ + if (tp_tap_dragging(tp)) { tp_gesture_cancel(tp, time); tp->gesture.finger_count = 1; tp->gesture.finger_count_pending = 0; diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 551c70f2..a4828499 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -1015,7 +1015,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 (tp_thumb_ignored(tp, t)) { + if (tp_thumb_ignored_for_tap(tp, t)) { t->tap.is_thumb = true; continue; } diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index 41e6f41c..5b0cf968 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -25,70 +25,79 @@ #include "config.h" #include "evdev-mt-touchpad.h" -#define THUMB_MOVE_TIMEOUT ms2us(300) - -bool -tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t) -{ - return t->thumb.state == THUMB_STATE_YES; -} +/* distance between fingers to assume it is not a scroll */ +#define SCROLL_MM_X 35 +#define SCROLL_MM_Y 25 static inline const char* thumb_state_to_str(enum tp_thumb_state state) { switch(state){ - CASE_RETURN_STRING(THUMB_STATE_NO); - CASE_RETURN_STRING(THUMB_STATE_YES); - CASE_RETURN_STRING(THUMB_STATE_MAYBE); + CASE_RETURN_STRING(THUMB_STATE_FINGER); + CASE_RETURN_STRING(THUMB_STATE_JAILED); + CASE_RETURN_STRING(THUMB_STATE_PINCH); + CASE_RETURN_STRING(THUMB_STATE_SUPPRESSED); + CASE_RETURN_STRING(THUMB_STATE_REVIVED); + CASE_RETURN_STRING(THUMB_STATE_REVIVED_JAILED); + CASE_RETURN_STRING(THUMB_STATE_DEAD); } return NULL; } -void +static void tp_thumb_set_state(struct tp_dispatch *tp, struct tp_touch *t, enum tp_thumb_state state) { - if (t->thumb.state == state) + unsigned int index = t ? t->index : UINT_MAX; + + if (tp->thumb.state == state && tp->thumb.index == index) return; evdev_log_debug(tp->device, "thumb: touch %d, %s → %s\n", - t->index, - thumb_state_to_str(t->thumb.state), + (int)index, + thumb_state_to_str(tp->thumb.state), thumb_state_to_str(state)); - t->thumb.state = state; + tp->thumb.state = state; + tp->thumb.index = index; } void -tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t) +tp_thumb_reset(struct tp_dispatch *tp) { - t->thumb.state = THUMB_STATE_MAYBE; + tp->thumb.state = THUMB_STATE_FINGER; + tp->thumb.index = UINT_MAX; + tp->thumb.pinch_eligible = true; +} + +static void +tp_thumb_lift(struct tp_dispatch *tp) +{ + tp->thumb.state = THUMB_STATE_FINGER; + tp->thumb.index = UINT_MAX; } static bool tp_thumb_in_exclusion_area(const struct tp_dispatch *tp, - const struct tp_touch *t, - uint64_t time) + const struct tp_touch *t) { return (t->point.y > tp->thumb.lower_thumb_line && - tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE && - t->thumb.first_touch_time + THUMB_MOVE_TIMEOUT < time); + tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE); } static bool tp_thumb_detect_pressure_size(const struct tp_dispatch *tp, - const struct tp_touch *t, - uint64_t time) + const struct tp_touch *t) { bool is_thumb = false; if (tp->thumb.use_pressure && t->pressure > tp->thumb.pressure_threshold && - tp_thumb_in_exclusion_area(tp, t, time)) { + tp_thumb_in_exclusion_area(tp, t)) { is_thumb = true; } @@ -101,10 +110,94 @@ tp_thumb_detect_pressure_size(const struct tp_dispatch *tp, return is_thumb; } +static bool +tp_thumb_needs_jail(const struct tp_dispatch *tp, const struct tp_touch *t) +{ + if (t->point.y < tp->thumb.upper_thumb_line) + return false; + + if (!tp_thumb_in_exclusion_area(tp, t) && + !tp_thumb_detect_pressure_size(tp, t)) + return false; + + if (t->speed.exceeded_count >= 10) + return false; + + return true; +} + +bool +tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t) +{ + return (tp->thumb.detect_thumbs && + tp->thumb.index == t->index && + (tp->thumb.state == THUMB_STATE_JAILED || + tp->thumb.state == THUMB_STATE_PINCH || + tp->thumb.state == THUMB_STATE_SUPPRESSED || + tp->thumb.state == THUMB_STATE_REVIVED_JAILED || + tp->thumb.state == THUMB_STATE_DEAD)); +} + +bool +tp_thumb_ignored_for_tap(const struct tp_dispatch *tp, + const struct tp_touch *t) +{ + return (tp->thumb.detect_thumbs && + tp->thumb.index == t->index && + (tp->thumb.state == THUMB_STATE_PINCH || + tp->thumb.state == THUMB_STATE_SUPPRESSED || + tp->thumb.state == THUMB_STATE_DEAD)); +} + +bool +tp_thumb_ignored_for_gesture(const struct tp_dispatch *tp, + const struct tp_touch *t) +{ + return (tp->thumb.detect_thumbs && + tp->thumb.index == t->index && + (tp->thumb.state == THUMB_STATE_JAILED || + tp->thumb.state == THUMB_STATE_SUPPRESSED || + tp->thumb.state == THUMB_STATE_REVIVED_JAILED || + tp->thumb.state == THUMB_STATE_DEAD)); +} + void tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t) { - tp_thumb_set_state(tp, t, THUMB_STATE_YES); + if(tp->thumb.state == THUMB_STATE_FINGER || + tp->thumb.state == THUMB_STATE_JAILED || + tp->thumb.state == THUMB_STATE_PINCH || + tp->thumb.index != t->index) { + tp_thumb_set_state(tp, t, THUMB_STATE_SUPPRESSED); + return; + } + + tp_thumb_set_state(tp, t, THUMB_STATE_DEAD); +} + +static void +tp_thumb_pinch(struct tp_dispatch *tp, struct tp_touch *t) +{ + if(tp->thumb.state == THUMB_STATE_FINGER || + tp->thumb.state == THUMB_STATE_JAILED || + tp->thumb.index != t->index) + tp_thumb_set_state(tp, t, THUMB_STATE_PINCH); + else if (tp->thumb.state != THUMB_STATE_PINCH) + tp_thumb_suppress(tp, t); +} + +static void +tp_thumb_revive(struct tp_dispatch *tp, struct tp_touch *t) +{ + if((tp->thumb.state != THUMB_STATE_SUPPRESSED && + tp->thumb.state != THUMB_STATE_PINCH) || + (tp->thumb.index != t->index)) + return; + + if(tp_thumb_needs_jail(tp, t)) + tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED_JAILED); + else + tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED); } void @@ -112,77 +205,62 @@ tp_thumb_update_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { - /* once a thumb, always a thumb, once ruled out always ruled out */ - if (!tp->thumb.detect_thumbs || - t->thumb.state != THUMB_STATE_MAYBE) + if (!tp->thumb.detect_thumbs) 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 */ - tp_thumb_set_state(tp, t, THUMB_STATE_NO); - return; - } - - /* 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 phys_coords mm; - - delta = device_delta(t->point, t->thumb.initial); - mm = tp_phys_delta(tp, delta); - if (length_in_mm(mm) > 7) { - tp_thumb_set_state(tp, t, THUMB_STATE_NO); - return; - } - } - - /* If the finger is below the upper thumb line and we have another - * finger in the same area, neither finger is a thumb (unless we've - * already labeled it as such). + /* Once any active touch exceeds the speed threshold, don't + * try to detect pinches until all touches lift. */ - if (t->point.y > tp->thumb.upper_thumb_line && - tp->nfingers_down > 1) { - struct tp_touch *other; + if (t->speed.exceeded_count >= 10 && + tp->thumb.pinch_eligible && + tp->gesture.state == GESTURE_STATE_NONE) { + tp->thumb.pinch_eligible = false; + if(tp->thumb.state == THUMB_STATE_PINCH) { + struct tp_touch *thumb; + tp_for_each_touch(tp, thumb) { + if (thumb->index != tp->thumb.index) + continue; - tp_for_each_touch(tp, other) { - if (other->state != TOUCH_BEGIN && - other->state != TOUCH_UPDATE) - continue; - - if (other->point.y > tp->thumb.upper_thumb_line) { - tp_thumb_set_state(tp, t, THUMB_STATE_NO); - if (other->thumb.state == THUMB_STATE_MAYBE) - tp_thumb_set_state(tp, - other, - THUMB_STATE_NO); + tp_thumb_set_state(tp, thumb, THUMB_STATE_SUPPRESSED); break; } } } - /* 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. - * A finger that remains at the very bottom of the touchpad becomes - * a thumb. - */ - if (tp_thumb_detect_pressure_size(tp, t, time) || - tp_thumb_in_exclusion_area(tp, t, time)) - tp_thumb_set_state(tp, t, THUMB_STATE_YES); + /* Handle the thumb lifting off the touchpad */ + if (t->state == TOUCH_END && t->index == tp->thumb.index) { + tp_thumb_lift(tp); + return; + } - /* now what? we marked it as thumb, so: - * - * - pointer motion must ignore this touch - * - clickfinger must ignore this touch for finger count - * - software buttons are unaffected - * - edge scrolling unaffected - * - gestures: unaffected - * - tapping: honour thumb on begin, ignore it otherwise for now, - * this gets a tad complicated otherwise + /* If this touch is not the only one, thumb updates happen by context + * instead of here */ + if (tp->nfingers_down > 1) + return; + + /* If we arrived here by other fingers lifting off, revive current touch + * if appropriate + */ + tp_thumb_revive(tp, t); + + /* First new touch below the lower_thumb_line, or below the upper_thumb_ + * line if hardware can't verify it's a finger, starts as JAILED. + */ + if (t->state == TOUCH_BEGIN && tp_thumb_needs_jail(tp, t)) { + tp_thumb_set_state(tp, t, THUMB_STATE_JAILED); + return; + } + + /* If a touch breaks the speed threshold, or leaves the thumb area + * (upper or lower, depending on HW detection), it "escapes" jail. + */ + if (tp->thumb.state == THUMB_STATE_JAILED && + !(tp_thumb_needs_jail(tp, t))) + tp_thumb_set_state(tp, t, THUMB_STATE_FINGER); + if (tp->thumb.state == THUMB_STATE_REVIVED_JAILED && + !(tp_thumb_needs_jail(tp, t))) + tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED); } void @@ -190,45 +268,81 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp) { struct tp_touch *t; struct tp_touch *first = NULL, - *second = NULL; + *second = NULL, + *newest = NULL; struct device_coords distance; struct phys_coords mm; + unsigned int speed_exceeded_count = 0; + /* Get the first and second bottom-most touches, the max speed exceeded + * count overall, and the newest touch (or one of them, if more). + */ tp_for_each_touch(tp, t) { if (t->state == TOUCH_NONE || t->state == TOUCH_HOVERING) continue; - if (t->state != TOUCH_BEGIN) - first = t; - else - second = t; + if (t->state == TOUCH_BEGIN) + newest = t; - if (first && second) - break; + speed_exceeded_count = max(speed_exceeded_count, + t->speed.exceeded_count); + + if (!first) { + first = t; + continue; + } + + if (t->point.y > first->point.y) { + second = first; + first = t; + continue; + } + + if (!second || t->point.y > second->point.y ) { + second = t; + } } + if (!first || !second) + return; + assert(first); assert(second); - if (tp->scroll.method == LIBINPUT_CONFIG_SCROLL_2FG) { - /* If the second finger comes down next to the other one, we - * assume this is a scroll motion. - */ - distance.x = abs(first->point.x - second->point.x); - distance.y = abs(first->point.y - second->point.y); - mm = evdev_device_unit_delta_to_mm(tp->device, &distance); + distance.x = abs(first->point.x - second->point.x); + distance.y = abs(first->point.y - second->point.y); + mm = evdev_device_unit_delta_to_mm(tp->device, &distance); - if (mm.x <= 25 && mm.y <= 15) - return; + /* Speed-based thumb detection: if an existing touch is moving, and + * a new touch arrives, mark it as a thumb if it doesn't qualify as a + * 2-finger scroll. + */ + if (newest && + tp->nfingers_down == 2 && + speed_exceeded_count > 5 && + (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG || + (mm.x > SCROLL_MM_X || mm.y > SCROLL_MM_Y))) { + evdev_log_debug(tp->device, + "touch %d is speed-based thumb\n", + newest->index); + tp_thumb_suppress(tp, newest); + return; } - /* Finger are too far apart or 2fg scrolling is disabled, mark - * second finger as thumb */ - evdev_log_debug(tp->device, - "touch %d is speed-based thumb\n", - second->index); - tp_thumb_suppress(tp, second); + /* Position-based thumb detection: When a new touch arrives, check the + * two lowest touches. If they qualify for 2-finger scrolling, clear + * thumb status. If not, mark the lower touch (based on pinch_eligible) + * as either PINCH or SUPPRESSED. + */ + if (mm.y > SCROLL_MM_Y) { + if (tp->thumb.pinch_eligible) + tp_thumb_pinch(tp, first); + else + tp_thumb_suppress(tp, first); + } else { + tp_thumb_lift(tp); + } } void @@ -289,6 +403,8 @@ tp_init_thumb(struct tp_dispatch *tp) } } + tp_thumb_reset(tp); + quirks_unref(q); evdev_log_debug(device, diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 8f43b4b8..4ffc4a39 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -353,8 +353,6 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) t->was_down = true; tp->nfingers_down++; t->palm.time = time; - tp_thumb_reset(tp, t); - t->thumb.first_touch_time = time; t->tap.is_thumb = false; t->tap.is_palm = false; t->speed.exceeded_count = 0; @@ -784,7 +782,7 @@ tp_touch_active_for_gesture(const struct tp_dispatch *tp, const struct tp_touch return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) && t->palm.state == PALM_NONE && !t->pinned.is_pinned && - !tp_thumb_ignored(tp, t) && + !tp_thumb_ignored_for_gesture(tp, t) && tp_button_touch_active(tp, t) && tp_edge_scroll_touch_active(tp, t); } @@ -1717,11 +1715,9 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) } } - /* If we have one touch that exceeds the speed and we get a new - * touch down while doing that, the second touch is a thumb */ - if (have_new_touch && - tp->nfingers_down == 2 && - speed_exceeded_count > 5) + if (tp->thumb.detect_thumbs && + have_new_touch && + tp->nfingers_down >= 2) tp_thumb_update_multifinger(tp); if (restart_filter) @@ -1770,6 +1766,9 @@ tp_post_process_state(struct tp_dispatch *tp, uint64_t time) tp->queued = TOUCHPAD_EVENT_NONE; + if (tp->nfingers_down == 0) + tp_thumb_reset(tp); + tp_tap_post_process_state(tp); } @@ -1961,6 +1960,8 @@ tp_clear_state(struct tp_dispatch *tp) * * Then lift all touches so the touchpad is in a neutral state. * + * Then reset thumb state. + * */ tp_release_all_buttons(tp, now); tp_release_all_taps(tp, now); @@ -1970,6 +1971,8 @@ tp_clear_state(struct tp_dispatch *tp) } tp_release_fake_touches(tp); + tp_thumb_reset(tp); + tp_handle_state(tp, now); } @@ -3301,7 +3304,6 @@ tp_init_sendevents(struct tp_dispatch *tp, tp_keyboard_timeout, tp); } - static bool tp_pass_sanity_check(struct tp_dispatch *tp, struct evdev_device *device) diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index ef1265b1..5df284f2 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -139,9 +139,13 @@ enum tp_gesture_state { }; enum tp_thumb_state { - THUMB_STATE_NO, - THUMB_STATE_YES, - THUMB_STATE_MAYBE, + THUMB_STATE_FINGER, + THUMB_STATE_JAILED, + THUMB_STATE_PINCH, + THUMB_STATE_SUPPRESSED, + THUMB_STATE_REVIVED, + THUMB_STATE_REVIVED_JAILED, + THUMB_STATE_DEAD, }; enum tp_jump_state { @@ -237,12 +241,6 @@ struct tp_touch { struct device_coords initial; } gesture; - struct { - enum tp_thumb_state state; - uint64_t first_touch_time; - struct device_coords initial; - } thumb; - struct { double last_speed; /* speed in mm/s at last sample */ unsigned int exceeded_count; @@ -457,6 +455,10 @@ struct tp_dispatch { bool use_size; int size_threshold; + + enum tp_thumb_state state; + unsigned int index; + bool pinch_eligible; } thumb; struct { @@ -685,7 +687,14 @@ bool tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t); void -tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t); +tp_thumb_reset(struct tp_dispatch *tp); + +bool +tp_thumb_ignored_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t); + +bool +tp_thumb_ignored_for_tap(const struct tp_dispatch *tp, + const struct tp_touch *t); void tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t); @@ -704,9 +713,4 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp); void tp_init_thumb(struct tp_dispatch *tp); -void -tp_thumb_set_state(struct tp_dispatch *tp, - struct tp_touch *t, - enum tp_thumb_state state); - #endif diff --git a/test/litest.c b/test/litest.c index b31a3f48..5b09ec4e 100644 --- a/test/litest.c +++ b/test/litest.c @@ -3808,12 +3808,6 @@ litest_timeout_hysteresis(void) msleep(90); } -void -litest_timeout_thumb(void) -{ - msleep(320); -} - void litest_push_event_frame(struct litest_device *dev) { diff --git a/test/litest.h b/test/litest.h index 98b536ba..85a0a1f2 100644 --- a/test/litest.h +++ b/test/litest.h @@ -847,9 +847,6 @@ litest_timeout_touch_arbitration(void); void litest_timeout_hysteresis(void); -void -litest_timeout_thumb(void); - void litest_push_event_frame(struct litest_device *dev); diff --git a/test/test-touchpad-buttons.c b/test/test-touchpad-buttons.c index 73c0a60f..4759b896 100644 --- a/test/test-touchpad-buttons.c +++ b/test/test-touchpad-buttons.c @@ -817,9 +817,9 @@ START_TEST(touchpad_clickfinger_3fg_tool_position) litest_event(dev, EV_SYN, SYN_REPORT, 0); libinput_dispatch(li); - litest_assert_button_event(li, BTN_MIDDLE, + litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED); - litest_assert_button_event(li, BTN_MIDDLE, + litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED); } END_TEST diff --git a/test/test-touchpad.c b/test/test-touchpad.c index 32d4fb30..38506768 100644 --- a/test/test-touchpad.c +++ b/test/test-touchpad.c @@ -5006,7 +5006,7 @@ has_thumb_detect(struct litest_device *dev) return h >= 50.0; } -START_TEST(touchpad_thumb_area_begin_no_motion) +START_TEST(touchpad_thumb_lower_area_movement) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; @@ -5018,86 +5018,38 @@ START_TEST(touchpad_thumb_area_begin_no_motion) litest_drain_events(li); + /* Thumb below lower line - slow movement - no events */ litest_touch_down(dev, 0, 50, 99); - libinput_dispatch(li); - litest_timeout_thumb(); - libinput_dispatch(li); - litest_touch_move_to(dev, 0, 50, 99, 80, 99, 10); + litest_touch_move_to(dev, 0, 55, 99, 60, 99, 50); + litest_assert_empty_queue(li); + + /* Thumb below lower line - fast movement - events */ + litest_touch_move_to(dev, 0, 60, 99, 90, 99, 30); litest_touch_up(dev, 0); - litest_assert_empty_queue(li); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); } END_TEST -START_TEST(touchpad_thumb_area_update_no_motion) +START_TEST(touchpad_thumb_lower_area_movement_rethumb) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; - litest_disable_tap(dev->libinput_device); - litest_enable_clickfinger(dev); - if (!has_thumb_detect(dev)) return; - litest_drain_events(li); - - litest_touch_down(dev, 0, 59, 99); - litest_touch_move_to(dev, 0, 59, 99, 61, 99, 10); - libinput_dispatch(li); - /* the first move may trigger events, but not after the timeout */ - litest_drain_events(li); - litest_timeout_thumb(); - libinput_dispatch(li); - litest_touch_move_to(dev, 0, 61, 99, 80, 99, 10); - litest_touch_up(dev, 0); - - litest_assert_empty_queue(li); -} -END_TEST - -START_TEST(touchpad_thumb_area_small_move) -{ - struct litest_device *dev = litest_current_device(); - struct libinput *li = dev->libinput; - litest_disable_tap(dev->libinput_device); - litest_enable_clickfinger(dev); - - if (!has_thumb_detect(dev)) - return; litest_drain_events(li); - /* movement less than the threshold */ + /* Thumb below lower line - fast movement - events */ litest_touch_down(dev, 0, 50, 99); - libinput_dispatch(li); - litest_timeout_thumb(); - libinput_dispatch(li); - - litest_touch_move_to(dev, 0, 50, 99, 52, 99, 10); - litest_touch_up(dev, 0); - - litest_assert_empty_queue(li); -} -END_TEST - -START_TEST(touchpad_thumb_area_large_move) -{ - struct litest_device *dev = litest_current_device(); - struct libinput *li = dev->libinput; - - litest_disable_tap(dev->libinput_device); - litest_enable_clickfinger(dev); - - if (!has_thumb_detect(dev)) - return; - + litest_touch_move_to(dev, 0, 50, 99, 90, 99, 30); litest_drain_events(li); - /* moving within the area triggers events */ - litest_touch_down(dev, 0, 50, 99); - litest_touch_move_to(dev, 0, 50, 99, 80, 99, 10); + /* slow movement after being a non-touch - still events */ + litest_touch_move_to(dev, 0, 90, 99, 60, 99, 50); litest_touch_up(dev, 0); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); @@ -5147,10 +5099,6 @@ START_TEST(touchpad_thumb_area_clickfinger) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct libinput_event *event; - struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 31 }, - { -1, 0 } - }; if (!has_thumb_detect(dev)) return; @@ -5162,13 +5110,7 @@ START_TEST(touchpad_thumb_area_clickfinger) litest_drain_events(li); - litest_touch_down(dev, 0, 50, 99); - libinput_dispatch(li); - litest_timeout_thumb(); - libinput_dispatch(li); - /* Need an extra event because the thumb doesn't have proper timers. - Shouldn't matter in real life */ - litest_touch_move_extended(dev, 0, 55, 99, axes); + litest_touch_down(dev, 0, 50, 99); /* thumb */ libinput_dispatch(li); litest_touch_down(dev, 1, 60, 50); libinput_dispatch(li); @@ -5189,13 +5131,7 @@ START_TEST(touchpad_thumb_area_clickfinger) litest_drain_events(li); - litest_touch_down(dev, 1, 60, 99); - libinput_dispatch(li); - litest_timeout_thumb(); - libinput_dispatch(li); - /* Need an extra event because the thumb doesn't have proper timers. - Shouldn't matter in real life */ - litest_touch_move_extended(dev, 1, 60, 99, axes); + litest_touch_down(dev, 1, 60, 99); /* thumb */ libinput_dispatch(li); litest_touch_down(dev, 0, 50, 50); libinput_dispatch(li); @@ -5217,10 +5153,6 @@ START_TEST(touchpad_thumb_area_btnarea) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct libinput_event *event; - struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 31 }, - { -1, 0 } - }; if (!has_thumb_detect(dev)) return; @@ -5232,11 +5164,8 @@ START_TEST(touchpad_thumb_area_btnarea) litest_drain_events(li); - litest_touch_down(dev, 0, 90, 99); + litest_touch_down(dev, 0, 90, 99); /* thumb */ libinput_dispatch(li); - litest_timeout_thumb(); - libinput_dispatch(li); - litest_touch_move_extended(dev, 0, 95, 99, axes); litest_button_click(dev, BTN_LEFT, true); /* button areas work as usual with a thumb */ @@ -5265,17 +5194,17 @@ START_TEST(touchpad_thumb_no_doublethumb) litest_drain_events(li); + /* two touches in thumb area but we can't have two thumbs */ litest_touch_down(dev, 0, 50, 99); + /* random sleep interval. we don't have a thumb timer, but let's not + * put both touches down and move them immediately because that + * should always be a scroll event anyway. Go with a delay in + * between to make it more likely that this is really testing thumb + * detection. + */ + msleep(200); + libinput_dispatch(li); litest_touch_down(dev, 1, 70, 99); - /* move touch to trigger the thumb detection */ - litest_touch_move(dev, 0, 50, 99.2); - - libinput_dispatch(li); - litest_timeout_thumb(); - libinput_dispatch(li); - - /* move touch to trigger the thumb detection */ - litest_touch_move(dev, 1, 70, 99.2); libinput_dispatch(li); litest_touch_move_two_touches(dev, 50, 99, 70, 99, 0, -20, 10); @@ -5286,42 +5215,6 @@ START_TEST(touchpad_thumb_no_doublethumb) } END_TEST -START_TEST(touchpad_thumb_no_doublethumb_with_timeout) -{ - struct litest_device *dev = litest_current_device(); - struct libinput *li = dev->libinput; - - litest_disable_tap(dev->libinput_device); - litest_enable_clickfinger(dev); - - if (!has_thumb_detect(dev)) - return; - - litest_drain_events(li); - - litest_touch_down(dev, 0, 50, 99.9); - libinput_dispatch(li); - litest_timeout_thumb(); - libinput_dispatch(li); - /* Thumbs don't have a timeout handler, so we have to move the thumb - * a bit to trigger. */ - litest_touch_move(dev, 0, 50, 99.8); - - /* first touch should now be a thumb */ - - litest_touch_down(dev, 1, 70, 99.9); - libinput_dispatch(li); - litest_timeout_thumb(); - libinput_dispatch(li); - litest_touch_move(dev, 1, 70, 99.8); - litest_touch_move_two_touches(dev, 50, 99, 70, 99, 0, -20, 10); - litest_touch_up(dev, 0); - litest_touch_up(dev, 1); - - litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); -} -END_TEST - START_TEST(touchpad_tool_tripletap_touch_count) { struct litest_device *dev = litest_current_device(); @@ -7137,15 +7030,12 @@ TEST_COLLECTION(touchpad) litest_add_for_device("touchpad:dwt", touchpad_dwt_multiple_keyboards_bothkeys_modifier, LITEST_SYNAPTICS_I2C); litest_add_ranged_for_device("touchpad:dwt", touchpad_dwt_multiple_keyboards_remove, LITEST_SYNAPTICS_I2C, &twice); - litest_add("touchpad:thumb", touchpad_thumb_area_begin_no_motion, LITEST_CLICKPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_area_update_no_motion, LITEST_CLICKPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_area_small_move, LITEST_CLICKPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_area_large_move, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_lower_area_movement, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_lower_area_movement_rethumb, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_speed_empty_slots, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:thumb", touchpad_thumb_area_clickfinger, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_area_btnarea, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_no_doublethumb, LITEST_CLICKPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_no_doublethumb_with_timeout, LITEST_CLICKPAD, LITEST_ANY); litest_add_for_device("touchpad:bugs", touchpad_tool_tripletap_touch_count, LITEST_SYNAPTICS_TOPBUTTONPAD); litest_add_for_device("touchpad:bugs", touchpad_tool_tripletap_touch_count_late, LITEST_SYNAPTICS_TOPBUTTONPAD); From 94a7cd4959b8e24e095d00b2afcdabe756c71224 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Jul 2019 14:06:07 +1000 Subject: [PATCH 23/25] doc/user: add documentation for the new thumb detection Signed-off-by: Peter Hutterer --- doc/user/features.rst | 1 + doc/user/meson.build | 1 + doc/user/touchpad-thumb-detection.rst | 89 +++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 doc/user/touchpad-thumb-detection.rst diff --git a/doc/user/features.rst b/doc/user/features.rst index 6e644a8a..55c9cbb5 100644 --- a/doc/user/features.rst +++ b/doc/user/features.rst @@ -18,6 +18,7 @@ to be useful. gestures.rst middle-button-emulation.rst palm-detection.rst + touchpad-thumb-detection.rst scrolling.rst t440-support.rst tapping.rst diff --git a/doc/user/meson.build b/doc/user/meson.build index fce6ec15..7d81bcff 100644 --- a/doc/user/meson.build +++ b/doc/user/meson.build @@ -159,6 +159,7 @@ src_rst = files( 'touchpad-pressure.rst', 'touchpad-pressure-debugging.rst', 'touchpad-jitter.rst', + 'touchpad-thumb-detection.rst', 'touchpads.rst', 'trackpoints.rst', 'trackpoint-configuration.rst', diff --git a/doc/user/touchpad-thumb-detection.rst b/doc/user/touchpad-thumb-detection.rst new file mode 100644 index 00000000..caf26dc5 --- /dev/null +++ b/doc/user/touchpad-thumb-detection.rst @@ -0,0 +1,89 @@ +.. _thumb_detection: + +============================================================================== +Thumb detection +============================================================================== + +Thumb detection tries to identify touches triggered by a thumb rather than a +pointer-moving finger. This is necessary on :ref:`touchpads_buttons_clickpads` +as a finger pressing a button always creates a new touch, causing +misinterpretation of gestures. Click-and-drag with two fingers (one holding +the button, one moving) would be interpreted as two-finger scrolling without +working thumb detection. + +libinput has built-in thumb detection, partially dependent on +hardware-specific capabilities. + +- :ref:`thumb_pressure` +- :ref:`thumb_areas` +- :ref:`thumb_speed` + +Thumb detection uses multiple approaches and the final decision on whether +to ignore a thumb depends on the interaction at the time. + +.. _thumb_pressure: + +------------------------------------------------------------------------------ +Thumb detection based on pressure or size +------------------------------------------------------------------------------ + +The simplest form of thumb detection identifies a touch as thumb when the +pressure value goes above a certain threshold. This threshold is usually +high enough that it cannot be triggered by a finger movement. + +On touchpads that support the ``ABS_MT_TOUCH_MAJOR`` axes, libinput can perform +thumb detection based on the size of the touch ellipse. This works similar to +the pressure-based palm detection in that a touch is labelled as palm when +it exceeds the (device-specific) touch size threshold. + +Pressure- and size-based thumb detection depends on the location of the +thumb and usually only applies within the :ref:`thumb_areas`. + +For some information on how to detect pressure on a touch and debug the +pressure ranges, see :ref:`touchpad_pressure`. Pressure- and size-based +thumb detection require thresholds set in the :ref:`device-quirks`. + +.. _thumb_areas: + +------------------------------------------------------------------------------ +Thumb detection areas +------------------------------------------------------------------------------ + +Pressure and size readings are unreliable at the far bottom of the touchpad. +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 +thumb. Immediately above that area is the area where libinput will label a +thumb as such if the pressure or size thresholds are exceeded. + + +.. figure:: thumb-detection.svg + :align: center + +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 always labelled +as thumb. + +Moving outside the areas generally releases the thumb from being a thumb. + +.. _thumb_speed: + +------------------------------------------------------------------------------ +Thumb movement based on speed +------------------------------------------------------------------------------ + +Regular interactions with thumbs do not usually move the thumb. When fingers +are moving across the touchpad and a thumb is dropped, this can cause +erroneous scroll motion or similar issues. libinput observes the finger +motion speed for all touches - where a finger has been moving a newly +dropped finger is more likely to be labeled as thumb. + +------------------------------------------------------------------------------ +Thumb detection based on finger positions +------------------------------------------------------------------------------ + +The shape of the human hand and the interactions that usually involve a +thumb imply that a thumb is situated in a specific position relative to +other fingers (usually to the side and below). This is used by libinput to +detect thumbs during some interactions that do not implicitly require a +thumb (e.g. pinch-and-rotate). From 35fd6e6c4e46940e8e2322afbb078675dc710b80 Mon Sep 17 00:00:00 2001 From: Matt Mayfield Date: Sun, 14 Jul 2019 12:50:14 -0500 Subject: [PATCH 24/25] touchpad: don't detect speed-based thumbs if there's already a thumb --- src/evdev-mt-touchpad-thumb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index 5b0cf968..e62c78c8 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -314,11 +314,12 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp) distance.y = abs(first->point.y - second->point.y); mm = evdev_device_unit_delta_to_mm(tp->device, &distance); - /* Speed-based thumb detection: if an existing touch is moving, and + /* Speed-based thumb detection: if an existing finger is moving, and * a new touch arrives, mark it as a thumb if it doesn't qualify as a * 2-finger scroll. */ if (newest && + tp->thumb.state == THUMB_STATE_FINGER && tp->nfingers_down == 2 && speed_exceeded_count > 5 && (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG || From c284d4aaf07c6ec9fe0dd5bcada94e173090f8c2 Mon Sep 17 00:00:00 2001 From: Matt Mayfield Date: Thu, 11 Jul 2019 10:14:37 -0500 Subject: [PATCH 25/25] touchpad: stricter thumb detection if no pressure/size --- src/evdev-mt-touchpad-thumb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index e62c78c8..eb20616a 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -113,10 +113,12 @@ tp_thumb_detect_pressure_size(const struct tp_dispatch *tp, static bool tp_thumb_needs_jail(const struct tp_dispatch *tp, const struct tp_touch *t) { - if (t->point.y < tp->thumb.upper_thumb_line) + if (t->point.y < tp->thumb.upper_thumb_line || + tp->scroll.method == LIBINPUT_CONFIG_SCROLL_EDGE) return false; if (!tp_thumb_in_exclusion_area(tp, t) && + (tp->thumb.use_size || tp->thumb.use_pressure) && !tp_thumb_detect_pressure_size(tp, t)) return false;