From e8121391593ab5b02a4816b2cda6ad2f7ec39c2f Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Mon, 23 Sep 2024 16:07:01 -0500 Subject: [PATCH] input/keyboard: extend bindsym --to-code to work with duplicate matches This modifies `get_active_binding` to treat bindsym --to-codes separately: per each keysym `i` in `binding->keys`, look through the list of matching keycodes in `binding->translations[i]`. Another solution is to take the cartesian product of all syms and make a binding per each product. This makes retranslation more difficult though because the dups from the old layout have to be cleared out before translating to the new `xkb_layout`. Whether by retaining a parent binding, searching through all bindings for matching `binding->command`, or some other ref count structure, that would introduce more complexity than modifying get_active binding to account for dups. Notice this requires no changes to the existing retranslation logic. --- include/sway/config.h | 2 +- sway/commands/bind.c | 110 ++++++++++++++++++------------------------ sway/input/keyboard.c | 29 +++++++++-- 3 files changed, 74 insertions(+), 67 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 71721393e2..2ec7137504 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -62,7 +62,7 @@ struct sway_binding { char *input; uint32_t flags; list_t *keys; // sorted in ascending order - list_t *syms; // sorted in ascending order; NULL if BINDING_CODE is not set + list_t **translations; // translations[i] = all keycodes for keysym keys[i] uint32_t modifiers; xkb_layout_index_t group; char *command; diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 268f285537..294ddedba9 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -14,7 +14,6 @@ #include "list.h" #include "log.h" #include "stringop.h" -#include "util.h" int binding_order = 0; @@ -22,9 +21,12 @@ void free_sway_binding(struct sway_binding *binding) { if (!binding) { return; } - + if (binding->translations) { + for (int i = 0; i < binding->keys->length; i++) { + list_free_items_and_destroy(binding->translations[i]); + } + } list_free_items_and_destroy(binding->keys); - list_free_items_and_destroy(binding->syms); free(binding->input); free(binding->command); free(binding); @@ -653,97 +655,81 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) * and the total count of matches. */ struct keycode_matches { - xkb_keysym_t keysym; - xkb_keycode_t keycode; - int count; + xkb_keysym_t *keysym; + list_t *keys; + int error; }; /** * Iterate through keycodes in the keymap to find ones matching * the specified keysym. */ -static void find_keycode(struct xkb_keymap *keymap, +static void add_matching_keycodes(struct xkb_keymap *keymap, xkb_keycode_t keycode, void *data) { xkb_keysym_t keysym = xkb_state_key_get_one_sym( config->keysym_translation_state, keycode); + struct keycode_matches *matches = data; if (keysym == XKB_KEY_NoSymbol) { return; } - struct keycode_matches *matches = data; - if (matches->keysym == keysym) { - matches->keycode = keycode; - matches->count++; + if (*matches->keysym == keysym) { + xkb_keycode_t *new_keycode = malloc(sizeof(keycode)); + if (!new_keycode) { + sway_log(SWAY_ERROR, "Unable to allocate memory for keysym"); + matches->error++; + return; + } + *new_keycode = keycode; + char buffer[64] = {0}; + xkb_keysym_get_name(keysym, buffer, sizeof(buffer)); + sway_log(SWAY_DEBUG, "Translated keysym [%d (%s)] -> keycode [%d (%s)]", + keysym, buffer, + *new_keycode, xkb_keymap_key_get_name(keymap, *new_keycode)); + list_add(matches->keys, new_keycode); } } -/** - * Return the keycode for the specified keysym. - */ -static struct keycode_matches get_keycode_for_keysym(xkb_keysym_t keysym) { - struct keycode_matches matches = { - .keysym = keysym, - .keycode = XKB_KEYCODE_INVALID, - .count = 0, - }; - - xkb_keymap_key_for_each( - xkb_state_get_keymap(config->keysym_translation_state), - find_keycode, &matches); - return matches; -} - bool translate_binding(struct sway_binding *binding) { if ((binding->flags & BINDING_CODE) == 0) { return true; } + int keys_len = binding->keys->length; - switch (binding->type) { - // a bindsym to translate - case BINDING_KEYSYM: - binding->syms = binding->keys; - binding->keys = create_list(); - break; - // a bindsym to re-translate - case BINDING_KEYCODE: - list_free_items_and_destroy(binding->keys); - binding->keys = create_list(); - break; - default: - return true; - } - - for (int i = 0; i < binding->syms->length; ++i) { - xkb_keysym_t *keysym = binding->syms->items[i]; - struct keycode_matches matches = get_keycode_for_keysym(*keysym); - - if (matches.count != 1) { - sway_log(SWAY_INFO, "Unable to convert keysym %" PRIu32 " into" - " a single keycode (found %d matches)", - *keysym, matches.count); - goto error; + // Clean out for retranslation + if (binding->type == BINDING_KEYCODE) { + for (int i = 0; i < keys_len; i++) { + list_free_items_and_destroy(binding->translations[i]); } + } - xkb_keycode_t *keycode = malloc(sizeof(xkb_keycode_t)); - if (!keycode) { - sway_log(SWAY_ERROR, "Unable to allocate memory for a keycode"); + // Begin translation + binding->translations = malloc(keys_len * sizeof(*binding->keys)); + for (int i = 0; i < keys_len; ++i) { + struct keycode_matches matches = { + .keysym = (xkb_keysym_t*)binding->keys->items[i], + .keys = binding->translations[i] = create_list(), + .error = 0, + }; + + xkb_keymap_key_for_each( + xkb_state_get_keymap(config->keysym_translation_state), + add_matching_keycodes, &matches); + + if (matches.error) { + sway_log(SWAY_INFO, "Unable to convert keysym %" PRIu32 " into", *matches.keysym); goto error; } - - *keycode = matches.keycode; - list_add(binding->keys, keycode); } - - list_qsort(binding->keys, key_qsort_cmp); binding->type = BINDING_KEYCODE; return true; error: - list_free_items_and_destroy(binding->keys); + for (int i = 0; i < keys_len; i++) { + list_free_items_and_destroy(binding->translations[i]); + } binding->type = BINDING_KEYSYM; - binding->keys = binding->syms; - binding->syms = NULL; return false; } diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 1a73df0147..52ba98d319 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -179,10 +179,31 @@ static void get_active_binding(const struct sway_shortcut_state *state, if (state->npressed == (size_t)binding->keys->length) { match = true; for (size_t j = 0; j < state->npressed; j++) { - uint32_t key = *(uint32_t *)binding->keys->items[j]; - if (key != state->pressed_keys[j]) { - match = false; - break; + uint32_t key; + + // If translated bindsym, keys are syms not keycodes. + // keysym j mapped to keycodes translations[j] + if (binding->type & BINDING_CODE) { + bool dup_match = false; + list_t *duplicate_keys = binding->translations[j]; + for (int k = 0; k < duplicate_keys->length; k++) { + key = *(uint32_t *)duplicate_keys->items[k]; + if (key == state->pressed_keys[j]) { + dup_match = true; + break; + } + } + if (!dup_match) { + match = false; + break; + } + } + else { + key = *(uint32_t *)binding->keys->items[j]; + if (key != state->pressed_keys[j]) { + match = false; + break; + } } } } else if (binding->keys->length == 1) {