From 8cf6893f6dc3e0e4ecc029c49ce5ceb1887a30d4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 14 Nov 2017 08:44:47 +1000 Subject: [PATCH 1/5] test: replace litest_button_click with a debounced version This is via a simple search & replace. Later auditing is needed to switch clicks that should not be debounced (e.g. touchpads) back to a non-debounced version. Signed-off-by: Peter Hutterer --- test/litest.c | 20 ++++-- test/litest.h | 7 +- test/test-device.c | 12 ++-- test/test-misc.c | 6 +- test/test-pad.c | 16 ++--- test/test-pointer.c | 122 ++++++++++++++++++----------------- test/test-touchpad-buttons.c | 8 +-- test/test-touchpad-tap.c | 12 ++-- test/test-touchpad.c | 116 ++++++++++++++++----------------- test/test-trackpoint.c | 16 ++--- 10 files changed, 177 insertions(+), 158 deletions(-) diff --git a/test/litest.c b/test/litest.c index 0784228b..9819bed9 100644 --- a/test/litest.c +++ b/test/litest.c @@ -2018,7 +2018,10 @@ litest_hover_move_two_touches(struct litest_device *d, } void -litest_button_click(struct litest_device *d, unsigned int button, bool is_press) +litest_button_click_debounced(struct litest_device *d, + struct libinput *li, + unsigned int button, + bool is_press) { struct input_event *ev; @@ -2029,7 +2032,9 @@ litest_button_click(struct litest_device *d, unsigned int button, bool is_press) ARRAY_FOR_EACH(click, ev) litest_event(d, ev->type, ev->code, ev->value); + libinput_dispatch(li); litest_timeout_debounce(); + libinput_dispatch(li); } void @@ -2039,7 +2044,7 @@ litest_button_scroll(struct litest_device *dev, { struct libinput *li = dev->libinput; - litest_button_click(dev, button, 1); + litest_button_click_debounced(dev, li, button, 1); libinput_dispatch(li); litest_timeout_buttonscroll(); @@ -2049,7 +2054,7 @@ litest_button_scroll(struct litest_device *dev, litest_event(dev, EV_REL, REL_Y, dy); litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_button_click(dev, button, 0); + litest_button_click_debounced(dev, li, button, 0); libinput_dispatch(li); } @@ -2057,7 +2062,14 @@ litest_button_scroll(struct litest_device *dev, void litest_keyboard_key(struct litest_device *d, unsigned int key, bool is_press) { - litest_button_click(d, key, is_press); + struct input_event *ev; + struct input_event click[] = { + { .type = EV_KEY, .code = key, .value = is_press ? 1 : 0 }, + { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, + }; + + ARRAY_FOR_EACH(click, ev) + litest_event(d, ev->type, ev->code, ev->value); } void diff --git a/test/litest.h b/test/litest.h index 400b15ec..5d1f97f5 100644 --- a/test/litest.h +++ b/test/litest.h @@ -597,9 +597,10 @@ litest_hover_move_two_touches(struct litest_device *d, int steps, int sleep_ms); void -litest_button_click(struct litest_device *d, - unsigned int button, - bool is_press); +litest_button_click_debounced(struct litest_device *d, + struct libinput *li, + unsigned int button, + bool is_press); void litest_button_scroll(struct litest_device *d, diff --git a/test/test-device.c b/test/test-device.c index c4dfdd47..7c39131f 100644 --- a/test/test-device.c +++ b/test/test-device.c @@ -463,7 +463,7 @@ START_TEST(device_disable_release_buttons) device = dev->libinput_device; - litest_button_click(dev, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, true); litest_drain_events(li); status = libinput_device_config_send_events_set_mode(device, @@ -497,7 +497,7 @@ START_TEST(device_disable_release_keys) device = dev->libinput_device; - litest_button_click(dev, KEY_A, true); + litest_button_click_debounced(dev, li, KEY_A, true); litest_drain_events(li); status = libinput_device_config_send_events_set_mode(device, @@ -616,7 +616,7 @@ START_TEST(device_disable_release_softbutton) litest_drain_events(li); litest_touch_down(dev, 0, 90, 90); - litest_button_click(dev, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, true); /* make sure softbutton works */ litest_assert_button_event(li, @@ -633,7 +633,7 @@ START_TEST(device_disable_release_softbutton) litest_assert_empty_queue(li); - litest_button_click(dev, BTN_LEFT, false); + litest_button_click_debounced(dev, li, BTN_LEFT, false); litest_touch_up(dev, 0); litest_assert_empty_queue(li); @@ -669,8 +669,8 @@ START_TEST(device_disable_topsoftbutton) litest_drain_events(li); litest_touch_down(dev, 0, 90, 10); - litest_button_click(dev, BTN_LEFT, true); - litest_button_click(dev, BTN_LEFT, false); + litest_button_click_debounced(dev, li, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, false); litest_touch_up(dev, 0); litest_wait_for_event(li); diff --git a/test/test-misc.c b/test/test-misc.c index 32081c03..5a4f6ad6 100644 --- a/test/test-misc.c +++ b/test/test-misc.c @@ -416,8 +416,8 @@ START_TEST(event_conversion_tablet) litest_tablet_proximity_in(dev, 50, 50, axes); litest_tablet_motion(dev, 60, 50, axes); - litest_button_click(dev, BTN_STYLUS, true); - litest_button_click(dev, BTN_STYLUS, false); + litest_button_click_debounced(dev, li, BTN_STYLUS, true); + litest_button_click_debounced(dev, li, BTN_STYLUS, false); libinput_dispatch(li); @@ -458,7 +458,7 @@ START_TEST(event_conversion_tablet_pad) struct libinput_event *event; int events = 0; - litest_button_click(dev, BTN_0, true); + litest_button_click_debounced(dev, li, BTN_0, true); litest_pad_ring_start(dev, 10); litest_pad_ring_end(dev); diff --git a/test/test-pad.c b/test/test-pad.c index 5e4a66f5..b0c583d7 100644 --- a/test/test-pad.c +++ b/test/test-pad.c @@ -68,8 +68,8 @@ START_TEST(pad_time) if (!libevdev_has_event_code(dev->evdev, EV_KEY, code)) continue; - litest_button_click(dev, code, 1); - litest_button_click(dev, code, 0); + litest_button_click_debounced(dev, li, code, 1); + litest_button_click_debounced(dev, li, code, 0); libinput_dispatch(li); switch (code) { @@ -98,8 +98,8 @@ START_TEST(pad_time) litest_drain_events(li); msleep(10); - litest_button_click(dev, code, 1); - litest_button_click(dev, code, 0); + litest_button_click_debounced(dev, li, code, 1); + litest_button_click_debounced(dev, li, code, 0); libinput_dispatch(li); ev = libinput_get_event(li); @@ -156,8 +156,8 @@ START_TEST(pad_button) if (!libevdev_has_event_code(dev->evdev, EV_KEY, code)) continue; - litest_button_click(dev, code, 1); - litest_button_click(dev, code, 0); + litest_button_click_debounced(dev, li, code, 1); + litest_button_click_debounced(dev, li, code, 0); libinput_dispatch(li); switch (code) { @@ -207,8 +207,8 @@ START_TEST(pad_button_mode_groups) if (!libevdev_has_event_code(dev->evdev, EV_KEY, code)) continue; - litest_button_click(dev, code, 1); - litest_button_click(dev, code, 0); + litest_button_click_debounced(dev, li, code, 1); + litest_button_click_debounced(dev, li, code, 0); libinput_dispatch(li); switch (code) { diff --git a/test/test-pointer.c b/test/test-pointer.c index 3ce80840..136aa48c 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -348,7 +348,7 @@ test_button_event(struct litest_device *dev, unsigned int button, int state) { struct libinput *li = dev->libinput; - litest_button_click(dev, button, state); + litest_button_click_debounced(dev, li, button, state); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_assert_button_event(li, button, @@ -500,7 +500,7 @@ START_TEST(pointer_recover_from_lost_button_count) litest_drain_events(dev->libinput); - litest_button_click(dev, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); litest_assert_button_event(li, BTN_LEFT, @@ -508,15 +508,15 @@ START_TEST(pointer_recover_from_lost_button_count) /* Grab for the release to make libinput lose count */ libevdev_grab(evdev, LIBEVDEV_GRAB); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); libevdev_grab(evdev, LIBEVDEV_UNGRAB); litest_assert_empty_queue(li); - litest_button_click(dev, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); litest_assert_empty_queue(li); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED); @@ -802,7 +802,10 @@ START_TEST(pointer_seat_button_count) } for (i = 0; i < num_devices; ++i) - litest_button_click(devices[i], BTN_LEFT, true); + litest_button_click_debounced(devices[i], + libinput, + BTN_LEFT, + true); libinput_dispatch(libinput); while ((ev = libinput_get_event(libinput))) { @@ -832,7 +835,10 @@ START_TEST(pointer_seat_button_count) ck_assert_int_eq(seat_button_count, num_devices); for (i = 0; i < num_devices; ++i) - litest_button_click(devices[i], BTN_LEFT, false); + litest_button_click_debounced(devices[i], + libinput, + BTN_LEFT, + false); libinput_dispatch(libinput); while ((ev = libinput_get_event(libinput))) { @@ -921,8 +927,8 @@ START_TEST(pointer_left_handed) ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); litest_drain_events(li); - litest_button_click(dev, BTN_LEFT, 1); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_assert_button_event(li, BTN_RIGHT, @@ -931,8 +937,8 @@ START_TEST(pointer_left_handed) BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED); - litest_button_click(dev, BTN_RIGHT, 1); - litest_button_click(dev, BTN_RIGHT, 0); + litest_button_click_debounced(dev, li, BTN_RIGHT, 1); + litest_button_click_debounced(dev, li, BTN_RIGHT, 0); litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); @@ -941,8 +947,8 @@ START_TEST(pointer_left_handed) LIBINPUT_BUTTON_STATE_RELEASED); if (libinput_device_pointer_has_button(d, BTN_MIDDLE)) { - litest_button_click(dev, BTN_MIDDLE, 1); - litest_button_click(dev, BTN_MIDDLE, 0); + litest_button_click_debounced(dev, li, BTN_MIDDLE, 1); + litest_button_click_debounced(dev, li, BTN_MIDDLE, 0); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED); @@ -961,14 +967,14 @@ START_TEST(pointer_left_handed_during_click) enum libinput_config_status status; litest_drain_events(li); - litest_button_click(dev, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); libinput_dispatch(li); /* Change while button is down, expect correct release event */ status = libinput_device_config_left_handed_set(d, 1); ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_assert_button_event(li, BTN_LEFT, @@ -992,16 +998,16 @@ START_TEST(pointer_left_handed_during_click_multiple_buttons) litest_disable_middleemu(dev); litest_drain_events(li); - litest_button_click(dev, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); libinput_dispatch(li); status = libinput_device_config_left_handed_set(d, 1); ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); /* No left-handed until all buttons were down */ - litest_button_click(dev, BTN_RIGHT, 1); - litest_button_click(dev, BTN_RIGHT, 0); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_RIGHT, 1); + litest_button_click_debounced(dev, li, BTN_RIGHT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_assert_button_event(li, BTN_LEFT, @@ -1102,7 +1108,7 @@ START_TEST(pointer_scroll_button_no_event_before_timeout) BTN_LEFT); litest_drain_events(li); - litest_button_click(device, BTN_LEFT, true); + litest_button_click_debounced(device, li, BTN_LEFT, true); litest_assert_empty_queue(li); for (i = 0; i < 10; i++) { @@ -1113,7 +1119,7 @@ START_TEST(pointer_scroll_button_no_event_before_timeout) litest_timeout_buttonscroll(); libinput_dispatch(li); - litest_button_click(device, BTN_LEFT, false); + litest_button_click_debounced(device, li, BTN_LEFT, false); litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); @@ -1146,8 +1152,8 @@ START_TEST(pointer_scroll_button_middle_emulation) litest_drain_events(li); - litest_button_click(dev, BTN_LEFT, 1); - litest_button_click(dev, BTN_RIGHT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_RIGHT, 1); libinput_dispatch(li); litest_timeout_buttonscroll(); libinput_dispatch(li); @@ -1159,8 +1165,8 @@ START_TEST(pointer_scroll_button_middle_emulation) libinput_dispatch(li); - litest_button_click(dev, BTN_LEFT, 0); - litest_button_click(dev, BTN_RIGHT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_RIGHT, 0); libinput_dispatch(li); litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -1); @@ -1525,16 +1531,16 @@ START_TEST(middlebutton) litest_drain_events(li); for (i = 0; i < ARRAY_LENGTH(btn); i++) { - litest_button_click(device, btn[i][0], true); - litest_button_click(device, btn[i][1], true); + litest_button_click_debounced(device, li, btn[i][0], true); + litest_button_click_debounced(device, li, btn[i][1], true); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); - litest_button_click(device, btn[i][2], false); - litest_button_click(device, btn[i][3], false); + litest_button_click_debounced(device, li, btn[i][2], false); + litest_button_click_debounced(device, li, btn[i][3], false); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED); @@ -1568,33 +1574,33 @@ START_TEST(middlebutton_nostart_while_down) if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) return; - litest_button_click(device, BTN_MIDDLE, true); + litest_button_click_debounced(device, li, BTN_MIDDLE, true); litest_drain_events(li); for (i = 0; i < ARRAY_LENGTH(btn); i++) { - litest_button_click(device, btn[i][0], true); + litest_button_click_debounced(device, li, btn[i][0], true); litest_assert_button_event(li, btn[i][0], LIBINPUT_BUTTON_STATE_PRESSED); - litest_button_click(device, btn[i][1], true); + litest_button_click_debounced(device, li, btn[i][1], true); litest_assert_button_event(li, btn[i][1], LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); - litest_button_click(device, btn[i][2], false); + litest_button_click_debounced(device, li, btn[i][2], false); litest_assert_button_event(li, btn[i][2], LIBINPUT_BUTTON_STATE_RELEASED); - litest_button_click(device, btn[i][3], false); + litest_button_click_debounced(device, li, btn[i][3], false); litest_assert_button_event(li, btn[i][3], LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } - litest_button_click(device, BTN_MIDDLE, false); + litest_button_click_debounced(device, li, BTN_MIDDLE, false); litest_drain_events(li); } END_TEST @@ -1616,7 +1622,7 @@ START_TEST(middlebutton_timeout) for (button = BTN_LEFT; button <= BTN_RIGHT; button++) { litest_drain_events(li); - litest_button_click(device, button, true); + litest_button_click_debounced(device, li, button, true); litest_assert_empty_queue(li); litest_timeout_middlebutton(); @@ -1624,7 +1630,7 @@ START_TEST(middlebutton_timeout) button, LIBINPUT_BUTTON_STATE_PRESSED); - litest_button_click(device, button, false); + litest_button_click_debounced(device, li, button, false); litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED); @@ -1657,22 +1663,22 @@ START_TEST(middlebutton_doubleclick) litest_drain_events(li); for (i = 0; i < ARRAY_LENGTH(btn); i++) { - litest_button_click(device, btn[i][0], true); - litest_button_click(device, btn[i][1], true); + litest_button_click_debounced(device, li, btn[i][0], true); + litest_button_click_debounced(device, li, btn[i][1], true); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); - litest_button_click(device, btn[i][2], false); - litest_button_click(device, btn[i][2], true); + litest_button_click_debounced(device, li, btn[i][2], false); + litest_button_click_debounced(device, li, btn[i][2], true); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED); - litest_button_click(device, btn[i][3], false); + litest_button_click_debounced(device, li, btn[i][3], false); litest_assert_button_event(li, BTN_MIDDLE, @@ -1705,8 +1711,8 @@ START_TEST(middlebutton_middleclick) for (button = BTN_LEFT; button <= BTN_RIGHT; button++) { /* release button before middle */ litest_drain_events(li); - litest_button_click(device, button, true); - litest_button_click(device, BTN_MIDDLE, true); + litest_button_click_debounced(device, li, button, true); + litest_button_click_debounced(device, li, BTN_MIDDLE, true); litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED); @@ -1714,19 +1720,19 @@ START_TEST(middlebutton_middleclick) BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); - litest_button_click(device, button, false); + litest_button_click_debounced(device, li, button, false); litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED); - litest_button_click(device, BTN_MIDDLE, false); + litest_button_click_debounced(device, li, BTN_MIDDLE, false); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); /* release middle before button */ - litest_button_click(device, button, true); - litest_button_click(device, BTN_MIDDLE, true); + litest_button_click_debounced(device, li, button, true); + litest_button_click_debounced(device, li, BTN_MIDDLE, true); litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED); @@ -1734,11 +1740,11 @@ START_TEST(middlebutton_middleclick) BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); - litest_button_click(device, BTN_MIDDLE, false); + litest_button_click_debounced(device, li, BTN_MIDDLE, false); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED); - litest_button_click(device, button, false); + litest_button_click_debounced(device, li, button, false); litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED); @@ -1770,14 +1776,14 @@ START_TEST(middlebutton_middleclick_during) /* trigger emulation, then real middle */ for (button = BTN_LEFT; button <= BTN_RIGHT; button++) { - litest_button_click(device, BTN_LEFT, true); - litest_button_click(device, BTN_RIGHT, true); + litest_button_click_debounced(device, li, BTN_LEFT, true); + litest_button_click_debounced(device, li, BTN_RIGHT, true); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED); - litest_button_click(device, BTN_MIDDLE, true); + litest_button_click_debounced(device, li, BTN_MIDDLE, true); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED); @@ -1788,23 +1794,23 @@ START_TEST(middlebutton_middleclick_during) litest_assert_empty_queue(li); /* middle still down, release left/right */ - litest_button_click(device, button, false); + litest_button_click_debounced(device, li, button, false); litest_assert_empty_queue(li); - litest_button_click(device, button, true); + litest_button_click_debounced(device, li, button, true); litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); /* release both */ - litest_button_click(device, BTN_LEFT, false); - litest_button_click(device, BTN_RIGHT, false); + litest_button_click_debounced(device, li, BTN_LEFT, false); + litest_button_click_debounced(device, li, BTN_RIGHT, false); litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); - litest_button_click(device, BTN_MIDDLE, false); + litest_button_click_debounced(device, li, BTN_MIDDLE, false); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED); diff --git a/test/test-touchpad-buttons.c b/test/test-touchpad-buttons.c index 9f38e833..d7da7f1b 100644 --- a/test/test-touchpad-buttons.c +++ b/test/test-touchpad-buttons.c @@ -1041,7 +1041,7 @@ START_TEST(clickpad_finger_pin) litest_touch_move_to(dev, 0, 48, 48, 50, 50, 10, 1); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); - litest_button_click(dev, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, true); litest_drain_events(li); litest_touch_move_to(dev, 0, 50, 50, 50 + dist, 50 + dist, 10, 1); @@ -1050,7 +1050,7 @@ START_TEST(clickpad_finger_pin) litest_assert_empty_queue(li); - litest_button_click(dev, BTN_LEFT, false); + litest_button_click_debounced(dev, li, BTN_LEFT, false); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); /* still pinned after release */ @@ -1490,7 +1490,7 @@ START_TEST(clickpad_softbutton_hover_into_buttons) litest_touch_move_to(dev, 0, 90, 90, 91, 91, 1, 0); - litest_button_click(dev, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, true); libinput_dispatch(li); litest_assert_button_event(li, @@ -1498,7 +1498,7 @@ START_TEST(clickpad_softbutton_hover_into_buttons) LIBINPUT_BUTTON_STATE_PRESSED); litest_assert_empty_queue(li); - litest_button_click(dev, BTN_LEFT, false); + litest_button_click_debounced(dev, li, BTN_LEFT, false); litest_touch_up(dev, 0); litest_assert_button_event(li, diff --git a/test/test-touchpad-tap.c b/test/test-touchpad-tap.c index 6fc3c4f6..cd2f3e63 100644 --- a/test/test-touchpad-tap.c +++ b/test/test-touchpad-tap.c @@ -332,8 +332,8 @@ START_TEST(touchpad_1fg_multitap_n_drag_click) litest_touch_down(dev, 0, 50, 50); libinput_dispatch(li); - litest_button_click(dev, BTN_LEFT, true); - litest_button_click(dev, BTN_LEFT, false); + litest_button_click_debounced(dev, li, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, false); libinput_dispatch(li); for (ntaps = 0; ntaps <= range; ntaps++) { @@ -627,8 +627,8 @@ START_TEST(touchpad_1fg_multitap_n_drag_tap_click) litest_touch_up(dev, 0); litest_touch_down(dev, 0, 70, 50); - litest_button_click(dev, BTN_LEFT, true); - litest_button_click(dev, BTN_LEFT, false); + litest_button_click_debounced(dev, li, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, false); libinput_dispatch(li); litest_assert_button_event(li, @@ -799,8 +799,8 @@ START_TEST(touchpad_1fg_tap_n_drag_draglock_tap_click) litest_touch_up(dev, 0); litest_touch_down(dev, 0, 50, 50); - litest_button_click(dev, BTN_LEFT, true); - litest_button_click(dev, BTN_LEFT, false); + litest_button_click_debounced(dev, li, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, false); libinput_dispatch(li); litest_assert_button_event(li, BTN_LEFT, diff --git a/test/test-touchpad.c b/test/test-touchpad.c index 895de1f6..638d0b85 100644 --- a/test/test-touchpad.c +++ b/test/test-touchpad.c @@ -842,7 +842,7 @@ START_TEST(touchpad_edge_scroll_buttonareas_click_stops_scroll) litest_touch_move_to(dev, 0, 20, 95, 70, 95, 10, 5); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS); - litest_button_click(dev, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, true); libinput_dispatch(li); event = libinput_get_event(li); @@ -865,7 +865,7 @@ START_TEST(touchpad_edge_scroll_buttonareas_click_stops_scroll) litest_touch_move_to(dev, 0, 70, 95, 90, 95, 10, 0); litest_assert_empty_queue(li); - litest_button_click(dev, BTN_LEFT, false); + litest_button_click_debounced(dev, li, BTN_LEFT, false); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -892,7 +892,7 @@ START_TEST(touchpad_edge_scroll_clickfinger_click_stops_scroll) litest_touch_move_to(dev, 0, 20, 95, 70, 95, 10, 5); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS); - litest_button_click(dev, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, true); libinput_dispatch(li); event = libinput_get_event(li); @@ -916,7 +916,7 @@ START_TEST(touchpad_edge_scroll_clickfinger_click_stops_scroll) litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); litest_assert_empty_queue(li); - litest_button_click(dev, BTN_LEFT, false); + litest_button_click_debounced(dev, li, BTN_LEFT, false); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -1717,8 +1717,8 @@ START_TEST(touchpad_left_handed) ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); litest_drain_events(li); - litest_button_click(dev, BTN_LEFT, 1); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_assert_button_event(li, BTN_RIGHT, @@ -1727,8 +1727,8 @@ START_TEST(touchpad_left_handed) BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED); - litest_button_click(dev, BTN_RIGHT, 1); - litest_button_click(dev, BTN_RIGHT, 0); + litest_button_click_debounced(dev, li, BTN_RIGHT, 1); + litest_button_click_debounced(dev, li, BTN_RIGHT, 0); litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); @@ -1739,8 +1739,8 @@ START_TEST(touchpad_left_handed) if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) { - litest_button_click(dev, BTN_MIDDLE, 1); - litest_button_click(dev, BTN_MIDDLE, 0); + litest_button_click_debounced(dev, li, BTN_MIDDLE, 1); + litest_button_click_debounced(dev, li, BTN_MIDDLE, 0); litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED); @@ -1779,8 +1779,8 @@ START_TEST(touchpad_left_handed_clickpad) litest_drain_events(li); litest_touch_down(dev, 0, 10, 90); - litest_button_click(dev, BTN_LEFT, 1); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_touch_up(dev, 0); litest_assert_button_event(li, @@ -1792,8 +1792,8 @@ START_TEST(touchpad_left_handed_clickpad) litest_drain_events(li); litest_touch_down(dev, 0, 90, 90); - litest_button_click(dev, BTN_LEFT, 1); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_touch_up(dev, 0); litest_assert_button_event(li, @@ -1805,8 +1805,8 @@ START_TEST(touchpad_left_handed_clickpad) litest_drain_events(li); litest_touch_down(dev, 0, 50, 50); - litest_button_click(dev, BTN_LEFT, 1); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_touch_up(dev, 0); litest_assert_button_event(li, @@ -1833,8 +1833,8 @@ START_TEST(touchpad_left_handed_clickfinger) litest_drain_events(li); litest_touch_down(dev, 0, 10, 90); - litest_button_click(dev, BTN_LEFT, 1); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_touch_up(dev, 0); /* Clickfinger is unaffected by left-handed setting */ @@ -1848,8 +1848,8 @@ START_TEST(touchpad_left_handed_clickfinger) litest_drain_events(li); litest_touch_down(dev, 0, 10, 90); litest_touch_down(dev, 1, 30, 90); - litest_button_click(dev, BTN_LEFT, 1); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_touch_up(dev, 0); litest_touch_up(dev, 1); @@ -1943,13 +1943,13 @@ START_TEST(touchpad_left_handed_delayed) return; litest_drain_events(li); - litest_button_click(dev, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); libinput_dispatch(li); status = libinput_device_config_left_handed_set(d, 1); ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_assert_button_event(li, BTN_LEFT, @@ -1959,18 +1959,18 @@ START_TEST(touchpad_left_handed_delayed) LIBINPUT_BUTTON_STATE_RELEASED); /* left-handed takes effect now */ - litest_button_click(dev, BTN_RIGHT, 1); + litest_button_click_debounced(dev, li, BTN_RIGHT, 1); libinput_dispatch(li); litest_timeout_middlebutton(); libinput_dispatch(li); - litest_button_click(dev, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); libinput_dispatch(li); status = libinput_device_config_left_handed_set(d, 0); ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); - litest_button_click(dev, BTN_RIGHT, 0); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_RIGHT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_assert_button_event(li, BTN_LEFT, @@ -1999,13 +1999,13 @@ START_TEST(touchpad_left_handed_clickpad_delayed) litest_drain_events(li); litest_touch_down(dev, 0, 10, 90); - litest_button_click(dev, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); libinput_dispatch(li); status = libinput_device_config_left_handed_set(d, 1); ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_touch_up(dev, 0); litest_assert_button_event(li, @@ -2018,13 +2018,13 @@ START_TEST(touchpad_left_handed_clickpad_delayed) /* left-handed takes effect now */ litest_drain_events(li); litest_touch_down(dev, 0, 90, 90); - litest_button_click(dev, BTN_LEFT, 1); + litest_button_click_debounced(dev, li, BTN_LEFT, 1); libinput_dispatch(li); status = libinput_device_config_left_handed_set(d, 0); ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); - litest_button_click(dev, BTN_LEFT, 0); + litest_button_click_debounced(dev, li, BTN_LEFT, 0); litest_touch_up(dev, 0); litest_assert_button_event(li, @@ -2657,12 +2657,12 @@ START_TEST(touchpad_trackpoint_buttons) litest_drain_events(li); ARRAY_FOR_EACH(buttons, b) { - litest_button_click(touchpad, b->device_value, true); + litest_button_click_debounced(touchpad, li, b->device_value, true); assert_btnevent_from_device(trackpoint, b->real_value, LIBINPUT_BUTTON_STATE_PRESSED); - litest_button_click(touchpad, b->device_value, false); + litest_button_click_debounced(touchpad, li, b->device_value, false); assert_btnevent_from_device(trackpoint, b->real_value, @@ -2683,7 +2683,7 @@ START_TEST(touchpad_trackpoint_mb_scroll) LITEST_TRACKPOINT); litest_drain_events(li); - litest_button_click(touchpad, BTN_2, true); /* middle */ + litest_button_click_debounced(touchpad, li, BTN_2, true); /* middle */ libinput_dispatch(li); litest_timeout_buttonscroll(); libinput_dispatch(li); @@ -2695,7 +2695,7 @@ START_TEST(touchpad_trackpoint_mb_scroll) litest_event(trackpoint, EV_SYN, SYN_REPORT, 0); litest_event(trackpoint, EV_REL, REL_Y, -2); litest_event(trackpoint, EV_SYN, SYN_REPORT, 0); - litest_button_click(touchpad, BTN_2, false); + litest_button_click_debounced(touchpad, li, BTN_2, false); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS); @@ -2718,8 +2718,8 @@ START_TEST(touchpad_trackpoint_mb_click) ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); litest_drain_events(li); - litest_button_click(touchpad, BTN_2, true); /* middle */ - litest_button_click(touchpad, BTN_2, false); + litest_button_click_debounced(touchpad, li, BTN_2, true); /* middle */ + litest_button_click_debounced(touchpad, li, BTN_2, false); assert_btnevent_from_device(trackpoint, BTN_MIDDLE, @@ -2743,11 +2743,11 @@ START_TEST(touchpad_trackpoint_buttons_softbuttons) litest_drain_events(li); litest_touch_down(touchpad, 0, 95, 90); - litest_button_click(touchpad, BTN_LEFT, true); - litest_button_click(touchpad, BTN_1, true); - litest_button_click(touchpad, BTN_LEFT, false); + litest_button_click_debounced(touchpad, li, BTN_LEFT, true); + litest_button_click_debounced(touchpad, li, BTN_1, true); + litest_button_click_debounced(touchpad, li, BTN_LEFT, false); litest_touch_up(touchpad, 0); - litest_button_click(touchpad, BTN_1, false); + litest_button_click_debounced(touchpad, li, BTN_1, false); assert_btnevent_from_device(touchpad, BTN_RIGHT, @@ -2763,10 +2763,10 @@ START_TEST(touchpad_trackpoint_buttons_softbuttons) LIBINPUT_BUTTON_STATE_RELEASED); litest_touch_down(touchpad, 0, 95, 90); - litest_button_click(touchpad, BTN_LEFT, true); - litest_button_click(touchpad, BTN_1, true); - litest_button_click(touchpad, BTN_1, false); - litest_button_click(touchpad, BTN_LEFT, false); + litest_button_click_debounced(touchpad, li, BTN_LEFT, true); + litest_button_click_debounced(touchpad, li, BTN_1, true); + litest_button_click_debounced(touchpad, li, BTN_1, false); + litest_button_click_debounced(touchpad, li, BTN_LEFT, false); litest_touch_up(touchpad, 0); assert_btnevent_from_device(touchpad, @@ -2818,7 +2818,7 @@ START_TEST(touchpad_trackpoint_buttons_2fg_scroll) libinput_event_destroy(e); } - litest_button_click(touchpad, BTN_1, true); + litest_button_click_debounced(touchpad, li, BTN_1, true); assert_btnevent_from_device(trackpoint, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED); @@ -2838,7 +2838,7 @@ START_TEST(touchpad_trackpoint_buttons_2fg_scroll) libinput_event_destroy(e); } - litest_button_click(touchpad, BTN_1, false); + litest_button_click_debounced(touchpad, li, BTN_1, false); assert_btnevent_from_device(trackpoint, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED); @@ -2874,16 +2874,16 @@ START_TEST(touchpad_trackpoint_no_trackpoint) struct libinput *li = touchpad->libinput; litest_drain_events(li); - litest_button_click(touchpad, BTN_0, true); /* left */ - litest_button_click(touchpad, BTN_0, false); + litest_button_click_debounced(touchpad, li, BTN_0, true); /* left */ + litest_button_click_debounced(touchpad, li, BTN_0, false); litest_assert_empty_queue(li); - litest_button_click(touchpad, BTN_1, true); /* right */ - litest_button_click(touchpad, BTN_1, false); + litest_button_click_debounced(touchpad, li, BTN_1, true); /* right */ + litest_button_click_debounced(touchpad, li, BTN_1, false); litest_assert_empty_queue(li); - litest_button_click(touchpad, BTN_2, true); /* middle */ - litest_button_click(touchpad, BTN_2, false); + litest_button_click_debounced(touchpad, li, BTN_2, true); /* middle */ + litest_button_click_debounced(touchpad, li, BTN_2, false); litest_assert_empty_queue(li); } END_TEST @@ -3679,8 +3679,8 @@ START_TEST(touchpad_dwt_click) litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); litest_touch_down(touchpad, 0, 50, 50); - litest_button_click(touchpad, BTN_LEFT, true); - litest_button_click(touchpad, BTN_LEFT, false); + litest_button_click_debounced(touchpad, li, BTN_LEFT, true); + litest_button_click_debounced(touchpad, li, BTN_LEFT, false); libinput_dispatch(li); litest_touch_up(touchpad, 0); litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); @@ -4492,7 +4492,7 @@ START_TEST(touchpad_thumb_clickfinger) litest_touch_down(dev, 0, 50, 99); litest_touch_down(dev, 1, 60, 99); litest_touch_move_extended(dev, 0, 55, 99, axes); - litest_button_click(dev, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, true); libinput_dispatch(li); event = libinput_get_event(li); @@ -4503,7 +4503,7 @@ START_TEST(touchpad_thumb_clickfinger) litest_assert_empty_queue(li); - litest_button_click(dev, BTN_LEFT, false); + litest_button_click_debounced(dev, li, BTN_LEFT, false); litest_touch_up(dev, 0); litest_touch_up(dev, 1); @@ -4512,7 +4512,7 @@ START_TEST(touchpad_thumb_clickfinger) litest_touch_down(dev, 0, 50, 99); litest_touch_down(dev, 1, 60, 99); litest_touch_move_extended(dev, 1, 65, 99, axes); - litest_button_click(dev, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, true); libinput_dispatch(li); event = libinput_get_event(li); @@ -4547,7 +4547,7 @@ START_TEST(touchpad_thumb_btnarea) litest_touch_down(dev, 0, 90, 99); litest_touch_move_extended(dev, 0, 95, 99, axes); - litest_button_click(dev, BTN_LEFT, true); + litest_button_click_debounced(dev, li, BTN_LEFT, true); /* button areas work as usual with a thumb */ diff --git a/test/test-trackpoint.c b/test/test-trackpoint.c index adbd46e8..46a66893 100644 --- a/test/test-trackpoint.c +++ b/test/test-trackpoint.c @@ -43,9 +43,9 @@ START_TEST(trackpoint_middlebutton) litest_drain_events(li); /* A quick middle button click should get reported normally */ - litest_button_click(dev, BTN_MIDDLE, 1); + litest_button_click_debounced(dev, li, BTN_MIDDLE, 1); msleep(2); - litest_button_click(dev, BTN_MIDDLE, 0); + litest_button_click_debounced(dev, li, BTN_MIDDLE, 0); litest_wait_for_event(li); @@ -173,7 +173,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_trackpoint) litest_touch_down(touchpad, 0, 5, 5); libinput_dispatch(li); - litest_button_click(touchpad, BTN_LEFT, true); + litest_button_click_debounced(touchpad, li, BTN_LEFT, true); libinput_dispatch(li); event = libinput_get_event(li); @@ -184,7 +184,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_trackpoint) ck_assert(device == trackpoint->libinput_device); libinput_event_destroy(event); - litest_button_click(touchpad, BTN_LEFT, false); + litest_button_click_debounced(touchpad, li, BTN_LEFT, false); libinput_dispatch(li); event = libinput_get_event(li); litest_is_button_event(event, @@ -216,7 +216,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_touchpad) litest_touch_down(touchpad, 0, 5, 5); libinput_dispatch(li); - litest_button_click(touchpad, BTN_LEFT, true); + litest_button_click_debounced(touchpad, li, BTN_LEFT, true); libinput_dispatch(li); event = libinput_get_event(li); @@ -225,7 +225,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_touchpad) ck_assert(device == trackpoint->libinput_device); libinput_event_destroy(event); - litest_button_click(touchpad, BTN_LEFT, false); + litest_button_click_debounced(touchpad, li, BTN_LEFT, false); libinput_dispatch(li); event = libinput_get_event(li); litest_is_button_event(event, @@ -260,7 +260,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_both) litest_touch_down(touchpad, 0, 5, 5); libinput_dispatch(li); - litest_button_click(touchpad, BTN_LEFT, true); + litest_button_click_debounced(touchpad, li, BTN_LEFT, true); libinput_dispatch(li); event = libinput_get_event(li); @@ -271,7 +271,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_both) ck_assert(device == trackpoint->libinput_device); libinput_event_destroy(event); - litest_button_click(touchpad, BTN_LEFT, false); + litest_button_click_debounced(touchpad, li, BTN_LEFT, false); libinput_dispatch(li); event = libinput_get_event(li); litest_is_button_event(event, From 87920f49926581ee5b3a2735edf41b007c74b29d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 13 Nov 2017 09:30:30 +1000 Subject: [PATCH 2/5] fallback: create the evdev-fallback.h header file So we can split up evdev-fallback.c into multiple files where needed. Signed-off-by: Peter Hutterer --- meson.build | 1 + src/evdev-fallback.c | 92 +------------------------------ src/evdev-fallback.h | 125 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 91 deletions(-) create mode 100644 src/evdev-fallback.h diff --git a/meson.build b/meson.build index eefe3d61..1dc0b7d4 100644 --- a/meson.build +++ b/meson.build @@ -157,6 +157,7 @@ src_libinput = [ 'src/evdev.c', 'src/evdev.h', 'src/evdev-fallback.c', + 'src/evdev-fallback.h', 'src/evdev-middle-button.c', 'src/evdev-mt-touchpad.c', 'src/evdev-mt-touchpad.h', diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c index 1482b4e2..e9fd7d62 100644 --- a/src/evdev-fallback.c +++ b/src/evdev-fallback.c @@ -28,100 +28,10 @@ #include -#include "evdev.h" +#include "evdev-fallback.h" #define DEBOUNCE_TIME ms2us(12) -struct fallback_dispatch { - struct evdev_dispatch base; - struct evdev_device *device; - - struct libinput_device_config_calibration calibration; - - struct { - bool is_enabled; - int angle; - struct matrix matrix; - struct libinput_device_config_rotation config; - } rotation; - - struct { - struct device_coords point; - int32_t seat_slot; - - struct { - struct device_coords min, max; - struct ratelimit range_warn_limit; - } warning_range; - } abs; - - struct { - int slot; - struct mt_slot *slots; - size_t slots_len; - bool want_hysteresis; - struct device_coords hysteresis_margin; - } mt; - - struct device_coords rel; - - struct { - /* The struct for the tablet mode switch device itself */ - struct { - int state; - } sw; - /* The struct for other devices listening to the tablet mode - switch */ - struct { - struct evdev_device *sw_device; - struct libinput_event_listener listener; - } other; - } tablet_mode; - - /* Bitmask of pressed keys used to ignore initial release events from - * the kernel. */ - unsigned long hw_key_mask[NLONGS(KEY_CNT)]; - - enum evdev_event_type pending_event; - - /* true if we're reading events (i.e. not suspended) but we're - ignoring them */ - bool ignore_events; - - struct { - enum evdev_debounce_state state; - unsigned int button_code; - uint64_t button_up_time; - struct libinput_timer timer; - } debounce; - - struct { - enum switch_reliability reliability; - - bool is_closed; - bool is_closed_client_state; - - /* We allow up to 3 paired keyboards for the lid switch - * listener. Only one keyboard should exist, but that can - * have more than one event node. - * - * Note: this is a sparse list, any element may have a - * non-NULL device. - */ - struct paired_keyboard { - struct evdev_device *device; - struct libinput_event_listener listener; - } paired_keyboard[3]; - } lid; -}; - -static inline struct fallback_dispatch* -fallback_dispatch(struct evdev_dispatch *dispatch) -{ - evdev_verify_dispatch_type(dispatch, DISPATCH_FALLBACK); - - return container_of(dispatch, struct fallback_dispatch, base); -} enum key_type { KEY_TYPE_NONE, KEY_TYPE_KEY, diff --git a/src/evdev-fallback.h b/src/evdev-fallback.h new file mode 100644 index 00000000..c22f3b3f --- /dev/null +++ b/src/evdev-fallback.h @@ -0,0 +1,125 @@ +/* + * Copyright © 2010 Intel Corporation + * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2017 Red Hat, Inc. + * Copyright © 2017 James Ye + * + * 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" + +#ifndef EVDEV_FALLBACK_H +#define EVDEV_FALLBACK_H + +#include "evdev.h" + +struct fallback_dispatch { + struct evdev_dispatch base; + struct evdev_device *device; + + struct libinput_device_config_calibration calibration; + + struct { + bool is_enabled; + int angle; + struct matrix matrix; + struct libinput_device_config_rotation config; + } rotation; + + struct { + struct device_coords point; + int32_t seat_slot; + + struct { + struct device_coords min, max; + struct ratelimit range_warn_limit; + } warning_range; + } abs; + + struct { + int slot; + struct mt_slot *slots; + size_t slots_len; + bool want_hysteresis; + struct device_coords hysteresis_margin; + } mt; + + struct device_coords rel; + + struct { + /* The struct for the tablet mode switch device itself */ + struct { + int state; + } sw; + /* The struct for other devices listening to the tablet mode + switch */ + struct { + struct evdev_device *sw_device; + struct libinput_event_listener listener; + } other; + } tablet_mode; + + /* Bitmask of pressed keys used to ignore initial release events from + * the kernel. */ + unsigned long hw_key_mask[NLONGS(KEY_CNT)]; + + enum evdev_event_type pending_event; + + /* true if we're reading events (i.e. not suspended) but we're + ignoring them */ + bool ignore_events; + + struct { + enum evdev_debounce_state state; + unsigned int button_code; + uint64_t button_up_time; + struct libinput_timer timer; + } debounce; + + struct { + enum switch_reliability reliability; + + bool is_closed; + bool is_closed_client_state; + + /* We allow up to 3 paired keyboards for the lid switch + * listener. Only one keyboard should exist, but that can + * have more than one event node. + * + * Note: this is a sparse list, any element may have a + * non-NULL device. + */ + struct paired_keyboard { + struct evdev_device *device; + struct libinput_event_listener listener; + } paired_keyboard[3]; + } lid; +}; + +static inline struct fallback_dispatch* +fallback_dispatch(struct evdev_dispatch *dispatch) +{ + evdev_verify_dispatch_type(dispatch, DISPATCH_FALLBACK); + + return container_of(dispatch, struct fallback_dispatch, base); +} + +#endif From 8e86f28931d727d4a364a6feec113297a9957d47 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 10 Nov 2017 15:04:43 +1000 Subject: [PATCH 3/5] fallback: drop unused ratelimit struct This one is present in the parent evdev device Signed-off-by: Peter Hutterer --- src/evdev-fallback.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/evdev-fallback.h b/src/evdev-fallback.h index c22f3b3f..e8783d4b 100644 --- a/src/evdev-fallback.h +++ b/src/evdev-fallback.h @@ -47,11 +47,6 @@ struct fallback_dispatch { struct { struct device_coords point; int32_t seat_slot; - - struct { - struct device_coords min, max; - struct ratelimit range_warn_limit; - } warning_range; } abs; struct { From db3b6fe5f7f847075ab3af1bc0a32241143d8993 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 13 Nov 2017 12:19:44 +1000 Subject: [PATCH 4/5] fallback: change to handle the state at EV_SYN time The previous approach was to remember the last event and flush it at the right time. The new approach is to update the device state during the frame and send out the events at EV_SYN time. This gives us two advantages: we are not dependent on the kernel order of how events come in and we can process events depending on other events in the same frame. This will come in handy later for button debouncing. This is also the approach we have in the touchpad and tablet backends. Two FIXMEs are left in place, the button debouncing code and the lid switch code. Both need to be handled in future patches. Signed-off-by: Peter Hutterer --- src/evdev-fallback.c | 404 ++++++++++++++++++++++++------------------- src/evdev-fallback.h | 2 + src/evdev.h | 24 ++- 3 files changed, 245 insertions(+), 185 deletions(-) diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c index e9fd7d62..70cbd3e1 100644 --- a/src/evdev-fallback.c +++ b/src/evdev-fallback.c @@ -44,6 +44,25 @@ hw_set_key_down(struct fallback_dispatch *dispatch, int code, int pressed) long_set_bit_state(dispatch->hw_key_mask, code, pressed); } +static bool +hw_key_has_changed(struct fallback_dispatch *dispatch, int code) +{ + return long_bit_is_set(dispatch->hw_key_mask, code) != + long_bit_is_set(dispatch->last_hw_key_mask, code); +} + +static void +hw_key_update_last_state(struct fallback_dispatch *dispatch) +{ + static_assert(sizeof(dispatch->hw_key_mask) == + sizeof(dispatch->last_hw_key_mask), + "Mismatching key mask size"); + + memcpy(dispatch->last_hw_key_mask, + dispatch->hw_key_mask, + sizeof(dispatch->hw_key_mask)); +} + static bool hw_is_key_down(struct fallback_dispatch *dispatch, int code) { @@ -227,6 +246,58 @@ fallback_flush_relative_motion(struct fallback_dispatch *dispatch, pointer_notify_motion(base, time, &accel, &raw); } +static void +fallback_flush_wheels(struct fallback_dispatch *dispatch, + struct evdev_device *device, + uint64_t time) +{ + struct normalized_coords wheel_degrees = { 0.0, 0.0 }; + struct discrete_coords discrete = { 0.0, 0.0 }; + enum libinput_pointer_axis_source source; + + if (!(device->seat_caps & EVDEV_DEVICE_POINTER)) + return; + + + if (dispatch->wheel.y != 0) { + wheel_degrees.y = -1 * dispatch->wheel.y * + device->scroll.wheel_click_angle.y; + discrete.y = -1 * dispatch->wheel.y; + + source = device->scroll.is_tilt.vertical ? + LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: + LIBINPUT_POINTER_AXIS_SOURCE_WHEEL; + + evdev_notify_axis( + device, + time, + AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL), + source, + &wheel_degrees, + &discrete); + dispatch->wheel.y = 0; + } + + if (dispatch->wheel.x != 0) { + wheel_degrees.x = dispatch->wheel.x * + device->scroll.wheel_click_angle.x; + discrete.x = dispatch->wheel.x; + + source = device->scroll.is_tilt.horizontal ? + LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: + LIBINPUT_POINTER_AXIS_SOURCE_WHEEL; + + evdev_notify_axis( + device, + time, + AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL), + source, + &wheel_degrees, + &discrete); + dispatch->wheel.x = 0; + } +} + static void fallback_flush_absolute_motion(struct fallback_dispatch *dispatch, struct evdev_device *device, @@ -424,78 +495,6 @@ fallback_flush_st_up(struct fallback_dispatch *dispatch, return true; } -static enum evdev_event_type -fallback_flush_pending_event(struct fallback_dispatch *dispatch, - struct evdev_device *device, - uint64_t time) -{ - enum evdev_event_type sent_event; - int slot_idx; - - sent_event = dispatch->pending_event; - - switch (dispatch->pending_event) { - case EVDEV_NONE: - break; - case EVDEV_RELATIVE_MOTION: - fallback_flush_relative_motion(dispatch, device, time); - break; - case EVDEV_ABSOLUTE_MT_DOWN: - slot_idx = dispatch->mt.slot; - if (!fallback_flush_mt_down(dispatch, - device, - slot_idx, - time)) - sent_event = EVDEV_NONE; - break; - case EVDEV_ABSOLUTE_MT_MOTION: - slot_idx = dispatch->mt.slot; - if (!fallback_flush_mt_motion(dispatch, - device, - slot_idx, - time)) - sent_event = EVDEV_NONE; - break; - case EVDEV_ABSOLUTE_MT_UP: - slot_idx = dispatch->mt.slot; - if (!fallback_flush_mt_up(dispatch, - device, - slot_idx, - time)) - sent_event = EVDEV_NONE; - break; - case EVDEV_ABSOLUTE_TOUCH_DOWN: - if (!fallback_flush_st_down(dispatch, device, time)) - sent_event = EVDEV_NONE; - break; - case EVDEV_ABSOLUTE_MOTION: - if (device->seat_caps & EVDEV_DEVICE_TOUCH) { - if (fallback_flush_st_motion(dispatch, - device, - time)) - sent_event = EVDEV_ABSOLUTE_MT_MOTION; - else - sent_event = EVDEV_NONE; - } else if (device->seat_caps & EVDEV_DEVICE_POINTER) { - fallback_flush_absolute_motion(dispatch, - device, - time); - } - break; - case EVDEV_ABSOLUTE_TOUCH_UP: - if (!fallback_flush_st_up(dispatch, device, time)) - sent_event = EVDEV_NONE; - break; - default: - assert(0 && "Unknown pending event type"); - break; - } - - dispatch->pending_event = EVDEV_NONE; - - return sent_event; -} - static enum key_type get_key_type(uint16_t code) { @@ -536,13 +535,9 @@ fallback_process_touch_button(struct fallback_dispatch *dispatch, struct evdev_device *device, uint64_t time, int value) { - if (dispatch->pending_event != EVDEV_NONE && - dispatch->pending_event != EVDEV_ABSOLUTE_MOTION) - fallback_flush_pending_event(dispatch, device, time); - - dispatch->pending_event = (value ? + dispatch->pending_event |= (value) ? EVDEV_ABSOLUTE_TOUCH_DOWN : - EVDEV_ABSOLUTE_TOUCH_UP); + EVDEV_ABSOLUTE_TOUCH_UP; } static inline void @@ -562,6 +557,7 @@ fallback_flush_debounce(struct fallback_dispatch *dispatch, button, LIBINPUT_BUTTON_STATE_RELEASED); hw_set_key_down(dispatch, code, 0); + hw_key_update_last_state(dispatch); } dispatch->debounce.state = DEBOUNCE_ON; @@ -707,8 +703,6 @@ fallback_process_key(struct fallback_dispatch *dispatch, return; } - fallback_flush_pending_event(dispatch, device, time); - type = get_key_type(e->code); /* Ignore key release events from the kernel for keys that libinput @@ -721,40 +715,22 @@ fallback_process_key(struct fallback_dispatch *dispatch, if ((e->value && hw_is_key_down(dispatch, e->code)) || (e->value == 0 && !hw_is_key_down(dispatch, e->code))) return; + + dispatch->pending_event |= EVDEV_KEY; break; case KEY_TYPE_BUTTON: + /* FIXME: should move to handle_state */ if (fallback_filter_debounce(dispatch, device, e, time)) return; if ((e->value && hw_is_key_down(dispatch, e->code)) || (e->value == 0 && !hw_is_key_down(dispatch, e->code))) return; + dispatch->pending_event |= EVDEV_KEY; break; } hw_set_key_down(dispatch, e->code, e->value); - - switch (type) { - case KEY_TYPE_NONE: - break; - case KEY_TYPE_KEY: - fallback_keyboard_notify_key( - dispatch, - device, - time, - e->code, - e->value ? LIBINPUT_KEY_STATE_PRESSED : - LIBINPUT_KEY_STATE_RELEASED); - break; - case KEY_TYPE_BUTTON: - evdev_pointer_notify_physical_button( - device, - time, - evdev_to_left_handed(device, e->code), - e->value ? LIBINPUT_BUTTON_STATE_PRESSED : - LIBINPUT_BUTTON_STATE_RELEASED); - break; - } } static void @@ -763,8 +739,9 @@ fallback_process_touch(struct fallback_dispatch *dispatch, struct input_event *e, uint64_t time) { - switch (e->code) { - case ABS_MT_SLOT: + struct mt_slot *slot = &dispatch->mt.slots[dispatch->mt.slot]; + + if (e->code == ABS_MT_SLOT) { if ((size_t)e->value >= dispatch->mt.slots_len) { evdev_log_bug_libinput(device, "exceeded slot count (%d of max %zd)\n", @@ -772,32 +749,36 @@ fallback_process_touch(struct fallback_dispatch *dispatch, dispatch->mt.slots_len); e->value = dispatch->mt.slots_len - 1; } - fallback_flush_pending_event(dispatch, device, time); dispatch->mt.slot = e->value; - break; + return; + } + + switch (e->code) { case ABS_MT_TRACKING_ID: - if (dispatch->pending_event != EVDEV_NONE && - dispatch->pending_event != EVDEV_ABSOLUTE_MT_MOTION) - fallback_flush_pending_event(dispatch, device, time); - if (e->value >= 0) - dispatch->pending_event = EVDEV_ABSOLUTE_MT_DOWN; - else - dispatch->pending_event = EVDEV_ABSOLUTE_MT_UP; + if (e->value >= 0) { + dispatch->pending_event |= EVDEV_ABSOLUTE_MT; + slot->state = SLOT_STATE_BEGIN; + } else { + dispatch->pending_event |= EVDEV_ABSOLUTE_MT; + slot->state = SLOT_STATE_END; + } + slot->dirty = true; break; case ABS_MT_POSITION_X: evdev_device_check_abs_axis_range(device, e->code, e->value); dispatch->mt.slots[dispatch->mt.slot].point.x = e->value; - if (dispatch->pending_event == EVDEV_NONE) - dispatch->pending_event = EVDEV_ABSOLUTE_MT_MOTION; + dispatch->pending_event |= EVDEV_ABSOLUTE_MT; + slot->dirty = true; break; case ABS_MT_POSITION_Y: evdev_device_check_abs_axis_range(device, e->code, e->value); dispatch->mt.slots[dispatch->mt.slot].point.y = e->value; - if (dispatch->pending_event == EVDEV_NONE) - dispatch->pending_event = EVDEV_ABSOLUTE_MT_MOTION; + dispatch->pending_event |= EVDEV_ABSOLUTE_MT; + slot->dirty = true; break; } } + static inline void fallback_process_absolute_motion(struct fallback_dispatch *dispatch, struct evdev_device *device, @@ -807,14 +788,12 @@ fallback_process_absolute_motion(struct fallback_dispatch *dispatch, case ABS_X: evdev_device_check_abs_axis_range(device, e->code, e->value); dispatch->abs.point.x = e->value; - if (dispatch->pending_event == EVDEV_NONE) - dispatch->pending_event = EVDEV_ABSOLUTE_MOTION; + dispatch->pending_event |= EVDEV_ABSOLUTE_MOTION; break; case ABS_Y: evdev_device_check_abs_axis_range(device, e->code, e->value); dispatch->abs.point.y = e->value; - if (dispatch->pending_event == EVDEV_NONE) - dispatch->pending_event = EVDEV_ABSOLUTE_MOTION; + dispatch->pending_event |= EVDEV_ABSOLUTE_MOTION; break; } } @@ -897,6 +876,8 @@ fallback_process_switch(struct fallback_dispatch *dispatch, enum libinput_switch_state state; bool is_closed; + /* TODO: this should to move to handle_state */ + switch (e->code) { case SW_LID: is_closed = !!e->value; @@ -947,61 +928,25 @@ fallback_process_relative(struct fallback_dispatch *dispatch, struct evdev_device *device, struct input_event *e, uint64_t time) { - struct normalized_coords wheel_degrees = { 0.0, 0.0 }; - struct discrete_coords discrete = { 0.0, 0.0 }; - enum libinput_pointer_axis_source source; - if (fallback_reject_relative(device, e, time)) return; switch (e->code) { case REL_X: - if (dispatch->pending_event != EVDEV_RELATIVE_MOTION) - fallback_flush_pending_event(dispatch, device, time); dispatch->rel.x += e->value; - dispatch->pending_event = EVDEV_RELATIVE_MOTION; + dispatch->pending_event |= EVDEV_RELATIVE_MOTION; break; case REL_Y: - if (dispatch->pending_event != EVDEV_RELATIVE_MOTION) - fallback_flush_pending_event(dispatch, device, time); dispatch->rel.y += e->value; - dispatch->pending_event = EVDEV_RELATIVE_MOTION; + dispatch->pending_event |= EVDEV_RELATIVE_MOTION; break; case REL_WHEEL: - fallback_flush_pending_event(dispatch, device, time); - wheel_degrees.y = -1 * e->value * - device->scroll.wheel_click_angle.y; - discrete.y = -1 * e->value; - - source = device->scroll.is_tilt.vertical ? - LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: - LIBINPUT_POINTER_AXIS_SOURCE_WHEEL; - - evdev_notify_axis( - device, - time, - AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL), - source, - &wheel_degrees, - &discrete); + dispatch->wheel.y += e->value; + dispatch->pending_event |= EVDEV_WHEEL; break; case REL_HWHEEL: - fallback_flush_pending_event(dispatch, device, time); - wheel_degrees.x = e->value * - device->scroll.wheel_click_angle.x; - discrete.x = e->value; - - source = device->scroll.is_tilt.horizontal ? - LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: - LIBINPUT_POINTER_AXIS_SOURCE_WHEEL; - - evdev_notify_axis( - device, - time, - AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL), - source, - &wheel_degrees, - &discrete); + dispatch->wheel.x += e->value; + dispatch->pending_event |= EVDEV_WHEEL; break; } } @@ -1033,6 +978,122 @@ fallback_any_button_down(struct fallback_dispatch *dispatch, return false; } +static void +fallback_handle_state(struct fallback_dispatch *dispatch, + struct evdev_device *device, + uint64_t time) +{ + bool need_touch_frame = false; + + /* Relative motion */ + if (dispatch->pending_event & EVDEV_RELATIVE_MOTION) + fallback_flush_relative_motion(dispatch, device, time); + + /* Single touch or absolute pointer devices */ + if (dispatch->pending_event & EVDEV_ABSOLUTE_TOUCH_DOWN) { + if (fallback_flush_st_down(dispatch, device, time)) + need_touch_frame = true; + } + + if (dispatch->pending_event & EVDEV_ABSOLUTE_MOTION) { + if (device->seat_caps & EVDEV_DEVICE_TOUCH) { + if (fallback_flush_st_motion(dispatch, + device, + time)) + need_touch_frame = true; + } else if (device->seat_caps & EVDEV_DEVICE_POINTER) { + fallback_flush_absolute_motion(dispatch, + device, + time); + } + } + + if (dispatch->pending_event & EVDEV_ABSOLUTE_TOUCH_UP) { + if (fallback_flush_st_up(dispatch, device, time)) + need_touch_frame = true; + } + + /* Multitouch devices */ + if (dispatch->pending_event & EVDEV_ABSOLUTE_MT) { + bool sent = false; + for (size_t i = 0; i < dispatch->mt.slots_len; i++) { + struct mt_slot *slot = &dispatch->mt.slots[i]; + + if (!slot->dirty) + continue; + + if (slot->state == SLOT_STATE_BEGIN) { + sent = fallback_flush_mt_down(dispatch, + device, + i, + time); + slot->state = SLOT_STATE_UPDATE; + } else if (slot->state == SLOT_STATE_UPDATE) { + sent = fallback_flush_mt_motion(dispatch, + device, + i, + time); + } else if (slot->state == SLOT_STATE_END) { + sent = fallback_flush_mt_up(dispatch, + device, + i, + time); + slot->state = SLOT_STATE_NONE; + } + + + slot->dirty = false; + } + + need_touch_frame = sent; + } + + if (need_touch_frame) + touch_notify_frame(&device->base, time); + + fallback_flush_wheels(dispatch, device, time); + + /* Buttons and keys */ + if (dispatch->pending_event & EVDEV_KEY) { + for (unsigned int code = 0; code <= KEY_MAX; code++) { + bool new_state; + + if (!hw_key_has_changed(dispatch, code)) + continue; + + new_state = hw_is_key_down(dispatch, code); + + switch (get_key_type(code)) { + case KEY_TYPE_NONE: + break; + case KEY_TYPE_KEY: + fallback_keyboard_notify_key( + dispatch, + device, + time, + code, + new_state ? + LIBINPUT_KEY_STATE_PRESSED : + LIBINPUT_KEY_STATE_RELEASED); + break; + case KEY_TYPE_BUTTON: + evdev_pointer_notify_physical_button( + device, + time, + evdev_to_left_handed(device, code), + new_state ? + LIBINPUT_BUTTON_STATE_PRESSED : + LIBINPUT_BUTTON_STATE_RELEASED); + break; + } + + } + hw_key_update_last_state(dispatch); + } + + dispatch->pending_event = EVDEV_NONE; +} + static void fallback_interface_process(struct evdev_dispatch *evdev_dispatch, struct evdev_device *device, @@ -1040,7 +1101,6 @@ fallback_interface_process(struct evdev_dispatch *evdev_dispatch, uint64_t time) { struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); - enum evdev_event_type sent; if (dispatch->ignore_events) return; @@ -1059,20 +1119,7 @@ fallback_interface_process(struct evdev_dispatch *evdev_dispatch, fallback_process_switch(dispatch, device, event, time); break; case EV_SYN: - sent = fallback_flush_pending_event(dispatch, device, time); - switch (sent) { - case EVDEV_ABSOLUTE_TOUCH_DOWN: - case EVDEV_ABSOLUTE_TOUCH_UP: - case EVDEV_ABSOLUTE_MT_DOWN: - case EVDEV_ABSOLUTE_MT_MOTION: - case EVDEV_ABSOLUTE_MT_UP: - touch_notify_frame(&device->base, time); - break; - case EVDEV_ABSOLUTE_MOTION: - case EVDEV_RELATIVE_MOTION: - case EVDEV_NONE: - break; - } + fallback_handle_state(dispatch, device, time); break; } } @@ -1164,6 +1211,7 @@ fallback_return_to_neutral_state(struct fallback_dispatch *dispatch, release_touches(dispatch, device, time); release_pressed_keys(dispatch, device, time); memset(dispatch->hw_key_mask, 0, sizeof(dispatch->hw_key_mask)); + memset(dispatch->hw_key_mask, 0, sizeof(dispatch->last_hw_key_mask)); } static void diff --git a/src/evdev-fallback.h b/src/evdev-fallback.h index e8783d4b..ba121c08 100644 --- a/src/evdev-fallback.h +++ b/src/evdev-fallback.h @@ -58,6 +58,7 @@ struct fallback_dispatch { } mt; struct device_coords rel; + struct device_coords wheel; struct { /* The struct for the tablet mode switch device itself */ @@ -75,6 +76,7 @@ struct fallback_dispatch { /* Bitmask of pressed keys used to ignore initial release events from * the kernel. */ unsigned long hw_key_mask[NLONGS(KEY_CNT)]; + unsigned long last_hw_key_mask[NLONGS(KEY_CNT)]; enum evdev_event_type pending_event; diff --git a/src/evdev.h b/src/evdev.h index 7d5e6efb..11ed7da5 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -41,13 +41,14 @@ enum evdev_event_type { EVDEV_NONE, - EVDEV_ABSOLUTE_TOUCH_DOWN, - EVDEV_ABSOLUTE_MOTION, - EVDEV_ABSOLUTE_TOUCH_UP, - EVDEV_ABSOLUTE_MT_DOWN, - EVDEV_ABSOLUTE_MT_MOTION, - EVDEV_ABSOLUTE_MT_UP, - EVDEV_RELATIVE_MOTION, + EVDEV_ABSOLUTE_TOUCH_DOWN = (1 << 0), + EVDEV_ABSOLUTE_MOTION = (1 << 1), + EVDEV_ABSOLUTE_TOUCH_UP = (1 << 2), + EVDEV_ABSOLUTE_MT= (1 << 3), + EVDEV_WHEEL = (1 << 4), + EVDEV_KEY = (1 << 5), + EVDEV_RELATIVE_MOTION = (1 << 6), + EVDEV_BUTTON = (1 << 7), }; enum evdev_device_seat_capability { @@ -150,7 +151,16 @@ enum evdev_debounce_state { DEBOUNCE_ACTIVE, }; +enum mt_slot_state { + SLOT_STATE_NONE, + SLOT_STATE_BEGIN, + SLOT_STATE_UPDATE, + SLOT_STATE_END, +}; + struct mt_slot { + bool dirty; + enum mt_slot_state state; int32_t seat_slot; struct device_coords point; struct device_coords hysteresis_center; From de994d135e452589d5cb9aff7111aed3082e48ee Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 13 Nov 2017 09:33:50 +1000 Subject: [PATCH 5/5] evdev: add new debouncing code The current debouncing code monitors events and switches on when events are too close together. From then on, any event can be delayed. Vicente Bergas provided an algorithm that avoids most of these delays: on a button state change we now forward the change without delay but start a timer. If the button changes state during that timer, the changes are ignored. On timer expiry, events are sent to match the hardware state with the client's view of the device. This is only done if needed. Thus, a press-release sequence of: PRP sends a single press event, a sequence of PRPR sends press and then the release at the end of the timeout. The timeout is short enough that the delay should not be noticeable. This new mode is called the 'bounce' mode. The old mode is now referred to as 'spurious' mode and only covers the case of a button held down that loses contact. It works as before, monitoring a button for these spurious contact losses and switching on. When on, button release events are delayed as before. The whole button debouncing moves to a state machine which makes debugging a lot easier. See the accompanying SVG for the diagram. Signed-off-by: Peter Hutterer --- doc/button-debouncing-state-machine.svg | 843 +++++++++++++++++++++ doc/button-debouncing-wave-diagram.txt | 50 ++ doc/button_debouncing.dox | 33 +- doc/svg/button-debouncing-wave-diagram.svg | 4 + meson.build | 2 + src/evdev-debounce.c | 564 ++++++++++++++ src/evdev-fallback.c | 260 +------ src/evdev-fallback.h | 102 ++- test/litest.c | 2 +- test/test-pointer.c | 258 +++++-- 10 files changed, 1793 insertions(+), 325 deletions(-) create mode 100644 doc/button-debouncing-state-machine.svg create mode 100644 doc/button-debouncing-wave-diagram.txt create mode 100644 doc/svg/button-debouncing-wave-diagram.svg create mode 100644 src/evdev-debounce.c diff --git a/doc/button-debouncing-state-machine.svg b/doc/button-debouncing-state-machine.svg new file mode 100644 index 00000000..6f106a89 --- /dev/null +++ b/doc/button-debouncing-state-machine.svg @@ -0,0 +1,843 @@ + + + + + + + + + +
+
+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 +
+
+
+
diff --git a/doc/button-debouncing-wave-diagram.txt b/doc/button-debouncing-wave-diagram.txt new file mode 100644 index 00000000..fe637af9 --- /dev/null +++ b/doc/button-debouncing-wave-diagram.txt @@ -0,0 +1,50 @@ +# Source for the button debouncing wave diagram +# Paste into http://wavedrom.com/editor.html +{signal: [ + {name:'current mode', wave: '3............', data: ['normal button press and release']}, + {name:'physical button', wave: '01......0....'}, + {name:'application ', wave: '01......0....'}, + {}, + ['bounce mode', + {name:'current mode', wave: '4............', data: ['debounced button press']}, + {name:'physical button', wave: '0101...0.....'}, + {name: 'timeouts', wave: '01...0.1...0.'}, + {name:'application ', wave: '01.....0.....'}, + {}, + {name:'current mode', wave: '4............', data: ['debounced button release']}, + {name:'physical button', wave: '1...010......'}, + {name: 'timeouts', wave: '0...1...0....'}, + {name:'application ', wave: '1...0........'}, + {}, + {name:'current mode', wave: '5............', data: ['delayed button press']}, + {name:'physical button', wave: '1...01.......'}, + {name: 'timeouts', wave: '0...1...0....'}, + {name:'application ', wave: '1...0...1....'}, + {}, + {name:'current mode', wave: '5............', data: ['delayed button release']}, + {name:'physical button', wave: '0...10.......'}, + {name: 'timeouts', wave: '0...1...0....'}, + {name:'application ', wave: '0...1...0....'}, + ], + {}, + ['spurious mode', + {name:'current mode', wave: '3............', data: ['first spurious button release ']}, + {name:'physical button', wave: '1.......01...'}, + {name:'application ', wave: '1.......01...'}, + {}, + {name:'current mode', wave: '3............', data: ['later spurious button release ']}, + {name:'physical button', wave: '1....01......'}, + {name: 'timeouts', wave: '0....1..0....'}, + {name:'application ', wave: '1............'}, + {}, + {name:'current mode', wave: '3............', data: ['delayed release in spurious mode ']}, + {name:'physical button', wave: '1....0.......'}, + {name: 'timeouts', wave: '0....1..0....'}, + {name:'application ', wave: '1.......0....'} + ], + +], + head:{ + text:'Button Debouncing Scenarios', + }, +} diff --git a/doc/button_debouncing.dox b/doc/button_debouncing.dox index fd52986e..22840dd8 100644 --- a/doc/button_debouncing.dox +++ b/doc/button_debouncing.dox @@ -9,12 +9,25 @@ though the user only pressed or clicked the button once. This effect can be counteracted by "debouncing" the buttons, usually by ignoring erroneous events. -libinput has a built-in debouncing for hardware defects. This feature is -available for all button-base devices but not active by default. When -libinput detects a faulty button on a device, debouncing is enabled and a -warning is printed to the log. Subsequent button events are handled -correctly in that bouncing button events are ignored, a user should thus see -the expected behavior. +libinput provides two methods of debouncing buttons, referred to as the +"bounce" and "spurious" methods: + +- In the "bounce" method, libinput monitors hardware bouncing on button + state changes, i.e. when a user clicks or releases a button. For example, + if a user presses a button but the hardware generates a + press-release-press sequence in quick succession, libinput ignores the + release and second press event. This method is always enabled. +- in the "spurious" method, libinput detects spurious releases of a button + while the button is physically held down by the user. These releases are + immediately followed by a press event. libinput monitors for these events + and ignores the release and press event. This method is disabled by + default and enables once libinput detects the first faulty event sequence. + +The "bounce" method guarantees that all press events are delivered +immediately and most release events are delivered immediately. The +"spurious" method requires that release events are delayed, libinput thus +does not enable this method unless a faulty event sequence is detected. A +message is printed to the log when spurious deboucing was detected. Note that libinput's debouncing intended to correct hardware damage or substandard hardware. Debouncing is also used as an accessibility feature @@ -23,4 +36,12 @@ physical key presses, usually caused by involuntary muscle movement, must be filtered to only one key press. This feature must be implemented higher in the stack, libinput is limited to hardware debouncing. +Below is an illustration of the button debouncing modes to show the relation +of the physical button state and the application state. Where applicable, an +extra line is added to show the timeouts used by libinput that +affect the button state handling. The waveform's high and low states +correspond to the buttons 'pressed' and 'released' states, respectively. + +@image html button-debouncing-wave-diagram.svg "Diagram illustrating button debouncing" + */ diff --git a/doc/svg/button-debouncing-wave-diagram.svg b/doc/svg/button-debouncing-wave-diagram.svg new file mode 100644 index 00000000..7c43c949 --- /dev/null +++ b/doc/svg/button-debouncing-wave-diagram.svg @@ -0,0 +1,4 @@ + + + +Button Debouncing Scenarioscurrent modenormal button press and releasephysical buttonapplication current modedebounced button pressphysical buttontimeoutsapplication current modedebounced button releasephysical buttontimeoutsapplication current modedelayed button pressphysical buttontimeoutsapplication current modedelayed button releasephysical buttontimeoutsapplication current modefirst spurious button release physical buttonapplication current modelater spurious button release physical buttontimeoutsapplication current modedelayed release in spurious mode physical buttontimeoutsapplication bounce modespurious mode \ No newline at end of file diff --git a/meson.build b/meson.build index 1dc0b7d4..175f6bac 100644 --- a/meson.build +++ b/meson.build @@ -156,6 +156,7 @@ src_libinput = [ 'src/libinput-private.h', 'src/evdev.c', 'src/evdev.h', + 'src/evdev-debounce.c', 'src/evdev-fallback.c', 'src/evdev-fallback.h', 'src/evdev-middle-button.c', @@ -299,6 +300,7 @@ if get_option('documentation') meson.source_root() + '/doc/dot/libinput-stack-gnome.gv', meson.source_root() + '/doc/dot/evemu.gv', # svgs + meson.source_root() + '/doc/svg/button-debouncing-wave-diagram.svg', meson.source_root() + '/doc/svg/button-scrolling.svg', meson.source_root() + '/doc/svg/clickfinger.svg', meson.source_root() + '/doc/svg/clickfinger-distance.svg', diff --git a/src/evdev-debounce.c b/src/evdev-debounce.c new file mode 100644 index 00000000..7f96249b --- /dev/null +++ b/src/evdev-debounce.c @@ -0,0 +1,564 @@ +/* + * Copyright © 2017 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 "evdev-fallback.h" + +/* Debounce cases to handle + P ... button press + R ... button release + ---| timeout duration + + 'normal' .... event sent when it happens + 'filtered' .. event is not sent (but may be sent later) + 'delayed' ... event is sent with wall-clock delay + + + 1) P---| R P normal, R normal + 2) R---| P R normal, P normal + 3) P---R--| P P normal, R filtered, delayed, P normal + 4) R---P--| R R normal, P filtered, delayed, R normal + 4.1) P---| R--P--| P normal, R filtered + 5) P--R-P-| R P normal, R filtered, P filtered, R normal + 6) R--P-R-| P R normal, P filtered, R filtered, P normal + 7) P--R--| + ---P-| P normal, R filtered, P filtered + 8) R--P--| + ---R-| R normal, P filtered, R filtered + + 1, 2 are the normal click cases without debouncing taking effect + 3, 4 are fast clicks where the second event is delivered with a delay + 5, 6 are contact bounces, fast + 7, 8 are contact bounces, slow + + 4.1 is a special case with the same event sequence as 4 but we want to + filter the *release* event out, it's a button losing contact while being + held down. + + 7 and 8 are cases where the first event happens within the first timeout + but the second event is outside that timeout (but within the timeout of + the second event). These cases are currently unhandled. +*/ + + +enum debounce_event { + DEBOUNCE_EVENT_PRESS = 50, + DEBOUNCE_EVENT_RELEASE, + DEBOUNCE_EVENT_TIMEOUT, + DEBOUNCE_EVENT_TIMEOUT_SHORT, + DEBOUNCE_EVENT_OTHERBUTTON, +}; + +static inline const char * +debounce_state_to_str(enum debounce_state state) +{ + switch(state) { + CASE_RETURN_STRING(DEBOUNCE_STATE_IS_UP); + CASE_RETURN_STRING(DEBOUNCE_STATE_IS_DOWN); + CASE_RETURN_STRING(DEBOUNCE_STATE_DOWN_WAITING); + CASE_RETURN_STRING(DEBOUNCE_STATE_RELEASE_PENDING); + CASE_RETURN_STRING(DEBOUNCE_STATE_RELEASE_DELAYED); + CASE_RETURN_STRING(DEBOUNCE_STATE_RELEASE_WAITING); + CASE_RETURN_STRING(DEBOUNCE_STATE_MAYBE_SPURIOUS); + CASE_RETURN_STRING(DEBOUNCE_STATE_RELEASED); + CASE_RETURN_STRING(DEBOUNCE_STATE_PRESS_PENDING); + } + + return NULL; +} + +static inline const char* +debounce_event_to_str(enum debounce_event event) +{ + switch(event) { + CASE_RETURN_STRING(DEBOUNCE_EVENT_PRESS); + CASE_RETURN_STRING(DEBOUNCE_EVENT_RELEASE); + CASE_RETURN_STRING(DEBOUNCE_EVENT_TIMEOUT); + CASE_RETURN_STRING(DEBOUNCE_EVENT_TIMEOUT_SHORT); + CASE_RETURN_STRING(DEBOUNCE_EVENT_OTHERBUTTON); + } + return NULL; +} + +static inline void +log_debounce_bug(struct fallback_dispatch *fallback, enum debounce_event event) +{ + evdev_log_bug_libinput(fallback->device, + "invalid debounce event %s in state %s\n", + debounce_event_to_str(event), + debounce_state_to_str(fallback->debounce.state)); + +} + +static inline void +debounce_set_state(struct fallback_dispatch *fallback, + enum debounce_state new_state) +{ + assert(new_state >= DEBOUNCE_STATE_IS_UP && + new_state <= DEBOUNCE_STATE_PRESS_PENDING); + + fallback->debounce.state = new_state; +} + +static inline void +debounce_set_timer(struct fallback_dispatch *fallback, + uint64_t time) +{ + const int DEBOUNCE_TIMEOUT_BOUNCE = ms2us(25); + + libinput_timer_set(&fallback->debounce.timer, + time + DEBOUNCE_TIMEOUT_BOUNCE); +} + +static inline void +debounce_set_timer_short(struct fallback_dispatch *fallback, + uint64_t time) +{ + const int DEBOUNCE_TIMEOUT_SPURIOUS = ms2us(12); + + libinput_timer_set(&fallback->debounce.timer_short, + time + DEBOUNCE_TIMEOUT_SPURIOUS); +} + +static inline void +debounce_cancel_timer(struct fallback_dispatch *fallback) +{ + libinput_timer_cancel(&fallback->debounce.timer); +} + +static inline void +debounce_cancel_timer_short(struct fallback_dispatch *fallback) +{ + libinput_timer_cancel(&fallback->debounce.timer_short); +} + +static inline void +debounce_enable_spurious(struct fallback_dispatch *fallback) +{ + if (fallback->debounce.spurious_enabled) + evdev_log_bug_libinput(fallback->device, + "tried to enable spurious debouncing twice\n"); + + fallback->debounce.spurious_enabled = true; + evdev_log_info(fallback->device, + "Enabling spurious button debouncing, " + "see %sbutton_debouncing.html for details\n", + HTTP_DOC_LINK); +} + +static void +debounce_notify_button(struct fallback_dispatch *fallback, + enum libinput_button_state state) +{ + struct evdev_device *device = fallback->device; + unsigned int code = fallback->debounce.button_code; + uint64_t time = fallback->debounce.button_time; + + code = evdev_to_left_handed(device, code); + + evdev_pointer_notify_physical_button(device, time, code, state); +} + +static void +debounce_is_up_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time) +{ + switch (event) { + case DEBOUNCE_EVENT_PRESS: + fallback->debounce.button_time = time; + debounce_set_timer(fallback, time); + debounce_set_state(fallback, DEBOUNCE_STATE_DOWN_WAITING); + debounce_notify_button(fallback, + LIBINPUT_BUTTON_STATE_PRESSED); + break; + case DEBOUNCE_EVENT_RELEASE: + case DEBOUNCE_EVENT_TIMEOUT: + case DEBOUNCE_EVENT_TIMEOUT_SHORT: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_OTHERBUTTON: + break; + } +} + +static void +debounce_is_down_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time) +{ + switch (event) { + case DEBOUNCE_EVENT_PRESS: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_RELEASE: + fallback->debounce.button_time = time; + debounce_set_timer(fallback, time); + debounce_set_timer_short(fallback, time); + if (fallback->debounce.spurious_enabled) { + debounce_set_state(fallback, DEBOUNCE_STATE_RELEASE_DELAYED); + } else { + debounce_set_state(fallback, DEBOUNCE_STATE_RELEASE_WAITING); + debounce_notify_button(fallback, + LIBINPUT_BUTTON_STATE_RELEASED); + } + break; + case DEBOUNCE_EVENT_TIMEOUT: + case DEBOUNCE_EVENT_TIMEOUT_SHORT: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_OTHERBUTTON: + break; + } +} + +static void +debounce_down_waiting_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time) +{ + switch (event) { + case DEBOUNCE_EVENT_PRESS: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_RELEASE: + debounce_set_state(fallback, DEBOUNCE_STATE_RELEASE_PENDING); + /* Note: In the debouncing RPR case, we use the last + * release's time stamp */ + fallback->debounce.button_time = time; + break; + case DEBOUNCE_EVENT_TIMEOUT: + debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN); + break; + case DEBOUNCE_EVENT_TIMEOUT_SHORT: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_OTHERBUTTON: + debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN); + break; + } +} + +static void +debounce_release_pending_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time) +{ + switch (event) { + case DEBOUNCE_EVENT_PRESS: + debounce_set_state(fallback, DEBOUNCE_STATE_DOWN_WAITING); + break; + case DEBOUNCE_EVENT_RELEASE: + case DEBOUNCE_EVENT_TIMEOUT_SHORT: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_TIMEOUT: + case DEBOUNCE_EVENT_OTHERBUTTON: + debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP); + debounce_notify_button(fallback, + LIBINPUT_BUTTON_STATE_RELEASED); + break; + } +} + +static void +debounce_release_delayed_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time) +{ + switch (event) { + case DEBOUNCE_EVENT_PRESS: + debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN); + debounce_cancel_timer(fallback); + debounce_cancel_timer_short(fallback); + break; + case DEBOUNCE_EVENT_RELEASE: + case DEBOUNCE_EVENT_TIMEOUT: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_TIMEOUT_SHORT: + debounce_set_state(fallback, DEBOUNCE_STATE_RELEASE_WAITING); + debounce_notify_button(fallback, + LIBINPUT_BUTTON_STATE_RELEASED); + break; + case DEBOUNCE_EVENT_OTHERBUTTON: + debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP); + debounce_notify_button(fallback, + LIBINPUT_BUTTON_STATE_RELEASED); + break; + } +} + +static void +debounce_release_waiting_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time) +{ + switch (event) { + case DEBOUNCE_EVENT_PRESS: + /* Note: in a bouncing PRP case, we use the last press + * event time */ + fallback->debounce.button_time = time; + debounce_set_state(fallback, DEBOUNCE_STATE_MAYBE_SPURIOUS); + break; + case DEBOUNCE_EVENT_RELEASE: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_TIMEOUT: + debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP); + break; + case DEBOUNCE_EVENT_TIMEOUT_SHORT: + debounce_set_state(fallback, DEBOUNCE_STATE_RELEASED); + break; + case DEBOUNCE_EVENT_OTHERBUTTON: + debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP); + break; + } +} + +static void +debounce_maybe_spurious_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time) +{ + switch (event) { + case DEBOUNCE_EVENT_PRESS: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_RELEASE: + debounce_set_state(fallback, DEBOUNCE_STATE_RELEASE_WAITING); + break; + case DEBOUNCE_EVENT_TIMEOUT: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_TIMEOUT_SHORT: + debounce_cancel_timer(fallback); + debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN); + debounce_enable_spurious(fallback); + debounce_notify_button(fallback, + LIBINPUT_BUTTON_STATE_PRESSED); + break; + case DEBOUNCE_EVENT_OTHERBUTTON: + debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN); + debounce_notify_button(fallback, + LIBINPUT_BUTTON_STATE_PRESSED); + break; + } +} + +static void +debounce_released_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time) +{ + switch (event) { + case DEBOUNCE_EVENT_PRESS: + /* Note: in a debouncing PRP case, we use the last press' + * time */ + fallback->debounce.button_time = time; + debounce_set_state(fallback, DEBOUNCE_STATE_PRESS_PENDING); + break; + case DEBOUNCE_EVENT_RELEASE: + case DEBOUNCE_EVENT_TIMEOUT_SHORT: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_TIMEOUT: + case DEBOUNCE_EVENT_OTHERBUTTON: + debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP); + break; + } +} + +static void +debounce_press_pending_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time) +{ + switch (event) { + case DEBOUNCE_EVENT_PRESS: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_RELEASE: + debounce_set_state(fallback, DEBOUNCE_STATE_RELEASED); + break; + case DEBOUNCE_EVENT_TIMEOUT_SHORT: + log_debounce_bug(fallback, event); + break; + case DEBOUNCE_EVENT_TIMEOUT: + case DEBOUNCE_EVENT_OTHERBUTTON: + debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN); + debounce_notify_button(fallback, + LIBINPUT_BUTTON_STATE_PRESSED); + break; + } +} + +static void +debounce_handle_event(struct fallback_dispatch *fallback, + enum debounce_event event, + uint64_t time) +{ + enum debounce_state current = fallback->debounce.state; + + if (event == DEBOUNCE_EVENT_OTHERBUTTON) { + debounce_cancel_timer(fallback); + debounce_cancel_timer_short(fallback); + } + + switch(current) { + case DEBOUNCE_STATE_IS_UP: + debounce_is_up_handle_event(fallback, event, time); + break; + case DEBOUNCE_STATE_IS_DOWN: + debounce_is_down_handle_event(fallback, event, time); + break; + case DEBOUNCE_STATE_DOWN_WAITING: + debounce_down_waiting_handle_event(fallback, event, time); + break; + case DEBOUNCE_STATE_RELEASE_PENDING: + debounce_release_pending_handle_event(fallback, event, time); + break; + case DEBOUNCE_STATE_RELEASE_DELAYED: + debounce_release_delayed_handle_event(fallback, event, time); + break; + case DEBOUNCE_STATE_RELEASE_WAITING: + debounce_release_waiting_handle_event(fallback, event, time); + break; + case DEBOUNCE_STATE_MAYBE_SPURIOUS: + debounce_maybe_spurious_handle_event(fallback, event, time); + break; + case DEBOUNCE_STATE_RELEASED: + debounce_released_handle_event(fallback, event, time); + break; + case DEBOUNCE_STATE_PRESS_PENDING: + debounce_press_pending_event(fallback, event, time); + break; + } + + evdev_log_debug(fallback->device, + "debounce state: %s → %s → %s\n", + debounce_state_to_str(current), + debounce_event_to_str(event), + debounce_state_to_str(fallback->debounce.state)); +} + +void +fallback_debounce_handle_state(struct fallback_dispatch *dispatch, + uint64_t time) +{ + unsigned int changed[16] = {0}; /* event codes of changed buttons */ + size_t nchanged = 0; + bool flushed = false; + + for (unsigned int code = 0; code <= KEY_MAX; code++) { + if (get_key_type(code) != KEY_TYPE_BUTTON) + continue; + + if (hw_key_has_changed(dispatch, code)) + changed[nchanged++] = code; + + /* If you manage to press more than 16 buttons in the same + * frame, we just quietly ignore the rest of them */ + if (nchanged == ARRAY_LENGTH(changed)) + break; + } + + /* If we have more than one button this frame or a different button, + * flush the state machine with otherbutton */ + if (nchanged > 1 || + changed[0] != dispatch->debounce.button_code) { + debounce_handle_event(dispatch, + DEBOUNCE_EVENT_OTHERBUTTON, + time); + flushed = true; + } + + /* The state machine has some pre-conditions: + * - the IS_DOWN and IS_UP states are neutral entry states without + * any timeouts + * - a OTHERBUTTON event always flushes the state to IS_DOWN or + * IS_UP + */ + + for (size_t i = 0; i < nchanged; i++) { + bool is_down = hw_is_key_down(dispatch, changed[i]); + + if (flushed) { + debounce_set_state(dispatch, + !is_down ? + DEBOUNCE_STATE_IS_DOWN : + DEBOUNCE_STATE_IS_UP); + flushed = false; + } + + + dispatch->debounce.button_code = changed[i]; + debounce_handle_event(dispatch, + is_down ? + DEBOUNCE_EVENT_PRESS : + DEBOUNCE_EVENT_RELEASE, + time); + + /* if we have more than one event, we flush the state + * machine immediately after the event itself */ + if (nchanged > 1) { + debounce_handle_event(dispatch, + DEBOUNCE_EVENT_OTHERBUTTON, + time); + flushed = true; + } + + } +} + +static void +debounce_timeout(uint64_t now, void *data) +{ + struct evdev_device *device = data; + struct fallback_dispatch *dispatch = + fallback_dispatch(device->dispatch); + + debounce_handle_event(dispatch, DEBOUNCE_EVENT_TIMEOUT, now); +} + +static void +debounce_timeout_short(uint64_t now, void *data) +{ + struct evdev_device *device = data; + struct fallback_dispatch *dispatch = + fallback_dispatch(device->dispatch); + + debounce_handle_event(dispatch, DEBOUNCE_EVENT_TIMEOUT_SHORT, now); +} + +void +fallback_init_debounce(struct fallback_dispatch *dispatch) +{ + struct evdev_device *device = dispatch->device; + char timer_name[64]; + + dispatch->debounce.state = DEBOUNCE_STATE_IS_UP; + + snprintf(timer_name, + sizeof(timer_name), + "%s debounce short", + evdev_device_get_sysname(device)); + libinput_timer_init(&dispatch->debounce.timer_short, + evdev_libinput_context(device), + timer_name, + debounce_timeout_short, + device); + + snprintf(timer_name, + sizeof(timer_name), + "%s debounce", + evdev_device_get_sysname(device)); + libinput_timer_init(&dispatch->debounce.timer, + evdev_libinput_context(device), + timer_name, + debounce_timeout, + device); +} diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c index 70cbd3e1..7bfcaf90 100644 --- a/src/evdev-fallback.c +++ b/src/evdev-fallback.c @@ -32,49 +32,6 @@ #define DEBOUNCE_TIME ms2us(12) -enum key_type { - KEY_TYPE_NONE, - KEY_TYPE_KEY, - KEY_TYPE_BUTTON, -}; - -static void -hw_set_key_down(struct fallback_dispatch *dispatch, int code, int pressed) -{ - long_set_bit_state(dispatch->hw_key_mask, code, pressed); -} - -static bool -hw_key_has_changed(struct fallback_dispatch *dispatch, int code) -{ - return long_bit_is_set(dispatch->hw_key_mask, code) != - long_bit_is_set(dispatch->last_hw_key_mask, code); -} - -static void -hw_key_update_last_state(struct fallback_dispatch *dispatch) -{ - static_assert(sizeof(dispatch->hw_key_mask) == - sizeof(dispatch->last_hw_key_mask), - "Mismatching key mask size"); - - memcpy(dispatch->last_hw_key_mask, - dispatch->hw_key_mask, - sizeof(dispatch->hw_key_mask)); -} - -static bool -hw_is_key_down(struct fallback_dispatch *dispatch, int code) -{ - return long_bit_is_set(dispatch->hw_key_mask, code); -} - -static int -get_key_down_count(struct evdev_device *device, int code) -{ - return device->key_count[code]; -} - static void fallback_keyboard_notify_key(struct fallback_dispatch *dispatch, struct evdev_device *device, @@ -495,41 +452,6 @@ fallback_flush_st_up(struct fallback_dispatch *dispatch, return true; } -static enum key_type -get_key_type(uint16_t code) -{ - switch (code) { - case BTN_TOOL_PEN: - case BTN_TOOL_RUBBER: - case BTN_TOOL_BRUSH: - case BTN_TOOL_PENCIL: - case BTN_TOOL_AIRBRUSH: - case BTN_TOOL_MOUSE: - case BTN_TOOL_LENS: - case BTN_TOOL_QUINTTAP: - case BTN_TOOL_DOUBLETAP: - case BTN_TOOL_TRIPLETAP: - case BTN_TOOL_QUADTAP: - case BTN_TOOL_FINGER: - case BTN_TOUCH: - return KEY_TYPE_NONE; - } - - if (code >= KEY_ESC && code <= KEY_MICMUTE) - return KEY_TYPE_KEY; - if (code >= BTN_MISC && code <= BTN_GEAR_UP) - return KEY_TYPE_BUTTON; - if (code >= KEY_OK && code <= KEY_LIGHTS_TOGGLE) - return KEY_TYPE_KEY; - if (code >= BTN_DPAD_UP && code <= BTN_DPAD_RIGHT) - return KEY_TYPE_BUTTON; - if (code >= KEY_ALS_TOGGLE && code <= KEY_ONSCREEN_KEYBOARD) - return KEY_TYPE_KEY; - if (code >= BTN_TRIGGER_HAPPY && code <= BTN_TRIGGER_HAPPY40) - return KEY_TYPE_BUTTON; - return KEY_TYPE_NONE; -} - static void fallback_process_touch_button(struct fallback_dispatch *dispatch, struct evdev_device *device, @@ -540,149 +462,6 @@ fallback_process_touch_button(struct fallback_dispatch *dispatch, EVDEV_ABSOLUTE_TOUCH_UP; } -static inline void -fallback_flush_debounce(struct fallback_dispatch *dispatch, - struct evdev_device *device) -{ - int code = dispatch->debounce.button_code; - int button; - - if (dispatch->debounce.state != DEBOUNCE_ACTIVE) - return; - - if (hw_is_key_down(dispatch, code)) { - button = evdev_to_left_handed(device, code); - evdev_pointer_notify_physical_button(device, - dispatch->debounce.button_up_time, - button, - LIBINPUT_BUTTON_STATE_RELEASED); - hw_set_key_down(dispatch, code, 0); - hw_key_update_last_state(dispatch); - } - - dispatch->debounce.state = DEBOUNCE_ON; -} - -static void -fallback_debounce_timeout(uint64_t now, void *data) -{ - struct evdev_device *device = data; - struct fallback_dispatch *dispatch = - fallback_dispatch(device->dispatch); - - fallback_flush_debounce(dispatch, device); -} - -static bool -fallback_filter_debounce_press(struct fallback_dispatch *dispatch, - struct evdev_device *device, - struct input_event *e, - uint64_t time) -{ - bool filter = false; - uint64_t tdelta; - - /* If other button is pressed while we're holding back the release, - * flush the pending release (if any) and continue. We don't handle - * this situation, if you have a mouse that needs per-button - * debouncing, consider writing to santa for a new mouse. - */ - if (e->code != dispatch->debounce.button_code) { - if (dispatch->debounce.state == DEBOUNCE_ACTIVE) { - libinput_timer_cancel(&dispatch->debounce.timer); - fallback_flush_debounce(dispatch, device); - } - return false; - } - - tdelta = time - dispatch->debounce.button_up_time; - assert((int64_t)tdelta >= 0); - - if (tdelta < DEBOUNCE_TIME) { - switch (dispatch->debounce.state) { - case DEBOUNCE_INIT: - /* This is the first time we debounce, enable proper debouncing - from now on but filter this press event */ - filter = true; - evdev_log_info(device, - "Enabling button debouncing, " - "see %sbutton_debouncing.html for details\n", - HTTP_DOC_LINK); - dispatch->debounce.state = DEBOUNCE_NEEDED; - break; - case DEBOUNCE_NEEDED: - case DEBOUNCE_ON: - break; - /* If a release event is pending and, filter press - * events until we flushed the release */ - case DEBOUNCE_ACTIVE: - filter = true; - break; - } - } else if (dispatch->debounce.state == DEBOUNCE_ACTIVE) { - /* call libinput_dispatch() more frequently */ - evdev_log_bug_client(device, - "Debouncing still active past timeout\n"); - } - - return filter; -} - -static bool -fallback_filter_debounce_release(struct fallback_dispatch *dispatch, - struct input_event *e, - uint64_t time) -{ - bool filter = false; - - dispatch->debounce.button_code = e->code; - dispatch->debounce.button_up_time = time; - - switch (dispatch->debounce.state) { - case DEBOUNCE_INIT: - break; - case DEBOUNCE_NEEDED: - filter = true; - dispatch->debounce.state = DEBOUNCE_ON; - break; - case DEBOUNCE_ON: - libinput_timer_set(&dispatch->debounce.timer, - time + DEBOUNCE_TIME); - filter = true; - dispatch->debounce.state = DEBOUNCE_ACTIVE; - break; - case DEBOUNCE_ACTIVE: - filter = true; - break; - } - - return filter; -} - -static bool -fallback_filter_debounce(struct fallback_dispatch *dispatch, - struct evdev_device *device, - struct input_event *e, uint64_t time) -{ - bool filter = false; - - /* Behavior: we monitor the time deltas between release and press - * events. Proper debouncing is disabled on init, but the first - * time we see a bouncing press event we enable it. - * - * The first bounced event is simply discarded, which ends up in the - * button being released sooner than it should be. Subsequent button - * presses are timer-based and thus released a bit later because we - * then wait for a timeout before we post the release event. - */ - if (e->value) - filter = fallback_filter_debounce_press(dispatch, device, e, time); - else - filter = fallback_filter_debounce_release(dispatch, e, time); - - return filter; -} - static inline void fallback_process_key(struct fallback_dispatch *dispatch, struct evdev_device *device, @@ -712,20 +491,11 @@ fallback_process_key(struct fallback_dispatch *dispatch, case KEY_TYPE_NONE: break; case KEY_TYPE_KEY: - if ((e->value && hw_is_key_down(dispatch, e->code)) || - (e->value == 0 && !hw_is_key_down(dispatch, e->code))) - return; - - dispatch->pending_event |= EVDEV_KEY; - break; case KEY_TYPE_BUTTON: - /* FIXME: should move to handle_state */ - if (fallback_filter_debounce(dispatch, device, e, time)) - return; - if ((e->value && hw_is_key_down(dispatch, e->code)) || (e->value == 0 && !hw_is_key_down(dispatch, e->code))) return; + dispatch->pending_event |= EVDEV_KEY; break; } @@ -1055,6 +825,7 @@ fallback_handle_state(struct fallback_dispatch *dispatch, /* Buttons and keys */ if (dispatch->pending_event & EVDEV_KEY) { + bool want_debounce = false; for (unsigned int code = 0; code <= KEY_MAX; code++) { bool new_state; @@ -1077,17 +848,14 @@ fallback_handle_state(struct fallback_dispatch *dispatch, LIBINPUT_KEY_STATE_RELEASED); break; case KEY_TYPE_BUTTON: - evdev_pointer_notify_physical_button( - device, - time, - evdev_to_left_handed(device, code), - new_state ? - LIBINPUT_BUTTON_STATE_PRESSED : - LIBINPUT_BUTTON_STATE_RELEASED); + want_debounce = true; break; } - } + + if (want_debounce) + fallback_debounce_handle_state(dispatch, time); + hw_key_update_last_state(dispatch); } @@ -1299,6 +1067,8 @@ fallback_interface_destroy(struct evdev_dispatch *evdev_dispatch) libinput_timer_cancel(&dispatch->debounce.timer); libinput_timer_destroy(&dispatch->debounce.timer); + libinput_timer_cancel(&dispatch->debounce.timer_short); + libinput_timer_destroy(&dispatch->debounce.timer_short); free(dispatch->mt.slots); free(dispatch); } @@ -1672,7 +1442,6 @@ fallback_dispatch_create(struct libinput_device *libinput_device) { struct evdev_device *device = evdev_device(libinput_device); struct fallback_dispatch *dispatch; - char timer_name[64]; dispatch = zalloc(sizeof *dispatch); dispatch->device = evdev_device(libinput_device); @@ -1722,14 +1491,7 @@ fallback_dispatch_create(struct libinput_device *libinput_device) want_config); } - snprintf(timer_name, - sizeof(timer_name), - "%s debounce", - evdev_device_get_sysname(device)); - libinput_timer_init(&dispatch->debounce.timer, - evdev_libinput_context(device), - timer_name, - fallback_debounce_timeout, - device); + fallback_init_debounce(dispatch); + return &dispatch->base; } diff --git a/src/evdev-fallback.h b/src/evdev-fallback.h index ba121c08..0d9e247e 100644 --- a/src/evdev-fallback.h +++ b/src/evdev-fallback.h @@ -31,6 +31,18 @@ #include "evdev.h" +enum debounce_state { + DEBOUNCE_STATE_IS_UP = 100, + DEBOUNCE_STATE_IS_DOWN, + DEBOUNCE_STATE_DOWN_WAITING, + DEBOUNCE_STATE_RELEASE_PENDING, + DEBOUNCE_STATE_RELEASE_DELAYED, + DEBOUNCE_STATE_RELEASE_WAITING, + DEBOUNCE_STATE_MAYBE_SPURIOUS, + DEBOUNCE_STATE_RELEASED, + DEBOUNCE_STATE_PRESS_PENDING, +}; + struct fallback_dispatch { struct evdev_dispatch base; struct evdev_device *device; @@ -85,10 +97,16 @@ struct fallback_dispatch { bool ignore_events; struct { +#if 0 enum evdev_debounce_state state; - unsigned int button_code; uint64_t button_up_time; +#endif + unsigned int button_code; + uint64_t button_time; struct libinput_timer timer; + struct libinput_timer timer_short; + enum debounce_state state; + bool spurious_enabled; } debounce; struct { @@ -119,4 +137,86 @@ fallback_dispatch(struct evdev_dispatch *dispatch) return container_of(dispatch, struct fallback_dispatch, base); } +enum key_type { + KEY_TYPE_NONE, + KEY_TYPE_KEY, + KEY_TYPE_BUTTON, +}; + +static inline enum key_type +get_key_type(uint16_t code) +{ + switch (code) { + case BTN_TOOL_PEN: + case BTN_TOOL_RUBBER: + case BTN_TOOL_BRUSH: + case BTN_TOOL_PENCIL: + case BTN_TOOL_AIRBRUSH: + case BTN_TOOL_MOUSE: + case BTN_TOOL_LENS: + case BTN_TOOL_QUINTTAP: + case BTN_TOOL_DOUBLETAP: + case BTN_TOOL_TRIPLETAP: + case BTN_TOOL_QUADTAP: + case BTN_TOOL_FINGER: + case BTN_TOUCH: + return KEY_TYPE_NONE; + } + + if (code >= KEY_ESC && code <= KEY_MICMUTE) + return KEY_TYPE_KEY; + if (code >= BTN_MISC && code <= BTN_GEAR_UP) + return KEY_TYPE_BUTTON; + if (code >= KEY_OK && code <= KEY_LIGHTS_TOGGLE) + return KEY_TYPE_KEY; + if (code >= BTN_DPAD_UP && code <= BTN_DPAD_RIGHT) + return KEY_TYPE_BUTTON; + if (code >= KEY_ALS_TOGGLE && code <= KEY_ONSCREEN_KEYBOARD) + return KEY_TYPE_KEY; + if (code >= BTN_TRIGGER_HAPPY && code <= BTN_TRIGGER_HAPPY40) + return KEY_TYPE_BUTTON; + return KEY_TYPE_NONE; +} + +static inline void +hw_set_key_down(struct fallback_dispatch *dispatch, int code, int pressed) +{ + long_set_bit_state(dispatch->hw_key_mask, code, pressed); +} + +static inline bool +hw_key_has_changed(struct fallback_dispatch *dispatch, int code) +{ + return long_bit_is_set(dispatch->hw_key_mask, code) != + long_bit_is_set(dispatch->last_hw_key_mask, code); +} + +static inline void +hw_key_update_last_state(struct fallback_dispatch *dispatch) +{ + static_assert(sizeof(dispatch->hw_key_mask) == + sizeof(dispatch->last_hw_key_mask), + "Mismatching key mask size"); + + memcpy(dispatch->last_hw_key_mask, + dispatch->hw_key_mask, + sizeof(dispatch->hw_key_mask)); +} + +static inline bool +hw_is_key_down(struct fallback_dispatch *dispatch, int code) +{ + return long_bit_is_set(dispatch->hw_key_mask, code); +} + +static inline int +get_key_down_count(struct evdev_device *device, int code) +{ + return device->key_count[code]; +} + +void fallback_init_debounce(struct fallback_dispatch *dispatch); +void fallback_debounce_handle_state(struct fallback_dispatch *dispatch, + uint64_t time); + #endif diff --git a/test/litest.c b/test/litest.c index 9819bed9..a447c81c 100644 --- a/test/litest.c +++ b/test/litest.c @@ -3182,7 +3182,7 @@ litest_timeout_tapndrag(void) void litest_timeout_debounce(void) { - msleep(15); + msleep(30); } void diff --git a/test/test-pointer.c b/test/test-pointer.c index 136aa48c..7324c0f6 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -2110,92 +2110,176 @@ START_TEST(pointer_time_usec) } END_TEST -START_TEST(debounce) +START_TEST(debounce_bounce) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + unsigned int button = _i; /* ranged test */ + + if (!libinput_device_pointer_has_button(dev->libinput_device, + button)) + return; litest_disable_middleemu(dev); + disable_button_scrolling(dev); litest_drain_events(li); - litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_KEY, button, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_KEY, button, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); - - /* expect debouncing on now, this event is ignored */ - litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_KEY, button, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 0); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - + libinput_dispatch(li); + litest_timeout_debounce(); libinput_dispatch(li); litest_assert_button_event(li, - BTN_LEFT, + button, LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_empty_queue(li); + + litest_event(dev, EV_KEY, button, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, button, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, button, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + litest_timeout_debounce(); + libinput_dispatch(li); + litest_assert_button_event(li, - BTN_LEFT, + button, LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); } END_TEST -START_TEST(debounce_timer) +START_TEST(debounce_bounce_check_immediate) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; litest_disable_middleemu(dev); + disable_button_scrolling(dev); litest_drain_events(li); + /* Press must be sent without delay */ litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 0); - litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_timeout_debounce(); + litest_assert_empty_queue(li); - /* expect debouncing on now, this event is ignored */ + /* held down & past timeout, we expect releases to be immediate */ + + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_timeout_debounce(); + litest_assert_empty_queue(li); +} +END_TEST + +/* Triggers the event sequence that initializes the spurious + * debouncing behavior */ +static inline void +debounce_trigger_spurious(struct litest_device *dev, struct libinput *li) +{ litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + litest_timeout_debounce(); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); libinput_dispatch(li); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); litest_timeout_debounce(); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + + /* gets filtered now */ + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + litest_timeout_debounce(); + libinput_dispatch(li); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); +} + +START_TEST(debounce_spurious) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + unsigned int button = _i; /* ranged test */ + + if (!libinput_device_pointer_has_button(dev->libinput_device, + button)) + return; + + litest_disable_middleemu(dev); + disable_button_scrolling(dev); litest_drain_events(li); + debounce_trigger_spurious(dev, li); + for (int i = 0; i < 3; i++) { - litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_KEY, button, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); libinput_dispatch(li); litest_timeout_debounce(); + libinput_dispatch(li); /* Not all devices can disable middle button emulation, time out on * middle button here to make sure the initial button press event * was flushed. */ - libinput_dispatch(li); litest_timeout_middlebutton(); libinput_dispatch(li); + litest_assert_button_event(li, - BTN_LEFT, + button, LIBINPUT_BUTTON_STATE_PRESSED); /* bouncy bouncy bouncy */ - litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_KEY, button, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_KEY, button, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_assert_empty_queue(li); - litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_KEY, button, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); libinput_dispatch(li); litest_timeout_debounce(); libinput_dispatch(li); litest_assert_button_event(li, - BTN_LEFT, + button, LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); @@ -2203,7 +2287,7 @@ START_TEST(debounce_timer) } END_TEST -START_TEST(debounce_multibounce) +START_TEST(debounce_spurious_multibounce) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; @@ -2211,15 +2295,7 @@ START_TEST(debounce_multibounce) litest_disable_middleemu(dev); litest_drain_events(li); - /* enable debouncing */ - litest_event(dev, EV_KEY, BTN_LEFT, 1); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 0); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 1); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 0); - litest_event(dev, EV_SYN, SYN_REPORT, 0); + debounce_trigger_spurious(dev, li); litest_drain_events(li); /* Let's assume our button has ventricular fibrilation and sends a @@ -2230,6 +2306,8 @@ START_TEST(debounce_multibounce) litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + litest_timeout_debounce(); /* Not all devices can disable middle button emulation, time out on * middle button here to make sure the initial button press event @@ -2268,20 +2346,34 @@ START_TEST(debounce_multibounce) } END_TEST -START_TEST(debounce_no_debounce_for_otherbutton) +START_TEST(debounce_spurious_dont_enable_on_otherbutton) { struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; struct libinput *li = dev->libinput; + if (!libinput_device_config_middle_emulation_is_available(device)) + return; + litest_disable_middleemu(dev); + disable_button_scrolling(dev); litest_drain_events(li); + /* Don't trigger spurious debouncing on otherbutton events */ litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + litest_timeout_debounce(); + libinput_dispatch(li); + litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_event(dev, EV_KEY, BTN_RIGHT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + 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); @@ -2297,43 +2389,68 @@ START_TEST(debounce_no_debounce_for_otherbutton) litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_empty_queue(li); + + /* Expect release to be immediate */ + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + litest_timeout_debounce(); + libinput_dispatch(li); + + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); } END_TEST -START_TEST(debounce_cancel_debounce_otherbutton) +START_TEST(debounce_spurious_cancel_debounce_otherbutton) { struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; struct libinput *li = dev->libinput; + if (!libinput_device_config_middle_emulation_is_available(device)) + return; + litest_disable_middleemu(dev); + disable_button_scrolling(dev); litest_drain_events(li); - litest_event(dev, EV_KEY, BTN_LEFT, 1); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 0); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 1); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 0); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - - litest_drain_events(li); + debounce_trigger_spurious(dev, li); litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + litest_timeout_debounce(); + libinput_dispatch(li); + + /* spurious debouncing is on but the release should get flushed by + * the other button */ litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 1); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - /* release is now held back, press was ignored, - * other button should flush the release */ litest_event(dev, EV_KEY, BTN_RIGHT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + 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); @@ -2349,6 +2466,12 @@ START_TEST(debounce_cancel_debounce_otherbutton) litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED); @@ -2357,31 +2480,28 @@ START_TEST(debounce_cancel_debounce_otherbutton) } END_TEST -START_TEST(debounce_switch_to_otherbutton) +START_TEST(debounce_spurious_switch_to_otherbutton) { struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; struct libinput *li = dev->libinput; - litest_drain_events(li); - - litest_event(dev, EV_KEY, BTN_LEFT, 1); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 0); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 1); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 0); - litest_event(dev, EV_SYN, SYN_REPORT, 0); + if (!libinput_device_config_middle_emulation_is_available(device)) + return; litest_drain_events(li); + debounce_trigger_spurious(dev, li); litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + litest_timeout_debounce(); + libinput_dispatch(li); + + litest_event(dev, EV_KEY, BTN_LEFT, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_event(dev, EV_KEY, BTN_LEFT, 1); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - /* release is now held back, press was ignored, + /* release is now held back, * other button should flush the release */ litest_event(dev, EV_KEY, BTN_RIGHT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); @@ -2419,6 +2539,7 @@ litest_setup_tests_pointer(void) { struct range axis_range = {ABS_X, ABS_Y + 1}; struct range compass = {0, 7}; /* cardinal directions */ + struct range buttons = {BTN_LEFT, BTN_TASK + 1}; litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_POINTINGSTICK); litest_add_for_device("pointer:motion", pointer_motion_relative_zero, LITEST_MOUSE); @@ -2481,10 +2602,11 @@ litest_setup_tests_pointer(void) litest_add("pointer:time", pointer_time_usec, LITEST_RELATIVE, LITEST_ANY); - litest_add("pointer:debounce", debounce, LITEST_BUTTON, LITEST_TOUCHPAD); - litest_add("pointer:debounce", debounce_timer, LITEST_BUTTON, LITEST_TOUCHPAD); - litest_add("pointer:debounce", debounce_multibounce, LITEST_BUTTON, LITEST_TOUCHPAD); - litest_add("pointer:debounce_otherbutton", debounce_no_debounce_for_otherbutton, LITEST_BUTTON, LITEST_TOUCHPAD); - litest_add("pointer:debounce_otherbutton", debounce_cancel_debounce_otherbutton, LITEST_BUTTON, LITEST_TOUCHPAD); - litest_add("pointer:debounce_otherbutton", debounce_switch_to_otherbutton, LITEST_BUTTON, LITEST_TOUCHPAD); + 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); }