From: José Expósito Date: Thu, 27 May 2021 17:20:37 +0000 (+0200) Subject: gestures: add quick hold implementation X-Git-Tag: 1.18.901~57 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9b024c6928686f41aea3d59a04cd80a581bcc857;p=platform%2Fupstream%2Flibinput.git gestures: add quick hold implementation When 1 or 2 fingers are used to hold, use a faster timer to make the "hold to stop kinetic scrolling" user interaction feel more immediate. Also handle double tap and tap and drag interations to send only one hold gesture instead of two. Holding with 3 or 4 fingers remains the same to try to avoid callers missusing hold gestures to build their own tap implementation. Signed-off-by: José Expósito --- diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 0d5fcedd..4202802e 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -28,6 +28,7 @@ #include "evdev-mt-touchpad.h" +#define QUICK_GESTURE_HOLD_TIMEOUT ms2us(40) #define DEFAULT_GESTURE_HOLD_TIMEOUT ms2us(180) #define DEFAULT_GESTURE_SWITCH_TIMEOUT ms2us(100) #define DEFAULT_GESTURE_SWIPE_TIMEOUT ms2us(150) @@ -476,6 +477,17 @@ log_gesture_bug(struct tp_dispatch *tp, enum gesture_event event) gesture_state_to_str(tp->gesture.state)); } +static bool +tp_gesture_is_quick_hold(struct tp_dispatch *tp) +{ + /* When 1 or 2 fingers are used to hold, always use a "quick" hold to + * make the hold to stop kinetic scrolling user interaction feel more + * natural. + */ + return (tp->gesture.finger_count == 1) || + (tp->gesture.finger_count == 2); +} + static bool tp_gesture_use_hold_timer(struct tp_dispatch *tp) { @@ -483,6 +495,10 @@ tp_gesture_use_hold_timer(struct tp_dispatch *tp) if (!tp->tap.enabled) return true; + /* Always use the timer if it is a quick hold */ + if (tp_gesture_is_quick_hold(tp)) + return true; + /* If the number of fingers on the touchpad exceeds the number of * allowed fingers to tap, use the timer. */ @@ -510,12 +526,17 @@ tp_gesture_use_hold_timer(struct tp_dispatch *tp) static void tp_gesture_set_hold_timer(struct tp_dispatch *tp, uint64_t time) { + uint64_t timeout; + if (!tp->gesture.hold_enabled) return; if (tp_gesture_use_hold_timer(tp)) { - libinput_timer_set(&tp->gesture.hold_timer, - time + DEFAULT_GESTURE_HOLD_TIMEOUT); + timeout = tp_gesture_is_quick_hold(tp) ? + QUICK_GESTURE_HOLD_TIMEOUT : + DEFAULT_GESTURE_HOLD_TIMEOUT; + + libinput_timer_set(&tp->gesture.hold_timer, time + timeout); } } @@ -750,6 +771,10 @@ static void tp_gesture_hold_timeout(uint64_t now, void *data) { struct tp_dispatch *tp = data; + + if (tp_tap_dragging_or_double_tapping(tp) || tp_tap_dragging(tp)) + return; + tp_gesture_handle_event(tp, GESTURE_EVENT_HOLD_TIMEOUT, now); } @@ -759,7 +784,8 @@ tp_gesture_tap_timeout(struct tp_dispatch *tp, uint64_t time) if (!tp->gesture.hold_enabled) return; - tp_gesture_handle_event(tp, GESTURE_EVENT_HOLD_TIMEOUT, time); + if (!tp_gesture_is_quick_hold(tp)) + tp_gesture_handle_event(tp, GESTURE_EVENT_HOLD_TIMEOUT, time); } static void @@ -1263,6 +1289,13 @@ tp_gesture_cancel(struct tp_dispatch *tp, uint64_t time) tp_gesture_end(tp, time, true); } +void +tp_gesture_cancel_motion_gestures(struct tp_dispatch *tp, uint64_t time) +{ + if (tp->gesture.started && tp->gesture.state != GESTURE_STATE_HOLD) + tp_gesture_end(tp, time, true); +} + void tp_gesture_stop(struct tp_dispatch *tp, uint64_t time) { diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index cbd51036..7c4349a6 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -1656,3 +1656,16 @@ tp_tap_dragging(const struct tp_dispatch *tp) return false; } } + +bool +tp_tap_dragging_or_double_tapping(const struct tp_dispatch *tp) +{ + switch (tp->tap.state) { + case TAP_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP: + case TAP_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP: + case TAP_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP: + return true; + default: + return false; + } +} diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index dbb8fd95..c97452a8 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1874,7 +1874,7 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time) if (ignore_motion) { tp_edge_scroll_stop_events(tp, time); - tp_gesture_cancel(tp, time); + tp_gesture_cancel_motion_gestures(tp, time); tp_gesture_post_events(tp, time, true); return; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 8286085f..c44c7514 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -669,6 +669,9 @@ tp_tap_resume(struct tp_dispatch *tp, uint64_t time); bool tp_tap_dragging(const struct tp_dispatch *tp); +bool +tp_tap_dragging_or_double_tapping(const struct tp_dispatch *tp); + void tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device); @@ -703,6 +706,9 @@ tp_gesture_stop(struct tp_dispatch *tp, uint64_t time); void tp_gesture_cancel(struct tp_dispatch *tp, uint64_t time); +void +tp_gesture_cancel_motion_gestures(struct tp_dispatch *tp, uint64_t time); + void tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time); diff --git a/test/litest.c b/test/litest.c index 120cbd7a..344d1749 100644 --- a/test/litest.c +++ b/test/litest.c @@ -4149,6 +4149,12 @@ litest_timeout_gesture_hold(void) msleep(300); } +void +litest_timeout_gesture_quick_hold(void) +{ + msleep(60); +} + void litest_timeout_trackpoint(void) { diff --git a/test/litest.h b/test/litest.h index e4e69bb7..888fb4a6 100644 --- a/test/litest.h +++ b/test/litest.h @@ -896,6 +896,9 @@ litest_timeout_gesture_scroll(void); void litest_timeout_gesture_hold(void); +void +litest_timeout_gesture_quick_hold(void); + void litest_timeout_trackpoint(void); diff --git a/test/test-gestures.c b/test/test-gestures.c index e488aab2..7f8701fd 100644 --- a/test/test-gestures.c +++ b/test/test-gestures.c @@ -1574,10 +1574,144 @@ START_TEST(gestures_hold_then_3fg_buttonarea_scroll) } END_TEST +START_TEST(gestures_hold_once_on_double_tap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (!libinput_device_has_capability(dev->libinput_device, + LIBINPUT_DEVICE_CAP_GESTURE)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* First tap, a hold gesture must be generated */ + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); + litest_timeout_gesture_quick_hold(); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_gesture_event(li, + LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, + 1); + litest_assert_gesture_event(li, + LIBINPUT_EVENT_GESTURE_HOLD_END, + 1); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); + + /* Double tap, don't generate an extra hold gesture */ + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + litest_timeout_gesture_quick_hold(); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(gestures_hold_once_tap_n_drag) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + int nfingers = _i; /* ranged test */ + unsigned int button = 0; + + if (nfingers > litest_slot_count(dev)) + return; + + if (!libinput_device_has_capability(dev->libinput_device, + LIBINPUT_DEVICE_CAP_GESTURE)) + return; + + litest_enable_tap(dev->libinput_device); + litest_disable_drag_lock(dev->libinput_device); + litest_drain_events(li); + + switch (nfingers) { + case 1: + button = BTN_LEFT; + break; + case 2: + button = BTN_RIGHT; + break; + case 3: + button = BTN_MIDDLE; + break; + default: + abort(); + } + + switch (nfingers) { + case 3: + litest_touch_down(dev, 2, 60, 30); + /* fallthrough */ + case 2: + litest_touch_down(dev, 1, 50, 30); + /* fallthrough */ + case 1: + litest_touch_down(dev, 0, 40, 30); + break; + } + libinput_dispatch(li); + litest_timeout_gesture_quick_hold(); + + switch (nfingers) { + case 3: + litest_touch_up(dev, 2); + /* fallthrough */ + case 2: + litest_touch_up(dev, 1); + /* fallthrough */ + case 1: + litest_touch_up(dev, 0); + break; + } + libinput_dispatch(li); + + /* "Quick" hold gestures are only generated when using 1 or 2 fingers */ + if (nfingers == 1 || nfingers == 2) { + litest_assert_gesture_event(li, + LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, + nfingers); + litest_assert_gesture_event(li, + LIBINPUT_EVENT_GESTURE_HOLD_END, + nfingers); + } + + /* Tap and drag, don't generate an extra hold gesture */ + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 80, 80, 20); + libinput_dispatch(li); + + litest_assert_button_event(li, button, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, button, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); +} +END_TEST + TEST_COLLECTION(gestures) { struct range cardinals = { N, N + NCARDINALS }; struct range range_hold = { 1, 5 }; + struct range range_multifinger_tap = {1, 4}; litest_add(gestures_cap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add(gestures_nocap, LITEST_ANY, LITEST_TOUCHPAD); @@ -1615,6 +1749,9 @@ TEST_COLLECTION(gestures) litest_add_ranged(gestures_hold_then_spread, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals); litest_add(gestures_hold_then_3fg_buttonarea_scroll, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH); + litest_add(gestures_hold_once_on_double_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + litest_add_ranged(gestures_hold_once_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY, &range_multifinger_tap); + /* Timing-sensitive test, valgrind is too slow */ if (!RUNNING_ON_VALGRIND) litest_add(gestures_swipe_3fg_unaccel, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);