#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)
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)
{
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.
*/
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);
}
}
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);
}
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
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)
{
}
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);
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);