From 052d905fc738fad052b84fa046abefd7c98adeb5 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sat, 14 Sep 2024 21:22:21 -0500 Subject: [PATCH] config/bindings: Extend bindsym --to-code to additionally handle the case in which a given keysym maps to more than one keycode. Sometimes, a keysym can map to duplicate keycodes and `libxkbcommon` does not prohibit this. In that case, it makes sense to find all the keycodes that map to the keysym. - Merges translate_binding into translate_keysyms, simplify logic - Changes add_matching_keycodes to find ALL keycodes, not just last - Removes unncessary list_t* syms. It's only used to alias keys as "syms". No other purpose. This commit also introduces a recursive cartesian product calculator in order to handle any cases. `bindsym --to-code (M + .. + N)` s.t. Keysym M maps to keycodes { M_0 .. M_m } ... Keysym N maps to keycodes { N_0 .. N_n } results in || M x .. x N || bindings (cartesian product) --- include/sway/config.h | 1 - sway/commands/bind.c | 166 ++++++++++++++++++++++++++---------------- sway/config.c | 44 +++-------- 3 files changed, 112 insertions(+), 99 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 13f576b00f..fb0d314900 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -62,7 +62,6 @@ 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 uint32_t modifiers; xkb_layout_index_t group; char *command; diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 268f285537..0f756e5d51 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -22,9 +22,7 @@ void free_sway_binding(struct sway_binding *binding) { if (!binding) { return; } - list_free_items_and_destroy(binding->keys); - list_free_items_and_destroy(binding->syms); free(binding->input); free(binding->command); free(binding); @@ -282,6 +280,7 @@ static struct cmd_results *binding_add(struct sway_binding *binding, const char *keycombo, bool warn) { struct sway_binding *config_binding = binding_upsert(binding, mode_bindings); + binding->order = binding_order++; if (config_binding) { sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' " "to `%s` from `%s`", keycombo, binding->input, @@ -467,10 +466,11 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, // sort ascending list_qsort(binding->keys, key_qsort_cmp); + binding->command = join_args(argv + 1, argc - 1); + // translate keysyms into keycodes if (!translate_binding(binding)) { - sway_log(SWAY_INFO, - "Unable to translate bindsym into bindcode: %s", argv[0]); + sway_log(SWAY_INFO,"Unable to translate bindsym into bindcode: %s", argv[0]); } list_t *mode_bindings; @@ -481,13 +481,10 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, } else { mode_bindings = config->current_mode->mouse_bindings; } - if (unbind) { return binding_remove(binding, mode_bindings, bindtype, argv[0]); } - binding->command = join_args(argv + 1, argc - 1); - binding->order = binding_order++; return binding_add(binding, mode_bindings, bindtype, argv[0], warn); } @@ -649,101 +646,141 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) } /** - * The last found keycode associated with the keysym - * and the total count of matches. + * All of the matching keycodes for the given keysym */ struct keycode_matches { - xkb_keysym_t keysym; - xkb_keycode_t keycode; - int count; + xkb_keysym_t *keysym; + list_t *keycodes; + bool error; }; /** - * Iterate through keycodes in the keymap to find ones matching - * the specified keysym. + * Iterate through keycodes in the keymap and add the matching + * the specified keysym's list of matched keycodes. */ -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); - if (keysym == XKB_KEY_NoSymbol) { + struct keycode_matches *matches = data; + if (*matches->keysym == keysym) { + xkb_keycode_t *new_keycode = malloc(sizeof(xkb_keycode_t)); + if (!new_keycode) { + matches->error = true; + return; + } + *new_keycode = keycode; + list_add(matches->keycodes, new_keycode); + } +} + +void cartesian_product_helper(list_t** sets, int n, xkb_keycode_t** current_result, int* curr_size, xkb_keycode_t* current_product, int depth) { + // Conquer + if (depth == n) { + current_result[*curr_size] = malloc(n * sizeof(xkb_keycode_t)); + for (int i = 0; i < n; ++i) { + current_result[*curr_size][i] = current_product[i]; + } + (*curr_size)++; return; } - struct keycode_matches *matches = data; - if (matches->keysym == keysym) { - matches->keycode = keycode; - matches->count++; + // Divide + for (int i = 0; i < sets[depth]->length; ++i) { + current_product[depth] = *(xkb_keycode_t*)sets[depth]->items[i]; + cartesian_product_helper(sets, n, current_result, curr_size, current_product, depth + 1); } } /** - * Return the keycode for the specified keysym. + * Compute the calculate the Cartesian product of `n` sets */ -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; +xkb_keycode_t** cartesian_product(list_t** sets, int n) { + int total_combinations = 1; + for (int i = 0; i < n; ++i) { + total_combinations *= sets[i]->length; + } + + // Allocate memory for the current_result + int result_size = 0; + xkb_keycode_t** result = malloc(total_combinations * sizeof(xkb_keycode_t*)); + xkb_keycode_t* current_product = malloc(n * sizeof(xkb_keycode_t)); + cartesian_product_helper(sets, n, result, &result_size, current_product, 0); + free(current_product); + + return result; } +/* + * Convert keysyms to keycodes for --to-code. If any keysym does not have at + * least 1 keycode: halt conversion and unset BINDING_CODE. Assumes + * identify_key() read in binding->keys in keysym format vs keycode format + */ bool translate_binding(struct sway_binding *binding) { - if ((binding->flags & BINDING_CODE) == 0) { - return true; - } - 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: + if (binding->type != BINDING_KEYSYM || + (binding->flags & BINDING_CODE) == 0) { 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); + int num_syms = binding->keys->length; + list_t ** sym2code = malloc(num_syms * sizeof(list_t*)); + // Collect all keycodes for all keysyms + for (int i = 0; i < num_syms; i++) { + struct keycode_matches matches = { + .keysym = (xkb_keysym_t*)binding->keys->items[i], + .keycodes = create_list(), + .error = false + }; - if (matches.count != 1) { - sway_log(SWAY_INFO, "Unable to convert keysym %" PRIu32 " into" - " a single keycode (found %d matches)", - *keysym, matches.count); + xkb_keymap_key_for_each( + xkb_state_get_keymap(config->keysym_translation_state), + add_matching_keycodes, &matches); + + if (matches.error) { + sway_log(SWAY_ERROR, "Failed to allocate memory for keycodes while iterating for keysym %" PRIu32, *matches.keysym); goto error; } - xkb_keycode_t *keycode = malloc(sizeof(xkb_keycode_t)); - if (!keycode) { - sway_log(SWAY_ERROR, "Unable to allocate memory for a keycode"); - goto error; + if (matches.keycodes->length == 0) { + sway_log(SWAY_INFO, "Unable to convert keysym %" PRIu32 " into" + "any keycodes", *matches.keysym); + goto error; } - *keycode = matches.keycode; - list_add(binding->keys, keycode); + sym2code[i] = matches.keycodes; } - list_qsort(binding->keys, key_qsort_cmp); - binding->type = BINDING_KEYCODE; + // If any keycode maps to more than one keysym, use all combinations. + xkb_keycode_t** combos = cartesian_product(sym2code, num_syms); + + for (int i = 0; i< num_syms; i++) { + struct sway_binding * copy_binding = malloc(sizeof(struct sway_binding)); + binding->type = BINDING_KEYCODE; + *copy_binding = *binding; + list_t *keys = create_list(); + + // copy the keys over + for (int j = 0; j < num_syms; j++) { + xkb_keycode_t * key = malloc(sizeof(xkb_keycode_t)); + *key = combos[i][j]; + list_add(keys, key); + } + copy_binding->keys = keys; + list_qsort(copy_binding->keys, key_qsort_cmp); + binding_add_translated(copy_binding, config->current_mode->keycode_bindings); + } return true; +// if any key cannot be translated, the binding revert to keysym binding error: list_free_items_and_destroy(binding->keys); + for (int i = 0; i < binding->keys->length; i++) { + list_free_items_and_destroy(sym2code[i]); + } + free(sym2code); binding->type = BINDING_KEYSYM; - binding->keys = binding->syms; - binding->syms = NULL; return false; } @@ -752,6 +789,7 @@ void binding_add_translated(struct sway_binding *binding, struct sway_binding *config_binding = binding_upsert(binding, mode_bindings); + binding->order = binding_order++; if (config_binding) { sway_log(SWAY_INFO, "Overwriting binding for device '%s' " "to `%s` from `%s`", binding->input, diff --git a/sway/config.c b/sway/config.c index 5fc414a142..24f795d7be 100644 --- a/sway/config.c +++ b/sway/config.c @@ -972,28 +972,6 @@ void config_update_font_height(void) { } } -static void translate_binding_list(list_t *bindings, list_t *bindsyms, - list_t *bindcodes) { - for (int i = 0; i < bindings->length; ++i) { - struct sway_binding *binding = bindings->items[i]; - translate_binding(binding); - - switch (binding->type) { - case BINDING_KEYSYM: - binding_add_translated(binding, bindsyms); - break; - case BINDING_KEYCODE: - binding_add_translated(binding, bindcodes); - break; - default: - sway_assert(false, "unexpected translated binding type: %d", - binding->type); - break; - } - - } -} - void translate_keysyms(struct input_config *input_config) { keysym_translation_state_destroy(config->keysym_translation_state); @@ -1008,18 +986,16 @@ void translate_keysyms(struct input_config *input_config) { for (int i = 0; i < config->modes->length; ++i) { struct sway_mode *mode = config->modes->items[i]; - - list_t *bindsyms = create_list(); - list_t *bindcodes = create_list(); - - translate_binding_list(mode->keysym_bindings, bindsyms, bindcodes); - translate_binding_list(mode->keycode_bindings, bindsyms, bindcodes); - - list_free(mode->keysym_bindings); - list_free(mode->keycode_bindings); - - mode->keysym_bindings = bindsyms; - mode->keycode_bindings = bindcodes; + for (int j = 0; j < mode->keysym_bindings->length; j++){ + struct sway_binding* binding = mode->keysym_bindings->items[j]; + if(translate_binding(binding)) { + return; + } + list_t *mode_bindings = binding->type == BINDING_KEYCODE + ? config->current_mode->keycode_bindings + : config->current_mode->keysym_bindings; + binding_add_translated(binding, mode_bindings); + } } sway_log(SWAY_DEBUG, "Translated keysyms using config for device '%s'",