plugins: restore the wheel plugin specifically for the MX Master 3

The MX Master 3 is difficult, its wheel events are all over the place
and heuristics are tricky to determine. The previous plugin behavior
was seemingly sufficient for the MX Master but not for other devices.

Restore the old behavior if the quirk is set for a device by adding a
fourth state ALWAYS_ACCUMULATE. In this state the min movement is never
updated from the original threshold, causing any wheel motion to
accumulate.

Ref: ca6b82841c ("plugin/wheel: tighten the wheel debouncing code")
Ref: bb05e0d1b5 ("plugin/wheel: don't accumulate for low HID resolution multipliers")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1262>
This commit is contained in:
Peter Hutterer 2025-07-08 16:39:10 +10:00 committed by Marge Bot
parent f8c582db51
commit 6cdf46f857
4 changed files with 38 additions and 15 deletions

View file

@ -93,22 +93,26 @@ ModelInvertHorizontalScrolling=1
MatchVendor=0x046D MatchVendor=0x046D
MatchProduct=0x4082 MatchProduct=0x4082
ModelInvertHorizontalScrolling=1 ModelInvertHorizontalScrolling=1
ModelLogitechMXMaster3=1
# MX Master 3 has a different PID on bluetooth # MX Master 3 has a different PID on bluetooth
[Logitech MX Master 3 Bluetooth] [Logitech MX Master 3 Bluetooth]
MatchVendor=0x046D MatchVendor=0x046D
MatchProduct=0xB023 MatchProduct=0xB023
ModelInvertHorizontalScrolling=1 ModelInvertHorizontalScrolling=1
ModelLogitechMXMaster3=1
[Logitech MX Master 3S] [Logitech MX Master 3S]
MatchVendor=0x046D MatchVendor=0x046D
MatchProduct=0xB034 MatchProduct=0xB034
ModelInvertHorizontalScrolling=1 ModelInvertHorizontalScrolling=1
ModelLogitechMXMaster3=1
[Logitech MX Master 3B] [Logitech MX Master 3B]
MatchVendor=0x046D MatchVendor=0x046D
MatchProduct=0xB028 MatchProduct=0xB028
ModelInvertHorizontalScrolling=1 ModelInvertHorizontalScrolling=1
ModelLogitechMXMaster3=1
# Don't add quirks for the Logitech Bolt Receiver: # Don't add quirks for the Logitech Bolt Receiver:
# MatchVendor=0x046D # MatchVendor=0x046D

View file

@ -63,8 +63,9 @@ enum wheel_event {
enum ignore_strategy { enum ignore_strategy {
MAYBE, /* use heuristics but don't yet accumulate */ MAYBE, /* use heuristics but don't yet accumulate */
ACCUMULATE, /* accumulate scroll wheel events */
PASSTHROUGH, /* do not accumulate, pass through */ PASSTHROUGH, /* do not accumulate, pass through */
ACCUMULATE, /* accumulate scroll wheel events */
ALWAYS_ACCUMULATE, /* always accumulate wheel events */
}; };
struct plugin_device { struct plugin_device {
@ -146,9 +147,16 @@ wheel_handle_event_on_state_none(struct plugin_device *pd,
{ {
switch (event) { switch (event) {
case WHEEL_EVENT_SCROLL: case WHEEL_EVENT_SCROLL:
pd->state = pd->ignore_small_hi_res_movements == ACCUMULATE switch (pd->ignore_small_hi_res_movements) {
? WHEEL_STATE_ACCUMULATING_SCROLL case ACCUMULATE:
: WHEEL_STATE_SCROLLING; case ALWAYS_ACCUMULATE:
pd->state = WHEEL_STATE_ACCUMULATING_SCROLL;
break;
case PASSTHROUGH:
case MAYBE:
pd->state = WHEEL_STATE_SCROLLING;
break;
}
break; break;
case WHEEL_EVENT_SCROLL_DIR_CHANGED: case WHEEL_EVENT_SCROLL_DIR_CHANGED:
break; break;
@ -344,6 +352,7 @@ wheel_handle_direction_change(struct plugin_device *pd,
static inline void static inline void
wheel_update_strategy(struct plugin_device *pd, int32_t value) wheel_update_strategy(struct plugin_device *pd, int32_t value)
{ {
if (pd->ignore_small_hi_res_movements != ALWAYS_ACCUMULATE) {
pd->min_movement = min(pd->min_movement, abs(value)); pd->min_movement = min(pd->min_movement, abs(value));
/* Only if a wheel sends movements less than the trigger threshold /* Only if a wheel sends movements less than the trigger threshold
@ -352,6 +361,7 @@ wheel_update_strategy(struct plugin_device *pd, int32_t value)
if (pd->ignore_small_hi_res_movements == MAYBE && if (pd->ignore_small_hi_res_movements == MAYBE &&
pd->min_movement < ACC_V120_TRIGGER_THRESHOLD) pd->min_movement < ACC_V120_TRIGGER_THRESHOLD)
pd->ignore_small_hi_res_movements = ACCUMULATE; pd->ignore_small_hi_res_movements = ACCUMULATE;
}
} }
static void static void
@ -434,11 +444,17 @@ wheel_plugin_device_create(struct libinput_plugin *libinput_plugin,
pd->device = libinput_device_ref(device); pd->device = libinput_device_ref(device);
pd->state = WHEEL_STATE_NONE; pd->state = WHEEL_STATE_NONE;
pd->dir = WHEEL_DIR_UNKNOW; pd->dir = WHEEL_DIR_UNKNOW;
pd->ignore_small_hi_res_movements =
evdev_device_is_virtual(evdev) ? PASSTHROUGH : MAYBE;
pd->min_movement = ACC_V120_THRESHOLD; pd->min_movement = ACC_V120_THRESHOLD;
ratelimit_init(&pd->hires_warning_limit, s2us(24 * 60 * 60), 1); ratelimit_init(&pd->hires_warning_limit, s2us(24 * 60 * 60), 1);
if (evdev_device_is_virtual(evdev))
pd->ignore_small_hi_res_movements = PASSTHROUGH;
else if (libinput_device_has_model_quirk(device,
QUIRK_MODEL_LOGITECH_MX_MASTER_3))
pd->ignore_small_hi_res_movements = ALWAYS_ACCUMULATE;
else
pd->ignore_small_hi_res_movements = MAYBE;
if (pd->ignore_small_hi_res_movements != PASSTHROUGH) { if (pd->ignore_small_hi_res_movements != PASSTHROUGH) {
pd->scroll_timer = pd->scroll_timer =
libinput_plugin_timer_new(libinput_plugin, libinput_plugin_timer_new(libinput_plugin,

View file

@ -296,6 +296,8 @@ quirk_get_name(enum quirk q)
return "ModelWacomTouchpad"; return "ModelWacomTouchpad";
case QUIRK_MODEL_PRESSURE_PAD: case QUIRK_MODEL_PRESSURE_PAD:
return "ModelPressurePad"; return "ModelPressurePad";
case QUIRK_MODEL_LOGITECH_MX_MASTER_3:
return "ModelLogitechMXMaster3";
case QUIRK_ATTR_SIZE_HINT: case QUIRK_ATTR_SIZE_HINT:
return "AttrSizeHint"; return "AttrSizeHint";

View file

@ -83,6 +83,7 @@ enum quirk {
QUIRK_MODEL_LENOVO_T450_TOUCHPAD, QUIRK_MODEL_LENOVO_T450_TOUCHPAD,
QUIRK_MODEL_LENOVO_X1GEN6_TOUCHPAD, QUIRK_MODEL_LENOVO_X1GEN6_TOUCHPAD,
QUIRK_MODEL_LENOVO_X230, QUIRK_MODEL_LENOVO_X230,
QUIRK_MODEL_LOGITECH_MX_MASTER_3,
QUIRK_MODEL_PRESSURE_PAD, QUIRK_MODEL_PRESSURE_PAD,
QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD, QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD,
QUIRK_MODEL_SYSTEM76_BONOBO, QUIRK_MODEL_SYSTEM76_BONOBO,