summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2017-02-22 07:56:07 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2017-02-22 07:56:07 +1000
commit4ce0fe4a2d2e54e401b1073dfc1c894d596a4a0f (patch)
tree8a8ee0fac17412f208e9759770c3386b847922da
parent765ef9a31dc3b8551f388858392bfa64ee013c66 (diff)
parentc225c0592c4b3b61a01b6227f293b7fb853d5dbf (diff)
Merge branch 'wip/logitech-marble-mouse-middleemulation'
-rw-r--r--src/evdev.c89
-rw-r--r--src/evdev.h11
-rw-r--r--src/timer.c22
-rw-r--r--src/timer.h10
-rw-r--r--test/test-device.c13
-rw-r--r--test/test-pointer.c227
-rw-r--r--test/test-trackpoint.c9
-rw-r--r--udev/90-libinput-model-quirks.hwdb4
8 files changed, 330 insertions, 55 deletions
diff --git a/src/evdev.c b/src/evdev.c
index b6913331..6132d54f 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,
@@ -221,27 +221,51 @@ 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_READY;
}
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) {
- libinput_timer_set(&device->scroll.timer,
- time + DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT);
+ 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
+ * 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_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) {
- evdev_stop_scroll(device, time,
- LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
- device->scroll.button_scroll_active = false;
- } else {
- /* If the button is released quickly enough emit the
+ 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:
+ case BUTTONSCROLL_READY:
+ log_debug(evdev_libinput_context(device),
+ "btnscroll: cancel\n");
+
+ /* 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,
@@ -250,7 +274,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;
}
}
@@ -359,18 +392,29 @@ 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)
+ 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_READY:
+ device->scroll.button_scroll_state = BUTTONSCROLL_SCROLLING;
+ /* fallthrough */
+ case BUTTONSCROLL_SCROLLING:
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 */
+ return true;
+ }
- return true;
+ assert(!"invalid scroll button state");
}
static inline bool
@@ -2245,6 +2289,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 },
@@ -2643,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) {
@@ -2842,6 +2889,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..392d71cf 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -126,6 +126,14 @@ 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),
+};
+
+enum evdev_button_scroll_state {
+ BUTTONSCROLL_IDLE,
+ BUTTONSCROLL_BUTTON_DOWN, /* button is down */
+ BUTTONSCROLL_READY, /* ready for scroll events */
+ BUTTONSCROLL_SCROLLING, /* have sent scroll events */
};
struct mt_slot {
@@ -187,8 +195,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;
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);
@@ -92,6 +96,12 @@ libinput_timer_set(struct libinput_timer *timer, uint64_t expire)
}
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)
{
if (!timer->expire)
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);
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 6485258e..06c45b2c 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,
@@ -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);
@@ -1001,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 */
@@ -1046,6 +1055,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,
@@ -1066,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
@@ -1126,19 +1146,41 @@ 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
+
+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
@@ -1614,7 +1656,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 +1721,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(
@@ -1740,22 +1784,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 (libevdev_has_event_code(dev->evdev, EV_KEY, 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);
@@ -1815,7 +1858,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(
@@ -1870,6 +1913,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();
@@ -1915,10 +2086,12 @@ 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);
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);
@@ -1955,6 +2128,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);
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);
}
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
##########################################