Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

input/keyboard: extend bindsym --to-code to work with duplicate matches #8358

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/sway/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
110 changes: 48 additions & 62 deletions sway/commands/bind.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,19 @@
#include "list.h"
#include "log.h"
#include "stringop.h"
#include "util.h"

int binding_order = 0;

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);
Expand Down Expand Up @@ -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;
}

Expand Down
29 changes: 25 additions & 4 deletions sway/input/keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down