Sticky mode removes the timeout from drag-lock, only a tap ends a drag.
Timeout mode remains available without changes.
Sticky mode is exposed as a new value for the existing drag-lock setting.
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1037>
- how many tapping fingers are supported by this device
- a toggle to enable/disable tapping
- a toggle to enable/disable tap-and-drag, see :ref:`tapndrag`.
-- a toggle to enable/disable tap-and-drag drag lock see :ref:`tapndrag`
+- a toggle to enable/disable tap-and-drag drag lock, see :ref:`tapndrag`
- The default order is 1, 2, 3 finger tap mapping to left, right, middle
click, respectively. This order can be changed to left, middle, right click,
respectively.
single-finger drag.
Also optional is a feature called "drag lock". With drag lock disabled, lifting
-the finger will stop any drag process. When enabled, libinput will ignore a
-finger up event during a drag process, provided the finger is set down again
-within a implementation-specific timeout. Drag lock can be enabled and
-disabled with **libinput_device_config_tap_set_drag_lock_enabled()**.
-Note that drag lock only applies if tap-and-drag is be enabled.
+the finger will stop any drag process. When enabled, the drag
+process continues even after lifting a finger but can be ended
+with an additional tap. If timeout-based drag-locks are enabled
+the drag process will also automatically end once the finger has
+been lifted for an implementation-specific timeout. Drag lock can be
+enabled and disabled with **libinput_device_config_tap_set_drag_lock_enabled()**.
+Note that drag lock only applies if tap-and-drag is enabled.
.. figure:: tap-n-drag.svg
:align: center
release (e) is triggered by a timeout. To release the button immediately,
simply tap again (f).
+If drag lock is enabled in sticky mode there is no timeout after
+releasing a finger and an extra tap is required to release the button.
+
If two fingers are supported by the hardware, a second finger can be used to
drag while the first is held in-place.
break;
}
case TAP_EVENT_RELEASE:
- if (tp->tap.drag_lock_enabled) {
+ if (tp->tap.drag_lock != LIBINPUT_CONFIG_DRAG_LOCK_DISABLED) {
enum tp_tap_state dest[3] = {
TAP_STATE_1FGTAP_DRAGGING_WAIT,
TAP_STATE_2FGTAP_DRAGGING_WAIT,
};
assert(nfingers_tapped >= 1 && nfingers_tapped <= 3);
tp->tap.state = dest[nfingers_tapped - 1];
- tp_tap_set_draglock_timer(tp, time);
+ if (tp->tap.drag_lock == LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT)
+ tp_tap_set_draglock_timer(tp, time);
} else {
tp_tap_notify(tp,
time,
struct evdev_dispatch *dispatch = evdev_device(device)->dispatch;
struct tp_dispatch *tp = tp_dispatch(dispatch);
- tp->tap.drag_lock_enabled = enabled;
+ tp->tap.drag_lock = enabled;
return LIBINPUT_CONFIG_STATUS_SUCCESS;
}
struct evdev_dispatch *dispatch = evdev_device(device)->dispatch;
struct tp_dispatch *tp = tp_dispatch(dispatch);
- return tp->tap.drag_lock_enabled;
+ return tp->tap.drag_lock;
}
static inline enum libinput_config_drag_lock_state
tp->tap.map = LIBINPUT_CONFIG_TAP_MAP_LRM;
tp->tap.want_map = tp->tap.map;
tp->tap.drag_enabled = tp_drag_default(tp->device);
- tp->tap.drag_lock_enabled = tp_drag_lock_default(tp->device);
+ tp->tap.drag_lock = tp_drag_lock_default(tp->device);
snprintf(timer_name,
sizeof(timer_name),
enum libinput_config_tap_button_map want_map;
bool drag_enabled;
- bool drag_lock_enabled;
+ enum libinput_config_drag_lock_state drag_lock;
unsigned int nfingers_down; /* number of fingers down for tapping (excl. thumb/palm) */
} tap;
libinput_device_config_tap_set_drag_lock_enabled(struct libinput_device *device,
enum libinput_config_drag_lock_state enable)
{
- if (enable != LIBINPUT_CONFIG_DRAG_LOCK_ENABLED &&
+ if (enable != LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY &&
+ enable != LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT &&
enable != LIBINPUT_CONFIG_DRAG_LOCK_DISABLED)
return LIBINPUT_CONFIG_STATUS_INVALID;
enum libinput_config_drag_lock_state {
/** Drag lock is to be disabled, or is currently disabled */
LIBINPUT_CONFIG_DRAG_LOCK_DISABLED,
- /** Drag lock is to be enabled, or is currently disabled */
- LIBINPUT_CONFIG_DRAG_LOCK_ENABLED,
+ /** Drag lock is to be enabled in timeout mode,
+ * or is currently enabled in timeout mode */
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT,
+ /** legacy spelling for LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT */
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT,
+ /** Drag lock is to be enabled in sticky mode,
+ * or is currently enabled in sticky mode */
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY,
};
/**
* @ingroup config
*
* Enable or disable drag-lock during tapping on this device. When enabled,
- * a finger may be lifted and put back on the touchpad within a timeout and
- * the drag process continues. When disabled, lifting the finger during a
- * tap-and-drag will immediately stop the drag. See the libinput
- * documentation for more details.
+ * a finger may be lifted and put back on the touchpad and the drag process
+ * continues. A timeout for lifting the finger is optional. When disabled,
+ * lifting the finger during a tap-and-drag will immediately stop the drag.
+ * See the libinput documentation for more details.
*
- * Enabling drag lock on a device that has tapping disabled is permitted,
- * but has no effect until tapping is enabled.
+ * Enabling drag lock on a device that has tapping or tap-and-drag disabled is
+ * permitted, but has no effect until tapping and tap-and-drag are enabled.
*
* @param device The device to configure
- * @param enable @ref LIBINPUT_CONFIG_DRAG_LOCK_ENABLED to enable drag lock
+ * @param enable @ref LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY to enable drag
+ * lock in sticky mode,
+ * @ref LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT to enable drag lock in timeout
+ * mode,
* or @ref LIBINPUT_CONFIG_DRAG_LOCK_DISABLED to disable drag lock
*
* @return A config status code. Disabling drag lock on a device that does not
* device does not support tapping, this function always returns
* @ref LIBINPUT_CONFIG_DRAG_LOCK_DISABLED.
*
- * Drag lock may be enabled even when tapping is disabled.
+ * Drag lock may be enabled even when tapping or tap-and-drag is disabled.
*
* @param device The device to configure
*
- * @retval LIBINPUT_CONFIG_DRAG_LOCK_ENABLED If drag lock is currently enabled
+ * @retval LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY If drag lock is currently
+ * enabled in sticky mode
+ * @retval LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT If drag lock is currently
+ * enabled in timeout mode
* @retval LIBINPUT_CONFIG_DRAG_LOCK_DISABLED If drag lock is currently disabled
*
* @see libinput_device_config_tap_set_drag_lock_enabled
* If the device does not support tapping, this function always returns
* @ref LIBINPUT_CONFIG_DRAG_LOCK_DISABLED.
*
- * Drag lock may be enabled by default even when tapping is disabled by
- * default.
+ * Drag lock may be enabled by default even when tapping or tap-and-drag is
+ * disabled by default.
*
* @param device The device to configure
*
- * @retval LIBINPUT_CONFIG_DRAG_LOCK_ENABLED If drag lock is enabled by
- * default
+ * @retval LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY If drag lock is enabled in
+ * sticky mode by default
+ * @retval LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT If drag lock is enabled in
+ * timeout mode by default
* @retval LIBINPUT_CONFIG_DRAG_LOCK_DISABLED If drag lock is disabled by
* default
*
litest_assert_int_eq(status, expected);
}
+static inline void
+litest_enable_drag_lock_sticky(struct libinput_device *device)
+{
+ enum libinput_config_status status, expected;
+
+ expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
+ status = libinput_device_config_tap_set_drag_lock_enabled(device,
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
+
+ litest_assert_int_eq(status, expected);
+}
+
static inline void
litest_enable_drag_lock(struct libinput_device *device)
{
expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
status = libinput_device_config_tap_set_drag_lock_enabled(device,
- LIBINPUT_CONFIG_DRAG_LOCK_ENABLED);
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
litest_assert_int_eq(status, expected);
}
}
END_TEST
+START_TEST(touchpad_tap_n_drag_draglock_sticky)
+{
+ 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;
+
+ litest_enable_tap(dev->libinput_device);
+ litest_enable_drag_lock_sticky(dev->libinput_device);
+ litest_disable_hold_gestures(dev->libinput_device);
+
+ switch (nfingers) {
+ case 1:
+ button = BTN_LEFT;
+ break;
+ case 2:
+ button = BTN_RIGHT;
+ break;
+ case 3:
+ button = BTN_MIDDLE;
+ break;
+ default:
+ abort();
+ }
+
+ litest_drain_events(li);
+
+ 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;
+ }
+ 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;
+ }
+ litest_touch_down(dev, 0, 50, 50);
+ libinput_dispatch(li);
+ litest_timeout_tap();
+
+ litest_assert_button_event(li, button,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+
+ litest_assert_empty_queue(li);
+ litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+
+ litest_timeout_tapndrag();
+ litest_assert_empty_queue(li);
+
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ litest_assert_button_event(li, button,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(touchpad_tap_n_drag_2fg)
{
/* Test: tap with 1-3 fingers (multiple times), then a 1fg move
status = libinput_device_config_tap_set_drag_lock_enabled(device,
LIBINPUT_CONFIG_DRAG_LOCK_ENABLED);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+ /* ENABLED is a legacy spelling for ENABLED_TIMEOUT */
+ ck_assert_int_eq(libinput_device_config_tap_get_drag_lock_enabled(device),
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
status = libinput_device_config_tap_set_drag_lock_enabled(device,
LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+ ck_assert_int_eq(libinput_device_config_tap_get_drag_lock_enabled(device),
+ LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
status = libinput_device_config_tap_set_drag_lock_enabled(device,
- LIBINPUT_CONFIG_DRAG_LOCK_ENABLED);
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+ ck_assert_int_eq(libinput_device_config_tap_get_drag_lock_enabled(device),
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
+
+ status = libinput_device_config_tap_set_drag_lock_enabled(device,
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+ ck_assert_int_eq(libinput_device_config_tap_get_drag_lock_enabled(device),
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
status = libinput_device_config_tap_set_drag_lock_enabled(device,
3);
LIBINPUT_CONFIG_DRAG_LOCK_ENABLED);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+ status = libinput_device_config_tap_set_drag_lock_enabled(device,
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+
+ status = libinput_device_config_tap_set_drag_lock_enabled(device,
+ LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+
status = libinput_device_config_tap_set_drag_lock_enabled(device,
LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_add_ranged(touchpad_tap_n_drag_draglock, LITEST_TOUCHPAD, LITEST_ANY, &range_multifinger_tap);
litest_add_ranged(touchpad_tap_n_drag_draglock_tap, LITEST_TOUCHPAD, LITEST_ANY, &range_multifinger_doubletap);
litest_add_ranged(touchpad_tap_n_drag_draglock_timeout, LITEST_TOUCHPAD, LITEST_ANY, &range_multifinger_tap);
+ litest_add_ranged(touchpad_tap_n_drag_draglock_sticky, LITEST_TOUCHPAD, LITEST_ANY, &range_multifinger_tap);
/* Real buttons don't interfere with tapping, so don't run those for
pads with buttons */
.B \-\-enable-drag|\-\-disable\-drag
Enable or disable tap-and-drag
.TP 8
-.B \-\-enable\-drag-lock|\-\-disable\-drag\-lock
-Enable or disable drag-lock
+.B \-\-enable\-drag\-lock|\-\-disable\-drag\-lock
+Enable (in timeout mode) or disable drag-lock
+.B \-\-enable\-drag\-lock=[sticky|timeout]
+Enable drag-lock in sticky or timeout mode
.TP 8
.B \-\-enable\-natural\-scrolling|\-\-disable\-natural\-scrolling
Enable or disable natural scrolling
options->drag = 0;
break;
case OPT_DRAG_LOCK_ENABLE:
- options->drag_lock = 1;
+ if (optarg) {
+ if (streq(optarg, "sticky")) {
+ options->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY;
+ } else if (streq(optarg, "timeout")) {
+ options->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT;
+ } else {
+ return 1;
+ }
+ } else {
+ options->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT;
+ }
break;
case OPT_DRAG_LOCK_DISABLE:
- options->drag_lock = 0;
+ options->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
break;
case OPT_NATURAL_SCROLL_ENABLE:
options->natural_scroll = 1;
{ "disable-tap", no_argument, 0, OPT_TAP_DISABLE }, \
{ "enable-drag", no_argument, 0, OPT_DRAG_ENABLE }, \
{ "disable-drag", no_argument, 0, OPT_DRAG_DISABLE }, \
- { "enable-drag-lock", no_argument, 0, OPT_DRAG_LOCK_ENABLE }, \
+ { "enable-drag-lock", optional_argument, 0, OPT_DRAG_LOCK_ENABLE }, \
{ "disable-drag-lock", no_argument, 0, OPT_DRAG_LOCK_DISABLE }, \
{ "enable-natural-scrolling", no_argument, 0, OPT_NATURAL_SCROLL_ENABLE }, \
{ "disable-natural-scrolling", no_argument, 0, OPT_NATURAL_SCROLL_DISABLE }, \
"set-profile": ["adaptive", "flat"],
"set-tap-map": ["lrm", "lmr"],
"set-clickfinger-map": ["lrm", "lmr"],
+ "enable-drag-lock": ["sticky", "timeout"],
},
# options with a range (and increment)
"ranges": {