mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-22 01:40:17 -05:00
Merge branch 'mouse-binding-actions' into master
Closes #79. Closes #75.
This commit is contained in:
commit
394db77b04
8 changed files with 163 additions and 56 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -35,6 +35,16 @@
|
|||
(https://codeberg.org/dnkl/foot/issues/77).
|
||||
* Click count support in mouse bindings, i.e double- and triple-click
|
||||
(https://codeberg.org/dnkl/foot/issues/78).
|
||||
* Modifier support in mouse bindings
|
||||
(https://codeberg.org/dnkl/foot/issues/77).
|
||||
* Click count support in mouse bindings, i.e double- and triple-click
|
||||
(https://codeberg.org/dnkl/foot/issues/78).
|
||||
* All mouse actions (begin selection, select word, select row etc) are
|
||||
now configurable, via the new **select-begin**,
|
||||
**select-begin-block**, **select-extend**, **select-word**,
|
||||
**select-word-whitespace** and **select-row** options in the
|
||||
**mouse-bindings** section in `footrc`
|
||||
(https://codeberg.org/dnkl/foot/issues/79).
|
||||
|
||||
|
||||
### Deprecated
|
||||
|
|
|
|||
33
config.c
33
config.c
|
|
@ -69,6 +69,14 @@ static const char *const binding_action_map[] = {
|
|||
[BIND_ACTION_PIPE_SCROLLBACK] = "pipe-scrollback",
|
||||
[BIND_ACTION_PIPE_VIEW] = "pipe-visible",
|
||||
[BIND_ACTION_PIPE_SELECTED] = "pipe-selected",
|
||||
|
||||
/* Mouse-specific actions */
|
||||
[BIND_ACTION_SELECT_BEGIN] = "select-begin",
|
||||
[BIND_ACTION_SELECT_BEGIN_BLOCK] = "select-begin-block",
|
||||
[BIND_ACTION_SELECT_EXTEND] = "select-extend",
|
||||
[BIND_ACTION_SELECT_WORD] = "select-word",
|
||||
[BIND_ACTION_SELECT_WORD_WS] = "select-word-whitespace",
|
||||
[BIND_ACTION_SELECT_ROW] = "select-row",
|
||||
};
|
||||
|
||||
static_assert(ALEN(binding_action_map) == BIND_ACTION_COUNT,
|
||||
|
|
@ -643,7 +651,8 @@ parse_section_csd(const char *key, const char *value, struct config *conf,
|
|||
}
|
||||
|
||||
else {
|
||||
LOG_AND_NOTIFY_ERR("%s:%u: [csd]: %s: invalid key", path, lineno, key);
|
||||
LOG_AND_NOTIFY_ERR("%s:%u: [csd]: %s: invalid action",
|
||||
path, lineno, key);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -859,7 +868,7 @@ parse_section_key_bindings(
|
|||
}
|
||||
|
||||
for (enum bind_action_normal action = 0;
|
||||
action < BIND_ACTION_COUNT;
|
||||
action < BIND_ACTION_KEY_COUNT;
|
||||
action++)
|
||||
{
|
||||
if (binding_action_map[action] == NULL)
|
||||
|
|
@ -932,7 +941,8 @@ parse_section_key_bindings(
|
|||
return true;
|
||||
}
|
||||
|
||||
LOG_AND_NOTIFY_ERR("%s:%u: [key-bindings]: %s: invalid key", path, lineno, key);
|
||||
LOG_AND_NOTIFY_ERR("%s:%u: [key-bindings]: %s: invalid action",
|
||||
path, lineno, key);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
|
@ -1016,6 +1026,11 @@ parse_mouse_combos(struct config *conf, const char *combos, key_combo_list_t *ke
|
|||
*key = '\0';
|
||||
if (!parse_modifiers(conf, combo, key - combo, &modifiers, path, lineno))
|
||||
goto err;
|
||||
if (modifiers.shift) {
|
||||
LOG_AND_NOTIFY_ERR("%s:%d: Shift cannot be used in mosue bindings",
|
||||
path, lineno);
|
||||
goto err;
|
||||
}
|
||||
key++; /* Skip past the '+' */
|
||||
}
|
||||
|
||||
|
|
@ -1121,7 +1136,10 @@ parse_section_mouse_bindings(
|
|||
const char *key, const char *value, struct config *conf,
|
||||
const char *path, unsigned lineno)
|
||||
{
|
||||
for (enum bind_action_normal action = 0; action < BIND_ACTION_COUNT; action++) {
|
||||
for (enum bind_action_normal action = 0;
|
||||
action < BIND_ACTION_COUNT;
|
||||
action++)
|
||||
{
|
||||
if (binding_action_map[action] == NULL)
|
||||
continue;
|
||||
|
||||
|
|
@ -1519,8 +1537,15 @@ add_default_mouse_bindings(struct config *conf)
|
|||
} while (0)
|
||||
|
||||
const struct config_key_modifiers none = {};
|
||||
const struct config_key_modifiers ctrl = {.ctrl = true};
|
||||
|
||||
add_binding(BIND_ACTION_PRIMARY_PASTE, none, BTN_MIDDLE, 1);
|
||||
add_binding(BIND_ACTION_SELECT_BEGIN, none, BTN_LEFT, 1);
|
||||
add_binding(BIND_ACTION_SELECT_BEGIN_BLOCK, ctrl, BTN_LEFT, 1);
|
||||
add_binding(BIND_ACTION_SELECT_EXTEND, none, BTN_RIGHT, 1);
|
||||
add_binding(BIND_ACTION_SELECT_WORD, none, BTN_LEFT, 2);
|
||||
add_binding(BIND_ACTION_SELECT_WORD_WS, ctrl, BTN_LEFT, 2);
|
||||
add_binding(BIND_ACTION_SELECT_ROW, none, BTN_LEFT, 3);
|
||||
|
||||
#undef add_binding
|
||||
}
|
||||
|
|
|
|||
|
|
@ -364,27 +364,53 @@ This section lets you override the default mouse bindings.
|
|||
|
||||
The general format is _action=combo1...comboN_. That is, each action
|
||||
may have one or more key combinations, space separated. Each
|
||||
combination is on the form _mod1+mod2+BTN\_<name>_[-COUNT]. The names
|
||||
of the modifiers and the key *must* be valid XKB key names. You can
|
||||
find the button names using *libinput debug-events*.
|
||||
combination is on the form _mod1+mod2+BTN\_<name>[-COUNT]_. The names
|
||||
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*.
|
||||
|
||||
Note that *Shift* cannot be used as a modifier in mouse bindings since
|
||||
it is used to enable selection when the client application is grabbing
|
||||
the mouse.
|
||||
|
||||
The trailing *COUNT* is optional and specifies the click count
|
||||
required to trigger the binding. The default is *COUNT* is omitted is
|
||||
required to trigger the binding. The default if *COUNT* is omitted is
|
||||
_1_.
|
||||
|
||||
Example: *primary-paste=Control+BTN_LEFT-2*
|
||||
|
||||
Trigger _primary-paste_ when *ctrl* is pressed, and the left mouse
|
||||
button is double clicked.
|
||||
|
||||
A button can only be mapped to *one* action. Lets say you want to bind
|
||||
*BTN\_MIDDLE* to *fullscreen*. Since *BTN\_MIDDLE* is the default
|
||||
binding for *primary-paste*, you first need to unmap the default
|
||||
binding. This can be done by setting _action=none_;
|
||||
e.g. *primary-paste=none*.
|
||||
A modifier+button combination can only be mapped to *one* action. Lets
|
||||
say you want to bind *BTN\_MIDDLE* to *fullscreen*. Since
|
||||
*BTN\_MIDDLE* is the default binding for *primary-paste*, you first
|
||||
need to unmap the default binding. This can be done by setting
|
||||
_action=none_; e.g. *primary-paste=none*.
|
||||
|
||||
All actions listed under *key-bindings* can be user here as well.
|
||||
|
||||
*select-begin*
|
||||
Begin an interactive selection. The selection is finalized, and
|
||||
copied to the _primary selection_, when the button is
|
||||
released. Default: _BTN\_LEFT_.
|
||||
|
||||
*select-begin-block*
|
||||
Begin an interactive block selection. The selection is finalized,
|
||||
and copied to the _primary selection_, when the button is
|
||||
released. Default: _Control+BTN\_LEFT_.
|
||||
|
||||
*select-extend*
|
||||
Interactively extend an existing selection. The selection is
|
||||
finalized, and copied to the _primary selection_, when the button
|
||||
is released. Default: _BTN\_RIGHT_.
|
||||
|
||||
*select-word*
|
||||
Select the _word_ (separated by spaces, period, comma, parenthesis
|
||||
etc) under the pointer. Default: _BTN\_LEFT-2_.
|
||||
|
||||
*select-word-whitespace*
|
||||
Select the _word_ (separated by spaces _only_) under the
|
||||
pointer. Default: Control+_BTN\_LEFT-2_.
|
||||
|
||||
*select-row*
|
||||
Select the whole row under the pointer. Default: _BTN\_LEFT-3_.
|
||||
|
||||
*primary-paste*
|
||||
Pastes from the _primary selection_. Default: _BTN\_MIDDLE_.
|
||||
|
||||
|
|
|
|||
6
footrc
6
footrc
|
|
@ -93,3 +93,9 @@
|
|||
|
||||
[mouse-bindings]
|
||||
# primary-paste=BTN_MIDDLE
|
||||
# select-begin=BTN_LEFT
|
||||
# select-begin-block=Control+BTN_LEFT
|
||||
# select-extend=BTN_RIGHT
|
||||
# select-word=BTN_LEFT-2
|
||||
# select-word-whitespace=Control+BTN_LEFT-2
|
||||
# select-row=BTN_LEFT-3
|
||||
|
|
|
|||
92
input.c
92
input.c
|
|
@ -81,6 +81,8 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
enum bind_action_normal action, char *const *pipe_argv,
|
||||
uint32_t serial)
|
||||
{
|
||||
const bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0;
|
||||
|
||||
switch (action) {
|
||||
case BIND_ACTION_NONE:
|
||||
break;
|
||||
|
|
@ -254,6 +256,46 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
break;
|
||||
}
|
||||
|
||||
case BIND_ACTION_SELECT_BEGIN:
|
||||
if (selection_enabled(term, seat) && cursor_is_on_grid) {
|
||||
selection_start(
|
||||
term, seat->mouse.col, seat->mouse.row, SELECTION_NORMAL);
|
||||
}
|
||||
break;
|
||||
|
||||
case BIND_ACTION_SELECT_BEGIN_BLOCK:
|
||||
if (selection_enabled(term, seat) && cursor_is_on_grid) {
|
||||
selection_start(
|
||||
term, seat->mouse.col, seat->mouse.row, SELECTION_BLOCK);
|
||||
}
|
||||
break;
|
||||
|
||||
case BIND_ACTION_SELECT_EXTEND:
|
||||
if (selection_enabled(term, seat) && cursor_is_on_grid) {
|
||||
selection_extend(
|
||||
seat, term, seat->mouse.col, seat->mouse.row, serial);
|
||||
}
|
||||
break;
|
||||
|
||||
case BIND_ACTION_SELECT_WORD:
|
||||
if (selection_enabled(term, seat) && cursor_is_on_grid) {
|
||||
selection_mark_word(
|
||||
seat, term, seat->mouse.col, seat->mouse.row, false, serial);
|
||||
}
|
||||
break;
|
||||
|
||||
case BIND_ACTION_SELECT_WORD_WS:
|
||||
if (selection_enabled(term, seat) && cursor_is_on_grid) {
|
||||
selection_mark_word(
|
||||
seat, term, seat->mouse.col, seat->mouse.row, true, serial);
|
||||
}
|
||||
break;
|
||||
|
||||
case BIND_ACTION_SELECT_ROW:
|
||||
if (selection_enabled(term, seat) && cursor_is_on_grid)
|
||||
selection_mark_row(seat, term, seat->mouse.row, serial);
|
||||
break;
|
||||
|
||||
case BIND_ACTION_COUNT:
|
||||
assert(false);
|
||||
break;
|
||||
|
|
@ -1254,7 +1296,7 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
|||
bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0;
|
||||
|
||||
/* Update selection */
|
||||
if (seat->mouse.button == BTN_LEFT || seat->mouse.button == BTN_RIGHT) {
|
||||
if (!term->is_searching) {
|
||||
if (cursor_is_on_new_cell || term->selection.end.row < 0)
|
||||
selection_update(term, selection_col, selection_row);
|
||||
}
|
||||
|
|
@ -1437,44 +1479,19 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
|
||||
bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0;
|
||||
|
||||
xkb_mod_mask_t mods = xkb_state_serialize_mods(
|
||||
seat->kbd.xkb_state, XKB_STATE_MODS_DEPRESSED);
|
||||
|
||||
switch (state) {
|
||||
case WL_POINTER_BUTTON_STATE_PRESSED: {
|
||||
if (button == BTN_LEFT && seat->mouse.count <= 3 && mods == 0) {
|
||||
selection_cancel(term);
|
||||
|
||||
if (selection_enabled(term, seat) && cursor_is_on_grid) {
|
||||
switch (seat->mouse.count) {
|
||||
case 1:
|
||||
selection_start(
|
||||
term, seat->mouse.col, seat->mouse.row,
|
||||
seat->kbd.ctrl ? SELECTION_BLOCK : SELECTION_NORMAL);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
selection_mark_word(
|
||||
seat, term, seat->mouse.col, seat->mouse.row,
|
||||
seat->kbd.ctrl, serial);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
selection_mark_row(seat, term, seat->mouse.row, serial);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (button == BTN_RIGHT && seat->mouse.count == 1 && mods == 0) {
|
||||
if (selection_enabled(term, seat) && cursor_is_on_grid) {
|
||||
selection_extend(
|
||||
seat, term, seat->mouse.col, seat->mouse.row, serial);
|
||||
}
|
||||
}
|
||||
|
||||
else if (seat->wl_keyboard != NULL) {
|
||||
if (seat->wl_keyboard != NULL) {
|
||||
/* Seat has keyboard - use mouse bindings *with* modifiers */
|
||||
|
||||
xkb_mod_mask_t mods = xkb_state_serialize_mods(
|
||||
seat->kbd.xkb_state, XKB_STATE_MODS_DEPRESSED);
|
||||
|
||||
/* Ignore Shift when matching modifiers, since it is
|
||||
* used to enable selection in mouse grabbing client
|
||||
* applications */
|
||||
mods &= ~(1 << seat->kbd.mod_shift);
|
||||
|
||||
tll_foreach(seat->mouse.bindings, it) {
|
||||
const struct mouse_binding *binding = &it->item;
|
||||
|
||||
|
|
@ -1533,8 +1550,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
}
|
||||
|
||||
case WL_POINTER_BUTTON_STATE_RELEASED:
|
||||
if (button == BTN_LEFT && term->selection.end.col != -1)
|
||||
selection_finalize(seat, term, serial);
|
||||
selection_finalize(seat, term, serial);
|
||||
|
||||
if (!term_mouse_grabbed(term, seat) && cursor_is_on_grid) {
|
||||
term_mouse_up(
|
||||
|
|
|
|||
12
selection.c
12
selection.c
|
|
@ -237,6 +237,7 @@ selection_start(struct terminal *term, int col, int row,
|
|||
term->selection.kind = kind;
|
||||
term->selection.start = (struct coord){col, term->grid->view + row};
|
||||
term->selection.end = (struct coord){-1, -1};
|
||||
term->selection.ongoing = true;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -310,6 +311,9 @@ selection_update(struct terminal *term, int col, int row)
|
|||
if (term->selection.start.row < 0)
|
||||
return;
|
||||
|
||||
if (!term->selection.ongoing)
|
||||
return;
|
||||
|
||||
LOG_DBG("selection updated: start = %d,%d, end = %d,%d -> %d, %d",
|
||||
term->selection.start.row, term->selection.start.col,
|
||||
term->selection.end.row, term->selection.end.col,
|
||||
|
|
@ -532,6 +536,8 @@ selection_extend(struct seat *seat, struct terminal *term,
|
|||
return;
|
||||
}
|
||||
|
||||
term->selection.ongoing = true;
|
||||
|
||||
row += term->grid->view;
|
||||
|
||||
if ((row == term->selection.start.row && col == term->selection.start.col) ||
|
||||
|
|
@ -563,6 +569,11 @@ selection_extend(struct seat *seat, struct terminal *term,
|
|||
void
|
||||
selection_finalize(struct seat *seat, struct terminal *term, uint32_t serial)
|
||||
{
|
||||
if (!term->selection.ongoing)
|
||||
return;
|
||||
|
||||
term->selection.ongoing = false;
|
||||
|
||||
if (term->selection.start.row < 0 || term->selection.end.row < 0)
|
||||
return;
|
||||
|
||||
|
|
@ -600,6 +611,7 @@ selection_cancel(struct terminal *term)
|
|||
term->selection.start = (struct coord){-1, -1};
|
||||
term->selection.end = (struct coord){-1, -1};
|
||||
term->selection.direction = SELECTION_UNDIR;
|
||||
term->selection.ongoing = false;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -311,6 +311,7 @@ struct terminal {
|
|||
enum selection_direction direction;
|
||||
struct coord start;
|
||||
struct coord end;
|
||||
bool ongoing;
|
||||
} selection;
|
||||
|
||||
bool is_searching;
|
||||
|
|
|
|||
11
wayland.h
11
wayland.h
|
|
@ -42,6 +42,17 @@ enum bind_action_normal {
|
|||
BIND_ACTION_PIPE_SCROLLBACK,
|
||||
BIND_ACTION_PIPE_VIEW,
|
||||
BIND_ACTION_PIPE_SELECTED,
|
||||
|
||||
BIND_ACTION_KEY_COUNT = BIND_ACTION_PIPE_SELECTED,
|
||||
|
||||
/* 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_WORD,
|
||||
BIND_ACTION_SELECT_WORD_WS,
|
||||
BIND_ACTION_SELECT_ROW,
|
||||
|
||||
BIND_ACTION_COUNT,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue