Skip to content

Commit

Permalink
input/keyboard: fix bindsym --to-code failing on duplicates
Browse files Browse the repository at this point in the history
The `libxkbcommon` api does not prohibit multiple keycodes mapping to a
single keysym. There is no reason to treat it as an error. With these changes,
all of the matched keycodes will be bound the keysym.

Error variable is to track whether malloc failed while iterating.
Can potentially keep calling a failing malloc but it will get
logged and aborted once `xkb_keymap_key_for_each` returns
control flow to sway. The only way to avoid malloc within the
iterator is by preallocating enough memory to hold possible every
keycode in the keymap, then copying over those matches->keycodes into the
binding->keycodes. But that feels even more problematic.

Retains cleanup semantics of blame-commit a09c144
  • Loading branch information
sahinf committed Sep 8, 2024
1 parent fb5eadc commit 2139d6d
Showing 1 changed file with 32 additions and 39 deletions.
71 changes: 32 additions & 39 deletions sway/commands/bind.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <libevdev/libevdev.h>
#include <linux/input-event-codes.h>
#include <stdbool.h>
#include <string.h>
#include <strings.h>
#include <xkbcommon/xkbcommon.h>
Expand Down Expand Up @@ -649,20 +650,21 @@ 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.
* The current keysym to find keycodes for
* and it's corresponding list of keycodes.
* Boolean to track whether iterator errored.
*/
struct keycode_matches {
xkb_keysym_t keysym;
xkb_keycode_t keycode;
int count;
xkb_keysym_t *keysym;
list_t *keys;
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
* ones to the binding->keys
*/
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);
Expand All @@ -672,28 +674,17 @@ static void find_keycode(struct xkb_keymap *keymap,
}

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(xkb_keycode_t));
if (!new_keycode) {
matches->error = true;
return;
}
*new_keycode = 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;
Expand All @@ -715,24 +706,26 @@ bool translate_binding(struct sway_binding *binding) {
}

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);
struct keycode_matches matches = {
.keysym = binding->syms->items[i],
.keys = binding->keys,
.error = false
};

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");
if (binding->keys->length < 1) {
sway_log(SWAY_INFO, "Unable to find any matching keycodes for keysym %" PRIu32, *matches.keysym);
goto error;
}

*keycode = matches.keycode;
list_add(binding->keys, keycode);
}

list_qsort(binding->keys, key_qsort_cmp);
Expand Down

0 comments on commit 2139d6d

Please sign in to comment.