Merge branch 'wip/fast-3fg-swipe' into 'main'

touchpad: add support for fast swipe when 3fg drag is enabled

See merge request libinput/libinput!1410
This commit is contained in:
Peter Hutterer 2026-02-02 16:45:28 +10:00
commit bfa943b912
5 changed files with 358 additions and 51 deletions

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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 {

View file

@ -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)

View file

@ -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);