mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-04-11 08:21:01 -04:00
input: repair key combos containing both explicit modifier and shifted symbol
If a user-provided key combo contains both an explicit modifier, and the shifted symbol it produces, replace the symbol with its un-shifted version. Example: Control+Shift+U contains both ‘Shift’, and ‘U’, where ‘Shift’ is the modifier that shifts ‘u’ to ‘U’. Such modifiers are “consumed”, i.e. filtered out, when matching key combos. As such, Control+Shift+U will never trigger since we’ll never be able to match the consumed modifier ‘Shift’. The above combo can be written in two ways: - Control+U - Control+Shift+u To be able to detect that Control+Shift+U is an invalid combination, we need to check all *shifted* symbols for all key *codes*. Once we’ve detected a shifted symbol (and where it differs from the un-shifted symbol), we loop all modifier sets that produce the shift level where our shifted symbol is. For each such set, check if it intersects with the user-provided key combo’s modifier set. If there is an intersection, it means the user provided a key combo containing a modifier and symbol, such that the modifier is consumed when the symbol is produced. In this case, we replace the symbol with its un-shifted version.
This commit is contained in:
parent
1b4d9eade0
commit
a5b554761a
2 changed files with 125 additions and 6 deletions
3
config.c
3
config.c
|
|
@ -1191,6 +1191,7 @@ parse_key_combos(struct config *conf, const char *combos, key_combo_list_t *key_
|
||||||
key++; /* Skip past the '+' */
|
key++; /* Skip past the '+' */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
if (modifiers.shift && strlen(key) == 1 && (*key >= 'A' && *key <= 'Z')) {
|
if (modifiers.shift && strlen(key) == 1 && (*key >= 'A' && *key <= 'Z')) {
|
||||||
LOG_WARN(
|
LOG_WARN(
|
||||||
"%s:%d: [%s]: %s: %s: "
|
"%s:%d: [%s]: %s: %s: "
|
||||||
|
|
@ -1204,7 +1205,7 @@ parse_key_combos(struct config *conf, const char *combos, key_combo_list_t *key_
|
||||||
path, lineno, section, option, combo);
|
path, lineno, section, option, combo);
|
||||||
*key = *key - 'A' + 'a';
|
*key = *key - 'A' + 'a';
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
/* Translate key name to symbol */
|
/* Translate key name to symbol */
|
||||||
xkb_keysym_t sym = xkb_keysym_from_name(key, 0);
|
xkb_keysym_t sym = xkb_keysym_from_name(key, 0);
|
||||||
if (sym == XKB_KEY_NoSymbol) {
|
if (sym == XKB_KEY_NoSymbol) {
|
||||||
|
|
|
||||||
128
input.c
128
input.c
|
|
@ -369,16 +369,134 @@ key_codes_for_xkb_sym(struct xkb_keymap *keymap, xkb_keysym_t sym)
|
||||||
return key_codes;
|
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))
|
||||||
|
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
|
static void
|
||||||
convert_key_binding(struct seat *seat,
|
convert_key_binding(const struct seat *seat,
|
||||||
const struct config_key_binding *conf_binding,
|
const struct config_key_binding *conf_binding,
|
||||||
key_binding_list_t *bindings)
|
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->sym, mods);
|
||||||
|
|
||||||
struct key_binding binding = {
|
struct key_binding binding = {
|
||||||
.mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers),
|
.mods = mods,
|
||||||
.sym = conf_binding->sym,
|
.sym = sym,
|
||||||
.key_codes = key_codes_for_xkb_sym(
|
.key_codes = key_codes_for_xkb_sym(seat->kbd.xkb_keymap, sym),
|
||||||
seat->kbd.xkb_keymap, conf_binding->sym),
|
|
||||||
.action = conf_binding->action,
|
.action = conf_binding->action,
|
||||||
.pipe_argv = conf_binding->pipe.argv,
|
.pipe_argv = conf_binding->pipe.argv,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue