Merge branch 'mouse-binding-actions' into master

Closes #79. Closes #75.
This commit is contained in:
Daniel Eklöf 2020-08-14 07:39:11 +02:00
commit 394db77b04
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
8 changed files with 163 additions and 56 deletions

View file

@ -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

View file

@ -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
}

View file

@ -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
View file

@ -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
View file

@ -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(

View file

@ -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

View file

@ -311,6 +311,7 @@ struct terminal {
enum selection_direction direction;
struct coord start;
struct coord end;
bool ongoing;
} selection;
bool is_searching;

View file

@ -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,
};