mirror of
https://github.com/swaywm/sway.git
synced 2026-04-18 06:46:56 -04:00
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)
This commit is contained in:
parent
f957c7e658
commit
052d905fc7
3 changed files with 111 additions and 98 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
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->type != BINDING_KEYSYM ||
|
||||
(binding->flags & BINDING_CODE) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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 = keysym,
|
||||
.keycode = XKB_KEYCODE_INVALID,
|
||||
.count = 0,
|
||||
.keysym = (xkb_keysym_t*)binding->keys->items[i],
|
||||
.keycodes = create_list(),
|
||||
.error = false
|
||||
};
|
||||
|
||||
xkb_keymap_key_for_each(
|
||||
xkb_state_get_keymap(config->keysym_translation_state),
|
||||
find_keycode, &matches);
|
||||
return matches;
|
||||
}
|
||||
add_matching_keycodes, &matches);
|
||||
|
||||
bool translate_binding(struct sway_binding *binding) {
|
||||
if ((binding->flags & BINDING_CODE) == 0) {
|
||||
return true;
|
||||
if (matches.error) {
|
||||
sway_log(SWAY_ERROR, "Failed to allocate memory for keycodes while iterating for keysym %" PRIu32, *matches.keysym);
|
||||
goto error;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (matches.keycodes->length == 0) {
|
||||
sway_log(SWAY_INFO, "Unable to convert keysym %" PRIu32 " into"
|
||||
" a single keycode (found %d matches)",
|
||||
*keysym, matches.count);
|
||||
"any keycodes", *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;
|
||||
sym2code[i] = matches.keycodes;
|
||||
}
|
||||
|
||||
*keycode = matches.keycode;
|
||||
list_add(binding->keys, keycode);
|
||||
}
|
||||
// If any keycode maps to more than one keysym, use all combinations.
|
||||
xkb_keycode_t** combos = cartesian_product(sym2code, num_syms);
|
||||
|
||||
list_qsort(binding->keys, key_qsort_cmp);
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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'",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue