aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Konstantin Pospelov <kupospelov@gmail.com>2019-02-17 19:08:22 +0300
committerLibravatar Simon Ser <contact@emersion.fr>2019-04-26 20:56:48 +0300
commita09c144b8b5f9d0518e7239a27e2fb86e00644b3 (patch)
treeacce9b57c319496e9f5dd194e22e49b0b6fcbfdf
parentci: enable all features (diff)
downloadsway-a09c144b8b5f9d0518e7239a27e2fb86e00644b3.tar.gz
sway-a09c144b8b5f9d0518e7239a27e2fb86e00644b3.tar.zst
sway-a09c144b8b5f9d0518e7239a27e2fb86e00644b3.zip
Implement bindsym --to-code
* `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.
-rw-r--r--include/sway/config.h30
-rw-r--r--sway/commands/bind.c169
-rw-r--r--sway/commands/input.c25
-rw-r--r--sway/config.c76
4 files changed, 274 insertions, 26 deletions
diff --git a/include/sway/config.h b/include/sway/config.h
index 86410544..392f6538 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -24,7 +24,6 @@ struct sway_variable {
24 char *value; 24 char *value;
25}; 25};
26 26
27
28enum binding_input_type { 27enum binding_input_type {
29 BINDING_KEYCODE, 28 BINDING_KEYCODE,
30 BINDING_KEYSYM, 29 BINDING_KEYSYM,
@@ -39,6 +38,7 @@ enum binding_flags {
39 BINDING_BORDER=4, // mouse only; trigger on container border 38 BINDING_BORDER=4, // mouse only; trigger on container border
40 BINDING_CONTENTS=8, // mouse only; trigger on container contents 39 BINDING_CONTENTS=8, // mouse only; trigger on container contents
41 BINDING_TITLEBAR=16, // mouse only; trigger on container titlebar 40 BINDING_TITLEBAR=16, // mouse only; trigger on container titlebar
41 BINDING_CODE=32, // keyboard only; convert keysyms into keycodes
42}; 42};
43 43
44/** 44/**
@@ -50,6 +50,7 @@ struct sway_binding {
50 char *input; 50 char *input;
51 uint32_t flags; 51 uint32_t flags;
52 list_t *keys; // sorted in ascending order 52 list_t *keys; // sorted in ascending order
53 list_t *syms; // sorted in ascending order; NULL if BINDING_CODE is not set
53 uint32_t modifiers; 54 uint32_t modifiers;
54 char *command; 55 char *command;
55}; 56};
@@ -407,6 +408,14 @@ enum alignment {
407}; 408};
408 409
409/** 410/**
411 * The keysym to keycode translation.
412 */
413struct keysym_translation_data {
414 struct xkb_keymap *xkb_keymap;
415 struct xkb_state *xkb_state;
416};
417
418/**
410 * The configuration struct. The result of loading a config file. 419 * The configuration struct. The result of loading a config file.
411 */ 420 */
412struct sway_config { 421struct sway_config {
@@ -508,6 +517,9 @@ struct sway_config {
508 list_t *feature_policies; 517 list_t *feature_policies;
509 list_t *ipc_policies; 518 list_t *ipc_policies;
510 519
520 // The keysym to keycode translation
521 struct keysym_translation_data keysym_translation;
522
511 // Context for command handlers 523 // Context for command handlers
512 struct { 524 struct {
513 struct input_config *input_config; 525 struct input_config *input_config;
@@ -617,12 +629,6 @@ bool spawn_swaybg(void);
617 629
618int workspace_output_cmp_workspace(const void *a, const void *b); 630int workspace_output_cmp_workspace(const void *a, const void *b);
619 631
620int sway_binding_cmp(const void *a, const void *b);
621
622int sway_binding_cmp_qsort(const void *a, const void *b);
623
624int sway_binding_cmp_keys(const void *a, const void *b);
625
626void free_sway_binding(struct sway_binding *sb); 632void free_sway_binding(struct sway_binding *sb);
627 633
628void free_switch_binding(struct sway_switch_binding *binding); 634void free_switch_binding(struct sway_switch_binding *binding);
@@ -651,6 +657,16 @@ void free_workspace_config(struct workspace_config *wsc);
651 */ 657 */
652void config_update_font_height(bool recalculate); 658void config_update_font_height(bool recalculate);
653 659
660/**
661 * Convert bindsym into bindcode using the first configured layout.
662 * Return false in case the conversion is unsuccessful.
663 */
664bool translate_binding(struct sway_binding *binding);
665
666void translate_keysyms(const char *layout);
667
668void binding_add_translated(struct sway_binding *binding, list_t *bindings);
669
654/* Global config singleton. */ 670/* Global config singleton. */
655extern struct sway_config *config; 671extern struct sway_config *config;
656 672
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index dc7e0b19..e5fd4433 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -24,6 +24,7 @@ void free_sway_binding(struct sway_binding *binding) {
24 } 24 }
25 25
26 list_free_items_and_destroy(binding->keys); 26 list_free_items_and_destroy(binding->keys);
27 list_free_items_and_destroy(binding->syms);
27 free(binding->input); 28 free(binding->input);
28 free(binding->command); 29 free(binding->command);
29 free(binding); 30 free(binding);
@@ -249,31 +250,41 @@ static struct cmd_results *switch_binding_remove(
249 switchcombo); 250 switchcombo);
250} 251}
251 252
252static struct cmd_results *binding_add(struct sway_binding *binding, 253/**
253 list_t *mode_bindings, const char *bindtype, 254 * Insert or update the binding.
254 const char *keycombo, bool warn) { 255 * Return the binding which has been replaced or NULL.
255 // overwrite the binding if it already exists 256 */
256 bool overwritten = false; 257static struct sway_binding *binding_upsert(struct sway_binding *binding,
258 list_t *mode_bindings) {
257 for (int i = 0; i < mode_bindings->length; ++i) { 259 for (int i = 0; i < mode_bindings->length; ++i) {
258 struct sway_binding *config_binding = mode_bindings->items[i]; 260 struct sway_binding *config_binding = mode_bindings->items[i];
259 if (binding_key_compare(binding, config_binding)) { 261 if (binding_key_compare(binding, config_binding)) {
260 sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' "
261 "to `%s` from `%s`", keycombo, binding->input,
262 binding->command, config_binding->command);
263 if (warn) {
264 config_add_swaynag_warning("Overwriting binding"
265 "'%s' for device '%s' to `%s` from `%s`",
266 keycombo, binding->input, binding->command,
267 config_binding->command);
268 }
269 free_sway_binding(config_binding);
270 mode_bindings->items[i] = binding; 262 mode_bindings->items[i] = binding;
271 overwritten = true; 263 return config_binding;
272 } 264 }
273 } 265 }
274 266
275 if (!overwritten) { 267 list_add(mode_bindings, binding);
276 list_add(mode_bindings, binding); 268 return NULL;
269}
270
271static struct cmd_results *binding_add(struct sway_binding *binding,
272 list_t *mode_bindings, const char *bindtype,
273 const char *keycombo, bool warn) {
274 struct sway_binding *config_binding = binding_upsert(binding, mode_bindings);
275
276 if (config_binding) {
277 sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' "
278 "to `%s` from `%s`", keycombo, binding->input,
279 binding->command, config_binding->command);
280 if (warn) {
281 config_add_swaynag_warning("Overwriting binding"
282 "'%s' for device '%s' to `%s` from `%s`",
283 keycombo, binding->input, binding->command,
284 config_binding->command);
285 }
286 free_sway_binding(config_binding);
287 } else {
277 sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s` for device '%s'", 288 sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s` for device '%s'",
278 bindtype, keycombo, binding->command, binding->input); 289 bindtype, keycombo, binding->command, binding->input);
279 } 290 }
@@ -329,7 +340,6 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
329 bool exclude_titlebar = false; 340 bool exclude_titlebar = false;
330 bool warn = true; 341 bool warn = true;
331 342
332 // Handle --release and --locked
333 while (argc > 0) { 343 while (argc > 0) {
334 if (strcmp("--release", argv[0]) == 0) { 344 if (strcmp("--release", argv[0]) == 0) {
335 binding->flags |= BINDING_RELEASE; 345 binding->flags |= BINDING_RELEASE;
@@ -339,6 +349,10 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
339 binding->flags |= BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR; 349 binding->flags |= BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR;
340 } else if (strcmp("--border", argv[0]) == 0) { 350 } else if (strcmp("--border", argv[0]) == 0) {
341 binding->flags |= BINDING_BORDER; 351 binding->flags |= BINDING_BORDER;
352 } else if (strcmp("--to-code", argv[0]) == 0) {
353 if (!bindcode) {
354 binding->flags |= BINDING_CODE;
355 }
342 } else if (strcmp("--exclude-titlebar", argv[0]) == 0) { 356 } else if (strcmp("--exclude-titlebar", argv[0]) == 0) {
343 exclude_titlebar = true; 357 exclude_titlebar = true;
344 } else if (strncmp("--input-device=", argv[0], 358 } else if (strncmp("--input-device=", argv[0],
@@ -410,6 +424,12 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
410 // sort ascending 424 // sort ascending
411 list_qsort(binding->keys, key_qsort_cmp); 425 list_qsort(binding->keys, key_qsort_cmp);
412 426
427 // translate keysyms into keycodes
428 if (!translate_binding(binding)) {
429 sway_log(SWAY_INFO,
430 "Unable to translate bindsym into bindcode: %s", argv[0]);
431 }
432
413 list_t *mode_bindings; 433 list_t *mode_bindings;
414 if (binding->type == BINDING_KEYCODE) { 434 if (binding->type == BINDING_KEYCODE) {
415 mode_bindings = config->current_mode->keycode_bindings; 435 mode_bindings = config->current_mode->keycode_bindings;
@@ -566,3 +586,114 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding)
566 ipc_event_binding(binding); 586 ipc_event_binding(binding);
567 } 587 }
568} 588}
589
590/**
591 * The last found keycode associated with the keysym
592 * and the total count of matches.
593 */
594struct keycode_matches {
595 xkb_keysym_t keysym;
596 xkb_keycode_t keycode;
597 int count;
598};
599
600/**
601 * Iterate through keycodes in the keymap to find ones matching
602 * the specified keysym.
603 */
604static void find_keycode(struct xkb_keymap *keymap,
605 xkb_keycode_t keycode, void *data) {
606 xkb_keysym_t keysym = xkb_state_key_get_one_sym(
607 config->keysym_translation.xkb_state, keycode);
608
609 if (keysym == XKB_KEY_NoSymbol) {
610 return;
611 }
612
613 struct keycode_matches *matches = data;
614 if (matches->keysym == keysym) {
615 matches->keycode = keycode;
616 matches->count++;
617 }
618}
619
620/**
621 * Return the keycode for the specified keysym.
622 */
623static struct keycode_matches get_keycode_for_keysym(xkb_keysym_t keysym) {
624 struct keycode_matches matches = {
625 .keysym = keysym,
626 .keycode = XKB_KEYCODE_INVALID,
627 .count = 0,
628 };
629
630 xkb_keymap_key_for_each(config->keysym_translation.xkb_keymap,
631 find_keycode, &matches);
632 return matches;
633}
634
635bool translate_binding(struct sway_binding *binding) {
636 if ((binding->flags & BINDING_CODE) == 0) {
637 return true;
638 }
639
640 switch (binding->type) {
641 // a bindsym to translate
642 case BINDING_KEYSYM:
643 binding->syms = binding->keys;
644 binding->keys = create_list();
645 break;
646 // a bindsym to re-translate
647 case BINDING_KEYCODE:
648 list_free_items_and_destroy(binding->keys);
649 binding->keys = create_list();
650 break;
651 default:
652 return true;
653 }
654
655 for (int i = 0; i < binding->syms->length; ++i) {
656 xkb_keysym_t *keysym = binding->syms->items[i];
657 struct keycode_matches matches = get_keycode_for_keysym(*keysym);
658
659 if (matches.count != 1) {
660 sway_log(SWAY_INFO, "Unable to convert keysym %d into"
661 " a single keycode (found %d matches)",
662 *keysym, matches.count);
663 goto error;
664 }
665
666 xkb_keycode_t *keycode = malloc(sizeof(xkb_keycode_t));
667 if (!keycode) {
668 sway_log(SWAY_ERROR, "Unable to allocate memory for a keycode");
669 goto error;
670 }
671
672 *keycode = matches.keycode;
673 list_add(binding->keys, keycode);
674 }
675
676 list_qsort(binding->keys, key_qsort_cmp);
677 binding->type = BINDING_KEYCODE;
678 return true;
679
680error:
681 list_free_items_and_destroy(binding->keys);
682 binding->type = BINDING_KEYSYM;
683 binding->keys = binding->syms;
684 binding->syms = NULL;
685 return false;
686}
687
688void binding_add_translated(struct sway_binding *binding,
689 list_t *mode_bindings) {
690 struct sway_binding *config_binding =
691 binding_upsert(binding, mode_bindings);
692
693 if (config_binding) {
694 sway_log(SWAY_INFO, "Overwriting binding for device '%s' "
695 "to `%s` from `%s`", binding->input,
696 binding->command, config_binding->command);
697 free_sway_binding(config_binding);
698 }
699}
diff --git a/sway/commands/input.c b/sway/commands/input.c
index b72bd76b..903d574f 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -39,6 +39,30 @@ static struct cmd_handler input_config_handlers[] = {
39 { "xkb_numlock", input_cmd_xkb_numlock }, 39 { "xkb_numlock", input_cmd_xkb_numlock },
40}; 40};
41 41
42/**
43 * Re-translate keysyms if a change in the input config could affect them.
44 */
45static void retranslate_keysyms(struct input_config *input_config) {
46 bool matched = false;
47 for (int i = 0; i < config->input_configs->length; ++i) {
48 struct input_config *ic = config->input_configs->items[i];
49 matched |= ic->identifier == input_config->identifier;
50
51 // the first configured xkb_layout
52 if (ic->xkb_layout) {
53 if (matched) {
54 translate_keysyms(ic->xkb_layout);
55 }
56
57 // nothing has changed
58 return;
59 }
60 }
61
62 // no xkb_layout has been set, restore the default
63 translate_keysyms(getenv("XKB_DEFAULT_LAYOUT"));
64}
65
42struct cmd_results *cmd_input(int argc, char **argv) { 66struct cmd_results *cmd_input(int argc, char **argv) {
43 struct cmd_results *error = NULL; 67 struct cmd_results *error = NULL;
44 if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) { 68 if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) {
@@ -73,6 +97,7 @@ struct cmd_results *cmd_input(int argc, char **argv) {
73 store_input_config(config->handler_context.input_config); 97 store_input_config(config->handler_context.input_config);
74 98
75 input_manager_apply_input_config(ic); 99 input_manager_apply_input_config(ic);
100 retranslate_keysyms(ic);
76 } else { 101 } else {
77 free_input_config(config->handler_context.input_config); 102 free_input_config(config->handler_context.input_config);
78 } 103 }
diff --git a/sway/config.c b/sway/config.c
index e14ea83a..45d16758 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -33,6 +33,31 @@
33 33
34struct sway_config *config = NULL; 34struct sway_config *config = NULL;
35 35
36static struct keysym_translation_data new_keysym_translation_data(
37 const char *layout) {
38 struct xkb_rule_names rules = {
39 .layout = layout,
40 };
41
42 struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names(
43 xkb_context_new(XKB_CONTEXT_NO_FLAGS),
44 &rules,
45 XKB_KEYMAP_COMPILE_NO_FLAGS);
46
47 struct keysym_translation_data result = {
48 .xkb_keymap = xkb_keymap,
49 .xkb_state = xkb_state_new(xkb_keymap),
50 };
51
52 return result;
53}
54
55static void free_keysym_translation_data(
56 struct keysym_translation_data config) {
57 xkb_state_unref(config.xkb_state);
58 xkb_keymap_unref(config.xkb_keymap);
59}
60
36static void free_mode(struct sway_mode *mode) { 61static void free_mode(struct sway_mode *mode) {
37 if (!mode) { 62 if (!mode) {
38 return; 63 return;
@@ -146,6 +171,7 @@ void free_config(struct sway_config *config) {
146 free(config->swaynag_command); 171 free(config->swaynag_command);
147 free((char *)config->current_config_path); 172 free((char *)config->current_config_path);
148 free((char *)config->current_config); 173 free((char *)config->current_config);
174 free_keysym_translation_data(config->keysym_translation);
149 free(config); 175 free(config);
150} 176}
151 177
@@ -317,6 +343,9 @@ static void config_defaults(struct sway_config *config) {
317 if (!(config->feature_policies = create_list())) goto cleanup; 343 if (!(config->feature_policies = create_list())) goto cleanup;
318 if (!(config->ipc_policies = create_list())) goto cleanup; 344 if (!(config->ipc_policies = create_list())) goto cleanup;
319 345
346 // The keysym to keycode translation
347 config->keysym_translation = new_keysym_translation_data(getenv("XKB_DEFAULT_LAYOUT"));
348
320 return; 349 return;
321cleanup: 350cleanup:
322 sway_abort("Unable to allocate config structures"); 351 sway_abort("Unable to allocate config structures");
@@ -937,3 +966,50 @@ void config_update_font_height(bool recalculate) {
937 arrange_root(); 966 arrange_root();
938 } 967 }
939} 968}
969
970static void translate_binding_list(list_t *bindings, list_t *bindsyms,
971 list_t *bindcodes) {
972 for (int i = 0; i < bindings->length; ++i) {
973 struct sway_binding *binding = bindings->items[i];
974 translate_binding(binding);
975
976 list_t *bindings;
977 switch (binding->type) {
978 case BINDING_KEYSYM:
979 bindings = bindsyms;
980 break;
981 case BINDING_KEYCODE:
982 bindings = bindcodes;
983 break;
984 default:
985 sway_assert(false, "unexpected translated binding type: %d",
986 binding->type);
987 break;
988 }
989
990 binding_add_translated(binding, bindings);
991 }
992}
993
994void translate_keysyms(const char *layout) {
995 free_keysym_translation_data(config->keysym_translation);
996 config->keysym_translation = new_keysym_translation_data(layout);
997
998 for (int i = 0; i < config->modes->length; ++i) {
999 struct sway_mode *mode = config->modes->items[i];
1000
1001 list_t *bindsyms = create_list();
1002 list_t *bindcodes = create_list();
1003
1004 translate_binding_list(mode->keysym_bindings, bindsyms, bindcodes);
1005 translate_binding_list(mode->keycode_bindings, bindsyms, bindcodes);
1006
1007 list_free(mode->keysym_bindings);
1008 list_free(mode->keycode_bindings);
1009
1010 mode->keysym_bindings = bindsyms;
1011 mode->keycode_bindings = bindcodes;
1012 }
1013
1014 sway_log(SWAY_DEBUG, "Translated keysyms for layout %s", layout);
1015}