diff --git a/config.c b/config.c index 11f4efdb..80163e2e 100644 --- a/config.c +++ b/config.c @@ -455,9 +455,10 @@ parse_section_csd(const char *key, const char *value, struct config *conf, } static bool -verify_key_combo(const struct config *conf, const char *combo, const char *path, unsigned lineno) +verify_key_combo(const struct config *conf, const char *combo, const char *path, + unsigned lineno) { - for (enum binding_action action = 0; action < BIND_ACTION_COUNT; action++) { + for (enum bind_action_normal action = 0; action < BIND_ACTION_COUNT; action++) { if (conf->bindings.key[action] == NULL) continue; @@ -481,8 +482,7 @@ verify_key_combo(const struct config *conf, const char *combo, const char *path, struct xkb_keymap *keymap = xkb_keymap_new_from_names( ctx, &(struct xkb_rule_names){0}, XKB_KEYMAP_COMPILE_NO_FLAGS); - bool valid_combo = input_parse_key_binding_for_action( - keymap, BIND_ACTION_NONE, combo, NULL); + bool valid_combo = input_parse_key_binding(keymap, combo, NULL); xkb_keymap_unref(keymap); xkb_context_unref(ctx); @@ -500,7 +500,7 @@ parse_section_key_bindings( const char *key, const char *value, struct config *conf, const char *path, unsigned lineno) { - for (enum binding_action action = 0; action < BIND_ACTION_COUNT; action++) { + for (enum bind_action_normal action = 0; action < BIND_ACTION_COUNT; action++) { if (binding_action_map[action] == NULL) continue; @@ -532,7 +532,7 @@ parse_section_mouse_bindings( const char *key, const char *value, struct config *conf, const char *path, unsigned lineno) { - for (enum binding_action action = 0; action < BIND_ACTION_COUNT; action++) { + for (enum bind_action_normal action = 0; action < BIND_ACTION_COUNT; action++) { if (binding_action_map[action] == NULL) continue; @@ -562,7 +562,7 @@ parse_section_mouse_bindings( const int count = 1; /* Make sure button isn't already mapped to another action */ - for (enum binding_action j = 0; j < BIND_ACTION_COUNT; j++) { + for (enum bind_action_normal j = 0; j < BIND_ACTION_COUNT; j++) { const struct mouse_binding *collision = &conf->bindings.mouse[j]; if (collision->button == i && collision->count == count) { LOG_ERR("%s:%d: %s already mapped to %s", path, lineno, @@ -856,6 +856,22 @@ config_load(struct config *conf, const char *conf_path) [BIND_ACTION_PRIMARY_PASTE] = {BTN_MIDDLE, 1, BIND_ACTION_PRIMARY_PASTE}, }, .search = { + [BIND_ACTION_SEARCH_CANCEL] = strdup("Control+g Escape"), + [BIND_ACTION_SEARCH_COMMIT] = strdup("Return"), + [BIND_ACTION_SEARCH_FIND_PREV] = strdup("Control+r"), + [BIND_ACTION_SEARCH_FIND_NEXT] = strdup("Control+s"), + [BIND_ACTION_SEARCH_EDIT_LEFT] = strdup("Left Control+b"), + [BIND_ACTION_SEARCH_EDIT_LEFT_WORD] = strdup("Control+Left Mod1+b"), + [BIND_ACTION_SEARCH_EDIT_RIGHT] = strdup("Right Control+f"), + [BIND_ACTION_SEARCH_EDIT_RIGHT_WORD] = strdup("Control+Right Mod1+f"), + [BIND_ACTION_SEARCH_EDIT_HOME] = strdup("Home Control+a"), + [BIND_ACTION_SEARCH_EDIT_END] = strdup("End Control+e"), + [BIND_ACTION_SEARCH_DELETE_PREV] = strdup("BackSpace"), + [BIND_ACTION_SEARCH_DELETE_PREV_WORD] = strdup("Mod1+BackSpace Control+BackSpace"), + [BIND_ACTION_SEARCH_DELETE_NEXT] = strdup("Delete "), + [BIND_ACTION_SEARCH_DELETE_NEXT_WORD] = strdup("Mod1+d Control+Delete"), + [BIND_ACTION_SEARCH_EXTEND_WORD] = strdup("Control+w"), + [BIND_ACTION_SEARCH_EXTEND_WORD_WS] = strdup("Control+Shift+W"), }, }, @@ -917,8 +933,8 @@ config_free(struct config conf) tll_free_and_free(conf.fonts, free); free(conf.server_socket_path); - for (size_t i = 0; i < BIND_ACTION_COUNT; i++) { + for (enum bind_action_normal i = 0; i < BIND_ACTION_COUNT; i++) free(conf.bindings.key[i]); + for (enum bind_action_search i = 0; i < BIND_ACTION_SEARCH_COUNT; i++) free(conf.bindings.search[i]); - } } diff --git a/config.h b/config.h index 5d6ec0f0..596aa368 100644 --- a/config.h +++ b/config.h @@ -47,7 +47,7 @@ struct config { /* While searching (not - action to *start* a search is in the * 'key' bindings above */ - char *search[BIND_ACTION_COUNT]; + char *search[BIND_ACTION_SEARCH_COUNT]; } bindings; struct { diff --git a/input.c b/input.c index 492080a1..4fff710f 100644 --- a/input.c +++ b/input.c @@ -33,9 +33,9 @@ #define ALEN(v) (sizeof(v) / sizeof(v[0])) -void -input_execute_binding(struct terminal *term, enum binding_action action, - uint32_t serial) +static void +execute_binding(struct terminal *term, enum bind_action_normal action, + uint32_t serial) { switch (action) { case BIND_ACTION_NONE: @@ -109,14 +109,12 @@ input_execute_binding(struct terminal *term, enum binding_action action, } bool -input_parse_key_binding_for_action( - struct xkb_keymap *keymap, enum binding_action action, - const char *combos, key_binding_list_t *bindings) +input_parse_key_binding(struct xkb_keymap *keymap, const char *combos, + key_binding_list_t *bindings) { if (combos == NULL) return true; - xkb_mod_mask_t mod_mask = 0; xkb_keysym_t sym = XKB_KEY_NoSymbol; char *copy = strdup(combos); @@ -125,6 +123,7 @@ input_parse_key_binding_for_action( combo != NULL; combo = strtok_r(NULL, " ", &save1)) { + xkb_mod_mask_t mod_mask = 0; xkb_keycode_list_t key_codes = tll_init(); LOG_DBG("%s", combo); @@ -177,7 +176,7 @@ input_parse_key_binding_for_action( } } - LOG_DBG("action=%u: mods=0x%08x, sym=%d", action, mod_mask, sym); + LOG_DBG("mods=0x%08x, sym=%d", mod_mask, sym); if (sym == XKB_KEY_NoSymbol) { assert(tll_length(key_codes) == 0); @@ -191,7 +190,6 @@ input_parse_key_binding_for_action( .mods = mod_mask, .sym = sym, .key_codes = key_codes, - .action = action, }; tll_push_back(*bindings, binding); @@ -235,11 +233,11 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, } tll_foreach(wayl->kbd.bindings.key, it) - tll_free(it->item.key_codes); + tll_free(it->item.bind.key_codes); tll_free(wayl->kbd.bindings.key); tll_foreach(wayl->kbd.bindings.search, it) - tll_free(it->item.key_codes); + tll_free(it->item.bind.key_codes); tll_free(wayl->kbd.bindings.search); wayl->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); @@ -264,14 +262,32 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, munmap(map_str, size); close(fd); - for (size_t i = 0; i < BIND_ACTION_COUNT; i++) { - input_parse_key_binding_for_action( - wayl->kbd.xkb_keymap, i, - wayl->conf->bindings.key[i], &wayl->kbd.bindings.key); + for (enum bind_action_normal i = 0; i < BIND_ACTION_COUNT; i++) { + key_binding_list_t bindings = tll_init(); + input_parse_key_binding( + wayl->kbd.xkb_keymap, wayl->conf->bindings.key[i], &bindings); - input_parse_key_binding_for_action( - wayl->kbd.xkb_keymap, i, - wayl->conf->bindings.search[i], &wayl->kbd.bindings.search); + tll_foreach(bindings, it) { + tll_push_back( + wayl->kbd.bindings.key, + ((struct key_binding_normal){.bind = it->item, .action = i})); + } + + tll_free(bindings); + } + + for (enum bind_action_search i = 0; i < BIND_ACTION_SEARCH_COUNT; i++) { + key_binding_list_t bindings = tll_init(); + input_parse_key_binding( + wayl->kbd.xkb_keymap, wayl->conf->bindings.search[i], &bindings); + + tll_foreach(bindings, it) { + tll_push_back( + wayl->kbd.bindings.search, + ((struct key_binding_search){.bind = it->item, .action = i})); + } + + tll_free(bindings); } } @@ -558,19 +574,19 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, * User configurable bindings */ tll_foreach(wayl->kbd.bindings.key, it) { - if (it->item.mods != effective_mods) + if (it->item.bind.mods != effective_mods) continue; /* Match symbol */ - if (it->item.sym == sym) { - input_execute_binding(term, it->item.action, serial); + if (it->item.bind.sym == sym) { + execute_binding(term, it->item.action, serial); goto maybe_repeat; } /* Match raw key code */ - tll_foreach(it->item.key_codes, code) { + tll_foreach(it->item.bind.key_codes, code) { if (code->item == key) { - input_execute_binding(term, it->item.action, serial); + execute_binding(term, it->item.action, serial); goto maybe_repeat; } } @@ -1192,7 +1208,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, continue; } - input_execute_binding(term, binding->action, serial); + execute_binding(term, binding->action, serial); break; } selection_cancel(term); diff --git a/input.h b/input.h index 9c274964..553a6249 100644 --- a/input.h +++ b/input.h @@ -10,9 +10,5 @@ extern const struct wl_pointer_listener pointer_listener; void input_repeat(struct wayland *wayl, uint32_t key); -bool input_parse_key_binding_for_action( - struct xkb_keymap *keymap, enum binding_action action, - const char *combos, key_binding_list_t *bindings); - -void input_execute_binding( - struct terminal *term, enum binding_action action, uint32_t serial); +bool input_parse_key_binding(struct xkb_keymap *keymap, const char *combos, + key_binding_list_t *bindings); diff --git a/search.c b/search.c index 1968565a..9304ab48 100644 --- a/search.c +++ b/search.c @@ -412,63 +412,29 @@ distance_prev_word(const struct terminal *term) return term->search.cursor - cursor; } -void -search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, - xkb_mod_mask_t mods, uint32_t serial) +static bool +execute_binding(struct terminal *term, enum bind_action_search action, + uint32_t serial) { - LOG_DBG("search: input: sym=%d/0x%x, mods=0x%08x", sym, sym, mods); + switch (action) { + case BIND_ACTION_SEARCH_NONE: + return false; - const xkb_mod_mask_t ctrl = 1 << term->wl->kbd.mod_ctrl; - const xkb_mod_mask_t alt = 1 << term->wl->kbd.mod_alt; - const xkb_mod_mask_t shift = 1 << term->wl->kbd.mod_shift; - //const xkb_mod_mask_t meta = 1 << term->wl->kbd.mod_meta; - - enum xkb_compose_status compose_status = xkb_compose_state_get_status( - term->wl->kbd.xkb_compose_state); - - /* - * User configurable bindings - */ - tll_foreach(term->wl->kbd.bindings.search, it) { - if (it->item.mods != mods) - continue; - - /* Match symbol */ - if (it->item.sym == sym) { - input_execute_binding(term, it->item.action, serial); - return; - } - - /* Match raw key code */ - tll_foreach(it->item.key_codes, code) { - if (code->item == key) { - input_execute_binding(term, it->item.action, serial); - return; - } - } - } - - /* Cancel search */ - if ((mods == 0 && sym == XKB_KEY_Escape) || - (mods == ctrl && sym == XKB_KEY_g)) - { + case BIND_ACTION_SEARCH_CANCEL: if (term->search.view_followed_offset) term->grid->view = term->grid->offset; else term->grid->view = term->search.original_view; term_damage_view(term); search_cancel(term); - return; - } + return true; - /* "Commit" search - copy selection to primary and cancel search */ - else if (mods == 0 && sym == XKB_KEY_Return) { + case BIND_ACTION_SEARCH_COMMIT: selection_finalize(term, term->wl->input_serial); search_cancel_keep_selection(term); - return; - } + return true; - else if (mods == ctrl && sym == XKB_KEY_r) { + case BIND_ACTION_SEARCH_FIND_PREV: if (term->search.match_len > 0) { int new_col = term->search.match.col - 1; int new_row = term->search.match.row; @@ -483,9 +449,9 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, term->search.match.row = new_row; } } - } + return false; - else if (mods == ctrl && sym == XKB_KEY_s) { + case BIND_ACTION_SEARCH_FIND_NEXT: if (term->search.match_len > 0) { int new_col = term->search.match.col + 1; int new_row = term->search.match.row; @@ -501,49 +467,43 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, term->search.direction = SEARCH_FORWARD; } } - } + return false; - else if ((mods == 0 && sym == XKB_KEY_Left) || - (mods == ctrl && sym == XKB_KEY_b)) - { + case BIND_ACTION_SEARCH_EDIT_LEFT: if (term->search.cursor > 0) term->search.cursor--; - } + return false; - else if ((mods == ctrl && sym == XKB_KEY_Left) || - (mods == alt && sym == XKB_KEY_b)) - { + case BIND_ACTION_SEARCH_EDIT_LEFT_WORD: { size_t diff = distance_prev_word(term); term->search.cursor -= diff; assert(term->search.cursor >= 0); assert(term->search.cursor <= term->search.len); + return false; } - else if ((mods == 0 && sym == XKB_KEY_Right) || - (mods == ctrl && sym == XKB_KEY_f)) - { + case BIND_ACTION_SEARCH_EDIT_RIGHT: if (term->search.cursor < term->search.len) term->search.cursor++; - } + return false; - else if ((mods == ctrl && sym == XKB_KEY_Right) || - (mods == alt && sym == XKB_KEY_f)) - { + case BIND_ACTION_SEARCH_EDIT_RIGHT_WORD: { size_t diff = distance_next_word(term); term->search.cursor += diff; assert(term->search.cursor >= 0); assert(term->search.cursor <= term->search.len); + return false; } - else if ((mods == 0 && sym == XKB_KEY_Home) || - (mods == ctrl && sym == XKB_KEY_a)) + case BIND_ACTION_SEARCH_EDIT_HOME: term->search.cursor = 0; + return false; - else if ((mods == 0 && sym == XKB_KEY_End) || - (mods == ctrl && sym == XKB_KEY_e)) + case BIND_ACTION_SEARCH_EDIT_END: term->search.cursor = term->search.len; + return false; - else if (mods == 0 && sym == XKB_KEY_BackSpace) { + case BIND_ACTION_SEARCH_DELETE_PREV: if (term->search.cursor > 0) { memmove( &term->search.buf[term->search.cursor - 1], @@ -552,9 +512,9 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, term->search.cursor--; term->search.buf[--term->search.len] = L'\0'; } - } + return false; - else if ((mods == alt || mods == ctrl) && sym == XKB_KEY_BackSpace) { + case BIND_ACTION_SEARCH_DELETE_PREV_WORD: { size_t diff = distance_prev_word(term); size_t old_cursor = term->search.cursor; size_t new_cursor = old_cursor - diff; @@ -565,10 +525,20 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, term->search.len -= diff; term->search.cursor = new_cursor; + return false; } - else if ((mods == alt && sym == XKB_KEY_d) || - (mods == ctrl && sym == XKB_KEY_Delete)) { + case BIND_ACTION_SEARCH_DELETE_NEXT: + if (term->search.cursor < term->search.len) { + memmove( + &term->search.buf[term->search.cursor], + &term->search.buf[term->search.cursor + 1], + (term->search.len - term->search.cursor - 1) * sizeof(wchar_t)); + term->search.buf[--term->search.len] = L'\0'; + } + return false; + + case BIND_ACTION_SEARCH_DELETE_NEXT_WORD: { size_t diff = distance_next_word(term); size_t cursor = term->search.cursor; @@ -577,66 +547,98 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, (term->search.len - (cursor + diff)) * sizeof(wchar_t)); term->search.len -= diff; + return false; } - else if (mods == 0 && sym == XKB_KEY_Delete) { - if (term->search.cursor < term->search.len) { - memmove( - &term->search.buf[term->search.cursor], - &term->search.buf[term->search.cursor + 1], - (term->search.len - term->search.cursor - 1) * sizeof(wchar_t)); - term->search.buf[--term->search.len] = L'\0'; - } - } - - else if (mods == ctrl && sym == XKB_KEY_w) + case BIND_ACTION_SEARCH_EXTEND_WORD: search_match_to_end_of_word(term, false); + return false; - else if (mods == (ctrl | shift) && sym == XKB_KEY_W) + case BIND_ACTION_SEARCH_EXTEND_WORD_WS: search_match_to_end_of_word(term, true); + return false; - else { - uint8_t buf[64] = {0}; - int count = 0; - - if (compose_status == XKB_COMPOSE_COMPOSED) { - count = xkb_compose_state_get_utf8( - term->wl->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); - xkb_compose_state_reset(term->wl->kbd.xkb_compose_state); - } else if (compose_status == XKB_COMPOSE_CANCELLED) { - count = 0; - } else { - count = xkb_state_key_get_utf8( - term->wl->kbd.xkb_state, key, (char *)buf, sizeof(buf)); - } - - const char *src = (const char *)buf; - mbstate_t ps = {0}; - size_t wchars = mbsnrtowcs(NULL, &src, count, 0, &ps); - - if (wchars == -1) { - LOG_ERRNO("failed to convert %.*s to wchars", count, buf); - return; - } - - if (!search_ensure_size(term, term->search.len + wchars)) - return; - - assert(term->search.len + wchars < term->search.sz); - - memmove(&term->search.buf[term->search.cursor + wchars], - &term->search.buf[term->search.cursor], - (term->search.len - term->search.cursor) * sizeof(wchar_t)); - - memset(&ps, 0, sizeof(ps)); - mbsnrtowcs(&term->search.buf[term->search.cursor], &src, count, - wchars, &ps); - - term->search.len += wchars; - term->search.cursor += wchars; - term->search.buf[term->search.len] = L'\0'; + case BIND_ACTION_SEARCH_COUNT: + assert(false); + return false; } + assert(false); + return false; +} + +void +search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, + xkb_mod_mask_t mods, uint32_t serial) +{ + LOG_DBG("search: input: sym=%d/0x%x, mods=0x%08x", sym, sym, mods); + + enum xkb_compose_status compose_status = xkb_compose_state_get_status( + term->wl->kbd.xkb_compose_state); + + /* Key bindings */ + tll_foreach(term->wl->kbd.bindings.search, it) { + if (it->item.bind.mods != mods) + continue; + + /* Match symbol */ + if (it->item.bind.sym == sym) { + if (!execute_binding(term, it->item.action, serial)) + goto update_search; + return; + } + + /* Match raw key code */ + tll_foreach(it->item.bind.key_codes, code) { + if (code->item == key) { + if (!execute_binding(term, it->item.action, serial)) + goto update_search; + return; + } + } + } + + uint8_t buf[64] = {0}; + int count = 0; + + if (compose_status == XKB_COMPOSE_COMPOSED) { + count = xkb_compose_state_get_utf8( + term->wl->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); + xkb_compose_state_reset(term->wl->kbd.xkb_compose_state); + } else if (compose_status == XKB_COMPOSE_CANCELLED) { + count = 0; + } else { + count = xkb_state_key_get_utf8( + term->wl->kbd.xkb_state, key, (char *)buf, sizeof(buf)); + } + + const char *src = (const char *)buf; + mbstate_t ps = {0}; + size_t wchars = mbsnrtowcs(NULL, &src, count, 0, &ps); + + if (wchars == -1) { + LOG_ERRNO("failed to convert %.*s to wchars", count, buf); + return; + } + + if (!search_ensure_size(term, term->search.len + wchars)) + return; + + assert(term->search.len + wchars < term->search.sz); + + memmove(&term->search.buf[term->search.cursor + wchars], + &term->search.buf[term->search.cursor], + (term->search.len - term->search.cursor) * sizeof(wchar_t)); + + memset(&ps, 0, sizeof(ps)); + mbsnrtowcs(&term->search.buf[term->search.cursor], &src, count, + wchars, &ps); + + term->search.len += wchars; + term->search.cursor += wchars; + term->search.buf[term->search.len] = L'\0'; + +update_search: LOG_DBG("search: buffer: %S", term->search.buf); search_find_next(term); render_refresh_search(term); diff --git a/wayland.c b/wayland.c index e4907f96..4a553cc6 100644 --- a/wayland.c +++ b/wayland.c @@ -924,11 +924,11 @@ wayl_destroy(struct wayland *wayl) wp_presentation_destroy(wayl->presentation); tll_foreach(wayl->kbd.bindings.key, it) - tll_free(it->item.key_codes); + tll_free(it->item.bind.key_codes); tll_free(wayl->kbd.bindings.key); tll_foreach(wayl->kbd.bindings.search, it) - tll_free(it->item.key_codes); + tll_free(it->item.bind.key_codes); tll_free(wayl->kbd.bindings.search); if (wayl->kbd.xkb_compose_state != NULL) diff --git a/wayland.h b/wayland.h index 6ad46d83..d827708c 100644 --- a/wayland.h +++ b/wayland.h @@ -66,7 +66,17 @@ struct monitor { float inch; /* e.g. 24" */ }; -enum binding_action { + +typedef tll(xkb_keycode_t) xkb_keycode_list_t; + +struct key_binding { + xkb_mod_mask_t mods; + xkb_keysym_t sym; + xkb_keycode_list_t key_codes; +}; +typedef tll(struct key_binding) key_binding_list_t; + +enum bind_action_normal { BIND_ACTION_NONE, BIND_ACTION_SCROLLBACK_UP, BIND_ACTION_SCROLLBACK_DOWN, @@ -84,20 +94,41 @@ enum binding_action { BIND_ACTION_COUNT, }; -typedef tll(xkb_keycode_t) xkb_keycode_list_t; - -struct key_binding { - xkb_mod_mask_t mods; - xkb_keysym_t sym; - xkb_keycode_list_t key_codes; - enum binding_action action; +struct key_binding_normal { + struct key_binding bind; + enum bind_action_normal action; }; -typedef tll(struct key_binding) key_binding_list_t; struct mouse_binding { uint32_t button; int count; - enum binding_action action; + enum bind_action_normal action; +}; + +enum bind_action_search { + BIND_ACTION_SEARCH_NONE, + BIND_ACTION_SEARCH_CANCEL, + BIND_ACTION_SEARCH_COMMIT, + BIND_ACTION_SEARCH_FIND_PREV, + BIND_ACTION_SEARCH_FIND_NEXT, + BIND_ACTION_SEARCH_EDIT_LEFT, + BIND_ACTION_SEARCH_EDIT_LEFT_WORD, + BIND_ACTION_SEARCH_EDIT_RIGHT, + BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, + BIND_ACTION_SEARCH_EDIT_HOME, + BIND_ACTION_SEARCH_EDIT_END, + BIND_ACTION_SEARCH_DELETE_PREV, + BIND_ACTION_SEARCH_DELETE_PREV_WORD, + BIND_ACTION_SEARCH_DELETE_NEXT, + BIND_ACTION_SEARCH_DELETE_NEXT_WORD, + BIND_ACTION_SEARCH_EXTEND_WORD, + BIND_ACTION_SEARCH_EXTEND_WORD_WS, + BIND_ACTION_SEARCH_COUNT, +}; + +struct key_binding_search { + struct key_binding bind; + enum bind_action_search action; }; struct kbd { @@ -127,8 +158,8 @@ struct kbd { bool meta; struct { - key_binding_list_t key; - key_binding_list_t search; + tll(struct key_binding_normal) key; + tll(struct key_binding_search) search; } bindings; };