diff --git a/CHANGELOG.md b/CHANGELOG.md index abc72229..309b8e99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,8 @@ about a non-monospaced primary font. * Rare crash when the window is resized while a mouse selection is ongoing (https://codeberg.org/dnkl/foot/issues/922). +* Large selections crossing the scrollback wrap-around + (https://codeberg.org/dnkl/foot/issues/924). ### Security diff --git a/grid.c b/grid.c index 844ea90c..5a0b5c7e 100644 --- a/grid.c +++ b/grid.c @@ -482,6 +482,13 @@ _line_wrap(struct grid *old_grid, struct row **new_grid, struct row *row, tll_remove(old_grid->sixel_images, it); } } + + /* + * TODO: detect if the re-used row is covered by the + * selection. Of so, cancel the selection. The problem: we + * don’t know if we’ve translated the selection coordinates + * yet. + */ } struct row_data *extra = row->extra; diff --git a/selection.c b/selection.c index 0ed25cd7..e9a9f752 100644 --- a/selection.c +++ b/selection.c @@ -107,24 +107,34 @@ selection_view_down(struct terminal *term, int new_view) static void foreach_selected_normal( struct terminal *term, struct coord _start, struct coord _end, - bool (*cb)(struct terminal *term, struct row *row, struct cell *cell, int row_no, int col, void *data), + bool (*cb)(struct terminal *term, struct row *row, struct cell *cell, + int row_no, int col, void *data), void *data) { const struct coord *start = &_start; const struct coord *end = &_end; + const int scrollback_start = term->grid->offset + term->rows; + const int grid_rows = term->grid->num_rows; + + /* Start/end rows, relative to the scrollback start */ + const int rel_start_row = + (start->row - scrollback_start + grid_rows) & (grid_rows - 1); + const int rel_end_row = + (end->row - scrollback_start + grid_rows) & (grid_rows - 1); + int start_row, end_row; int start_col, end_col; - if (start->row < end->row) { + if (rel_start_row < rel_end_row) { start_row = start->row; - end_row = end->row; start_col = start->col; + end_row = end->row; end_col = end->col; - } else if (start->row > end->row) { + } else if (rel_start_row > rel_end_row) { start_row = end->row; - end_row = start->row; start_col = end->col; + end_row = start->row; end_col = start->col; } else { start_row = end_row = start->row; @@ -132,51 +142,77 @@ foreach_selected_normal( end_col = max(start->col, end->col); } - for (int r = start_row; r <= end_row; r++) { - size_t real_r = r & (term->grid->num_rows - 1); - struct row *row = term->grid->rows[real_r]; + start_row &= (grid_rows - 1); + end_row &= (grid_rows - 1); + + for (int r = start_row; r != end_row; r = (r + 1) & (grid_rows - 1)) { + struct row *row = term->grid->rows[r]; xassert(row != NULL); - for (int c = start_col; - c <= (r == end_row ? end_col : term->cols - 1); - c++) - { - if (!cb(term, row, &row->cells[c], real_r, c, data)) + for (int c = start_col; c <= term->cols - 1; c++) { + if (!cb(term, row, &row->cells[c], r, c, data)) return; } start_col = 0; } + + /* Last, partial row */ + struct row *row = term->grid->rows[end_row]; + xassert(row != NULL); + + for (int c = start_col; c <= end_col; c++) { + if (!cb(term, row, &row->cells[c], end_row, c, data)) + return; + } } static void foreach_selected_block( struct terminal *term, struct coord _start, struct coord _end, - bool (*cb)(struct terminal *term, struct row *row, struct cell *cell, int row_no, int col, void *data), + bool (*cb)(struct terminal *term, struct row *row, struct cell *cell, + int row_no, int col, void *data), void *data) { const struct coord *start = &_start; const struct coord *end = &_end; + const int scrollback_start = term->grid->offset + term->rows; + const int grid_rows = term->grid->num_rows; + + /* Start/end rows, relative to the scrollback start */ + const int rel_start_row = + (start->row - scrollback_start + grid_rows) & (grid_rows - 1); + const int rel_end_row = + (end->row - scrollback_start + grid_rows) & (grid_rows - 1); + struct coord top_left = { - .row = min(start->row, end->row), + .row = (rel_start_row < rel_end_row + ? start->row : end->row) & (grid_rows - 1), .col = min(start->col, end->col), }; struct coord bottom_right = { - .row = max(start->row, end->row), + .row = (rel_start_row > rel_end_row + ? start->row : end->row) & (grid_rows - 1), .col = max(start->col, end->col), }; - for (int r = top_left.row; r <= bottom_right.row; r++) { - size_t real_r = r & (term->grid->num_rows - 1); - struct row *row = term->grid->rows[real_r]; + int r = top_left.row; + while (true) { + struct row *row = term->grid->rows[r]; xassert(row != NULL); for (int c = top_left.col; c <= bottom_right.col; c++) { - if (!cb(term, row, &row->cells[c], real_r, c, data)) + if (!cb(term, row, &row->cells[c], r, c, data)) return; } + + if (r == bottom_right.row) + break; + + r++; + r &= grid_rows - 1; } } @@ -1108,16 +1144,8 @@ selection_finalize(struct seat *seat, struct terminal *term, uint32_t serial) xassert(term->selection.start.row != -1); xassert(term->selection.end.row != -1); - if (term->selection.start.row > term->selection.end.row || - (term->selection.start.row == term->selection.end.row && - term->selection.start.col > term->selection.end.col)) - { - struct coord tmp = term->selection.start; - term->selection.start = term->selection.end; - term->selection.end = tmp; - } - - xassert(term->selection.start.row <= term->selection.end.row); + term->selection.start.row &= (term->grid->num_rows - 1); + term->selection.end.row &= (term->grid->num_rows - 1); switch (term->conf->selection_target) { case SELECTION_TARGET_NONE: