diff options
Diffstat (limited to 'sway/input')
-rw-r--r-- | sway/input/input-manager.c | 2 | ||||
-rw-r--r-- | sway/input/keyboard.c | 272 | ||||
-rw-r--r-- | sway/input/seat.c | 9 |
3 files changed, 245 insertions, 38 deletions
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 4f9ed891..cfd39bab 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -47,7 +47,7 @@ struct sway_seat *input_manager_get_seat(const char *seat_name, bool create) { char *input_device_get_identifier(struct wlr_input_device *device) { int vendor = device->vendor; int product = device->product; - char *name = strdup(device->name); + char *name = strdup(device->name ? device->name : ""); strip_whitespace(name); char *p = name; diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index cdc4258d..e925c00d 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -3,8 +3,9 @@ #include <strings.h> #include <wlr/backend/multi.h> #include <wlr/backend/session.h> -#include <wlr/types/wlr_idle.h> #include <wlr/interfaces/wlr_keyboard.h> +#include <wlr/types/wlr_idle.h> +#include <wlr/types/wlr_keyboard_group.h> #include <xkbcommon/xkbcommon-names.h> #include "sway/commands.h" #include "sway/desktop/transaction.h" @@ -150,7 +151,7 @@ static bool update_shortcut_state(struct sway_shortcut_state *state, static void get_active_binding(const struct sway_shortcut_state *state, list_t *bindings, struct sway_binding **current_binding, uint32_t modifiers, bool release, bool locked, const char *input, - xkb_layout_index_t group) { + bool exact_input, xkb_layout_index_t group) { for (int i = 0; i < bindings->length; ++i) { struct sway_binding *binding = bindings->items[i]; bool binding_locked = (binding->flags & BINDING_LOCKED) != 0; @@ -162,7 +163,7 @@ static void get_active_binding(const struct sway_shortcut_state *state, (binding->group != XKB_LAYOUT_INVALID && binding->group != group) || (strcmp(binding->input, input) != 0 && - strcmp(binding->input, "*") != 0)) { + (strcmp(binding->input, "*") != 0 || exact_input))) { continue; } @@ -317,16 +318,15 @@ void sway_keyboard_disarm_key_repeat(struct sway_keyboard *keyboard) { } } -static void handle_keyboard_key(struct wl_listener *listener, void *data) { - struct sway_keyboard *keyboard = - wl_container_of(listener, keyboard, keyboard_key); +static void handle_key_event(struct sway_keyboard *keyboard, + struct wlr_event_keyboard_key *event) { struct sway_seat* seat = keyboard->seat_device->sway_seat; struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_input_device *wlr_device = keyboard->seat_device->input_device->wlr_device; char *device_identifier = input_device_get_identifier(wlr_device); + bool exact_identifier = wlr_device->keyboard->group != NULL; wlr_idle_notify_activity(server.idle, wlr_seat); - struct wlr_event_keyboard_key *event = data; bool input_inhibited = seat->exclusive_client != NULL; // Identify new keycode, raw keysym(s), and translated keysym(s) @@ -360,21 +360,20 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { } bool handled = false; - // Identify active release binding struct sway_binding *binding_released = NULL; get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding_released, code_modifiers, true, input_inhibited, device_identifier, - keyboard->effective_layout); + exact_identifier, keyboard->effective_layout); get_active_binding(&keyboard->state_keysyms_raw, config->current_mode->keysym_bindings, &binding_released, raw_modifiers, true, input_inhibited, device_identifier, - keyboard->effective_layout); + exact_identifier, keyboard->effective_layout); get_active_binding(&keyboard->state_keysyms_translated, config->current_mode->keysym_bindings, &binding_released, translated_modifiers, true, input_inhibited, device_identifier, - keyboard->effective_layout); + exact_identifier, keyboard->effective_layout); // Execute stored release binding once no longer active if (keyboard->held_binding && binding_released != keyboard->held_binding && @@ -395,15 +394,16 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding, code_modifiers, false, input_inhibited, device_identifier, - keyboard->effective_layout); + exact_identifier, keyboard->effective_layout); get_active_binding(&keyboard->state_keysyms_raw, config->current_mode->keysym_bindings, &binding, raw_modifiers, false, input_inhibited, device_identifier, - keyboard->effective_layout); + exact_identifier, keyboard->effective_layout); get_active_binding(&keyboard->state_keysyms_translated, config->current_mode->keysym_bindings, &binding, translated_modifiers, false, input_inhibited, - device_identifier, keyboard->effective_layout); + device_identifier, exact_identifier, + keyboard->effective_layout); } // Set up (or clear) keyboard repeat for a pressed binding. Since the @@ -423,6 +423,12 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { handled = true; } + if (!handled && wlr_device->keyboard->group) { + // Only handle device specific bindings for keyboards in a group + free(device_identifier); + return; + } + // Compositor bindings if (!handled && event->state == WLR_KEY_PRESSED) { handled = keyboard_execute_compositor_binding( @@ -450,6 +456,19 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { free(device_identifier); } +static void handle_keyboard_key(struct wl_listener *listener, void *data) { + struct sway_keyboard *keyboard = + wl_container_of(listener, keyboard, keyboard_key); + handle_key_event(keyboard, data); +} + +static void handle_keyboard_group_key(struct wl_listener *listener, + void *data) { + struct sway_keyboard_group *sway_group = + wl_container_of(listener, sway_group, keyboard_key); + handle_key_event(sway_group->seat_device->keyboard, data); +} + static int handle_keyboard_repeat(void *data) { struct sway_keyboard *keyboard = (struct sway_keyboard *)data; struct wlr_keyboard *wlr_device = @@ -491,25 +510,40 @@ static void determine_bar_visibility(uint32_t modifiers) { } } -static void handle_keyboard_modifiers(struct wl_listener *listener, - void *data) { - struct sway_keyboard *keyboard = - wl_container_of(listener, keyboard, keyboard_modifiers); - struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; +static void handle_modifier_event(struct sway_keyboard *keyboard) { struct wlr_input_device *wlr_device = keyboard->seat_device->input_device->wlr_device; - wlr_seat_set_keyboard(wlr_seat, wlr_device); - wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers); + if (!wlr_device->keyboard->group) { + struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; + wlr_seat_set_keyboard(wlr_seat, wlr_device); + wlr_seat_keyboard_notify_modifiers(wlr_seat, + &wlr_device->keyboard->modifiers); - uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); - determine_bar_visibility(modifiers); + uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); + determine_bar_visibility(modifiers); + } - if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { + if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout && + !wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { keyboard->effective_layout = wlr_device->keyboard->modifiers.group; ipc_event_input("xkb_layout", keyboard->seat_device->input_device); } } +static void handle_keyboard_modifiers(struct wl_listener *listener, + void *data) { + struct sway_keyboard *keyboard = + wl_container_of(listener, keyboard, keyboard_modifiers); + handle_modifier_event(keyboard); +} + +static void handle_keyboard_group_modifiers(struct wl_listener *listener, + void *data) { + struct sway_keyboard_group *group = + wl_container_of(listener, group, keyboard_modifiers); + handle_modifier_event(group->seat_device->keyboard); +} + struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, struct sway_seat_device *device) { struct sway_keyboard *keyboard = @@ -616,6 +650,163 @@ cleanup: return keymap; } +static bool keymaps_match(struct xkb_keymap *km1, struct xkb_keymap *km2) { + char *km1_str = xkb_keymap_get_as_string(km1, XKB_KEYMAP_FORMAT_TEXT_V1); + char *km2_str = xkb_keymap_get_as_string(km2, XKB_KEYMAP_FORMAT_TEXT_V1); + bool result = strcmp(km1_str, km2_str) == 0; + free(km1_str); + free(km2_str); + return result; +} + +static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { + struct sway_input_device *device = keyboard->seat_device->input_device; + struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; + struct wlr_keyboard_group *wlr_group = wlr_keyboard->group; + + sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", + device->identifier, wlr_group); + + wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); + + if (wl_list_empty(&wlr_group->devices)) { + sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", + wlr_group); + struct sway_keyboard_group *sway_group = wlr_group->data; + wlr_group->data = NULL; + wl_list_remove(&sway_group->link); + wl_list_remove(&sway_group->keyboard_key.link); + wl_list_remove(&sway_group->keyboard_modifiers.link); + free(sway_group->seat_device->keyboard); + free(sway_group->seat_device->input_device); + free(sway_group->seat_device); + free(sway_group); + wlr_keyboard_group_destroy(wlr_group); + } +} + +static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { + struct sway_input_device *device = keyboard->seat_device->input_device; + struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; + if (!wlr_keyboard->group) { + return; + } + + struct sway_seat *seat = keyboard->seat_device->sway_seat; + struct seat_config *sc = seat_get_config(seat); + if (!sc) { + sc = seat_get_config_by_name("*"); + } + + switch (sc ? sc->keyboard_grouping : KEYBOARD_GROUP_DEFAULT) { + case KEYBOARD_GROUP_NONE: + sway_keyboard_group_remove(keyboard); + break; + case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ + case KEYBOARD_GROUP_KEYMAP:; + struct wlr_keyboard_group *group = wlr_keyboard->group; + if (!keymaps_match(keyboard->keymap, group->keyboard.keymap)) { + sway_keyboard_group_remove(keyboard); + } + break; + } +} + +static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { + struct sway_input_device *device = keyboard->seat_device->input_device; + struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; + struct sway_seat *seat = keyboard->seat_device->sway_seat; + struct seat_config *sc = seat_get_config(seat); + if (!sc) { + sc = seat_get_config_by_name("*"); + } + + if (sc && sc->keyboard_grouping == KEYBOARD_GROUP_NONE) { + // Keyboard grouping is disabled for the seat + return; + } + + struct sway_keyboard_group *group; + wl_list_for_each(group, &seat->keyboard_groups, link) { + switch (sc ? sc->keyboard_grouping : KEYBOARD_GROUP_DEFAULT) { + case KEYBOARD_GROUP_NONE: + // Nothing to do. This shouldn't even be reached + return; + case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ + case KEYBOARD_GROUP_KEYMAP:; + struct wlr_keyboard_group *wlr_group = group->wlr_group; + if (keymaps_match(keyboard->keymap, wlr_group->keyboard.keymap)) { + sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", + device->identifier, wlr_group); + wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); + return; + } + break; + } + } + + struct sway_keyboard_group *sway_group = + calloc(1, sizeof(struct sway_keyboard_group)); + if (!sway_group) { + sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard_group"); + return; + } + + sway_group->wlr_group = wlr_keyboard_group_create(); + if (!sway_group->wlr_group) { + sway_log(SWAY_ERROR, "Failed to create keyboard group"); + goto cleanup; + } + sway_group->wlr_group->data = sway_group; + wlr_keyboard_set_keymap(&sway_group->wlr_group->keyboard, keyboard->keymap); + sway_log(SWAY_DEBUG, "Created keyboard group %p", sway_group->wlr_group); + + sway_group->seat_device = calloc(1, sizeof(struct sway_seat_device)); + if (!sway_group->seat_device) { + sway_log(SWAY_ERROR, "Failed to allocate sway_seat_device for group"); + goto cleanup; + } + sway_group->seat_device->sway_seat = seat; + + sway_group->seat_device->input_device = + calloc(1, sizeof(struct sway_input_device)); + if (!sway_group->seat_device->input_device) { + sway_log(SWAY_ERROR, "Failed to allocate sway_input_device for group"); + goto cleanup; + } + sway_group->seat_device->input_device->wlr_device = + sway_group->wlr_group->input_device; + + if (!sway_keyboard_create(seat, sway_group->seat_device)) { + sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); + goto cleanup; + } + + sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", + device->identifier, sway_group->wlr_group); + wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); + + wl_list_insert(&seat->keyboard_groups, &sway_group->link); + + wl_signal_add(&sway_group->wlr_group->keyboard.events.key, + &sway_group->keyboard_key); + sway_group->keyboard_key.notify = handle_keyboard_group_key; + + wl_signal_add(&sway_group->wlr_group->keyboard.events.modifiers, + &sway_group->keyboard_modifiers); + sway_group->keyboard_modifiers.notify = handle_keyboard_group_modifiers; + return; + +cleanup: + if (sway_group && sway_group->wlr_group) { + wlr_keyboard_group_destroy(sway_group->wlr_group); + } + free(sway_group->seat_device->keyboard); + free(sway_group->seat_device->input_device); + free(sway_group->seat_device); + free(sway_group); +} + void sway_keyboard_configure(struct sway_keyboard *keyboard) { struct input_config *input_config = input_device_get_config(keyboard->seat_device->input_device); @@ -633,26 +824,23 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { } } - bool keymap_changed = false; + bool keymap_changed = + keyboard->keymap ? !keymaps_match(keyboard->keymap, keymap) : true; bool effective_layout_changed = keyboard->effective_layout != 0; - if (keyboard->keymap) { - char *old_keymap_string = xkb_keymap_get_as_string(keyboard->keymap, - XKB_KEYMAP_FORMAT_TEXT_V1); - char *new_keymap_string = xkb_keymap_get_as_string(keymap, - XKB_KEYMAP_FORMAT_TEXT_V1); - keymap_changed = strcmp(old_keymap_string, new_keymap_string); - free(old_keymap_string); - free(new_keymap_string); - } else { - keymap_changed = true; - } if (keymap_changed || config->reloading) { xkb_keymap_unref(keyboard->keymap); keyboard->keymap = keymap; keyboard->effective_layout = 0; + + sway_keyboard_group_remove_invalid(keyboard); + wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); + if (!wlr_device->keyboard->group) { + sway_keyboard_group_add(keyboard); + } + xkb_mod_mask_t locked_mods = 0; if (input_config && input_config->xkb_numlock > 0) { xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, @@ -679,10 +867,19 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { leds |= (1 << i); } } - wlr_keyboard_led_update(wlr_device->keyboard, leds); + if (wlr_device->keyboard->group) { + wlr_keyboard_led_update( + &wlr_device->keyboard->group->keyboard, leds); + } else { + wlr_keyboard_led_update(wlr_device->keyboard, leds); + } } } else { xkb_keymap_unref(keymap); + sway_keyboard_group_remove_invalid(keyboard); + if (!wlr_device->keyboard->group) { + sway_keyboard_group_add(keyboard); + } } int repeat_rate = 25; @@ -721,6 +918,7 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { if (!keyboard) { return; } + sway_keyboard_group_remove(keyboard); if (keyboard->keymap) { xkb_keymap_unref(keyboard->keymap); } diff --git a/sway/input/seat.c b/sway/input/seat.c index f486d5e7..fb3e68ee 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -102,6 +102,14 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( return seat_device->keyboard; } } + struct sway_keyboard_group *group; + wl_list_for_each(group, &seat->keyboard_groups, link) { + struct sway_input_device *input_device = + group->seat_device->input_device; + if (input_device->wlr_device->keyboard == wlr_keyboard) { + return group->seat_device->keyboard; + } + } return NULL; } @@ -519,6 +527,7 @@ struct sway_seat *seat_create(const char *seat_name) { handle_request_set_primary_selection; wl_list_init(&seat->devices); + wl_list_init(&seat->keyboard_groups); wl_list_insert(&server.input->seats, &seat->link); |