mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-04 04:06:06 -05:00
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways: * By translated (by XKB) symbols * By untranslated symbols * By raw key codes A translated symbol is affected by pressed modifiers, some of which can be “consumed”. Consumed modifiers to not partake in the comparison with the binding’s modifiers. In this mode, ctrl+shift+2 maps to ctrl+@ on a US layout. Untranslated symbols, or un-shifted symbols refer to the “base” symbol of the pressed key, i.e. it’s unaffected by modifiers. In this mode, consumed modifiers *do* partake in the comparison with the binding’s modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout. More examples: ctrl+shift+u maps to ctrl+U in the translated lookup, while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup. Finally, we also match raw key codes. This allows our bindings to work using the same physical keys when the user switches between latin and non-latin layouts. This means key bindings in foot.ini *must* not include both +shift+ and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it cannot be triggered. Unfortunately, this was how you were supposed to write bindings up until now... so, we try to detect such bindings, log a deprecation warning and then “fix” the binding for the user. When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are valid, and will work. The latter is preferred though, since we cannot detect the raw key code for the former variant. Personally, I also prefer the latter one because it is more explicit; it’s more obvious which keys are involved. However, in some cases it makes more sense to use the other variant. Typically for non-letter combos.
This commit is contained in:
parent
decc655d48
commit
5e64e06a55
6 changed files with 117 additions and 42 deletions
35
config.c
35
config.c
|
|
@ -1168,6 +1168,7 @@ out:
|
|||
|
||||
static bool
|
||||
parse_key_combos(struct config *conf, const char *combos, key_combo_list_t *key_combos,
|
||||
const char *section, const char *option,
|
||||
const char *path, unsigned lineno)
|
||||
{
|
||||
xassert(tll_length(*key_combos) == 0);
|
||||
|
|
@ -1179,7 +1180,7 @@ parse_key_combos(struct config *conf, const char *combos, key_combo_list_t *key_
|
|||
combo = strtok_r(NULL, " ", &tok_ctx))
|
||||
{
|
||||
struct config_key_modifiers modifiers = {0};
|
||||
const char *key = strrchr(combo, '+');
|
||||
char *key = strrchr(combo, '+');
|
||||
|
||||
if (key == NULL) {
|
||||
/* No modifiers */
|
||||
|
|
@ -1190,11 +1191,26 @@ parse_key_combos(struct config *conf, const char *combos, key_combo_list_t *key_
|
|||
key++; /* Skip past the '+' */
|
||||
}
|
||||
|
||||
if (modifiers.shift && strlen(key) == 1 && (*key >= 'A' && *key <= 'Z')) {
|
||||
LOG_WARN(
|
||||
"%s:%d: [%s]: %s: %s: "
|
||||
"upper case keys not supported with explicit 'Shift' modifier",
|
||||
path, lineno, section, option, combo);
|
||||
user_notification_add(
|
||||
&conf->notifications, USER_NOTIFICATION_DEPRECATED,
|
||||
"%s:%d: [%s]: %s: \033[1m%s\033[m: "
|
||||
"shifted keys not supported with explicit \033[1mShift\033[m "
|
||||
"modifier",
|
||||
path, lineno, section, option, combo);
|
||||
*key = tolower(*key);
|
||||
}
|
||||
|
||||
/* Translate key name to symbol */
|
||||
xkb_keysym_t sym = xkb_keysym_from_name(key, 0);
|
||||
if (sym == XKB_KEY_NoSymbol) {
|
||||
LOG_AND_NOTIFY_ERR("%s:%d: %s: key is not a valid XKB key name",
|
||||
path, lineno, key);
|
||||
LOG_AND_NOTIFY_ERR(
|
||||
"%s:%d: [%s]: %s: ]%s: key is not a valid XKB key name",
|
||||
path, lineno, section, option, key);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
|
@ -1373,7 +1389,8 @@ parse_key_binding_section(
|
|||
}
|
||||
|
||||
key_combo_list_t key_combos = tll_init();
|
||||
if (!parse_key_combos(conf, value, &key_combos, path, lineno) ||
|
||||
if (!parse_key_combos(
|
||||
conf, value, &key_combos, section, key, path, lineno) ||
|
||||
has_key_binding_collisions(
|
||||
conf, action, binding_action_map, bindings, &key_combos,
|
||||
path, lineno))
|
||||
|
|
@ -2040,10 +2057,10 @@ add_default_key_bindings(struct config *conf)
|
|||
|
||||
add_binding(BIND_ACTION_SCROLLBACK_UP_PAGE, shift, XKB_KEY_Page_Up);
|
||||
add_binding(BIND_ACTION_SCROLLBACK_DOWN_PAGE, shift, XKB_KEY_Page_Down);
|
||||
add_binding(BIND_ACTION_CLIPBOARD_COPY, ctrl_shift, XKB_KEY_C);
|
||||
add_binding(BIND_ACTION_CLIPBOARD_PASTE, ctrl_shift, XKB_KEY_V);
|
||||
add_binding(BIND_ACTION_CLIPBOARD_COPY, ctrl_shift, XKB_KEY_c);
|
||||
add_binding(BIND_ACTION_CLIPBOARD_PASTE, ctrl_shift, XKB_KEY_v);
|
||||
add_binding(BIND_ACTION_PRIMARY_PASTE, shift, XKB_KEY_Insert);
|
||||
add_binding(BIND_ACTION_SEARCH_START, ctrl_shift, XKB_KEY_R);
|
||||
add_binding(BIND_ACTION_SEARCH_START, ctrl_shift, XKB_KEY_r);
|
||||
add_binding(BIND_ACTION_FONT_SIZE_UP, ctrl, XKB_KEY_plus);
|
||||
add_binding(BIND_ACTION_FONT_SIZE_UP, ctrl, XKB_KEY_equal);
|
||||
add_binding(BIND_ACTION_FONT_SIZE_UP, ctrl, XKB_KEY_KP_Add);
|
||||
|
|
@ -2051,8 +2068,8 @@ add_default_key_bindings(struct config *conf)
|
|||
add_binding(BIND_ACTION_FONT_SIZE_DOWN, ctrl, XKB_KEY_KP_Subtract);
|
||||
add_binding(BIND_ACTION_FONT_SIZE_RESET, ctrl, XKB_KEY_0);
|
||||
add_binding(BIND_ACTION_FONT_SIZE_RESET, ctrl, XKB_KEY_KP_0);
|
||||
add_binding(BIND_ACTION_SPAWN_TERMINAL, ctrl_shift, XKB_KEY_N);
|
||||
add_binding(BIND_ACTION_SHOW_URLS_LAUNCH, ctrl_shift, XKB_KEY_U);
|
||||
add_binding(BIND_ACTION_SPAWN_TERMINAL, ctrl_shift, XKB_KEY_n);
|
||||
add_binding(BIND_ACTION_SHOW_URLS_LAUNCH, ctrl_shift, XKB_KEY_u);
|
||||
|
||||
#undef add_binding
|
||||
}
|
||||
|
|
|
|||
50
input.c
50
input.c
|
|
@ -350,20 +350,18 @@ 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 the lower case
|
||||
* version of the symbol.
|
||||
* Find all key codes that map to this symbol.
|
||||
*
|
||||
* This allows us to match bindings in other layouts
|
||||
* too.
|
||||
*/
|
||||
xkb_keysym_t lower_sym = xkb_keysym_to_lower(sym);
|
||||
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) == lower_sym)
|
||||
if (xkb_state_key_get_one_sym(state, code) == sym)
|
||||
tll_push_back(key_codes, code);
|
||||
}
|
||||
|
||||
|
|
@ -829,15 +827,24 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|||
mods &= significant;
|
||||
consumed &= significant;
|
||||
|
||||
xkb_layout_index_t layout_idx =
|
||||
xkb_state_key_get_layout(seat->kbd.xkb_state, key);
|
||||
|
||||
const xkb_keysym_t *raw_syms = NULL;
|
||||
size_t raw_count = xkb_keymap_key_get_syms_by_level(
|
||||
seat->kbd.xkb_keymap, key, layout_idx, 0, &raw_syms);
|
||||
|
||||
if (term->is_searching) {
|
||||
if (should_repeat)
|
||||
start_repeater(seat, key);
|
||||
search_input(seat, term, key, sym, mods, serial);
|
||||
search_input(
|
||||
seat, term, key, sym, mods, consumed, raw_syms, raw_count, serial);
|
||||
return;
|
||||
} else if (urls_mode_is_active(term)) {
|
||||
if (should_repeat)
|
||||
start_repeater(seat, key);
|
||||
urls_input(seat, term, key, sym, mods, serial);
|
||||
urls_input(
|
||||
seat, term, key, sym, mods, consumed, raw_syms, raw_count, serial);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -863,20 +870,35 @@ 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.mods != mods)
|
||||
const struct key_binding *bind = &it->item;
|
||||
|
||||
/* Match translated symbol */
|
||||
if (bind->sym == sym &&
|
||||
bind->mods == (mods & ~consumed) &&
|
||||
execute_binding(
|
||||
seat, term, bind->action, bind->pipe_argv, serial))
|
||||
{
|
||||
goto maybe_repeat;
|
||||
}
|
||||
|
||||
if (bind->mods != mods)
|
||||
continue;
|
||||
|
||||
/* Match symbol */
|
||||
if (it->item.sym == sym) {
|
||||
if (execute_binding(seat, term, it->item.action, it->item.pipe_argv, serial))
|
||||
/* Match untranslated symbols */
|
||||
for (size_t i = 0; i < raw_count; i++) {
|
||||
if (bind->sym == raw_syms[i] && execute_binding(
|
||||
seat, term, bind->action, bind->pipe_argv, serial))
|
||||
{
|
||||
goto maybe_repeat;
|
||||
}
|
||||
}
|
||||
|
||||
/* Match raw key 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;
|
||||
tll_foreach(bind->key_codes, code) {
|
||||
if (code->item == key && execute_binding(
|
||||
seat, term, bind->action, bind->pipe_argv, serial))
|
||||
{
|
||||
goto maybe_repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
34
search.c
34
search.c
|
|
@ -783,7 +783,9 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
|
||||
void
|
||||
search_input(struct seat *seat, struct terminal *term, uint32_t key,
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, uint32_t serial)
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed,
|
||||
const xkb_keysym_t *raw_syms, size_t raw_count,
|
||||
uint32_t serial)
|
||||
{
|
||||
LOG_DBG("search: input: sym=%d/0x%x, mods=0x%08x", sym, sym, mods);
|
||||
|
||||
|
|
@ -795,12 +797,13 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key,
|
|||
|
||||
/* Key bindings */
|
||||
tll_foreach(seat->kbd.bindings.search, it) {
|
||||
if (it->item.mods != mods)
|
||||
continue;
|
||||
const struct key_binding *bind = &it->item;
|
||||
|
||||
/* Match symbol */
|
||||
if (it->item.sym == sym) {
|
||||
if (execute_binding(seat, term, it->item.action, serial,
|
||||
/* Match translated symbol */
|
||||
if (bind->sym == sym &&
|
||||
bind->mods == (mods & ~consumed)) {
|
||||
|
||||
if (execute_binding(seat, term, bind->action, serial,
|
||||
&update_search_result, &redraw))
|
||||
{
|
||||
goto update_search;
|
||||
|
|
@ -808,10 +811,25 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key,
|
|||
return;
|
||||
}
|
||||
|
||||
if (bind->mods != mods)
|
||||
continue;
|
||||
|
||||
/* Match untranslated symbols */
|
||||
for (size_t i = 0; i < raw_count; i++) {
|
||||
if (bind->sym == raw_syms[i]) {
|
||||
if (execute_binding(seat, term, bind->action, serial,
|
||||
&update_search_result, &redraw))
|
||||
{
|
||||
goto update_search;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Match raw key code */
|
||||
tll_foreach(it->item.key_codes, code) {
|
||||
tll_foreach(bind->key_codes, code) {
|
||||
if (code->item == key) {
|
||||
if (execute_binding(seat, term, it->item.action, serial,
|
||||
if (execute_binding(seat, term, bind->action, serial,
|
||||
&update_search_result, &redraw))
|
||||
{
|
||||
goto update_search;
|
||||
|
|
|
|||
7
search.h
7
search.h
|
|
@ -5,6 +5,9 @@
|
|||
|
||||
void search_begin(struct terminal *term);
|
||||
void search_cancel(struct terminal *term);
|
||||
void search_input(struct seat *seat, struct terminal *term, uint32_t key,
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, uint32_t serial);
|
||||
void search_input(
|
||||
struct seat *seat, struct terminal *term, uint32_t key,
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed,
|
||||
const xkb_keysym_t *raw_syms, size_t raw_count,
|
||||
uint32_t serial);
|
||||
void search_add_chars(struct terminal *term, const char *text, size_t len);
|
||||
|
|
|
|||
29
url-mode.c
29
url-mode.c
|
|
@ -115,23 +115,36 @@ activate_url(struct seat *seat, struct terminal *term, const struct url *url)
|
|||
|
||||
void
|
||||
urls_input(struct seat *seat, struct terminal *term, uint32_t key,
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, uint32_t serial)
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed,
|
||||
const xkb_keysym_t *raw_syms, size_t raw_count,
|
||||
uint32_t serial)
|
||||
{
|
||||
/* Key bindings */
|
||||
tll_foreach(seat->kbd.bindings.url, it) {
|
||||
if (it->item.mods != mods)
|
||||
continue;
|
||||
const struct key_binding *bind = &it->item;
|
||||
|
||||
/* Match symbol */
|
||||
if (it->item.sym == sym) {
|
||||
execute_binding(seat, term, it->item.action, serial);
|
||||
/* Match translated symbol */
|
||||
if (bind->sym == sym &&
|
||||
bind->mods == (mods & ~consumed))
|
||||
{
|
||||
execute_binding(seat, term, bind->action, serial);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bind->mods != mods)
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < raw_count; i++) {
|
||||
if (bind->sym == raw_syms[i]) {
|
||||
execute_binding(seat, term, bind->action, serial);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Match raw key code */
|
||||
tll_foreach(it->item.key_codes, code) {
|
||||
tll_foreach(bind->key_codes, code) {
|
||||
if (code->item == key) {
|
||||
execute_binding(seat, term, it->item.action, serial);
|
||||
execute_binding(seat, term, bind->action, serial);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,4 +20,6 @@ void urls_render(struct terminal *term);
|
|||
void urls_reset(struct terminal *term);
|
||||
|
||||
void urls_input(struct seat *seat, struct terminal *term, uint32_t key,
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, uint32_t serial);
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed,
|
||||
const xkb_keysym_t *raw_syms, size_t raw_count,
|
||||
uint32_t serial);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue