From 3a3fd645c4a78ff85e66327d51c4262292c5b09a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 31 Jan 2018 16:18:15 +1000 Subject: [PATCH] evdev: add a quirk to disable debouncing on the MS Nano Transcievers A set of wireless devices that can scramble the timestamps, so we get press/release within 8ms even though I doubt the user is capable of doing this. Since they're generally good quality anyway, let's just disable debouncing on those until someone complains and we need something more sophisticated. https://bugs.freedesktop.org/show_bug.cgi?id=104415 Signed-off-by: Peter Hutterer --- doc/button-debouncing-state-machine.svg | 714 +----------------- meson.build | 1 + src/evdev-debounce.c | 38 +- src/evdev-fallback.h | 2 + src/evdev.c | 1 + src/evdev.h | 1 + .../litest-device-ms-nano-transceiver-mouse.c | 59 ++ test/litest.h | 2 + test/test-pointer.c | 14 +- udev/90-libinput-model-quirks.hwdb | 4 + 10 files changed, 115 insertions(+), 721 deletions(-) create mode 100644 test/litest-device-ms-nano-transceiver-mouse.c diff --git a/doc/button-debouncing-state-machine.svg b/doc/button-debouncing-state-machine.svg index 48723a13..02ffc460 100644 --- a/doc/button-debouncing-state-machine.svg +++ b/doc/button-debouncing-state-machine.svg @@ -1,714 +1,2 @@ - - - - - - - - -
-
IS_UP
-
-
- IS_UP -
-
- - - - -
-
button
press
-
-
- [Not supported by viewer] -
-
- - - - - - -
-
DOWN_WAITING
-
-
- DOWN_WAITING -
-
- - - - -
-
RELEASE_PENDING
-
-
- RELEASE_PENDING -
-
- - - - -
-
timeout
-
-
- timeout -
-
- - - - -
-
notify
button
release
-
-
- [Not supported by viewer] -
-
- - - - -
-
notify
button
press
-
-
- [Not supported by viewer] -
-
- - - - - - -
-
IS_DOWN
-
-
- IS_DOWN -
-
- - - - - - -
-
button
release
-
-
- button<br>release -
-
- - - - - - - - -
-
timeout
-
-
- timeout -
-
- - - - - - -
-
IS_UP
-
-
- IS_UP -
-
- - - - -
-
button
release
-
-
- button<br>release -
-
- - - - - - -
-
button
press
-
-
- [Not supported by viewer] -
-
- - - - - - - - -
-
notify
button
release
-
-
- [Not supported by viewer] -
-
- - - - -
-
RELEASE_WAITING
-
-
- RELEASE_WAITING -
-
- - - - -
-
button
press
-
-
- [Not supported by viewer] -
-
- - - - - - -
-
timeout
-
-
- timeout -
-
- - - - - - - - - - -
-
MAYBE_SPURIOUS
-
-
- MAYBE_SPURIOUS -
-
- - - - - - -
-
button
release
-
-
- button<br>release -
-
- - - - - - - - -
-
timeout
short
-
-
- timeout<br>short -
-
- - - - - - - - - - -
-
spurious
enabled?
-
-
- spurious<br>enabled? -
-
- - - - - -
-
no
-
-
- no -
-
- - - - -
-
timeout
short
-
-
- timeout<br>short -
-
- - - - - - -
-
RELEASED
-
-
- RELEASED -
-
- - - - - - - - -
-
button
press
-
-
- [Not supported by viewer] -
-
- - - - - - -
-
PRESS_PENDING
-
-
- PRESS_PENDING -
-
- - - - - - -
-
button
release
-
-
- button<br>release -
-
- - - - - - - - -
-
timeout
-
-
- timeout -
-
- - - - -
-
notify
button
press
-
-
- [Not supported by viewer] -
-
- - - - - - - - - - - - -
-
RELEASE_DELAYED
-
-
- RELEASE_DELAYED -
-
- - - - - -
-
yes
-
-
- yes -
-
- - - - -
-
timeout
short
-
-
- timeout<br>short -
-
- - - - - - - - -
-
button
press
-
-
- [Not supported by viewer] -
-
- - - - - - - - -
-
set
timer
-
-
- [Not supported by viewer] -
-
- - - - - - - - -
-
set
timer
-
-
- [Not supported by viewer] -
-
- - - - - - - - -
-
set short
timer
-
-
- set short<br>timer -
-
- - - - - - -
-
other
button
-
-
- [Not supported by viewer] -
-
- - - - - - - - -
-
other
button
-
-
- [Not supported by viewer] -
-
- - - - - - - - -
-
other
button
-
-
- [Not supported by viewer] -
-
- - - - - - - - -
-
other
button
-
-
- [Not supported by viewer] -
-
- - - - - - - - -
-
other
button
-
-
- [Not supported by viewer] -
-
- - - - - - - - -
-
other
button
-
-
- [Not supported by viewer] -
-
- - - - -
-
timeout
-
-
- timeout -
-
- - - - - - - - - - -
-
notify
button
press
-
-
- [Not supported by viewer] -
-
- - - - - - - - -
-
other
button
-
-
- [Not supported by viewer] -
-
- - - - - - -
-
other
button
-
-
- [Not supported by viewer] -
-
- - - - - - -
-
notify
button
release
-
-
- [Not supported by viewer] -
-
- - - - - - - -
-
-

Entry states: IS_UP, IS_DOWN

-

Assumption: state is stored per-button, and OTHER BUTTON events are always processed before the actual button. Stored state per button is a single bit (up/down), a single state for the state machine across the device is sufficient.

-

Start the state machine with IS_UP or IS_DOWN based on the button's bit, any OTHER BUTTON event will reset it to that state anyway, so the state can be re-used for the new button.

-
-
-
- [Not supported by viewer] -
-
- - - - -
-
other
button
-
-
- [Not supported by viewer] -
-
- - - - - - - - -
-
enable
spurious
-
-
- enable<br>spurious -
-
- - - - - - - - - - - - -
-
IS_DOWN
-
-
- IS_DOWN -
-
- - - - - - -
-
timeout
-
-
- timeout -
-
-
-
+
IS_UP
IS_UP
button
press
[Not supported by viewer]
DOWN_WAITING
DOWN_WAITING
RELEASE_PENDING
RELEASE_PENDING
timeout
timeout
notify
button
release
[Not supported by viewer]
notify
button
press
[Not supported by viewer]
IS_DOWN
IS_DOWN
button
release
button<br>release
timeout
timeout
IS_UP
IS_UP
button
release
button<br>release
button
press
[Not supported by viewer]
notify
button
release
[Not supported by viewer]
RELEASE_WAITING
RELEASE_WAITING
button
press
[Not supported by viewer]
timeout
timeout
MAYBE_SPURIOUS
MAYBE_SPURIOUS
button
release
button<br>release
timeout
short
timeout<br>short
spurious
enabled?
spurious<br>enabled?
no
no
timeout
short
timeout<br>short
RELEASED
RELEASED
button
press
[Not supported by viewer]
PRESS_PENDING
PRESS_PENDING
button
release
button<br>release
timeout
timeout
notify
button
press
[Not supported by viewer]
RELEASE_DELAYED
RELEASE_DELAYED
yes
yes
timeout
short
timeout<br>short
button
press
[Not supported by viewer]
set
timer
[Not supported by viewer]
set
timer
[Not supported by viewer]
set short
timer
set short<br>timer
other
button
[Not supported by viewer]
other
button
[Not supported by viewer]
other
button
[Not supported by viewer]
other
button
[Not supported by viewer]
other
button
[Not supported by viewer]
other
button
[Not supported by viewer]
timeout
timeout
notify
button
press
[Not supported by viewer]
other
button
[Not supported by viewer]
other
button
[Not supported by viewer]
notify
button
release
[Not supported by viewer]

Entry states: IS_UP, IS_DOWN

Assumption: state is stored per-button, and OTHER BUTTON events are always processed before the actual button. Stored state per button is a single bit (up/down), a single state for the state machine across the device is sufficient.

Start the state machine with IS_UP or IS_DOWN based on the button's bit, any OTHER BUTTON event will reset it to that state anyway, so the state can be re-used for the new button.

[Not supported by viewer]
other
button
[Not supported by viewer]
enable
spurious
enable<br>spurious
IS_DOWN
IS_DOWN
timeout
timeout
DISABLED
DISABLED
button
press
[Not supported by viewer]
notify
button
press
[Not supported by viewer]
button
release
button<br>release
notify
button
release
[Not supported by viewer]
other
button
[Not supported by viewer]

Entry state: DISABLED

Only set on devices that have button debouncing disabled. This state is effectively a noop, it just forwards the events as they come in and returns back to the same state.
[Not supported by viewer]
\ No newline at end of file diff --git a/meson.build b/meson.build index e1c81372..720fbdb3 100644 --- a/meson.build +++ b/meson.build @@ -563,6 +563,7 @@ if get_option('tests') 'test/litest-device-mouse-low-dpi.c', 'test/litest-device-mouse-wheel-click-angle.c', 'test/litest-device-mouse-wheel-click-count.c', + 'test/litest-device-ms-nano-transceiver-mouse.c', 'test/litest-device-ms-surface-cover.c', 'test/litest-device-protocol-a-touch-screen.c', 'test/litest-device-qemu-usb-tablet.c', diff --git a/src/evdev-debounce.c b/src/evdev-debounce.c index 74faf39b..f7252dcd 100644 --- a/src/evdev-debounce.c +++ b/src/evdev-debounce.c @@ -83,6 +83,7 @@ debounce_state_to_str(enum debounce_state state) CASE_RETURN_STRING(DEBOUNCE_STATE_MAYBE_SPURIOUS); CASE_RETURN_STRING(DEBOUNCE_STATE_RELEASED); CASE_RETURN_STRING(DEBOUNCE_STATE_PRESS_PENDING); + CASE_RETURN_STRING(DEBOUNCE_STATE_DISABLED); } return NULL; @@ -394,6 +395,31 @@ debounce_press_pending_event(struct fallback_dispatch *fallback, enum debounce_e } } +static void +debounce_disabled_event(struct fallback_dispatch *fallback, + enum debounce_event event, + uint64_t time) +{ + switch (event) { + case DEBOUNCE_EVENT_PRESS: + fallback->debounce.button_time = time; + debounce_notify_button(fallback, + LIBINPUT_BUTTON_STATE_PRESSED); + break; + case DEBOUNCE_EVENT_RELEASE: + fallback->debounce.button_time = time; + debounce_notify_button(fallback, + LIBINPUT_BUTTON_STATE_RELEASED); + break; + case DEBOUNCE_EVENT_TIMEOUT_SHORT: + case DEBOUNCE_EVENT_TIMEOUT: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_OTHERBUTTON: + break; + } +} + static void debounce_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, @@ -434,6 +460,9 @@ debounce_handle_event(struct fallback_dispatch *fallback, case DEBOUNCE_STATE_PRESS_PENDING: debounce_press_pending_event(fallback, event, time); break; + case DEBOUNCE_STATE_DISABLED: + debounce_disabled_event(fallback, event, time); + break; } evdev_log_debug(fallback->device, @@ -484,7 +513,8 @@ fallback_debounce_handle_state(struct fallback_dispatch *dispatch, for (size_t i = 0; i < nchanged; i++) { bool is_down = hw_is_key_down(dispatch, changed[i]); - if (flushed) { + if (flushed && + dispatch->debounce.state != DEBOUNCE_STATE_DISABLED) { debounce_set_state(dispatch, !is_down ? DEBOUNCE_STATE_IS_DOWN : @@ -538,6 +568,12 @@ fallback_init_debounce(struct fallback_dispatch *dispatch) struct evdev_device *device = dispatch->device; char timer_name[64]; + if (device->model_flags & EVDEV_MODEL_MS_NANO_TRANSCEIVER) { + dispatch->debounce.state = DEBOUNCE_STATE_DISABLED; + return; + } + + dispatch->debounce.state = DEBOUNCE_STATE_IS_UP; snprintf(timer_name, diff --git a/src/evdev-fallback.h b/src/evdev-fallback.h index 0d9e247e..d64d36d0 100644 --- a/src/evdev-fallback.h +++ b/src/evdev-fallback.h @@ -41,6 +41,8 @@ enum debounce_state { DEBOUNCE_STATE_MAYBE_SPURIOUS, DEBOUNCE_STATE_RELEASED, DEBOUNCE_STATE_PRESS_PENDING, + + DEBOUNCE_STATE_DISABLED = 999, }; struct fallback_dispatch { diff --git a/src/evdev.c b/src/evdev.c index 63b93ec3..b98dc32d 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1264,6 +1264,7 @@ evdev_read_model_flags(struct evdev_device *device) MODEL(APPLE_TOUCHPAD_ONEBUTTON), MODEL(LOGITECH_MARBLE_MOUSE), MODEL(TABLET_NO_PROXIMITY_OUT), + MODEL(MS_NANO_TRANSCEIVER), #undef MODEL { "ID_INPUT_TRACKBALL", EVDEV_MODEL_TRACKBALL }, { NULL, EVDEV_MODEL_DEFAULT }, diff --git a/src/evdev.h b/src/evdev.h index 20a7e352..0301a7f5 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -124,6 +124,7 @@ enum evdev_device_model { EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON = (1 << 25), EVDEV_MODEL_LOGITECH_MARBLE_MOUSE = (1 << 26), EVDEV_MODEL_TABLET_NO_PROXIMITY_OUT = (1 << 27), + EVDEV_MODEL_MS_NANO_TRANSCEIVER = (1 << 28), }; enum evdev_button_scroll_state { diff --git a/test/litest-device-ms-nano-transceiver-mouse.c b/test/litest-device-ms-nano-transceiver-mouse.c new file mode 100644 index 00000000..dc534637 --- /dev/null +++ b/test/litest-device-ms-nano-transceiver-mouse.c @@ -0,0 +1,59 @@ +/* + * Copyright © 2018 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "litest.h" +#include "litest-int.h" + +static struct input_id input_id = { + .bustype = 0x3, + .vendor = 0x045e, + .product = 0x0800, +}; + +static int events[] = { + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_SIDE, + EV_KEY, BTN_EXTRA, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_REL, REL_DIAL, + EV_REL, REL_HWHEEL, + -1 , -1, +}; + +TEST_DEVICE("ms-nano-mouse", + .type = LITEST_MS_NANO_TRANSCEIVER_MOUSE, + .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL | LITEST_NO_DEBOUNCE, + .interface = NULL, + + .name = "Microsoft Microsoft® Nano Transceiver v2.0", + .id = &input_id, + .absinfo = NULL, + .events = events, +) + diff --git a/test/litest.h b/test/litest.h index faa469c0..0c57e158 100644 --- a/test/litest.h +++ b/test/litest.h @@ -270,6 +270,7 @@ enum litest_device_type { LITEST_WACOM_BAMBOO_2FG_PEN, LITEST_WACOM_BAMBOO_2FG_FINGER, LITEST_HP_WMI_HOTKEYS, + LITEST_MS_NANO_TRANSCEIVER_MOUSE, }; enum litest_device_feature { @@ -303,6 +304,7 @@ enum litest_device_feature { LITEST_LEDS = 1 << 25, LITEST_SWITCH = 1 << 26, LITEST_IGNORED = 1 << 27, + LITEST_NO_DEBOUNCE = 1 << 28, }; /* this is a semi-mt device, so we keep track of the touches that the tests diff --git a/test/test-pointer.c b/test/test-pointer.c index 7324c0f6..a4dcdaa3 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -2602,11 +2602,11 @@ litest_setup_tests_pointer(void) litest_add("pointer:time", pointer_time_usec, LITEST_RELATIVE, LITEST_ANY); - litest_add_ranged("pointer:debounce", debounce_bounce, LITEST_BUTTON, LITEST_TOUCHPAD, &buttons); - litest_add("pointer:debounce", debounce_bounce_check_immediate, LITEST_BUTTON, LITEST_TOUCHPAD); - litest_add_ranged("pointer:debounce", debounce_spurious, LITEST_BUTTON, LITEST_TOUCHPAD, &buttons); - litest_add("pointer:debounce", debounce_spurious_multibounce, LITEST_BUTTON, LITEST_TOUCHPAD); - litest_add("pointer:debounce_otherbutton", debounce_spurious_dont_enable_on_otherbutton, LITEST_BUTTON, LITEST_TOUCHPAD); - litest_add("pointer:debounce_otherbutton", debounce_spurious_cancel_debounce_otherbutton, LITEST_BUTTON, LITEST_TOUCHPAD); - litest_add("pointer:debounce_otherbutton", debounce_spurious_switch_to_otherbutton, LITEST_BUTTON, LITEST_TOUCHPAD); + litest_add_ranged("pointer:debounce", debounce_bounce, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_NO_DEBOUNCE, &buttons); + litest_add("pointer:debounce", debounce_bounce_check_immediate, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_NO_DEBOUNCE); + litest_add_ranged("pointer:debounce", debounce_spurious, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_NO_DEBOUNCE, &buttons); + litest_add("pointer:debounce", debounce_spurious_multibounce, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_NO_DEBOUNCE); + litest_add("pointer:debounce_otherbutton", debounce_spurious_dont_enable_on_otherbutton, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_NO_DEBOUNCE); + litest_add("pointer:debounce_otherbutton", debounce_spurious_cancel_debounce_otherbutton, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_NO_DEBOUNCE); + litest_add("pointer:debounce_otherbutton", debounce_spurious_switch_to_otherbutton, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_NO_DEBOUNCE); } diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index c23ef833..514b637c 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -229,6 +229,10 @@ libinput:name:*Lid Switch*:dmi:*svnMicrosoftCorporation:pnSurface3:* libinput:name:*Microsoft Surface Type Cover Keyboard*:dmi:*svnMicrosoftCorporation:pnSurface3:* LIBINPUT_ATTR_KEYBOARD_INTEGRATION=internal +# Microsoft Microsoft® Nano Transceiver v2.0" +libinput:mouse:input:b0003v045Ep0800* + LIBINPUT_MODEL_MS_NANO_TRANSCEIVER=1 + ########################################## # Razer ##########################################