From 210c0ee5cd9cdfac3a8f6430c5f5881e01bf48c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Apr 2020 11:57:08 +0200 Subject: [PATCH 1/8] input: only cancel selection on left mouse button clicks --- input.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/input.c b/input.c index 4fff710f..1b1a7fff 100644 --- a/input.c +++ b/input.c @@ -1175,6 +1175,8 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, switch (state) { case WL_POINTER_BUTTON_STATE_PRESSED: { if (button == BTN_LEFT) { + selection_cancel(term); + switch (wayl->mouse.count) { case 1: selection_start( @@ -1211,7 +1213,6 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, execute_binding(term, binding->action, serial); break; } - selection_cancel(term); } term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col); @@ -1219,9 +1220,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_cancel(term); - else + if (button == BTN_LEFT && term->selection.end.col != -1) selection_finalize(term, serial); term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col); From 2344f153d938d4d49ccfbd60fb25e1b2170209b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Apr 2020 11:57:33 +0200 Subject: [PATCH 2/8] input: hardcoded left mouse button handler requires click-count <= 3 This enables user mappings for the left mouse button with click count > 3 I.e. it is now possible to create custom quad-click mappings (except we don't yet support this in footrc). --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 1b1a7fff..23a0c3a7 100644 --- a/input.c +++ b/input.c @@ -1174,7 +1174,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, switch (state) { case WL_POINTER_BUTTON_STATE_PRESSED: { - if (button == BTN_LEFT) { + if (button == BTN_LEFT && wayl->mouse.count <= 3) { selection_cancel(term); switch (wayl->mouse.count) { From aa01521ff668f201ba4297bf0558a76835144cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Apr 2020 11:59:15 +0200 Subject: [PATCH 3/8] selection: add selection_extend() This function extends an existing selection in the following way: If the extension point is *before* the upper boundary of the current selection, extend the selection upwards. If the extension point is *after* the bottom boundary of the current selection, extend the selection downwards. If the extension point is *inside* the current selection, shrink the selection such that the new size is maximized. This means we move the *closest* start/end point from in the current selection. --- selection.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++---- selection.h | 1 + 2 files changed, 172 insertions(+), 14 deletions(-) diff --git a/selection.c b/selection.c index 189119d9..ba6271ab 100644 --- a/selection.c +++ b/selection.c @@ -335,6 +335,32 @@ mark_selected(struct terminal *term, struct row *row, struct cell *cell, cell->attrs.clean = 0; } +static void +selection_modify(struct terminal *term, struct coord start, struct coord end) +{ + assert(selection_enabled(term)); + assert(term->selection.start.row != -1); + assert(start.row != -1 && start.col != -1); + assert(end.row != -1 && end.col != -1); + + /* Premark all cells that *will* be selected */ + foreach_selected(term, start, end, &premark_selected, NULL); + + if (term->selection.end.row != -1) { + /* Unmark previous selection, ignoring cells that are part of + * the new selection */ + foreach_selected(term, term->selection.start, term->selection.end, + &unmark_selected, NULL); + } + + term->selection.start = start; + term->selection.end = end; + + /* Mark new selection */ + foreach_selected(term, start, end, &mark_selected, NULL); + render_refresh(term); +} + void selection_update(struct terminal *term, int col, int row) { @@ -350,26 +376,157 @@ selection_update(struct terminal *term, int col, int row) assert(term->grid->view + row != -1); struct coord new_end = {col, term->grid->view + row}; + selection_modify(term, term->selection.start, new_end); +} - /* Premark all cells that *will* be selected */ - foreach_selected( - term, term->selection.start, new_end, &premark_selected, NULL); +static void +selection_extend_normal(struct terminal *term, int col, int row, uint32_t serial) +{ + const struct coord *start = &term->selection.start; + const struct coord *end = &term->selection.end; - if (term->selection.end.row != -1) { - /* Unmark previous selection, ignoring cells that are part of - * the new selection */ - foreach_selected(term, term->selection.start, term->selection.end, - &unmark_selected, NULL); + if (start->row > end->row || + (start->row == end->row && start->col > end->col)) + { + const struct coord *tmp = start; + start = end; + end = tmp; } - term->selection.end = new_end; - assert(term->selection.start.row != -1 && term->selection.end.row != -1); + assert(start->row < end->row || start->col < end->col); - /* Mark new selection */ - foreach_selected( - term, term->selection.start, term->selection.end, &mark_selected, NULL); + struct coord new_start, new_end; - render_refresh(term); + if (row < start->row || (row == start->row && col < start->col)) { + /* Extend selection to start *before* current start */ + new_start = (struct coord){col, row}; + new_end = *end; + } + + else if (row > end->row || (row == end->row && col > end->col)) { + /* Extend selection to end *after* current end */ + new_start = *start; + new_end = (struct coord){col, row}; + } + + else { + /* Shrink selection from start or end, depending on which one is closest */ + + const int linear = row * term->cols + col; + + if (abs(linear - (start->row * term->cols + start->col)) < + abs(linear - (end->row * term->cols + end->col))) + { + /* Move start point */ + new_start = (struct coord){col, row}; + new_end = *end; + } + + else { + /* Move end point */ + new_start = *start; + new_end = (struct coord){col, row}; + } + } + + selection_modify(term, new_start, new_end); +} + +static void +selection_extend_block(struct terminal *term, int col, int row, uint32_t serial) +{ + const struct coord *start = &term->selection.start; + const struct coord *end = &term->selection.end; + + struct coord top_left = { + .row = min(start->row, end->row), + .col = min(start->col, end->col), + }; + + struct coord top_right = { + .row = min(start->row, end->row), + .col = max(start->col, end->col), + }; + + struct coord bottom_left = { + .row = max(start->row, end->row), + .col = min(start->col, end->col), + }; + + struct coord bottom_right = { + .row = max(start->row, end->row), + .col = max(start->col, end->col), + }; + + struct coord new_start; + struct coord new_end; + + if (row <= top_left.row || + abs(row - top_left.row) < abs(row - bottom_left.row)) + { + /* Move one of the top corners */ + + if (abs(col - top_left.col) < abs(col - top_right.col)) { + new_start = (struct coord){col, row}; + new_end = bottom_right; + } + + else { + new_start = (struct coord){col, row}; + new_end = bottom_left; + } + } + + else { + /* Move one of the bottom corners */ + + if (abs(col - bottom_left.col) < abs(col - bottom_right.col)) { + new_start = top_right; + new_end = (struct coord){col, row}; + } + + else { + new_start = top_left; + new_end = (struct coord){col, row}; + } + } + + selection_modify(term, new_start, new_end); +} + +void +selection_extend(struct terminal *term, int col, int row, uint32_t serial) +{ + if (!selection_enabled(term)) + return; + + if (term->selection.start.row == -1 || term->selection.end.row == -1) { + /* No existing selection */ + return; + } + + if ((row == term->selection.start.row && col == term->selection.start.col) || + (row == term->selection.end.row && col == term->selection.end.col)) + { + /* Extension point *is* one of the current end points */ + return; + } + + switch (term->selection.kind) { + case SELECTION_NONE: + assert(false); + return; + + case SELECTION_NORMAL: + selection_extend_normal(term, col, row, serial); + break; + + case SELECTION_BLOCK: + selection_extend_block(term, col, row, serial); + break; + } + + selection_to_primary(term, serial); } static const struct zwp_primary_selection_source_v1_listener primary_selection_source_listener; diff --git a/selection.h b/selection.h index bc690eb1..5bc216e1 100644 --- a/selection.h +++ b/selection.h @@ -14,6 +14,7 @@ void selection_start( void selection_update(struct terminal *term, int col, int row); void selection_finalize(struct terminal *term, uint32_t serial); void selection_cancel(struct terminal *term); +void selection_extend(struct terminal *term, int col, int row, uint32_t serial); bool selection_on_row_in_view(const struct terminal *term, int row_no); From fac4b7310706684bbb719287094d9a9bcd73cb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Apr 2020 12:02:07 +0200 Subject: [PATCH 4/8] input: right mouse click extends the current selection --- input.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/input.c b/input.c index 23a0c3a7..22cefa1e 100644 --- a/input.c +++ b/input.c @@ -1195,6 +1195,10 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, } } + else if (button == BTN_RIGHT && wayl->mouse.count == 1) { + selection_extend(term, wayl->mouse.col, wayl->mouse.row, serial); + } + else { for (size_t i = 0; i < ALEN(wayl->conf->bindings.mouse); i++) { const struct mouse_binding *binding = From 035eccbb13653695c088ea49672315a589b9db59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Apr 2020 12:05:40 +0200 Subject: [PATCH 5/8] selection: extend: new row must be offsetted with the current view offset --- selection.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selection.c b/selection.c index ba6271ab..534a0888 100644 --- a/selection.c +++ b/selection.c @@ -505,6 +505,8 @@ selection_extend(struct terminal *term, int col, int row, uint32_t serial) return; } + row += term->grid->view; + if ((row == term->selection.start.row && col == term->selection.start.col) || (row == term->selection.end.row && col == term->selection.end.col)) { From 51718f2e9ce3fd3eb3dab468f9e473f9dfe0d672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Apr 2020 12:08:46 +0200 Subject: [PATCH 6/8] changelog: right mouse button extends current selection --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67b5a1df..0299d3b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ the Wayland window. * **title** option to `footrc`, that sets the initial window title. * `--title` command line option, that sets the initial window title. +* Right mouse button extends the current selection. ### Changed From 6ed8dc0dda0894f741c783b44c76526c21d8ece0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Apr 2020 12:09:30 +0200 Subject: [PATCH 7/8] readme: right mouse button extends current selection --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 8f5cc079..18dfc559 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,9 @@ These are the default shortcuts. See `man 5 foot` and the example middle : Paste from _primary_ selection +right +: Extend current selection + wheel : Scroll up/down in history From 934452e1d5c59858059989ac726f97ecf30508f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Apr 2020 12:09:56 +0200 Subject: [PATCH 8/8] doc: foot.1: right mouse button extends current selection --- doc/foot.1.scd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/foot.1.scd b/doc/foot.1.scd index 7c2dbccd..551b6a33 100644 --- a/doc/foot.1.scd +++ b/doc/foot.1.scd @@ -194,6 +194,9 @@ Note that these are just the defaults; they can be changed in the *middle* Paste from the _primary_ selection +*right* + Extend current selection + *wheel* Scroll up/down in history