diff --git a/src/libei-device.c b/src/libei-device.c index 1926b6b..b48ef0a 100644 --- a/src/libei-device.c +++ b/src/libei-device.c @@ -60,8 +60,8 @@ ei_device_set_state(struct ei_device *device, static void ei_device_destroy(struct ei_device *device) { + ei_keymap_unref(device->keymap); free(device->name); - xclose(device->keymap.fd); } _public_ @@ -101,10 +101,6 @@ ei_device_new(struct ei *ei) device->id = ++deviceid; device->state = EI_DEVICE_STATE_NEW; device->name = xaprintf("unnamed device %d", device->id); - device->keymap.source = EI_KEYMAP_SOURCE_SERVER; - device->keymap.fd = -1; - device->keymap.type = EI_KEYMAP_TYPE_NONE; - device->keymap.size = 0; return device; } @@ -170,31 +166,96 @@ ei_device_touch_configure_range(struct ei_device *device, device->touch.dim.height = height; } +_public_ +OBJECT_IMPLEMENT_REF(ei_keymap); +_public_ +OBJECT_IMPLEMENT_UNREF(ei_keymap); +#define _cleanup_ei_keymap_ _cleanup_(ei_keymap_cleanup) +_public_ +OBJECT_IMPLEMENT_GETTER(ei_keymap, type, enum ei_keymap_type); +_public_ +OBJECT_IMPLEMENT_GETTER(ei_keymap, source, enum ei_keymap_source); +_public_ +OBJECT_IMPLEMENT_GETTER(ei_keymap, fd, int); +_public_ +OBJECT_IMPLEMENT_GETTER(ei_keymap, size, size_t); + +_public_ struct ei_device * +ei_keymap_get_device(struct ei_keymap *keymap) +{ + return keymap->device; +} + +static void +ei_keymap_destroy(struct ei_keymap *keymap) +{ + xclose(keymap->fd); +} + +static +OBJECT_IMPLEMENT_CREATE(ei_keymap); + +_public_ struct ei_keymap * +ei_device_keyboard_get_keymap(struct ei_device *device) +{ + return device->keymap; +} + _public_ void ei_device_keyboard_configure_keymap(struct ei_device *device, - enum ei_keymap_type type, - int fd, size_t size) + struct ei_keymap *keymap) { - if (device->state != EI_DEVICE_STATE_NEW || - !flag_is_set(device->capabilities, EI_DEVICE_CAP_KEYBOARD)) + if (!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD)) return; - if (fd < 0) + if (device->state != EI_DEVICE_STATE_NEW) return; - /* - * This can be a quiet error because it should never happen - * anyway and if it does, the device will just have a -1 keymap - * after being accepted by the server. - */ + /* Can only call this once */ + if (device->keymap) { + log_bug(ei_device_get_context(device), + "Device already has a keymap\n"); + return; + } + + if (!keymap) + return; + + /* Reject any previously used keymaps */ + if (keymap->assigned) + return; + + keymap->assigned = true; + keymap->source = EI_KEYMAP_SOURCE_CLIENT; + keymap->device = device; + device->keymap = ei_keymap_ref(keymap); +} + +_public_ struct ei_keymap * +ei_keymap_new(enum ei_keymap_type type, int fd, size_t size) +{ + + _cleanup_ei_keymap_ struct ei_keymap *keymap = ei_keymap_create(NULL); + + switch (type) { + case EI_KEYMAP_TYPE_XKB: + break; + default: + return NULL; + } + + if (fd < 0 || size == 0) + return NULL; + int newfd = dup(fd); if (newfd < 0) - return; + return NULL; - device->keymap.source = EI_KEYMAP_SOURCE_CLIENT; - device->keymap.fd = newfd; - device->keymap.type = type; - device->keymap.size = size; + keymap->fd = newfd; + keymap->type = type; + keymap->size = size; + + return ei_keymap_ref(keymap); } void @@ -202,10 +263,15 @@ ei_device_set_keymap(struct ei_device *device, enum ei_keymap_type type, int keymap_fd, size_t size) { - device->keymap.source = EI_KEYMAP_SOURCE_SERVER; - device->keymap.fd = xclose(device->keymap.fd); - device->keymap.type = type; - device->keymap.size = size; + device->keymap = ei_keymap_unref(device->keymap); + + _cleanup_ei_keymap_ struct ei_keymap *keymap = ei_keymap_new(type, keymap_fd, size); + if (!keymap) + return; /* FIXME: ei_device_remove() here */ + + keymap->device = device; + keymap->source = EI_KEYMAP_SOURCE_SERVER; + device->keymap = ei_keymap_ref(keymap); } _public_ void @@ -306,30 +372,6 @@ ei_device_touch_get_height(struct ei_device *device) return device->touch.dim.height; } -_public_ enum ei_keymap_source -ei_device_keyboard_get_keymap_source(struct ei_device *device) -{ - return device->keymap.source; -} - -_public_ enum ei_keymap_type -ei_device_keyboard_get_keymap_type(struct ei_device *device) -{ - return device->keymap.type; -} - -_public_ int -ei_device_keyboard_get_keymap(struct ei_device *device) -{ - return device->keymap.fd; -} - -_public_ size_t -ei_device_keyboard_get_keymap_size(struct ei_device *device) -{ - return device->keymap.size; -} - _public_ void ei_device_pointer_motion(struct ei_device *device, double x, double y) @@ -469,6 +511,7 @@ ei_touch_up(struct ei_touch *touch) #ifdef _enable_tests_ #include "src/util-munit.h" +#include "src/util-memfile.h" MUNIT_TEST(test_device_new) { @@ -620,4 +663,5 @@ MUNIT_TEST(test_device_touch_ranges) return MUNIT_OK; } + #endif diff --git a/src/libei-private.h b/src/libei-private.h index 2c50046..e2cf6a3 100644 --- a/src/libei-private.h +++ b/src/libei-private.h @@ -89,12 +89,17 @@ struct ei_device { struct dimensions dim; } touch; - struct { - enum ei_keymap_source source; - enum ei_keymap_type type; - int fd; - size_t size; - } keymap; + struct ei_keymap *keymap; +}; + +struct ei_keymap { + struct object object; + struct ei_device *device; + enum ei_keymap_source source; + enum ei_keymap_type type; + int fd; + size_t size; + bool assigned; }; struct ei_touch { diff --git a/src/libei-proto.c b/src/libei-proto.c index 0486f25..01e3640 100644 --- a/src/libei-proto.c +++ b/src/libei-proto.c @@ -90,8 +90,7 @@ ei_proto_parse_message(struct brei_message *bmsg, size_t *consumed) .added.keymap_from_server = a->keymap_from_server, .added.keymap_size = a->keymap_size, }; - if (a->keymap_type != EI_KEYMAP_TYPE_NONE && - a->keymap_from_server) + if (a->keymap_type && a->keymap_from_server) msg->added.keymap_fd = brei_message_take_fd(bmsg); } break; @@ -237,14 +236,20 @@ ei_proto_send_add(struct ei *ei, struct ei_device *device) add.pointer_height = device->abs.dim.height; add.touch_width = device->touch.dim.width; add.touch_height = device->touch.dim.height; - add.keymap_type = device->keymap.type; - add.keymap_size = device->keymap.size; + add.keymap_type = 0; + + int fd[2] = {-1, -1}; + + struct ei_keymap *keymap = device->keymap; + if (keymap) { + add.keymap_type = ei_keymap_get_type(keymap); + add.keymap_size = ei_keymap_get_size(keymap); + fd[0] = ei_keymap_get_fd(keymap); + } msg.add_device = &add; msg.msg_case = CLIENT_MESSAGE__MSG_ADD_DEVICE; - int fd[2] = {device->keymap.fd, -1}; - return ei_proto_send_msg_with_fds(ei, &msg, fd); } diff --git a/src/libei.h b/src/libei.h index 28e26c1..6202e95 100644 --- a/src/libei.h +++ b/src/libei.h @@ -74,6 +74,15 @@ struct ei_device; */ struct ei_event; +/** + * @struct ei_keymap + * + * An keymap for a device with the @ref EI_DEVICE_CAP_KEYBOARD capability. + * + * An @ref ei_keymap is refcounted, see ei_keymap_unref(). + */ +struct ei_keymap; + /** * @enum ei_device_capability * @@ -100,23 +109,19 @@ enum ei_device_capability { /** * @enum ei_keymap_type * - * The set of supported keymap types. + * The set of supported keymap types for a struct @ref ei_keymap. */ enum ei_keymap_type { - /** - * No explicit keymap has been assigned. - */ - EI_KEYMAP_TYPE_NONE = 0, /** * A libxkbcommon-compatible XKB keymap. */ - EI_KEYMAP_TYPE_XKB, + EI_KEYMAP_TYPE_XKB = 1, }; /** * @enum ei_keymap_source * - * Identifies who provided the device's keymap. + * Identifies who provided a struct @ref ei_keymap. */ enum ei_keymap_source { /** @@ -531,32 +536,129 @@ ei_device_touch_configure_range(struct ei_device *device, uint32_t width, uint32_t height); + /** - * Set the keymap for this device. The keymap for this device is a suggestion to - * the server, the actual keymap used by this device is provided with the - * @ref EI_EVENT_DEVICE_ADDED event. It is the responsibility of the client - * to handle the situation where a specific keymap is requested but the - * server does not allow its use on the device. + * Create a new keymap of the given @p type. This keymap does not immediately + * apply to the device, use ei_device_keyboard_configure_keymap() to apply + * this keymap. A keymap may only be applied once and to a single device. + * + * The returned keymap has a refcount of at least 1, use ei_keymap_unref() + * to release resources associated with this keymap. + * + * @param type The type of the keymap. + * @param fd A memmap-able file descriptor of size @p size pointing to the + * keymap used by this device. @p fd can be closed by the caller after this + * function completes. + * @param size The size of the data at @p fd in bytes + * + * @return A keymap object or `NULL` on failure. + */ +struct ei_keymap * +ei_keymap_new(enum ei_keymap_type type, int fd, size_t size); + +/** + * @return the size of the keymap in bytes + */ +size_t +ei_keymap_get_size(struct ei_keymap *keymap); + +/** + * Returns the type for this keymap. The type specifies how to interpret the + * data at the file descriptor returned by ei_keymap_get_fd(). + */ +enum ei_keymap_type +ei_keymap_get_type(struct ei_keymap *keymap); + +/** + * Return a memmap-able file descriptor pointing to the keymap used by the + * device. The keymap is constant for the lifetime of the device and + * assigned to this device individually. + */ +int +ei_keymap_get_fd(struct ei_keymap *keymap); + +/** + * Returns the source for the keymap on this device, if any. This is a + * convenience function for the client to check if its keymap was accepted. + * + * Where ei_device_keyboard_get_keymap() returns a value other `NULL` and + * this function returns @ref EI_KEYMAP_SOURCE_CLIENT, the keymap is the one + * provided with ei_device_keyboard_configure_keymap(). + * + * Where ei_device_keyboard_get_keymap() returns a value other than `NULL` + * and this function returns @ref EI_KEYMAP_SOURCE_SERVER, the keymap is one + * created by the server and **not** the one provided with + * ei_device_keyboard_configure_keymap(). + */ +enum ei_keymap_source +ei_keymap_get_source(struct ei_keymap *keymap); + +/** + * Return the device this keymap belongs to, or `NULL` if it has not yet + * been assigned to a device. + * + * Between ei_device_keyboard_configure_keymap() and libei processing an + * @ref EI_EVENT_DEVICE_ADDED event, the returned device is the one provided + * in ei_device_keyboard_configure_keymap(). + * + * After processing and if the server changed the keymap or set the keymap + * to NULL, this keymap may no longer be in use by the device and future + * calls to this function return `NULL`. + */ +struct ei_device * +ei_keymap_get_device(struct ei_keymap *keymap); + +/** + * Increase the refcount of this struct by one. Use ei_keymap_unref() to + * decrease the refcount. + * + * @return the argument passed into the function + */ +struct ei_keymap * +ei_keymap_ref(struct ei_keymap *keymap); + +/** + * Decrease the refcount of this struct by one. When the refcount reaches + * zero, the context disconnects from the server and all allocated resources + * are released. + * + * @return always NULL + */ +struct ei_keymap * +ei_keymap_unref(struct ei_keymap *keymap); + +/** + * Request this keymap for this device with the @ref EI_DEVICE_CAP_KEYBOARD + * capability. + * + * The keymap for this device is a suggestion to the server, the actual + * keymap used by this device is provided with the @ref + * EI_EVENT_DEVICE_ADDED event. It is the client's responsibility to + * handle the situation where the server assigns a specific keymap (or none) + * that differs to the requested keymap. * * Note that keymap handling for individual input devices is largely * left to private implementation details in the server. For example, * modifier state or group handling may differ between server * implementations. * - * The keymap is constant on the device. To change keymaps, disconnect the - * device and create a new one. + * A keymap can only be used once and for one device only. + * Once a keymap has been assigned to a device, the caller may drop + * remaining references using ei_keymap_unref(). * - * This function has no effect if called after ei_device_add() + * This function can only be called once per device, further calls are + * ignored. + * + * This function has no effect if called after ei_device_add(). To change + * the keymap, the device must be removed and re-added with a different + * keymap. * * @param device The EI device - * @param type the type of the keymap - * @param fd a memmap-able file descriptor to the keymap - * @param size size of the keymap in bytes + * @param keymap A non-`NULL` keymap */ void ei_device_keyboard_configure_keymap(struct ei_device *device, - enum ei_keymap_type type, - int fd, size_t size); + struct ei_keymap *keymap); /** * Request that the device be added to the server. @@ -650,56 +752,28 @@ uint32_t ei_device_touch_get_height(struct ei_device *device); /** - * Return a memmap-able file descriptor pointing to the keymap used by the - * device. The keymap is constant for the lifetime of the device and - * assigned to this device individually. + * Return the keymap for this device or `NULL`. The keymap is constant for + * the lifetime of the device after the @ref EI_EVENT_DEVICE_ADDED was + * received and applies to this device individually. * - * If this function returns -1, this device does not have - * an individual keymap assigned and the ei_device_keyboard_get_keymap_type() is @ref - * EI_KEYMAP_TYPE_NONE. + * If this function returns `NULL`, this device does not have + * an individual keymap assigned. What keymap applies to the device in this + * case is a server implementation detail. + * + * This does not increase the refcount of the keymap. Use ei_keymap_ref() to + * keep a reference beyond the immediate scope. * * FIXME: the current API makes it impossible to know when the keymap has * been consumed so the file stays open forever. */ -int +struct ei_keymap * ei_device_keyboard_get_keymap(struct ei_device *device); /** - * @return the size of the keymap in bytes + * Return the struct @ref ei_device this keymap is associated with. */ -size_t -ei_device_keyboard_get_keymap_size(struct ei_device *device); - -/** - * Returns the type for this keymap. If the type is @ref - * EI_KEYMAP_TYPE_NONE this device does not have a custom keymap assigned to - * it and ei_device_keyboard_get_keymap() returns -1. - * - * Otherwise, the type specifies how to interpret the data at the file - * descriptor returned by ei_device_keyboard_get_keymap(). - */ -enum ei_keymap_type -ei_device_keyboard_get_keymap_type(struct ei_device *device); - -/** - * Returns the source for the keymap on this device, if any. This is a - * convenience function for the client to check if its keymap was accepted. - * - * Where ei_device_keyboard_get_keymap() returns a value other than -1 and this - * function returns @ref EI_KEYMAP_SOURCE_CLIENT, the keymap is the one - * provided with ei_device_keyboard_configure_keymap(). - * - * Where ei_device_keyboard_get_keymap() returns a value other than -1 and this - * function returns @ref EI_KEYMAP_SOURCE_SERVER, the keymap is one created - * by the server and **not** the one provided with - * ei_device_keyboard_configure_keymap(). - * - * Where ei_device_keyboard_get_keymap() returns -1, the return value of this - * function is undefined. - */ -enum ei_keymap_source -ei_device_keyboard_get_keymap_source(struct ei_device *device); - +struct ei_device * +ei_keymap_get_context(struct ei_keymap *keymap); /** * Return the struct @ref ei context this device is associated with. diff --git a/test/eierpecken.h b/test/eierpecken.h index aacc391..d86a935 100644 --- a/test/eierpecken.h +++ b/test/eierpecken.h @@ -222,6 +222,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct ei_device *, ei_device_unref); #define _cleanup_ei_device_ _cleanup_(ei_device_unrefp) DEFINE_TRIVIAL_CLEANUP_FUNC(struct ei_touch *, ei_touch_unref); #define _cleanup_ei_touch_ _cleanup_(ei_touch_unrefp) +DEFINE_TRIVIAL_CLEANUP_FUNC(struct ei_keymap *, ei_keymap_unref); +#define _cleanup_ei_keymap_ _cleanup_(ei_keymap_unrefp) DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis *, eis_unref); #define _cleanup_eis_ _cleanup_(eis_unrefp) @@ -236,3 +238,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis_device *, eis_device_unref); of a test handles server vs client */ #define with_server(peck_) for (struct eis *eis = peck_get_eis(peck_); eis; eis = NULL) #define with_client(peck_) for (struct ei *ei = peck_get_ei(peck_); ei; ei = NULL) + +#define peck_errno_check(_rc) { \ + const int xrc = (_rc); \ + if (xrc != 0) \ + munit_errorf("errno is not 0: %d - %s\n", -xrc, strerror(-xrc)); \ + } diff --git a/test/test-ei.c b/test/test-ei.c index 50fcf70..ce1d57b 100644 --- a/test/test-ei.c +++ b/test/test-ei.c @@ -1,13 +1,18 @@ - #include "config.h" +#include #include #include +#include "util-io.h" #include "util-munit.h" +#include "util-memfile.h" #include "libei.h" #include "eierpecken.h" +DEFINE_TRIVIAL_CLEANUP_FUNC(struct memfile *, memfile_unref); +#define _cleanup_memfile_ _cleanup_(memfile_unrefp) + MUNIT_TEST(test_ei_ref_unref) { struct ei *ei = ei_new(NULL); @@ -933,6 +938,207 @@ MUNIT_TEST(test_ei_device_multitouch) return MUNIT_OK; } +MUNIT_TEST(test_ei_keymap_invalid) +{ + const char data[5] = {1, 2, 3, 4, 5}; + _cleanup_memfile_ struct memfile *fd = memfile_new(data, sizeof(data)); + + munit_assert_ptr_null(ei_keymap_new(EI_KEYMAP_TYPE_XKB + 1, + memfile_get_fd(fd), memfile_get_size(fd))); + munit_assert_ptr_null(ei_keymap_new(EI_KEYMAP_TYPE_XKB - 1, + memfile_get_fd(fd), memfile_get_size(fd))); + munit_assert_ptr_null(ei_keymap_new(EI_KEYMAP_TYPE_XKB, + -1, memfile_get_size(fd))); + munit_assert_ptr_null(ei_keymap_new(EI_KEYMAP_TYPE_XKB, + memfile_get_fd(fd), 0)); + + /* Valid keymap, valgrind checks only */ + _cleanup_ei_keymap_ struct ei_keymap *unused = + ei_keymap_new(EI_KEYMAP_TYPE_XKB, memfile_get_fd(fd), memfile_get_size(fd)); + munit_assert_ptr_not_null(unused); + + return MUNIT_OK; +} + +MUNIT_TEST(test_ei_keymap_set) +{ + const char data[5] = {1, 2, 3, 4, 5}; + _cleanup_peck_ struct peck *peck = peck_new(); + _cleanup_ei_device_ struct ei_device *device = NULL; + _cleanup_memfile_ struct memfile *fd1 = memfile_new(data, sizeof(data)); + _cleanup_ei_keymap_ struct ei_keymap *keymap = NULL; + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + 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); + ei_device_configure_name(device, __func__); + ei_device_configure_capability(device, EI_DEVICE_CAP_KEYBOARD); + + keymap = ei_keymap_new(EI_KEYMAP_TYPE_XKB, memfile_get_fd(fd1), memfile_get_size(fd1)); + ei_device_keyboard_configure_keymap(device, keymap); + munit_assert_ptr_equal(ei_device_keyboard_get_keymap(device), keymap); + + /* Not possible to overwrite a keymap on a device */ + _cleanup_ei_keymap_ struct ei_keymap *overwrite = + ei_keymap_new(EI_KEYMAP_TYPE_XKB, memfile_get_fd(fd2), memfile_get_size(fd2)); + ei_device_keyboard_configure_keymap(device, overwrite); + munit_assert_ptr_equal(ei_device_keyboard_get_keymap(device), keymap); + + ei_device_add(device); + + /* Still impossible to overwrite after add */ + _cleanup_ei_keymap_ struct ei_keymap *ignored = + ei_keymap_new(EI_KEYMAP_TYPE_XKB, memfile_get_fd(fd2), memfile_get_size(fd2)); + ei_device_keyboard_configure_keymap(device, ignored); + munit_assert_ptr_equal(ei_device_keyboard_get_keymap(device), keymap); + } + + peck_dispatch_until_stable(peck); + + with_server(peck) { + _cleanup_eis_event_ struct eis_event *event = + peck_eis_next_event(eis, EIS_EVENT_DEVICE_ADDED); + struct eis_device *eis_device = eis_event_get_device(event); + + int fd = eis_device_keyboard_get_keymap(eis_device); + munit_assert_int(fd, !=, -1); + munit_assert_uint(eis_device_keyboard_get_keymap_size(eis_device), ==, memfile_get_size(fd1)); + munit_assert_uint(eis_device_keyboard_get_keymap_type(eis_device), ==, EIS_KEYMAP_TYPE_XKB); + eis_device_disconnect(eis_device); + } + + peck_dispatch_until_stable(peck); + + with_client(peck) { + _cleanup_ei_event_ struct ei_event *event = + peck_ei_next_event(ei, EI_EVENT_DEVICE_REMOVED); + struct ei_device *d = ei_event_get_device(event); + munit_assert_ptr_equal(d, device); + + /* Rejecting a device does not unset the keymap because + * you're not supposed to do anything with the device anyway */ + struct ei_keymap *km = ei_device_keyboard_get_keymap(device); + munit_assert_ptr_equal(keymap, km); + munit_assert_int(ei_keymap_get_source(keymap), ==, EI_KEYMAP_SOURCE_CLIENT); + } + + return MUNIT_OK; +} + +MUNIT_TEST(test_ei_keymap_null) +{ + const char data[5] = {1, 2, 3, 4, 5}; + _cleanup_peck_ struct peck *peck = peck_new(); + _cleanup_memfile_ struct memfile *fd = memfile_new(data, sizeof(data)); + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + 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); + ei_device_configure_name(device, __func__); + ei_device_configure_capability(device, EI_DEVICE_CAP_KEYBOARD); + + _cleanup_ei_keymap_ struct ei_keymap *keymap = + ei_keymap_new(EI_KEYMAP_TYPE_XKB, memfile_get_fd(fd), memfile_get_size(fd)); + ei_device_keyboard_configure_keymap(device, keymap); + ei_device_add(device); + } + + peck_dispatch_until_stable(peck); + + /* server sets the keymap to NULL */ + with_server(peck) { + _cleanup_eis_event_ struct eis_event *event = + peck_eis_next_event(eis, EIS_EVENT_DEVICE_ADDED); + struct eis_device *device = eis_event_get_device(event); + eis_device_keyboard_set_keymap(device, EIS_KEYMAP_TYPE_NONE, -1, 0); + eis_device_allow_capability(device, EIS_DEVICE_CAP_KEYBOARD); + eis_device_connect(device); + } + + peck_dispatch_until_stable(peck); + + with_client(peck) { + _cleanup_ei_event_ struct ei_event *event = + peck_ei_next_event(ei, EI_EVENT_DEVICE_ADDED); + struct ei_device *device = ei_event_get_device(event); + + struct ei_keymap *keymap = ei_device_keyboard_get_keymap(device); + munit_assert_ptr_null(keymap); + } + + return MUNIT_OK; +} + +MUNIT_TEST(test_ei_keymap_changed) +{ + const char data[5] = {1, 2, 3, 4, 5}; + _cleanup_peck_ struct peck *peck = peck_new(); + _cleanup_memfile_ struct memfile *fd1 = memfile_new(data, sizeof(data)); + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOCONNNECT); + peck_dispatch_until_stable(peck); + + with_client(peck) { + _cleanup_ei_device_ struct ei_device *device = NULL; + const char otherdata[2] = {10, 20}; + _cleanup_memfile_ struct memfile *fd2 = memfile_new(data, sizeof(otherdata)); + + device = ei_device_new(ei); + ei_device_configure_name(device, __func__); + ei_device_configure_capability(device, EI_DEVICE_CAP_KEYBOARD); + + _cleanup_ei_keymap_ struct ei_keymap *keymap = + ei_keymap_new(EI_KEYMAP_TYPE_XKB, memfile_get_fd(fd2), memfile_get_size(fd2)); + ei_device_keyboard_configure_keymap(device, keymap); + ei_device_add(device); + } + + peck_dispatch_until_stable(peck); + + /* server sets the keymap to some other fd */ + with_server(peck) { + _cleanup_eis_event_ struct eis_event *event = + peck_eis_next_event(eis, EIS_EVENT_DEVICE_ADDED); + struct eis_device *device = eis_event_get_device(event); + eis_device_keyboard_set_keymap(device, EIS_KEYMAP_TYPE_XKB, + memfile_get_fd(fd1), + memfile_get_size(fd1)); + eis_device_allow_capability(device, EIS_DEVICE_CAP_KEYBOARD); + eis_device_connect(device); + } + + peck_dispatch_until_stable(peck); + + with_client(peck) { + _cleanup_ei_event_ struct ei_event *event = + peck_ei_next_event(ei, EI_EVENT_DEVICE_ADDED); + struct ei_device *device = ei_event_get_device(event); + + struct ei_keymap *keymap = ei_device_keyboard_get_keymap(device); + munit_assert_ptr_not_null(keymap); + + munit_assert_int(ei_keymap_get_type(keymap), ==, EI_KEYMAP_TYPE_XKB); + munit_assert_int(ei_keymap_get_source(keymap), ==, EI_KEYMAP_SOURCE_SERVER); + int km = ei_keymap_get_fd(keymap); + + char buf[5]; + int rc = min(xread(km, buf, sizeof(buf)), 0); + peck_errno_check(rc); + munit_assert_int(memcmp(buf, data, sizeof(buf)), ==, 0); + } + + return MUNIT_OK; +} + int main(int argc, char **argv) { diff --git a/tools/ei-demo-client.c b/tools/ei-demo-client.c index 89e0cda..53db66d 100644 --- a/tools/ei-demo-client.c +++ b/tools/ei-demo-client.c @@ -46,6 +46,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct ei *, ei_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(struct ei_device *, ei_device_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct ei_keymap *, ei_keymap_unref); static inline void _printf_(1, 2) @@ -99,8 +100,12 @@ setup_keymap(struct ei_device *kbd, const char *layout) if (!f) return; - ei_device_keyboard_configure_keymap(kbd, EI_KEYMAP_TYPE_XKB, - memfile_get_fd(f), memfile_get_size(f)); + _cleanup_(ei_keymap_unrefp) struct ei_keymap *ei_keymap = + ei_keymap_new(EI_KEYMAP_TYPE_XKB, + memfile_get_fd(f), + memfile_get_size(f)); + ei_device_keyboard_configure_keymap(kbd, ei_keymap); + memfile_unref(f); #endif } @@ -113,16 +118,18 @@ handle_keymap(struct ei_event *event) if (!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD)) return; - const char *type = "unknown"; - switch (ei_device_keyboard_get_keymap_type(device)) { - case EI_KEYMAP_TYPE_NONE: type = "none"; break; - case EI_KEYMAP_TYPE_XKB: type = "xkb"; break; - } - + const char *type = "none"; const char *source = "unknown"; - switch(ei_device_keyboard_get_keymap_source(device)) { - case EI_KEYMAP_SOURCE_CLIENT: source = "client"; break; - case EI_KEYMAP_SOURCE_SERVER: source = "server"; break; + struct ei_keymap *keymap = ei_device_keyboard_get_keymap(device); + if (keymap) { + switch (ei_keymap_get_type(keymap)) { + case EI_KEYMAP_TYPE_XKB: type = "xkb"; break; + } + + switch(ei_keymap_get_source(keymap)) { + case EI_KEYMAP_SOURCE_CLIENT: source = "client"; break; + case EI_KEYMAP_SOURCE_SERVER: source = "server"; break; + } } colorprint("Using keymap type %s from the %s\n", type, source);