From 03bac9dada76690971943f824637407100f7a3ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 7 Feb 2021 14:46:29 +0100 Subject: [PATCH] key-bindings: refactor: use a single type for all key bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now, the various key binding modes (“normal”, “search” and “url”) have used their own struct definitions for their key bindings. The only reason for this was to have a properly typed “action” (using the appropriate “action” enum). This caused lots of duplicated code. This patch refactors this to use a single struct definition for the “unparsed” key bindings handled by the configuration, and another single definition for “parsed” bindings used while handling input. This allows us to implement configuration parsing, keymap translation and so on using one set of functions, regardless of key binding mode. --- config.c | 282 +++++++++++++++-------------------------------------- config.h | 37 +++---- input.c | 65 +++--------- search.c | 6 +- url-mode.c | 6 +- wayland.c | 23 ++--- wayland.h | 57 +++++------ 7 files changed, 145 insertions(+), 331 deletions(-) diff --git a/config.c b/config.c index f7452994..b12c7858 100644 --- a/config.c +++ b/config.c @@ -1175,11 +1175,13 @@ err: } static bool -has_key_binding_collisions(struct config *conf, enum bind_action_normal action, +has_key_binding_collisions(struct config *conf, + int action, const char *const action_map[], + config_key_binding_list_t *bindings, const key_combo_list_t *key_combos, const char *path, unsigned lineno) { - tll_foreach(conf->bindings.key, it) { + tll_foreach(*bindings, it) { if (it->item.action == action) continue; @@ -1197,7 +1199,7 @@ has_key_binding_collisions(struct config *conf, enum bind_action_normal action, bool has_pipe = it->item.pipe.cmd != NULL; LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s%s%s%s'", path, lineno, it2->item.text, - binding_action_map[it->item.action], + action_map[it->item.action], has_pipe ? " [" : "", has_pipe ? it->item.pipe.cmd : "", has_pipe ? "]" : ""); @@ -1209,68 +1211,6 @@ has_key_binding_collisions(struct config *conf, enum bind_action_normal action, return false; } -static bool -has_search_binding_collisions(struct config *conf, enum bind_action_search action, - const key_combo_list_t *key_combos, - const char *path, unsigned lineno) -{ - tll_foreach(conf->bindings.search, it) { - if (it->item.action == action) - continue; - - tll_foreach(*key_combos, it2) { - const struct config_key_modifiers *mods1 = &it->item.modifiers; - const struct config_key_modifiers *mods2 = &it2->item.modifiers; - - bool shift = mods1->shift == mods2->shift; - bool alt = mods1->alt == mods2->alt; - bool ctrl = mods1->ctrl == mods2->ctrl; - bool meta = mods1->meta == mods2->meta; - bool sym = it->item.sym == it2->item.sym; - - if (shift && alt && ctrl && meta && sym) { - LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s'", - path, lineno, it2->item.text, - search_binding_action_map[it->item.action]); - return true; - } - } - } - - return false; -} - -static bool -has_url_binding_collisions(struct config *conf, enum bind_action_url action, - const key_combo_list_t *key_combos, - const char *path, unsigned lineno) -{ - tll_foreach(conf->bindings.url, it) { - if (it->item.action == action) - continue; - - tll_foreach(*key_combos, it2) { - const struct config_key_modifiers *mods1 = &it->item.modifiers; - const struct config_key_modifiers *mods2 = &it2->item.modifiers; - - bool shift = mods1->shift == mods2->shift; - bool alt = mods1->alt == mods2->alt; - bool ctrl = mods1->ctrl == mods2->ctrl; - bool meta = mods1->meta == mods2->meta; - bool sym = it->item.sym == it2->item.sym; - - if (shift && alt && ctrl && meta && sym) { - LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s'", - path, lineno, it2->item.text, - url_binding_action_map[it->item.action]); - return true; - } - } - } - - return false; -} - static int argv_compare(char *const *argv1, char *const *argv2) { @@ -1351,10 +1291,12 @@ pipe_argv_from_string(const char *value, char **cmd, char ***argv, return remove_len; } -static bool -parse_section_key_bindings( - const char *key, const char *value, struct config *conf, - const char *path, unsigned lineno) +static bool NOINLINE +parse_key_binding_section( + const char *section, const char *key, const char *value, + int action_count, const char *const action_map[static action_count], + config_key_binding_list_t *bindings, + struct config *conf, const char *path, unsigned lineno) { char *pipe_cmd; char **pipe_argv; @@ -1367,25 +1309,22 @@ parse_section_key_bindings( value += pipe_remove_len; - for (enum bind_action_normal action = 0; - action < BIND_ACTION_KEY_COUNT; - action++) - { - if (binding_action_map[action] == NULL) + for (int action = 0; action < action_count; action++) { + if (action_map[action] == NULL) continue; - if (strcmp(key, binding_action_map[action]) != 0) + if (strcmp(key, action_map[action]) != 0) continue; /* Unset binding */ if (strcasecmp(value, "none") == 0) { - tll_foreach(conf->bindings.key, it) { + tll_foreach(*bindings, it) { if (it->item.action == action) { if (it->item.pipe.master_copy) { free(it->item.pipe.cmd); free(it->item.pipe.argv); } - tll_remove(conf->bindings.key, it); + tll_remove(*bindings, it); } } free(pipe_argv); @@ -1395,7 +1334,9 @@ parse_section_key_bindings( key_combo_list_t key_combos = tll_init(); if (!parse_key_combos(conf, value, &key_combos, path, lineno) || - has_key_binding_collisions(conf, action, &key_combos, path, lineno)) + has_key_binding_collisions( + conf, action, binding_action_map, bindings, &key_combos, + path, lineno)) { free(pipe_argv); free(pipe_cmd); @@ -1404,7 +1345,7 @@ parse_section_key_bindings( } /* Remove existing bindings for this action+pipe */ - tll_foreach(conf->bindings.key, it) { + tll_foreach(*bindings, it) { if (it->item.action == action && ((it->item.pipe.argv == NULL && pipe_argv == NULL) || (it->item.pipe.argv != NULL && pipe_argv != NULL && @@ -1415,14 +1356,14 @@ parse_section_key_bindings( free(it->item.pipe.cmd); free(it->item.pipe.argv); } - tll_remove(conf->bindings.key, it); + tll_remove(*bindings, it); } } /* Emit key bindings */ bool first = true; tll_foreach(key_combos, it) { - struct config_key_binding_normal binding = { + struct config_key_binding binding = { .action = action, .modifiers = it->item.modifiers, .sym = it->item.sym, @@ -1433,7 +1374,7 @@ parse_section_key_bindings( }, }; - tll_push_back(conf->bindings.key, binding); + tll_push_back(*bindings, binding); first = false; } @@ -1441,12 +1382,21 @@ parse_section_key_bindings( return true; } - LOG_AND_NOTIFY_ERR("%s:%u: [key-bindings]: %s: invalid action", - path, lineno, key); + LOG_AND_NOTIFY_ERR("%s:%u: [%s]: %s: invalid action", + path, lineno, section, key); free(pipe_cmd); free(pipe_argv); return false; +} +static bool +parse_section_key_bindings( + const char *key, const char *value, struct config *conf, + const char *path, unsigned lineno) +{ + return parse_key_binding_section( + "key-bindings", key, value, BIND_ACTION_KEY_COUNT, binding_action_map, + &conf->bindings.key, conf, path, lineno); } static bool @@ -1454,56 +1404,9 @@ parse_section_search_bindings( const char *key, const char *value, struct config *conf, const char *path, unsigned lineno) { - for (enum bind_action_search action = 0; - action < BIND_ACTION_SEARCH_COUNT; - action++) - { - if (search_binding_action_map[action] == NULL) - continue; - - if (strcmp(key, search_binding_action_map[action]) != 0) - continue; - - /* Unset binding */ - if (strcasecmp(value, "none") == 0) { - tll_foreach(conf->bindings.search, it) { - if (it->item.action == action) - tll_remove(conf->bindings.search, it); - } - return true; - } - - key_combo_list_t key_combos = tll_init(); - if (!parse_key_combos(conf, value, &key_combos, path, lineno) || - has_search_binding_collisions(conf, action, &key_combos, path, lineno)) - { - free_key_combo_list(&key_combos); - return false; - } - - /* Remove existing bindings for this action */ - tll_foreach(conf->bindings.search, it) { - if (it->item.action == action) - tll_remove(conf->bindings.search, it); - } - - /* Emit key bindings */ - tll_foreach(key_combos, it) { - struct config_key_binding_search binding = { - .action = action, - .modifiers = it->item.modifiers, - .sym = it->item.sym, - }; - tll_push_back(conf->bindings.search, binding); - } - - free_key_combo_list(&key_combos); - return true; - } - - LOG_AND_NOTIFY_ERR("%s:%u: [search-bindings]: %s: invalid key", path, lineno, key); - return false; - + return parse_key_binding_section( + "search-bindings", key, value, BIND_ACTION_SEARCH_COUNT, + search_binding_action_map, &conf->bindings.search, conf, path, lineno); } static bool @@ -1511,56 +1414,9 @@ parse_section_url_bindings( const char *key, const char *value, struct config *conf, const char *path, unsigned lineno) { - for (enum bind_action_url action = 0; - action < BIND_ACTION_URL_COUNT; - action++) - { - if (url_binding_action_map[action] == NULL) - continue; - - if (strcmp(key, url_binding_action_map[action]) != 0) - continue; - - /* Unset binding */ - if (strcasecmp(value, "none") == 0) { - tll_foreach(conf->bindings.url, it) { - if (it->item.action == action) - tll_remove(conf->bindings.url, it); - } - return true; - } - - key_combo_list_t key_combos = tll_init(); - if (!parse_key_combos(conf, value, &key_combos, path, lineno) || - has_url_binding_collisions(conf, action, &key_combos, path, lineno)) - { - free_key_combo_list(&key_combos); - return false; - } - - /* Remove existing bindings for this action */ - tll_foreach(conf->bindings.url, it) { - if (it->item.action == action) - tll_remove(conf->bindings.url, it); - } - - /* Emit key bindings */ - tll_foreach(key_combos, it) { - struct config_key_binding_url binding = { - .action = action, - .modifiers = it->item.modifiers, - .sym = it->item.sym, - }; - tll_push_back(conf->bindings.url, binding); - } - - free_key_combo_list(&key_combos); - return true; - } - - LOG_AND_NOTIFY_ERR("%s:%u: [url-bindings]: %s: invalid key", path, lineno, key); - return false; - + return parse_key_binding_section( + "url-bindings", key, value, BIND_ACTION_URL_COUNT, + url_binding_action_map, &conf->bindings.url, conf, path, lineno); } static bool @@ -1659,9 +1515,10 @@ parse_mouse_combos(struct config *conf, const char *combos, key_combo_list_t *ke return true; err: - tll_foreach(*key_combos, it) + tll_foreach(*key_combos, it) { free(it->item.text); - tll_free(*key_combos); + tll_remove(*key_combos, it); + } free(copy); return false; } @@ -2134,7 +1991,7 @@ add_default_key_bindings(struct config *conf) do { \ tll_push_back( \ conf->bindings.key, \ - ((struct config_key_binding_normal){action, mods, sym})); \ + ((struct config_key_binding){action, mods, sym})); \ } while (0) const struct config_key_modifiers shift = {.shift = true}; @@ -2167,7 +2024,7 @@ add_default_search_bindings(struct config *conf) do { \ tll_push_back( \ conf->bindings.search, \ - ((struct config_key_binding_search){action, mods, sym})); \ + ((struct config_key_binding){action, mods, sym})); \ } while (0) const struct config_key_modifiers none = {0}; @@ -2215,7 +2072,7 @@ add_default_url_bindings(struct config *conf) do { \ tll_push_back( \ conf->bindings.url, \ - ((struct config_key_binding_url){action, mods, sym})); \ + ((struct config_key_binding){action, mods, sym})); \ } while (0) const struct config_key_modifiers none = {0}; @@ -2450,6 +2307,30 @@ free_spawn_template(struct config_spawn_template *template) free(template->argv); } +static void +binding_pipe_free(struct config_binding_pipe *pipe) +{ + if (pipe->master_copy) { + free(pipe->cmd); + free(pipe->argv); + } +} + +static void +key_binding_free(struct config_key_binding *binding) +{ + binding_pipe_free(&binding->pipe); +} + +static void +key_binding_list_free(config_key_binding_list_t *bindings) +{ + tll_foreach(*bindings, it) { + key_binding_free(&it->item); + tll_remove(*bindings, it); + } +} + void config_free(struct config conf) { @@ -2468,23 +2349,14 @@ config_free(struct config conf) } free(conf.server_socket_path); - tll_foreach(conf.bindings.key, it) { - if (it->item.pipe.master_copy) { - free(it->item.pipe.cmd); - free(it->item.pipe.argv); - } - } - tll_foreach(conf.bindings.mouse, it) { - if (it->item.pipe.master_copy) { - free(it->item.pipe.cmd); - free(it->item.pipe.argv); - } - } + key_binding_list_free(&conf.bindings.key); + key_binding_list_free(&conf.bindings.search); + key_binding_list_free(&conf.bindings.url); - tll_free(conf.bindings.key); - tll_free(conf.bindings.mouse); - tll_free(conf.bindings.search); - tll_free(conf.bindings.url); + tll_foreach(conf.bindings.mouse, it) { + binding_pipe_free(&it->item.pipe); + tll_remove(conf.bindings.mouse, it); + } user_notifications_free(&conf.notifications); } diff --git a/config.h b/config.h index 5ae70ae3..68a13434 100644 --- a/config.h +++ b/config.h @@ -25,39 +25,26 @@ struct config_key_modifiers { bool meta; }; -struct config_key_binding_normal { - enum bind_action_normal action; - struct config_key_modifiers modifiers; - xkb_keysym_t sym; - struct { - char *cmd; - char **argv; - bool master_copy; - } pipe; +struct config_binding_pipe { + char *cmd; + char **argv; + bool master_copy; }; -struct config_key_binding_search { - enum bind_action_search action; - struct config_key_modifiers modifiers; - xkb_keysym_t sym; -}; - -struct config_key_binding_url { - enum bind_action_url action; +struct config_key_binding { + int action; /* One of the varios bind_action_* enums from wayland.h */ struct config_key_modifiers modifiers; xkb_keysym_t sym; + struct config_binding_pipe pipe; }; +typedef tll(struct config_key_binding) config_key_binding_list_t; struct config_mouse_binding { enum bind_action_normal action; struct config_key_modifiers modifiers; int button; int count; - struct { - char *cmd; - char **argv; - bool master_copy; - } pipe; + struct config_binding_pipe pipe; }; /* If px != 0 then px is valid, otherwise pt is valid */ @@ -169,7 +156,7 @@ struct config { struct { /* Bindings for "normal" mode */ - tll(struct config_key_binding_normal) key; + config_key_binding_list_t key; tll(struct config_mouse_binding) mouse; /* @@ -178,10 +165,10 @@ struct config { /* While searching (not - action to *start* a search is in the * 'key' bindings above */ - tll(struct config_key_binding_search) search; + config_key_binding_list_t search; /* While showing URL jump labels */ - tll(struct config_key_binding_url) url; + config_key_binding_list_t url; } bindings; struct { diff --git a/input.c b/input.c index 69fce248..79368270 100644 --- a/input.c +++ b/input.c @@ -373,72 +373,39 @@ key_codes_for_xkb_sym(struct xkb_keymap *keymap, xkb_keysym_t sym) static void convert_key_binding(struct seat *seat, - const struct config_key_binding_normal *conf_binding) + const struct config_key_binding *conf_binding, + key_binding_list_t *bindings) { - struct key_binding_normal binding = { + struct key_binding binding = { + .mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers), + .sym = conf_binding->sym, + .key_codes = key_codes_for_xkb_sym( + seat->kbd.xkb_keymap, conf_binding->sym), .action = conf_binding->action, - .bind = { - .mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers), - .sym = conf_binding->sym, - .key_codes = key_codes_for_xkb_sym( - seat->kbd.xkb_keymap, conf_binding->sym), - }, .pipe_argv = conf_binding->pipe.argv, }; - tll_push_back(seat->kbd.bindings.key, binding); + tll_push_back(*bindings, binding); } static void convert_key_bindings(const struct config *conf, struct seat *seat) { tll_foreach(conf->bindings.key, it) - convert_key_binding(seat, &it->item); -} - -static void -convert_search_binding(struct seat *seat, - const struct config_key_binding_search *conf_binding) -{ - struct key_binding_search binding = { - .action = conf_binding->action, - .bind = { - .mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers), - .sym = conf_binding->sym, - .key_codes = key_codes_for_xkb_sym( - seat->kbd.xkb_keymap, conf_binding->sym), - }, - }; - tll_push_back(seat->kbd.bindings.search, binding); + convert_key_binding(seat, &it->item, &seat->kbd.bindings.key); } static void convert_search_bindings(const struct config *conf, struct seat *seat) { tll_foreach(conf->bindings.search, it) - convert_search_binding(seat, &it->item); -} - -static void -convert_url_binding(struct seat *seat, - const struct config_key_binding_url *conf_binding) -{ - struct key_binding_url binding = { - .action = conf_binding->action, - .bind = { - .mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers), - .sym = conf_binding->sym, - .key_codes = key_codes_for_xkb_sym( - seat->kbd.xkb_keymap, conf_binding->sym), - }, - }; - tll_push_back(seat->kbd.bindings.url, binding); + convert_key_binding(seat, &it->item, &seat->kbd.bindings.search); } static void convert_url_bindings(const struct config *conf, struct seat *seat) { tll_foreach(conf->bindings.url, it) - convert_url_binding(seat, &it->item); + convert_key_binding(seat, &it->item, &seat->kbd.bindings.url); } static void @@ -498,11 +465,11 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, } tll_foreach(seat->kbd.bindings.key, it) - tll_free(it->item.bind.key_codes); + tll_free(it->item.key_codes); tll_free(seat->kbd.bindings.key); tll_foreach(seat->kbd.bindings.search, it) - tll_free(it->item.bind.key_codes); + tll_free(it->item.key_codes); tll_free(seat->kbd.bindings.search); tll_free(seat->mouse.bindings); @@ -895,17 +862,17 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, * User configurable bindings */ tll_foreach(seat->kbd.bindings.key, it) { - if (it->item.bind.mods != effective_mods) + if (it->item.mods != effective_mods) continue; /* Match symbol */ - if (it->item.bind.sym == sym) { + if (it->item.sym == sym) { if (execute_binding(seat, term, it->item.action, it->item.pipe_argv, serial)) goto maybe_repeat; } /* Match raw key code */ - tll_foreach(it->item.bind.key_codes, code) { + tll_foreach(it->item.key_codes, code) { if (code->item == key) { if (execute_binding(seat, term, it->item.action, it->item.pipe_argv, serial)) goto maybe_repeat; diff --git a/search.c b/search.c index 0391c281..4fde8a92 100644 --- a/search.c +++ b/search.c @@ -807,11 +807,11 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key, /* Key bindings */ tll_foreach(seat->kbd.bindings.search, it) { - if (it->item.bind.mods != mods) + if (it->item.mods != mods) continue; /* Match symbol */ - if (it->item.bind.sym == sym) { + if (it->item.sym == sym) { if (execute_binding(seat, term, it->item.action, serial, &update_search_result, &redraw)) { @@ -821,7 +821,7 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key, } /* Match raw key code */ - tll_foreach(it->item.bind.key_codes, code) { + tll_foreach(it->item.key_codes, code) { if (code->item == key) { if (execute_binding(seat, term, it->item.action, serial, &update_search_result, &redraw)) diff --git a/url-mode.c b/url-mode.c index 40f3372f..304590ff 100644 --- a/url-mode.c +++ b/url-mode.c @@ -80,17 +80,17 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key, { /* Key bindings */ tll_foreach(seat->kbd.bindings.url, it) { - if (it->item.bind.mods != mods) + if (it->item.mods != mods) continue; /* Match symbol */ - if (it->item.bind.sym == sym) { + if (it->item.sym == sym) { execute_binding(seat, term, it->item.action, serial); return; } /* Match raw key code */ - tll_foreach(it->item.bind.key_codes, code) { + tll_foreach(it->item.key_codes, code) { if (code->item == key) { execute_binding(seat, term, it->item.action, serial); return; diff --git a/wayland.c b/wayland.c index 2a79bb10..fa3f873e 100644 --- a/wayland.c +++ b/wayland.c @@ -132,6 +132,15 @@ seat_add_text_input(struct seat *seat) #endif } +static void +key_bindings_destroy(key_binding_list_t *bindings) +{ + tll_foreach(*bindings, it) { + tll_free(it->item.key_codes); + tll_remove(*bindings, it); + } +} + static void seat_destroy(struct seat *seat) { @@ -140,17 +149,9 @@ seat_destroy(struct seat *seat) tll_free(seat->mouse.buttons); - tll_foreach(seat->kbd.bindings.key, it) - tll_free(it->item.bind.key_codes); - tll_free(seat->kbd.bindings.key); - - tll_foreach(seat->kbd.bindings.search, it) - tll_free(it->item.bind.key_codes); - tll_free(seat->kbd.bindings.search); - - tll_foreach(seat->kbd.bindings.url, it) - tll_free(it->item.bind.key_codes); - tll_free(seat->kbd.bindings.url); + key_bindings_destroy(&seat->kbd.bindings.key); + key_bindings_destroy(&seat->kbd.bindings.search); + key_bindings_destroy(&seat->kbd.bindings.url); tll_free(seat->mouse.bindings); diff --git a/wayland.h b/wayland.h index 3c160b7c..bd1102c4 100644 --- a/wayland.h +++ b/wayland.h @@ -18,15 +18,6 @@ /* Forward declarations */ struct terminal; -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_PAGE, @@ -65,21 +56,6 @@ enum bind_action_normal { BIND_ACTION_COUNT = BIND_ACTION_SELECT_ROW + 1, }; -struct key_binding_normal { - struct key_binding bind; - enum bind_action_normal action; - char **pipe_argv; -}; - -struct mouse_binding { - enum bind_action_normal action; - xkb_mod_mask_t mods; - uint32_t button; - int count; - char **pipe_argv; -}; -typedef tll(struct mouse_binding) mouse_binding_list_t; - enum bind_action_search { BIND_ACTION_SEARCH_NONE, BIND_ACTION_SEARCH_CANCEL, @@ -103,21 +79,32 @@ enum bind_action_search { BIND_ACTION_SEARCH_COUNT, }; -struct key_binding_search { - struct key_binding bind; - enum bind_action_search action; -}; - enum bind_action_url { BIND_ACTION_URL_NONE, BIND_ACTION_URL_CANCEL, BIND_ACTION_URL_COUNT, }; -struct key_binding_url { - struct key_binding bind; - enum bind_action_url 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; + + int action; /* enum bind_action_* */ + char **pipe_argv; }; +typedef tll(struct key_binding) key_binding_list_t; + +struct mouse_binding { + enum bind_action_normal action; + xkb_mod_mask_t mods; + uint32_t button; + int count; + char **pipe_argv; +}; +typedef tll(struct mouse_binding) mouse_binding_list_t; /* Mime-types we support when dealing with data offers (e.g. copy-paste, or DnD) */ enum data_offer_mime_type { @@ -203,9 +190,9 @@ struct seat { bool meta; struct { - tll(struct key_binding_normal) key; - tll(struct key_binding_search) search; - tll(struct key_binding_url) url; + key_binding_list_t key; + key_binding_list_t search; + key_binding_list_t url; } bindings; } kbd;