From 422d94fb46ad18e52f5385e416cd00cb7b4c2fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 6 Feb 2022 19:36:44 +0100 Subject: [PATCH] wip: map key combos to custom text strings (including escapes) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this, it is now possible to map key combos to custom escapes. The new bindings are defined in a new section, “text-bindings”, on the form “string=key combo”. The string can consist of printable characters, or \xNN style hex digits: [text-bindings] abcd = Control+a \x1b[A = Control+b Control+c Control+d # map ctrl+b/c/d to UP --- config.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- config.h | 11 ++++++ foot.ini | 3 ++ input.c | 5 +++ wayland.h | 2 +- 5 files changed, 132 insertions(+), 4 deletions(-) diff --git a/config.c b/config.c index 37768a8e..99083130 100644 --- a/config.c +++ b/config.c @@ -111,6 +111,7 @@ static const char *const binding_action_map[] = { [BIND_ACTION_PIPE_SELECTED] = "pipe-selected", [BIND_ACTION_SHOW_URLS_COPY] = "show-urls-copy", [BIND_ACTION_SHOW_URLS_LAUNCH] = "show-urls-launch", + [BIND_ACTION_TEXT_BINDING] = "text-binding", /* Mouse-specific actions */ [BIND_ACTION_SELECT_BEGIN] = "select-begin", @@ -1480,7 +1481,8 @@ free_binding_aux(struct binding_aux *aux) { switch (aux->type) { case BINDING_AUX_NONE: break; - case BINDING_AUX_PIPE: free_argv(&aux->pipe); + case BINDING_AUX_PIPE: free_argv(&aux->pipe); break; + case BINDING_AUX_TEXT: free(aux->text.data); break; } } @@ -1578,8 +1580,15 @@ binding_aux_equal(const struct binding_aux *a, return false; switch (a->type) { - case BINDING_AUX_NONE: return true; - case BINDING_AUX_PIPE: return argv_compare(&a->pipe, &b->pipe) == 0; + case BINDING_AUX_NONE: + return true; + + case BINDING_AUX_PIPE: + return argv_compare(&a->pipe, &b->pipe) == 0; + + case BINDING_AUX_TEXT: + return a->text.len == b->text.len && + memcmp(a->text.data, b->text.data, a->text.len) == 0; } BUG("invalid AUX type: %d", a->type); @@ -2230,6 +2239,81 @@ parse_section_mouse_bindings(struct context *ctx) return false; } +static bool +parse_section_text_bindings(struct context *ctx) +{ + struct config *conf = ctx->conf; + const char *key = ctx->key; + + const size_t key_len = strlen(key); + + uint8_t *data = xmalloc(key_len + 1); + size_t data_len = 0; + bool esc = false; + + for (size_t i = 0; i < key_len; i++) { + if (key[i] == '\\') { + if (i + 1 >= key_len) { + ctx->value = ""; + LOG_CONTEXTUAL_ERR("trailing backslash"); + goto err; + } + + esc = true; + } + + else if (esc) { + if (key[i] != 'x') { + ctx->value = ""; + LOG_CONTEXTUAL_ERR("invalid escaped character: %c", key[i]); + goto err; + } + if (i + 2 >= key_len) { + ctx->value = ""; + LOG_CONTEXTUAL_ERR("\\x sequence too short"); + goto err; + } + + const uint8_t nib1 = hex2nibble(key[i + 1]); + const uint8_t nib2 = hex2nibble(key[i + 2]); + + if (nib1 >= HEX_DIGIT_INVALID || nib2 >= HEX_DIGIT_INVALID) { + ctx->value = ""; + LOG_CONTEXTUAL_ERR("invalid \\x sequence: \\x%c%c", + key[i + 1], key[i + 2]); + goto err; + } + + data[data_len++] = nib1 << 4 | nib2; + esc = false; + i += 2; + } + + else + data[data_len++] = key[i]; + } + + struct binding_aux aux = { + .type = BINDING_AUX_TEXT, + .text = { + .data = data, + .len = data_len, + }, + }; + + if (!value_to_key_combos(ctx, BIND_ACTION_TEXT_BINDING, &aux, + &conf->bindings.key, KEY_BINDING)) + { + goto err; + } + + return true; + +err: + free(data); + return false; +} + static bool parse_section_tweak(struct context *ctx) { @@ -2429,6 +2513,7 @@ enum section { SECTION_SEARCH_BINDINGS, SECTION_URL_BINDINGS, SECTION_MOUSE_BINDINGS, + SECTION_TEXT_BINDINGS, SECTION_TWEAK, SECTION_COUNT, }; @@ -2452,6 +2537,7 @@ static const struct { [SECTION_SEARCH_BINDINGS] = {&parse_section_search_bindings, "search-bindings"}, [SECTION_URL_BINDINGS] = {&parse_section_url_bindings, "url-bindings"}, [SECTION_MOUSE_BINDINGS] = {&parse_section_mouse_bindings, "mouse-bindings"}, + [SECTION_TEXT_BINDINGS] = {&parse_section_text_bindings, "text-bindings"}, [SECTION_TWEAK] = {&parse_section_tweak, "tweak"}, }; @@ -3085,6 +3171,8 @@ key_binding_list_clone(struct config_key_binding_list *dst, const struct config_key_binding_list *src) { struct argv *last_master_argv = NULL; + uint8_t *last_master_text_data = NULL; + size_t last_master_text_len = 0; dst->count = src->count; dst->arr = xmalloc(src->count * sizeof(dst->arr[0])); @@ -3098,6 +3186,8 @@ key_binding_list_clone(struct config_key_binding_list *dst, switch (old->aux.type) { case BINDING_AUX_NONE: last_master_argv = NULL; + last_master_text_data = NULL; + last_master_text_len = 0; break; case BINDING_AUX_PIPE: @@ -3108,6 +3198,25 @@ key_binding_list_clone(struct config_key_binding_list *dst, xassert(last_master_argv != NULL); new->aux.pipe = *last_master_argv; } + last_master_text_data = NULL; + last_master_text_len = 0; + break; + + case BINDING_AUX_TEXT: + if (old->aux.master_copy) { + const size_t len = old->aux.text.len; + new->aux.text.len = len; + new->aux.text.data = xmalloc(len); + memcpy(new->aux.text.data, old->aux.text.data, len); + + last_master_text_len = len; + last_master_text_data = new->aux.text.data; + } else { + xassert(last_master_text_data != NULL); + new->aux.text.len = last_master_text_len; + new->aux.text.data = last_master_text_data; + } + last_master_argv = NULL; break; } } diff --git a/config.h b/config.h index cd5bbd14..cf9ca3db 100644 --- a/config.h +++ b/config.h @@ -47,6 +47,7 @@ struct argv { enum binding_aux_type { BINDING_AUX_NONE, BINDING_AUX_PIPE, + BINDING_AUX_TEXT, }; struct binding_aux { @@ -55,6 +56,11 @@ struct binding_aux { union { struct argv pipe; + + struct { + uint8_t *data; + size_t len; + } text; }; }; @@ -63,6 +69,11 @@ enum key_binding_type { MOUSE_BINDING, }; +struct config_key_binding_text { + char *text; + bool master_copy; +}; + struct config_key_binding { int action; /* One of the varios bind_action_* enums from wayland.h */ struct config_key_modifiers modifiers; diff --git a/foot.ini b/foot.ini index 072613d0..9e6aa601 100644 --- a/foot.ini +++ b/foot.ini @@ -178,4 +178,7 @@ # select-word-whitespace=Control+BTN_LEFT-2 # select-row=BTN_LEFT-3 +[text-bindings] +# \x01=Mod4+a # Map Super+a -> Ctrl+a + # vim: ft=conf diff --git a/input.c b/input.c index e7b02fb8..26af0807 100644 --- a/input.c +++ b/input.c @@ -313,6 +313,11 @@ execute_binding(struct seat *seat, struct terminal *term, return true; } + case BIND_ACTION_TEXT_BINDING: + xassert(binding->aux->type == BINDING_AUX_TEXT); + term_to_slave(term, binding->aux->text.data, binding->aux->text.len); + return true; + case BIND_ACTION_SELECT_BEGIN: selection_start( term, seat->mouse.col, seat->mouse.row, SELECTION_CHAR_WISE, false); diff --git a/wayland.h b/wayland.h index 81109673..2d753e70 100644 --- a/wayland.h +++ b/wayland.h @@ -54,6 +54,7 @@ enum bind_action_normal { BIND_ACTION_PIPE_SELECTED, BIND_ACTION_SHOW_URLS_COPY, BIND_ACTION_SHOW_URLS_LAUNCH, + BIND_ACTION_TEXT_BINDING, /* Mouse specific actions - i.e. they require a mouse coordinate */ BIND_ACTION_SELECT_BEGIN, @@ -119,7 +120,6 @@ struct key_binding { }; const struct binding_aux *aux; - }; typedef tll(struct key_binding) key_binding_list_t;