From 8ed3f66ad9092dcdbed210cabceed8b0584c7034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 10 Aug 2020 19:16:26 +0200 Subject: [PATCH 01/16] changelog: modifier support in mouse bindings --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8b7aba5..c11c9adb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ (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). ### Deprecated From 1dd142aeab67bdfed75eee21d8408593013ebef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 10 Aug 2020 20:59:05 +0200 Subject: [PATCH 02/16] changelog: click count in mouse bindings --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11c9adb..b0eab47a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ (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). ### Deprecated From 20f0334e1314508683651b0e5c59fd637d1d13f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 09:55:33 +0200 Subject: [PATCH 03/16] config: add mouse specific bind actions This extends the "normal" bind action enum with mouse specific actions. When parsing key bindings, we only check up to the last valid keyboard binding, while mouse bindings support *both* key actions and mouse actions. The new actions are: * select-begin: starts an interactive selection * select-extend: interactively extend an existing selection * select-word: select word under cursor * select-word-whitespace: select word under cursor, where the only word separating characters are whitespace characters. The old hard-coded selection "bindings" have been converted to instead use these actions, via default bindings added to the configuration. --- config.c | 24 +++++++++++++++++---- footrc | 4 ++++ input.c | 61 ++++++++++++++++++++++++----------------------------- selection.c | 7 ++++++ terminal.h | 1 + wayland.h | 9 ++++++++ 6 files changed, 68 insertions(+), 38 deletions(-) diff --git a/config.c b/config.c index 8e51b608..69883b68 100644 --- a/config.c +++ b/config.c @@ -69,6 +69,12 @@ 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_WORD] = "select-word", + [BIND_ACTION_SELECT_WORD_WS] = "select-word-whitespace", + [BIND_ACTION_SELECT_EXTEND] = "select-extend", }; static_assert(ALEN(binding_action_map) == BIND_ACTION_COUNT, @@ -643,7 +649,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 +866,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 +939,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; } @@ -1121,7 +1129,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 +1530,13 @@ 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_WORD, none, BTN_LEFT, 2); + add_binding(BIND_ACTION_SELECT_WORD_WS, ctrl, BTN_LEFT, 2); + add_binding(BIND_ACTION_SELECT_EXTEND, none, BTN_RIGHT, 1); #undef add_binding } diff --git a/footrc b/footrc index ec36bbc1..062b9177 100644 --- a/footrc +++ b/footrc @@ -93,3 +93,7 @@ [mouse-bindings] # primary-paste=BTN_MIDDLE +# select-begin=BTN_LEFT +# select-word=BTN_LEFT-2 +# select-word-whitespace=Control+BTN_LEFT-2 +# select-extend=BTN_RIGHT diff --git a/input.c b/input.c index b7c5031a..9e260e69 100644 --- a/input.c +++ b/input.c @@ -254,6 +254,31 @@ execute_binding(struct seat *seat, struct terminal *term, break; } + case BIND_ACTION_SELECT_BEGIN: + selection_start( + term, seat->mouse.col, seat->mouse.row, + seat->kbd.ctrl ? SELECTION_BLOCK : SELECTION_NORMAL); + break; + + case BIND_ACTION_SELECT_WORD: + selection_mark_word( + seat, term, seat->mouse.col, seat->mouse.row, false, serial); + break; + + case BIND_ACTION_SELECT_WORD_WS: + selection_mark_word( + seat, term, seat->mouse.col, seat->mouse.row, true, serial); + break; + + case BIND_ACTION_SELECT_EXTEND: { + bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + if (selection_enabled(term, seat) && cursor_is_on_grid) { + selection_extend( + seat, term, seat->mouse.col, seat->mouse.row, serial); + } + break; + } + case BIND_ACTION_COUNT: assert(false); break; @@ -1442,38 +1467,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, 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 */ tll_foreach(seat->mouse.bindings, it) { const struct mouse_binding *binding = &it->item; @@ -1533,8 +1527,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( diff --git a/selection.c b/selection.c index a078a698..3eadf2ea 100644 --- a/selection.c +++ b/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 @@ -566,6 +567,11 @@ selection_finalize(struct seat *seat, struct terminal *term, uint32_t serial) if (term->selection.start.row < 0 || term->selection.end.row < 0) return; + if (!term->selection.ongoing) + return; + + term->selection.ongoing = false; + assert(term->selection.start.row != -1); assert(term->selection.end.row != -1); @@ -600,6 +606,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 diff --git a/terminal.h b/terminal.h index 8509634a..2f2d8881 100644 --- a/terminal.h +++ b/terminal.h @@ -311,6 +311,7 @@ struct terminal { enum selection_direction direction; struct coord start; struct coord end; + bool ongoing; } selection; bool is_searching; diff --git a/wayland.h b/wayland.h index b08c25fd..b7528a7f 100644 --- a/wayland.h +++ b/wayland.h @@ -42,6 +42,15 @@ 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_WORD, + BIND_ACTION_SELECT_WORD_WS, + BIND_ACTION_SELECT_EXTEND, + BIND_ACTION_COUNT, }; From 9352befd13d42199f9c9b67fb6b147df5d2998c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:03:14 +0200 Subject: [PATCH 04/16] doc: footrc: document the new mouse actions --- doc/footrc.5.scd | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/footrc.5.scd b/doc/footrc.5.scd index 5f39f72d..4476daa1 100644 --- a/doc/footrc.5.scd +++ b/doc/footrc.5.scd @@ -385,6 +385,24 @@ 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-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_. + *primary-paste* Pastes from the _primary selection_. Default: _BTN\_MIDDLE_. From cddeaa2c1c648682e5d966d173f1663bee1d95a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:14:38 +0200 Subject: [PATCH 05/16] selection: update: don't update if there's no ongoing selection --- selection.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selection.c b/selection.c index 3eadf2ea..d0ed7bae 100644 --- a/selection.c +++ b/selection.c @@ -311,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, From 9517c6443c0a0a9c8abf350e388c0be505c03de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:15:01 +0200 Subject: [PATCH 06/16] selection: finalize: clear ongoing selection Check for ongoing selection, and *clear* it before bailing out due to the selection not having an end-point. This fixes an issue where a selection was kept as ongoing, even though the button had been released. Typically triggered by clicking without moving the mouse; this started a new selection, then (tried to) finalize it, but failed since the selection didn't have an end-point. --- selection.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selection.c b/selection.c index d0ed7bae..43ef2938 100644 --- a/selection.c +++ b/selection.c @@ -567,14 +567,14 @@ selection_extend(struct seat *seat, struct terminal *term, void selection_finalize(struct seat *seat, struct terminal *term, uint32_t serial) { - if (term->selection.start.row < 0 || term->selection.end.row < 0) - return; - if (!term->selection.ongoing) return; term->selection.ongoing = false; + if (term->selection.start.row < 0 || term->selection.end.row < 0) + return; + assert(term->selection.start.row != -1); assert(term->selection.end.row != -1); From f14b49068ac404879495b42343a6ab2e78b0609f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:16:52 +0200 Subject: [PATCH 07/16] selection: extend: set ongoing to true --- selection.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selection.c b/selection.c index 43ef2938..0f1731e6 100644 --- a/selection.c +++ b/selection.c @@ -536,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) || From 4d2bc54fa216dfda98c4b228b9576dcad471858c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:17:19 +0200 Subject: [PATCH 08/16] config: mouse bindings: add select-begin-block and select-row --- config.c | 8 ++++++-- doc/footrc.5.scd | 10 +++++++++- footrc | 4 +++- input.c | 33 +++++++++++++++++++-------------- wayland.h | 4 +++- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/config.c b/config.c index 69883b68..d46b5f8e 100644 --- a/config.c +++ b/config.c @@ -72,9 +72,11 @@ static const char *const binding_action_map[] = { /* 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_EXTEND] = "select-extend", + [BIND_ACTION_SELECT_ROW] = "select-row", }; static_assert(ALEN(binding_action_map) == BIND_ACTION_COUNT, @@ -1534,9 +1536,11 @@ add_default_mouse_bindings(struct config *conf) 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_EXTEND, none, BTN_RIGHT, 1); + add_binding(BIND_ACTION_SELECT_ROW, none, BTN_LEFT, 3); #undef add_binding } diff --git a/doc/footrc.5.scd b/doc/footrc.5.scd index 4476daa1..a5792671 100644 --- a/doc/footrc.5.scd +++ b/doc/footrc.5.scd @@ -388,7 +388,12 @@ 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_. + 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 @@ -403,6 +408,9 @@ All actions listed under *key-bindings* can be user here as well. 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_. diff --git a/footrc b/footrc index 062b9177..a76c71e9 100644 --- a/footrc +++ b/footrc @@ -94,6 +94,8 @@ [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-extend=BTN_RIGHT +# select-row=BTN_LEFT-3 diff --git a/input.c b/input.c index 9e260e69..518a690b 100644 --- a/input.c +++ b/input.c @@ -255,11 +255,22 @@ execute_binding(struct seat *seat, struct terminal *term, } case BIND_ACTION_SELECT_BEGIN: - selection_start( - term, seat->mouse.col, seat->mouse.row, - seat->kbd.ctrl ? SELECTION_BLOCK : SELECTION_NORMAL); + selection_start(term, seat->mouse.col, seat->mouse.row, SELECTION_NORMAL); break; + case BIND_ACTION_SELECT_BEGIN_BLOCK: + selection_start(term, seat->mouse.col, seat->mouse.row, SELECTION_BLOCK); + break; + + case BIND_ACTION_SELECT_EXTEND: { + bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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: selection_mark_word( seat, term, seat->mouse.col, seat->mouse.row, false, serial); @@ -270,14 +281,10 @@ execute_binding(struct seat *seat, struct terminal *term, seat, term, seat->mouse.col, seat->mouse.row, true, serial); break; - case BIND_ACTION_SELECT_EXTEND: { - bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; - if (selection_enabled(term, seat) && cursor_is_on_grid) { - selection_extend( - seat, term, seat->mouse.col, seat->mouse.row, serial); - } + case BIND_ACTION_SELECT_ROW: + selection_mark_row(seat, term, seat->mouse.row, serial); break; - } + case BIND_ACTION_COUNT: assert(false); @@ -1279,10 +1286,8 @@ 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 (cursor_is_on_new_cell || term->selection.end.row < 0) - selection_update(term, selection_col, selection_row); - } + if (cursor_is_on_new_cell || term->selection.end.row < 0) + selection_update(term, selection_col, selection_row); /* Send mouse event to client application */ if (!term_mouse_grabbed(term, seat) && diff --git a/wayland.h b/wayland.h index b7528a7f..85cc1ca7 100644 --- a/wayland.h +++ b/wayland.h @@ -47,9 +47,11 @@ enum bind_action_normal { /* 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_EXTEND, + BIND_ACTION_SELECT_ROW, BIND_ACTION_COUNT, }; From 8f99a032c98f4ea26408ebb47036e73e4e723647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:26:31 +0200 Subject: [PATCH 09/16] changelog: new mouse actions --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0eab47a..9351816b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,12 @@ (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 From 24ee6d836be6ce3952fecfb5edc44386046a1b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:30:27 +0200 Subject: [PATCH 10/16] doc: footrc: mouse-bindings: clean up description --- doc/footrc.5.scd | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/doc/footrc.5.scd b/doc/footrc.5.scd index a5792671..64f9c500 100644 --- a/doc/footrc.5.scd +++ b/doc/footrc.5.scd @@ -364,24 +364,20 @@ 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\__[-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\_[-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*. 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. From 28410f1b998d1b298941530355d1e6bb37f269a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:42:23 +0200 Subject: [PATCH 11/16] input: mouse-bindings: check selection is enabled/possible and that pointer is on grid We shouldn't start or modify a selection if selection isn't possible (i.e. client application is grabbing the mouse, and user isn't holding Shift). We also shouldn't start or modify a selection if the pointer is outside the grid (updating an ongoing selection on motion events is an exception to this). --- input.c | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/input.c b/input.c index 518a690b..e1e061ed 100644 --- a/input.c +++ b/input.c @@ -254,13 +254,23 @@ execute_binding(struct seat *seat, struct terminal *term, break; } - case BIND_ACTION_SELECT_BEGIN: - selection_start(term, seat->mouse.col, seat->mouse.row, SELECTION_NORMAL); + case BIND_ACTION_SELECT_BEGIN: { + bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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: - selection_start(term, seat->mouse.col, seat->mouse.row, SELECTION_BLOCK); + case BIND_ACTION_SELECT_BEGIN_BLOCK: { + bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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: { bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; @@ -271,20 +281,30 @@ execute_binding(struct seat *seat, struct terminal *term, break; } - case BIND_ACTION_SELECT_WORD: - selection_mark_word( - seat, term, seat->mouse.col, seat->mouse.row, false, serial); + case BIND_ACTION_SELECT_WORD: { + bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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: - selection_mark_word( - seat, term, seat->mouse.col, seat->mouse.row, true, serial); + case BIND_ACTION_SELECT_WORD_WS: { + bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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: - selection_mark_row(seat, term, seat->mouse.row, serial); + case BIND_ACTION_SELECT_ROW: { + bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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); From 517d13fbce3ae56f154343671c325c000a7de040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:44:27 +0200 Subject: [PATCH 12/16] config: don't allow Shift in mouse bindings Shift is a special modifier that is used to enable selection when the client application is grabbing the mouse. --- config.c | 5 +++++ doc/footrc.5.scd | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/config.c b/config.c index d46b5f8e..40a7e81b 100644 --- a/config.c +++ b/config.c @@ -1026,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 '+' */ } diff --git a/doc/footrc.5.scd b/doc/footrc.5.scd index 64f9c500..d5ea808d 100644 --- a/doc/footrc.5.scd +++ b/doc/footrc.5.scd @@ -369,6 +369,10 @@ 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 if *COUNT* is omitted is _1_. From 364412bfaa09b282389a988432cbe1aa2ad30bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:45:01 +0200 Subject: [PATCH 13/16] input: mouse-bindings: ignore Shift Shift is used to enable selection when the client application is grabbing the mouse. As such, mouse bindings *never* have Shift as a modifier, but should still trigger when Shift is being pressed. --- input.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/input.c b/input.c index e1e061ed..d7ae76f3 100644 --- a/input.c +++ b/input.c @@ -1487,13 +1487,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 (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; From 2764a8394a365571ff8937723316fe38c754b8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:52:02 +0200 Subject: [PATCH 14/16] footrc: fix combo for select-begin-block Typo: should be a '+', not a '='. --- footrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/footrc b/footrc index a76c71e9..207ee657 100644 --- a/footrc +++ b/footrc @@ -94,7 +94,7 @@ [mouse-bindings] # primary-paste=BTN_MIDDLE # select-begin=BTN_LEFT -# select-begin-block=Control=BTN_LEFT +# select-begin-block=Control+BTN_LEFT # select-extend=BTN_RIGHT # select-word=BTN_LEFT-2 # select-word-whitespace=Control+BTN_LEFT-2 From bb9228dd21785c8621d9b413c3e630b5f47bbce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 10:53:21 +0200 Subject: [PATCH 15/16] input: bindings: calculate 'cursor_is_on_grid' once only --- input.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/input.c b/input.c index d7ae76f3..cb4c84cf 100644 --- a/input.c +++ b/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,57 +256,45 @@ execute_binding(struct seat *seat, struct terminal *term, break; } - case BIND_ACTION_SELECT_BEGIN: { - bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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: { - bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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: { - bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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: { - bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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: { - bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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: { - bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; + 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); From d29c4aed8a424b67eb0be4dbb7f8d964357cdfaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Aug 2020 16:36:42 +0200 Subject: [PATCH 16/16] input: mouse motion: don't update selection while scrollback searching When we are doing a scrollback search, the selection *is* ongoing. We still don't want to update the selection when the mouse moves. --- input.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/input.c b/input.c index cb4c84cf..3cd6830d 100644 --- a/input.c +++ b/input.c @@ -1296,8 +1296,10 @@ 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 (cursor_is_on_new_cell || term->selection.end.row < 0) - selection_update(term, selection_col, selection_row); + if (!term->is_searching) { + if (cursor_is_on_new_cell || term->selection.end.row < 0) + selection_update(term, selection_col, selection_row); + } /* Send mouse event to client application */ if (!term_mouse_grabbed(term, seat) &&