From 541dcb415daa7c42f53ccea7af3af33b812194f4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 4 Mar 2022 12:03:28 +1000 Subject: [PATCH] ei: require the client to confirm capabilities rather than drop them Our API requires a client to know which capability to pass into the drop_capabilities function. This doesn't work for capabilities newer than the client's version so they do not get disabled. The client will thus receive devices it didn't ask for and doesn't know how to handle. Let's invert the requirement and require the caller to confirm the capabilities it wants - all others are dropped. This is an API break but also requires updates of all clients, the previous simple case of just calling ei_seat_bind() will now result in zero capabilities. --- src/libei-private.h | 2 +- src/libei-seat.c | 11 +++++------ src/libei.h | 30 +++++++++++++++++++++--------- test/eierpecken.c | 4 ++++ test/test-ei-seat.c | 12 ++++++++++++ test/test-eis.c | 8 ++++++-- tools/ei-demo-client.c | 4 ++++ 7 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/libei-private.h b/src/libei-private.h index 573784b..6f399c5 100644 --- a/src/libei-private.h +++ b/src/libei-private.h @@ -87,7 +87,7 @@ struct ei_seat { struct list devices_removed; /* removed from seat but client still has a ref */ uint32_t id; uint32_t capabilities; - uint32_t capabilities_mask; + uint32_t capabilities_confirmed; char *name; }; diff --git a/src/libei-seat.c b/src/libei-seat.c index 499b77e..d65dda3 100644 --- a/src/libei-seat.c +++ b/src/libei-seat.c @@ -70,7 +70,7 @@ ei_seat_new(struct ei *ei, uint32_t id, const char *name, uint32_t capabilities) seat->name = xstrdup(name); seat->id = id; seat->capabilities = capabilities; - seat->capabilities_mask = ~0; /* Masked out by client */ + seat->capabilities_confirmed = 0; list_init(&seat->devices); list_init(&seat->devices_removed); @@ -134,21 +134,20 @@ ei_seat_has_capability(struct ei_seat *seat, case EI_DEVICE_CAP_POINTER_ABSOLUTE: case EI_DEVICE_CAP_KEYBOARD: case EI_DEVICE_CAP_TOUCH: - return flag_is_set(seat->capabilities_mask, cap); + return flag_is_set(seat->capabilities, cap); } return false; } _public_ void -ei_seat_drop_capability(struct ei_seat *seat, - enum ei_device_capability cap) +ei_seat_confirm_capability(struct ei_seat *seat, enum ei_device_capability cap) { if (seat->state != EI_SEAT_STATE_NEW) { log_bug_client(ei_seat_get_context(seat), "Seat is already bound\n"); return; } - flag_clear(seat->capabilities_mask, cap); + flag_set(seat->capabilities_confirmed, cap); } _public_ void @@ -159,7 +158,7 @@ ei_seat_bind(struct ei_seat *seat) return; } - ei_send_seat_bind(seat, seat->capabilities & seat->capabilities_mask); + ei_send_seat_bind(seat, seat->capabilities & seat->capabilities_confirmed); seat->state = EI_SEAT_STATE_BOUND; } diff --git a/src/libei.h b/src/libei.h index 5b25402..c9ee12a 100644 --- a/src/libei.h +++ b/src/libei.h @@ -134,7 +134,7 @@ struct ei_region; * device with capabilities unsupported by the client. * * Capabilities are initialized by the EIS implementation but the client may - * further reduce the requested capabilities, see ei_seat_drop_capability(). + * further reduce the requested capabilities, see ei_seat_confirm_capability(). * For example, a client may bind to a seat with the pointer and keyboard * capability but only the former is permitted by the EIS implementation. * Keyboard events sent through such a device will be treated as client bug @@ -646,28 +646,40 @@ ei_event_unref(struct ei_event *event); const char * ei_seat_get_name(struct ei_seat *seat); +/** + * Return true if the capabilitiy is available on this seat or false + * otherwise. The return value of this function is not affected by + * ei_seat_confirm_capability(). + */ bool ei_seat_has_capability(struct ei_seat *seat, enum ei_device_capability cap); /** - * Remove a capability from this seat before calling ei_seat_bind(). - * Devices with only the dropped capability will never be added to this - * client, devices with multiple capabilities will not show the dropped - * capabilities. + * Confirm a capability from this seat before calling ei_seat_bind(). + * + * A client must call this function for every capability on the seat it wants + * to receive devices for. Only devices with one or more supported + * capabilities will be added to this client. + * + * Calling this function for a capability that ei_seat_has_capability() + * returns false for is a noop and permitted. * * This function has no effect if called after ei_seat_bind(). */ void -ei_seat_drop_capability(struct ei_seat *seat, - enum ei_device_capability cap); +ei_seat_confirm_capability(struct ei_seat *seat, + enum ei_device_capability cap); /** - * Bind this client to the given seat for the seat's capabilities. + * Bind this client to the given seat for the confirmed seat's capabilities. * This function can only be called once per seat. Once bound, the server will - * create devices for the seat's capabillities and send the respective @ref + * create devices for the seat's capabilities and send the respective @ref * EI_EVENT_DEVICE_ADDED events. * + * A client must call ei_seat_confirm_capability() before calling + * ei_seat_bind(). + * * Devices may be added and removed at any time. */ void diff --git a/test/eierpecken.c b/test/eierpecken.c index cc90214..fc81903 100644 --- a/test/eierpecken.c +++ b/test/eierpecken.c @@ -814,6 +814,10 @@ _peck_dispatch_ei(struct peck *peck, int lineno) munit_assert_ptr_null(peck->ei_seat); peck->ei_seat = ei_seat_ref(seat); log_debug(peck, "default seat: %s\n", ei_seat_get_name(peck->ei_seat)); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_POINTER); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_POINTER_ABSOLUTE); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_KEYBOARD); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_TOUCH); ei_seat_bind(seat); break; } diff --git a/test/test-ei-seat.c b/test/test-ei-seat.c index 8061c1b..8b09483 100644 --- a/test/test-ei-seat.c +++ b/test/test-ei-seat.c @@ -45,6 +45,10 @@ MUNIT_TEST(test_ei_seat_bind_unbind) with_client(peck) { _unref_(ei_event) *event = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); seat = ei_seat_ref(ei_event_get_seat(event)); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_POINTER); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_POINTER_ABSOLUTE); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_KEYBOARD); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_TOUCH); ei_seat_bind(seat); } @@ -101,6 +105,10 @@ MUNIT_TEST(test_ei_seat_bind_unbind_noref) with_client(peck) { _unref_(ei_event) *event = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); seat = ei_seat_ref(ei_event_get_seat(event)); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_POINTER); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_POINTER_ABSOLUTE); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_KEYBOARD); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_TOUCH); ei_seat_bind(seat); } @@ -149,6 +157,10 @@ MUNIT_TEST(test_ei_seat_bind_unbind_immediately) with_client(peck) { _unref_(ei_event) *event = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); seat = ei_event_get_seat(event); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_POINTER); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_POINTER_ABSOLUTE); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_KEYBOARD); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_TOUCH); ei_seat_bind(seat); ei_seat_unbind(seat); } diff --git a/test/test-eis.c b/test/test-eis.c index deaa463..6e02038 100644 --- a/test/test-eis.c +++ b/test/test-eis.c @@ -120,8 +120,12 @@ MUNIT_TEST(eistest_cliend_bind_some_caps) _unref_(ei_event) *event = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); struct ei_seat *seat = ei_event_get_seat(event); - ei_seat_drop_capability(seat, EI_DEVICE_CAP_KEYBOARD); - ei_seat_drop_capability(seat, EI_DEVICE_CAP_TOUCH); + munit_assert_true(ei_seat_has_capability(seat, EI_DEVICE_CAP_KEYBOARD)); + munit_assert_true(ei_seat_has_capability(seat, EI_DEVICE_CAP_POINTER)); + munit_assert_true(ei_seat_has_capability(seat, EI_DEVICE_CAP_POINTER_ABSOLUTE)); + munit_assert_true(ei_seat_has_capability(seat, EI_DEVICE_CAP_TOUCH)); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_POINTER); + ei_seat_confirm_capability(seat, EI_DEVICE_CAP_POINTER_ABSOLUTE); ei_seat_bind(seat); } diff --git a/tools/ei-demo-client.c b/tools/ei-demo-client.c index e9ab43b..d1d6e72 100644 --- a/tools/ei-demo-client.c +++ b/tools/ei-demo-client.c @@ -312,6 +312,10 @@ int main(int argc, char **argv) } default_seat = ei_seat_ref(ei_event_get_seat(e)); colorprint("seat added: %s\n", ei_seat_get_name(default_seat)); + ei_seat_confirm_capability(default_seat, EI_DEVICE_CAP_POINTER); + ei_seat_confirm_capability(default_seat, EI_DEVICE_CAP_POINTER_ABSOLUTE); + ei_seat_confirm_capability(default_seat, EI_DEVICE_CAP_KEYBOARD); + ei_seat_confirm_capability(default_seat, EI_DEVICE_CAP_TOUCH); ei_seat_bind(default_seat); break; }