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.
This commit is contained in:
Peter Hutterer 2022-03-04 12:03:28 +10:00
parent 8474d03e03
commit 541dcb415d
7 changed files with 53 additions and 18 deletions

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}