diff --git a/proto/ei.proto b/proto/ei.proto index 92324a4..84e6960 100644 --- a/proto/ei.proto +++ b/proto/ei.proto @@ -217,6 +217,14 @@ message DevicePaused { uint32 deviceid = 1; } +message KeyboardModifiers { + uint32 deviceid = 1; + uint32 depressed = 2; + uint32 locked = 3; + uint32 latched = 4; + uint32 group = 5; +} + message ServerMessage { oneof msg { Connected connected = 2; @@ -230,6 +238,7 @@ message ServerMessage { DeviceRemoved device_removed = 10; DeviceResumed device_resumed = 11; DevicePaused device_paused = 12; + KeyboardModifiers keyboard_modifiers = 13; } } diff --git a/src/libei-event.c b/src/libei-event.c index a970b97..5774bad 100644 --- a/src/libei-event.c +++ b/src/libei-event.c @@ -47,6 +47,7 @@ ei_event_type_to_string(enum ei_event_type type) CASE_RETURN_STRING(EI_EVENT_DEVICE_REMOVED); CASE_RETURN_STRING(EI_EVENT_DEVICE_PAUSED); CASE_RETURN_STRING(EI_EVENT_DEVICE_RESUMED); + CASE_RETURN_STRING(EI_EVENT_KEYBOARD_MODIFIERS); } assert(!"Unhandled event type"); @@ -64,6 +65,7 @@ ei_event_destroy(struct ei_event *event) case EI_EVENT_DEVICE_REMOVED: case EI_EVENT_DEVICE_PAUSED: case EI_EVENT_DEVICE_RESUMED: + case EI_EVENT_KEYBOARD_MODIFIERS: break; } ei_device_unref(event->device); @@ -121,3 +123,35 @@ check_event_type(struct ei_event *event, if (!check_event_type(event_, __func__, __VA_ARGS__, -1)) \ return retval_; \ + +_public_ uint32_t +ei_event_keyboard_get_xkb_mods_depressed(struct ei_event *event) +{ + require_event_type(event, 0, EI_EVENT_KEYBOARD_MODIFIERS); + + return event->modifiers.depressed; +} + +_public_ uint32_t +ei_event_keyboard_get_xkb_mods_latched(struct ei_event *event) +{ + require_event_type(event, 0, EI_EVENT_KEYBOARD_MODIFIERS); + + return event->modifiers.latched; +} + +_public_ uint32_t +ei_event_keyboard_get_xkb_mods_locked(struct ei_event *event) +{ + require_event_type(event, 0, EI_EVENT_KEYBOARD_MODIFIERS); + + return event->modifiers.locked; +} + +_public_ uint32_t +ei_event_keyboard_get_xkb_group(struct ei_event *event) +{ + require_event_type(event, 0, EI_EVENT_KEYBOARD_MODIFIERS); + + return event->modifiers.group; +} diff --git a/src/libei-private.h b/src/libei-private.h index 63131aa..aff44f5 100644 --- a/src/libei-private.h +++ b/src/libei-private.h @@ -153,12 +153,21 @@ struct ei_touch { double x, y; }; +struct ei_xkb_modifiers { + uint32_t depressed; + uint32_t latched; + uint32_t locked; + uint32_t group; +}; + struct ei_event { struct object object; /* Parent is struct ei */ enum ei_event_type type; struct list link; struct ei_seat *seat; /* NULL if device is non-NULL */ struct ei_device *device; + + struct ei_xkb_modifiers modifiers; }; struct ei_event * diff --git a/src/libei-proto.c b/src/libei-proto.c index a885edc..b74437b 100644 --- a/src/libei-proto.c +++ b/src/libei-proto.c @@ -105,6 +105,14 @@ ei_proto_handle_message(struct ei *ei, proto->device_region->height, proto->device_region->scale); break; + case SERVER_MESSAGE__MSG_KEYBOARD_MODIFIERS: + rc = call(keyboard_modifiers, ei, + proto->keyboard_modifiers->deviceid, + proto->keyboard_modifiers->depressed, + proto->keyboard_modifiers->latched, + proto->keyboard_modifiers->locked, + proto->keyboard_modifiers->group); + break; case SERVER_MESSAGE__MSG_DEVICE_REMOVED: rc = call(device_removed, ei, proto->device_removed->deviceid); diff --git a/src/libei-proto.h b/src/libei-proto.h index 118b59e..3a53141 100644 --- a/src/libei-proto.h +++ b/src/libei-proto.h @@ -51,6 +51,9 @@ struct ei_proto_interface { enum ei_keymap_type keymap_type, int keymap_fd, size_t keymap_size); + int (*keyboard_modifiers)(struct ei *ei, uint32_t deviceid, + uint32_t depressed, uint32_t latched, + uint32_t locked, uint32_t group); }; struct ei_proto_requests { diff --git a/src/libei.c b/src/libei.c index ac70e68..aa52581 100644 --- a/src/libei.c +++ b/src/libei.c @@ -262,6 +262,21 @@ queue_resumed_event(struct ei_device *device) queue_event(ei, e); } +static void +queue_keyboard_modifiers_event(struct ei_device *device, + const struct ei_xkb_modifiers *mods) +{ + struct ei *ei= ei_device_get_context(device); + + struct ei_event *e = ei_event_new(ei); + e->type = EI_EVENT_KEYBOARD_MODIFIERS; + e->seat = ei_seat_ref(ei_device_get_seat(device)); + e->device = ei_device_ref(device); + e->modifiers = *mods; + + queue_event(ei, e); +} + void ei_disconnect(struct ei *ei) { @@ -423,6 +438,32 @@ handle_msg_device_region(struct ei *ei, uint32_t deviceid, return 0; } +static int +handle_msg_keyboard_modifiers(struct ei *ei, uint32_t deviceid, + uint32_t depressed, uint32_t latched, + uint32_t locked, uint32_t group) +{ + log_debug(ei, "Setting modifiers for %#x\n", deviceid); + + struct ei_device *device = ei_find_device(ei, deviceid); + if (!device) + return 0; + + if (!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD)) { + log_bug(ei,"Modifier event for non-keyboard\n"); + return -EPROTO; + } + + struct ei_xkb_modifiers mods = { + .depressed = depressed, + .latched = latched, + .locked = locked, + .group = group, + }; + queue_keyboard_modifiers_event(device, &mods); + + return 0; +} static int handle_msg_device_removed(struct ei *ei, uint32_t deviceid) @@ -687,6 +728,7 @@ static const struct ei_proto_interface intf_state_connected = { .device_region = handle_msg_device_region, .device_keymap = handle_msg_device_keymap, .device_done = handle_msg_device_added_done, + .keyboard_modifiers = handle_msg_keyboard_modifiers, }; static const struct ei_proto_interface *interfaces[] = { diff --git a/src/libei.h b/src/libei.h index 2a4d187..cacdc11 100644 --- a/src/libei.h +++ b/src/libei.h @@ -236,6 +236,22 @@ enum ei_event_type { * The client may send events. */ EI_EVENT_DEVICE_RESUMED, + + /** + * The server has changed the modifier state on the device's + * keymap. See + * ei_event_keyboard_get_xkb_mods_depressed(), + * ei_event_keyboard_get_xkb_mods_latched(), + * ei_event_keyboard_get_xkb_mods_locked(), and + * ei_event_keyboard_get_xkb_group(). + * + * This event is sent in response to an external modifier state + * change. Where the client triggers a modifier state change in + * response to ei_device_keyboard_key(), no such event is sent. + * + * This event may arrive while a device is paused. + */ + EI_EVENT_KEYBOARD_MODIFIERS, }; /** @@ -545,6 +561,38 @@ ei_event_get_device(struct ei_event *event); uint64_t ei_event_get_time(struct ei_event *event); +/** + * For an event of type @ref EI_EVENT_KEYBOARD_MODIFIERS, get the + * mask of currently logically pressed-down modifiers. + * See ei_device_get_keymap() for the corresponding keymap. + */ +uint32_t +ei_event_keyboard_get_xkb_mods_depressed(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_KEYBOARD_MODIFIERS, get the + * mask of currently logically latched modifiers. + * See ei_device_get_keymap() for the corresponding keymap. + */ +uint32_t +ei_event_keyboard_get_xkb_mods_latched(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_KEYBOARD_MODIFIERS, get the + * mask of currently logically locked modifiers. + * See ei_device_get_keymap() for the corresponding keymap. + */ +uint32_t +ei_event_keyboard_get_xkb_mods_locked(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_KEYBOARD_MODIFIERS, get the + * logical group state. + * See ei_device_get_keymap() for the corresponding keymap. + */ +uint32_t +ei_event_keyboard_get_xkb_group(struct ei_event *event); + /** * Increase the refcount of this struct by one. Use ei_device_unref() to * decrease the refcount. diff --git a/src/libeis-client.c b/src/libeis-client.c index 3c3b461..626a993 100644 --- a/src/libeis-client.c +++ b/src/libeis-client.c @@ -143,6 +143,14 @@ client_send_device_resumed(struct eis_client *client, struct eis_device *device) return eis->requests->device_resumed(device); } +static int +client_send_keyboard_modifiers(struct eis_client *client, struct eis_device *device, + const struct eis_xkb_modifiers *mods) +{ + struct eis *eis = eis_client_get_context(client); + return eis->requests->keyboard_modifiers(device, mods); +} + _public_ void eis_client_connect(struct eis_client *client) { @@ -643,3 +651,18 @@ eis_client_resume_device(struct eis_client *client, struct eis_device *device) { client_send_device_resumed(client, device); } + +void +eis_client_keyboard_modifiers(struct eis_client *client, struct eis_device *device, + uint32_t depressed, uint32_t latched, uint32_t locked, + uint32_t group) +{ + struct eis_xkb_modifiers mods = { + .depressed = depressed, + .locked = locked, + .latched = latched, + .group = group, + }; + client_send_keyboard_modifiers(client, device, &mods); +} + diff --git a/src/libeis-device.c b/src/libeis-device.c index e77f73d..03f8e45 100644 --- a/src/libeis-device.c +++ b/src/libeis-device.c @@ -474,3 +474,17 @@ eis_device_resume(struct eis_device *device) device->state = EIS_DEVICE_STATE_RESUMED; eis_client_resume_device(eis_device_get_client(device), device); } + +_public_ void +eis_device_keyboard_send_xkb_modifiers(struct eis_device *device, uint32_t depressed, + uint32_t latched, uint32_t locked, uint32_t group) +{ + if (!eis_device_has_capability(device, EIS_DEVICE_CAP_KEYBOARD)) { + log_bug_client(eis_device_get_context(device), + "%s: device is not a keyboard\n", __func__); + return; + } + + eis_client_keyboard_modifiers(eis_device_get_client(device), + device, depressed, latched, locked, group); +} diff --git a/src/libeis-private.h b/src/libeis-private.h index 6281b69..4a16e62 100644 --- a/src/libeis-private.h +++ b/src/libeis-private.h @@ -181,6 +181,13 @@ struct eis_keymap { bool assigned; }; +struct eis_xkb_modifiers { + uint32_t depressed; + uint32_t locked; + uint32_t latched; + uint32_t group; +}; + void eis_init_object(struct eis *eis, struct object *parent); @@ -213,6 +220,11 @@ void eis_client_pause_device(struct eis_client *client, struct eis_device *device); +void +eis_client_keyboard_modifiers(struct eis_client *client, struct eis_device *device, + uint32_t depressed, uint32_t latched, uint32_t locked, + uint32_t group); + void eis_seat_bind(struct eis_seat *seat, uint32_t cap); diff --git a/src/libeis-proto.c b/src/libeis-proto.c index 88e5917..2e23fdb 100644 --- a/src/libeis-proto.c +++ b/src/libeis-proto.c @@ -56,6 +56,7 @@ log_wire_message(struct eis *eis, const ServerMessage *msg) MSG_STRING_CASE(DEVICE_REMOVED); MSG_STRING_CASE(DEVICE_RESUMED); MSG_STRING_CASE(DEVICE_PAUSED); + MSG_STRING_CASE(KEYBOARD_MODIFIERS); break; default: assert(!"Unimplemented message type"); @@ -202,6 +203,21 @@ eis_proto_send_device_region(struct eis_device *device, const struct eis_region return eis_proto_send_msg(eis_device_get_client(device), &msg); } +static int +eis_proto_send_keyboard_modifiers(struct eis_device *device, const struct eis_xkb_modifiers *mods) +{ + prepare_msg(KEYBOARD_MODIFIERS, KeyboardModifiers, keyboard_modifiers); + + keyboard_modifiers.deviceid = device->id; + keyboard_modifiers.depressed = mods->depressed; + keyboard_modifiers.locked = mods->locked; + keyboard_modifiers.latched = mods->latched; + keyboard_modifiers.group = mods->group; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + + static int eis_proto_send_device_removed(struct eis_device *device) { @@ -244,6 +260,7 @@ static const struct eis_proto_requests requests = { .device_keymap = eis_proto_send_device_keymap, .device_done = eis_proto_send_device_done, .device_region = eis_proto_send_device_region, + .keyboard_modifiers = eis_proto_send_keyboard_modifiers, }; const struct eis_proto_requests * diff --git a/src/libeis-proto.h b/src/libeis-proto.h index 75ead8f..f4be539 100644 --- a/src/libeis-proto.h +++ b/src/libeis-proto.h @@ -70,6 +70,8 @@ struct eis_proto_requests { int (*device_done)(struct eis_device *device); int (*device_region)(struct eis_device *device, const struct eis_region *region); + int (*keyboard_modifiers)(struct eis_device *device, + const struct eis_xkb_modifiers *mods); }; int diff --git a/src/libeis.h b/src/libeis.h index 92ed402..9a65e52 100644 --- a/src/libeis.h +++ b/src/libeis.h @@ -633,6 +633,15 @@ eis_keymap_get_device(struct eis_keymap *keymap); struct eis_keymap * eis_device_keyboard_get_keymap(struct eis_device *device); +/** + * Notify the client of the current XKB modifier state. + */ +void +eis_device_keyboard_send_xkb_modifiers(struct eis_device *device, + uint32_t depressed, + uint32_t latched, + uint32_t locked, + uint32_t group); /** * For an event of type @ref EIS_EVENT_SEAT_BIND, return the capabilities diff --git a/test/eierpecken.c b/test/eierpecken.c index bc6a747..db5f104 100644 --- a/test/eierpecken.c +++ b/test/eierpecken.c @@ -943,6 +943,7 @@ peck_ei_event_type_name(enum ei_event_type type) CASE_STRING(DEVICE_REMOVED); CASE_STRING(DEVICE_PAUSED); CASE_STRING(DEVICE_RESUMED); + CASE_STRING(KEYBOARD_MODIFIERS); } #undef CASE_STRING assert(!"Unhandled ei event type"); diff --git a/test/test-ei-device.c b/test/test-ei-device.c index b9f77c4..138d5c3 100644 --- a/test/test-ei-device.c +++ b/test/test-ei-device.c @@ -768,3 +768,38 @@ MUNIT_TEST(test_ei_keymap_set) return MUNIT_OK; } + +MUNIT_TEST(test_ei_keyboard_modifiers) +{ + _unref_(peck) *peck = peck_new(); + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_KEYBOARD); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES); + peck_dispatch_until_stable(peck); + + with_server(peck) { + struct eis_device *kbd = peck_eis_get_default_keyboard(peck); + eis_device_keyboard_send_xkb_modifiers(kbd, 0x1, 0x2, 0x4, 0x8); + } + + peck_dispatch_until_stable(peck); + + with_client(peck) { + _unref_(ei_event) *event = + peck_ei_next_event(ei, EI_EVENT_KEYBOARD_MODIFIERS); + + uint32_t depressed, locked, latched, group; + depressed = ei_event_keyboard_get_xkb_mods_depressed(event); + latched = ei_event_keyboard_get_xkb_mods_latched(event); + locked = ei_event_keyboard_get_xkb_mods_locked(event); + group = ei_event_keyboard_get_xkb_group(event); + + munit_assert_uint(depressed, ==, 0x1); + munit_assert_uint(latched, ==, 0x2); + munit_assert_uint(locked, ==, 0x4); + munit_assert_uint(group, ==, 0x8); + } + + return MUNIT_OK; +}