From 90a2ca966f657e5fdd82997aad9663e9984f3cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 17 Apr 2022 15:39:51 +0200 Subject: [PATCH] key-binding: new API, for handling sets of key bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now, our Wayland seats have been tracking key bindings. This makes sense, since the seat’s keymap determines how the key bindings are resolved. However, tying bindings to the seat/keymap alone isn’t enough, since we also depend on the current configuration (i.e. user settings) when resolving a key binding. This means configurations that doesn’t match the wayland object’s configuration, currently don’t resolve key bindings correctly. This applies to footclients where the user has overridden key bindings on the command line (e.g. --override key-bindings.foo=bar). Thus, to correctly resolve key bindings, each set of key bindings must be tied *both* to a seat/keymap, *and* a configuration. This patch introduces a key-binding manager, with an API to add/remove/lookup, and load/unload keymaps from sets of key bindings. In the API, sets are tied to a seat and terminal instance, since this makes the most sense (we need to instantiate, or incref a set whenever a new terminal instance is created). Internally, the set is tied to a seat and the terminal’s configuration. Sets are *added* when a new seat is added, and when a new terminal instance is created. Since there can only be one instance of each seat, sets are always removed when a seat is removed. Terminals on the other hand can re-use the same configuration (and typically do). Thus, sets ref-count the configuration. In other words, when instantiating a new terminal, we may not have to instantiate a new set of key bindings, but can often be incref:ed instead. Whenever the keymap changes on a seat, all key bindings sets associated with that seat reloads (re-resolves) their key bindings. Closes #931 --- config.c | 18 +- config.h | 5 + input.c | 253 ++----------------------- key-binding.c | 514 ++++++++++++++++++++++++++++++++++++++++++++++++++ key-binding.h | 143 ++++++++++++++ main.c | 10 +- meson.build | 1 + search.c | 6 +- search.h | 5 +- terminal.c | 4 + terminal.h | 3 +- url-mode.c | 6 +- url-mode.h | 4 +- wayland.c | 29 +-- wayland.h | 112 +---------- 15 files changed, 732 insertions(+), 381 deletions(-) create mode 100644 key-binding.c create mode 100644 key-binding.h diff --git a/config.c b/config.c index f9ddf5e4..10b899e0 100644 --- a/config.c +++ b/config.c @@ -23,10 +23,10 @@ #include "char32.h" #include "debug.h" #include "input.h" +#include "key-binding.h" #include "macros.h" #include "tokenize.h" #include "util.h" -#include "wayland.h" #include "xmalloc.h" #include "xsnprintf.h" @@ -3307,3 +3307,19 @@ check_if_font_is_monospaced(const char *pattern, fcft_destroy(f); return is_monospaced; } + +xkb_mod_mask_t +conf_modifiers_to_mask(const struct seat *seat, + const struct config_key_modifiers *modifiers) +{ + xkb_mod_mask_t mods = 0; + if (seat->kbd.mod_shift != XKB_MOD_INVALID) + mods |= modifiers->shift << seat->kbd.mod_shift; + if (seat->kbd.mod_ctrl != XKB_MOD_INVALID) + mods |= modifiers->ctrl << seat->kbd.mod_ctrl; + if (seat->kbd.mod_alt != XKB_MOD_INVALID) + mods |= modifiers->alt << seat->kbd.mod_alt; + if (seat->kbd.mod_super != XKB_MOD_INVALID) + mods |= modifiers->super << seat->kbd.mod_super; + return mods; +} diff --git a/config.h b/config.h index 38ff758f..2061415e 100644 --- a/config.h +++ b/config.h @@ -336,5 +336,10 @@ struct config *config_clone(const struct config *old); bool config_font_parse(const char *pattern, struct config_font *font); void config_font_list_destroy(struct config_font_list *font_list); +struct seat; +xkb_mod_mask_t +conf_modifiers_to_mask( + const struct seat *seat, const struct config_key_modifiers *modifiers); + bool check_if_font_is_monospaced( const char *pattern, user_notifications_t *notifications); diff --git a/input.c b/input.c index 403ee965..04d03bbe 100644 --- a/input.c +++ b/input.c @@ -380,237 +380,6 @@ execute_binding(struct seat *seat, struct terminal *term, return false; } -static xkb_mod_mask_t -conf_modifiers_to_mask(const struct seat *seat, - const struct config_key_modifiers *modifiers) -{ - xkb_mod_mask_t mods = 0; - if (seat->kbd.mod_shift != XKB_MOD_INVALID) - mods |= modifiers->shift << seat->kbd.mod_shift; - if (seat->kbd.mod_ctrl != XKB_MOD_INVALID) - mods |= modifiers->ctrl << seat->kbd.mod_ctrl; - if (seat->kbd.mod_alt != XKB_MOD_INVALID) - mods |= modifiers->alt << seat->kbd.mod_alt; - if (seat->kbd.mod_super != XKB_MOD_INVALID) - mods |= modifiers->super << seat->kbd.mod_super; - return mods; -} - -static xkb_keycode_list_t -key_codes_for_xkb_sym(struct xkb_keymap *keymap, xkb_keysym_t sym) -{ - xkb_keycode_list_t key_codes = tll_init(); - - /* - * Find all key codes that map to this symbol. - * - * This allows us to match bindings in other layouts - * too. - */ - struct xkb_state *state = xkb_state_new(keymap); - - for (xkb_keycode_t code = xkb_keymap_min_keycode(keymap); - code <= xkb_keymap_max_keycode(keymap); - code++) - { - if (xkb_state_key_get_one_sym(state, code) == sym) - tll_push_back(key_codes, code); - } - - xkb_state_unref(state); - return key_codes; -} - -static xkb_keysym_t -maybe_repair_key_combo(const struct seat *seat, - xkb_keysym_t sym, xkb_mod_mask_t mods) -{ - /* - * Detect combos containing a shifted symbol and the corresponding - * modifier, and replace the shifted symbol with its unshifted - * variant. - * - * For example, the combo is “Control+Shift+U”. In this case, - * Shift is the modifier used to “shift” ‘u’ to ‘U’, after which - * ‘Shift’ will have been “consumed”. Since we filter out consumed - * modifiers when matching key combos, this key combo will never - * trigger (we will never be able to match the ‘Shift’ modifier). - * - * There are two correct variants of the above key combo: - * - “Control+U” (upper case ‘U’) - * - “Control+Shift+u” (lower case ‘u’) - * - * What we do here is, for each key *code*, check if there are any - * (shifted) levels where it produces ‘sym’. If there are, check - * *which* sets of modifiers are needed to produce it, and compare - * with ‘mods’. - * - * If there is at least one common modifier, it means ‘sym’ is a - * “shifted” symbol, with the corresponding shifting modifier - * explicitly included in the key combo. I.e. the key combo will - * never trigger. - * - * We then proceed and “repair” the key combo by replacing ‘sym’ - * with the corresponding unshifted symbol. - * - * To reduce the noise, we ignore all key codes where the shifted - * symbol is the same as the unshifted symbol. - */ - - for (xkb_keycode_t code = xkb_keymap_min_keycode(seat->kbd.xkb_keymap); - code <= xkb_keymap_max_keycode(seat->kbd.xkb_keymap); - code++) - { - xkb_layout_index_t layout_idx = - xkb_state_key_get_layout(seat->kbd.xkb_state, code); - - /* Get all unshifted symbols for this key */ - const xkb_keysym_t *base_syms = NULL; - size_t base_count = xkb_keymap_key_get_syms_by_level( - seat->kbd.xkb_keymap, code, layout_idx, 0, &base_syms); - - if (base_count == 0 || sym == base_syms[0]) { - /* No unshifted symbols, or unshifted symbol is same as ‘sym’ */ - continue; - } - - /* Name of the unshifted symbol, for logging */ - char base_name[100]; - xkb_keysym_get_name(base_syms[0], base_name, sizeof(base_name)); - - /* Iterate all shift levels */ - for (xkb_level_index_t level_idx = 1; - level_idx < xkb_keymap_num_levels_for_key( - seat->kbd.xkb_keymap, code, layout_idx); - level_idx++) { - - /* Get all symbols for current shift level */ - const xkb_keysym_t *shifted_syms = NULL; - size_t shifted_count = xkb_keymap_key_get_syms_by_level( - seat->kbd.xkb_keymap, code, - layout_idx, level_idx, &shifted_syms); - - for (size_t i = 0; i < shifted_count; i++) { - if (shifted_syms[i] != sym) - continue; - - /* Get modifier sets that produces the current shift level */ - xkb_mod_mask_t mod_masks[16]; - size_t mod_mask_count = xkb_keymap_key_get_mods_for_level( - seat->kbd.xkb_keymap, code, layout_idx, level_idx, - mod_masks, ALEN(mod_masks)); - - /* Check if key combo’s modifier set intersects */ - for (size_t j = 0; j < mod_mask_count; j++) { - if ((mod_masks[j] & mods) != mod_masks[j]) - continue; - - char combo[64] = {0}; - - for (int k = 0; k < sizeof(xkb_mod_mask_t) * 8; k++) { - if (!(mods & (1u << k))) - continue; - - const char *mod_name = xkb_keymap_mod_get_name( - seat->kbd.xkb_keymap, k); - strcat(combo, mod_name); - strcat(combo, "+"); - } - - size_t len = strlen(combo); - xkb_keysym_get_name( - sym, &combo[len], sizeof(combo) - len); - - LOG_WARN( - "%s: combo with both explicit modifier and shifted symbol " - "(level=%d, mod-mask=0x%08x), " - "replacing with %s", - combo, level_idx, mod_masks[j], base_name); - - /* Replace with unshifted symbol */ - return base_syms[0]; - } - } - } - } - - return sym; -} - -static void -convert_key_binding(const struct seat *seat, - const struct config_key_binding *conf_binding, - key_binding_list_t *bindings) -{ - xkb_mod_mask_t mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers); - xkb_keysym_t sym = maybe_repair_key_combo(seat, conf_binding->k.sym, mods); - - struct key_binding binding = { - .type = KEY_BINDING, - .action = conf_binding->action, - .aux = &conf_binding->aux, - .mods = mods, - .k = { - .sym = sym, - .key_codes = key_codes_for_xkb_sym(seat->kbd.xkb_keymap, sym), - }, - }; - tll_push_back(*bindings, binding); -} - -static void -convert_key_bindings(const struct config *conf, struct seat *seat) -{ - for (size_t i = 0; i < conf->bindings.key.count; i++) { - const struct config_key_binding *binding = &conf->bindings.key.arr[i]; - convert_key_binding(seat, binding, &seat->kbd.bindings.key); - } -} - -static void -convert_search_bindings(const struct config *conf, struct seat *seat) -{ - for (size_t i = 0; i < conf->bindings.search.count; i++) { - const struct config_key_binding *binding = &conf->bindings.search.arr[i]; - convert_key_binding(seat, binding, &seat->kbd.bindings.search); - } -} - -static void -convert_url_bindings(const struct config *conf, struct seat *seat) -{ - for (size_t i = 0; i < conf->bindings.url.count; i++) { - const struct config_key_binding *binding = &conf->bindings.url.arr[i]; - convert_key_binding(seat, binding, &seat->kbd.bindings.url); - } -} - -static void -convert_mouse_binding(struct seat *seat, - const struct config_key_binding *conf_binding) -{ - struct key_binding binding = { - .type = MOUSE_BINDING, - .action = conf_binding->action, - .aux = &conf_binding->aux, - .mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers), - .m = { - .button = conf_binding->m.button, - .count = conf_binding->m.count, - }, - }; - tll_push_back(seat->mouse.bindings, binding); -} - -static void -convert_mouse_bindings(const struct config *conf, struct seat *seat) -{ - for (size_t i = 0; i < conf->bindings.mouse.count; i++) { - const struct config_key_binding *binding = &conf->bindings.mouse.arr[i]; - convert_mouse_binding(seat, binding); - } -} - static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) @@ -646,7 +415,7 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, seat->kbd.xkb = NULL; } - wayl_bindings_reset(seat); + key_binding_unload_keymap(wayl->key_binding_manager, seat); /* Verify keymap is in a format we understand */ switch ((enum wl_keyboard_keymap_format)format) { @@ -728,10 +497,7 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, munmap(map_str, size); close(fd); - convert_key_bindings(wayl->conf, seat); - convert_search_bindings(wayl->conf, seat); - convert_url_bindings(wayl->conf, seat); - convert_mouse_bindings(wayl->conf, seat); + key_binding_load_keymap(wayl->key_binding_manager, seat); } static void @@ -1558,13 +1324,17 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, size_t raw_count = xkb_keymap_key_get_syms_by_level( seat->kbd.xkb_keymap, key, layout_idx, 0, &raw_syms); + const struct key_binding_set *bindings = key_binding_for( + seat->wayl->key_binding_manager, term, seat); + xassert(bindings != NULL); + if (pressed) { if (term->is_searching) { if (should_repeat) start_repeater(seat, key); search_input( - seat, term, key, sym, mods, consumed, locked, + seat, term, bindings, key, sym, mods, consumed, locked, raw_syms, raw_count, serial); return; } @@ -1574,7 +1344,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, start_repeater(seat, key); urls_input( - seat, term, key, sym, mods, consumed, locked, + seat, term, bindings, key, sym, mods, consumed, locked, raw_syms, raw_count, serial); return; } @@ -1602,7 +1372,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, * User configurable bindings */ if (pressed) { - tll_foreach(seat->kbd.bindings.key, it) { + tll_foreach(bindings->key, it) { const struct key_binding *bind = &it->item; /* Match translated symbol */ @@ -2475,8 +2245,11 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, mods &= ~seat->kbd.selection_override_modmask; const struct key_binding *match = NULL; + const struct key_binding_set *bindings = key_binding_for( + wayl->key_binding_manager, term, seat); + xassert(bindings != NULL); - tll_foreach(seat->mouse.bindings, it) { + tll_foreach(bindings->mouse, it) { const struct key_binding *binding = &it->item; if (binding->m.button != button) { diff --git a/key-binding.c b/key-binding.c new file mode 100644 index 00000000..81a9f6a2 --- /dev/null +++ b/key-binding.c @@ -0,0 +1,514 @@ +#include "key-binding.h" + +#include + +#define LOG_MODULE "key-binding" +#define LOG_ENABLE_DBG 1 +#include "log.h" + +#include "config.h" +#include "debug.h" +#include "terminal.h" +#include "util.h" +#include "wayland.h" +#include "xmalloc.h" + +struct key_set { + struct key_binding_set public; + + const struct config *conf; + const struct seat *seat; + size_t conf_ref_count; +}; +typedef tll(struct key_set) bind_set_list_t; + +struct key_binding_manager { + struct key_set *last_used_set; + bind_set_list_t binding_sets; +}; + +static void load_keymap(struct key_set *set); +static void unload_keymap(struct key_set *set); + +struct key_binding_manager * +key_binding_manager_new(void) +{ + struct key_binding_manager *mgr = xcalloc(1, sizeof(*mgr)); + return mgr; +} + +void +key_binding_manager_destroy(struct key_binding_manager *mgr) +{ + xassert(tll_length(mgr->binding_sets) == 0); + free(mgr); +} + +void +key_binding_new_for_seat(struct key_binding_manager *mgr, + const struct seat *seat) +{ +#if defined(_DEBUG) + tll_foreach(mgr->binding_sets, it) + xassert(it->item.seat != seat); +#endif + + tll_foreach(seat->wayl->terms, it) { + struct key_set set = { + .public = { + .key = tll_init(), + .search = tll_init(), + .url = tll_init(), + .mouse = tll_init(), + }, + .conf = it->item->conf, + .seat = seat, + .conf_ref_count = 1, + }; + + tll_push_back(mgr->binding_sets, set); + + LOG_DBG("new (seat): set=%p, seat=%p, conf=%p, ref-count=1", + (void *)&tll_back(mgr->binding_sets), + (void *)set.seat, (void *)set.conf); + + load_keymap(&tll_back(mgr->binding_sets)); + } + + LOG_DBG("new (seat): total number of sets: %zu", + tll_length(mgr->binding_sets)); +} + +void +key_binding_new_for_term(struct key_binding_manager *mgr, + const struct terminal *term) +{ + const struct config *conf = term->conf; + const struct wayland *wayl = term->wl; + + tll_foreach(wayl->seats, it) { + struct seat *seat = &it->item; + + struct key_set *existing = + (struct key_set *)key_binding_for(mgr, term, seat); + + if (existing != NULL) { + existing->conf_ref_count++; + continue; + } + + struct key_set set = { + .public = { + .key = tll_init(), + .search = tll_init(), + .url = tll_init(), + .mouse = tll_init(), + }, + .conf = conf, + .seat = seat, + .conf_ref_count = 1, + }; + + tll_push_back(mgr->binding_sets, set); + + load_keymap(&tll_back(mgr->binding_sets)); + + /* Chances are high this set will be requested next */ + mgr->last_used_set = &tll_back(mgr->binding_sets); + + LOG_DBG("new (term): set=%p, seat=%p, conf=%p, ref-count=1", + (void *)&tll_back(mgr->binding_sets), + (void *)set.seat, (void *)set.conf); + } + + LOG_DBG("new (term): total number of sets: %zu", + tll_length(mgr->binding_sets)); +} + +struct key_binding_set * +key_binding_for(struct key_binding_manager *mgr, const struct terminal *term, + const struct seat *seat) +{ + const struct config *conf = term->conf; + + struct key_set *last_used = mgr->last_used_set; + if (last_used != NULL && + last_used->conf == conf && + last_used->seat == seat) + { + // LOG_DBG("lookup: last used"); + return &last_used->public; + } + + tll_foreach(mgr->binding_sets, it) { + struct key_set *set = &it->item; + + if (set->conf != conf) + continue; + if (set->seat != seat) + continue; + +#if 0 + LOG_DBG("lookup: set=%p, seat=%p, conf=%p, ref-count=%zu", + (void *)set, (void *)seat, (void *)conf, set->conf_ref_count); +#endif + mgr->last_used_set = set; + return &set->public; + } + + return NULL; +} + +static void +key_binding_set_destroy(struct key_binding_manager *mgr, + struct key_set *set) +{ + unload_keymap(set); + if (mgr->last_used_set == set) + mgr->last_used_set = NULL; + + /* Note: caller must remove from binding_sets */ +} + +void +key_binding_remove_seat(struct key_binding_manager *mgr, + const struct seat *seat) +{ + tll_foreach(mgr->binding_sets, it) { + struct key_set *set = &it->item; + + if (set->seat != seat) + continue; + + key_binding_set_destroy(mgr, set); + tll_remove(mgr->binding_sets, it); + + LOG_DBG("remove seat: set=%p, seat=%p, total number of sets: %zu", + (void *)set, (void *)seat, tll_length(mgr->binding_sets)); + } + + LOG_DBG("remove seat: total number of sets: %zu", + tll_length(mgr->binding_sets)); +} + +void +key_binding_unref_term(struct key_binding_manager *mgr, + const struct terminal *term) +{ + const struct config *conf = term->conf; + + tll_foreach(mgr->binding_sets, it) { + struct key_set *set = &it->item; + + if (set->conf != conf) + continue; + + xassert(set->conf_ref_count > 0); + if (--set->conf_ref_count == 0) { + LOG_DBG("unref conf: set=%p, seat=%p, conf=%p", + (void *)set, (void *)set->seat, (void *)conf); + + key_binding_set_destroy(mgr, set); + tll_remove(mgr->binding_sets, it); + } + } + + LOG_DBG("unref conf: total number of sets: %zu", + tll_length(mgr->binding_sets)); +} + +static xkb_keycode_list_t +key_codes_for_xkb_sym(struct xkb_keymap *keymap, xkb_keysym_t sym) +{ + xkb_keycode_list_t key_codes = tll_init(); + + /* + * Find all key codes that map to this symbol. + * + * This allows us to match bindings in other layouts + * too. + */ + struct xkb_state *state = xkb_state_new(keymap); + + for (xkb_keycode_t code = xkb_keymap_min_keycode(keymap); + code <= xkb_keymap_max_keycode(keymap); + code++) + { + if (xkb_state_key_get_one_sym(state, code) == sym) + tll_push_back(key_codes, code); + } + + xkb_state_unref(state); + return key_codes; +} + +static xkb_keysym_t +maybe_repair_key_combo(const struct seat *seat, + xkb_keysym_t sym, xkb_mod_mask_t mods) +{ + /* + * Detect combos containing a shifted symbol and the corresponding + * modifier, and replace the shifted symbol with its unshifted + * variant. + * + * For example, the combo is “Control+Shift+U”. In this case, + * Shift is the modifier used to “shift” ‘u’ to ‘U’, after which + * ‘Shift’ will have been “consumed”. Since we filter out consumed + * modifiers when matching key combos, this key combo will never + * trigger (we will never be able to match the ‘Shift’ modifier). + * + * There are two correct variants of the above key combo: + * - “Control+U” (upper case ‘U’) + * - “Control+Shift+u” (lower case ‘u’) + * + * What we do here is, for each key *code*, check if there are any + * (shifted) levels where it produces ‘sym’. If there are, check + * *which* sets of modifiers are needed to produce it, and compare + * with ‘mods’. + * + * If there is at least one common modifier, it means ‘sym’ is a + * “shifted” symbol, with the corresponding shifting modifier + * explicitly included in the key combo. I.e. the key combo will + * never trigger. + * + * We then proceed and “repair” the key combo by replacing ‘sym’ + * with the corresponding unshifted symbol. + * + * To reduce the noise, we ignore all key codes where the shifted + * symbol is the same as the unshifted symbol. + */ + + for (xkb_keycode_t code = xkb_keymap_min_keycode(seat->kbd.xkb_keymap); + code <= xkb_keymap_max_keycode(seat->kbd.xkb_keymap); + code++) + { + xkb_layout_index_t layout_idx = + xkb_state_key_get_layout(seat->kbd.xkb_state, code); + + /* Get all unshifted symbols for this key */ + const xkb_keysym_t *base_syms = NULL; + size_t base_count = xkb_keymap_key_get_syms_by_level( + seat->kbd.xkb_keymap, code, layout_idx, 0, &base_syms); + + if (base_count == 0 || sym == base_syms[0]) { + /* No unshifted symbols, or unshifted symbol is same as ‘sym’ */ + continue; + } + + /* Name of the unshifted symbol, for logging */ + char base_name[100]; + xkb_keysym_get_name(base_syms[0], base_name, sizeof(base_name)); + + /* Iterate all shift levels */ + for (xkb_level_index_t level_idx = 1; + level_idx < xkb_keymap_num_levels_for_key( + seat->kbd.xkb_keymap, code, layout_idx); + level_idx++) { + + /* Get all symbols for current shift level */ + const xkb_keysym_t *shifted_syms = NULL; + size_t shifted_count = xkb_keymap_key_get_syms_by_level( + seat->kbd.xkb_keymap, code, + layout_idx, level_idx, &shifted_syms); + + for (size_t i = 0; i < shifted_count; i++) { + if (shifted_syms[i] != sym) + continue; + + /* Get modifier sets that produces the current shift level */ + xkb_mod_mask_t mod_masks[16]; + size_t mod_mask_count = xkb_keymap_key_get_mods_for_level( + seat->kbd.xkb_keymap, code, layout_idx, level_idx, + mod_masks, ALEN(mod_masks)); + + /* Check if key combo’s modifier set intersects */ + for (size_t j = 0; j < mod_mask_count; j++) { + if ((mod_masks[j] & mods) != mod_masks[j]) + continue; + + char combo[64] = {0}; + + for (int k = 0; k < sizeof(xkb_mod_mask_t) * 8; k++) { + if (!(mods & (1u << k))) + continue; + + const char *mod_name = xkb_keymap_mod_get_name( + seat->kbd.xkb_keymap, k); + strcat(combo, mod_name); + strcat(combo, "+"); + } + + size_t len = strlen(combo); + xkb_keysym_get_name( + sym, &combo[len], sizeof(combo) - len); + + LOG_WARN( + "%s: combo with both explicit modifier and shifted symbol " + "(level=%d, mod-mask=0x%08x), " + "replacing with %s", + combo, level_idx, mod_masks[j], base_name); + + /* Replace with unshifted symbol */ + return base_syms[0]; + } + } + } + } + + return sym; +} + +static void +convert_key_binding(struct key_set *set, + const struct config_key_binding *conf_binding, + key_binding_list_t *bindings) +{ + const struct seat *seat = set->seat; + + xkb_mod_mask_t mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers); + xkb_keysym_t sym = maybe_repair_key_combo(seat, conf_binding->k.sym, mods); + + struct key_binding binding = { + .type = KEY_BINDING, + .action = conf_binding->action, + .aux = &conf_binding->aux, + .mods = mods, + .k = { + .sym = sym, + .key_codes = key_codes_for_xkb_sym(seat->kbd.xkb_keymap, sym), + }, + }; + tll_push_back(*bindings, binding); +} + +static void +convert_key_bindings(struct key_set *set) +{ + const struct config *conf = set->conf; + + for (size_t i = 0; i < conf->bindings.key.count; i++) { + const struct config_key_binding *binding = &conf->bindings.key.arr[i]; + convert_key_binding(set, binding, &set->public.key); + } +} + +static void +convert_search_bindings(struct key_set *set) +{ + const struct config *conf = set->conf; + + for (size_t i = 0; i < conf->bindings.search.count; i++) { + const struct config_key_binding *binding = &conf->bindings.search.arr[i]; + convert_key_binding(set, binding, &set->public.search); + } +} + +static void +convert_url_bindings(struct key_set *set) +{ + const struct config *conf = set->conf; + + for (size_t i = 0; i < conf->bindings.url.count; i++) { + const struct config_key_binding *binding = &conf->bindings.url.arr[i]; + convert_key_binding(set, binding, &set->public.url); + } +} + +static void +convert_mouse_binding(struct key_set *set, + const struct config_key_binding *conf_binding) +{ + struct key_binding binding = { + .type = MOUSE_BINDING, + .action = conf_binding->action, + .aux = &conf_binding->aux, + .mods = conf_modifiers_to_mask(set->seat, &conf_binding->modifiers), + .m = { + .button = conf_binding->m.button, + .count = conf_binding->m.count, + }, + }; + tll_push_back(set->public.mouse, binding); +} + +static void +convert_mouse_bindings(struct key_set *set) +{ + const struct config *conf = set->conf; + + for (size_t i = 0; i < conf->bindings.mouse.count; i++) { + const struct config_key_binding *binding = + &conf->bindings.mouse.arr[i]; + convert_mouse_binding(set, binding); + } +} + +static void +load_keymap(struct key_set *set) +{ + LOG_DBG("load keymap: set=%p, seat=%p, conf=%p", + (void *)set, (void *)set->seat, (void *)set->conf); + + if (set->seat->kbd.xkb_state == NULL || + set->seat->kbd.xkb_keymap == NULL) + { + LOG_DBG("no XKB keymap"); + return; + } + + convert_key_bindings(set); + convert_search_bindings(set); + convert_url_bindings(set); + convert_mouse_bindings(set); +} + +void +key_binding_load_keymap(struct key_binding_manager *mgr, + const struct seat *seat) +{ + tll_foreach(mgr->binding_sets, it) { + struct key_set *set = &it->item; + + if (set->seat == seat) + load_keymap(set); + } +} + +static void NOINLINE +key_bindings_destroy(key_binding_list_t *bindings) +{ + tll_foreach(*bindings, it) { + struct key_binding *bind = &it->item; + switch (bind->type) { + case KEY_BINDING: tll_free(it->item.k.key_codes); break; + case MOUSE_BINDING: break; + } + + tll_remove(*bindings, it); + } +} + +static void +unload_keymap(struct key_set *set) +{ + key_bindings_destroy(&set->public.key); + key_bindings_destroy(&set->public.search); + key_bindings_destroy(&set->public.url); + key_bindings_destroy(&set->public.mouse); +} + +void +key_binding_unload_keymap(struct key_binding_manager *mgr, + const struct seat *seat) +{ + tll_foreach(mgr->binding_sets, it) { + struct key_set *set = &it->item; + if (set->seat != seat) + continue; + + LOG_DBG("unload keymap: set=%p, seat=%p, conf=%p", + (void *)set, (void *)seat, (void *)set->conf); + + unload_keymap(set); + } +} diff --git a/key-binding.h b/key-binding.h new file mode 100644 index 00000000..a8301fd4 --- /dev/null +++ b/key-binding.h @@ -0,0 +1,143 @@ +#pragma once + +#include + +#include +#include + +#include "config.h" + +enum bind_action_normal { + BIND_ACTION_NONE, + BIND_ACTION_NOOP, + BIND_ACTION_SCROLLBACK_UP_PAGE, + BIND_ACTION_SCROLLBACK_UP_HALF_PAGE, + BIND_ACTION_SCROLLBACK_UP_LINE, + BIND_ACTION_SCROLLBACK_DOWN_PAGE, + BIND_ACTION_SCROLLBACK_DOWN_HALF_PAGE, + BIND_ACTION_SCROLLBACK_DOWN_LINE, + BIND_ACTION_SCROLLBACK_HOME, + BIND_ACTION_SCROLLBACK_END, + BIND_ACTION_CLIPBOARD_COPY, + BIND_ACTION_CLIPBOARD_PASTE, + BIND_ACTION_PRIMARY_PASTE, + BIND_ACTION_SEARCH_START, + BIND_ACTION_FONT_SIZE_UP, + BIND_ACTION_FONT_SIZE_DOWN, + BIND_ACTION_FONT_SIZE_RESET, + BIND_ACTION_SPAWN_TERMINAL, + BIND_ACTION_MINIMIZE, + BIND_ACTION_MAXIMIZE, + BIND_ACTION_FULLSCREEN, + BIND_ACTION_PIPE_SCROLLBACK, + BIND_ACTION_PIPE_VIEW, + BIND_ACTION_PIPE_SELECTED, + BIND_ACTION_SHOW_URLS_COPY, + BIND_ACTION_SHOW_URLS_LAUNCH, + BIND_ACTION_SHOW_URLS_PERSISTENT, + BIND_ACTION_TEXT_BINDING, + + /* Mouse specific actions - i.e. they require a mouse coordinate */ + BIND_ACTION_SELECT_BEGIN, + BIND_ACTION_SELECT_BEGIN_BLOCK, + BIND_ACTION_SELECT_EXTEND, + BIND_ACTION_SELECT_EXTEND_CHAR_WISE, + BIND_ACTION_SELECT_WORD, + BIND_ACTION_SELECT_WORD_WS, + BIND_ACTION_SELECT_ROW, + + BIND_ACTION_KEY_COUNT = BIND_ACTION_TEXT_BINDING + 1, + BIND_ACTION_COUNT = BIND_ACTION_SELECT_ROW + 1, +}; + +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_CLIPBOARD_PASTE, + BIND_ACTION_SEARCH_PRIMARY_PASTE, + BIND_ACTION_SEARCH_COUNT, +}; + +enum bind_action_url { + BIND_ACTION_URL_NONE, + BIND_ACTION_URL_CANCEL, + BIND_ACTION_URL_TOGGLE_URL_ON_JUMP_LABEL, + BIND_ACTION_URL_COUNT, +}; + +typedef tll(xkb_keycode_t) xkb_keycode_list_t; + +struct key_binding { + enum key_binding_type type; + + int action; /* enum bind_action_* */ + xkb_mod_mask_t mods; + + union { + struct { + xkb_keysym_t sym; + xkb_keycode_list_t key_codes; + } k; + + struct { + uint32_t button; + int count; + } m; + }; + + const struct binding_aux *aux; +}; +typedef tll(struct key_binding) key_binding_list_t; + +struct terminal; +struct seat; + +struct key_binding_set { + key_binding_list_t key; + key_binding_list_t search; + key_binding_list_t url; + key_binding_list_t mouse; +}; + +struct key_binding_manager; + +struct key_binding_manager *key_binding_manager_new(void); +void key_binding_manager_destroy(struct key_binding_manager *mgr); + +void key_binding_new_for_seat( + struct key_binding_manager *mgr, const struct seat *seat); + +void key_binding_new_for_term( + struct key_binding_manager *mgr, const struct terminal *term); + +/* Returns the set of key bindings associated with this seat/term pair */ +struct key_binding_set *key_binding_for( + struct key_binding_manager *mgr, const struct terminal *term, + const struct seat *seat); + +/* Remove all key bindings tied to the specified seat */ +void key_binding_remove_seat( + struct key_binding_manager *mgr, const struct seat *seat); + +void key_binding_unref_term( + struct key_binding_manager *mgr, const struct terminal *term); + +void key_binding_load_keymap( + struct key_binding_manager *mgr, const struct seat *seat); +void key_binding_unload_keymap( + struct key_binding_manager *mgr, const struct seat *seat); diff --git a/main.c b/main.c index 58399a93..0acb7ea6 100644 --- a/main.c +++ b/main.c @@ -21,8 +21,9 @@ #include "log.h" #include "config.h" -#include "foot-features.h" #include "fdm.h" +#include "foot-features.h" +#include "key-binding.h" #include "macros.h" #include "reaper.h" #include "render.h" @@ -569,6 +570,7 @@ main(int argc, char *const *argv) struct fdm *fdm = NULL; struct reaper *reaper = NULL; + struct key_binding_manager *key_binding_manager = NULL; struct wayland *wayl = NULL; struct renderer *renderer = NULL; struct terminal *term = NULL; @@ -600,7 +602,10 @@ main(int argc, char *const *argv) if ((reaper = reaper_init(fdm)) == NULL) goto out; - if ((wayl = wayl_init(&conf, fdm)) == NULL) + if ((key_binding_manager = key_binding_manager_new()) == NULL) + goto out; + + if ((wayl = wayl_init(&conf, fdm, key_binding_manager)) == NULL) goto out; if ((renderer = render_init(fdm, wayl)) == NULL) @@ -658,6 +663,7 @@ out: shm_fini(); render_destroy(renderer); wayl_destroy(wayl); + key_binding_manager_destroy(key_binding_manager); reaper_destroy(reaper); fdm_signal_del(fdm, SIGTERM); fdm_signal_del(fdm, SIGINT); diff --git a/meson.build b/meson.build index 735419f9..c552d2a2 100644 --- a/meson.build +++ b/meson.build @@ -210,6 +210,7 @@ executable( 'foot-features.h', 'ime.c', 'ime.h', 'input.c', 'input.h', + 'key-binding.c', 'key-binding.h', 'main.c', 'notify.c', 'notify.h', 'quirks.c', 'quirks.h', diff --git a/search.c b/search.c index 2c2941ff..2a7903d7 100644 --- a/search.c +++ b/search.c @@ -13,6 +13,7 @@ #include "extract.h" #include "grid.h" #include "input.h" +#include "key-binding.h" #include "misc.h" #include "render.h" #include "selection.h" @@ -899,7 +900,8 @@ execute_binding(struct seat *seat, struct terminal *term, } void -search_input(struct seat *seat, struct terminal *term, uint32_t key, +search_input(struct seat *seat, struct terminal *term, + const struct key_binding_set *bindings, uint32_t key, xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed, xkb_mod_mask_t locked, const xkb_keysym_t *raw_syms, size_t raw_count, @@ -919,7 +921,7 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key, bool redraw = false; /* Key bindings */ - tll_foreach(seat->kbd.bindings.search, it) { + tll_foreach(bindings->search, it) { const struct key_binding *bind = &it->item; /* Match translated symbol */ diff --git a/search.h b/search.h index 24828512..d5d4162b 100644 --- a/search.h +++ b/search.h @@ -1,12 +1,15 @@ #pragma once #include + +#include "key-binding.h" #include "terminal.h" void search_begin(struct terminal *term); void search_cancel(struct terminal *term); void search_input( - struct seat *seat, struct terminal *term, uint32_t key, + struct seat *seat, struct terminal *term, + const struct key_binding_set *bindings, uint32_t key, xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed, xkb_mod_mask_t locked, const xkb_keysym_t *raw_syms, size_t raw_count, diff --git a/terminal.c b/terminal.c index 8a8a5451..2bb35ef5 100644 --- a/terminal.c +++ b/terminal.c @@ -1262,6 +1262,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, memcpy(term->colors.table, term->conf->colors.table, sizeof(term->colors.table)); + key_binding_new_for_term(wayl->key_binding_manager, term); + /* Initialize the Wayland window backend */ if ((term->window = wayl_win_init(term, token)) == NULL) goto err; @@ -1577,6 +1579,8 @@ term_destroy(struct terminal *term) if (term == NULL) return 0; + key_binding_unref_term(term->wl->key_binding_manager, term); + tll_foreach(term->wl->terms, it) { if (it->item == term) { tll_remove(term->wl->terms, it); diff --git a/terminal.h b/terminal.h index 042af6dc..6c566e6e 100644 --- a/terminal.h +++ b/terminal.h @@ -14,10 +14,11 @@ #include #include -#include "config.h" #include "composed.h" +#include "config.h" #include "debug.h" #include "fdm.h" +#include "key-binding.h" #include "macros.h" #include "reaper.h" #include "shm.h" diff --git a/url-mode.c b/url-mode.c index 5b144e9f..8a46c6bf 100644 --- a/url-mode.c +++ b/url-mode.c @@ -13,6 +13,7 @@ #include "log.h" #include "char32.h" #include "grid.h" +#include "key-binding.h" #include "render.h" #include "selection.h" #include "spawn.h" @@ -118,7 +119,8 @@ activate_url(struct seat *seat, struct terminal *term, const struct url *url) } void -urls_input(struct seat *seat, struct terminal *term, uint32_t key, +urls_input(struct seat *seat, struct terminal *term, + const struct key_binding_set *bindings, uint32_t key, xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed, xkb_mod_mask_t locked, const xkb_keysym_t *raw_syms, size_t raw_count, @@ -130,7 +132,7 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key, consumed & seat->kbd.bind_significant & ~locked; /* Key bindings */ - tll_foreach(seat->kbd.bindings.url, it) { + tll_foreach(bindings->url, it) { const struct key_binding *bind = &it->item; /* Match translated symbol */ diff --git a/url-mode.h b/url-mode.h index 0f1451da..abfcb57b 100644 --- a/url-mode.h +++ b/url-mode.h @@ -5,6 +5,7 @@ #include #include "config.h" +#include "key-binding.h" #include "terminal.h" static inline bool urls_mode_is_active(const struct terminal *term) @@ -19,7 +20,8 @@ void urls_assign_key_combos(const struct config *conf, url_list_t *urls); void urls_render(struct terminal *term); void urls_reset(struct terminal *term); -void urls_input(struct seat *seat, struct terminal *term, uint32_t key, +void urls_input(struct seat *seat, struct terminal *term, + const struct key_binding_set *bindings, uint32_t key, xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed, xkb_mod_mask_t locked, const xkb_keysym_t *raw_syms, size_t raw_count, diff --git a/wayland.c b/wayland.c index 7ffbe4d5..2eeb1d72 100644 --- a/wayland.c +++ b/wayland.c @@ -156,27 +156,9 @@ seat_add_text_input(struct seat *seat) } static void -key_bindings_destroy(key_binding_list_t *bindings) +seat_add_key_bindings(struct seat *seat) { - tll_foreach(*bindings, it) { - struct key_binding *bind = &it->item; - switch (bind->type) { - case KEY_BINDING: tll_free(it->item.k.key_codes); break; - case MOUSE_BINDING: break; - } - - tll_remove(*bindings, it); - } -} - -void -wayl_bindings_reset(struct seat *seat) -{ - - key_bindings_destroy(&seat->kbd.bindings.key); - key_bindings_destroy(&seat->kbd.bindings.search); - key_bindings_destroy(&seat->kbd.bindings.url); - key_bindings_destroy(&seat->mouse.bindings); + key_binding_new_for_seat(seat->wayl->key_binding_manager, seat); } static void @@ -186,7 +168,7 @@ seat_destroy(struct seat *seat) return; tll_free(seat->mouse.buttons); - wayl_bindings_reset(seat); + key_binding_remove_seat(seat->wayl->key_binding_manager, seat); if (seat->kbd.xkb_compose_state != NULL) xkb_compose_state_unref(seat->kbd.xkb_compose_state); @@ -952,6 +934,7 @@ handle_global(void *data, struct wl_registry *registry, seat_add_data_device(seat); seat_add_primary_selection(seat); seat_add_text_input(seat); + seat_add_key_bindings(seat); wl_seat_add_listener(wl_seat, &seat_listener, seat); } @@ -1207,7 +1190,8 @@ fdm_wayl(struct fdm *fdm, int fd, int events, void *data) } struct wayland * -wayl_init(const struct config *conf, struct fdm *fdm) +wayl_init(const struct config *conf, struct fdm *fdm, + struct key_binding_manager *key_binding_manager) { struct wayland *wayl = calloc(1, sizeof(*wayl)); if (unlikely(wayl == NULL)) { @@ -1217,6 +1201,7 @@ wayl_init(const struct config *conf, struct fdm *fdm) wayl->conf = conf; wayl->fdm = fdm; + wayl->key_binding_manager = key_binding_manager; wayl->fd = -1; if (!fdm_hook_add(fdm, &fdm_hook, wayl, FDM_HOOK_PRIORITY_LOW)) { diff --git a/wayland.h b/wayland.h index ff4f2d77..90087706 100644 --- a/wayland.h +++ b/wayland.h @@ -6,7 +6,6 @@ #include #include -#include /* Wayland protocols */ #include @@ -29,103 +28,6 @@ /* Forward declarations */ struct terminal; -enum bind_action_normal { - BIND_ACTION_NONE, - BIND_ACTION_NOOP, - BIND_ACTION_SCROLLBACK_UP_PAGE, - BIND_ACTION_SCROLLBACK_UP_HALF_PAGE, - BIND_ACTION_SCROLLBACK_UP_LINE, - BIND_ACTION_SCROLLBACK_DOWN_PAGE, - BIND_ACTION_SCROLLBACK_DOWN_HALF_PAGE, - BIND_ACTION_SCROLLBACK_DOWN_LINE, - BIND_ACTION_SCROLLBACK_HOME, - BIND_ACTION_SCROLLBACK_END, - BIND_ACTION_CLIPBOARD_COPY, - BIND_ACTION_CLIPBOARD_PASTE, - BIND_ACTION_PRIMARY_PASTE, - BIND_ACTION_SEARCH_START, - BIND_ACTION_FONT_SIZE_UP, - BIND_ACTION_FONT_SIZE_DOWN, - BIND_ACTION_FONT_SIZE_RESET, - BIND_ACTION_SPAWN_TERMINAL, - BIND_ACTION_MINIMIZE, - BIND_ACTION_MAXIMIZE, - BIND_ACTION_FULLSCREEN, - BIND_ACTION_PIPE_SCROLLBACK, - BIND_ACTION_PIPE_VIEW, - BIND_ACTION_PIPE_SELECTED, - BIND_ACTION_SHOW_URLS_COPY, - BIND_ACTION_SHOW_URLS_LAUNCH, - BIND_ACTION_SHOW_URLS_PERSISTENT, - BIND_ACTION_TEXT_BINDING, - - /* Mouse specific actions - i.e. they require a mouse coordinate */ - BIND_ACTION_SELECT_BEGIN, - BIND_ACTION_SELECT_BEGIN_BLOCK, - BIND_ACTION_SELECT_EXTEND, - BIND_ACTION_SELECT_EXTEND_CHAR_WISE, - BIND_ACTION_SELECT_WORD, - BIND_ACTION_SELECT_WORD_WS, - BIND_ACTION_SELECT_ROW, - - BIND_ACTION_KEY_COUNT = BIND_ACTION_TEXT_BINDING + 1, - BIND_ACTION_COUNT = BIND_ACTION_SELECT_ROW + 1, -}; - -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_CLIPBOARD_PASTE, - BIND_ACTION_SEARCH_PRIMARY_PASTE, - BIND_ACTION_SEARCH_COUNT, -}; - -enum bind_action_url { - BIND_ACTION_URL_NONE, - BIND_ACTION_URL_CANCEL, - BIND_ACTION_URL_TOGGLE_URL_ON_JUMP_LABEL, - BIND_ACTION_URL_COUNT, -}; - -typedef tll(xkb_keycode_t) xkb_keycode_list_t; - -struct key_binding { - enum key_binding_type type; - - int action; /* enum bind_action_* */ - xkb_mod_mask_t mods; - - union { - struct { - xkb_keysym_t sym; - xkb_keycode_list_t key_codes; - } k; - - struct { - uint32_t button; - int count; - } m; - }; - - const struct binding_aux *aux; -}; -typedef tll(struct key_binding) key_binding_list_t; - /* Mime-types we support when dealing with data offers (e.g. copy-paste, or DnD) */ enum data_offer_mime_type { DATA_OFFER_MIME_UNSET, @@ -219,12 +121,6 @@ struct seat { bool alt; bool ctrl; bool super; - - struct { - key_binding_list_t key; - key_binding_list_t search; - key_binding_list_t url; - } bindings; } kbd; /* Pointer state */ @@ -260,8 +156,6 @@ struct seat { /* We used a discrete axis event in the current pointer frame */ double aggregated[2]; bool have_discrete; - - key_binding_list_t bindings; } mouse; /* Clipboard */ @@ -465,6 +359,7 @@ struct terminal; struct wayland { const struct config *conf; struct fdm *fdm; + struct key_binding_manager *key_binding_manager; int fd; @@ -500,7 +395,8 @@ struct wayland { tll(struct terminal *) terms; }; -struct wayland *wayl_init(const struct config *conf, struct fdm *fdm); +struct wayland *wayl_init(const struct config *conf, struct fdm *fdm, + struct key_binding_manager *key_binding_manager); void wayl_destroy(struct wayland *wayl); bool wayl_reload_xcursor_theme(struct seat *seat, int new_scale); @@ -523,5 +419,3 @@ bool wayl_win_subsurface_new_with_custom_parent( struct wl_window *win, struct wl_surface *parent, struct wl_surf_subsurf *surf, bool allow_pointer_input); void wayl_win_subsurface_destroy(struct wl_surf_subsurf *surf); - -void wayl_bindings_reset(struct seat *seat);