selection: update: don't dirty cells that don't change state

Previously when updating a selection, we would unmark *all* cells in
the old selection, and then mark all cells in the new selection.

This caused *all* cells to be dirtied and thus re-rendered.

Avoid this, by adding a temporary state to the cells' selected state.

Before unmarking the old selection, pre-mark the new selection using a
temporary state.

When unmarking the old selection, ignore cells in this temporary state.
When marking the new selection, ignore cells in this temporary
state (except clearing the temporary state).
This commit is contained in:
Daniel Eklöf 2020-01-06 11:56:18 +01:00
parent 6833abf33c
commit 457eb573c4
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
3 changed files with 51 additions and 22 deletions

View file

@ -385,7 +385,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
int x = term->x_margin + col * width;
int y = term->y_margin + row * height;
//bool is_selected = coord_is_selected(term, col, row);
assert(cell->attrs.selected == 0 || cell->attrs.selected == 1);
bool is_selected = cell->attrs.selected;
uint32_t _fg = 0;

View file

@ -47,12 +47,12 @@ selection_on_row_in_view(const struct terminal *term, int row_no)
static void
foreach_selected_normal(
struct terminal *term,
struct terminal *term, struct coord _start, struct coord _end,
void (*cb)(struct terminal *term, struct row *row, struct cell *cell, void *data),
void *data)
{
const struct coord *start = &term->selection.start;
const struct coord *end = &term->selection.end;
const struct coord *start = &_start;
const struct coord *end = &_end;
int start_row, end_row;
int start_col, end_col;
@ -91,12 +91,12 @@ foreach_selected_normal(
static void
foreach_selected_block(
struct terminal *term,
struct terminal *term, struct coord _start, struct coord _end,
void (*cb)(struct terminal *term, struct row *row, struct cell *cell, void *data),
void *data)
{
const struct coord *start = &term->selection.start;
const struct coord *end = &term->selection.end;
const struct coord *start = &_start;
const struct coord *end = &_end;
struct coord top_left = {
.row = min(start->row, end->row),
@ -120,16 +120,16 @@ foreach_selected_block(
static void
foreach_selected(
struct terminal *term,
struct terminal *term, struct coord start, struct coord end,
void (*cb)(struct terminal *term, struct row *row, struct cell *cell, void *data),
void *data)
{
switch (term->selection.kind) {
case SELECTION_NORMAL:
return foreach_selected_normal(term, cb, data);
return foreach_selected_normal(term, start, end, cb, data);
case SELECTION_BLOCK:
return foreach_selected_block(term, cb, data);
return foreach_selected_block(term, start, end, cb, data);
case SELECTION_NONE:
assert(false);
@ -251,7 +251,9 @@ extract_selection(const struct terminal *term)
.size = buf_size,
};
foreach_selected((struct terminal *)term, &extract_one, &ctx);
foreach_selected(
(struct terminal *)term, term->selection.start, term->selection.end,
&extract_one, &ctx);
if (ctx.idx == 0) {
/* Selection of empty cells only */
@ -300,8 +302,10 @@ static void
unmark_selected(struct terminal *term, struct row *row, struct cell *cell,
void *data)
{
if (!cell->attrs.selected)
if (cell->attrs.selected == 0 || (cell->attrs.selected & 2)) {
/* Ignore if already deselected, or if premarked for updated selection */
return;
}
row->dirty = 1;
cell->attrs.selected = 0;
@ -309,11 +313,21 @@ unmark_selected(struct terminal *term, struct row *row, struct cell *cell,
}
static void
mark_selected(struct terminal *term, struct row *row, struct cell *cell,
void *data)
premark_selected(struct terminal *term, struct row *row, struct cell *cell,
void *data)
{
if (cell->attrs.selected)
/* Tell unmark to leave this be */
cell->attrs.selected |= 2;
}
static void
mark_selected(struct terminal *term, struct row *row, struct cell *cell,
void *data)
{
if (cell->attrs.selected & 1) {
cell->attrs.selected = 1; /* Clear the pre-mark bit */
return;
}
row->dirty = 1;
cell->attrs.selected = 1;
@ -334,13 +348,26 @@ selection_update(struct terminal *term, int col, int row)
assert(term->selection.start.row != -1);
assert(term->grid->view + row != -1);
if (term->selection.end.row != -1)
foreach_selected(term, &unmark_selected, NULL);
struct coord new_end = {col, term->grid->view + row};
term->selection.end = (struct coord){col, term->grid->view + row};
/* Premark all cells that *will* be selected */
foreach_selected(
term, term->selection.start, new_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.end = new_end;
assert(term->selection.start.row != -1 && term->selection.end.row != -1);
foreach_selected(term, &mark_selected, NULL);
/* Mark new selection */
foreach_selected(
term, term->selection.start, term->selection.end, &mark_selected, NULL);
render_refresh(term);
}
@ -382,7 +409,9 @@ selection_cancel(struct terminal *term)
term->selection.end.row, term->selection.end.col);
if (term->selection.start.row != -1 && term->selection.end.row != -1) {
foreach_selected(term, &unmark_selected, NULL);
foreach_selected(
term, term->selection.start, term->selection.end,
&unmark_selected, NULL);
render_refresh(term);
}

View file

@ -41,8 +41,8 @@ struct attributes {
uint32_t clean:1;
uint32_t have_fg:1;
uint32_t have_bg:1;
uint32_t selected:1;
uint32_t reserved:4;
uint32_t selected:2;
uint32_t reserved:3;
uint32_t bg:24;
};
static_assert(sizeof(struct attributes) == 8, "bad size");