Skip to content

Commit

Permalink
Implement bindsym --to-code
Browse files Browse the repository at this point in the history
* `bindsym --to-code` enables keysym to keycode translation.
* If there are no `xkb_layout` commands in the config file, the translation
uses the XKB_DEFAULT_LAYOUT value.
* It there is one or more `xkb_layout` command, the translation uses
the first one.
* If the translation is unsuccessful, a message is logged and the binding
is stored as BINDING_KEYSYM.
* The binding keysyms are stored and re-translated when a change in the input
configuration may affect the translated bindings.
  • Loading branch information
kupospelov authored and mstoeckl committed Apr 19, 2019
1 parent 5e925f0 commit b8a6a0b
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 15 deletions.
34 changes: 27 additions & 7 deletions include/sway/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ struct sway_variable {
char *value;
};


enum binding_input_type {
BINDING_KEYCODE,
BINDING_KEYSYM,
Expand All @@ -39,6 +38,7 @@ enum binding_flags {
BINDING_BORDER=4, // mouse only; trigger on container border
BINDING_CONTENTS=8, // mouse only; trigger on container contents
BINDING_TITLEBAR=16, // mouse only; trigger on container titlebar
BINDING_CODE=32, // keyboard only; convert keysyms into keycodes
};

/**
Expand All @@ -50,6 +50,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
uint32_t modifiers;
char *command;
};
Expand Down Expand Up @@ -406,6 +407,14 @@ enum alignment {
ALIGN_RIGHT
};

/**
* The keysym to keycode translation.
*/
struct keysym_translation_data {
struct xkb_keymap *xkb_keymap;
struct xkb_state *xkb_state;
};

/**
* The configuration struct. The result of loading a config file.
*/
Expand Down Expand Up @@ -508,6 +517,9 @@ struct sway_config {
list_t *feature_policies;
list_t *ipc_policies;

// The keysym to keycode translation
struct keysym_translation_data keysym_translation;

// Context for command handlers
struct {
struct input_config *input_config;
Expand Down Expand Up @@ -617,12 +629,6 @@ bool spawn_swaybg(void);

int workspace_output_cmp_workspace(const void *a, const void *b);

int sway_binding_cmp(const void *a, const void *b);

int sway_binding_cmp_qsort(const void *a, const void *b);

int sway_binding_cmp_keys(const void *a, const void *b);

void free_sway_binding(struct sway_binding *sb);

void free_switch_binding(struct sway_switch_binding *binding);
Expand Down Expand Up @@ -651,6 +657,20 @@ void free_workspace_config(struct workspace_config *wsc);
*/
void config_update_font_height(bool recalculate);

/**
* Add the binding to the binding list. Overwrite if the binding already exists.
*/
void binding_add(struct sway_binding *binding, list_t *mode_bindings,
const char *bindtype, const char *keycombo, bool warn);

/**
* Convert bindsym into bindcode using the first configured layout.
* Return false in case the conversion is unsuccessful.
*/
bool translate_binding(struct sway_binding *binding);

void translate_keysyms(const char *layout);

/* Global config singleton. */
extern struct sway_config *config;

Expand Down
8 changes: 4 additions & 4 deletions sway/commands/bar/bind.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include "log.h"
#include "stringop.h"

static struct cmd_results *binding_add(struct bar_binding *binding,
static struct cmd_results *bar_binding_add(struct bar_binding *binding,
list_t *mode_bindings) {
const char *name = get_mouse_button_name(binding->button);
bool overwritten = false;
Expand All @@ -35,7 +35,7 @@ static struct cmd_results *binding_add(struct bar_binding *binding,
return cmd_results_new(CMD_SUCCESS, NULL);
}

static struct cmd_results *binding_remove(struct bar_binding *binding,
static struct cmd_results *bar_binding_remove(struct bar_binding *binding,
list_t *mode_bindings) {
const char *name = get_mouse_button_name(binding->button);
for (int i = 0; i < mode_bindings->length; i++) {
Expand Down Expand Up @@ -108,11 +108,11 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code,
}
list_t *bindings = config->current_bar->bindings;
if (unbind) {
return binding_remove(binding, bindings);
return bar_binding_remove(binding, bindings);
}

binding->command = join_args(argv + 1, argc - 1);
return binding_add(binding, bindings);
return bar_binding_add(binding, bindings);
}

struct cmd_results *bar_cmd_bindcode(int argc, char **argv) {
Expand Down
117 changes: 113 additions & 4 deletions sway/commands/bind.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ void free_sway_binding(struct sway_binding *binding) {
}

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 @@ -249,7 +250,7 @@ static struct cmd_results *switch_binding_remove(
switchcombo);
}

static struct cmd_results *binding_add(struct sway_binding *binding,
void binding_add(struct sway_binding *binding,
list_t *mode_bindings, const char *bindtype,
const char *keycombo, bool warn) {
// overwrite the binding if it already exists
Expand Down Expand Up @@ -277,8 +278,6 @@ static struct cmd_results *binding_add(struct sway_binding *binding,
sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s` for device '%s'",
bindtype, keycombo, binding->command, binding->input);
}

return cmd_results_new(CMD_SUCCESS, NULL);
}

static struct cmd_results *binding_remove(struct sway_binding *binding,
Expand Down Expand Up @@ -339,6 +338,11 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
binding->flags |= BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR;
} else if (strcmp("--border", argv[0]) == 0) {
binding->flags |= BINDING_BORDER;
} else if (strcmp("--to-code", argv[0]) == 0) {
// Only makes sense for a bindsym
if (!bindcode) {
binding->flags |= BINDING_CODE;
}
} else if (strcmp("--exclude-titlebar", argv[0]) == 0) {
exclude_titlebar = true;
} else if (strncmp("--input-device=", argv[0],
Expand Down Expand Up @@ -410,6 +414,12 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
// sort ascending
list_qsort(binding->keys, key_qsort_cmp);

// translate keysyms into keycodes
if (!translate_binding(binding)) {
sway_log(SWAY_INFO,
"Unable to translate bindsym into bindcode: %s", argv[0]);
}

list_t *mode_bindings;
if (binding->type == BINDING_KEYCODE) {
mode_bindings = config->current_mode->keycode_bindings;
Expand All @@ -425,7 +435,8 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,

binding->command = join_args(argv + 1, argc - 1);
binding->order = binding_order++;
return binding_add(binding, mode_bindings, bindtype, argv[0], warn);
binding_add(binding, mode_bindings, bindtype, argv[0], warn);
return cmd_results_new(CMD_SUCCESS, NULL);
}

struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
Expand Down Expand Up @@ -566,3 +577,101 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding)
ipc_event_binding(binding);
}
}

/**
* The last found keycode associated with the keysym
* and the total count of matches.
*/
struct keycode_matches {
xkb_keysym_t keysym;
xkb_keycode_t keycode;
int count;
};

/**
* Iterate through keycodes in the keymap to find ones matching
* the specified keysym.
*/
static void find_keycode(struct xkb_keymap *keymap,
xkb_keycode_t keycode, void *data) {
xkb_keysym_t keysym = xkb_state_key_get_one_sym(
config->keysym_translation.xkb_state, keycode);

if (keysym == XKB_KEY_NoSymbol) {
return;
}

struct keycode_matches *matches = data;
if (matches->keysym == keysym) {
matches->keycode = keycode;
matches->count++;
}
}

/**
* 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(config->keysym_translation.xkb_keymap,
find_keycode, &matches);
return matches;
}

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:
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 %d into"
" a single keycode (found %d matches)",
*keysym, matches.count);
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;
}

*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);
binding->type = BINDING_KEYSYM;
binding->keys = binding->syms;
binding->syms = NULL;
return false;
}
25 changes: 25 additions & 0 deletions sway/commands/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,30 @@ static struct cmd_handler input_config_handlers[] = {
{ "xkb_numlock", input_cmd_xkb_numlock },
};

/**
* Re-translate keysyms if a change in the input config could affect them.
*/
static void retranslate_keysyms(struct input_config *input_config) {
bool matched = false;
for (int i = 0; i < config->input_configs->length; ++i) {
struct input_config *ic = config->input_configs->items[i];
matched |= ic->identifier == input_config->identifier;

// the first configured xkb_layout
if (ic->xkb_layout) {
if (matched) {
translate_keysyms(ic->xkb_layout);
}

// nothing has changed
return;
}
}

// no xkb_layout has been set, restore the default
translate_keysyms(getenv("XKB_DEFAULT_LAYOUT"));
}

struct cmd_results *cmd_input(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) {
Expand Down Expand Up @@ -73,6 +97,7 @@ struct cmd_results *cmd_input(int argc, char **argv) {
store_input_config(config->handler_context.input_config);

input_manager_apply_input_config(ic);
retranslate_keysyms(ic);
} else {
free_input_config(config->handler_context.input_config);
}
Expand Down
Loading

0 comments on commit b8a6a0b

Please sign in to comment.