From 5f0ceb72f176caf7673ea1d4bc5195f26f4b4df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 16 Jul 2021 16:45:36 +0200 Subject: [PATCH] csi: erase scrollback: cancel selection if it touches the scrollback This breaks out the scrollback erasing logic for \E[3J from csi.c, and moves it to the new function term_erase_scrollback(), and changes the logic to calculate the start and end row (absolute) numbers of the scrollback, and only iterate those, instead of iterating *all* rows, filtering out those that are on-screen. It also adds an intersection range check of the selection range, and cancels the selection if it touches any of the deleted scrollback rows. This fixes a crash when trying to render the next frame, since the selection now references rows that have been freed. Closes #633 --- csi.c | 21 +----------------- selection.c | 4 +--- terminal.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++ terminal.h | 1 + 4 files changed, 66 insertions(+), 23 deletions(-) diff --git a/csi.c b/csi.c index 80ee75cd..dc5a018a 100644 --- a/csi.c +++ b/csi.c @@ -917,26 +917,7 @@ csi_dispatch(struct terminal *term, uint8_t final) case 3: { /* Erase scrollback */ - int end = (term->grid->offset + term->rows - 1) % term->grid->num_rows; - for (size_t i = 0; i < term->grid->num_rows; i++) { - if (end >= term->grid->offset) { - /* Not wrapped */ - if (i >= term->grid->offset && i <= end) - continue; - } else { - /* Wrapped */ - if (i >= term->grid->offset || i <= end) - continue; - } - - if (term->render.last_cursor.row == term->grid->rows[i]) - term->render.last_cursor.row = NULL; - - grid_row_free(term->grid->rows[i]); - term->grid->rows[i] = NULL; - } - term->grid->view = term->grid->offset; - term_damage_view(term); + term_erase_scrollback(term); break; } diff --git a/selection.c b/selection.c index 57ab8bf6..537614f6 100644 --- a/selection.c +++ b/selection.c @@ -69,10 +69,8 @@ selection_on_rows(const struct terminal *term, int row_start, int row_end) end = tmp; } - if (row_start >= start->row && row_end <= end->row) { - LOG_INFO("ON ROWS"); + if (row_start >= start->row && row_end <= end->row) return true; - } return false; } diff --git a/terminal.c b/terminal.c index 72ab1f2b..df6ddbed 100644 --- a/terminal.c +++ b/terminal.c @@ -2011,6 +2011,69 @@ term_erase(struct terminal *term, const struct coord *start, const struct coord sixel_overwrite_by_row(term, end->row, 0, end->col + 1); } +void +term_erase_scrollback(struct terminal *term) +{ + const int mask = term->grid->num_rows - 1; + const int start = (term->grid->offset + term->rows) & mask; + const int end = (term->grid->offset - 1) & mask; + const int sel_start = term->selection.start.row; + const int sel_end = term->selection.end.row; + + if (sel_end >= 0) { + /* + * Cancel selection if it touches any of the rows in the + * scrollback, since we can’t have the selection reference + * soon-to-be deleted rows. + * + * This is done by range checking the selection range against + * the scrollback range. + * + * To make this comparison simpler, the start/end absolute row + * numbers are “rebased” against the scrollback start, where + * row 0 is the *first* row in the scrollback. A high number + * thus means the row is further *down* in the scrollback, + * closer to the screen bottom. + */ + int scrollback_start = term->grid->offset + term->rows; + + int rel_sel_start = sel_start - scrollback_start + term->grid->num_rows; + int rel_sel_end = sel_end - scrollback_start + term->grid->num_rows; + + int rel_start = start - scrollback_start + term->grid->num_rows; + int rel_end = end - scrollback_start + term->grid->num_rows; + + rel_sel_start &= mask; + rel_sel_end &= mask; + rel_start &= mask; + rel_end &= mask; + + if ((rel_sel_start <= rel_start && rel_sel_end >= rel_start) || + (rel_sel_start <= rel_end && rel_sel_end >= rel_end) || + (rel_sel_start >= rel_start && rel_sel_end <= rel_end)) + { + selection_cancel(term); + } + } + + for (int i = start;; i = (i + 1) & mask) { + struct row *row = term->grid->rows[i]; + if (row != NULL) { + if (term->render.last_cursor.row == row) + term->render.last_cursor.row = NULL; + + grid_row_free(row); + term->grid->rows[i] = NULL; + } + + if (i == end) + break; + } + + term->grid->view = term->grid->offset; + term_damage_view(term); +} + int term_row_rel_to_abs(const struct terminal *term, int row) { diff --git a/terminal.h b/terminal.h index b02036a2..d699d644 100644 --- a/terminal.h +++ b/terminal.h @@ -662,6 +662,7 @@ void term_damage_scroll( void term_erase( struct terminal *term, const struct coord *start, const struct coord *end); +void term_erase_scrollback(struct terminal *term); int term_row_rel_to_abs(const struct terminal *term, int row); void term_cursor_home(struct terminal *term);