From 55594cd09c99edf8cb7d791d509f1e465b73fefd Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 23 Aug 2021 10:50:23 +1000 Subject: [PATCH] Add a frame event to logically group events together Already present in e.g. libinput and wayland, this event allows us to group several events together to denote them as a logical group. Required for multi-touch but as we've learned with Wayland it's also required to group other events (scroll events in the case of Wayland). Signed-off-by: Peter Hutterer --- proto/ei.proto | 5 +++++ src/libei-device.c | 9 +++++++++ src/libei-private.h | 3 +++ src/libei-proto.c | 12 ++++++++++++ src/libei-proto.h | 1 + src/libei.c | 14 ++++++++++++++ src/libei.h | 8 ++++++++ src/libeis-client.c | 20 ++++++++++++++++++++ src/libeis-device.c | 10 ++++++++++ src/libeis-event.c | 1 + src/libeis-private.h | 5 +++++ src/libeis-proto.c | 3 +++ src/libeis-proto.h | 1 + src/libeis.c | 9 +++++++++ src/libeis.h | 10 ++++++++++ test/eierpecken.c | 24 ++++++++++++++++++++++++ test/eierpecken.h | 5 +++++ test/test-ei-device.c | 39 ++++++++++++++++++++++++++++++++++++++- test/test-ei.c | 1 + tools/ei-demo-client.c | 9 ++++++++- tools/eis-demo-server.c | 3 +++ 21 files changed, 190 insertions(+), 2 deletions(-) diff --git a/proto/ei.proto b/proto/ei.proto index 84e6960..b7c1976 100644 --- a/proto/ei.proto +++ b/proto/ei.proto @@ -141,6 +141,10 @@ message TouchUp { uint32 touchid = 2; } +message Frame { + uint32 deviceid = 1; +} + message ClientMessage { oneof msg { Connect connect = 1; @@ -159,6 +163,7 @@ message ClientMessage { TouchUp touch_up = 14; ConfigureName configure_name = 15; ConfigureCapabilities configure_caps = 16; + Frame frame = 17; } } diff --git a/src/libei-device.c b/src/libei-device.c index af36338..b5978cc 100644 --- a/src/libei-device.c +++ b/src/libei-device.c @@ -528,3 +528,12 @@ ei_touch_up(struct ei_touch *touch) touch->state = TOUCH_IS_UP; ei_send_touch_up(touch->device, touch->tracking_id); } + +_public_ void +ei_device_frame(struct ei_device *device) +{ + if (device->state != EI_DEVICE_STATE_RESUMED) + return; + + ei_send_frame(device); +} diff --git a/src/libei-private.h b/src/libei-private.h index aff44f5..8a6594e 100644 --- a/src/libei-private.h +++ b/src/libei-private.h @@ -207,6 +207,9 @@ ei_send_seat_unbind(struct ei_seat *seat); int ei_send_close_device(struct ei_device *device); +int +ei_send_frame(struct ei_device *device); + void ei_queue_device_removed_event(struct ei_device *device); diff --git a/src/libei-proto.c b/src/libei-proto.c index b74437b..f69747f 100644 --- a/src/libei-proto.c +++ b/src/libei-proto.c @@ -162,6 +162,7 @@ log_wire_message(struct ei *ei, const ClientMessage *msg, int error) MSG_STRING_CASE(TOUCH_UP); MSG_STRING_CASE(CONFIGURE_NAME); MSG_STRING_CASE(CONFIGURE_CAPS); + MSG_STRING_CASE(FRAME); } if (message == NULL) assert(!"Unimplemented message type"); @@ -374,6 +375,16 @@ ei_proto_send_touch_up(struct ei_device *device, uint32_t tid) return ei_proto_send_msg(ei_device_get_context(device), &msg); } +static int +ei_proto_send_frame(struct ei_device *device) +{ + prepare_msg(FRAME, Frame, frame); + + frame.deviceid = device->id; + + return ei_proto_send_msg(ei_device_get_context(device), &msg); +} + static const struct ei_proto_requests requests = { .connect = ei_proto_send_connect, .disconnect = ei_proto_send_disconnect, @@ -389,6 +400,7 @@ static const struct ei_proto_requests requests = { .touch_down = ei_proto_send_touch_down, .touch_motion = ei_proto_send_touch_motion, .touch_up = ei_proto_send_touch_up, + .frame = ei_proto_send_frame, }; const struct ei_proto_requests * diff --git a/src/libei-proto.h b/src/libei-proto.h index 3a53141..22c745a 100644 --- a/src/libei-proto.h +++ b/src/libei-proto.h @@ -74,6 +74,7 @@ struct ei_proto_requests { int (*touch_motion)(struct ei_device *device, uint32_t tid, double x, double y); int (*touch_up)(struct ei_device *device, uint32_t tid); + int (*frame)(struct ei_device *device); }; int diff --git a/src/libei.c b/src/libei.c index aa52581..f340421 100644 --- a/src/libei.c +++ b/src/libei.c @@ -549,6 +549,20 @@ ei_send_seat_unbind(struct ei_seat *seat) return rc; } +int +ei_send_frame(struct ei_device *device) +{ + struct ei *ei = ei_device_get_context(device); + + if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) + return 0; + + int rc = ei->requests->frame(device); + if (rc) + ei_disconnect(ei); + return rc; +} + int ei_send_pointer_rel(struct ei_device *device, double x, double y) { diff --git a/src/libei.h b/src/libei.h index cacdc11..8d80c03 100644 --- a/src/libei.h +++ b/src/libei.h @@ -911,6 +911,14 @@ ei_keymap_get_context(struct ei_keymap *keymap); struct ei * ei_device_get_context(struct ei_device *device); +/** + * Generate a frame event to group the current set of events + * into a logical hardware event. This function **must** be called after any + * other event has been generated. + */ +void +ei_device_frame(struct ei_device *device); + /** * Generate a relative motion event on a device with * the @ref EI_DEVICE_CAP_POINTER capability. diff --git a/src/libeis-client.c b/src/libeis-client.c index 626a993..38efd54 100644 --- a/src/libeis-client.c +++ b/src/libeis-client.c @@ -249,6 +249,25 @@ client_msg_unbind_seat(struct eis_client *client, uint32_t seatid) return -EINVAL; } +static int +client_msg_frame(struct eis_client *client, uint32_t deviceid) +{ + struct eis_seat *seat; + + list_for_each(seat, &client->seats, link) { + struct eis_device *device; + + list_for_each(device, &seat->devices, link) { + if (device->id == deviceid) { + eis_device_frame(device); + break; + } + } + } + return 0; +} + + static int client_msg_pointer_rel(struct eis_client *client, uint32_t deviceid, double x, double y) @@ -496,6 +515,7 @@ static const struct eis_proto_interface intf_state_connected = { .touch_up = client_msg_touch_up, .configure_name = client_msg_configure_name, .configure_capabilities = client_msg_configure_capabilities, + .frame = client_msg_frame, }; static const struct eis_proto_interface *interfaces[] = { diff --git a/src/libeis-device.c b/src/libeis-device.c index 03f8e45..6734926 100644 --- a/src/libeis-device.c +++ b/src/libeis-device.c @@ -260,6 +260,16 @@ eis_device_has_capability(struct eis_device *device, return false; } +int +eis_device_frame(struct eis_device *device) +{ + if (device->state != EIS_DEVICE_STATE_RESUMED) + return -EINVAL; + + eis_queue_frame_event(device); + + return 0; +} int eis_device_pointer_rel(struct eis_device *device, diff --git a/src/libeis-event.c b/src/libeis-event.c index f78a063..012cab1 100644 --- a/src/libeis-event.c +++ b/src/libeis-event.c @@ -51,6 +51,7 @@ eis_event_destroy(struct eis_event *event) case EIS_EVENT_TOUCH_DOWN: case EIS_EVENT_TOUCH_MOTION: case EIS_EVENT_TOUCH_UP: + case EIS_EVENT_FRAME: handled = true; break; } diff --git a/src/libeis-private.h b/src/libeis-private.h index 4a16e62..df3efda 100644 --- a/src/libeis-private.h +++ b/src/libeis-private.h @@ -238,6 +238,8 @@ void eis_device_set_client_keymap(struct eis_device *device, enum eis_keymap_type type, int keymap_fd, size_t size); +int +eis_device_frame(struct eis_device *device); int eis_device_pointer_rel(struct eis_device *device, @@ -307,6 +309,9 @@ eis_queue_seat_unbind_event(struct eis_seat *seat); void eis_queue_device_closed_event(struct eis_device *device); +void +eis_queue_frame_event(struct eis_device *device); + void eis_queue_pointer_rel_event(struct eis_device *device, double x, double y); diff --git a/src/libeis-proto.c b/src/libeis-proto.c index 2e23fdb..c337e5f 100644 --- a/src/libeis-proto.c +++ b/src/libeis-proto.c @@ -377,6 +377,9 @@ eis_proto_handle_message(struct eis_client *client, proto->configure_caps->allowed_capabilities, proto->configure_caps->denied_capabilities); break; + case CLIENT_MESSAGE__MSG_FRAME: + rc = call(frame, client, proto->frame->deviceid); + break; default: rc = -EBADMSG; break; diff --git a/src/libeis-proto.h b/src/libeis-proto.h index f4be539..9cf2a5f 100644 --- a/src/libeis-proto.h +++ b/src/libeis-proto.h @@ -55,6 +55,7 @@ struct eis_proto_interface { int (*configure_capabilities)(struct eis_client *client, bool policy_is_allow, uint32_t allow, uint32_t deny); + int (*frame) (struct eis_client *client, uint32_t deviceid); }; struct eis_proto_requests { diff --git a/src/libeis.c b/src/libeis.c index bf67577..17d0bc6 100644 --- a/src/libeis.c +++ b/src/libeis.c @@ -117,6 +117,7 @@ eis_event_type_to_string(enum eis_event_type type) CASE_RETURN_STRING(EIS_EVENT_TOUCH_DOWN); CASE_RETURN_STRING(EIS_EVENT_TOUCH_UP); CASE_RETURN_STRING(EIS_EVENT_TOUCH_MOTION); + CASE_RETURN_STRING(EIS_EVENT_FRAME); } assert(!"Unhandled event type"); @@ -174,6 +175,14 @@ eis_queue_device_closed_event(struct eis_device *device) eis_queue_event(e); } +void +eis_queue_frame_event(struct eis_device *device) +{ + struct eis_event *e = eis_event_new_for_device(device); + e->type = EIS_EVENT_FRAME; + eis_queue_event(e); +} + void eis_queue_pointer_rel_event(struct eis_device *device, double dx, double dy) diff --git a/src/libeis.h b/src/libeis.h index 9a65e52..9a3de6e 100644 --- a/src/libeis.h +++ b/src/libeis.h @@ -117,6 +117,16 @@ enum eis_event_type { */ EIS_EVENT_DEVICE_CLOSED, + /** + * "Hardware" frame event. This event **must** be sent by the client + * and notifies the server that the previous set of events belong to + * the same logical hardware event. + * + * This event is most commonly used to implement multitouch (multiple + * touches may update within the same hardware scanout cycle). + */ + EIS_EVENT_FRAME = 100, + EIS_EVENT_POINTER_MOTION = 300, EIS_EVENT_POINTER_MOTION_ABSOLUTE, EIS_EVENT_POINTER_BUTTON, diff --git a/test/eierpecken.c b/test/eierpecken.c index db5f104..1478e9a 100644 --- a/test/eierpecken.c +++ b/test/eierpecken.c @@ -315,6 +315,7 @@ peck_new(void) munit_assert_int(rc, ==, 0); peck->eis = eis; peck->eis_behavior = PECK_EIS_BEHAVIOR_NONE; + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_FRAME); peck->logger = logger_new("peck", peck); logger_set_handler(peck->logger, peck_log_handler); @@ -354,6 +355,7 @@ peck_enable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior) case PECK_EIS_BEHAVIOR_HANDLE_BIND_SEAT: case PECK_EIS_BEHAVIOR_HANDLE_UNBIND_SEAT: case PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE: + case PECK_EIS_BEHAVIOR_HANDLE_FRAME: case PECK_EIS_BEHAVIOR_ADD_POINTER: case PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE: case PECK_EIS_BEHAVIOR_ADD_KEYBOARD: @@ -620,6 +622,10 @@ _peck_dispatch_eis(struct peck *peck, int lineno) else process_event = tristate_no; break; + case EIS_EVENT_FRAME: + if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_FRAME)) + process_event = tristate_yes; + break; default: break; } @@ -855,11 +861,20 @@ peck_assert_no_ei_events(struct ei *ei) void peck_assert_no_eis_events(struct eis *eis) { + struct peck *peck = eis_get_user_data(eis); + eis_dispatch(eis); while (true) { _unref_(eis_event) *e = eis_get_event(eis); if (!e) return; + + if (peck && flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_FRAME) && + eis_event_get_type(e) == EIS_EVENT_FRAME) { + log_debug(peck, "Skipping over frame event\n"); + continue; + } + munit_errorf("Expected empty event queue, have: %s\n", peck_eis_event_name(e)); } @@ -893,6 +908,14 @@ _peck_eis_next_event(struct eis *eis, enum eis_event_type type, int lineno) struct eis_event *event = eis_get_event(eis); struct peck *peck = eis_get_user_data(eis); + if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_FRAME)) { + while (eis_event_get_type(event) == EIS_EVENT_FRAME) { + eis_event_unref(event); + log_debug(peck, "Skipping over frame event\n"); + event = eis_get_event(eis); + } + } + if (!event) munit_errorf("Expected EIS event type %s, got none, line %d\n", peck_eis_event_type_name(type), @@ -976,6 +999,7 @@ peck_eis_event_type_name(enum eis_event_type type) CASE_STRING(TOUCH_DOWN); CASE_STRING(TOUCH_UP); CASE_STRING(TOUCH_MOTION); + CASE_STRING(FRAME); } #undef CASE_STRING assert(!"Unhandled EIS event type"); diff --git a/test/eierpecken.h b/test/eierpecken.h index 62a7a18..ad1e0b4 100644 --- a/test/eierpecken.h +++ b/test/eierpecken.h @@ -80,6 +80,11 @@ enum peck_eis_behavior { */ PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE, + /** + * Handle frame events. This behavior is enabled by default. + */ + PECK_EIS_BEHAVIOR_HANDLE_FRAME, + /** * Create default devices */ diff --git a/test/test-ei-device.c b/test/test-ei-device.c index 138d5c3..ad5ca51 100644 --- a/test/test-ei-device.c +++ b/test/test-ei-device.c @@ -237,8 +237,11 @@ MUNIT_TEST(test_ei_device_pointer_rel) with_client(peck) { struct ei_device *device = peck_ei_get_default_pointer(peck); ei_device_pointer_motion(device, 1, 2); + ei_device_frame(device); ei_device_pointer_motion(device, 0.3, 1.4); + ei_device_frame(device); ei_device_pointer_motion(device, 100, 200); + ei_device_frame(device); } peck_dispatch_until_stable(peck); @@ -419,8 +422,10 @@ MUNIT_TEST(test_ei_device_pointer_abs) maxx = ei_region_get_x(r) + ei_region_get_width(r); maxy = ei_region_get_y(r) + ei_region_get_height(r); - for (int i = 0; i < 10; i++) + for (int i = 0; i < 10; i++) { ei_device_pointer_motion_absolute(device, 1 * i , 2 + i); + ei_device_frame(device); + } } peck_dispatch_until_stable(peck); @@ -437,7 +442,9 @@ MUNIT_TEST(test_ei_device_pointer_abs) with_client(peck) { /* outside of pointer range, expect to be discarded */ ei_device_pointer_motion_absolute(device, maxx + 1, maxy/2); + ei_device_frame(device); ei_device_pointer_motion_absolute(device, maxx/2 , maxy + 1); + ei_device_frame(device); } peck_dispatch_until_stable(peck); @@ -487,7 +494,9 @@ MUNIT_TEST(test_ei_device_touch) _unref_(ei_touch) *t = ei_device_touch_new(device); ei_touch_down(t, 1, 2); + ei_device_frame(device); ei_touch_motion(t, 200, 500); + ei_device_frame(device); ei_touch_up(t); } @@ -508,13 +517,19 @@ MUNIT_TEST(test_ei_device_touch) _unref_(ei_touch) *t = ei_device_touch_new(device); /* outside clip range, expect touch to be dropped */ ei_touch_down(t, maxx + 1, maxy/2); + ei_device_frame(device); ei_touch_motion(t, maxx + 1, maxy/3); + ei_device_frame(device); ei_touch_up(t); + ei_device_frame(device); /* outside clip range, expect touch to be dropped */ ei_touch_down(t, maxx/2, maxy + 1); + ei_device_frame(device); ei_touch_motion(t, maxx/3, maxy + 1); + ei_device_frame(device); ei_touch_up(t); + ei_device_frame(device); } peck_dispatch_until_stable(peck); @@ -526,9 +541,12 @@ MUNIT_TEST(test_ei_device_touch) with_client(peck) { _unref_(ei_touch) *t = ei_device_touch_new(device); ei_touch_down(t, 100, 200); + ei_device_frame(device); /* outside allowed range, generates a touch up */ ei_touch_motion(t, maxx + 1, 200); + ei_device_frame(device); ei_touch_up(t); + ei_device_frame(device); } peck_dispatch_until_stable(peck); @@ -541,7 +559,9 @@ MUNIT_TEST(test_ei_device_touch) with_client(peck) { _unref_(ei_touch) *t = ei_device_touch_new(device); ei_touch_down(t, 100, 100); + ei_device_frame(device); /* client forgets to touch up, touch_unref takes care of it */ + /* FIXME: this doesn't work with frames */ } peck_dispatch_until_stable(peck); @@ -558,11 +578,14 @@ MUNIT_TEST(test_ei_device_touch) /* touch never set down */ _unref_(ei_touch) *t2 = ei_device_touch_new(device); ei_touch_up(t2); + ei_device_frame(device); /* touch never set down */ _unref_(ei_touch) *t3 = ei_device_touch_new(device); ei_touch_motion(t3, 100, 200); + ei_device_frame(device); ei_touch_up(t3); + ei_device_frame(device); } peck_dispatch_until_stable(peck); @@ -574,10 +597,15 @@ MUNIT_TEST(test_ei_device_touch) /* touch re-used */ _unref_(ei_touch) *t = ei_device_touch_new(device); ei_touch_down(t, 100, 200); + ei_device_frame(device); ei_touch_up(t); + ei_device_frame(device); ei_touch_down(t, 200, 300); + ei_device_frame(device); ei_touch_motion(t, 300, 400); + ei_device_frame(device); ei_touch_up(t); + ei_device_frame(device); } peck_dispatch_until_stable(peck); @@ -592,10 +620,15 @@ MUNIT_TEST(test_ei_device_touch) /* double-down, double-up */ _unref_(ei_touch) *t = ei_device_touch_new(device); ei_touch_down(t, 100, 200); + ei_device_frame(device); ei_touch_down(t, 200, 300); /* ignored */ + ei_device_frame(device); ei_touch_motion(t, 300, 400); + ei_device_frame(device); ei_touch_up(t); + ei_device_frame(device); ei_touch_up(t); /* ignored */ + ei_device_frame(device); } peck_dispatch_until_stable(peck); @@ -627,10 +660,14 @@ MUNIT_TEST(test_ei_device_multitouch) _unref_(ei_touch) *t1 = ei_device_touch_new(device); _unref_(ei_touch) *t2 = ei_device_touch_new(device); ei_touch_down(t1, 1, 2); + ei_device_frame(device); ei_touch_motion(t1, 2, 3); + ei_device_frame(device); ei_touch_down(t2, 3, 4); + ei_device_frame(device); ei_touch_motion(t2, 4, 5); + ei_device_frame(device); ei_touch_up(t2); ei_touch_up(t1); diff --git a/test/test-ei.c b/test/test-ei.c index 54fb92f..5071b95 100644 --- a/test/test-ei.c +++ b/test/test-ei.c @@ -471,6 +471,7 @@ MUNIT_TEST(test_xdotool_rel_motion) with_client(peck) { struct ei_device *device = peck_ei_get_default_pointer(peck); ei_device_pointer_motion(device, -1, 10); + ei_device_frame(device); ei_device_close(device); ei_unref(ei); peck_drop_ei(peck); diff --git a/tools/ei-demo-client.c b/tools/ei-demo-client.c index f162c1a..9a5695b 100644 --- a/tools/ei-demo-client.c +++ b/tools/ei-demo-client.c @@ -362,17 +362,23 @@ int main(int argc, char **argv) /* BTN_LEFT */ colorprint("sending button event\n"); ei_device_pointer_button(ptr, BTN_LEFT, true); + ei_device_frame(ptr); ei_device_pointer_button(ptr, BTN_LEFT, false); - colorprint("sending scroll event\n"); + ei_device_frame(ptr); + colorprint("sending scroll events\n"); ei_device_pointer_scroll(ptr, 1, 1); + ei_device_frame(ptr); ei_device_pointer_scroll_discrete(ptr, 1, 1); + ei_device_frame(ptr); } if (have_kbd) { static int key = 0; colorprint("sending key event\n"); ei_device_keyboard_key(kbd, KEY_Q + key, true); /* KEY_Q */ + ei_device_frame(ptr); ei_device_keyboard_key(kbd, KEY_Q + key, false); /* KEY_Q */ + ei_device_frame(ptr); key = (key + 1) % 6; } @@ -380,6 +386,7 @@ int main(int argc, char **argv) static int x, y; colorprint("sending abs event\n"); ei_device_pointer_motion_absolute(abs, 150 + ++x, 150 - ++y); + ei_device_frame(ptr); } } diff --git a/tools/eis-demo-server.c b/tools/eis-demo-server.c index 6e26ce5..b6a6d86 100644 --- a/tools/eis-demo-server.c +++ b/tools/eis-demo-server.c @@ -278,6 +278,9 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server, eis_event_keyboard_get_key_is_press(e)); } break; + case EIS_EVENT_FRAME: + /* nothing to do, we're not fancy enough to accumulate events properly */ + break; default: abort(); }