touchpad: add sticky mode to drag-lock

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>
This commit is contained in:
satrmb 2024-09-02 16:29:02 +02:00 committed by Marge Bot
parent ee9043d6b4
commit 910d59e836
12 changed files with 178 additions and 35 deletions

View file

@ -28,7 +28,7 @@ options exposed by libinput are:
- 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.

View file

@ -56,11 +56,13 @@ tap-and-drag enabled by default.
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
@ -78,6 +80,9 @@ If drag lock is enabled, the release of the mouse buttons after the finger
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.

View file

@ -815,7 +815,7 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp,
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,
@ -823,6 +823,7 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp,
};
assert(nfingers_tapped >= 1 && nfingers_tapped <= 3);
tp->tap.state = dest[nfingers_tapped - 1];
if (tp->tap.drag_lock == LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT)
tp_tap_set_draglock_timer(tp, time);
} else {
tp_tap_notify(tp,
@ -1517,7 +1518,7 @@ tp_tap_config_set_draglock_enabled(struct libinput_device *device,
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;
}
@ -1528,7 +1529,7 @@ tp_tap_config_get_draglock_enabled(struct libinput_device *device)
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
@ -1570,7 +1571,7 @@ tp_init_tap(struct tp_dispatch *tp)
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),

View file

@ -435,7 +435,7 @@ struct tp_dispatch {
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;

View file

@ -4067,7 +4067,8 @@ LIBINPUT_EXPORT enum libinput_config_status
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;

View file

@ -4929,24 +4929,33 @@ libinput_device_config_tap_get_default_drag_enabled(struct libinput_device *devi
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
@ -4966,11 +4975,14 @@ libinput_device_config_tap_set_drag_lock_enabled(struct libinput_device *device,
* 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
@ -4986,13 +4998,15 @@ libinput_device_config_tap_get_drag_lock_enabled(struct libinput_device *device)
* 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
*

View file

@ -1179,6 +1179,18 @@ litest_enable_buttonareas(struct litest_device *dev)
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)
{
@ -1186,7 +1198,7 @@ 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);
}

View file

@ -1579,6 +1579,81 @@ START_TEST(touchpad_tap_n_drag_draglock_timeout)
}
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
@ -4133,14 +4208,27 @@ START_TEST(touchpad_drag_lock_default_disabled)
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);
@ -4163,6 +4251,14 @@ START_TEST(touchpad_drag_lock_default_unavailable)
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);
@ -5658,6 +5754,7 @@ TEST_COLLECTION(touchpad_tap)
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 */

View file

@ -64,8 +64,10 @@ Enable or disable tap-to-click
.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

View file

@ -158,10 +158,20 @@ tools_parse_option(int option,
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;

View file

@ -74,7 +74,7 @@ enum configuration_options {
{ "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 }, \

View file

@ -219,6 +219,7 @@ options = {
"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": {