diff --git a/doc/Makefile.am b/doc/Makefile.am index 95a96e45..ddda230b 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -39,6 +39,7 @@ diagram_files = \ $(srcdir)/svg/pinch-gestures.svg \ $(srcdir)/svg/swipe-gestures.svg \ $(srcdir)/svg/tap-n-drag.svg \ + $(srcdir)/svg/thumb-detection.svg \ $(srcdir)/svg/top-software-buttons.svg \ $(srcdir)/svg/touchscreen-gestures.svg \ $(srcdir)/svg/twofinger-scrolling.svg diff --git a/doc/palm-detection.dox b/doc/palm-detection.dox index d787455e..1a5e6572 100644 --- a/doc/palm-detection.dox +++ b/doc/palm-detection.dox @@ -80,4 +80,32 @@ Notable behaviors of libinput's disable-while-typing feature: - Physical buttons work even while the touchpad is disabled. This includes software-emulated buttons. +@section thumb-detection Thumb detection + +Many users rest their thumb on the touchpad while using the index finger to +move the finger around. For clicks, often the thumb is used rather than the +finger. The thumb should otherwise be ignored as a touch, i.e. it should not +count towards @ref clickfinger and it should not cause a single-finger +movement to trigger @ref twofinger_scrolling. + +libinput uses two triggers for thumb detection: pressure and +location. A touch exceeding a pressure threshold is considered a thumb if it +is within the thumb detection zone. + +@note "Pressure" on touchpads is synonymous with "contact area", a large +touch surface area has a higher pressure and thus hints at a thumb or palm +touching the surface. + +Pressure readings are unreliable at the far bottom of the touchpad as a +thumb hanging mostly off the touchpad will have a small surface area. +libinput has a definitive thumb zone where any touch is considered a resting +thumb. + +@image html thumb-detection.svg + +The picture above shows the two detection areas. In the larger (light red) +area, a touch is labelled as thumb when it exceeds a device-specific +pressure threshold. In the lower (dark red) area, a touch is labelled as +thumb if it remains in that area for a time without moving outside. + */ diff --git a/doc/svg/thumb-detection.svg b/doc/svg/thumb-detection.svg new file mode 100644 index 00000000..bc746981 --- /dev/null +++ b/doc/svg/thumb-detection.svg @@ -0,0 +1,116 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index a2fb4b41..1ee74cad 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -807,7 +807,8 @@ tp_check_clickfinger_distance(struct tp_dispatch *tp, if (!t1 || !t2) return 0; - if (t1->is_thumb || t2->is_thumb) + if (t1->thumb.state == THUMB_STATE_YES || + t2->thumb.state == THUMB_STATE_YES) return 0; x = abs(t1->point.x - t2->point.x); @@ -869,6 +870,9 @@ 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) + continue; + if (!first) first = t; else if (!second) @@ -899,9 +903,8 @@ out: case 0: case 1: button = BTN_LEFT; break; case 2: button = BTN_RIGHT; break; - case 3: button = BTN_MIDDLE; break; default: - button = 0; + button = BTN_MIDDLE; break; break; } diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 1354e899..b2977545 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -740,7 +740,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) /* The simple version: if a touch is a thumb on * begin we ignore it. All other thumb touches * follow the normal tap state for now */ - if (t->is_thumb) { + if (t->thumb.state == THUMB_STATE_YES) { t->tap.is_thumb = true; continue; } @@ -772,7 +772,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time); } else if (tp->tap.state != TAP_STATE_IDLE && - t->is_thumb && + t->thumb.state == THUMB_STATE_YES && !t->tap.is_thumb) { tp_tap_handle_event(tp, t, TAP_EVENT_THUMB, time); } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index a5695844..6718b616 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -208,7 +208,8 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) t->millis = time; tp->nfingers_down++; t->palm.time = time; - t->is_thumb = false; + t->thumb.state = THUMB_STATE_MAYBE; + t->thumb.first_touch_time = time; t->tap.is_thumb = false; assert(tp->nfingers_down >= 1); } @@ -314,6 +315,8 @@ tp_process_absolute(struct tp_dispatch *tp, break; case ABS_MT_PRESSURE: t->pressure = e->value; + t->dirty = true; + tp->queued |= TOUCHPAD_EVENT_MOTION; break; } } @@ -499,7 +502,7 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t) return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) && t->palm.state == PALM_NONE && !t->pinned.is_pinned && - !t->is_thumb && + t->thumb.state != THUMB_STATE_YES && tp_button_touch_active(tp, t) && tp_edge_scroll_touch_active(tp, t); } @@ -642,20 +645,63 @@ out: t->palm.state == PALM_TYPING ? "typing" : "trackpoint"); } -static void -tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t) +static inline const char* +thumb_state_to_str(enum tp_thumb_state state) { - /* once a thumb, always a thumb */ - if (!tp->thumb.detect_thumbs || t->is_thumb) + switch(state){ + CASE_RETURN_STRING(THUMB_STATE_NO); + CASE_RETURN_STRING(THUMB_STATE_YES); + CASE_RETURN_STRING(THUMB_STATE_MAYBE); + } + + return NULL; +} + +static void +tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) +{ + enum tp_thumb_state state = t->thumb.state; + + /* once a thumb, always a thumb, once ruled out always ruled out */ + if (!tp->thumb.detect_thumbs || + t->thumb.state != THUMB_STATE_MAYBE) return; + if (t->point.y < tp->thumb.upper_thumb_line) { + /* if a potential thumb is above the line, it won't ever + * label as thumb */ + t->thumb.state = THUMB_STATE_NO; + goto out; + } + + /* If the thumb moves by more than 7mm, it's not a resting thumb */ + if (t->state == TOUCH_BEGIN) + t->thumb.initial = t->point; + else if (t->state == TOUCH_UPDATE) { + struct device_float_coords delta; + struct normalized_coords normalized; + + delta = device_delta(t->point, t->thumb.initial); + normalized = tp_normalize_delta(tp, delta); + if (normalized_length(normalized) > + TP_MM_TO_DPI_NORMALIZED(7)) { + t->thumb.state = THUMB_STATE_NO; + goto out; + } + } + /* Note: a thumb at the edge of the touchpad won't trigger the - * threshold, the surface area is usually too small. + * threshold, the surface area is usually too small. So we have a + * two-stage detection: pressure and time within the area. + * A finger that remains at the very bottom of the touchpad becomes + * a thumb. */ - if (t->pressure < tp->thumb.threshold) - return; - - t->is_thumb = true; + if (t->pressure > tp->thumb.threshold) + t->thumb.state = THUMB_STATE_YES; + else if (t->point.y > tp->thumb.lower_thumb_line && + tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE && + t->thumb.first_touch_time + 300 < time) + t->thumb.state = THUMB_STATE_YES; /* now what? we marked it as thumb, so: * @@ -667,6 +713,12 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t) * - tapping: honour thumb on begin, ignore it otherwise for now, * this gets a tad complicated otherwise */ +out: + if (t->thumb.state != state) + log_debug(tp_libinput_context(tp), + "thumb state: %s → %s\n", + thumb_state_to_str(state), + thumb_state_to_str(t->thumb.state)); } static void @@ -760,6 +812,48 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time) } +static inline void +tp_position_fake_touches(struct tp_dispatch *tp) +{ + struct tp_touch *t; + struct tp_touch *topmost = NULL; + unsigned int start, i; + + if (tp_fake_finger_count(tp) <= tp->num_slots) + return; + + /* We have at least one fake touch down. Find the top-most real + * touch and copy its coordinates over to to all fake touches. + * This is more reliable than just taking the first touch. + */ + for (i = 0; i < tp->num_slots; i++) { + t = tp_get_touch(tp, i); + if (t->state == TOUCH_END || + t->state == TOUCH_NONE) + continue; + + if (topmost == NULL || t->point.y < topmost->point.y) + topmost = t; + } + + if (!topmost) { + log_bug_libinput(tp_libinput_context(tp), + "Unable to find topmost touch\n"); + return; + } + + start = tp->has_mt ? tp->num_slots : 1; + for (i = start; i < tp->ntouches; i++) { + t = tp_get_touch(tp, i); + if (t->state == TOUCH_NONE) + continue; + + t->point = topmost->point; + if (!t->dirty) + t->dirty = topmost->dirty; + } +} + static inline bool tp_need_motion_history_reset(struct tp_dispatch *tp) { @@ -782,13 +876,13 @@ static void tp_process_state(struct tp_dispatch *tp, uint64_t time) { struct tp_touch *t; - struct tp_touch *first = tp_get_touch(tp, 0); unsigned int i; bool restart_filter = false; bool want_motion_reset; tp_process_fake_touches(tp, time); tp_unhover_touches(tp, time); + tp_position_fake_touches(tp); want_motion_reset = tp_need_motion_history_reset(tp); @@ -803,16 +897,10 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) t->quirks.reset_motion_history = false; } - if (i >= tp->num_slots && t->state != TOUCH_NONE) { - t->point = first->point; - if (!t->dirty) - t->dirty = first->dirty; - } - if (!t->dirty) continue; - tp_thumb_detect(tp, t); + tp_thumb_detect(tp, t, time); tp_palm_detect(tp, t, time); tp_motion_hysteresis(tp, t); @@ -1635,6 +1723,13 @@ tp_init_thumb(struct tp_dispatch *tp) { struct evdev_device *device = tp->device; const struct input_absinfo *abs; + double w = 0.0, h = 0.0; + int xres, yres; + int ymax; + double threshold; + + if (!tp->buttons.is_clickpad) + return 0; abs = libevdev_get_abs_info(device->evdev, ABS_MT_PRESSURE); if (!abs) @@ -1643,14 +1738,33 @@ tp_init_thumb(struct tp_dispatch *tp) if (abs->maximum - abs->minimum < 255) return 0; - /* The touchpads we looked at so far have a clear thumb threshold of - * ~100, you don't reach that with a normal finger interaction. + /* if the touchpad is less than 50mm high, skip thumb detection. + * it's too small to meaningfully interact with a thumb on the + * touchpad */ + evdev_device_get_size(device, &w, &h); + if (h < 50) + return 0; + + /* Our reference touchpad is the T440s with 42x42 resolution. + * Higher-res touchpads exhibit higher pressure for the same + * interaction. On the T440s, the threshold value is 100, you don't + * reach that with a normal finger interaction. * Note: "thumb" means massive touch that should not interact, not * "using the tip of my thumb for a pinch gestures". */ - tp->thumb.threshold = 100; + xres = tp->device->abs.absinfo_x->resolution; + yres = tp->device->abs.absinfo_y->resolution; + threshold = 100.0 * hypot(xres, yres)/hypot(42, 42); + tp->thumb.threshold = max(100, threshold); tp->thumb.detect_thumbs = true; + /* detect thumbs by pressure in the bottom 15mm, detect thumbs by + * lingering in the bottom 8mm */ + ymax = tp->device->abs.absinfo_y->maximum; + yres = tp->device->abs.absinfo_y->resolution; + tp->thumb.upper_thumb_line = ymax - yres * 15; + tp->thumb.lower_thumb_line = ymax - yres * 8; + return 0; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 1cc4925e..6350a9f6 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -136,12 +136,17 @@ enum tp_gesture_2fg_state { GESTURE_2FG_STATE_PINCH, }; +enum tp_thumb_state { + THUMB_STATE_NO, + THUMB_STATE_YES, + THUMB_STATE_MAYBE, +}; + struct tp_touch { struct tp_dispatch *tp; enum touch_state state; bool has_ended; /* TRACKING_ID == -1 */ bool dirty; - bool is_thumb; struct device_coords point; uint64_t millis; int distance; /* distance == 0 means touch */ @@ -204,6 +209,12 @@ struct tp_touch { struct { struct device_coords initial; } gesture; + + struct { + enum tp_thumb_state state; + uint64_t first_touch_time; + struct device_coords initial; + } thumb; }; struct tp_dispatch { @@ -336,6 +347,8 @@ struct tp_dispatch { struct { bool detect_thumbs; int threshold; + int upper_thumb_line; + int lower_thumb_line; } thumb; }; diff --git a/test/litest.c b/test/litest.c index 50ae1412..d2d82a18 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1231,6 +1231,9 @@ axis_replacement_value(struct axis_replacement *axes, { struct axis_replacement *axis = axes; + if (!axes) + return false; + while (axis->evcode != -1) { if (axis->evcode == evcode) { *value = axis->value; @@ -1275,9 +1278,6 @@ litest_auto_assign_value(struct litest_device *d, break; default: value = -1; - if (!axes) - break; - if (!axis_replacement_value(axes, ev->code, &value) && d->interface->get_axis_default) d->interface->get_axis_default(d, ev->code, &value); diff --git a/test/touchpad-buttons.c b/test/touchpad-buttons.c index ccc3b88f..896b34b4 100644 --- a/test/touchpad-buttons.c +++ b/test/touchpad-buttons.c @@ -288,6 +288,7 @@ START_TEST(touchpad_4fg_clickfinger) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + struct libinput_event *event; if (libevdev_get_num_slots(dev->evdev) < 4) return; @@ -311,6 +312,18 @@ START_TEST(touchpad_4fg_clickfinger) libinput_dispatch(li); + litest_wait_for_event(li); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + libinput_event_destroy(event); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + libinput_event_destroy(event); + litest_assert_empty_queue(li); } END_TEST @@ -319,6 +332,7 @@ START_TEST(touchpad_4fg_clickfinger_btntool_2slots) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + struct libinput_event *event; if (libevdev_get_num_slots(dev->evdev) >= 3 || !libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_QUADTAP)) @@ -343,7 +357,17 @@ START_TEST(touchpad_4fg_clickfinger_btntool_2slots) litest_touch_up(dev, 0); litest_touch_up(dev, 1); - libinput_dispatch(li); + litest_wait_for_event(li); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + libinput_event_destroy(event); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + libinput_event_destroy(event); litest_assert_empty_queue(li); } @@ -353,8 +377,10 @@ START_TEST(touchpad_4fg_clickfinger_btntool_3slots) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + struct libinput_event *event; - if (libevdev_get_num_slots(dev->evdev) != 3 || + if (libevdev_get_num_slots(dev->evdev) >= 4 || + libevdev_get_num_slots(dev->evdev) < 3 || !libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_TRIPLETAP)) return; @@ -381,6 +407,18 @@ START_TEST(touchpad_4fg_clickfinger_btntool_3slots) libinput_dispatch(li); + litest_wait_for_event(li); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + libinput_event_destroy(event); + event = libinput_get_event(li); + litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + libinput_event_destroy(event); + litest_assert_empty_queue(li); } END_TEST @@ -682,6 +720,66 @@ START_TEST(touchpad_area_to_clickfinger_method_while_down) } END_TEST +START_TEST(touchpad_clickfinger_3fg_tool_position) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + enable_clickfinger(dev); + litest_drain_events(li); + + /* one in thumb area, one in normal area. spread is wide so the two + * real fingers don't count together. we expect a 2-finger click */ + litest_touch_down(dev, 0, 5, 99); + litest_touch_down(dev, 1, 90, 15); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0); + litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); +} +END_TEST + +START_TEST(touchpad_clickfinger_4fg_tool_position) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + enable_clickfinger(dev); + litest_drain_events(li); + + litest_touch_down(dev, 0, 5, 99); + litest_touch_down(dev, 1, 90, 15); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0); + litest_event(dev, EV_KEY, BTN_TOOL_QUADTAP, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); +} +END_TEST + START_TEST(touchpad_btn_left) { struct litest_device *dev = litest_current_device(); @@ -1422,6 +1520,10 @@ litest_setup_tests(void) litest_add("touchpad:clickfinger", touchpad_area_to_clickfinger_method, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_area_to_clickfinger_method_while_down, LITEST_CLICKPAD, LITEST_ANY); + /* run those two for the T440 one only so we don't have to worry + * about small touchpads messing with thumb detection expectations */ + litest_add_for_device("touchpad:clickfinger", touchpad_clickfinger_3fg_tool_position, LITEST_SYNAPTICS_TOPBUTTONPAD); + litest_add_for_device("touchpad:clickfinger", touchpad_clickfinger_4fg_tool_position, LITEST_SYNAPTICS_TOPBUTTONPAD); litest_add("touchpad:click", touchpad_click_defaults_clickfinger, LITEST_APPLE_CLICKPAD, LITEST_ANY); litest_add("touchpad:click", touchpad_click_defaults_btnarea, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); diff --git a/test/touchpad.c b/test/touchpad.c index 34ca80d0..b910d0ea 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2893,7 +2893,15 @@ END_TEST static int has_thumb_detect(struct litest_device *dev) { - return libevdev_has_event_code(dev->evdev, EV_ABS, ABS_MT_PRESSURE); + double w, h; + + if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_MT_PRESSURE)) + return 0; + + if (libinput_device_get_size(dev->libinput_device, &w, &h) != 0) + return 0; + + return h >= 50.0; } START_TEST(touchpad_thumb_begin_no_motion) @@ -2901,7 +2909,7 @@ START_TEST(touchpad_thumb_begin_no_motion) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -2912,8 +2920,8 @@ START_TEST(touchpad_thumb_begin_no_motion) litest_drain_events(li); - litest_touch_down_extended(dev, 0, 50, 50, axes); - litest_touch_move_to(dev, 0, 50, 50, 80, 50, 10, 0); + litest_touch_down_extended(dev, 0, 50, 99, axes); + litest_touch_move_to(dev, 0, 50, 99, 80, 99, 10, 0); litest_touch_up(dev, 0); litest_assert_empty_queue(li); @@ -2925,29 +2933,54 @@ START_TEST(touchpad_thumb_update_no_motion) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; litest_disable_tap(dev->libinput_device); + enable_clickfinger(dev); if (!has_thumb_detect(dev)) return; litest_drain_events(li); - litest_touch_down(dev, 0, 50, 50); - litest_touch_move_to(dev, 0, 50, 50, 60, 50, 10, 0); - litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); - - litest_touch_move_extended(dev, 0, 65, 50, axes); - litest_touch_move_to(dev, 0, 65, 50, 80, 50, 10, 0); + litest_touch_down(dev, 0, 59, 99); + litest_touch_move_extended(dev, 0, 59, 99, axes); + litest_touch_move_to(dev, 0, 60, 99, 80, 99, 10, 0); litest_touch_up(dev, 0); litest_assert_empty_queue(li); } END_TEST +START_TEST(touchpad_thumb_moving) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 190 }, + { -1, 0 } + }; + + litest_disable_tap(dev->libinput_device); + enable_clickfinger(dev); + + if (!has_thumb_detect(dev)) + return; + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 99); + litest_touch_move_to(dev, 0, 50, 99, 60, 99, 10, 0); + litest_touch_move_extended(dev, 0, 65, 99, axes); + litest_touch_move_to(dev, 0, 65, 99, 80, 99, 10, 0); + litest_touch_up(dev, 0); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); +} +END_TEST + START_TEST(touchpad_thumb_clickfinger) { struct litest_device *dev = litest_current_device(); @@ -2955,7 +2988,7 @@ START_TEST(touchpad_thumb_clickfinger) struct libinput_event *event; struct libinput_event_pointer *ptrev; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -2969,9 +3002,9 @@ START_TEST(touchpad_thumb_clickfinger) litest_drain_events(li); - litest_touch_down(dev, 0, 50, 50); - litest_touch_down(dev, 1, 60, 50); - litest_touch_move_extended(dev, 0, 55, 50, axes); + litest_touch_down(dev, 0, 50, 99); + litest_touch_down(dev, 1, 60, 99); + litest_touch_move_extended(dev, 0, 55, 99, axes); litest_button_click(dev, BTN_LEFT, true); libinput_dispatch(li); @@ -2989,9 +3022,9 @@ START_TEST(touchpad_thumb_clickfinger) litest_drain_events(li); - litest_touch_down(dev, 0, 50, 50); - litest_touch_down(dev, 1, 60, 50); - litest_touch_move_extended(dev, 1, 65, 50, axes); + litest_touch_down(dev, 0, 50, 99); + litest_touch_down(dev, 1, 60, 99); + litest_touch_move_extended(dev, 1, 65, 99, axes); litest_button_click(dev, BTN_LEFT, true); libinput_dispatch(li); @@ -3012,7 +3045,7 @@ START_TEST(touchpad_thumb_btnarea) struct libinput_event *event; struct libinput_event_pointer *ptrev; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -3026,8 +3059,8 @@ START_TEST(touchpad_thumb_btnarea) litest_drain_events(li); - litest_touch_down(dev, 0, 90, 95); - litest_touch_move_extended(dev, 0, 95, 95, axes); + litest_touch_down(dev, 0, 90, 99); + litest_touch_move_extended(dev, 0, 95, 99, axes); litest_button_click(dev, BTN_LEFT, true); /* button areas work as usual with a thumb */ @@ -3048,7 +3081,7 @@ START_TEST(touchpad_thumb_edgescroll) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -3079,7 +3112,7 @@ START_TEST(touchpad_thumb_tap_begin) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -3087,17 +3120,18 @@ START_TEST(touchpad_thumb_tap_begin) return; litest_enable_tap(dev->libinput_device); + enable_clickfinger(dev); litest_drain_events(li); /* touch down is a thumb */ - litest_touch_down_extended(dev, 0, 50, 50, axes); + litest_touch_down_extended(dev, 0, 50, 99, axes); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_empty_queue(li); /* make sure normal tap still works */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -3109,7 +3143,7 @@ START_TEST(touchpad_thumb_tap_touch) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -3117,17 +3151,18 @@ START_TEST(touchpad_thumb_tap_touch) return; litest_enable_tap(dev->libinput_device); + enable_clickfinger(dev); litest_drain_events(li); /* event after touch down is thumb */ litest_touch_down(dev, 0, 50, 50); - litest_touch_move_extended(dev, 0, 51, 50, axes); + litest_touch_move_extended(dev, 0, 51, 99, axes); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_empty_queue(li); /* make sure normal tap still works */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -3139,7 +3174,7 @@ START_TEST(touchpad_thumb_tap_hold) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -3147,18 +3182,19 @@ START_TEST(touchpad_thumb_tap_hold) return; litest_enable_tap(dev->libinput_device); + enable_clickfinger(dev); litest_drain_events(li); /* event in state HOLD is thumb */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_timeout_tap(); libinput_dispatch(li); - litest_touch_move_extended(dev, 0, 51, 50, axes); + litest_touch_move_extended(dev, 0, 51, 99, axes); litest_touch_up(dev, 0); litest_assert_empty_queue(li); /* make sure normal tap still works */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -3170,7 +3206,7 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -3178,13 +3214,14 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg) return; litest_enable_tap(dev->libinput_device); + enable_clickfinger(dev); litest_drain_events(li); /* event in state HOLD is thumb */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_timeout_tap(); libinput_dispatch(li); - litest_touch_move_extended(dev, 0, 51, 50, axes); + litest_touch_move_extended(dev, 0, 51, 99, axes); litest_assert_empty_queue(li); @@ -3203,7 +3240,7 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg) litest_assert_empty_queue(li); /* make sure normal tap still works */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -3217,7 +3254,7 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg_tap) struct libinput_event *event; struct libinput_event_pointer *ptrev; struct axis_replacement axes[] = { - { ABS_MT_PRESSURE, 100 }, + { ABS_MT_PRESSURE, 190 }, { -1, 0 } }; @@ -3228,10 +3265,10 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg_tap) litest_drain_events(li); /* event in state HOLD is thumb */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_timeout_tap(); libinput_dispatch(li); - litest_touch_move_extended(dev, 0, 51, 50, axes); + litest_touch_move_extended(dev, 0, 51, 99, axes); litest_assert_empty_queue(li); @@ -3261,7 +3298,7 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg_tap) libinput_event_destroy(libinput_event_pointer_get_base_event(ptrev)); /* make sure normal tap still works */ - litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 0, 50, 99); litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -3473,16 +3510,17 @@ litest_setup_tests(void) litest_add("touchpad:dwt", touchpad_dwt_enable_before_touch, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:dwt", touchpad_dwt_enable_during_tap, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_begin_no_motion, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_update_no_motion, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_begin_no_motion, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_update_no_motion, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_moving, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_clickfinger, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_btnarea, LITEST_CLICKPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_edgescroll, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_tap_begin, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_tap_touch, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_tap_hold, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:thumb", touchpad_thumb_tap_hold_2ndfg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); - litest_add("touchpad:thumb", touchpad_thumb_tap_hold_2ndfg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + litest_add("touchpad:thumb", touchpad_thumb_edgescroll, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_tap_begin, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_tap_touch, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_tap_hold, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:thumb", touchpad_thumb_tap_hold_2ndfg, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH); + litest_add("touchpad:thumb", touchpad_thumb_tap_hold_2ndfg_tap, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH); litest_add_for_device("touchpad:bugs", touchpad_tool_tripletap_touch_count, LITEST_SYNAPTICS_TOPBUTTONPAD); }