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

Implement layout-independent keysym bindings #3058

Merged
merged 7 commits into from
Apr 26, 2019
Merged
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
25 changes: 18 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 @@ -508,6 +509,9 @@ struct sway_config {
list_t *feature_policies;
list_t *ipc_policies;

// The keysym to keycode translation
struct xkb_state *keysym_translation_state;

// Context for command handlers
struct {
struct input_config *input_config;
Expand Down Expand Up @@ -573,6 +577,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src);

struct input_config *store_input_config(struct input_config *ic);

void input_config_fill_rule_names(struct input_config *ic,
struct xkb_rule_names *rules);

void free_input_config(struct input_config *ic);

int seat_name_cmp(const void *item, const void *data);
Expand Down Expand Up @@ -617,12 +624,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 +652,16 @@ void free_workspace_config(struct workspace_config *wsc);
*/
void config_update_font_height(bool recalculate);

/**
* 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(struct input_config *input_config);

void binding_add_translated(struct sway_binding *binding, list_t *bindings);

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

Expand Down
170 changes: 151 additions & 19 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,31 +250,41 @@ static struct cmd_results *switch_binding_remove(
switchcombo);
}

static struct cmd_results *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
bool overwritten = false;
/**
* Insert or update the binding.
* Return the binding which has been replaced or NULL.
*/
static struct sway_binding *binding_upsert(struct sway_binding *binding,
list_t *mode_bindings) {
for (int i = 0; i < mode_bindings->length; ++i) {
struct sway_binding *config_binding = mode_bindings->items[i];
if (binding_key_compare(binding, config_binding)) {
sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' "
"to `%s` from `%s`", keycombo, binding->input,
binding->command, config_binding->command);
if (warn) {
config_add_swaynag_warning("Overwriting binding"
"'%s' for device '%s' to `%s` from `%s`",
keycombo, binding->input, binding->command,
config_binding->command);
}
free_sway_binding(config_binding);
mode_bindings->items[i] = binding;
overwritten = true;
return config_binding;
}
}

if (!overwritten) {
list_add(mode_bindings, binding);
list_add(mode_bindings, binding);
return NULL;
}

static struct cmd_results *binding_add(struct sway_binding *binding,
list_t *mode_bindings, const char *bindtype,
const char *keycombo, bool warn) {
struct sway_binding *config_binding = binding_upsert(binding, mode_bindings);

if (config_binding) {
sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' "
"to `%s` from `%s`", keycombo, binding->input,
binding->command, config_binding->command);
if (warn) {
config_add_swaynag_warning("Overwriting binding"
"'%s' for device '%s' to `%s` from `%s`",
keycombo, binding->input, binding->command,
config_binding->command);
}
free_sway_binding(config_binding);
} else {
sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s` for device '%s'",
bindtype, keycombo, binding->command, binding->input);
}
Expand Down Expand Up @@ -329,7 +340,6 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
bool exclude_titlebar = false;
bool warn = true;

// Handle --release and --locked
while (argc > 0) {
if (strcmp("--release", argv[0]) == 0) {
binding->flags |= BINDING_RELEASE;
Expand All @@ -339,6 +349,10 @@ 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) {
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 +424,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 Down Expand Up @@ -566,3 +586,115 @@ 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_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(
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;
}

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;
}

void binding_add_translated(struct sway_binding *binding,
list_t *mode_bindings) {
struct sway_binding *config_binding =
binding_upsert(binding, mode_bindings);

if (config_binding) {
sway_log(SWAY_INFO, "Overwriting binding for device '%s' "
"to `%s` from `%s`", binding->input,
binding->command, config_binding->command);
free_sway_binding(config_binding);
}
}
18 changes: 18 additions & 0 deletions sway/commands/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ 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) {
for (int i = 0; i < config->input_configs->length; ++i) {
struct input_config *ic = config->input_configs->items[i];
if (ic->xkb_layout) {
kupospelov marked this conversation as resolved.
Show resolved Hide resolved
// this is the first config with xkb_layout
if (ic->identifier == input_config->identifier) {
translate_keysyms(ic);
}

return;
}
}
}

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 +90,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