diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 7478f8d9..50956207 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -38,9 +38,11 @@ enum gesture_cancelled { #define DEFAULT_GESTURE_SWITCH_TIMEOUT usec_from_millis(100) #define DEFAULT_GESTURE_SWIPE_TIMEOUT usec_from_millis(150) #define DEFAULT_GESTURE_PINCH_TIMEOUT usec_from_millis(300) +#define DRAG_3FG_OR_SWIPE_TIMEOUT usec_from_millis(80) #define HOLD_AND_MOTION_THRESHOLD 0.5 /* mm */ #define PINCH_DISAMBIGUATION_MOVE_THRESHOLD 1.5 /* mm */ +#define DRAG_3FG_OR_SWIPE_MOVE_THRESHOLD 5 /* mm */ enum gesture_event { GESTURE_EVENT_RESET, @@ -55,7 +57,8 @@ enum gesture_event { GESTURE_EVENT_SCROLL_START, GESTURE_EVENT_SWIPE_START, GESTURE_EVENT_PINCH_START, - GESTURE_EVENT_3FG_DRAG_START, + GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START, + GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT, GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT, }; @@ -83,6 +86,8 @@ gesture_state_to_str(enum tp_gesture_state state) CASE_RETURN_STRING(GESTURE_STATE_PINCH); CASE_RETURN_STRING(GESTURE_STATE_SWIPE_START); CASE_RETURN_STRING(GESTURE_STATE_SWIPE); + CASE_RETURN_STRING(GESTURE_STATE_3FG_DRAG_OR_SWIPE_START); + CASE_RETURN_STRING(GESTURE_STATE_3FG_DRAG_OR_SWIPE); CASE_RETURN_STRING(GESTURE_STATE_3FG_DRAG_START); CASE_RETURN_STRING(GESTURE_STATE_3FG_DRAG); CASE_RETURN_STRING(GESTURE_STATE_3FG_DRAG_RELEASED); @@ -106,7 +111,8 @@ gesture_event_to_str(enum gesture_event event) CASE_RETURN_STRING(GESTURE_EVENT_SCROLL_START); CASE_RETURN_STRING(GESTURE_EVENT_SWIPE_START); CASE_RETURN_STRING(GESTURE_EVENT_PINCH_START); - CASE_RETURN_STRING(GESTURE_EVENT_3FG_DRAG_START); + CASE_RETURN_STRING(GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START); + CASE_RETURN_STRING(GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT); CASE_RETURN_STRING(GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT); } return NULL; @@ -315,16 +321,6 @@ tp_gesture_init_pinch(struct tp_dispatch *tp) tp->gesture.prev_scale = 1.0; } -static inline void -tp_gesture_init_3fg_drag(struct tp_dispatch *tp, usec_t time) -{ -} - -static inline void -tp_gesture_stop_3fg_drag(struct tp_dispatch *tp, usec_t time) -{ -} - static void tp_gesture_set_scroll_buildup(struct tp_dispatch *tp) { @@ -559,7 +555,7 @@ tp_gesture_handle_event_on_state_none(struct tp_dispatch *tp, */ if (!tp->tap.enabled && tp->drag_3fg.nfingers == tp->gesture.finger_count) { - tp->gesture.state = GESTURE_STATE_3FG_DRAG_START; + tp->gesture.state = GESTURE_STATE_3FG_DRAG_OR_SWIPE_START; } else { tp_gesture_set_hold_timer(tp, time); tp->gesture.state = GESTURE_STATE_UNKNOWN; @@ -578,13 +574,21 @@ tp_gesture_handle_event_on_state_none(struct tp_dispatch *tp, case GESTURE_EVENT_HOLD_AND_MOTION_START: case GESTURE_EVENT_SWIPE_START: case GESTURE_EVENT_PINCH_START: - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; } } +static void +tp_gesture_set_3fg_drag_3fg_or_swipe_timer(struct tp_dispatch *tp, usec_t time) +{ + libinput_timer_set(&tp->gesture.drag_3fg_or_swipe_timer, + usec_add(time, DRAG_3FG_OR_SWIPE_TIMEOUT)); +} + static void tp_gesture_handle_event_on_state_unknown(struct tp_dispatch *tp, enum gesture_event event, @@ -625,13 +629,13 @@ tp_gesture_handle_event_on_state_unknown(struct tp_dispatch *tp, tp_gesture_init_pinch(tp); tp->gesture.state = GESTURE_STATE_PINCH_START; break; - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: libinput_timer_cancel(&tp->gesture.hold_timer); - tp_gesture_init_3fg_drag(tp, time); - tp->gesture.state = GESTURE_STATE_3FG_DRAG_START; + tp->gesture.state = GESTURE_STATE_3FG_DRAG_OR_SWIPE_START; break; case GESTURE_EVENT_HOLD_AND_MOTION_START: case GESTURE_EVENT_FINGER_DETECTED: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; @@ -682,14 +686,15 @@ tp_gesture_handle_event_on_state_hold(struct tp_dispatch *tp, tp_gesture_init_pinch(tp); tp->gesture.state = GESTURE_STATE_PINCH_START; break; - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + libinput_timer_cancel(&tp->gesture.hold_timer); tp_gesture_cancel(tp, time); - tp_gesture_init_3fg_drag(tp, time); - tp->gesture.state = GESTURE_STATE_3FG_DRAG_START; + tp->gesture.state = GESTURE_STATE_3FG_DRAG_OR_SWIPE_START; break; case GESTURE_EVENT_HOLD_TIMEOUT: case GESTURE_EVENT_TAP_TIMEOUT: case GESTURE_EVENT_FINGER_DETECTED: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; @@ -730,7 +735,8 @@ tp_gesture_handle_event_on_state_hold_and_motion(struct tp_dispatch *tp, case GESTURE_EVENT_SCROLL_START: case GESTURE_EVENT_SWIPE_START: case GESTURE_EVENT_PINCH_START: - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; @@ -776,7 +782,8 @@ tp_gesture_handle_event_on_state_pointer_motion(struct tp_dispatch *tp, case GESTURE_EVENT_SCROLL_START: case GESTURE_EVENT_SWIPE_START: case GESTURE_EVENT_PINCH_START: - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; @@ -809,7 +816,8 @@ tp_gesture_handle_event_on_state_scroll_start(struct tp_dispatch *tp, case GESTURE_EVENT_POINTER_MOTION_START: case GESTURE_EVENT_SCROLL_START: case GESTURE_EVENT_SWIPE_START: - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; @@ -844,7 +852,8 @@ tp_gesture_handle_event_on_state_scroll(struct tp_dispatch *tp, case GESTURE_EVENT_POINTER_MOTION_START: case GESTURE_EVENT_SCROLL_START: case GESTURE_EVENT_SWIPE_START: - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; @@ -872,7 +881,8 @@ tp_gesture_handle_event_on_state_pinch_start(struct tp_dispatch *tp, case GESTURE_EVENT_SCROLL_START: case GESTURE_EVENT_SWIPE_START: case GESTURE_EVENT_PINCH_START: - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; @@ -911,7 +921,8 @@ tp_gesture_handle_event_on_state_pinch(struct tp_dispatch *tp, case GESTURE_EVENT_SCROLL_START: case GESTURE_EVENT_SWIPE_START: case GESTURE_EVENT_PINCH_START: - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; @@ -940,7 +951,8 @@ tp_gesture_handle_event_on_state_swipe_start(struct tp_dispatch *tp, case GESTURE_EVENT_SCROLL_START: case GESTURE_EVENT_SWIPE_START: case GESTURE_EVENT_PINCH_START: - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; @@ -978,13 +990,101 @@ tp_gesture_handle_event_on_state_swipe(struct tp_dispatch *tp, case GESTURE_EVENT_SCROLL_START: case GESTURE_EVENT_SWIPE_START: case GESTURE_EVENT_PINCH_START: - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; } } +static void +tp_gesture_handle_event_on_state_3fg_drag_or_swipe_start(struct tp_dispatch *tp, + enum gesture_event event, + usec_t time) +{ + switch (event) { + case GESTURE_EVENT_RESET: + case GESTURE_EVENT_END: + case GESTURE_EVENT_CANCEL: + libinput_timer_cancel(&tp->gesture.hold_timer); + tp->gesture.state = GESTURE_STATE_NONE; + break; + case GESTURE_EVENT_FINGER_SWITCH_TIMEOUT: + break; + case GESTURE_EVENT_HOLD_AND_MOTION_START: + case GESTURE_EVENT_FINGER_DETECTED: + case GESTURE_EVENT_TAP_TIMEOUT: + case GESTURE_EVENT_HOLD_TIMEOUT: + case GESTURE_EVENT_POINTER_MOTION_START: + case GESTURE_EVENT_SCROLL_START: + case GESTURE_EVENT_SWIPE_START: + case GESTURE_EVENT_PINCH_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: + case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: + log_gesture_bug(tp, event); + break; + } +} + +static void +tp_gesture_handle_event_on_state_3fg_drag_or_swipe(struct tp_dispatch *tp, + enum gesture_event event, + usec_t time) +{ + struct tp_touch *first = tp->gesture.touches[0], + *second = tp->gesture.touches[1]; + struct phys_coords first_moved, second_moved; + double first_mm, second_mm; + + switch (event) { + case GESTURE_EVENT_RESET: + libinput_timer_cancel(&tp->gesture.hold_timer); + tp->gesture.state = GESTURE_STATE_NONE; + break; + case GESTURE_EVENT_END: + case GESTURE_EVENT_CANCEL: { + bool cancelled = event == GESTURE_EVENT_CANCEL; + gesture_notify_swipe_end(&tp->device->base, + time, + tp->gesture.finger_count, + cancelled); + tp->gesture.state = GESTURE_STATE_NONE; + break; + } + case GESTURE_EVENT_FINGER_SWITCH_TIMEOUT: + break; + case GESTURE_EVENT_HOLD_AND_MOTION_START: + case GESTURE_EVENT_FINGER_DETECTED: + case GESTURE_EVENT_TAP_TIMEOUT: + case GESTURE_EVENT_HOLD_TIMEOUT: + case GESTURE_EVENT_POINTER_MOTION_START: + case GESTURE_EVENT_SCROLL_START: + case GESTURE_EVENT_SWIPE_START: + case GESTURE_EVENT_PINCH_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: + log_gesture_bug(tp, event); + break; + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: + libinput_timer_cancel(&tp->gesture.drag_3fg_or_swipe_timer); + + first_moved = tp_gesture_mm_moved(tp, first); + second_moved = tp_gesture_mm_moved(tp, second); + first_mm = hypot(first_moved.x, first_moved.y); + second_mm = hypot(second_moved.x, second_moved.y); + if ((first_mm + second_mm) / 2.0 >= DRAG_3FG_OR_SWIPE_MOVE_THRESHOLD) { + tp->gesture.state = GESTURE_STATE_SWIPE; + } else { + /* Cancel the swipe */ + tp_gesture_cancel(tp, time); + tp->gesture.state = GESTURE_STATE_3FG_DRAG_START; + } + break; + } +} + static void tp_gesture_handle_event_on_state_3fg_drag_start(struct tp_dispatch *tp, enum gesture_event event, @@ -1007,7 +1107,8 @@ tp_gesture_handle_event_on_state_3fg_drag_start(struct tp_dispatch *tp, case GESTURE_EVENT_SCROLL_START: case GESTURE_EVENT_SWIPE_START: case GESTURE_EVENT_PINCH_START: - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; @@ -1062,7 +1163,8 @@ tp_gesture_handle_event_on_state_3fg_drag(struct tp_dispatch *tp, case GESTURE_EVENT_SCROLL_START: case GESTURE_EVENT_SWIPE_START: case GESTURE_EVENT_PINCH_START: - case GESTURE_EVENT_3FG_DRAG_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: log_gesture_bug(tp, event); break; @@ -1081,7 +1183,6 @@ tp_gesture_handle_event_on_state_3fg_drag_released(struct tp_dispatch *tp, case GESTURE_EVENT_END: case GESTURE_EVENT_CANCEL: case GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT: - tp_gesture_stop_3fg_drag(tp, time); libinput_timer_cancel(&tp->gesture.drag_3fg_timer); libinput_timer_cancel(&tp->gesture.finger_count_switch_timer); evdev_pointer_notify_button(tp->device, @@ -1100,7 +1201,6 @@ tp_gesture_handle_event_on_state_3fg_drag_released(struct tp_dispatch *tp, case GESTURE_EVENT_FINGER_DETECTED: break; case GESTURE_EVENT_POINTER_MOTION_START: - tp_gesture_stop_3fg_drag(tp, time); libinput_timer_cancel(&tp->gesture.drag_3fg_timer); evdev_pointer_notify_button(tp->device, tp->gesture.drag_3fg_release_time, @@ -1124,10 +1224,13 @@ tp_gesture_handle_event_on_state_3fg_drag_released(struct tp_dispatch *tp, break; case GESTURE_EVENT_SWIPE_START: case GESTURE_EVENT_PINCH_START: - case GESTURE_EVENT_3FG_DRAG_START: libinput_timer_cancel(&tp->gesture.drag_3fg_timer); tp->gesture.state = GESTURE_STATE_3FG_DRAG; break; + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START: + case GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT: + log_gesture_bug(tp, event); + break; } } @@ -1172,6 +1275,14 @@ tp_gesture_handle_event(struct tp_dispatch *tp, enum gesture_event event, usec_t case GESTURE_STATE_SWIPE: tp_gesture_handle_event_on_state_swipe(tp, event, time); break; + case GESTURE_STATE_3FG_DRAG_OR_SWIPE_START: + tp_gesture_handle_event_on_state_3fg_drag_or_swipe_start(tp, + event, + time); + break; + case GESTURE_STATE_3FG_DRAG_OR_SWIPE: + tp_gesture_handle_event_on_state_3fg_drag_or_swipe(tp, event, time); + break; case GESTURE_STATE_3FG_DRAG_START: tp_gesture_handle_event_on_state_3fg_drag_start(tp, event, time); break; @@ -1222,6 +1333,14 @@ tp_gesture_3fg_drag_timeout(usec_t now, void *data) tp_gesture_handle_event(tp, GESTURE_EVENT_3FG_DRAG_RELEASE_TIMEOUT, now); } +static void +tp_gesture_3fg_drag_or_swipe_timeout(usec_t now, void *data) +{ + struct tp_dispatch *tp = data; + + tp_gesture_handle_event(tp, GESTURE_EVENT_3FG_DRAG_OR_SWIPE_TIMEOUT, now); +} + static void tp_gesture_detect_motion_gestures(struct tp_dispatch *tp, usec_t time) { @@ -1264,7 +1383,9 @@ tp_gesture_detect_motion_gestures(struct tp_dispatch *tp, usec_t time) if (tp->gesture.enabled && tp->gesture.finger_count > 2 && tp->gesture.finger_count > tp->num_slots) { if (tp->drag_3fg.nfingers == tp->gesture.finger_count) - tp_gesture_handle_event(tp, GESTURE_EVENT_3FG_DRAG_START, time); + tp_gesture_handle_event(tp, + GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START, + time); else tp_gesture_handle_event(tp, GESTURE_EVENT_SWIPE_START, time); return; @@ -1294,7 +1415,9 @@ tp_gesture_detect_motion_gestures(struct tp_dispatch *tp, usec_t time) if (tp->gesture.finger_count == 2) tp_gesture_handle_event(tp, GESTURE_EVENT_SCROLL_START, time); else if (tp->drag_3fg.nfingers == tp->gesture.finger_count) - tp_gesture_handle_event(tp, GESTURE_EVENT_3FG_DRAG_START, time); + tp_gesture_handle_event(tp, + GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START, + time); else tp_gesture_handle_event(tp, GESTURE_EVENT_SWIPE_START, time); @@ -1302,10 +1425,12 @@ tp_gesture_detect_motion_gestures(struct tp_dispatch *tp, usec_t time) } /* If 3fg dragging touches are within a 60x10mm box, start - * dragging immediately */ + * dragging (or swiping) immediately */ if (tp->gesture.finger_count == tp->drag_3fg.nfingers && distance_mm.x < 60.0 && distance_mm.y < 10.0) { - tp_gesture_handle_event(tp, GESTURE_EVENT_3FG_DRAG_START, time); + tp_gesture_handle_event(tp, + GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START, + time); return; } @@ -1383,7 +1508,9 @@ tp_gesture_detect_motion_gestures(struct tp_dispatch *tp, usec_t time) } if (tp->drag_3fg.nfingers == tp->gesture.finger_count) { - tp_gesture_handle_event(tp, GESTURE_EVENT_3FG_DRAG_START, time); + tp_gesture_handle_event(tp, + GESTURE_EVENT_3FG_DRAG_OR_SWIPE_START, + time); return; } @@ -1738,11 +1865,45 @@ tp_gesture_handle_state_3fg_drag_released(struct tp_dispatch *tp, tp_gesture_detect_motion_gestures(tp, time); } +static void +tp_gesture_handle_state_3fg_drag_or_swipe(struct tp_dispatch *tp, usec_t time) +{ + struct device_float_coords raw; + struct normalized_coords delta, unaccel; + + raw = tp_get_average_touches_delta(tp); + delta = tp_filter_motion(tp, &raw, time); + + if (!normalized_is_zero(delta) || !device_float_is_zero(raw)) { + unaccel = tp_filter_motion_unaccelerated(tp, &raw, time); + gesture_notify_swipe(&tp->device->base, + time, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, + tp->gesture.finger_count, + &delta, + &unaccel); + } +} + +static void +tp_gesture_handle_state_3fg_drag_or_swipe_start(struct tp_dispatch *tp, usec_t time) +{ + const struct normalized_coords zero = { 0.0, 0.0 }; + gesture_notify_swipe(&tp->device->base, + time, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + tp->gesture.finger_count, + &zero, + &zero); + tp->gesture.state = GESTURE_STATE_3FG_DRAG_OR_SWIPE; + tp_gesture_set_3fg_drag_3fg_or_swipe_timer(tp, time); +} + static void tp_gesture_handle_state(struct tp_dispatch *tp, usec_t time, bool ignore_motion) { enum tp_gesture_state oldstate = tp->gesture.state; - enum tp_gesture_state transitions[16] = { 0 }; + enum tp_gesture_state transitions[18] = { 0 }; enum tp_gesture_state *transition_state = transitions; #define REMEMBER_TRANSITION(_ts, _state) { \ @@ -1810,6 +1971,14 @@ tp_gesture_handle_state(struct tp_dispatch *tp, usec_t time, bool ignore_motion) tp_gesture_handle_state_3fg_drag_released(tp, time, ignore_motion); REMEMBER_TRANSITION(transition_state, tp->gesture.state); } + if (tp->gesture.state == GESTURE_STATE_3FG_DRAG_OR_SWIPE) { + tp_gesture_handle_state_3fg_drag_or_swipe(tp, time); + REMEMBER_TRANSITION(transition_state, tp->gesture.state); + } + if (tp->gesture.state == GESTURE_STATE_3FG_DRAG_OR_SWIPE_START) { + tp_gesture_handle_state_3fg_drag_or_swipe_start(tp, time); + REMEMBER_TRANSITION(transition_state, tp->gesture.state); + } #undef REMEMBER_TRANSITION @@ -1907,6 +2076,7 @@ tp_gesture_end(struct tp_dispatch *tp, usec_t time, enum gesture_cancelled cance case GESTURE_STATE_PINCH_START: case GESTURE_STATE_SWIPE_START: case GESTURE_STATE_3FG_DRAG_START: + case GESTURE_STATE_3FG_DRAG_OR_SWIPE_START: tp_gesture_handle_event(tp, GESTURE_EVENT_RESET, time); break; case GESTURE_STATE_HOLD: @@ -1917,6 +2087,7 @@ tp_gesture_end(struct tp_dispatch *tp, usec_t time, enum gesture_cancelled cance case GESTURE_STATE_SWIPE: case GESTURE_STATE_3FG_DRAG: case GESTURE_STATE_3FG_DRAG_RELEASED: + case GESTURE_STATE_3FG_DRAG_OR_SWIPE: switch (cancelled) { case CANCEL_GESTURE: tp_gesture_handle_event(tp, GESTURE_EVENT_CANCEL, time); @@ -1961,6 +2132,11 @@ tp_gesture_cancel_motion_gestures(struct tp_dispatch *tp, usec_t time) break; case GESTURE_STATE_3FG_DRAG_RELEASED: break; + case GESTURE_STATE_3FG_DRAG_OR_SWIPE: + case GESTURE_STATE_3FG_DRAG_OR_SWIPE_START: + evdev_log_debug(tp->device, "Cancelling motion gestures\n"); + tp_gesture_cancel(tp, time); + break; } } @@ -2002,6 +2178,8 @@ tp_gesture_debounce_finger_changes(struct tp_dispatch *tp) case GESTURE_STATE_3FG_DRAG_START: case GESTURE_STATE_3FG_DRAG_RELEASED: case GESTURE_STATE_3FG_DRAG: + case GESTURE_STATE_3FG_DRAG_OR_SWIPE: + case GESTURE_STATE_3FG_DRAG_OR_SWIPE_START: return true; } @@ -2244,6 +2422,15 @@ tp_init_gesture(struct tp_dispatch *tp) timer_name, tp_gesture_3fg_drag_timeout, tp); + snprintf(timer_name, + sizeof(timer_name), + "%s drag_or_swipe", + evdev_device_get_sysname(tp->device)); + libinput_timer_init(&tp->gesture.drag_3fg_or_swipe_timer, + tp_libinput_context(tp), + timer_name, + tp_gesture_3fg_drag_or_swipe_timeout, + tp); } void @@ -2252,4 +2439,5 @@ tp_remove_gesture(struct tp_dispatch *tp) libinput_timer_cancel(&tp->gesture.finger_count_switch_timer); libinput_timer_cancel(&tp->gesture.hold_timer); libinput_timer_cancel(&tp->gesture.drag_3fg_timer); + libinput_timer_cancel(&tp->gesture.drag_3fg_or_swipe_timer); } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index e1090ff4..3db9eea4 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1975,6 +1975,7 @@ tp_interface_destroy(struct evdev_dispatch *dispatch) libinput_timer_destroy(&tp->gesture.finger_count_switch_timer); libinput_timer_destroy(&tp->gesture.hold_timer); libinput_timer_destroy(&tp->gesture.drag_3fg_timer); + libinput_timer_destroy(&tp->gesture.drag_3fg_or_swipe_timer); free(tp->touches); free(tp); } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index b9c7aa7c..7e85baf7 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -166,6 +166,8 @@ enum tp_gesture_state { GESTURE_STATE_PINCH, GESTURE_STATE_SWIPE_START, GESTURE_STATE_SWIPE, + GESTURE_STATE_3FG_DRAG_OR_SWIPE_START, + GESTURE_STATE_3FG_DRAG_OR_SWIPE, GESTURE_STATE_3FG_DRAG_START, GESTURE_STATE_3FG_DRAG, GESTURE_STATE_3FG_DRAG_RELEASED, @@ -371,6 +373,8 @@ struct tp_dispatch { struct libinput_timer drag_3fg_timer; usec_t drag_3fg_release_time; + + struct libinput_timer drag_3fg_or_swipe_timer; } gesture; struct { diff --git a/test/litest.h b/test/litest.h index 2f2ebfb0..6d5faec0 100644 --- a/test/litest.h +++ b/test/litest.h @@ -1371,6 +1371,7 @@ _litest_timeout(struct libinput *li, const char *func, int lineno, int millis); #define litest_timeout_tablet_proxout(li_) litest_timeout(li_, 170) #define litest_timeout_touch_arbitration(li_) litest_timeout(li_, 100) #define litest_timeout_hysteresis(li_) litest_timeout(li_, 90) +#define litest_timeout_3fg_drag_or_swipe(li_) litest_timeout(li_, 90) #define litest_timeout_3fg_drag(li_) litest_timeout(li_, 800) #define litest_timeout_eraser_button(li_) litest_timeout(li_, 50) diff --git a/test/test-gestures.c b/test/test-gestures.c index e5f87cc1..cb53be68 100644 --- a/test/test-gestures.c +++ b/test/test-gestures.c @@ -1587,6 +1587,18 @@ START_TEST(gestures_hold_and_motion_after_timeout) } END_TEST +static void +drain_cancelled_swipe_gesture(struct libinput *li) +{ + litest_drain_events_of_type(li, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); + + _destroy_(libinput_event) *end = libinput_get_event(li); + auto gev = litest_is_gesture_event(end, LIBINPUT_EVENT_GESTURE_SWIPE_END, -1); + litest_assert(libinput_event_gesture_get_cancelled(gev)); +} + START_TEST(gestures_3fg_drag) { struct litest_device *dev = litest_current_device(); @@ -1626,21 +1638,25 @@ START_TEST(gestures_3fg_drag) litest_assert_empty_queue(li); } else { litest_checkpoint( - "Expecting immediate button press as tapping is disabled"); - litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); + "Expecting immediate swipe begin tapping is disabled"); + litest_assert_gesture_event(li, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + finger_count); } while (y < 60.0) { y += 2; - for (uint32_t i = 0; i < finger_count; i++) + for (uint32_t i = 0; i < finger_count; i++) { litest_touch_move(dev, i, 10 + i, y); + if (i == + 0) /* Wait after the first movement to escape the swipe */ + litest_timeout_3fg_drag_or_swipe(li); + } litest_dispatch(li); } - if (tap_enabled) { - litest_checkpoint("Expecting late button press as tapping is enabled"); - litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); - } + drain_cancelled_swipe_gesture(li); + litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); for (uint32_t i = 0; i < finger_count; i++) @@ -1688,10 +1704,15 @@ START_TEST(gestures_3fg_drag_lock_resume_3fg_motion) while (y < 60.0) { y += 2; - for (uint32_t i = 0; i < finger_count; i++) + for (uint32_t i = 0; i < finger_count; i++) { litest_touch_move(dev, i, 10 + i, y); + if (i == + 0) /* Wait after the first movement to escape the swipe */ + litest_timeout_3fg_drag_or_swipe(li); + } litest_dispatch(li); } + drain_cancelled_swipe_gesture(li); litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); @@ -1777,10 +1798,15 @@ START_TEST(gestures_3fg_drag_lock_resume_3fg_release_no_motion) litest_dispatch(li); while (y < 60.0) { y += 2; - for (uint32_t i = 0; i < finger_count; i++) + for (uint32_t i = 0; i < finger_count; i++) { litest_touch_move(dev, i, 10 + i, y); + if (i == + 0) /* Wait after the first movement to escape the swipe */ + litest_timeout_3fg_drag_or_swipe(li); + } litest_dispatch(li); } + drain_cancelled_swipe_gesture(li); litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); @@ -1870,10 +1896,15 @@ START_TEST(gestures_3fg_drag_lock_resume_1fg_motion) while (y < 60.0) { y += 2; - for (uint32_t i = 0; i < finger_count; i++) + for (uint32_t i = 0; i < finger_count; i++) { litest_touch_move(dev, i, 10 + i, y); + if (i == + 0) /* Wait after the first movement to escape the swipe */ + litest_timeout_3fg_drag_or_swipe(li); + } litest_dispatch(li); } + drain_cancelled_swipe_gesture(li); litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); @@ -1943,10 +1974,15 @@ START_TEST(gestures_3fg_drag_lock_resume_2fg_scroll) while (y < 60.0) { y += 2; - for (uint32_t i = 0; i < finger_count; i++) + for (uint32_t i = 0; i < finger_count; i++) { litest_touch_move(dev, i, 10 + i, y); + if (i == + 0) /* Wait after the first movement to escape the swipe */ + litest_timeout_3fg_drag_or_swipe(li); + } litest_dispatch(li); } + drain_cancelled_swipe_gesture(li); litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); @@ -2012,14 +2048,19 @@ START_TEST(gestures_3fg_drag_lock_resume_1fg_tap) while (y < 60.0) { y += 2; - for (int i = 0; i < finger_count; i++) + for (int i = 0; i < finger_count; i++) { litest_touch_move(dev, i, 10 + i, y); + if (i == + 0) /* Wait after the first movement to escape the swipe */ + litest_timeout_3fg_drag_or_swipe(li); + } litest_dispatch(li); } litest_drain_events_of_type(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, LIBINPUT_EVENT_GESTURE_HOLD_END, -1); + drain_cancelled_swipe_gesture(li); litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); @@ -2054,6 +2095,72 @@ START_TEST(gestures_3fg_drag_lock_resume_1fg_tap) } END_TEST +START_TEST(gestures_3fg_drag_fast_swipe) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + uint32_t finger_count = litest_test_param_get_u32(test_env->params, "fingers"); + bool tap_enabled = litest_test_param_get_bool(test_env->params, "tap-enabled"); + + if (litest_slot_count(dev) < 3) + return LITEST_NOT_APPLICABLE; + if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) < + (int)finger_count) + return LITEST_NOT_APPLICABLE; + + litest_enable_3fg_drag(dev->libinput_device, finger_count); + if (tap_enabled) + litest_enable_tap(dev->libinput_device); + else + litest_disable_tap(dev->libinput_device); + + litest_drain_events(li); + + double y = 30.0; + for (uint32_t i = 0; i < finger_count; i++) + litest_touch_down(dev, i, 10 + i, y); + + while (y < 60.0) { + y += 2; + for (uint32_t i = 0; i < finger_count; i++) { + litest_touch_move(dev, i, 10 + i, y); + } + litest_dispatch(li); + } + + litest_drain_events_of_type(li, + LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, + LIBINPUT_EVENT_GESTURE_HOLD_END); + + auto begin = libinput_get_event(li); + litest_is_gesture_event(begin, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + finger_count); + libinput_event_destroy(begin); + + struct libinput_event *update; + while ((update = libinput_get_event(li))) { + litest_is_gesture_event(update, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, + finger_count); + libinput_event_destroy(update); + } + + for (uint32_t i = 0; i < finger_count; i++) + litest_touch_up(dev, i); + + litest_dispatch(li); + + auto end = libinput_get_event(li); + auto gev = litest_is_gesture_event(end, + LIBINPUT_EVENT_GESTURE_SWIPE_END, + finger_count); + litest_assert(!libinput_event_gesture_get_cancelled(gev)); + libinput_event_destroy(end); +} +END_TEST + TEST_COLLECTION(gestures) { /* clang-format off */ @@ -2136,6 +2243,12 @@ TEST_COLLECTION(gestures) litest_add_parametrized(gestures_3fg_drag_lock_resume_1fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, params); } + litest_with_parameters(params, + "fingers", 'u', 2, 3, 4, + "tap-enabled", 'b') { + litest_add_parametrized(gestures_3fg_drag_fast_swipe, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, params); + } + /* Timing-sensitive test, valgrind is too slow */ if (!RUNNING_ON_VALGRIND) litest_add(gestures_swipe_3fg_unaccel, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);