diff options
Diffstat (limited to 'sway/commands/bind.c')
-rw-r--r-- | sway/commands/bind.c | 169 |
1 files changed, 150 insertions, 19 deletions
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 | ||
252 | static 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; | 257 | static 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 | |||
271 | static 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 | */ | ||
594 | struct 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 | */ | ||
604 | static 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 | */ | ||
623 | static 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 | |||
635 | bool 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 | |||
680 | error: | ||
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 | |||
688 | void 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 | } | ||