render: bug: previous cursor wasn't always redrawn correctly

The main problem is knowing:

* The correct *cell* (to be able to render the *content* when erasing
  the old cursor)
* Whether the cursor has moved (to determine whether to stop the
  rendering loop or not)
* Where on the *screen* the cursor is/was (since the terminal may be
  partly scrolled back)

This patch stores three static variables:

* last_cursor is used to compare against current cursor to see if the
  cursor has moved or not
* last_cursor_on_screen is the actual screen coordinates the cursor
  was rendered at (typically the same as last_cursor, but may be
  offset by the view)
* last_cursor_cell is a pointer to the cell to render
This commit is contained in:
Daniel Eklöf 2019-07-24 18:15:24 +02:00
parent bf4847d3e0
commit 10a7b94804
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F

View file

@ -361,14 +361,42 @@ grid_render(struct terminal *term)
gettimeofday(&start_time, NULL); gettimeofday(&start_time, NULL);
#endif #endif
static int last_cursor;
assert(term->width > 0); assert(term->width > 0);
assert(term->height > 0); assert(term->height > 0);
struct buffer *buf = shm_get_buffer(term->wl.shm, term->width, term->height); struct buffer *buf = shm_get_buffer(term->wl.shm, term->width, term->height);
cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE); cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE);
gseq.g = gseq.glyphs;
gseq.count = 0;
static struct coord last_cursor = {0,0};
static struct coord last_cursor_on_screen = {0, 0};
static struct cell *last_cursor_cell = NULL;
bool all_clean = tll_length(term->grid->scroll_damage) == 0;
/* Erase old cursor (if we rendered a cursor last time) */
if (last_cursor_cell != NULL) {
render_cell(
term, buf, last_cursor_cell,
last_cursor_on_screen.col, last_cursor_on_screen.row, false);
wl_surface_damage_buffer(
term->wl.surface,
last_cursor_on_screen.col * term->cell_width,
last_cursor_on_screen.row * term->cell_height,
term->cell_width, term->cell_height);
last_cursor_cell = NULL;
if (last_cursor.col != term->cursor.col ||
last_cursor.row != term->cursor.row) {
/* Detect cursor movement - we don't dirty cells touched
* by the cursor, since only the final cell matters. */
all_clean = false;
}
}
if (term->flash.active) if (term->flash.active)
term_damage_view(term); term_damage_view(term);
@ -405,8 +433,6 @@ grid_render(struct terminal *term)
last_flash = term->flash.active; last_flash = term->flash.active;
} }
bool all_clean = tll_length(term->grid->scroll_damage) == 0;
tll_foreach(term->grid->scroll_damage, it) { tll_foreach(term->grid->scroll_damage, it) {
switch (it->item.type) { switch (it->item.type) {
case DAMAGE_SCROLL: case DAMAGE_SCROLL:
@ -421,9 +447,6 @@ grid_render(struct terminal *term)
tll_remove(term->grid->scroll_damage, it); tll_remove(term->grid->scroll_damage, it);
} }
gseq.g = gseq.glyphs;
gseq.count = 0;
for (int r = 0; r < term->rows; r++) { for (int r = 0; r < term->rows; r++) {
struct row *row = grid_row_in_view(term->grid, r); struct row *row = grid_row_in_view(term->grid, r);
@ -473,34 +496,10 @@ grid_render(struct terminal *term)
} }
} }
/* TODO: break out to function */ /*
/* Re-render last cursor cell and current cursor cell */ * Determine if we need to render a cursor or not. The cursor
/* Make sure previous cursor is refreshed (to avoid "ghost" cursors) */ * could be hidden. Or it could have been scrolled out of view.
int cursor_as_linear */
= (term->grid->offset + term->cursor.row) * term->cols + term->cursor.col;
if (last_cursor != cursor_as_linear) {
int row = last_cursor / term->cols - term->grid->offset;
int col = last_cursor % term->cols;
/* Last cursor cell may have scrolled off screen */
if (row >= 0 && row < term->rows) {
render_cell(term, buf, &grid_row_in_view(term->grid, row)->cells[col], col, row, false);
all_clean = false;
wl_surface_damage_buffer(
term->wl.surface, col * term->cell_width, row * term->cell_height,
term->cell_width, term->cell_height);
}
last_cursor = cursor_as_linear;
}
if (all_clean) {
buf->busy = false;
return;
}
/* Current cursor cell - may be invisible if we've scrolled back */
bool cursor_is_visible = false; bool cursor_is_visible = false;
int view_end = (term->grid->view + term->rows - 1) % term->grid->num_rows; int view_end = (term->grid->view + term->rows - 1) % term->grid->num_rows;
int cursor_row = (term->grid->offset + term->cursor.row) % term->grid->num_rows; int cursor_row = (term->grid->offset + term->cursor.row) % term->grid->num_rows;
@ -515,10 +514,22 @@ grid_render(struct terminal *term)
} }
if (cursor_is_visible && !term->hide_cursor) { if (cursor_is_visible && !term->hide_cursor) {
render_cell( /* Remember cursor coordinates so that we can erase it next
term, buf, * time. Note that we need to re-align it against the view. */
&grid_row_in_view(term->grid, term->cursor.row)->cells[term->cursor.col], last_cursor = term->cursor;
term->cursor.col, term->cursor.row, true); last_cursor_on_screen = (struct coord) {
term->cursor.col,
(cursor_row - term->grid->view + term->grid->num_rows) % term->grid->num_rows,
};
struct row *row = grid_row_in_view(
term->grid, last_cursor_on_screen.row);
last_cursor_cell = &row->cells[term->cursor.col];
render_cell(term, buf, last_cursor_cell,
term->cursor.col,
last_cursor_on_screen.row,
true);
wl_surface_damage_buffer( wl_surface_damage_buffer(
term->wl.surface, term->wl.surface,
@ -527,6 +538,12 @@ grid_render(struct terminal *term)
term->cell_width, term->cell_height); term->cell_width, term->cell_height);
} }
if (all_clean) {
buf->busy = false;
gseq_flush(term, buf);
return;
}
if (gseq.count > 0) if (gseq.count > 0)
gseq_flush(term, buf); gseq_flush(term, buf);