mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-04 04:06:06 -05:00
input: make wheel events mappable
Un-grabbed wheel events are now passed through the mouse binding matching logic, instead of being hardcoded to scrolling the terminal contents. They are mappable through the BTN_BACK and BTN_FORWARD buttons. Since they're not actually button *presses*, they never generate a click count other than 1. This limitation is documented, but not checked in the config. This means it's possible to create bindings like "BTN_BACK+3" (i.e. triple "click"). They will however never trigger. The old, hardcoded logic is now accessible through the new scrollback-up-mouse and scrollback-down-mouse mouse bindings. They (obiously) default to BTN_BACK and BTN_FORWARD, respectively. Example usage: keep the default of scrolling terminal contents with the wheel, when used without modifiers, but map Control+wheel to font zoom in/out: [mouse-bindings] font-increase=Control+BTN_FORWARD font-decrease=Control+BTN_BACK (this also keeps the default key bindings to zoom in/out; ctrl-+ and ctrl+-) Closes #1077
This commit is contained in:
parent
f0f0d02bf7
commit
fe7aa25ad8
6 changed files with 162 additions and 105 deletions
|
|
@ -50,6 +50,13 @@
|
|||
### Added
|
||||
|
||||
* Support for building with _wayland-protocols_ as a subproject.
|
||||
* Mouse wheel scrolls can now be used in `mouse-bindings`
|
||||
([#1077](1077)).
|
||||
* New mouse bindings: `scrollback-up-mouse` and
|
||||
`scrollback-down-mouse`, bound to `BTN_BACK` and `BTN_FORWARD`
|
||||
respectively.
|
||||
|
||||
[1077]: https://codeberg.org/dnkl/foot/issues/1077
|
||||
|
||||
|
||||
### Changed
|
||||
|
|
|
|||
4
config.c
4
config.c
|
|
@ -120,6 +120,8 @@ static const char *const binding_action_map[] = {
|
|||
[BIND_ACTION_UNICODE_INPUT] = "unicode-input",
|
||||
|
||||
/* Mouse-specific actions */
|
||||
[BIND_ACTION_SCROLLBACK_UP_MOUSE] = "scrollback-up-mouse",
|
||||
[BIND_ACTION_SCROLLBACK_DOWN_MOUSE] = "scrollback-down-mouse",
|
||||
[BIND_ACTION_SELECT_BEGIN] = "select-begin",
|
||||
[BIND_ACTION_SELECT_BEGIN_BLOCK] = "select-begin-block",
|
||||
[BIND_ACTION_SELECT_EXTEND] = "select-extend",
|
||||
|
|
@ -2871,6 +2873,8 @@ static void
|
|||
add_default_mouse_bindings(struct config *conf)
|
||||
{
|
||||
static const struct config_key_binding bindings[] = {
|
||||
{BIND_ACTION_SCROLLBACK_UP_MOUSE, m_none, {.m = {BTN_BACK, 1}}},
|
||||
{BIND_ACTION_SCROLLBACK_DOWN_MOUSE, m_none, {.m = {BTN_FORWARD, 1}}},
|
||||
{BIND_ACTION_PRIMARY_PASTE, m_none, {.m = {BTN_MIDDLE, 1}}},
|
||||
{BIND_ACTION_SELECT_BEGIN, m_none, {.m = {BTN_LEFT, 1}}},
|
||||
{BIND_ACTION_SELECT_BEGIN_BLOCK, m_ctrl, {.m = {BTN_LEFT, 1}}},
|
||||
|
|
|
|||
|
|
@ -1032,9 +1032,14 @@ of the modifiers *must* be valid XKB key names, and the button name
|
|||
*must* be a valid libinput name. You can find the button names using
|
||||
*libinput debug-events*.
|
||||
|
||||
The trailing *COUNT* is optional and specifies the click count
|
||||
required to trigger the binding. The default if *COUNT* is omitted is
|
||||
_1_.
|
||||
The trailing *COUNT* (number of times the button has to be clicked) is
|
||||
optional and specifies the click count required to trigger the
|
||||
binding. The default if *COUNT* is omitted is _1_.
|
||||
|
||||
To map wheel events (i.e. scrolling), use the button names *BTN_BACK*
|
||||
(up) and *BTN_FORWARD* (down). Note that these events never generate a
|
||||
*COUNT* larger than 1. That is, *BTN_BACK+2*, for example, will never
|
||||
trigger.
|
||||
|
||||
A modifier+button combination can only be mapped to *one* action. Lets
|
||||
say you want to bind *BTN\_MIDDLE* to *fullscreen*. Since
|
||||
|
|
@ -1056,6 +1061,22 @@ _action=none_; e.g. *primary-paste=none*.
|
|||
The actions to which mouse combos can be bound are listed below. All
|
||||
actions listed under *key-bindings* can be used here as well.
|
||||
|
||||
*scrollback-up-mouse*
|
||||
Normal screen: scrolls up the contents.
|
||||
|
||||
Alt screen: send fake _KeyUP_ events to the client application, if
|
||||
alternate scroll mode is enabled.
|
||||
|
||||
Default: _BTN_BACK_
|
||||
|
||||
*scrollback-down-mouse*
|
||||
Normal screen: scrolls down the contents.
|
||||
|
||||
Alt screen: send fake _KeyDOWN_ events to the client application, if
|
||||
alternate scroll mode is enabled.
|
||||
|
||||
Default: _BTN_FORWARD_
|
||||
|
||||
*select-begin*
|
||||
Begin an interactive selection. The selection is finalized, and
|
||||
copied to the _primary selection_, when the button is
|
||||
|
|
|
|||
2
foot.ini
2
foot.ini
|
|
@ -190,6 +190,8 @@
|
|||
# \x03=Mod4+c # Map Super+c -> Ctrl+c
|
||||
|
||||
[mouse-bindings]
|
||||
# scrollback-up-mouse=BTN_BACK
|
||||
# scrollback-down-mouse=BTN_FORWARD
|
||||
# selection-override-modifiers=Shift
|
||||
# primary-paste=BTN_MIDDLE
|
||||
# select-begin=BTN_LEFT
|
||||
|
|
|
|||
225
input.c
225
input.c
|
|
@ -81,9 +81,11 @@ pipe_closed:
|
|||
return true;
|
||||
}
|
||||
|
||||
static void alternate_scroll(struct seat *seat, int amount, int button);
|
||||
|
||||
static bool
|
||||
execute_binding(struct seat *seat, struct terminal *term,
|
||||
const struct key_binding *binding, uint32_t serial)
|
||||
const struct key_binding *binding, uint32_t serial, int amount)
|
||||
{
|
||||
const enum bind_action_normal action = binding->action;
|
||||
|
||||
|
|
@ -115,6 +117,14 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
}
|
||||
break;
|
||||
|
||||
case BIND_ACTION_SCROLLBACK_UP_MOUSE:
|
||||
if (term->grid == &term->alt) {
|
||||
if (term->alt_scrolling)
|
||||
alternate_scroll(seat, amount, BTN_BACK);
|
||||
} else
|
||||
cmd_scrollback_up(term, amount);
|
||||
break;
|
||||
|
||||
case BIND_ACTION_SCROLLBACK_DOWN_PAGE:
|
||||
if (term->grid == &term->normal) {
|
||||
cmd_scrollback_down(term, term->rows);
|
||||
|
|
@ -136,6 +146,14 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
}
|
||||
break;
|
||||
|
||||
case BIND_ACTION_SCROLLBACK_DOWN_MOUSE:
|
||||
if (term->grid == &term->alt) {
|
||||
if (term->alt_scrolling)
|
||||
alternate_scroll(seat, amount, BTN_FORWARD);
|
||||
} else
|
||||
cmd_scrollback_down(term, amount);
|
||||
break;
|
||||
|
||||
case BIND_ACTION_SCROLLBACK_HOME:
|
||||
if (term->grid == &term->normal) {
|
||||
cmd_scrollback_up(term, term->grid->num_rows);
|
||||
|
|
@ -1478,7 +1496,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|||
/* Match translated symbol */
|
||||
if (bind->k.sym == sym &&
|
||||
bind->mods == (bind_mods & ~bind_consumed) &&
|
||||
execute_binding(seat, term, bind, serial))
|
||||
execute_binding(seat, term, bind, serial, 1))
|
||||
{
|
||||
goto maybe_repeat;
|
||||
}
|
||||
|
|
@ -1489,7 +1507,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|||
/* Match untranslated symbols */
|
||||
for (size_t i = 0; i < raw_count; i++) {
|
||||
if (bind->k.sym == raw_syms[i] &&
|
||||
execute_binding(seat, term, bind, serial))
|
||||
execute_binding(seat, term, bind, serial, 1))
|
||||
{
|
||||
goto maybe_repeat;
|
||||
}
|
||||
|
|
@ -1498,7 +1516,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|||
/* Match raw key code */
|
||||
tll_foreach(bind->k.key_codes, code) {
|
||||
if (code->item == key &&
|
||||
execute_binding(seat, term, bind, serial))
|
||||
execute_binding(seat, term, bind, serial, 1))
|
||||
{
|
||||
goto maybe_repeat;
|
||||
}
|
||||
|
|
@ -2152,6 +2170,95 @@ fdm_csd_move(struct fdm *fdm, int fd, int events, void *data)
|
|||
return true;
|
||||
}
|
||||
|
||||
static const struct key_binding *
|
||||
match_mouse_binding(const struct seat *seat, const struct terminal *term,
|
||||
int button)
|
||||
{
|
||||
if (seat->wl_keyboard != NULL && seat->kbd.xkb_state != NULL) {
|
||||
/* Seat has keyboard - use mouse bindings *with* modifiers */
|
||||
|
||||
const struct key_binding_set *bindings =
|
||||
key_binding_for(term->wl->key_binding_manager, term->conf, seat);
|
||||
xassert(bindings != NULL);
|
||||
|
||||
xkb_mod_mask_t mods;
|
||||
get_current_modifiers(seat, &mods, NULL, 0);
|
||||
mods &= seat->kbd.bind_significant;
|
||||
|
||||
/* Ignore selection override modifiers when
|
||||
* matching modifiers */
|
||||
mods &= ~bindings->selection_overrides;
|
||||
|
||||
const struct key_binding *match = NULL;
|
||||
|
||||
tll_foreach(bindings->mouse, it) {
|
||||
const struct key_binding *binding = &it->item;
|
||||
|
||||
if (binding->m.button != button) {
|
||||
/* Wrong button */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (binding->mods != mods) {
|
||||
/* Modifier mismatch */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (binding->m.count > seat->mouse.count) {
|
||||
/* Not correct click count */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match == NULL || binding->m.count > match->m.count)
|
||||
match = binding;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
else {
|
||||
/* Seat does NOT have a keyboard - use mouse bindings *without*
|
||||
* modifiers */
|
||||
const struct config_key_binding *match = NULL;
|
||||
const struct config *conf = term->conf;
|
||||
|
||||
for (size_t i = 0; i < conf->bindings.mouse.count; i++) {
|
||||
const struct config_key_binding *binding =
|
||||
&conf->bindings.mouse.arr[i];
|
||||
|
||||
if (binding->m.button != button) {
|
||||
/* Wrong button */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (binding->m.count > seat->mouse.count) {
|
||||
/* Incorrect click count */
|
||||
continue;
|
||||
}
|
||||
|
||||
const struct config_key_modifiers no_mods = {0};
|
||||
if (memcmp(&binding->modifiers, &no_mods, sizeof(no_mods)) != 0) {
|
||||
/* Binding has modifiers */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match == NULL || binding->m.count > match->m.count)
|
||||
match = binding;
|
||||
}
|
||||
|
||||
if (match != NULL) {
|
||||
static struct key_binding bind;
|
||||
bind.action = match->action;
|
||||
bind.aux = &match->aux;
|
||||
return &bind;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BUG("should not get here");
|
||||
}
|
||||
|
||||
static void
|
||||
wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
|
||||
|
|
@ -2426,86 +2533,11 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
bool consumed = false;
|
||||
|
||||
if (cursor_is_on_grid && term_mouse_grabbed(term, seat)) {
|
||||
if (seat->wl_keyboard != NULL && seat->kbd.xkb_state != NULL) {
|
||||
/* Seat has keyboard - use mouse bindings *with* modifiers */
|
||||
const struct key_binding *match =
|
||||
match_mouse_binding(seat, term, button);
|
||||
|
||||
const struct key_binding_set *bindings = key_binding_for(
|
||||
wayl->key_binding_manager, term->conf, seat);
|
||||
xassert(bindings != NULL);
|
||||
|
||||
xkb_mod_mask_t mods;
|
||||
get_current_modifiers(seat, &mods, NULL, 0);
|
||||
mods &= seat->kbd.bind_significant;
|
||||
|
||||
/* Ignore selection override modifiers when
|
||||
* matching modifiers */
|
||||
mods &= ~bindings->selection_overrides;
|
||||
|
||||
const struct key_binding *match = NULL;
|
||||
|
||||
tll_foreach(bindings->mouse, it) {
|
||||
const struct key_binding *binding = &it->item;
|
||||
|
||||
if (binding->m.button != button) {
|
||||
/* Wrong button */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (binding->mods != mods) {
|
||||
/* Modifier mismatch */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (binding->m.count > seat->mouse.count) {
|
||||
/* Not correct click count */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match == NULL || binding->m.count > match->m.count)
|
||||
match = binding;
|
||||
}
|
||||
|
||||
if (match != NULL)
|
||||
consumed = execute_binding(seat, term, match, serial);
|
||||
}
|
||||
|
||||
else {
|
||||
/* Seat does NOT have a keyboard - use mouse bindings *without* modifiers */
|
||||
const struct config_key_binding *match = NULL;
|
||||
const struct config *conf = term->conf;
|
||||
|
||||
for (size_t i = 0; i < conf->bindings.mouse.count; i++) {
|
||||
const struct config_key_binding *binding =
|
||||
&conf->bindings.mouse.arr[i];
|
||||
|
||||
if (binding->m.button != button) {
|
||||
/* Wrong button */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (binding->m.count > seat->mouse.count) {
|
||||
/* Incorrect click count */
|
||||
continue;
|
||||
}
|
||||
|
||||
const struct config_key_modifiers no_mods = {0};
|
||||
if (memcmp(&binding->modifiers, &no_mods, sizeof(no_mods)) != 0) {
|
||||
/* Binding has modifiers */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match == NULL || binding->m.count > match->m.count)
|
||||
match = binding;
|
||||
}
|
||||
|
||||
if (match != NULL) {
|
||||
struct key_binding bind = {
|
||||
.action = match->action,
|
||||
.aux = &match->aux,
|
||||
};
|
||||
consumed = execute_binding(seat, term, &bind, serial);
|
||||
}
|
||||
}
|
||||
if (match != NULL)
|
||||
consumed = execute_binding(seat, term, match, serial, 1);
|
||||
}
|
||||
|
||||
send_to_client = !consumed && cursor_is_on_grid;
|
||||
|
|
@ -2580,26 +2612,15 @@ mouse_scroll(struct seat *seat, int amount, enum wl_pointer_axis axis)
|
|||
amount = abs(amount);
|
||||
|
||||
if (term_mouse_grabbed(term, seat)) {
|
||||
if (term->grid == &term->alt) {
|
||||
if (term->alt_scrolling) {
|
||||
switch (button) {
|
||||
case BTN_BACK:
|
||||
case BTN_FORWARD:
|
||||
alternate_scroll(seat, amount, button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (button) {
|
||||
case BTN_BACK:
|
||||
cmd_scrollback_up(term, amount);
|
||||
break;
|
||||
seat->mouse.count = 1;
|
||||
|
||||
case BTN_FORWARD:
|
||||
cmd_scrollback_down(term, amount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const struct key_binding *match =
|
||||
match_mouse_binding(seat, term, button);
|
||||
|
||||
if (match != NULL)
|
||||
execute_binding(seat, term, match, seat->pointer.serial, amount);
|
||||
|
||||
seat->mouse.last_released_button = button;
|
||||
}
|
||||
|
||||
else if (seat->mouse.col >= 0 && seat->mouse.row >= 0) {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ enum bind_action_normal {
|
|||
BIND_ACTION_UNICODE_INPUT,
|
||||
|
||||
/* Mouse specific actions - i.e. they require a mouse coordinate */
|
||||
BIND_ACTION_SCROLLBACK_UP_MOUSE,
|
||||
BIND_ACTION_SCROLLBACK_DOWN_MOUSE,
|
||||
BIND_ACTION_SELECT_BEGIN,
|
||||
BIND_ACTION_SELECT_BEGIN_BLOCK,
|
||||
BIND_ACTION_SELECT_EXTEND,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue