term: scrolling: hopefully fix all selection/scrolling related crashes

When scrolling, there are a couple of cases where an existing
selection must be canceled because we cannot meaningfully represent it
after scrolling.

These are when the selection is (partly) inside:

* The top scrolling region
* The bottom scrolling region
* The new lines scrolled in. I.e. re-used lines

For the scrolling regions, the real problem is when the selection
crosses the scrolling region boundary; a selection that is completely
inside a scrolling regions _might_ be possible to keep, but we would
need to translate the selection coordinates to the new scrolling
region lines.

For simplicity, we cancel the selection if it touches the scrolling
region. Period.

The last item, newly scrolled in lines is when the selection covers
very old lines and we're now wrapping around the scrollback history.

Then there's a fourth problem case: when the user has started a
selection, but hasn't yet moved the cursor. In this case, we have no
end point.

What's more problematic is that when the user (after scrolling) moves
the cursor, we try to create a huge selection that covers mostly
empty (NULL) rows, causing us to crash.

This can happen e.g. when reverse scrolling in such a way that we wrap
around the scrollback history.

The actual viewport in this case is something like `-n - m`. But the
selection we'll end up trying to create will be `m - (rows - n)`. This
range may very well contain NULL rows.

To deal with this, we simply cancel the selection.
This commit is contained in:
Daniel Eklöf 2020-05-17 15:34:49 +02:00
parent 5d643e63fe
commit 96a4f1b993
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
4 changed files with 89 additions and 17 deletions

View file

@ -30,31 +30,39 @@ selection_enabled(const struct terminal *term)
}
bool
selection_on_rows_in_view(const struct terminal *term, int row_start, int row_end)
selection_on_rows(const struct terminal *term, int row_start, int row_end)
{
LOG_DBG("selection: %d-%d, range: %d-%d (view=%d)",
LOG_DBG("on rows: %d-%d, range: %d-%d (offset=%d)",
term->selection.start.row, term->selection.end.row,
row_start, row_end, term->grid->view);
row_start, row_end, term->grid->offset);
if (term->selection.start.row == -1 || term->selection.end.row == -1)
if (term->selection.end.row == -1)
return false;
assert(term->selection.start.row != -1);
row_start += term->grid->offset;
row_end += term->grid->offset;
const struct coord *start = &term->selection.start;
const struct coord *end = &term->selection.end;
if ((row_start <= start->row && row_end >= start->row) ||
(row_start <= end->row && row_end >= end->row))
{
/* The range crosses one of the selection boundaries */
return true;
}
/* For the last check we must ensure start <= end */
if (start->row > end->row) {
const struct coord *tmp = start;
start = end;
end = tmp;
}
row_start += term->grid->view;
row_end += term->grid->view;
if ((row_start <= start->row && row_end >= start->row) ||
(row_start <= end->row && row_end >= end->row) ||
(row_start >= start->row && row_end <= end->row))
{
if (row_start >= start->row && row_end <= end->row) {
LOG_INFO("ON ROWS");
return true;
}