diff --git a/meson.build b/meson.build index b1b335f..e224c24 100644 --- a/meson.build +++ b/meson.build @@ -46,6 +46,7 @@ src_libei = [ 'src/libei.c', 'src/libei-device.c', 'src/libei-log.c', + 'src/libei-seat.c', 'src/libei-socket.c', 'src/libei-fd.c', 'src/libei-proto.h', @@ -99,6 +100,7 @@ src_libeis = [ 'src/libeis-device.c', 'src/libeis-event.c', 'src/libeis-log.c', + 'src/libeis-seat.c', 'src/libeis-socket.c', 'src/libeis-fd.c', 'src/libeis-proto.h', diff --git a/proto/ei.proto b/proto/ei.proto index f0915d0..e7e32a6 100644 --- a/proto/ei.proto +++ b/proto/ei.proto @@ -15,13 +15,14 @@ syntax = "proto3"; * 2. - client sends "Connect" * 2.a - server replies with "Connected" or * 2.b - server replies with "Disconnected" and closes its end of the socket - * 3. - client sends "AddDevice" - * 3.a - server replies with "DeviceAdded" or - * 3.b - server replies with "DeviceRemoved" - * 4. - server sends "DeviceResumed" - * 5. - client sends "PointerRelative" or any other event - * 6. - client sends "RemoveDevice" - * 7. - client sends "Disconnect" and closes its end of the socket + * 3. - server sends "AddSeat" (once or multiple times) + * 4. - client sends "AddDevice" for a specific seat + * 4.a - server replies with "DeviceAdded" or + * 4.b - server replies with "DeviceRemoved" + * 5. - server sends "DeviceResumed" + * 6. - client sends "PointerRelative" or any other event + * 7. - client sends "RemoveDevice" + * 8. - client sends "Disconnect" and closes its end of the socket * * The server may send Disconnect at any time. * The server may send Removed for a device at any time after that device's @@ -91,6 +92,7 @@ message AddDevice { uint32 keymap_type = 8; uint32 keymap_size = 9; string name = 10; + uint32 seat = 11; } message RemoveDevice { @@ -152,6 +154,16 @@ message Connected { message Disconnected { } +message SeatAdded { + uint32 seatid = 1; + uint32 capabilities = 2; + string name = 3; +} + +message SeatRemoved { + uint32 seatid = 1; +} + message DeviceAdded { uint32 deviceid = 1; uint32 capabilities = 2; @@ -160,6 +172,7 @@ message DeviceAdded { uint32 keymap_type = 4; uint32 keymap_size = 5; string name = 6; + uint32 seatid = 7; } message DeviceRemoved { @@ -178,10 +191,12 @@ message ServerMessage { oneof msg { Connected connected = 2; Disconnected disconnected = 3; - DeviceAdded device_added = 4; - DeviceRemoved device_removed = 5; - DeviceResumed device_resumed = 6; - DeviceSuspended device_suspended = 7; + SeatAdded seat_added = 4; + SeatRemoved seat_removed = 5; + DeviceAdded device_added = 6; + DeviceRemoved device_removed = 7; + DeviceResumed device_resumed = 8; + DeviceSuspended device_suspended = 9; } } diff --git a/src/libei-device.c b/src/libei-device.c index 4b6865b..4b01579 100644 --- a/src/libei-device.c +++ b/src/libei-device.c @@ -52,7 +52,7 @@ ei_device_set_state(struct ei_device *device, { enum ei_device_state old_state = device->state; device->state = state; - log_debug(ei_device_get_context(device), "device %d: %s → %s\n", + log_debug(ei_device_get_context(device), "device %#x: %s → %s\n", device->id, ei_device_state_to_string(old_state), ei_device_state_to_string(state)); } @@ -60,6 +60,19 @@ ei_device_set_state(struct ei_device *device, static void ei_device_destroy(struct ei_device *device) { + struct ei_seat *seat = ei_device_get_seat(device); + struct ei_device *d; + + /* If the device was still pending, the client held the only ref to + * it. Any other case, the device should've been removed from the + * seat by the time we get here. */ + list_for_each(d, &seat->devices_pending, link) { + if (device == d) { + list_remove(&d->link); + break; + } + } + ei_keymap_unref(device->keymap); free(device->name); } @@ -73,7 +86,7 @@ OBJECT_IMPLEMENT_UNREF(ei_device); static OBJECT_IMPLEMENT_CREATE(ei_device); static -OBJECT_IMPLEMENT_PARENT(ei_device, ei); +OBJECT_IMPLEMENT_PARENT(ei_device, ei_seat); _public_ OBJECT_IMPLEMENT_GETTER(ei_device, name, const char *); _public_ @@ -81,27 +94,34 @@ OBJECT_IMPLEMENT_GETTER(ei_device, user_data, void *); _public_ OBJECT_IMPLEMENT_SETTER(ei_device, user_data, void *); +_public_ struct ei_seat * +ei_device_get_seat(struct ei_device *device) +{ + return ei_device_parent(device); +} + _public_ struct ei* ei_device_get_context(struct ei_device *device) { assert(device); - return ei_device_parent(device); + return ei_seat_get_context(ei_device_get_seat(device)); } -_public_ -struct ei_device * -ei_device_new(struct ei *ei) +_public_ struct ei_device * +ei_device_new(struct ei_seat *seat) { /* device IDs are managed by the client, the server merely accepts * them and fails where they're being reused. */ static uint32_t deviceid = 0; - struct ei_device *device = ei_device_create(&ei->object); + struct ei_device *device = ei_device_create(&seat->object); device->capabilities = 0; - device->id = ++deviceid; + device->id = seat->id | ++deviceid; device->state = EI_DEVICE_STATE_NEW; device->name = xaprintf("unnamed device %d", device->id); + list_append(&seat->devices_pending, &device->link); /* not a ref! */ + return device; } @@ -552,10 +572,21 @@ ei_touch_up(struct ei_touch *touch) #include "src/util-munit.h" #include "src/util-memfile.h" +#define FAKE_SEAT(_seat) \ + struct ei_seat seat_##__LINE__ = { \ + .id = __LINE__ << 16, \ + .capabilities = ~0, \ + .name = "default", \ + }; \ + list_init(&seat_##__LINE__.devices); \ + list_init(&seat_##__LINE__.devices_pending); \ + struct ei_seat *_seat = &seat_ ## __LINE__; + + MUNIT_TEST(test_device_new) { - struct ei ei = {0}; - struct ei_device *d = ei_device_new(&ei); + FAKE_SEAT(seat); + struct ei_device *d = ei_device_new(seat); munit_assert_int(d->id, >, 0); munit_assert_int(d->capabilities, ==, 0); @@ -568,10 +599,10 @@ MUNIT_TEST(test_device_new) MUNIT_TEST(test_device_ids) { - struct ei ei = {0}; - _cleanup_ei_device_ struct ei_device *d1 = ei_device_new(&ei); - _cleanup_ei_device_ struct ei_device *d2 = ei_device_new(&ei); - _cleanup_ei_device_ struct ei_device *d3 = ei_device_new(&ei); + FAKE_SEAT(seat); + _cleanup_ei_device_ struct ei_device *d1 = ei_device_new(seat); + _cleanup_ei_device_ struct ei_device *d2 = ei_device_new(seat); + _cleanup_ei_device_ struct ei_device *d3 = ei_device_new(seat); munit_assert_int(d1->id, <, d2->id); munit_assert_int(d1->id, <, d3->id); @@ -582,8 +613,8 @@ MUNIT_TEST(test_device_ids) MUNIT_TEST(test_device_ref_unref) { - struct ei ei = {0}; - struct ei_device *d = ei_device_new(&ei); + FAKE_SEAT(seat); + struct ei_device *d = ei_device_new(seat); munit_assert_int(d->object.refcount, ==, 1); @@ -603,8 +634,8 @@ MUNIT_TEST(test_device_ref_unref) MUNIT_TEST(test_device_cap) { - struct ei ei = {0}; - _cleanup_ei_device_ struct ei_device *d = ei_device_new(&ei); + FAKE_SEAT(seat); + _cleanup_ei_device_ struct ei_device *d = ei_device_new(seat); munit_assert(ei_device_configure_capability(d, EI_DEVICE_CAP_POINTER)); /* twice is fine */ @@ -629,20 +660,21 @@ MUNIT_TEST(test_device_cap) return MUNIT_OK; } -MUNIT_TEST(test_device_context) +MUNIT_TEST(test_device_get_seat) { - struct ei ei = {0}; - _cleanup_ei_device_ struct ei_device *d = ei_device_new(&ei); + FAKE_SEAT(seat); + _cleanup_ei_device_ struct ei_device *d = ei_device_new(seat); - munit_assert_ptr_equal(d->object.parent, &ei); + munit_assert_ptr_equal(d->object.parent, seat); + munit_assert_ptr_equal(ei_device_get_seat(d), seat); return MUNIT_OK; } MUNIT_TEST(test_device_pointer_ranges) { - struct ei ei = {0}; - _cleanup_ei_device_ struct ei_device *d = ei_device_new(&ei); + FAKE_SEAT(seat); + _cleanup_ei_device_ struct ei_device *d = ei_device_new(seat); /* Missing the cap */ ei_device_pointer_configure_range(d, 1920, 1200); @@ -673,8 +705,8 @@ MUNIT_TEST(test_device_pointer_ranges) MUNIT_TEST(test_device_touch_ranges) { - struct ei ei = {0}; - _cleanup_ei_device_ struct ei_device *d = ei_device_new(&ei); + FAKE_SEAT(seat); + _cleanup_ei_device_ struct ei_device *d = ei_device_new(seat); /* Missing the cap */ ei_device_touch_configure_range(d, 1920, 1200); diff --git a/src/libei-private.h b/src/libei-private.h index b780765..2c4cb75 100644 --- a/src/libei-private.h +++ b/src/libei-private.h @@ -55,7 +55,7 @@ struct ei { void *backend; enum ei_state state; struct list event_queue; - struct list devices; + struct list seats; char *name; struct { @@ -64,6 +64,18 @@ struct ei { } log; }; +struct ei_seat { + struct object object; + void *user_data; + struct list link; + /* devices created by client but not yet added */ + struct list devices_pending; + struct list devices; + uint32_t id; + uint32_t capabilities; + char *name; +}; + enum ei_device_state { EI_DEVICE_STATE_NEW, EI_DEVICE_STATE_CONNECTING, @@ -121,6 +133,7 @@ struct ei_event { enum ei_event_type type; struct list link; struct ei_client *client; + struct ei_seat *seat; /* NULL if device is non-NULL */ struct ei_device *device; }; @@ -130,12 +143,31 @@ ei_set_connection(struct ei *ei, int fd); void ei_disconnect(struct ei *ei); +struct ei_seat * +ei_seat_new(struct ei *ei, uint32_t id, const char *name, + uint32_t capabilities); + +struct ei_device * +ei_seat_find_device(struct ei_seat *seat, uint32_t deviceid); + +void +ei_seat_remove(struct ei_seat *seat); + +void +ei_seat_add_device(struct ei_device *device); + +void +ei_seat_remove_device(struct ei_device *device); + int ei_add_device(struct ei_device *device); int ei_remove_device(struct ei_device *device); +void +ei_drop_device(struct ei_device *device); + int ei_pointer_rel(struct ei_device *device, double x, double y); @@ -173,6 +205,9 @@ ei_device_resumed(struct ei_device *device); void ei_device_set_name(struct ei_device *device, const char *name); +void +ei_device_set_seat(struct ei_device *device, const char *seat); + void ei_device_set_capabilities(struct ei_device *device, uint32_t capabilities); diff --git a/src/libei-proto.c b/src/libei-proto.c index bc49755..b452807 100644 --- a/src/libei-proto.c +++ b/src/libei-proto.c @@ -36,6 +36,9 @@ void message_free(struct message *msg) { switch (msg->type) { + case MESSAGE_SEAT_ADDED: + free(msg->seat_added.name); + break; case MESSAGE_DEVICE_ADDED: xclose(msg->device_added.keymap_fd); free(msg->device_added.name); @@ -77,6 +80,26 @@ ei_proto_parse_message(struct brei_message *bmsg, size_t *consumed) .type = MESSAGE_DISCONNECTED, }; break; + case SERVER_MESSAGE__MSG_SEAT_ADDED: + { + SeatAdded *a = proto->seat_added; + *msg = (struct message) { + .type = MESSAGE_SEAT_ADDED, + .seat_added.seatid = a->seatid, + .seat_added.name = xstrdup(a->name), + .seat_added.capabilities = a->capabilities, + }; + } + break; + case SERVER_MESSAGE__MSG_SEAT_REMOVED: + { + SeatRemoved *r = proto->seat_removed; + *msg = (struct message) { + .type = MESSAGE_SEAT_REMOVED, + .seat_removed.seatid = r->seatid, + }; + } + break; case SERVER_MESSAGE__MSG_DEVICE_ADDED: { DeviceAdded *a = proto->device_added; @@ -89,6 +112,7 @@ ei_proto_parse_message(struct brei_message *bmsg, size_t *consumed) .device_added.keymap_type = a->keymap_type, .device_added.keymap_from_server = a->keymap_from_server, .device_added.keymap_size = a->keymap_size, + .device_added.seatid = a->seatid, }; if (a->keymap_type && a->keymap_from_server) msg->device_added.keymap_fd = brei_message_take_fd(bmsg); @@ -228,6 +252,7 @@ ei_proto_send_add(struct ei *ei, struct ei_device *device) { ClientMessage msg = CLIENT_MESSAGE__INIT; AddDevice add = ADD_DEVICE__INIT; + struct ei_seat *seat = ei_device_get_seat(device); add.deviceid = device->id; add.name = device->name; @@ -246,6 +271,7 @@ ei_proto_send_add(struct ei *ei, struct ei_device *device) add.keymap_size = ei_keymap_get_size(keymap); fd[0] = ei_keymap_get_fd(keymap); } + add.seat = seat->id; msg.add_device = &add; msg.msg_case = CLIENT_MESSAGE__MSG_ADD_DEVICE; diff --git a/src/libei-proto.h b/src/libei-proto.h index db1e895..9df52c3 100644 --- a/src/libei-proto.h +++ b/src/libei-proto.h @@ -35,6 +35,8 @@ enum message_type { MESSAGE_CONNECTED = 1, MESSAGE_DISCONNECTED, + MESSAGE_SEAT_ADDED, + MESSAGE_SEAT_REMOVED, MESSAGE_DEVICE_ADDED, MESSAGE_DEVICE_REMOVED, MESSAGE_DEVICE_RESUMED, @@ -47,6 +49,8 @@ message_type_to_string(enum message_type type) switch(type) { CASE_RETURN_STRING(MESSAGE_CONNECTED); CASE_RETURN_STRING(MESSAGE_DISCONNECTED); + CASE_RETURN_STRING(MESSAGE_SEAT_ADDED); + CASE_RETURN_STRING(MESSAGE_SEAT_REMOVED); CASE_RETURN_STRING(MESSAGE_DEVICE_ADDED); CASE_RETURN_STRING(MESSAGE_DEVICE_REMOVED); CASE_RETURN_STRING(MESSAGE_DEVICE_RESUMED); @@ -64,6 +68,14 @@ struct message { struct message_disconnected { uint8_t pad; /* no data */ } disconnected; + struct message_seat_added { + uint32_t seatid; + char *name; + uint32_t capabilities; + } seat_added; + struct message_seat_removed { + uint32_t seatid; + } seat_removed; struct message_device_added { uint32_t deviceid; char *name; @@ -72,6 +84,7 @@ struct message { bool keymap_from_server; int keymap_fd; size_t keymap_size; + uint32_t seatid; } device_added; struct message_device_removed { uint32_t deviceid; diff --git a/src/libei-seat.c b/src/libei-seat.c new file mode 100644 index 0000000..7f86638 --- /dev/null +++ b/src/libei-seat.c @@ -0,0 +1,129 @@ +/* + * Copyright © 2020 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include + +#include "util-bits.h" +#include "util-macros.h" +#include "util-mem.h" +#include "util-io.h" +#include "util-strings.h" + +#include "libei-private.h" + +static void +ei_seat_destroy(struct ei_seat *seat) +{ + free(seat->name); +} + +_public_ +OBJECT_IMPLEMENT_REF(ei_seat); +_public_ +OBJECT_IMPLEMENT_UNREF(ei_seat); +#define _cleanup_ei_seat_ _cleanup_(ei_seat_cleanup) + +static +OBJECT_IMPLEMENT_CREATE(ei_seat); +static +OBJECT_IMPLEMENT_PARENT(ei_seat, ei); +_public_ +OBJECT_IMPLEMENT_GETTER(ei_seat, name, const char *); +_public_ +OBJECT_IMPLEMENT_SETTER(ei_seat, user_data, void *); + +_public_ struct ei* +ei_seat_get_context(struct ei_seat *seat) +{ + assert(seat); + return ei_seat_parent(seat); +} + +struct ei_seat * +ei_seat_new(struct ei *ei, uint32_t id, const char *name, uint32_t capabilities) +{ + struct ei_seat *seat = ei_seat_create(&ei->object); + + seat->name = xstrdup(name); + seat->id = id; + seat->capabilities = capabilities; + + list_init(&seat->devices); + list_init(&seat->devices_pending); + list_init(&seat->link); + + return seat; /* ref owned by caller */ +} + +void +ei_seat_remove(struct ei_seat *seat) +{ + struct ei_device *d, *tmp; + + /* If the server disconnects us before processing a new device, we + * need to clean this up in the library */ + list_for_each_safe(d, tmp, &seat->devices, link) { + ei_drop_device(d); + } + + list_remove(&seat->link); + list_init(&seat->link); +} + +void +ei_seat_add_device(struct ei_device *device) +{ + struct ei_seat *seat = ei_device_get_seat(device); + + /* the seat owns the ref to the device */ + ei_device_ref(device); + /* remove from pending list */ + list_remove(&device->link); + list_append(&seat->devices, &device->link); +} + +struct ei_device * +ei_seat_find_device(struct ei_seat *seat, uint32_t deviceid) +{ + struct ei_device *device; + + list_for_each(device, &seat->devices, link) { + if (device->id == deviceid) + return device; + } + + list_for_each(device, &seat->devices_pending, link) { + if (device->id == deviceid) + return device; + } + return NULL; +} + +void +ei_seat_remove_device(struct ei_device *device) +{ + list_remove(&device->link); + ei_device_unref(device); +} diff --git a/src/libei.c b/src/libei.c index f52db0d..df5e407 100644 --- a/src/libei.c +++ b/src/libei.c @@ -45,6 +45,8 @@ ei_event_type_to_string(enum ei_event_type type) switch(type) { CASE_RETURN_STRING(EI_EVENT_CONNECT); CASE_RETURN_STRING(EI_EVENT_DISCONNECT); + CASE_RETURN_STRING(EI_EVENT_SEAT_ADDED); + CASE_RETURN_STRING(EI_EVENT_SEAT_REMOVED); CASE_RETURN_STRING(EI_EVENT_DEVICE_ADDED); CASE_RETURN_STRING(EI_EVENT_DEVICE_REMOVED); CASE_RETURN_STRING(EI_EVENT_DEVICE_SUSPENDED); @@ -60,6 +62,8 @@ ei_event_destroy(struct ei_event *event) switch (event->type) { case EI_EVENT_CONNECT: case EI_EVENT_DISCONNECT: + case EI_EVENT_SEAT_ADDED: + case EI_EVENT_SEAT_REMOVED: case EI_EVENT_DEVICE_ADDED: case EI_EVENT_DEVICE_REMOVED: case EI_EVENT_DEVICE_SUSPENDED: @@ -69,6 +73,7 @@ ei_event_destroy(struct ei_event *event) assert(!"destroy not implemented for this type"); } ei_device_unref(event->device); + ei_seat_unref(event->seat); } static @@ -82,6 +87,39 @@ OBJECT_IMPLEMENT_GETTER(ei_event, type, enum ei_event_type); _public_ OBJECT_IMPLEMENT_GETTER(ei_event, device, struct ei_device*); +_public_ struct ei_seat * +ei_event_get_seat(struct ei_event *event) +{ + return event->device ? ei_device_get_seat(event->device) : event->seat; +} + +static struct ei_seat * +ei_find_seat(struct ei *ei, uint32_t seatid) +{ + struct ei_seat *seat; + + list_for_each(seat, &ei->seats, link) { + if (seat->id == seatid) + return seat; + } + + return NULL; +} + +static struct ei_device * +ei_find_device(struct ei *ei, uint32_t deviceid) +{ + struct ei_seat *seat; + + list_for_each(seat, &ei->seats, link) { + struct ei_device *device = ei_seat_find_device(seat, deviceid); + if (device) + return device; + } + + return NULL; +} + static void ei_destroy(struct ei *ei) { @@ -117,7 +155,7 @@ ei_new(void *user_data) ei->state = EI_STATE_NEW; list_init(&ei->event_queue); - list_init(&ei->devices); + list_init(&ei->seats); ei_log_set_handler(ei, NULL); ei_log_set_priority(ei, EI_LOG_PRIORITY_INFO); @@ -170,6 +208,30 @@ queue_disconnect_event(struct ei *ei) queue_event(ei, e); } +static void +queue_seat_added_event(struct ei_seat *seat) +{ + struct ei *ei= ei_seat_get_context(seat); + + struct ei_event *e = ei_event_create(&ei->object); + e->type = EI_EVENT_SEAT_ADDED; + e->seat = ei_seat_ref(seat); + + queue_event(ei, e); +} + +static void +queue_seat_removed_event(struct ei_seat *seat) +{ + struct ei *ei= ei_seat_get_context(seat); + + struct ei_event *e = ei_event_create(&ei->object); + e->type = EI_EVENT_SEAT_REMOVED; + e->seat = ei_seat_ref(seat); + + queue_event(ei, e); +} + static void queue_device_added_event(struct ei_device *device) { @@ -334,7 +396,7 @@ connection_send_touch_up(struct ei *ei, struct ei_device *device, uint32_t tid) return ei_proto_send_touch_up(ei, device, tid); } -static void +void ei_drop_device(struct ei_device *device) { switch (device->state) { @@ -349,8 +411,15 @@ ei_drop_device(struct ei_device *device) queue_device_removed_event(device); break; } - list_remove(&device->link); - ei_device_unref(device); + ei_seat_remove_device(device); +} + +static void +ei_drop_seat(struct ei_seat *seat) +{ + queue_seat_removed_event(seat); + list_remove(&seat->link); + ei_seat_unref(seat); } void @@ -366,13 +435,18 @@ ei_disconnect(struct ei *ei) ei_device_remove() may call ei_disconnect() on a socket error */ ei->state = EI_STATE_DISCONNECTING; - struct ei_device *d, *tmp; - list_for_each_safe(d, tmp, &ei->devices, link) { - /* remove the device */ - ei_device_remove(d); - /* And pretend to process the removed message from the - * server */ - ei_drop_device(d); + struct ei_seat *seat, *tmps; + list_for_each_safe(seat, tmps, &ei->seats, link) { + struct ei_device *d, *tmpd; + list_for_each_safe(d, tmpd, &seat->devices, link) { + /* remove the device */ + ei_device_remove(d); + /* And pretend to process the removed message from + * the server */ + ei_drop_device(d); + } + ei_seat_remove(seat); + ei_drop_seat(seat); } if (state != EI_STATE_NEW) { @@ -395,8 +469,7 @@ ei_add_device(struct ei_device *device) return rc; } - ei_device_ref(device); - list_append(&ei->devices, &device->link); + ei_seat_add_device(device); return 0; } @@ -412,51 +485,88 @@ ei_remove_device(struct ei_device *device) return rc; } +static int +handle_msg_seat_added(struct ei *ei, uint32_t seatid, + const char *name, uint32_t capabilities) +{ + log_debug(ei, "Added seat %#x '%s' with caps %#x\n", + seatid, name, capabilities); + + struct ei_seat *seat = ei_seat_new(ei, seatid, name, capabilities); + + list_append(&ei->seats, &seat->link); + + queue_seat_added_event(seat); + + return 0; +} + +static int +handle_msg_seat_removed(struct ei *ei, uint32_t seatid) +{ + log_debug(ei, "Removed seat %#x\n", seatid); + + struct ei_seat *seat = ei_find_seat(ei, seatid); + if (seat) { + ei_seat_remove(seat); + queue_seat_removed_event(seat); + ei_seat_unref(seat); + } + + return 0; +} + static int handle_msg_device_added(struct ei *ei, uint32_t deviceid, const char *name, uint32_t capabilities, bool keymap_from_server, enum ei_keymap_type keymap_type, - int keymap_fd, size_t keymap_sz) + int keymap_fd, size_t keymap_sz, + uint32_t seatid) { - struct ei_device *d; + struct ei_seat *seat = ei_find_seat(ei, seatid); - list_for_each(d, &ei->devices, link) { - if (d->id == deviceid) { - ei_device_set_name(d, name); - ei_device_set_capabilities(d, capabilities); - if (keymap_from_server) - ei_device_set_keymap(d, keymap_type, keymap_fd, keymap_sz); - ei_device_added(d); - - log_debug(ei, "Added device %d '%s' caps: %s%s%s%s\n", deviceid, name, - ei_device_has_capability(d, EI_DEVICE_CAP_POINTER) ? "p" : "", - ei_device_has_capability(d, EI_DEVICE_CAP_POINTER_ABSOLUTE) ? "a" : "", - ei_device_has_capability(d, EI_DEVICE_CAP_KEYBOARD) ? "k" : "", - ei_device_has_capability(d, EI_DEVICE_CAP_TOUCH) ? "t" : ""); - - queue_device_added_event(d); - return 0; - } + if (!seat) { + log_bug(ei, "Invalid seat id %d for device %s (%d)\n", + seatid, name, deviceid); + return 0; } + struct ei_device *device = ei_seat_find_device(seat, deviceid); + /* Wrong device id or a device already removed by the client but we * won't know which unless we keep some device ID table. Not worth * it, so just silently ignore */ + if (!device) + return 0; + + ei_device_set_name(device, name); + ei_device_set_capabilities(device, capabilities); + if (keymap_from_server) + ei_device_set_keymap(device, keymap_type, + keymap_fd, keymap_sz); + ei_device_added(device); + + log_debug(ei, + "Added device %d '%s' caps: %s%s%s%s seat: %s\n", + deviceid, name, + ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) ? "p" : "", + ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) ? "a" : "", + ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD) ? "k" : "", + ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH) ? "t" : "", + ei_seat_get_name(seat)); + + queue_device_added_event(device); return 0; } static int handle_msg_device_removed(struct ei *ei, uint32_t deviceid) { - struct ei_device *d; - log_debug(ei, "Removed device %d\n", deviceid); - list_for_each(d, &ei->devices, link) { - if (d->id == deviceid) { - ei_drop_device(d); - break; - } + struct ei_device *device = ei_find_device(ei, deviceid); + if (device) { + ei_drop_device(device); } return 0; @@ -465,16 +575,12 @@ handle_msg_device_removed(struct ei *ei, uint32_t deviceid) static int handle_msg_resumed(struct ei *ei, uint32_t deviceid) { - struct ei_device *d; - log_debug(ei, "Resumed device %d\n", deviceid); - list_for_each(d, &ei->devices, link) { - if (d->id == deviceid) { - ei_device_resumed(d); - queue_resumed_event(d); - break; - } + struct ei_device *device = ei_find_device(ei, deviceid); + if (device) { + ei_device_resumed(device); + queue_resumed_event(device); } return 0; @@ -483,16 +589,12 @@ handle_msg_resumed(struct ei *ei, uint32_t deviceid) static int handle_msg_suspended(struct ei *ei, uint32_t deviceid) { - struct ei_device *d; - log_debug(ei, "Suspended device %d\n", deviceid); - list_for_each(d, &ei->devices, link) { - if (d->id == deviceid) { - ei_device_suspended(d); - queue_suspended_event(d); - break; - } + struct ei_device *device = ei_find_device(ei, deviceid); + if (device) { + ei_device_suspended(device); + queue_suspended_event(device); } return 0; @@ -600,6 +702,8 @@ connection_new_handle_msg(struct ei *ei, struct message *msg) switch (msg->type) { case MESSAGE_CONNECTED: case MESSAGE_DISCONNECTED: + case MESSAGE_SEAT_ADDED: + case MESSAGE_SEAT_REMOVED: case MESSAGE_DEVICE_ADDED: case MESSAGE_DEVICE_REMOVED: case MESSAGE_DEVICE_RESUMED: @@ -624,6 +728,8 @@ connection_connecting_handle_msg(struct ei *ei, struct message *msg) case MESSAGE_DISCONNECTED: rc = -ECANCELED; break; + case MESSAGE_SEAT_ADDED: + case MESSAGE_SEAT_REMOVED: case MESSAGE_DEVICE_ADDED: case MESSAGE_DEVICE_REMOVED: case MESSAGE_DEVICE_RESUMED: @@ -647,6 +753,15 @@ connection_connected_handle_msg(struct ei *ei, struct message *msg) case MESSAGE_DISCONNECTED: rc = -ECANCELED; break; + case MESSAGE_SEAT_ADDED: + rc = handle_msg_seat_added(ei, + msg->seat_added.seatid, + msg->seat_added.name, + msg->seat_added.capabilities); + break; + case MESSAGE_SEAT_REMOVED: + rc = handle_msg_seat_removed(ei, msg->seat_removed.seatid); + break; case MESSAGE_DEVICE_ADDED: rc = handle_msg_device_added(ei, msg->device_added.deviceid, @@ -655,7 +770,8 @@ connection_connected_handle_msg(struct ei *ei, struct message *msg) msg->device_added.keymap_from_server, msg->device_added.keymap_type, msg->device_added.keymap_fd, - msg->device_added.keymap_size); + msg->device_added.keymap_size, + msg->device_added.seatid); break; case MESSAGE_DEVICE_REMOVED: rc = handle_msg_device_removed(ei, msg->device_removed.deviceid); @@ -698,7 +814,8 @@ connection_message_callback(struct brei_message *bmsg, void *userdata) break; case EI_STATE_DISCONNECTING: case EI_STATE_DISCONNECTED: - abort(); + assert(!"Protocol error: message received while disconnecting\n"); + break; } if (rc < 0) @@ -783,7 +900,7 @@ MUNIT_TEST(test_init_unref) munit_assert_int(ei->state, ==, EI_STATE_NEW); munit_assert(list_empty(&ei->event_queue)); - munit_assert(list_empty(&ei->devices)); + munit_assert(list_empty(&ei->seats)); munit_assert_not_null(ei->sink); diff --git a/src/libei.h b/src/libei.h index c840a86..ec741e3 100644 --- a/src/libei.h +++ b/src/libei.h @@ -64,6 +64,21 @@ struct ei; */ struct ei_device; +/** + * @struct ei_seat + * + * A logical seat for a group of devices. Seats are provided by the EIS + * implementation, devices may be added to a seat. The hierarchy of objects + * looks like this: + *
+ *    ei ---- ei_seat one ---- ei_device 1
+ *       \                \
+ *        \                --- ei device 2
+ *         --- ei_seat two --- ei device 3
+ * 
+ */ +struct ei_seat; + /** * @struct ei_event * @@ -162,6 +177,29 @@ enum ei_event_type { */ EI_EVENT_DISCONNECT, + /** + * The server has added a seat available to this client. + * + * libei guarantees that any seat added has a corresponding @ref + * EI_EVENT_SEAT_REMOVED event before @ref EI_EVENT_DISCONNECT. + * libei guarantees that any device in this seat generates a @ref + * EI_EVENT_DEVICE_REMOVED event before the @ref + * EI_EVENT_SEAT_REMOVED event. + */ + EI_EVENT_SEAT_ADDED, + + /** + * The server has removed a seat previously available to this + * client. The caller should release the struct @ref ei_seat and + * all its associated resources. No devices can be created through + * this seat anymore. + * + * libei guarantees that any device in this seat generates a @ref + * EI_EVENT_DEVICE_REMOVED event before the @ref + * EI_EVENT_SEAT_REMOVED event. + */ + EI_EVENT_SEAT_REMOVED, + /** * The server has added a device for this client. The capabilities * of the device may not match the requested capabilities - it is up @@ -418,6 +456,25 @@ ei_peek_event(struct ei *ei); struct ei_event * ei_event_unref(struct ei_event *event); +const char * +ei_seat_get_name(struct ei_seat *seat); + +bool +ei_seat_has_capability(struct ei_seat *seat, + enum ei_device_capability cap); + +struct ei_seat * +ei_seat_ref(struct ei_seat *seat); + +struct ei_seat * +ei_seat_unref(struct ei_seat *seat); + +/** + * Return the struct @ref ei context this seat is associated with. + */ +struct ei * +ei_seat_get_context(struct ei_seat *seat); + /** * @return the type of this event */ @@ -462,10 +519,10 @@ struct ei_device * ei_device_unref(struct ei_device *device); /** - * Create a new device. This device is a proxy representing the server's - * device and should be used for initial device configuration. It does not - * represent the server-created device until the @ref EI_EVENT_DEVICE_ADDED - * for this device has been received. + * Create a new device in the given seat. This device is a proxy + * representing the server's device and should be used for initial device + * configuration. It does not represent the server-created device until the + * @ref EI_EVENT_DEVICE_ADDED for this device has been received. * * Use the configure API (e.g. ei_device_configure_capability()) to set up * the device, then call ei_device_add() to request creation of the device @@ -474,7 +531,25 @@ ei_device_unref(struct ei_device *device); * The returned object must be released by the caller with ei_event_unref() */ struct ei_device * -ei_device_new(struct ei *ei); +ei_device_new(struct ei_seat *ei_seat); + +struct ei_seat * +ei_device_get_seat(struct ei_device *device); + +/** + * Set a custom data pointer for this context. libei will not look at or + * modify the pointer. Use ei_seat_get_user_data() to retrieve a + * previously set user data. + */ +void +ei_seat_set_user_data(struct ei_seat *seat, void *user_data); + +/** + * Return the custom data pointer for this context. libei will not look at or + * modify the pointer. Use ei_seat_get_user_data() to change the user data. + */ +void * +ei_seat_get_user_data(struct ei_seat *seat); /** * Set a custom data pointer for this context. libei will not look at or @@ -991,6 +1066,18 @@ ei_touch_get_user_data(struct ei_touch *touch); struct ei_device * ei_touch_get_device(struct ei_touch *touch); +/** + * Return the seat from this event. + * + * For events of type @ref EI_EVENT_CONNECT and @ref EI_EVENT_DISCONNECT, + * this function returns NULL. + * + * This does not increase the refcount of the seat. Use eis_seat_ref() + * to keep a reference beyond the immediate scope. + */ +struct ei_seat * +ei_event_get_seat(struct ei_event *event); + /** * @} */ diff --git a/src/libeis-client.c b/src/libeis-client.c index 311e9cf..a6578c3 100644 --- a/src/libeis-client.c +++ b/src/libeis-client.c @@ -82,6 +82,18 @@ client_send_connect(struct eis_client *client) return eis_proto_send_connect(client); } +static int +client_send_seat_added(struct eis_client *client, struct eis_seat *seat) +{ + return eis_proto_send_seat_added(client, seat); +} + +static int +client_send_seat_removed(struct eis_client *client, struct eis_seat *seat) +{ + return eis_proto_send_seat_removed(client, seat); +} + static int client_send_device_added(struct eis_client *client, struct eis_device *device) { @@ -138,9 +150,9 @@ eis_client_disconnect(struct eis_client *client) case EIS_CLIENT_STATE_CONNECTING: case EIS_CLIENT_STATE_CONNECTED: { - struct eis_device *d, *tmp; - list_for_each_safe(d, tmp, &client->devices, link) - eis_device_disconnect(d); + struct eis_seat *s, *tmp; + list_for_each_safe(s, tmp, &client->seats, link) + eis_seat_remove(s); } eis_queue_disconnect_event(client); _fallthrough_; @@ -160,11 +172,24 @@ client_new_device(struct eis_client *client, const struct dimensions *dim_pointer, const struct dimensions *dim_touch, enum eis_keymap_type keymap_type, - int keymap_fd, size_t keymap_sz) + int keymap_fd, size_t keymap_sz, + uint32_t seatid) { + struct eis_seat *s, *seat = NULL; + + list_for_each(s, &client->seats, link) { + if (s->id == seatid) { + seat = s; + break; + } + } + + if (seat == NULL) + return -EINVAL; + /* Check for duplicate IDs */ struct eis_device *d; - list_for_each(d, &client->devices, link) { + list_for_each(d, &seat->devices, link) { if (d->id == id) return -EINVAL; } @@ -176,8 +201,8 @@ client_new_device(struct eis_client *client, bit(EIS_DEVICE_CAP_TOUCH))) return -EINVAL; - struct eis_device *device = eis_device_new(client, id, name, capabilities); - list_append(&client->devices, &device->link); + struct eis_device *device = eis_device_new(seat, id, name, capabilities); + list_append(&seat->devices, &device->link); if (flag_is_set(capabilities, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) eis_device_set_pointer_range(device, dim_pointer->width, @@ -190,12 +215,13 @@ client_new_device(struct eis_client *client, eis_device_set_client_keymap(device, keymap_type, keymap_fd, keymap_sz); log_debug(eis_client_parent(client), - "New device %d '%s' caps: %s%s%s%s\n", + "New device %#x '%s' caps: %s%s%s%s seat: %s\n", id, name, eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) ? "p" : "", eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE) ? "a" : "", eis_device_has_capability(device, EIS_DEVICE_CAP_KEYBOARD) ? "k" : "", - eis_device_has_capability(device, EIS_DEVICE_CAP_TOUCH) ? "t" : ""); + eis_device_has_capability(device, EIS_DEVICE_CAP_TOUCH) ? "t" : "", + eis_seat_get_name(seat)); eis_queue_added_event(device); @@ -205,12 +231,16 @@ client_new_device(struct eis_client *client, static int client_remove_device(struct eis_client *client, uint32_t deviceid) { - struct eis_device *device; + struct eis_seat *seat; - list_for_each(device, &client->devices, link) { - if (device->id == deviceid) { - eis_queue_removed_event(device); - break; + list_for_each(seat, &client->seats, link) { + struct eis_device *device; + + list_for_each(device, &seat->devices, link) { + if (device->id == deviceid) { + eis_queue_removed_event(device); + break; + } } } return 0; @@ -220,11 +250,15 @@ static int client_pointer_rel(struct eis_client *client, uint32_t deviceid, double x, double y) { - struct eis_device *device; + struct eis_seat *seat; - list_for_each(device, &client->devices, link) { - if (device->id == deviceid) { - return eis_device_pointer_rel(device, x, y); + list_for_each(seat, &client->seats, link) { + struct eis_device *device; + + list_for_each(device, &seat->devices, link) { + if (device->id == deviceid) { + return eis_device_pointer_rel(device, x, y); + } } } return -EINVAL; @@ -234,11 +268,15 @@ static int client_pointer_abs(struct eis_client *client, uint32_t deviceid, double x, double y) { - struct eis_device *device; + struct eis_seat *seat; - list_for_each(device, &client->devices, link) { - if (device->id == deviceid) { - return eis_device_pointer_abs(device, x, y); + list_for_each(seat, &client->seats, link) { + struct eis_device *device; + + list_for_each(device, &seat->devices, link) { + if (device->id == deviceid) { + return eis_device_pointer_abs(device, x, y); + } } } return -EINVAL; @@ -248,11 +286,15 @@ static int client_pointer_button(struct eis_client *client, uint32_t deviceid, uint32_t button, bool state) { - struct eis_device *device; + struct eis_seat *seat; - list_for_each(device, &client->devices, link) { - if (device->id == deviceid) { - return eis_device_pointer_button(device, button, state); + list_for_each(seat, &client->seats, link) { + struct eis_device *device; + + list_for_each(device, &seat->devices, link) { + if (device->id == deviceid) { + return eis_device_pointer_button(device, button, state); + } } } return -EINVAL; @@ -262,11 +304,15 @@ static int client_keyboard_key(struct eis_client *client, uint32_t deviceid, uint32_t key, bool state) { - struct eis_device *device; + struct eis_seat *seat; - list_for_each(device, &client->devices, link) { - if (device->id == deviceid) { - return eis_device_keyboard_key(device, key, state); + list_for_each(seat, &client->seats, link) { + struct eis_device *device; + + list_for_each(device, &seat->devices, link) { + if (device->id == deviceid) { + return eis_device_keyboard_key(device, key, state); + } } } return -EINVAL; @@ -277,12 +323,16 @@ client_touch(struct eis_client *client, uint32_t deviceid, uint32_t touchid, bool is_down, bool is_up, double x, double y) { - struct eis_device *device; + struct eis_seat *seat; - list_for_each(device, &client->devices, link) { - if (device->id == deviceid) { - return eis_device_touch(device, touchid, - is_down, is_up, x, y); + list_for_each(seat, &client->seats, link) { + struct eis_device *device; + + list_for_each(device, &seat->devices, link) { + if (device->id == deviceid) { + return eis_device_touch(device, touchid, + is_down, is_up, x, y); + } } } return -EINVAL; @@ -418,7 +468,8 @@ client_connected_handle_msg(struct eis_client *client, &msg->add_device.dim_touch, msg->add_device.keymap_type, msg->add_device.keymap_fd, - msg->add_device.keymap_size); + msg->add_device.keymap_size, + msg->add_device.seat); break; case MESSAGE_REMOVE_DEVICE: rc = client_remove_device(client, msg->remove_device.deviceid); @@ -527,7 +578,8 @@ eis_client_new(struct eis *eis, int fd) struct eis_client *client = eis_client_create(&eis->object); client->id = ++client_id; - list_init(&client->devices); + list_init(&client->seats); + list_init(&client->seats_pending); struct source *s = source_new(fd, client_dispatch, client); int rc = sink_add_source(eis->sink, s); @@ -550,6 +602,31 @@ eis_client_new(struct eis *eis, int fd) return client; } +void +eis_client_add_seat(struct eis_client *client, struct eis_seat *seat) +{ + /* remove from the pending list first */ + list_remove(&seat->link); + /* We own this seat now */ + eis_seat_ref(seat); + list_append(&client->seats, &seat->link); + client_send_seat_added(client, seat); +} + +void +eis_client_remove_seat(struct eis_client *client, struct eis_seat *seat) +{ + struct eis_device *d, *tmp; + + list_for_each_safe(d, tmp, &seat->devices, link) { + eis_device_disconnect(d); + } + list_remove(&seat->link); + if (seat->state != EIS_SEAT_STATE_ADDED) + client_send_seat_removed(client, seat); + eis_seat_unref(seat); +} + void eis_client_connect_device(struct eis_client *client, struct eis_device *device) { diff --git a/src/libeis-device.c b/src/libeis-device.c index 4037776..f0f81a4 100644 --- a/src/libeis-device.c +++ b/src/libeis-device.c @@ -133,7 +133,7 @@ OBJECT_IMPLEMENT_UNREF(eis_device); static OBJECT_IMPLEMENT_CREATE(eis_device); static -OBJECT_IMPLEMENT_PARENT(eis_device, eis_client); +OBJECT_IMPLEMENT_PARENT(eis_device, eis_seat); _public_ OBJECT_IMPLEMENT_GETTER(eis_device, user_data, void *); _public_ @@ -141,6 +141,12 @@ OBJECT_IMPLEMENT_SETTER(eis_device, user_data, void *); _public_ OBJECT_IMPLEMENT_GETTER(eis_device, name, const char *); +_public_ struct eis_seat * +eis_device_get_seat(struct eis_device *device) +{ + return eis_device_parent(device); +} + _public_ void eis_device_set_name(struct eis_device *device, const char *name) { @@ -157,16 +163,16 @@ eis_device_set_name(struct eis_device *device, const char *name) _public_ struct eis_client * eis_device_get_client(struct eis_device *device) { - return eis_device_parent(device); + return eis_seat_get_client(eis_device_get_seat(device)); } struct eis_device * -eis_device_new(struct eis_client *client, +eis_device_new(struct eis_seat *seat, uint32_t id, const char *name, uint32_t capabilities) { - struct eis_device *device = eis_device_create(&client->object); + struct eis_device *device = eis_device_create(&seat->object); device->name = xstrdup(name); device->capabilities_mask = 0; diff --git a/src/libeis-private.h b/src/libeis-private.h index 93500ab..d7f52e5 100644 --- a/src/libeis-private.h +++ b/src/libeis-private.h @@ -67,7 +67,8 @@ struct eis_client { enum eis_client_state state; char *name; - struct list devices; + struct list seats; + struct list seats_pending; struct { enum { @@ -79,6 +80,25 @@ struct eis_client { } restrictions; }; +enum eis_seat_state { + EIS_SEAT_STATE_PENDING, + EIS_SEAT_STATE_ADDED, + EIS_SEAT_STATE_REMOVED, +}; + +struct eis_seat { + struct object object; /* parent is ei_client */ + struct list link; + void *user_data; + uint32_t id; + + enum eis_seat_state state; + char *name; + uint32_t capabilities_mask; + + struct list devices; +}; + enum eis_device_state { EIS_DEVICE_STATE_NEW, EIS_DEVICE_STATE_SUSPENDED, @@ -86,8 +106,9 @@ enum eis_device_state { EIS_DEVICE_STATE_REMOVED, }; + struct eis_device { - struct object object; + struct object object; /* parent is ei_seat */ struct list link; uint32_t id; char *name; @@ -156,6 +177,11 @@ eis_client_get_context(struct eis_client *client); void eis_add_client(struct eis *eis, struct eis_client *client); +void +eis_client_add_seat(struct eis_client *client, struct eis_seat *seat); +void +eis_client_remove_seat(struct eis_client *client, struct eis_seat *seat); + void eis_client_connect_device(struct eis_client *client, struct eis_device *device); @@ -171,7 +197,7 @@ eis_client_suspend_device(struct eis_client *client, struct eis_device *device); struct eis_device * -eis_device_new(struct eis_client *client, +eis_device_new(struct eis_seat *seat, uint32_t id, const char *name, uint32_t capabilities); diff --git a/src/libeis-proto.c b/src/libeis-proto.c index 644a1de..200a280 100644 --- a/src/libeis-proto.c +++ b/src/libeis-proto.c @@ -66,6 +66,8 @@ log_wire_message(struct eis *eis, const ServerMessage *msg) abort(); MSG_STRING_CASE(CONNECTED); MSG_STRING_CASE(DISCONNECTED); + MSG_STRING_CASE(SEAT_ADDED); + MSG_STRING_CASE(SEAT_REMOVED); MSG_STRING_CASE(DEVICE_ADDED); MSG_STRING_CASE(DEVICE_REMOVED); MSG_STRING_CASE(DEVICE_RESUMED); @@ -137,16 +139,50 @@ eis_proto_send_connect(struct eis_client *client) return eis_proto_send_msg(client, &msg); } +int +eis_proto_send_seat_added(struct eis_client *client, + struct eis_seat *seat) +{ + ServerMessage msg = SERVER_MESSAGE__INIT; + SeatAdded added = SEAT_ADDED__INIT; + + added.seatid = seat->id; + added.name = seat->name; + added.capabilities = seat->capabilities_mask; + + msg.seat_added = &added; + msg.msg_case = SERVER_MESSAGE__MSG_SEAT_ADDED; + + return eis_proto_send_msg(client, &msg); +} + +int +eis_proto_send_seat_removed(struct eis_client *client, + struct eis_seat *seat) +{ + ServerMessage msg = SERVER_MESSAGE__INIT; + SeatRemoved removed = SEAT_REMOVED__INIT; + + removed.seatid = seat->id; + + msg.seat_removed = &removed; + msg.msg_case = SERVER_MESSAGE__MSG_SEAT_REMOVED; + + return eis_proto_send_msg(client, &msg); +} + int eis_proto_send_device_added(struct eis_client *client, struct eis_device *device) { ServerMessage msg = SERVER_MESSAGE__INIT; DeviceAdded added = DEVICE_ADDED__INIT; + struct eis_seat *seat = eis_device_get_seat(device); int fds[2] = {-1, -1}; added.deviceid = device->id; added.name = device->name; added.capabilities = device->capabilities; + if (device->keymap) { struct eis_keymap *keymap = device->keymap; added.keymap_type = keymap->type; @@ -158,6 +194,7 @@ eis_proto_send_device_added(struct eis_client *client, struct eis_device *device /* it's NULL anyway */ added.keymap_from_server = true; } + added.seatid = seat->id; msg.device_added = &added; msg.msg_case = SERVER_MESSAGE__MSG_DEVICE_ADDED; @@ -250,6 +287,7 @@ eis_proto_parse_message(struct brei_message *bmsg, size_t *consumed) .add_device.keymap_fd = -1, .add_device.keymap_type = a->keymap_type, .add_device.keymap_size = a->keymap_size, + .add_device.seat = a->seat, }; if (a->keymap_type) diff --git a/src/libeis-proto.h b/src/libeis-proto.h index 4121537..d5cc42f 100644 --- a/src/libeis-proto.h +++ b/src/libeis-proto.h @@ -68,6 +68,7 @@ struct message { enum eis_keymap_type keymap_type; int keymap_fd; size_t keymap_size; + uint32_t seat; } add_device; struct message_remove_device { uint32_t deviceid; @@ -122,6 +123,14 @@ eis_proto_send_disconnect(struct eis_client *client); int eis_proto_send_connect(struct eis_client *client); +int +eis_proto_send_seat_added(struct eis_client *client, + struct eis_seat *seat); + +int +eis_proto_send_seat_removed(struct eis_client *client, + struct eis_seat *seat); + int eis_proto_send_device_added(struct eis_client *client, struct eis_device *device); diff --git a/src/libeis-seat.c b/src/libeis-seat.c new file mode 100644 index 0000000..fc83ba3 --- /dev/null +++ b/src/libeis-seat.c @@ -0,0 +1,130 @@ +/* + * Copyright © 2020 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "util-macros.h" +#include "util-bits.h" +#include "util-strings.h" +#include "libeis-private.h" + +static void +eis_seat_destroy(struct eis_seat *seat) +{ + struct eis_device *d; + + /* We expect those to have been removed already*/ + list_for_each(d, &seat->devices, link) { + assert(!"device list not empty"); + } + free(seat->name); +} + +_public_ +OBJECT_IMPLEMENT_REF(eis_seat); +_public_ +OBJECT_IMPLEMENT_UNREF(eis_seat); +static +OBJECT_IMPLEMENT_CREATE(eis_seat); +static +OBJECT_IMPLEMENT_PARENT(eis_seat, eis_client); +_public_ +OBJECT_IMPLEMENT_GETTER(eis_seat, user_data, void *); +_public_ +OBJECT_IMPLEMENT_SETTER(eis_seat, user_data, void *); +_public_ +OBJECT_IMPLEMENT_GETTER(eis_seat, name, const char *); + +_public_ struct eis_client * +eis_seat_get_client(struct eis_seat *seat) +{ + return eis_seat_parent(seat); +} + +_public_ struct eis_seat * +eis_client_new_seat(struct eis_client *client, const char *name) +{ + static uint32_t seatid = 0x10000; /* we leave the lower bits to the deviceids */ + struct eis_seat *seat = eis_seat_create(&client->object); + + seat->id = seatid++; + seat->state = EIS_SEAT_STATE_PENDING; + seat->name = xstrdup(name); + list_init(&seat->devices); + /* seat is owned by caller until it's added */ + list_append(&client->seats_pending, &seat->link); + + return seat; +} + +_public_ void +eis_seat_add(struct eis_seat *seat) +{ + struct eis_client *client = eis_seat_get_client(seat); + + switch (seat->state) { + case EIS_SEAT_STATE_PENDING: + break; + case EIS_SEAT_STATE_ADDED: + case EIS_SEAT_STATE_REMOVED: + log_bug_client(eis_client_get_context(client), + "%s: seat already added/removed\n", __func__); + return; + } + + seat->state = EIS_SEAT_STATE_ADDED; + eis_client_add_seat(client, seat); +} + +_public_ void +eis_seat_remove(struct eis_seat *seat) +{ + struct eis_client *client = eis_seat_get_client(seat); + + switch (seat->state) { + case EIS_SEAT_STATE_PENDING: + case EIS_SEAT_STATE_ADDED: + break; + case EIS_SEAT_STATE_REMOVED: + log_bug_client(eis_client_get_context(client), + "%s: seat already removed\n", __func__); + return; + } + + seat->state = EIS_SEAT_STATE_REMOVED; + eis_client_remove_seat(eis_seat_get_client(seat), seat); +} + +_public_ void +eis_seat_allow_capability(struct eis_seat *seat, + enum eis_device_capability cap) +{ + switch (cap) { + case EIS_DEVICE_CAP_POINTER: + case EIS_DEVICE_CAP_POINTER_ABSOLUTE: + case EIS_DEVICE_CAP_KEYBOARD: + case EIS_DEVICE_CAP_TOUCH: + seat->capabilities_mask |= bit(cap); + break; + } +} diff --git a/src/libeis.h b/src/libeis.h index 3be31c3..475147a 100644 --- a/src/libeis.h +++ b/src/libeis.h @@ -44,6 +44,7 @@ extern "C" { struct eis; struct eis_client; struct eis_device; +struct eis_seat; struct eis_event; struct eis_keymap; @@ -277,6 +278,65 @@ eis_client_connect(struct eis_client *client); void eis_client_disconnect(struct eis_client *client); +/** + * Create a new logical seat with a given name. Devices created by the + * client must bind to a seat, or in other words: a client cannot create + * devices until at least one seat is bound. + * + * This seat is not immediately active, use eis_seat_add() to bind this + * seat on the client and notify the client of it's availability. + * + * The returned seat is refcounted, use eis_seat_unref() to drop the + * reference. + */ +struct eis_seat * +eis_client_new_seat(struct eis_client *client, const char *name); + +struct eis_seat * +eis_seat_ref(struct eis_seat *seat); + +struct eis_seat * +eis_seat_unref(struct eis_seat *seat); + +struct eis_client * +eis_seat_get_client(struct eis_seat *eis_seat); + +const char * +eis_seat_get_name(struct eis_seat *eis_seat); + +void * +eis_seat_get_user_data(struct eis_seat *eis_seat); + +void +eis_seat_set_user_data(struct eis_seat *eis_seat, void *user_data); + +/** + * Allow a capability on the seat. This indicates to the client + * that it may create devices with with the given capabilities, though the + * EIS implementation may restrict the of capabilities on a device to a + * subset of those in the seat, see eis_device_allow_capability(). + * + * This function must be called before eis_seat_add(). + * + * This function has no effect if called after eis_seat_add() + */ +void +eis_seat_allow_capability(struct eis_seat *seat, + enum eis_device_capability cap); + +/** + * Add this seat to its client and notify the client of the seat's + * availability. This allows the client to create a device within this seat. + */ +void +eis_seat_add(struct eis_seat *seat); + +/** + * Remove this seat and all its remaining devices. + */ +void +eis_seat_remove(struct eis_seat *seat); + enum eis_event_type eis_event_get_type(struct eis_event *event); @@ -286,6 +346,9 @@ eis_event_get_client(struct eis_event *event); struct eis_client * eis_device_get_client(struct eis_device *device); +struct eis_seat * +eis_device_get_seat(struct eis_device *device); + struct eis_device * eis_device_ref(struct eis_device *device); @@ -323,7 +386,8 @@ eis_device_has_capability(struct eis_device *device, * eis_device_connect(). * * This function has no effect if called for a capability the device does - * not support. + * not support. This function has no effect if called for a capability not + * allowed on the seat this device belongs to. * * This function has no effect if called after eis_device_connect() */ diff --git a/test/eierpecken.c b/test/eierpecken.c index 3b9e5e5..2fcf9bd 100644 --- a/test/eierpecken.c +++ b/test/eierpecken.c @@ -48,6 +48,11 @@ struct peck { uint32_t ei_behavior; struct logger *logger; + struct eis_seat *eis_seat; + struct ei_seat *ei_seat; + + struct eis_client *eis_client; + uint32_t indent; }; @@ -76,6 +81,11 @@ peck_destroy(struct peck *peck) peck_drain_ei(peck->ei); peck_drain_eis(peck->eis); + eis_client_unref(peck->eis_client); + + ei_seat_unref(peck->ei_seat); + eis_seat_unref(peck->eis_seat); + ei_unref(peck->ei); eis_unref(peck->eis); logger_unref(peck->logger); @@ -87,6 +97,27 @@ OBJECT_IMPLEMENT_UNREF(peck); OBJECT_IMPLEMENT_GETTER(peck, ei, struct ei*); OBJECT_IMPLEMENT_GETTER(peck, eis, struct eis*); +struct eis_client * +peck_eis_get_default_client(struct peck *peck) +{ + munit_assert_ptr_not_null(peck->eis_client); + return peck->eis_client; +}; + +struct eis_seat * +peck_eis_get_default_seat(struct peck *peck) +{ + munit_assert_ptr_not_null(peck->eis_seat); + return peck->eis_seat; +}; + +struct ei_seat * +peck_ei_get_default_seat(struct peck *peck) +{ + munit_assert_ptr_not_null(peck->ei_seat); + return peck->ei_seat; +} + static void peck_ei_log_handler(struct ei *ei, enum ei_log_priority priority, @@ -178,6 +209,7 @@ peck_new(void) munit_assert_int(rc, ==, 0); peck->ei = ei; flag_set(peck->ei_behavior, PECK_EI_BEHAVIOR_AUTOCONNNECT); + flag_set(peck->ei_behavior, PECK_EI_BEHAVIOR_AUTOSEAT); struct eis *eis = eis_new(peck); eis_log_set_handler(eis, peck_eis_log_handler); @@ -203,7 +235,12 @@ peck_enable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior) case PECK_EIS_BEHAVIOR_NONE: peck->eis_behavior = 0; break; + case PECK_EIS_BEHAVIOR_DEFAULT_SEAT: + case PECK_EIS_BEHAVIOR_NO_DEFAULT_SEAT: + flag_set(peck->eis_behavior, behavior); + break; case PECK_EIS_BEHAVIOR_ACCEPT_ALL: + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_DEVICE); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_RESUME_DEVICE); @@ -266,10 +303,12 @@ peck_enable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior) peck->ei_behavior = 0; break; case PECK_EI_BEHAVIOR_AUTOCONNNECT: + case PECK_EI_BEHAVIOR_AUTOSEAT: flag_set(peck->ei_behavior, behavior); break; case PECK_EI_BEHAVIOR_AUTODEVICES: peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOCONNNECT); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOSEAT); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_RESUMED); break; @@ -292,6 +331,22 @@ peck_enable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior) } } +static inline void +peck_create_eis_seat(struct peck *peck, struct eis_client *client) +{ + _cleanup_eis_seat_ struct eis_seat *seat = eis_client_new_seat(client, "default"); + + eis_seat_allow_capability(seat, EIS_DEVICE_CAP_POINTER); + eis_seat_allow_capability(seat, EIS_DEVICE_CAP_POINTER_ABSOLUTE); + eis_seat_allow_capability(seat, EIS_DEVICE_CAP_KEYBOARD); + eis_seat_allow_capability(seat, EIS_DEVICE_CAP_TOUCH); + + log_debug(peck, "EIS adding seat: '%s'\n", eis_seat_get_name(seat)); + eis_seat_add(seat); + + peck->eis_seat = eis_seat_ref(seat); +} + static inline void peck_handle_eis_connect(struct peck *peck, struct eis_event *e) { @@ -299,7 +354,10 @@ peck_handle_eis_connect(struct peck *peck, struct eis_event *e) if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT)) { log_debug(peck, "EIS accepting client: '%s'\n", eis_client_get_name(client)); + peck->eis_client = eis_client_ref(client); eis_client_connect(client); + if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_DEFAULT_SEAT)) + peck_create_eis_seat(peck, client); } else { log_debug(peck, "EIS disconnecting client: '%s'\n", eis_client_get_name(client)); eis_client_disconnect(client); @@ -470,6 +528,12 @@ _peck_dispatch_ei(struct peck *peck, int lineno) PECK_EI_BEHAVIOR_AUTOCONNNECT)) process_event = tristate_yes; break; + case EI_EVENT_SEAT_ADDED: + if (peck->ei_seat == NULL && + flag_is_set(peck->ei_behavior, + PECK_EI_BEHAVIOR_AUTOSEAT)) + process_event = tristate_yes; + break; case EI_EVENT_DEVICE_ADDED: process_event = peck_check_ei_added(peck, e); break; @@ -504,6 +568,14 @@ _peck_dispatch_ei(struct peck *peck, int lineno) log_debug(peck, "ei is connected\n"); /* Nothing to do here */ break; + case EI_EVENT_SEAT_ADDED: + { + struct ei_seat *seat = ei_event_get_seat(e); + munit_assert_ptr_null(peck->ei_seat); + peck->ei_seat = ei_seat_ref(seat); + log_debug(peck, "default seat: %s\n"); + break; + } case EI_EVENT_DEVICE_ADDED: case EI_EVENT_DEVICE_RESUMED: case EI_EVENT_DEVICE_SUSPENDED: @@ -644,12 +716,14 @@ peck_ei_event_type_name(enum ei_event_type type) switch (type) { CASE_STRING(CONNECT); CASE_STRING(DISCONNECT); + CASE_STRING(SEAT_ADDED); + CASE_STRING(SEAT_REMOVED); CASE_STRING(DEVICE_ADDED); CASE_STRING(DEVICE_REMOVED); CASE_STRING(DEVICE_SUSPENDED); CASE_STRING(DEVICE_RESUMED); default: - abort(); + assert(!"Unhandled ei event type"); } #undef CASE_STRING } @@ -679,7 +753,7 @@ peck_eis_event_type_name(enum eis_event_type type) CASE_STRING(TOUCH_UP); CASE_STRING(TOUCH_MOTION); default: - abort(); + assert(!"Unhandled EIS event type"); } #undef CASE_STRING } diff --git a/test/eierpecken.h b/test/eierpecken.h index f0035a0..991e40d 100644 --- a/test/eierpecken.h +++ b/test/eierpecken.h @@ -42,9 +42,19 @@ enum peck_eis_behavior { * Behavior of EIS is implemented in the test case. */ PECK_EIS_BEHAVIOR_NONE, + /** + * Create a "default" seat. This behavior is enabled as part of + * PECK_EIS_BEHAVIOR_ACCEPT_ALL. + */ + PECK_EIS_BEHAVIOR_DEFAULT_SEAT, + /** + * Do not create a "default" seat. + */ + PECK_EIS_BEHAVIOR_NO_DEFAULT_SEAT, /** * Accept all client connection requests and device additions * **and** resume any device immediately after add. + * This also enables @ref PECK_EIS_BEHAVIOR_DEFAULT_SEAT. */ PECK_EIS_BEHAVIOR_ACCEPT_ALL, /** @@ -83,10 +93,13 @@ enum peck_eis_behavior { enum peck_ei_behavior { PECK_EI_BEHAVIOR_NONE, - /* the default - handle the Connect event */ + /* enabled by default - handle the Connect event */ PECK_EI_BEHAVIOR_AUTOCONNNECT, - /* handle Connect/Added/Resumed events, i.e. anything until - * the first real device event */ + /* enabled by default - handle the first seat added event, setting + * the default seat to the first seat */ + PECK_EI_BEHAVIOR_AUTOSEAT, + /* handle Connect/Seat/Added/Resumed events, i.e. anything until the + * first real device event */ PECK_EI_BEHAVIOR_AUTODEVICES, PECK_EI_BEHAVIOR_HANDLE_ADDED, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER, @@ -115,6 +128,15 @@ peck_get_ei(struct peck *peck); struct eis * peck_get_eis(struct peck *peck); +struct eis_client * +peck_eis_get_default_client(struct peck *peck); + +struct eis_seat * +peck_eis_get_default_seat(struct peck *peck); + +struct ei_seat * +peck_ei_get_default_seat(struct peck *peck); + /** * Dispatch all events according to the currently defined behavior. * When this function returns false, the connection is in a "stable" state @@ -235,6 +257,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis_device *, eis_device_unref); #define _cleanup_eis_device_ _cleanup_(eis_device_unrefp) DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis_keymap *, eis_keymap_unref); #define _cleanup_eis_keymap_ _cleanup_(eis_keymap_unrefp) +DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis_seat *, eis_seat_unref); +#define _cleanup_eis_seat_ _cleanup_(eis_seat_unrefp) /* Macros intended just for readability to make it more obvious which part of a test handles server vs client */ diff --git a/test/test-ei.c b/test/test-ei.c index d234d07..b6427fb 100644 --- a/test/test-ei.c +++ b/test/test-ei.c @@ -87,20 +87,12 @@ MUNIT_TEST(test_ei_disconnect_after_add_before_received) _cleanup_peck_ struct peck *peck = peck_new(); _cleanup_eis_client_ struct eis_client *client = NULL; - peck_dispatch_until_stable(peck); - - with_server(peck) { - eis_dispatch(eis); - _cleanup_eis_event_ struct eis_event *e = - peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); - client = eis_client_ref(eis_event_get_client(e)); - eis_client_connect(client); - } - + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_dispatch_until_stable(peck); with_client(peck) { - _cleanup_ei_device_ struct ei_device *device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + _cleanup_ei_device_ struct ei_device *device = ei_device_new(seat); ei_device_configure_name(device, __func__); ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER); ei_device_add(device); @@ -109,13 +101,16 @@ MUNIT_TEST(test_ei_disconnect_after_add_before_received) /* We have *not* called eis_dispatch, so the device add hasn't been * processed by the server yet */ with_server(peck) { + struct eis_client *client = peck_eis_get_default_client(peck); eis_client_disconnect(client); } with_client(peck) { ei_dispatch(ei); - _cleanup_ei_event_ struct ei_event *removed = + _cleanup_ei_event_ struct ei_event *device_removed = peck_ei_next_event(ei, EI_EVENT_DEVICE_REMOVED); + _cleanup_ei_event_ struct ei_event *seat_removed = + peck_ei_next_event(ei, EI_EVENT_SEAT_REMOVED); _cleanup_ei_event_ struct ei_event *disconnect = peck_ei_next_event(ei, EI_EVENT_DISCONNECT); } @@ -128,20 +123,13 @@ MUNIT_TEST(test_ei_disconnect_after_add_after_received) _cleanup_peck_ struct peck *peck = peck_new(); _cleanup_eis_client_ struct eis_client *client = NULL; - peck_dispatch_until_stable(peck); - - with_server(peck) { - eis_dispatch(eis); - _cleanup_eis_event_ struct eis_event *e = - peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); - client = eis_client_ref(eis_event_get_client(e)); - eis_client_connect(client); - } - + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_dispatch_until_stable(peck); with_client(peck) { - _cleanup_ei_device_ struct ei_device *device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + _cleanup_ei_device_ struct ei_device *device = ei_device_new(seat); ei_device_configure_name(device, __func__); ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER); ei_device_add(device); @@ -151,13 +139,16 @@ MUNIT_TEST(test_ei_disconnect_after_add_after_received) peck_dispatch_eis(peck); with_server(peck) { + struct eis_client *client = peck_eis_get_default_client(peck); eis_client_disconnect(client); } with_client(peck) { ei_dispatch(ei); - _cleanup_ei_event_ struct ei_event *removed = + _cleanup_ei_event_ struct ei_event *device_removed = peck_ei_next_event(ei, EI_EVENT_DEVICE_REMOVED); + _cleanup_ei_event_ struct ei_event *seat_removed = + peck_ei_next_event(ei, EI_EVENT_SEAT_REMOVED); _cleanup_ei_event_ struct ei_event *disconnect = peck_ei_next_event(ei, EI_EVENT_DISCONNECT); } @@ -168,29 +159,22 @@ MUNIT_TEST(test_ei_disconnect_after_add_after_received) MUNIT_TEST(test_ei_disconnect_after_remove_before_received) { _cleanup_peck_ struct peck *peck = peck_new(); - _cleanup_eis_client_ struct eis_client *client = NULL; _cleanup_ei_device_ struct ei_device *device = NULL; - peck_dispatch_until_stable(peck); - - with_server(peck) { - eis_dispatch(eis); - _cleanup_eis_event_ struct eis_event *e = - peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); - client = eis_client_ref(eis_event_get_client(e)); - eis_client_connect(client); - } - - peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_dispatch_until_stable(peck); with_client(peck) { - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); ei_device_configure_name(device, __func__); ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER); ei_device_add(device); } + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); + /* server has the device now */ peck_dispatch_until_stable(peck); @@ -206,13 +190,16 @@ MUNIT_TEST(test_ei_disconnect_after_remove_before_received) * ei_device_remove() call. Disconnect the client, this * automatically removes all devices */ with_server(peck) { + struct eis_client *client = peck_eis_get_default_client(peck); eis_client_disconnect(client); } with_client(peck) { ei_dispatch(ei); - _cleanup_ei_event_ struct ei_event *removed = + _cleanup_ei_event_ struct ei_event *device_removed = peck_ei_next_event(ei, EI_EVENT_DEVICE_REMOVED); + _cleanup_ei_event_ struct ei_event *seat_removed = + peck_ei_next_event(ei, EI_EVENT_SEAT_REMOVED); _cleanup_ei_event_ struct ei_event *disconnect = peck_ei_next_event(ei, EI_EVENT_DISCONNECT); } @@ -223,24 +210,14 @@ MUNIT_TEST(test_ei_disconnect_after_remove_before_received) MUNIT_TEST(test_ei_disconnect_after_remove_after_received) { _cleanup_peck_ struct peck *peck = peck_new(); - _cleanup_eis_client_ struct eis_client *client = NULL; _cleanup_ei_device_ struct ei_device *device = NULL; - peck_dispatch_until_stable(peck); - - with_server(peck) { - eis_dispatch(eis); - _cleanup_eis_event_ struct eis_event *e = - peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); - client = eis_client_ref(eis_event_get_client(e)); - eis_client_connect(client); - } - peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_dispatch_until_stable(peck); with_client(peck) { - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); ei_device_configure_name(device, __func__); ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER); ei_device_add(device); @@ -260,14 +237,18 @@ MUNIT_TEST(test_ei_disconnect_after_remove_after_received) /* Dispatch, server is aware of the ei_device_remove() */ peck_dispatch_eis(peck); with_server(peck) { + struct eis_client *client = peck_eis_get_default_client(peck); eis_client_disconnect(client); } with_client(peck) { ei_dispatch(ei); - _cleanup_ei_event_ struct ei_event *removed = + _cleanup_ei_event_ struct ei_event *device_removed = peck_ei_next_event(ei, EI_EVENT_DEVICE_REMOVED); + _cleanup_ei_event_ struct ei_event *seat_removed = + peck_ei_next_event(ei, EI_EVENT_SEAT_REMOVED); + _cleanup_ei_event_ struct ei_event *disconnect = peck_ei_next_event(ei, EI_EVENT_DISCONNECT); } @@ -285,7 +266,8 @@ MUNIT_TEST(test_ei_device_basics) /* device creation and getters/setters test */ with_client(peck) { - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); munit_assert_not_null(device); ei_device_configure_name(device, __func__); munit_assert_string_equal(ei_device_get_name(device), __func__); @@ -329,21 +311,23 @@ MUNIT_TEST(test_ei_device_set_name) _cleanup_ei_device_ struct ei_device *d3 = NULL; peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_dispatch_until_stable(peck); with_client(peck) { - d1 = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + d1 = ei_device_new(seat); ei_device_configure_name(d1, "first device"); ei_device_configure_capability(d1, EI_DEVICE_CAP_POINTER); ei_device_add(d1); - d2 = ei_device_new(ei); + d2 = ei_device_new(seat); munit_assert_not_null(d2); ei_device_configure_name(d2, "second device"); ei_device_configure_capability(d2, EI_DEVICE_CAP_POINTER); ei_device_add(d2); - d3 = ei_device_new(ei); + d3 = ei_device_new(seat); munit_assert_not_null(d3); ei_device_configure_name(d3, "third device"); ei_device_configure_capability(d3, EI_DEVICE_CAP_POINTER); @@ -411,19 +395,15 @@ MUNIT_TEST(test_ei_device_add_remove) { _cleanup_peck_ struct peck *peck = peck_new(); - with_server(peck) { - peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); - peck_dispatch_eis(peck); - } - - with_client(peck) { - peck_drain_ei(ei); - } + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); + peck_dispatch_until_stable(peck); _cleanup_ei_device_ struct ei_device *device = NULL; with_client(peck) { - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); ei_device_configure_name(device, __func__); ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER); ei_device_add(device); @@ -490,12 +470,13 @@ MUNIT_TEST(test_ei_device_remove_forget_disconnect) _cleanup_ei_device_ struct ei_device *d2 = NULL; with_client(peck) { - d1 = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + d1 = ei_device_new(seat); ei_device_configure_name(d1, "first"); ei_device_configure_capability(d1, EI_DEVICE_CAP_POINTER); ei_device_add(d1); - d2 = ei_device_new(ei); + d2 = ei_device_new(seat); ei_device_configure_name(d2, "second"); ei_device_configure_capability(d2, EI_DEVICE_CAP_KEYBOARD); ei_device_add(d2); @@ -564,7 +545,8 @@ MUNIT_TEST(test_ei_device_add_drop_caps) _cleanup_ei_device_ struct ei_device *device = NULL; with_client(peck) { - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); ei_device_configure_name(device, __func__); ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER); ei_device_configure_capability(device, EI_DEVICE_CAP_KEYBOARD); @@ -602,7 +584,8 @@ MUNIT_TEST(test_ei_device_add_zero_caps) _cleanup_ei_device_ struct ei_device *device = NULL; with_client(peck) { - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); ei_device_configure_name(device, __func__); ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER); ei_device_add(device); @@ -630,7 +613,8 @@ MUNIT_TEST(test_ei_device_pointer_rel) peck_dispatch_until_stable(peck); with_client(peck) { - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); munit_assert_not_null(device); ei_device_configure_name(device, __func__); @@ -674,11 +658,13 @@ MUNIT_TEST(test_ei_device_pointer_abs) _cleanup_ei_device_ struct ei_device *device = NULL; peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES); peck_dispatch_until_stable(peck); with_client(peck) { - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); munit_assert_not_null(device); ei_device_configure_name(device, __func__); @@ -746,7 +732,8 @@ MUNIT_TEST(test_ei_device_touch) peck_dispatch_until_stable(peck); with_client(peck) { - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); munit_assert_not_null(device); ei_device_configure_name(device, __func__); @@ -888,7 +875,8 @@ MUNIT_TEST(test_ei_device_multitouch) peck_dispatch_until_stable(peck); with_client(peck) { - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); munit_assert_not_null(device); ei_device_configure_name(device, __func__); @@ -969,13 +957,15 @@ MUNIT_TEST(test_ei_keymap_set) _cleanup_ei_keymap_ struct ei_keymap *keymap = NULL; peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES); peck_dispatch_until_stable(peck); with_client(peck) { _cleanup_memfile_ struct memfile *fd2 = memfile_new(data, sizeof(data)); - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); ei_device_configure_name(device, __func__); ei_device_configure_capability(device, EI_DEVICE_CAP_KEYBOARD); @@ -1038,11 +1028,13 @@ MUNIT_TEST(test_ei_keymap_null) _cleanup_memfile_ struct memfile *fd = memfile_new(data, sizeof(data)); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOCONNNECT); peck_dispatch_until_stable(peck); with_client(peck) { - _cleanup_ei_device_ struct ei_device *device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + _cleanup_ei_device_ struct ei_device *device = ei_device_new(seat); ei_device_configure_name(device, __func__); ei_device_configure_capability(device, EI_DEVICE_CAP_KEYBOARD); @@ -1085,6 +1077,7 @@ MUNIT_TEST(test_ei_keymap_changed) _cleanup_memfile_ struct memfile *fd1 = memfile_new(data, sizeof(data)); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOCONNNECT); peck_dispatch_until_stable(peck); @@ -1093,7 +1086,8 @@ MUNIT_TEST(test_ei_keymap_changed) const char otherdata[2] = {10, 20}; _cleanup_memfile_ struct memfile *fd2 = memfile_new(data, sizeof(otherdata)); - device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + device = ei_device_new(seat); ei_device_configure_name(device, __func__); ei_device_configure_capability(device, EI_DEVICE_CAP_KEYBOARD); diff --git a/test/test-eis.c b/test/test-eis.c index 1a74690..2b20733 100644 --- a/test/test-eis.c +++ b/test/test-eis.c @@ -79,11 +79,13 @@ MUNIT_TEST(eistest_ranges) _cleanup_peck_ struct peck *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_dispatch_until_stable(peck); with_client(peck) { - _cleanup_ei_device_ struct ei_device *d = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + _cleanup_ei_device_ struct ei_device *d = ei_device_new(seat); ei_device_configure_capability(d, EI_DEVICE_CAP_POINTER_ABSOLUTE); ei_device_configure_capability(d, EI_DEVICE_CAP_TOUCH); ei_device_pointer_configure_range(d, 1024, 768); @@ -115,10 +117,12 @@ MUNIT_TEST(eistest_device_resume_suspend_twice) _cleanup_eis_device_ struct eis_device *device = NULL; peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_dispatch_until_stable(peck); with_client(peck) { - _cleanup_ei_device_ struct ei_device *d = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + _cleanup_ei_device_ struct ei_device *d = ei_device_new(seat); ei_device_configure_capability(d, EI_DEVICE_CAP_POINTER); ei_device_add(d); } @@ -181,10 +185,12 @@ MUNIT_TEST(eistest_device_ignore_suspended) _cleanup_eis_device_ struct eis_device *device = NULL; peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_dispatch_until_stable(peck); with_client(peck) { - _cleanup_ei_device_ struct ei_device *d = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + _cleanup_ei_device_ struct ei_device *d = ei_device_new(seat); ei_device_configure_capability(d, EI_DEVICE_CAP_POINTER); ei_device_add(d); } @@ -264,10 +270,12 @@ MUNIT_TEST(eistest_device_late_connect) _cleanup_ei_device_ struct ei_device *ei_device = NULL; peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_dispatch_until_stable(peck); with_client(peck) { - ei_device = ei_device_new(ei); + struct ei_seat *seat = peck_ei_get_default_seat(peck); + ei_device = ei_device_new(seat); ei_device_configure_capability(ei_device, EI_DEVICE_CAP_POINTER); ei_device_add(ei_device); } diff --git a/tools/ei-demo-client.c b/tools/ei-demo-client.c index 53db66d..9b8d868 100644 --- a/tools/ei-demo-client.c +++ b/tools/ei-demo-client.c @@ -247,18 +247,14 @@ int main(int argc, char **argv) .events = POLLIN, .revents = 0, }; - - _cleanup_(ei_device_unrefp) struct ei_device *ptr = ei_device_new(ei); - ei_device_configure_capability(ptr, EI_DEVICE_CAP_POINTER); - - _cleanup_(ei_device_unrefp) struct ei_device *kbd = ei_device_new(ei); - ei_device_configure_capability(kbd, EI_DEVICE_CAP_KEYBOARD); - if (layout) - setup_keymap(kbd, layout); + _cleanup_(ei_device_unrefp) struct ei_device *ptr = NULL; + _cleanup_(ei_device_unrefp) struct ei_device *kbd = NULL; bool stop = false; bool have_ptr = false; bool have_kbd = false; + struct ei_seat *default_seat = NULL; + while (!stop && poll(&fds, 1, 2000) > -1) { ei_dispatch(ei); @@ -270,8 +266,6 @@ int main(int argc, char **argv) switch(ei_event_get_type(e)) { case EI_EVENT_CONNECT: colorprint("connected\n"); - ei_device_add(ptr); - ei_device_add(kbd); break; case EI_EVENT_DISCONNECT: { @@ -279,6 +273,30 @@ int main(int argc, char **argv) stop = true; break; } + case EI_EVENT_SEAT_ADDED: + { + if (default_seat) { + colorprint("ignoring other seats\n"); + break; + } + default_seat = ei_seat_ref(ei_event_get_seat(e)); + colorprint("seat added: %s\n", ei_seat_get_name(default_seat)); + ptr = ei_device_new(default_seat); + ei_device_configure_capability(ptr, EI_DEVICE_CAP_POINTER); + + kbd = ei_device_new(default_seat); + ei_device_configure_capability(kbd, EI_DEVICE_CAP_KEYBOARD); + if (layout) + setup_keymap(kbd, layout); + + ei_device_add(ptr); + ei_device_add(kbd); + break; + } + case EI_EVENT_SEAT_REMOVED: + if (ei_event_get_seat(e) == default_seat) + default_seat = ei_seat_unref(default_seat); + break; case EI_EVENT_DEVICE_ADDED: colorprint("our device was accepted, waiting for resume\n"); handle_keymap(e); diff --git a/tools/eis-demo-server.c b/tools/eis-demo-server.c index 27f352f..1cadd90 100644 --- a/tools/eis-demo-server.c +++ b/tools/eis-demo-server.c @@ -55,6 +55,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis_event *, eis_event_unref); #define _cleanup_eis_event_ _cleanup_(eis_event_unrefp) DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis_keymap *, eis_keymap_unref); #define _cleanup_eis_keymap_ _cleanup_(eis_keymap_unrefp) +DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis_seat *, eis_seat_unref); +#define _cleanup_eis_seat_ _cleanup_(eis_seat_unrefp) static void unlink_free(char **path) { if (*path) { @@ -192,7 +194,13 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server, colorprint("new client: %s\n", eis_client_get_name(client)); /* insert sophisticated authentication here */ eis_client_connect(client); - colorprint("accepting client\n"); + colorprint("accepting client, creating new seat 'default'\n"); + _cleanup_eis_seat_ struct eis_seat *seat = eis_client_new_seat(client, "default"); + eis_seat_allow_capability(seat, EIS_DEVICE_CAP_POINTER); + eis_seat_allow_capability(seat, EIS_DEVICE_CAP_POINTER_ABSOLUTE); + eis_seat_allow_capability(seat, EIS_DEVICE_CAP_KEYBOARD); + eis_seat_allow_capability(seat, EIS_DEVICE_CAP_TOUCH); + eis_seat_add(seat); break; } case EIS_EVENT_CLIENT_DISCONNECT: