From e461f21c5232baba105c0aaf17987e319143ba9d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 27 Apr 2017 14:12:11 +1000 Subject: [PATCH 1/7] touchpad: fix the button timestamps for double/tripletap Both events had the same timestamp but we have the timestamp from the original event - use it. https://bugs.freedesktop.org/show_bug.cgi?id=100796 Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-tap.c | 10 ++++-- test/test-touchpad-tap.c | 68 ++++++++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 29989c9e..6c989d91 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -341,7 +341,10 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp, tp_tap_clear_timer(tp); break; case TAP_EVENT_RELEASE: - tp_tap_notify(tp, time, 2, LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, + tp->tap.first_press_time, + 2, + LIBINPUT_BUTTON_STATE_PRESSED); tp_tap_notify(tp, time, 2, LIBINPUT_BUTTON_STATE_RELEASED); tp->tap.state = TAP_STATE_IDLE; break; @@ -376,7 +379,10 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_TOUCH_2_HOLD; if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { - tp_tap_notify(tp, time, 3, LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, + tp->tap.first_press_time, + 3, + LIBINPUT_BUTTON_STATE_PRESSED); tp_tap_notify(tp, time, 3, LIBINPUT_BUTTON_STATE_RELEASED); } break; diff --git a/test/test-touchpad-tap.c b/test/test-touchpad-tap.c index cc0d86fd..2739f853 100644 --- a/test/test-touchpad-tap.c +++ b/test/test-touchpad-tap.c @@ -916,6 +916,9 @@ START_TEST(touchpad_2fg_tap) struct libinput *li = dev->libinput; enum libinput_config_tap_button_map map = _i; /* ranged test */ unsigned int button = 0; + struct libinput_event *ev; + struct libinput_event_pointer *ptrev; + uint64_t ptime, rtime; litest_enable_tap(dev->libinput_device); litest_set_tap_map(dev->libinput_device, map); @@ -940,11 +943,20 @@ START_TEST(touchpad_2fg_tap) libinput_dispatch(li); - litest_assert_button_event(li, button, - LIBINPUT_BUTTON_STATE_PRESSED); - litest_timeout_tap(); - litest_assert_button_event(li, button, - LIBINPUT_BUTTON_STATE_RELEASED); + ev = libinput_get_event(li); + ptrev = litest_is_button_event(ev, + button, + LIBINPUT_BUTTON_STATE_PRESSED); + ptime = libinput_event_pointer_get_time_usec(ptrev); + libinput_event_destroy(ev); + ev = libinput_get_event(li); + ptrev = litest_is_button_event(ev, + button, + LIBINPUT_BUTTON_STATE_RELEASED); + rtime = libinput_event_pointer_get_time_usec(ptrev); + libinput_event_destroy(ev); + + ck_assert_int_lt(ptime, rtime); litest_assert_empty_queue(li); } @@ -956,6 +968,9 @@ START_TEST(touchpad_2fg_tap_inverted) struct libinput *li = dev->libinput; enum libinput_config_tap_button_map map = _i; /* ranged test */ unsigned int button = 0; + struct libinput_event *ev; + struct libinput_event_pointer *ptrev; + uint64_t ptime, rtime; litest_enable_tap(dev->libinput_device); litest_set_tap_map(dev->libinput_device, map); @@ -980,11 +995,20 @@ START_TEST(touchpad_2fg_tap_inverted) libinput_dispatch(li); - litest_assert_button_event(li, button, - LIBINPUT_BUTTON_STATE_PRESSED); - litest_timeout_tap(); - litest_assert_button_event(li, button, - LIBINPUT_BUTTON_STATE_RELEASED); + ev = libinput_get_event(li); + ptrev = litest_is_button_event(ev, + button, + LIBINPUT_BUTTON_STATE_PRESSED); + ptime = libinput_event_pointer_get_time_usec(ptrev); + libinput_event_destroy(ev); + ev = libinput_get_event(li); + ptrev = litest_is_button_event(ev, + button, + LIBINPUT_BUTTON_STATE_RELEASED); + rtime = libinput_event_pointer_get_time_usec(ptrev); + libinput_event_destroy(ev); + + ck_assert_int_lt(ptime, rtime); litest_assert_empty_queue(li); } @@ -1379,6 +1403,10 @@ START_TEST(touchpad_3fg_tap) } for (i = 0; i < 3; i++) { + uint64_t ptime, rtime; + struct libinput_event *ev; + struct libinput_event_pointer *ptrev; + litest_drain_events(li); litest_touch_down(dev, 0, 50, 50); @@ -1391,13 +1419,21 @@ START_TEST(touchpad_3fg_tap) libinput_dispatch(li); - litest_assert_button_event(li, button, - LIBINPUT_BUTTON_STATE_PRESSED); - litest_timeout_tap(); - litest_assert_button_event(li, button, - LIBINPUT_BUTTON_STATE_RELEASED); + ev = libinput_get_event(li); + ptrev = litest_is_button_event(ev, + button, + LIBINPUT_BUTTON_STATE_PRESSED); + ptime = libinput_event_pointer_get_time_usec(ptrev); + libinput_event_destroy(ev); + ev = libinput_get_event(li); + ptrev = litest_is_button_event(ev, + button, + LIBINPUT_BUTTON_STATE_RELEASED); + rtime = libinput_event_pointer_get_time_usec(ptrev); + libinput_event_destroy(ev); + + ck_assert_int_lt(ptime, rtime); - litest_assert_empty_queue(li); } } END_TEST From 68ed1b96a82740e80b258c4b4e1c8b8c0031cbc7 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 28 Apr 2017 13:34:56 +1000 Subject: [PATCH 2/7] test: enable drag lock for multitap tests Without this enabled, we stay in the single/double tap part of the state machine and a triple tap is just a double tap followed by a single tap. Signed-off-by: Peter Hutterer --- test/test-touchpad-tap.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/test-touchpad-tap.c b/test/test-touchpad-tap.c index 2739f853..1c90896b 100644 --- a/test/test-touchpad-tap.c +++ b/test/test-touchpad-tap.c @@ -125,6 +125,7 @@ START_TEST(touchpad_1fg_multitap) ntaps; litest_enable_tap(dev->libinput_device); + litest_enable_drag_lock(dev->libinput_device); litest_drain_events(li); @@ -173,6 +174,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_move) ntaps; litest_enable_tap(dev->libinput_device); + litest_enable_drag_lock(dev->libinput_device); litest_drain_events(li); @@ -242,6 +244,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_2fg) return; litest_enable_tap(dev->libinput_device); + litest_enable_drag_lock(dev->libinput_device); litest_drain_events(li); @@ -313,6 +316,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_click) ntaps; litest_enable_tap(dev->libinput_device); + litest_enable_drag_lock(dev->libinput_device); litest_drain_events(li); @@ -373,6 +377,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_timeout) ntaps; litest_enable_tap(dev->libinput_device); + litest_enable_drag_lock(dev->libinput_device); litest_drain_events(li); From 4c0b8ba4c2b3eab439921429ec8334d486c30735 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 28 Apr 2017 13:52:07 +1000 Subject: [PATCH 3/7] touchpad: send multitap button events with the correct timestamps For multitap, we're one tap behind with the button clicks, i.e. we send the first full click button on the second tap, etc. Remember the timestamps of the touches so we can send the events with the right timestamps. This makes tapping more accurate because the time between taps and various timeouts matter less for double-click detection. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-tap.c | 58 +++++++++++++++++++++++++++-------- src/evdev-mt-touchpad.h | 3 +- test/test-touchpad-tap.c | 61 +++++++++++++++++++++++++++++++++++-- 3 files changed, 105 insertions(+), 17 deletions(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 6c989d91..882b4cf6 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -152,7 +152,7 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_TOUCH; - tp->tap.first_press_time = time; + tp->tap.saved_press_time = time; tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: @@ -186,11 +186,12 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_RELEASE: tp_tap_notify(tp, - tp->tap.first_press_time, + tp->tap.saved_press_time, 1, LIBINPUT_BUTTON_STATE_PRESSED); if (tp->tap.drag_enabled) { tp->tap.state = TAP_STATE_TAPPED; + tp->tap.saved_release_time = time; tp_tap_set_timer(tp, time); } else { tp_tap_notify(tp, @@ -258,15 +259,22 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_DRAGGING_OR_DOUBLETAP; + tp->tap.saved_press_time = time; tp_tap_set_timer(tp, time); break; case TAP_EVENT_TIMEOUT: tp->tap.state = TAP_STATE_IDLE; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_DEAD; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); break; case TAP_EVENT_THUMB: break; @@ -342,7 +350,7 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_RELEASE: tp_tap_notify(tp, - tp->tap.first_press_time, + tp->tap.saved_press_time, 2, LIBINPUT_BUTTON_STATE_PRESSED); tp_tap_notify(tp, time, 2, LIBINPUT_BUTTON_STATE_RELEASED); @@ -380,7 +388,7 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, tp->tap.state = TAP_STATE_TOUCH_2_HOLD; if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { tp_tap_notify(tp, - tp->tap.first_press_time, + tp->tap.saved_press_time, 3, LIBINPUT_BUTTON_STATE_PRESSED); tp_tap_notify(tp, time, 3, LIBINPUT_BUTTON_STATE_RELEASED); @@ -430,7 +438,11 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_MULTITAP; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.saved_release_time = time; break; case TAP_EVENT_MOTION: case TAP_EVENT_TIMEOUT: @@ -438,7 +450,10 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_DEAD; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); break; case TAP_EVENT_THUMB: break; @@ -574,7 +589,11 @@ tp_tap_multitap_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_MULTITAP_DOWN; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, + tp->tap.saved_press_time, + 1, + LIBINPUT_BUTTON_STATE_PRESSED); + tp->tap.saved_press_time = time; tp_tap_set_timer(tp, time); break; case TAP_EVENT_MOTION: @@ -583,8 +602,14 @@ tp_tap_multitap_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_TIMEOUT: tp->tap.state = TAP_STATE_IDLE; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED); - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, + tp->tap.saved_press_time, + 1, + LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_IDLE; @@ -604,7 +629,11 @@ tp_tap_multitap_down_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_MULTITAP; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.saved_release_time = time; break; case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_DRAGGING_2; @@ -617,7 +646,10 @@ tp_tap_multitap_down_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_DEAD; - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); tp_tap_clear_timer(tp); break; case TAP_EVENT_THUMB: diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 8d5bbc44..304e92f8 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -321,7 +321,8 @@ struct tp_dispatch { struct libinput_timer timer; enum tp_tap_state state; uint32_t buttons_pressed; - uint64_t first_press_time; + uint64_t saved_press_time, + saved_release_time; enum libinput_config_tap_button_map map; enum libinput_config_tap_button_map want_map; diff --git a/test/test-touchpad-tap.c b/test/test-touchpad-tap.c index 1c90896b..021ccaa5 100644 --- a/test/test-touchpad-tap.c +++ b/test/test-touchpad-tap.c @@ -69,8 +69,11 @@ START_TEST(touchpad_1fg_doubletap) litest_drain_events(li); litest_touch_down(dev, 0, 50, 50); + msleep(10); litest_touch_up(dev, 0); + msleep(10); litest_touch_down(dev, 0, 50, 50); + msleep(10); litest_touch_up(dev, 0); libinput_dispatch(li); @@ -90,7 +93,7 @@ START_TEST(touchpad_1fg_doubletap) LIBINPUT_BUTTON_STATE_RELEASED); curtime = libinput_event_pointer_get_time(ptrev); libinput_event_destroy(event); - ck_assert_int_le(oldtime, curtime); + ck_assert_int_lt(oldtime, curtime); event = libinput_get_event(li); ptrev = litest_is_button_event(event, @@ -107,7 +110,7 @@ START_TEST(touchpad_1fg_doubletap) LIBINPUT_BUTTON_STATE_RELEASED); curtime = libinput_event_pointer_get_time(ptrev); libinput_event_destroy(event); - ck_assert_int_le(oldtime, curtime); + ck_assert_int_lt(oldtime, curtime); litest_assert_empty_queue(li); } @@ -365,6 +368,54 @@ START_TEST(touchpad_1fg_multitap_n_drag_click) } END_TEST +START_TEST(touchpad_1fg_multitap_timeout) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + uint32_t ptime, rtime; + int range = _i, /* looped test */ + ntaps; + + litest_enable_tap(dev->libinput_device); + litest_enable_drag_lock(dev->libinput_device); + + litest_drain_events(li); + + for (ntaps = 0; ntaps <= range; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + msleep(10); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + libinput_dispatch(li); + litest_timeout_tap(); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= range; ntaps++) { + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + ptime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + rtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_lt(ptime, rtime); + } + + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(touchpad_1fg_multitap_n_drag_timeout) { struct litest_device *dev = litest_current_device(); @@ -383,6 +434,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_timeout) for (ntaps = 0; ntaps <= range; ntaps++) { litest_touch_down(dev, 0, 50, 50); + msleep(10); litest_touch_up(dev, 0); libinput_dispatch(li); msleep(10); @@ -410,7 +462,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_timeout) LIBINPUT_BUTTON_STATE_RELEASED); curtime = libinput_event_pointer_get_time(ptrev); libinput_event_destroy(event); - ck_assert_int_ge(curtime, oldtime); + ck_assert_int_gt(curtime, oldtime); oldtime = curtime; } @@ -454,6 +506,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_tap) for (ntaps = 0; ntaps <= range; ntaps++) { litest_touch_down(dev, 0, 50, 50); + msleep(10); litest_touch_up(dev, 0); libinput_dispatch(li); msleep(10); @@ -527,6 +580,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_tap_click) for (ntaps = 0; ntaps <= range; ntaps++) { litest_touch_down(dev, 0, 50, 50); + msleep(10); litest_touch_up(dev, 0); libinput_dispatch(li); msleep(10); @@ -2223,6 +2277,7 @@ litest_setup_tests_touchpad_tap(void) litest_add("tap-1fg:1fg", touchpad_1fg_tap, LITEST_TOUCHPAD, LITEST_ANY); litest_add("tap-1fg:1fg", touchpad_1fg_doubletap, LITEST_TOUCHPAD, LITEST_ANY); litest_add_ranged("tap-multitap:1fg", touchpad_1fg_multitap, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); + litest_add_ranged("tap-multitap:1fg", touchpad_1fg_multitap_timeout, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); litest_add_ranged("tap-multitap:1fg", touchpad_1fg_multitap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); litest_add_ranged("tap-multitap:1fg", touchpad_1fg_multitap_n_drag_tap, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); litest_add_ranged("tap-multitap:1fg", touchpad_1fg_multitap_n_drag_move, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); From 691aea6d060834f233c6fb29d81b1e9610728545 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 28 Apr 2017 14:51:06 +1000 Subject: [PATCH 4/7] touchpad: for 2/3-finger tap, use the last finger down as press time This makes the tapping times shorter and hopefully more obvious. It also fixes a bug where repeated tripletap (by tapping with one finger while leaving the other two down) could cause incorrect timestamps. https://bugs.freedesktop.org/show_bug.cgi?id=100796 Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-tap.c | 10 ++++++- test/test-touchpad-tap.c | 56 +++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 882b4cf6..5a237fec 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -182,6 +182,7 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_TOUCH_2; + tp->tap.saved_press_time = time; tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: @@ -227,6 +228,7 @@ tp_tap_hold_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_TOUCH_2; + tp->tap.saved_press_time = time; tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: @@ -290,10 +292,12 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_TOUCH_3; + tp->tap.saved_press_time = time; tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_TOUCH_2_RELEASE; + tp->tap.saved_release_time = time; tp_tap_set_timer(tp, time); break; case TAP_EVENT_MOTION: @@ -319,6 +323,7 @@ tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_TOUCH_3; + tp->tap.saved_press_time = time; tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: @@ -353,7 +358,10 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp, tp->tap.saved_press_time, 2, LIBINPUT_BUTTON_STATE_PRESSED); - tp_tap_notify(tp, time, 2, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 2, + LIBINPUT_BUTTON_STATE_RELEASED); tp->tap.state = TAP_STATE_IDLE; break; case TAP_EVENT_MOTION: diff --git a/test/test-touchpad-tap.c b/test/test-touchpad-tap.c index 021ccaa5..d7b39ef9 100644 --- a/test/test-touchpad-tap.c +++ b/test/test-touchpad-tap.c @@ -1469,8 +1469,11 @@ START_TEST(touchpad_3fg_tap) litest_drain_events(li); litest_touch_down(dev, 0, 50, 50); + msleep(5); litest_touch_down(dev, 1, 70, 50); + msleep(5); litest_touch_down(dev, 2, 80, 50); + msleep(10); litest_touch_up(dev, (i + 2) % 3); litest_touch_up(dev, (i + 1) % 3); @@ -1497,6 +1500,58 @@ START_TEST(touchpad_3fg_tap) } END_TEST +START_TEST(touchpad_3fg_tap_tap_again) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + int i; + + if (libevdev_get_abs_maximum(dev->evdev, ABS_MT_SLOT) <= 2) + return; + + litest_enable_tap(dev->libinput_device); + + uint64_t ptime, rtime; + struct libinput_event *ev; + struct libinput_event_pointer *ptrev; + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + msleep(5); + litest_touch_down(dev, 1, 70, 50); + msleep(5); + litest_touch_down(dev, 2, 80, 50); + msleep(10); + litest_touch_up(dev, 0); + msleep(10); + litest_touch_down(dev, 0, 80, 50); + msleep(10); + litest_touch_up(dev, 0); + litest_touch_up(dev, 1); + litest_touch_up(dev, 2); + + libinput_dispatch(li); + + for (i = 0; i < 2; i++) { + ev = libinput_get_event(li); + ptrev = litest_is_button_event(ev, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + ptime = libinput_event_pointer_get_time_usec(ptrev); + libinput_event_destroy(ev); + ev = libinput_get_event(li); + ptrev = litest_is_button_event(ev, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + rtime = libinput_event_pointer_get_time_usec(ptrev); + libinput_event_destroy(ev); + + ck_assert_int_lt(ptime, rtime); + } +} +END_TEST + START_TEST(touchpad_3fg_tap_quickrelease) { struct litest_device *dev = litest_current_device(); @@ -2306,6 +2361,7 @@ litest_setup_tests_touchpad_tap(void) litest_add_ranged("tap-3fg:3fg", touchpad_3fg_tap_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &tap_map_range); litest_add_ranged("tap-3fg:3fg", touchpad_3fg_tap_btntool_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &tap_map_range); litest_add_ranged("tap-3fg:3fg", touchpad_3fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &tap_map_range); + litest_add("tap-3fg:3fg", touchpad_3fg_tap_tap_again, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("tap-3fg:3fg", touchpad_3fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("tap-4fg:4fg", touchpad_4fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); litest_add("tap-4fg:4fg", touchpad_4fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); From ea4332a6cf84bad73adc8f61da027ca2591105ec Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 26 Apr 2017 14:40:11 +1000 Subject: [PATCH 5/7] touchpad: add touch state debugging helper Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 54a3c8ea..a2cdf43f 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1268,6 +1268,28 @@ tp_handle_state(struct tp_dispatch *tp, tp_clickpad_middlebutton_apply_config(tp->device); } +static inline void +tp_debug_touch_state(struct tp_dispatch *tp, + struct evdev_device *device) +{ + char buf[1024] = {0}; + struct tp_touch *t; + size_t i = 0; + + tp_for_each_touch(tp, t) { + if (i >= tp->nfingers_down) + break; + sprintf(&buf[strlen(buf)], + "slot %zd: %04d/%04d p%03d %s |", + i++, + t->point.x, + t->point.y, + t->pressure, + tp_touch_active(tp, t) ? "" : "inactive"); + } + evdev_log_debug(device, "touch state: %s\n", buf); +} + static void tp_interface_process(struct evdev_dispatch *dispatch, struct evdev_device *device, @@ -1291,6 +1313,9 @@ tp_interface_process(struct evdev_dispatch *dispatch, break; case EV_SYN: tp_handle_state(tp, time); +#if 0 + tp_debug_touch_state(tp, device); +#endif break; } } From 2b867feab754ff19dc7cdd694ef74d1d63982e62 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 28 Apr 2017 13:44:59 +1000 Subject: [PATCH 6/7] Put a check in to make sure our events have correct timestamps This is for debugging purposes only, we cannot guarantee that event timestamps always go up - at least not across devices. Example: tapping on a touchpad may delay an event until a timeout expires, but that event is then sent with the original touch timestamps (i.e. in the past). If any other device produces events during that timeout period, our timestamps are out-of-order. This isn't really a bug because we are forced to do that, but for bug-fixing it can be useful to detect. Signed-off-by: Peter Hutterer --- src/libinput-private.h | 2 + src/libinput.c | 86 ++++++++++++++++++++++++------------------ 2 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/libinput-private.h b/src/libinput-private.h index a1d50002..1a564f9f 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -136,6 +136,8 @@ struct libinput { int refcount; struct list device_group_list; + + uint64_t last_event_time; }; typedef void (*libinput_seat_destroy_func) (struct libinput_seat *seat); diff --git a/src/libinput.c b/src/libinput.c index 514a6119..472b1c0c 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -25,6 +25,7 @@ #include "config.h" #include +#include #include #include #include @@ -73,6 +74,43 @@ check_event_type(struct libinput *libinput, return rc; } +static inline const char * +event_type_to_str(enum libinput_event_type type) +{ + switch(type) { + CASE_RETURN_STRING(LIBINPUT_EVENT_DEVICE_ADDED); + CASE_RETURN_STRING(LIBINPUT_EVENT_DEVICE_REMOVED); + CASE_RETURN_STRING(LIBINPUT_EVENT_KEYBOARD_KEY); + CASE_RETURN_STRING(LIBINPUT_EVENT_POINTER_MOTION); + CASE_RETURN_STRING(LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE); + CASE_RETURN_STRING(LIBINPUT_EVENT_POINTER_BUTTON); + CASE_RETURN_STRING(LIBINPUT_EVENT_POINTER_AXIS); + CASE_RETURN_STRING(LIBINPUT_EVENT_TOUCH_DOWN); + CASE_RETURN_STRING(LIBINPUT_EVENT_TOUCH_UP); + CASE_RETURN_STRING(LIBINPUT_EVENT_TOUCH_MOTION); + CASE_RETURN_STRING(LIBINPUT_EVENT_TOUCH_CANCEL); + CASE_RETURN_STRING(LIBINPUT_EVENT_TOUCH_FRAME); + CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_AXIS); + CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY); + CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_TIP); + CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_BUTTON); + CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_BUTTON); + CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_RING); + CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_STRIP); + CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN); + CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); + CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_END); + CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_BEGIN); + CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_UPDATE); + CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_END); + CASE_RETURN_STRING(LIBINPUT_EVENT_SWITCH_TOGGLE); + case LIBINPUT_EVENT_NONE: + abort(); + } + + return NULL; +} + struct libinput_source { libinput_source_dispatch_t dispatch; void *user_data; @@ -2026,6 +2064,17 @@ post_device_event(struct libinput_device *device, struct libinput_event *event) { struct libinput_event_listener *listener, *tmp; +#if 0 + struct libinput *libinput = device->seat->libinput; + + if (libinput->last_event_time > time) { + log_bug_libinput(device->seat->libinput, + "out-of-order timestamps for %s time %" PRIu64 "\n", + event_type_to_str(type), + time); + } + libinput->last_event_time = time; +#endif init_event_base(event, device, type); @@ -2660,43 +2709,6 @@ gesture_notify_pinch_end(struct libinput_device *device, finger_count, cancelled, &zero, &zero, scale, 0.0); } -static inline const char * -event_type_to_str(enum libinput_event_type type) -{ - switch(type) { - CASE_RETURN_STRING(LIBINPUT_EVENT_DEVICE_ADDED); - CASE_RETURN_STRING(LIBINPUT_EVENT_DEVICE_REMOVED); - CASE_RETURN_STRING(LIBINPUT_EVENT_KEYBOARD_KEY); - CASE_RETURN_STRING(LIBINPUT_EVENT_POINTER_MOTION); - CASE_RETURN_STRING(LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE); - CASE_RETURN_STRING(LIBINPUT_EVENT_POINTER_BUTTON); - CASE_RETURN_STRING(LIBINPUT_EVENT_POINTER_AXIS); - CASE_RETURN_STRING(LIBINPUT_EVENT_TOUCH_DOWN); - CASE_RETURN_STRING(LIBINPUT_EVENT_TOUCH_UP); - CASE_RETURN_STRING(LIBINPUT_EVENT_TOUCH_MOTION); - CASE_RETURN_STRING(LIBINPUT_EVENT_TOUCH_CANCEL); - CASE_RETURN_STRING(LIBINPUT_EVENT_TOUCH_FRAME); - CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_AXIS); - CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY); - CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_TIP); - CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_BUTTON); - CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_BUTTON); - CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_RING); - CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_STRIP); - CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN); - CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); - CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_END); - CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_BEGIN); - CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_UPDATE); - CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_END); - CASE_RETURN_STRING(LIBINPUT_EVENT_SWITCH_TOGGLE); - case LIBINPUT_EVENT_NONE: - abort(); - } - - return NULL; -} - void switch_notify_toggle(struct libinput_device *device, uint64_t time, From 2d032019b6a46ae0ce837137a4890fc2f6409c7c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 1 May 2017 19:58:13 +1000 Subject: [PATCH 7/7] doc: document the event timestamps Signed-off-by: Peter Hutterer --- doc/Makefile.am | 1 + doc/page-hierarchy.dox | 3 ++- doc/timestamps.dox | 36 ++++++++++++++++++++++++++++++++++++ src/libinput.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 doc/timestamps.dox diff --git a/doc/Makefile.am b/doc/Makefile.am index 5391f225..382f64da 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -27,6 +27,7 @@ header_files = \ $(srcdir)/tablet-support.dox \ $(srcdir)/tapping.dox \ $(srcdir)/test-suite.dox \ + $(srcdir)/timestamps.dox \ $(srcdir)/tools.dox \ $(srcdir)/touchpad-jumping-cursors.dox \ $(srcdir)/touchpad-pressure.dox \ diff --git a/doc/page-hierarchy.dox b/doc/page-hierarchy.dox index 65749feb..1f0b735a 100644 --- a/doc/page-hierarchy.dox +++ b/doc/page-hierarchy.dox @@ -24,10 +24,11 @@ - @subpage tablet-support -@page general General setup +@page general General - @subpage udev_config - @subpage seats +- @subpage timestamps @page misc Users diff --git a/doc/timestamps.dox b/doc/timestamps.dox new file mode 100644 index 00000000..a823c002 --- /dev/null +++ b/doc/timestamps.dox @@ -0,0 +1,36 @@ +/** + +@page timestamps Timestamps + +@section event_timestamps Event timestamps + +Most libinput events provide a timestamp in millisecond and/or microsecond +resolution. These timestamp usually increase monotonically, but libinput +does not guarantee that this always the case. In other words, it is possible +to receive an event with a timestamp earlier than the previous event. + +For example, if a touchpad has @ref tapping enabled, a button event may have a +lower timestamp than an event from a different device. Tapping requires the +use of timeouts to detect multi-finger taps and/or @ref tapndrag. + +Consider the following event sequences from a touchpad and a mouse: + +
+Time      Touchpad      Mouse
+---------------------------------
+t1       finger down
+t2        finger up
+t3                     movement
+t4       tap timeout
+
+ +For this event sequence, the first event to be sent to a caller is in +response to the mouse movement: an event of type @ref +LIBINPUT_EVENT_POINTER_MOTION with the timestamp t3. +Once the timeout expires at t4, libinput generates an event of +@ref LIBINPUT_EVENT_POINTER_BUTTON (press) with a timestamp t1 and an event +@ref LIBINPUT_EVENT_POINTER_BUTTON (release) with a timestamp t2. + +Thus, the caller gets events with timestamps in the order t3, t1, t2, +despite t3 > t2 > t1. +*/ diff --git a/src/libinput.h b/src/libinput.h index 430fe18c..54bbc7f2 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -972,6 +972,9 @@ libinput_event_device_notify_get_base_event(struct libinput_event_device_notify /** * @ingroup event_keyboard * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @return The event time for this event */ uint32_t @@ -980,6 +983,9 @@ libinput_event_keyboard_get_time(struct libinput_event_keyboard *event); /** * @ingroup event_keyboard * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @return The event time for this event in microseconds */ uint64_t @@ -1035,6 +1041,9 @@ libinput_event_keyboard_get_seat_key_count( /** * @ingroup event_pointer * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @return The event time for this event */ uint32_t @@ -1043,6 +1052,9 @@ libinput_event_pointer_get_time(struct libinput_event_pointer *event); /** * @ingroup event_pointer * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @return The event time for this event in microseconds */ uint64_t @@ -1400,6 +1412,9 @@ libinput_event_pointer_get_base_event(struct libinput_event_pointer *event); /** * @ingroup event_touch * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @return The event time for this event */ uint32_t @@ -1408,6 +1423,9 @@ libinput_event_touch_get_time(struct libinput_event_touch *event); /** * @ingroup event_touch * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @return The event time for this event in microseconds */ uint64_t @@ -1564,6 +1582,9 @@ libinput_event_touch_get_base_event(struct libinput_event_touch *event); /** * @ingroup event_gesture * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @return The event time for this event */ uint32_t @@ -1572,6 +1593,9 @@ libinput_event_gesture_get_time(struct libinput_event_gesture *event); /** * @ingroup event_gesture * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @return The event time for this event in microseconds */ uint64_t @@ -2300,6 +2324,9 @@ libinput_event_tablet_tool_get_seat_button_count(struct libinput_event_tablet_to /** * @ingroup event_tablet * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @param event The libinput tablet tool event * @return The event time for this event */ @@ -2309,6 +2336,9 @@ libinput_event_tablet_tool_get_time(struct libinput_event_tablet_tool *event); /** * @ingroup event_tablet * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @param event The libinput tablet tool event * @return The event time for this event in microseconds */ @@ -2735,6 +2765,9 @@ libinput_event_tablet_pad_get_mode_group(struct libinput_event_tablet_pad *event /** * @ingroup event_tablet * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @param event The libinput tablet pad event * @return The event time for this event */ @@ -2744,6 +2777,9 @@ libinput_event_tablet_pad_get_time(struct libinput_event_tablet_pad *event); /** * @ingroup event_tablet_pad * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @param event The libinput tablet pad event * @return The event time for this event in microseconds */ @@ -2799,6 +2835,9 @@ libinput_event_switch_get_base_event(struct libinput_event_switch *event); /** * @ingroup event_switch * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @param event The libinput switch event * @return The event time for this event */ @@ -2808,6 +2847,9 @@ libinput_event_switch_get_time(struct libinput_event_switch *event); /** * @ingroup event_switch * + * @note Timestamps may not always increase. See @ref event_timestamps for + * details. + * * @param event The libinput switch event * @return The event time for this event in microseconds */