From 8d6d23e84d3e7063f8a3042b2c8293de047cbc3b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Dec 2025 16:30:33 +1000 Subject: [PATCH] eis: end any ongoing touch in response to touch_end Even if the client is no longer emulating (and we're not queuing an event) our local touch must be ended. Otherwise our touch will live on forever, despite the client thinking it has ended properly. This can't be triggered with libei because we can't send touch events while not emulating and the only way to get into this state is pausing the device - which already resets the state. But let's add a test case anyway, in the hope that one day it picks up a bug. --- src/libeis-device.c | 12 ++++-- test/test-ei-device.c | 90 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/src/libeis-device.c b/src/libeis-device.c index 4510ef1..a2345fb 100644 --- a/src/libeis-device.c +++ b/src/libeis-device.c @@ -749,8 +749,10 @@ client_msg_touch_up(struct eis_touchscreen *touchscreen, uint32_t touchid) "Touch up event for non-touch device"); } - if (device->state == EIS_DEVICE_STATE_EMULATING) { - if (release_touch(device, touchid)) + /* End the touch locally even if we're not emulating, but + * silently ignore touch end/cancel for non-existing touches */ + if (release_touch(device, touchid)) { + if (device->state == EIS_DEVICE_STATE_EMULATING) eis_queue_touch_up_event(device, touchid); return NULL; } @@ -776,8 +778,10 @@ client_msg_touch_cancel(struct eis_touchscreen *touchscreen, uint32_t touchid) "Touch cancel event for touchscreen version v1"); } - if (device->state == EIS_DEVICE_STATE_EMULATING) { - if (release_touch(device, touchid)) + /* End the touch locally even if we're not emulating, but + * silently ignore touch end/cancel for non-existing touches */ + if (release_touch(device, touchid)) { + if (device->state == EIS_DEVICE_STATE_EMULATING) eis_queue_touch_cancel_event(device, touchid); return NULL; } diff --git a/test/test-ei-device.c b/test/test-ei-device.c index df24604..782b8e4 100644 --- a/test/test-ei-device.c +++ b/test/test-ei-device.c @@ -1523,6 +1523,96 @@ MUNIT_TEST(test_ei_device_multitouch) return MUNIT_OK; } +MUNIT_TEST(test_ei_device_touch_up_after_paused) +{ + _unref_(peck) *peck = peck_new(); + _unref_(ei_device) *device = NULL; + _unref_(eis_device) *eis_device = NULL; + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_TOUCH); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES); + peck_dispatch_until_stable(peck); + + _unref_(ei_touch) *t1 = NULL; + _unref_(ei_touch) *t2 = NULL; + + with_client(peck) { + device = ei_device_ref(peck_ei_get_default_touch(peck)); + t1 = ei_device_touch_new(device); + t2 = ei_device_touch_new(device); + ei_touch_down(t1, 1, 2); + ei_touch_down(t2, 3, 4); + ei_device_frame(device, peck_ei_now(peck)); + } + + peck_dispatch_until_stable(peck); + + with_server(peck) { + _unref_(eis_event) *down1 = peck_eis_touch_down(eis, 1, 2); + _unref_(eis_event) *down2 = peck_eis_touch_down(eis, 3, 4); + + peck_assert_no_eis_events(eis); /* drain the frame */ + + eis_device = eis_device_ref(eis_event_get_device(down1)); + eis_device_pause(eis_device); + } + + /* No ei dispatch here */ + peck_dispatch_eis(peck); + + with_client(peck) { + /* These events will arrive when the device is paused and + * are discarded by EIS */ + ei_touch_up(t1); + ei_touch_up(t2); + ei_device_frame(device, peck_ei_now(peck)); + ei_touch_unref(t1); + ei_touch_unref(t2); + } + + peck_dispatch_until_stable(peck); + + with_client(peck) { + _unref_(ei_event) *pause = peck_ei_next_event(ei, EI_EVENT_DEVICE_PAUSED); + } + + with_server(peck) { + /* touch up and empty frame were discarded */ + peck_assert_no_eis_events(eis); + eis_device_resume(eis_device); + } + + peck_dispatch_until_stable(peck); + + with_client(peck) { + ei_device_start_emulating(device, 123); + + /* The C API doesn't allow us to set a touch id + * so we can't really test for the correct behavior. + * All we can do is exercise most of the code by creating + * new touches and hope. + */ + t1 = ei_device_touch_new(device); + t2 = ei_device_touch_new(device); + ei_touch_down(t1, 1, 2); + ei_touch_down(t2, 3, 4); + ei_device_frame(device, peck_ei_now(peck)); + } + + peck_dispatch_until_stable(peck); + + with_server(peck) { + _unref_(eis_event) *down1 = peck_eis_touch_down(eis, 1, 2); + _unref_(eis_event) *down2 = peck_eis_touch_down(eis, 3, 4); + peck_assert_no_eis_events(eis); /* drain the frame */ + } + + peck_dispatch_until_stable(peck); + + return MUNIT_OK; +} + #if HAVE_MEMFD_CREATE MUNIT_TEST(test_ei_keymap_invalid) {