mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-22 01:40:17 -05:00
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings. Before, the bindings where parsed *both* when loading the configuration, and then on every keyboard enter event. This was done since keys require a keymap to be decoded. Something we don't have at configuration time. The idea was that at config time, we used a default keymap just to verify the key combo strings were valid. The following has changed: * The bindings in the config struct is now *one* key combo per entry. Previously, it was one *action* per entry, and each entry had one or more key combos. Doing it this way makes it easier when converting the binding in the keyboard enter event (which previously had to expand the combos anyway). * The bindings in the config struct no longer contains any unparsed strings. A key binding contains a decoded 'modifier' struct (which specifies whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the binding to be used). It also contains a decoded XKB keysym. * A mouse binding in the config struct is similar to a key binding, except it contains the button, and click count instead of the XKB key sym. * The modifiers in the user-specified key combo is decoded at config time, by using the pre-defined XKB constants XKB_MOD_NAME_<modifier>. The result is stored in a 'modifiers' struct, which is just a collection of booleans; one for each supported modifier. The supported modifiers are: shift, ctrl, alt and meta/super. * The key sym is decoded at config time using xkb_keysym_from_name(). This call does *not* depend on a keymap. * The mouse button is decoded at config time using a hardcoded mapping table (just like before). * The click count is currently hard-coded to 1. * In the keyboard enter event, all we need to do is pre-compute the xkb_mod_mask_t variable for each key/mouse binding, and find all the *key codes* that map to the (already decoded) symbol. For mouse bindings, the modifiers are the *only* reason we convert the mouse bindings at all. In fact, on button events, we check if the seat has a keyboard. If not, we use the mouse bindings from the configuration directly, and simply filter out those with a non-empty set of modifiers.
This commit is contained in:
parent
375dcf0810
commit
b97ef5819f
4 changed files with 548 additions and 467 deletions
332
input.c
332
input.c
|
|
@ -260,182 +260,110 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
input_parse_key_binding(struct xkb_keymap *keymap, const char *combos,
|
||||
key_binding_list_t *bindings)
|
||||
static xkb_mod_mask_t
|
||||
conf_modifiers_to_mask(const struct seat *seat,
|
||||
const struct config_key_modifiers *modifiers)
|
||||
{
|
||||
if (combos == NULL)
|
||||
return true;
|
||||
|
||||
xkb_keysym_t sym = XKB_KEY_NoSymbol;
|
||||
|
||||
char *copy = xstrdup(combos);
|
||||
|
||||
for (char *save1 = NULL, *combo = strtok_r(copy, " ", &save1);
|
||||
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);
|
||||
for (char *save2 = NULL, *key = strtok_r(combo, "+", &save2),
|
||||
*next_key = strtok_r(NULL, "+", &save2);
|
||||
key != NULL;
|
||||
key = next_key, next_key = strtok_r(NULL, "+", &save2))
|
||||
{
|
||||
if (next_key != NULL) {
|
||||
/* Modifier */
|
||||
xkb_mod_index_t mod = xkb_keymap_mod_get_index(keymap, key);
|
||||
|
||||
if (mod == -1) {
|
||||
LOG_ERR("%s: not a valid modifier name", key);
|
||||
free(copy);
|
||||
return false;
|
||||
}
|
||||
|
||||
mod_mask |= 1 << mod;
|
||||
LOG_DBG("MOD: %d - %s", mod, key);
|
||||
} else {
|
||||
/* Symbol */
|
||||
sym = xkb_keysym_from_name(key, 0);
|
||||
|
||||
if (sym == XKB_KEY_NoSymbol) {
|
||||
LOG_ERR("%s: key binding is not a valid XKB symbol name",
|
||||
key);
|
||||
free(copy);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find all key codes that map to the lower case
|
||||
* version of the 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)
|
||||
tll_push_back(key_codes, code);
|
||||
}
|
||||
|
||||
xkb_state_unref(state);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DBG("mods=0x%08x, sym=%d", mod_mask, sym);
|
||||
|
||||
if (sym == XKB_KEY_NoSymbol) {
|
||||
assert(tll_length(key_codes) == 0);
|
||||
tll_free(key_codes);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(sym != 0);
|
||||
if (bindings != NULL) {
|
||||
const struct key_binding binding = {
|
||||
.mods = mod_mask,
|
||||
.sym = sym,
|
||||
.key_codes = key_codes,
|
||||
};
|
||||
|
||||
tll_push_back(*bindings, binding);
|
||||
} else
|
||||
tll_free(key_codes);
|
||||
}
|
||||
|
||||
free(copy);
|
||||
return true;
|
||||
xkb_mod_mask_t mods = 0;
|
||||
mods |= modifiers->shift << seat->kbd.mod_shift;
|
||||
mods |= modifiers->ctrl << seat->kbd.mod_ctrl;
|
||||
mods |= modifiers->alt << seat->kbd.mod_alt;
|
||||
mods |= modifiers->meta << seat->kbd.mod_meta;
|
||||
return mods;
|
||||
}
|
||||
|
||||
bool
|
||||
input_parse_mouse_binding(struct xkb_keymap *keymap, const char *combos,
|
||||
enum bind_action_normal action,
|
||||
mouse_binding_list_t *bindings)
|
||||
static xkb_keycode_list_t
|
||||
key_codes_for_xkb_sym(struct xkb_keymap *keymap, xkb_keysym_t sym)
|
||||
{
|
||||
if (combos == NULL)
|
||||
return true;
|
||||
xkb_keycode_list_t key_codes = tll_init();
|
||||
|
||||
char *copy = xstrdup(combos);
|
||||
for (char *save1 = NULL, *combo = strtok_r(copy, " ", &save1);
|
||||
combo != NULL;
|
||||
combo = strtok_r(NULL, " ", &save1))
|
||||
/*
|
||||
* Find all key codes that map to the lower case
|
||||
* version of the 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++)
|
||||
{
|
||||
xkb_mod_mask_t mod_mask = 0;
|
||||
int button = 0;
|
||||
|
||||
for (char *save2 = NULL, *key = strtok_r(combo, "+", &save2),
|
||||
*next_key = strtok_r(NULL, "+", &save2);
|
||||
key != NULL;
|
||||
key = next_key, next_key = strtok_r(NULL, "+", &save2))
|
||||
{
|
||||
if (next_key != NULL) {
|
||||
/* Modifier */
|
||||
xkb_mod_index_t mod = xkb_keymap_mod_get_index(keymap, key);
|
||||
|
||||
if (mod == -1) {
|
||||
LOG_ERR("%s: not a valid modifier name", key);
|
||||
free(copy);
|
||||
return false;
|
||||
}
|
||||
|
||||
mod_mask |= 1 << mod;
|
||||
}
|
||||
|
||||
else {
|
||||
/* Button */
|
||||
static const struct {
|
||||
const char *name;
|
||||
int code;
|
||||
} map[] = {
|
||||
{"BTN_LEFT", BTN_LEFT},
|
||||
{"BTN_RIGHT", BTN_RIGHT},
|
||||
{"BTN_MIDDLE", BTN_MIDDLE},
|
||||
{"BTN_SIDE", BTN_SIDE},
|
||||
{"BTN_EXTRA", BTN_EXTRA},
|
||||
{"BTN_FORWARD", BTN_FORWARD},
|
||||
{"BTN_BACK", BTN_BACK},
|
||||
{"BTN_TASK", BTN_TASK},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ALEN(map); i++) {
|
||||
if (strcmp(key, map[i].name) == 0) {
|
||||
button = map[i].code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (button == 0) {
|
||||
LOG_ERR("%s: invalid mouse button name", key);
|
||||
free(copy);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (button == 0)
|
||||
continue;
|
||||
|
||||
if (bindings != NULL) {
|
||||
const struct mouse_binding binding = {
|
||||
.action = action,
|
||||
.mods = mod_mask,
|
||||
.button = button,
|
||||
.count = 1,
|
||||
};
|
||||
|
||||
tll_push_back(*bindings, binding);
|
||||
}
|
||||
if (xkb_state_key_get_one_sym(state, code) == lower_sym)
|
||||
tll_push_back(key_codes, code);
|
||||
}
|
||||
|
||||
free(copy);
|
||||
return true;
|
||||
xkb_state_unref(state);
|
||||
return key_codes;
|
||||
}
|
||||
|
||||
static void
|
||||
convert_key_binding(struct seat *seat,
|
||||
const struct config_key_binding_normal *conf_binding)
|
||||
{
|
||||
struct key_binding_normal 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),
|
||||
},
|
||||
.pipe_argv = conf_binding->pipe.argv,
|
||||
};
|
||||
tll_push_back(seat->kbd.bindings.key, 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);
|
||||
}
|
||||
|
||||
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_mouse_binding(struct seat *seat,
|
||||
const struct config_mouse_binding *conf_binding)
|
||||
{
|
||||
struct mouse_binding binding = {
|
||||
.action = conf_binding->action,
|
||||
.mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers),
|
||||
.button = conf_binding->button,
|
||||
.count = conf_binding->count,
|
||||
};
|
||||
tll_push_back(seat->mouse.bindings, binding);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_mouse_bindings(const struct config *conf, struct seat *seat)
|
||||
{
|
||||
tll_foreach(conf->bindings.mouse, it)
|
||||
convert_mouse_binding(seat, &it->item);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -494,7 +422,6 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
|||
seat->kbd.xkb, map_str, size, XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
|
||||
/* TODO: initialize in enter? */
|
||||
seat->kbd.xkb_state = xkb_state_new(seat->kbd.xkb_keymap);
|
||||
|
||||
seat->kbd.mod_shift = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Shift");
|
||||
|
|
@ -511,41 +438,9 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
|||
munmap(map_str, size);
|
||||
close(fd);
|
||||
|
||||
tll_foreach(wayl->conf->bindings.key, it) {
|
||||
key_binding_list_t bindings = tll_init();
|
||||
input_parse_key_binding(
|
||||
seat->kbd.xkb_keymap, it->item.combos, &bindings);
|
||||
|
||||
tll_foreach(bindings, it2) {
|
||||
tll_push_back(
|
||||
seat->kbd.bindings.key,
|
||||
((struct key_binding_normal){
|
||||
.bind = it2->item,
|
||||
.action = it->item.action,
|
||||
.pipe_argv = it->item.pipe.argv}));
|
||||
}
|
||||
tll_free(bindings);
|
||||
}
|
||||
|
||||
tll_foreach(wayl->conf->bindings.search, it) {
|
||||
key_binding_list_t bindings = tll_init();
|
||||
input_parse_key_binding(
|
||||
seat->kbd.xkb_keymap, it->item.combos, &bindings);
|
||||
|
||||
tll_foreach(bindings, it2) {
|
||||
tll_push_back(
|
||||
seat->kbd.bindings.search,
|
||||
((struct key_binding_search){
|
||||
.bind = it2->item, .action = it->item.action}));
|
||||
}
|
||||
tll_free(bindings);
|
||||
}
|
||||
|
||||
tll_foreach(wayl->conf->bindings.mouse, it) {
|
||||
input_parse_mouse_binding(
|
||||
seat->kbd.xkb_keymap, it->item.combos, it->item.action,
|
||||
&seat->mouse.bindings);
|
||||
}
|
||||
convert_key_bindings(wayl->conf, seat);
|
||||
convert_search_bindings(wayl->conf, seat);
|
||||
convert_mouse_bindings(wayl->conf, seat);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1574,7 +1469,8 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
}
|
||||
}
|
||||
|
||||
else {
|
||||
else if (seat->wl_keyboard != NULL) {
|
||||
/* Seat has keyboard - use mouse bindings *with* modifiers */
|
||||
tll_foreach(seat->mouse.bindings, it) {
|
||||
const struct mouse_binding *binding = &it->item;
|
||||
|
||||
|
|
@ -1598,6 +1494,32 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
}
|
||||
}
|
||||
|
||||
else {
|
||||
/* Seat does NOT have a keyboard - use mouse bindings *without* modifiers */
|
||||
tll_foreach(seat->wayl->conf->bindings.mouse, it) {
|
||||
const struct config_mouse_binding *binding = &it->item;
|
||||
|
||||
if (binding->button != button) {
|
||||
/* Wrong button */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (binding->count != seat->mouse.count) {
|
||||
/* Incorrect click count */
|
||||
continue;
|
||||
}
|
||||
|
||||
const struct config_key_modifiers no_mods = {};
|
||||
if (memcmp(&binding->modifiers, &no_mods, sizeof(no_mods)) != 0) {
|
||||
/* Binding has modifiers */
|
||||
continue;
|
||||
}
|
||||
|
||||
execute_binding(seat, term, binding->action, NULL, serial);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!term_mouse_grabbed(term, seat) && cursor_is_on_grid) {
|
||||
term_mouse_down(
|
||||
term, button, seat->mouse.row, seat->mouse.col,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue