From 42b68397f9e21e35a9a4ae36ad9144dbe8a87293 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 17 Feb 2017 10:38:09 +1000 Subject: test: check the libinput device for BTN_MIDDLE, not the libevdev device We don't have the same libevdev context that libinput has so if libinput disables/enables event codes we don't see that and may get unexpected behavior in the test. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/test-pointer.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/test-pointer.c b/test/test-pointer.c index 6485258e..986ec8b7 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -371,13 +371,15 @@ START_TEST(pointer_button) test_button_event(dev, BTN_LEFT, 1); test_button_event(dev, BTN_LEFT, 0); - if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_RIGHT)) { + if (libinput_device_pointer_has_button(dev->libinput_device, + BTN_RIGHT)) { test_button_event(dev, BTN_RIGHT, 1); test_button_event(dev, BTN_RIGHT, 0); } /* Skip middle button test on trackpoints (used for scrolling) */ - if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) { + if (libinput_device_pointer_has_button(dev->libinput_device, + BTN_MIDDLE)) { test_button_event(dev, BTN_MIDDLE, 1); test_button_event(dev, BTN_MIDDLE, 0); } @@ -904,9 +906,7 @@ START_TEST(pointer_left_handed) BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED); - if (libevdev_has_event_code(dev->evdev, - EV_KEY, - BTN_MIDDLE)) { + if (libinput_device_pointer_has_button(d, BTN_MIDDLE)) { litest_button_click(dev, BTN_MIDDLE, 1); litest_button_click(dev, BTN_MIDDLE, 0); litest_assert_button_event(li, @@ -1614,7 +1614,8 @@ START_TEST(middlebutton_middleclick) disable_button_scrolling(device); - if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE)) + if (!libinput_device_pointer_has_button(device->libinput_device, + BTN_MIDDLE)) return; status = libinput_device_config_middle_emulation_set_enabled( @@ -1678,7 +1679,8 @@ START_TEST(middlebutton_middleclick_during) disable_button_scrolling(device); - if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE)) + if (!libinput_device_pointer_has_button(device->libinput_device, + BTN_MIDDLE)) return; status = libinput_device_config_middle_emulation_set_enabled( @@ -1745,7 +1747,7 @@ START_TEST(middlebutton_default_enabled) available = libinput_device_config_middle_emulation_is_available(device); ck_assert(available); - if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) + if (libinput_device_pointer_has_button(device, BTN_MIDDLE)) deflt = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; else deflt = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; @@ -1815,7 +1817,7 @@ START_TEST(middlebutton_default_touchpad) available = libinput_device_config_middle_emulation_is_available(device); ck_assert(!available); - if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) + if (libinput_device_pointer_has_button(device, BTN_MIDDLE)) return; state = libinput_device_config_middle_emulation_get_enabled( -- cgit v1.2.3 From 9ab05b0bfda91bec5bdc5f1789aaa6a854558fa7 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 17 Feb 2017 10:32:55 +1000 Subject: test: fix middle button defaults test This failed on devices without a middle button, we just didn't have a test device to trigger this. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/test-pointer.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/test-pointer.c b/test/test-pointer.c index 986ec8b7..abc8a118 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -1742,22 +1742,21 @@ START_TEST(middlebutton_default_enabled) struct libinput_device *device = dev->libinput_device; enum libinput_config_status status; int available; - enum libinput_config_middle_emulation_state deflt, state; + enum libinput_config_middle_emulation_state state; + + if (!libinput_device_pointer_has_button(dev->libinput_device, + BTN_MIDDLE)) + return; available = libinput_device_config_middle_emulation_is_available(device); ck_assert(available); - if (libinput_device_pointer_has_button(device, BTN_MIDDLE)) - deflt = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; - else - deflt = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; - state = libinput_device_config_middle_emulation_get_enabled(device); - ck_assert_int_eq(state, deflt); + ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); state = libinput_device_config_middle_emulation_get_default_enabled( device); - ck_assert_int_eq(state, deflt); + ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); status = libinput_device_config_middle_emulation_set_enabled(device, LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); -- cgit v1.2.3 From 72c5821a3b57e9e6fa713bdfb82939c8299e543d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 17 Feb 2017 10:40:34 +1000 Subject: test: skip two tests when middle button is missing And disable middle button emulation for this test, it would mess with the test results. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/test-pointer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/test-pointer.c b/test/test-pointer.c index abc8a118..d4d26079 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -952,6 +952,11 @@ START_TEST(pointer_left_handed_during_click_multiple_buttons) struct libinput *li = dev->libinput; enum libinput_config_status status; + if (!libinput_device_pointer_has_button(d, BTN_MIDDLE)) + return; + + litest_disable_middleemu(dev); + litest_drain_events(li); litest_button_click(dev, BTN_LEFT, 1); libinput_dispatch(li); @@ -1046,6 +1051,11 @@ START_TEST(pointer_scroll_button_no_event_before_timeout) struct libinput *li = device->libinput; int i; + if (!libinput_device_pointer_has_button(device->libinput_device, + BTN_MIDDLE)) + return; + + litest_disable_middleemu(device); disable_button_scrolling(device); libinput_device_config_scroll_set_method(device->libinput_device, -- cgit v1.2.3 From 53e263fce7c83ca7636a656ecfb1d455564f2c86 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Feb 2017 09:26:05 +1000 Subject: test: fix the pointer scroll-defaults test The button-scroll by default behavior is only true on devices with a middle button. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/test-pointer.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test/test-pointer.c b/test/test-pointer.c index d4d26079..27978a98 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -1136,19 +1136,28 @@ START_TEST(pointer_scroll_nowheel_defaults) { struct litest_device *dev = litest_current_device(); struct libinput_device *device = dev->libinput_device; - enum libinput_config_scroll_method method; + enum libinput_config_scroll_method method, expected; uint32_t button; + /* button scrolling is only enabled if there is a + middle button present */ + if (libinput_device_pointer_has_button(device, BTN_MIDDLE)) + expected = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; + else + expected = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; + method = libinput_device_config_scroll_get_method(device); - ck_assert_int_eq(method, LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN); + ck_assert_int_eq(method, expected); method = libinput_device_config_scroll_get_default_method(device); - ck_assert_int_eq(method, LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN); + ck_assert_int_eq(method, expected); - button = libinput_device_config_scroll_get_button(device); - ck_assert_int_eq(button, BTN_MIDDLE); - button = libinput_device_config_scroll_get_default_button(device); - ck_assert_int_eq(button, BTN_MIDDLE); + if (method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) { + button = libinput_device_config_scroll_get_button(device); + ck_assert_int_eq(button, BTN_MIDDLE); + button = libinput_device_config_scroll_get_default_button(device); + ck_assert_int_eq(button, BTN_MIDDLE); + } } END_TEST -- cgit v1.2.3 From e43f9da9ecea6d17100c0012a89f5d162b2d7d7b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Feb 2017 09:14:42 +1000 Subject: evdev: allow button scrolling on the L/R button with middle emulation active This worked before, but triggered a negative timer bug. When one of the physical L/R buttons is pressed with middle button emulation enabled, the flow is: 1) phys left button down 2) middle button state machine discards events, sets timer 3) timer expires or button is released 4) middle button state machine sends button press with time from 1) 5) emulation code sees button press, sets timer for scroll emulation 6) timer logs bug because (original-button-time + timeout) is less than now() That log_bug_libinput() warning fails the tests but works otherwise. Allow this situation explicitly, on some devices we only have left and right buttons and no scroll wheel, so having middle button emulation *and* button-scroll working is useful. https://bugs.freedesktop.org/show_bug.cgi?id=99845 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 20 ++++++++++++++++++-- src/timer.c | 22 ++++++++++++++++------ src/timer.h | 10 ++++++++++ 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index b6913331..1b7d5a83 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -231,8 +231,24 @@ evdev_button_scroll_button(struct evdev_device *device, device->scroll.button_scroll_btn_pressed = is_press; if (is_press) { - libinput_timer_set(&device->scroll.timer, - time + DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT); + enum timer_flags flags = TIMER_FLAG_NONE; + + /* Special case: if middle button emulation is enabled and + * our scroll button is the left or right button, we only + * get here *after* the middle button timeout has expired + * for that button press. The time passed is the button-down + * time though (which is in the past), so we have to allow + * for a negative timer to be set. + */ + if (device->middlebutton.enabled && + (device->scroll.button == BTN_LEFT || + device->scroll.button == BTN_RIGHT)) { + flags = TIMER_FLAG_ALLOW_NEGATIVE; + } + + libinput_timer_set_flags(&device->scroll.timer, + time + DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT, + flags); device->scroll.button_down_time = time; } else { libinput_timer_cancel(&device->scroll.timer); diff --git a/src/timer.c b/src/timer.c index c9247942..362d81db 100644 --- a/src/timer.c +++ b/src/timer.c @@ -67,19 +67,23 @@ libinput_timer_arm_timer_fd(struct libinput *libinput) } void -libinput_timer_set(struct libinput_timer *timer, uint64_t expire) +libinput_timer_set_flags(struct libinput_timer *timer, + uint64_t expire, + uint32_t flags) { #ifndef NDEBUG uint64_t now = libinput_now(timer->libinput); - if (expire < now) - log_bug_libinput(timer->libinput, - "timer offset negative (-%" PRIu64 ")\n", - now - expire); - else if ((expire - now) > ms2us(5000)) + if (expire < now) { + if ((flags & TIMER_FLAG_ALLOW_NEGATIVE) == 0) + log_bug_libinput(timer->libinput, + "timer offset negative (-%" PRIu64 ")\n", + now - expire); + } else if ((expire - now) > ms2us(5000)) { log_bug_libinput(timer->libinput, "timer offset more than 5s, now %" PRIu64 " expire %" PRIu64 "\n", now, expire); + } #endif assert(expire); @@ -91,6 +95,12 @@ libinput_timer_set(struct libinput_timer *timer, uint64_t expire) libinput_timer_arm_timer_fd(timer->libinput); } +void +libinput_timer_set(struct libinput_timer *timer, uint64_t expire) +{ + libinput_timer_set_flags(timer, expire, TIMER_FLAG_NONE); +} + void libinput_timer_cancel(struct libinput_timer *timer) { diff --git a/src/timer.h b/src/timer.h index f8315cfe..93d684ab 100644 --- a/src/timer.h +++ b/src/timer.h @@ -47,6 +47,16 @@ libinput_timer_init(struct libinput_timer *timer, struct libinput *libinput, void libinput_timer_set(struct libinput_timer *timer, uint64_t expire); +enum timer_flags { + TIMER_FLAG_NONE = 0, + TIMER_FLAG_ALLOW_NEGATIVE = (1 << 0), +}; + +void +libinput_timer_set_flags(struct libinput_timer *timer, + uint64_t expire, + uint32_t flags); + void libinput_timer_cancel(struct libinput_timer *timer); -- cgit v1.2.3 From f7f849e576a5481751a8c3d086fc1b1b752fecc4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 17 Feb 2017 08:39:51 +1000 Subject: evdev: add quirk for Logitech Marble Mouse Device needs BTN_MIDDLE disabled, this way middle button emulation is present by default. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 5 +++++ src/evdev.h | 1 + test/test-device.c | 13 +++++++++++++ test/test-pointer.c | 14 ++++++++++++++ udev/90-libinput-model-quirks.hwdb | 4 ++++ 5 files changed, 37 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index 1b7d5a83..b1d2db20 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -2261,6 +2261,7 @@ evdev_read_model_flags(struct evdev_device *device) MODEL(HP_ZBOOK_STUDIO_G3), MODEL(HP_PAVILION_DM4_TOUCHPAD), MODEL(APPLE_TOUCHPAD_ONEBUTTON), + MODEL(LOGITECH_MARBLE_MOUSE), #undef MODEL { "ID_INPUT_TRACKBALL", EVDEV_MODEL_TRACKBALL }, { NULL, EVDEV_MODEL_DEFAULT }, @@ -2858,6 +2859,10 @@ evdev_pre_configure_model_quirks(struct evdev_device *device) * https://bugs.freedesktop.org/show_bug.cgi?id=98100 */ if (device->model_flags & EVDEV_MODEL_HP_ZBOOK_STUDIO_G3) libevdev_set_abs_maximum(device->evdev, ABS_MT_SLOT, 1); + + /* Logitech Marble Mouse claims to have a middle button */ + if (device->model_flags & EVDEV_MODEL_LOGITECH_MARBLE_MOUSE) + libevdev_disable_event_code(device->evdev, EV_KEY, BTN_MIDDLE); } struct evdev_device * diff --git a/src/evdev.h b/src/evdev.h index 967feaf3..35eec842 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -126,6 +126,7 @@ enum evdev_device_model { EVDEV_MODEL_HP_ZBOOK_STUDIO_G3 = (1 << 23), EVDEV_MODEL_HP_PAVILION_DM4_TOUCHPAD = (1 << 24), EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON = (1 << 25), + EVDEV_MODEL_LOGITECH_MARBLE_MOUSE = (1 << 26), }; struct mt_slot { diff --git a/test/test-device.c b/test/test-device.c index 48ba55db..fdbab20c 100644 --- a/test/test-device.c +++ b/test/test-device.c @@ -1482,6 +1482,18 @@ START_TEST(device_quirks_apple_magicmouse) } END_TEST +START_TEST(device_quirks_logitech_marble_mouse) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(li); + + ck_assert(!libinput_device_pointer_has_button(dev->libinput_device, + BTN_MIDDLE)); +} +END_TEST + START_TEST(device_capability_at_least_one) { struct litest_device *dev = litest_current_device(); @@ -1648,6 +1660,7 @@ litest_setup_tests_device(void) litest_add_for_device("device:quirks", device_quirks_no_abs_mt_y, LITEST_ANKER_MOUSE_KBD); litest_add_for_device("device:quirks", device_quirks_cyborg_rat_mode_button, LITEST_CYBORG_RAT); litest_add_for_device("device:quirks", device_quirks_apple_magicmouse, LITEST_MAGICMOUSE); + litest_add_for_device("device:quirks", device_quirks_logitech_marble_mouse, LITEST_LOGITECH_TRACKBALL); litest_add("device:capability", device_capability_at_least_one, LITEST_ANY, LITEST_ANY); litest_add("device:capability", device_capability_check_invalid, LITEST_ANY, LITEST_ANY); diff --git a/test/test-pointer.c b/test/test-pointer.c index 27978a98..8929a0da 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -1161,6 +1161,19 @@ START_TEST(pointer_scroll_nowheel_defaults) } END_TEST +START_TEST(pointer_scroll_defaults_logitech_marble) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_scroll_method method; + + method = libinput_device_config_scroll_get_method(device); + ck_assert_int_eq(method, LIBINPUT_CONFIG_SCROLL_NO_SCROLL); + method = libinput_device_config_scroll_get_default_method(device); + ck_assert_int_eq(method, LIBINPUT_CONFIG_SCROLL_NO_SCROLL); +} +END_TEST + START_TEST(pointer_accel_defaults) { struct litest_device *dev = litest_current_device(); @@ -1939,6 +1952,7 @@ litest_setup_tests_pointer(void) litest_add("pointer:scroll", pointer_scroll_button_no_event_before_timeout, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); litest_add("pointer:scroll", pointer_scroll_button_middle_emulation, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); litest_add("pointer:scroll", pointer_scroll_nowheel_defaults, LITEST_RELATIVE|LITEST_BUTTON, LITEST_WHEEL); + litest_add_for_device("pointer:scroll", pointer_scroll_defaults_logitech_marble , LITEST_LOGITECH_TRACKBALL); litest_add("pointer:scroll", pointer_scroll_natural_defaults, LITEST_WHEEL, LITEST_TABLET); litest_add("pointer:scroll", pointer_scroll_natural_defaults_noscroll, LITEST_ANY, LITEST_WHEEL); litest_add("pointer:scroll", pointer_scroll_natural_enable_config, LITEST_WHEEL, LITEST_TABLET); diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index c1d6235f..2ff50b68 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -169,6 +169,10 @@ libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:*:pvrThinkPadX1Carbon3rd libinput:name:*Logitech M570*:dmi:* LIBINPUT_MODEL_TRACKBALL=1 +# Logitech Marble Mouse trackball +libinput:mouse:input:b0003v046DpC408* + LIBINPUT_MODEL_LOGITECH_MARBLE_MOUSE=1 + ########################################## # Microsoft ########################################## -- cgit v1.2.3 From 08d1655ebe9bcf5ad9dd24de3f3596939369e335 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Feb 2017 12:56:06 +1000 Subject: evdev: rename define for button scroll timeout This is merely 'button scrolling' now, only the original implementation was middle button only. And to avoid confusing with the middle button emulation, drop "MIDDLE" from the define. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index b1d2db20..1af29a4a 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -48,7 +48,7 @@ #endif #define DEFAULT_WHEEL_CLICK_ANGLE 15 -#define DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT ms2us(200) +#define DEFAULT_BUTTON_SCROLL_TIMEOUT ms2us(200) enum evdev_key_type { EVDEV_KEY_TYPE_NONE, @@ -247,7 +247,7 @@ evdev_button_scroll_button(struct evdev_device *device, } libinput_timer_set_flags(&device->scroll.timer, - time + DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT, + time + DEFAULT_BUTTON_SCROLL_TIMEOUT, flags); device->scroll.button_down_time = time; } else { -- cgit v1.2.3 From 1cd901694f7d3e74b039098e989edb8ae5e710cc Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Feb 2017 13:08:58 +1000 Subject: evdev: add state debugging to button scrolling Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 1af29a4a..bf35acb3 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -250,13 +250,19 @@ evdev_button_scroll_button(struct evdev_device *device, time + DEFAULT_BUTTON_SCROLL_TIMEOUT, flags); device->scroll.button_down_time = time; + log_debug(evdev_libinput_context(device), + "btnscroll: down\n"); } else { libinput_timer_cancel(&device->scroll.timer); if (device->scroll.button_scroll_active) { + log_debug(evdev_libinput_context(device), + "btnscroll: up\n"); evdev_stop_scroll(device, time, LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS); device->scroll.button_scroll_active = false; } else { + log_debug(evdev_libinput_context(device), + "btnscroll: cancel\n"); /* If the button is released quickly enough emit the * button press/release events. */ evdev_pointer_post_button(device, @@ -383,8 +389,11 @@ evdev_post_trackpoint_scroll(struct evdev_device *device, evdev_post_scroll(device, time, LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS, &unaccel); - /* if the button is down but scroll is not active, we're within the - timeout where swallow motion events but don't post scroll buttons */ + else + /* if the button is down but scroll is not active, we're within the + timeout where swallow motion events but don't post scroll buttons */ + log_debug(evdev_libinput_context(device), + "btnscroll: discarding\n"); return true; } -- cgit v1.2.3 From bcf8c222cb0402779b9a59e88d6929c29a275d99 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Feb 2017 13:09:57 +1000 Subject: test: add tests for middle button + button scrolling on BTN_LEFT Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/test-pointer.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/test/test-pointer.c b/test/test-pointer.c index 8929a0da..f6dae75d 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -1903,6 +1903,134 @@ START_TEST(middlebutton_default_disabled) } END_TEST +START_TEST(middlebutton_button_scrolling) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + struct libinput *li = dev->libinput; + enum libinput_config_status status; + struct libinput_event *ev; + struct libinput_event_pointer *pev; + int i; + + status = libinput_device_config_middle_emulation_set_enabled( + device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); + if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) + return; + + status = libinput_device_config_scroll_set_method(device, + LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN); + if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) + return; + + status = libinput_device_config_scroll_set_button(device, BTN_LEFT); + if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) + return; + + litest_drain_events(li); + + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + /* middle emulation discards */ + litest_assert_empty_queue(li); + + litest_timeout_middlebutton(); + libinput_dispatch(li); + + /* scroll discards */ + litest_assert_empty_queue(li); + litest_timeout_buttonscroll(); + libinput_dispatch(li); + + for (i = 0; i < 10; i++) { + litest_event(dev, EV_REL, REL_Y, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + } + + ev = libinput_get_event(li); + do { + pev = litest_is_axis_event(ev, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, + LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS); + ck_assert_double_gt(libinput_event_pointer_get_axis_value(pev, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL), + 0.0); + libinput_event_destroy(ev); + ev = libinput_get_event(li); + } while (ev); + + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + ev = libinput_get_event(li); + pev = litest_is_axis_event(ev, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, + LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS); + ck_assert_double_eq(libinput_event_pointer_get_axis_value(pev, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL), + 0.0); + libinput_event_destroy(ev); + + /* no button release */ + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(middlebutton_button_scrolling_middle) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + struct libinput *li = dev->libinput; + enum libinput_config_status status; + + status = libinput_device_config_middle_emulation_set_enabled( + device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); + if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) + return; + + status = libinput_device_config_scroll_set_method(device, + LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN); + if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) + return; + + status = libinput_device_config_scroll_set_button(device, BTN_LEFT); + if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) + return; + + litest_drain_events(li); + + /* button scrolling should not stop middle emulation */ + + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_RIGHT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_RIGHT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(pointer_time_usec) { struct litest_device *dev = litest_current_device(); @@ -1989,6 +2117,8 @@ litest_setup_tests_pointer(void) litest_add("pointer:middlebutton", middlebutton_default_touchpad, LITEST_TOUCHPAD, LITEST_CLICKPAD); litest_add("pointer:middlebutton", middlebutton_default_disabled, LITEST_ANY, LITEST_BUTTON); litest_add_for_device("pointer:middlebutton", middlebutton_default_alps, LITEST_ALPS_SEMI_MT); + litest_add("pointer:middlebutton", middlebutton_button_scrolling, LITEST_RELATIVE|LITEST_BUTTON, LITEST_CLICKPAD); + litest_add("pointer:middlebutton", middlebutton_button_scrolling_middle, LITEST_RELATIVE|LITEST_BUTTON, LITEST_CLICKPAD); litest_add_ranged("pointer:state", pointer_absolute_initial_state, LITEST_ABSOLUTE, LITEST_ANY, &axis_range); -- cgit v1.2.3 From 3697b72071fadb5ba7319cfd54810bf2462039f7 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Feb 2017 13:31:23 +1000 Subject: evdev: convert button scrolling into a state machine No functional changes, preparation work for adding another state. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 49 +++++++++++++++++++++++++++++++------------------ src/evdev.h | 9 +++++++-- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index bf35acb3..a50d7fa9 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -221,18 +221,18 @@ evdev_button_scroll_timeout(uint64_t time, void *data) { struct evdev_device *device = data; - device->scroll.button_scroll_active = true; + device->scroll.button_scroll_state = BUTTONSCROLL_SCROLLING; } static void evdev_button_scroll_button(struct evdev_device *device, uint64_t time, int is_press) { - device->scroll.button_scroll_btn_pressed = is_press; - if (is_press) { enum timer_flags flags = TIMER_FLAG_NONE; + device->scroll.button_scroll_state = BUTTONSCROLL_BUTTON_DOWN; + /* Special case: if middle button emulation is enabled and * our scroll button is the left or right button, we only * get here *after* the middle button timeout has expired @@ -254,13 +254,12 @@ evdev_button_scroll_button(struct evdev_device *device, "btnscroll: down\n"); } else { libinput_timer_cancel(&device->scroll.timer); - if (device->scroll.button_scroll_active) { - log_debug(evdev_libinput_context(device), - "btnscroll: up\n"); - evdev_stop_scroll(device, time, - LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS); - device->scroll.button_scroll_active = false; - } else { + switch(device->scroll.button_scroll_state) { + case BUTTONSCROLL_IDLE: + log_bug_libinput(evdev_libinput_context(device), + "invalid state IDLE for button up\n"); + break; + case BUTTONSCROLL_BUTTON_DOWN: log_debug(evdev_libinput_context(device), "btnscroll: cancel\n"); /* If the button is released quickly enough emit the @@ -272,7 +271,16 @@ evdev_button_scroll_button(struct evdev_device *device, evdev_pointer_post_button(device, time, device->scroll.button, LIBINPUT_BUTTON_STATE_RELEASED); + break; + case BUTTONSCROLL_SCROLLING: + log_debug(evdev_libinput_context(device), + "btnscroll: up\n"); + evdev_stop_scroll(device, time, + LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS); + break; } + + device->scroll.button_scroll_state = BUTTONSCROLL_IDLE; } } @@ -381,21 +389,26 @@ evdev_post_trackpoint_scroll(struct evdev_device *device, struct normalized_coords unaccel, uint64_t time) { - if (device->scroll.method != LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN || - !device->scroll.button_scroll_btn_pressed) + if (device->scroll.method != LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) return false; - if (device->scroll.button_scroll_active) - evdev_post_scroll(device, time, - LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS, - &unaccel); - else + switch(device->scroll.button_scroll_state) { + case BUTTONSCROLL_IDLE: + return false; + case BUTTONSCROLL_BUTTON_DOWN: /* if the button is down but scroll is not active, we're within the timeout where swallow motion events but don't post scroll buttons */ log_debug(evdev_libinput_context(device), "btnscroll: discarding\n"); + return true; + case BUTTONSCROLL_SCROLLING: + evdev_post_scroll(device, time, + LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS, + &unaccel); + return true; + } - return true; + assert(!"invalid scroll button state"); } static inline bool diff --git a/src/evdev.h b/src/evdev.h index 35eec842..d481d5ce 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -129,6 +129,12 @@ enum evdev_device_model { EVDEV_MODEL_LOGITECH_MARBLE_MOUSE = (1 << 26), }; +enum evdev_button_scroll_state { + BUTTONSCROLL_IDLE, + BUTTONSCROLL_BUTTON_DOWN, /* button is down */ + BUTTONSCROLL_SCROLLING, /* scrolling */ +}; + struct mt_slot { int32_t seat_slot; struct device_coords point; @@ -188,8 +194,7 @@ struct evdev_device { uint32_t want_button; /* Checks if buttons are down and commits the setting */ void (*change_scroll_method)(struct evdev_device *device); - bool button_scroll_active; - bool button_scroll_btn_pressed; + enum evdev_button_scroll_state button_scroll_state; double threshold; double direction_lock_threshold; uint32_t direction; -- cgit v1.2.3 From 6a2870f5ca88d20e9e5a9c668ba95154815e0d49 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Feb 2017 13:32:07 +1000 Subject: evdev: add "READY" state to button scrolling Before, our states were idle, button down and scrolling. This adds a state where the button is down and the timeout has expired (i.e. we're ready to send scroll events) but we haven't actually sent any events anymore. If the button is released in this state, we generate a normal click event. https://bugs.freedesktop.org/show_bug.cgi?id=99666 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 10 ++++++++-- src/evdev.h | 3 ++- test/test-pointer.c | 16 +++++++++++++--- test/test-trackpoint.c | 9 +++++++-- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index a50d7fa9..d50f9bb1 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -221,7 +221,7 @@ evdev_button_scroll_timeout(uint64_t time, void *data) { struct evdev_device *device = data; - device->scroll.button_scroll_state = BUTTONSCROLL_SCROLLING; + device->scroll.button_scroll_state = BUTTONSCROLL_READY; } static void @@ -260,9 +260,12 @@ evdev_button_scroll_button(struct evdev_device *device, "invalid state IDLE for button up\n"); break; case BUTTONSCROLL_BUTTON_DOWN: + case BUTTONSCROLL_READY: log_debug(evdev_libinput_context(device), "btnscroll: cancel\n"); - /* If the button is released quickly enough emit the + + /* If the button is released quickly enough or + * without scroll events, emit the * button press/release events. */ evdev_pointer_post_button(device, device->scroll.button_down_time, @@ -401,6 +404,9 @@ evdev_post_trackpoint_scroll(struct evdev_device *device, log_debug(evdev_libinput_context(device), "btnscroll: discarding\n"); return true; + case BUTTONSCROLL_READY: + device->scroll.button_scroll_state = BUTTONSCROLL_SCROLLING; + /* fallthrough */ case BUTTONSCROLL_SCROLLING: evdev_post_scroll(device, time, LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS, diff --git a/src/evdev.h b/src/evdev.h index d481d5ce..392d71cf 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -132,7 +132,8 @@ enum evdev_device_model { enum evdev_button_scroll_state { BUTTONSCROLL_IDLE, BUTTONSCROLL_BUTTON_DOWN, /* button is down */ - BUTTONSCROLL_SCROLLING, /* scrolling */ + BUTTONSCROLL_READY, /* ready for scroll events */ + BUTTONSCROLL_SCROLLING, /* have sent scroll events */ }; struct mt_slot { diff --git a/test/test-pointer.c b/test/test-pointer.c index f6dae75d..8498da95 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -1006,11 +1006,15 @@ START_TEST(pointer_scroll_button) litest_button_scroll(dev, BTN_LEFT, -9, 1); litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -9); - /* scroll smaller than the threshold should not generate events */ + /* scroll smaller than the threshold should not generate axis events */ litest_button_scroll(dev, BTN_LEFT, 1, 1); - /* left press without movement should not generate events */ - litest_button_scroll(dev, BTN_LEFT, 0, 0); + litest_button_scroll(dev, BTN_LEFT, 0, 0); + 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); /* Restore default scroll behavior */ @@ -1076,6 +1080,12 @@ START_TEST(pointer_scroll_button_no_event_before_timeout) litest_timeout_buttonscroll(); libinput_dispatch(li); litest_button_click(device, BTN_LEFT, false); + + 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 diff --git a/test/test-trackpoint.c b/test/test-trackpoint.c index e9ba0271..adbd46e8 100644 --- a/test/test-trackpoint.c +++ b/test/test-trackpoint.c @@ -85,10 +85,15 @@ START_TEST(trackpoint_scroll) litest_button_scroll(dev, BTN_MIDDLE, -9, 1); litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -9); - /* scroll smaller than the threshold should not generate events */ + /* scroll smaller than the threshold should not generate axis events */ litest_button_scroll(dev, BTN_MIDDLE, 1, 1); - /* long middle press without movement should not generate events */ + litest_button_scroll(dev, BTN_MIDDLE, 0, 0); + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } -- cgit v1.2.3 From c225c0592c4b3b61a01b6227f293b7fb853d5dbf Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Feb 2017 16:29:02 +1000 Subject: evdev: don't provide button scrolling on absolute pointer devices This may be a feature for the future but for now be honest and don't claim that button-based scrolling is available, it's not hooked up in the absolute code path. https://bugs.freedesktop.org/show_bug.cgi?id=99865 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 4 +++- test/test-pointer.c | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/evdev.c b/src/evdev.c index d50f9bb1..6132d54f 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -2688,7 +2688,9 @@ evdev_configure_device(struct evdev_device *device) /* want natural-scroll config option */ device->scroll.natural_scrolling_enabled = true; /* want button scrolling config option */ - device->scroll.want_button = 1; + if (libevdev_has_event_code(evdev, EV_REL, REL_X) || + libevdev_has_event_code(evdev, EV_REL, REL_Y)) + device->scroll.want_button = 1; } if (udev_tags & EVDEV_UDEV_TAG_KEYBOARD) { diff --git a/test/test-pointer.c b/test/test-pointer.c index 8498da95..06c45b2c 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -2086,6 +2086,7 @@ litest_setup_tests_pointer(void) litest_add_for_device("pointer:button", pointer_button_has_no_button, LITEST_KEYBOARD); litest_add("pointer:scroll", pointer_scroll_wheel, LITEST_WHEEL, LITEST_TABLET); litest_add("pointer:scroll", pointer_scroll_button, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); + litest_add("pointer:scroll", pointer_scroll_button_noscroll, LITEST_ABSOLUTE|LITEST_BUTTON, LITEST_RELATIVE); litest_add("pointer:scroll", pointer_scroll_button_noscroll, LITEST_ANY, LITEST_RELATIVE|LITEST_BUTTON); litest_add("pointer:scroll", pointer_scroll_button_no_event_before_timeout, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); litest_add("pointer:scroll", pointer_scroll_button_middle_emulation, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); -- cgit v1.2.3