interactive resize: improve user experience

Re-initialize the temporary ‘normal’ grid instance each time we
receive a configure event while doing an interactive resize.

This way, window content will not be "erased" when the window is first
made smaller, then larger again.

And, if the viewport is up in the scrollback history, increasing the
window size will reveal more of the scrollback, instead of just being
black.

The last issue is the cursor; it’s currently not "stuck" where it
should be. Instead, it follows the window around. This is due to two
things:

1) the temporary grid we create is large enough to contain the current
   viewport, but not more than that. That means we can’t "scroll up", to
   hide the cursor.

2) grid_resize_without_reflow() doesn’t know anything about
   "interactive resizing". As such, it will ensure the cursor is bound
   to the new grid dimensions.

I don’t yet have a solution for this. This patch implements a
workaround to at least reduce the impact, by simply hiding the cursor
while we’re doing an interactive resize.
This commit is contained in:
Daniel Eklöf 2022-10-17 18:49:57 +02:00
parent 3c9a51afa6
commit 0ac0d0647a
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 83 additions and 60 deletions

141
render.c
View file

@ -3691,10 +3691,13 @@ delayed_reflow_of_normal_grid(struct terminal *term)
term->normal = *term->interactive_resizing.grid;
free(term->interactive_resizing.grid);
term->hide_cursor = term->interactive_resizing.old_hide_cursor;
/* Reset */
term->interactive_resizing.grid = NULL;
term->interactive_resizing.old_screen_rows = 0;
term->interactive_resizing.new_rows = 0;
term->interactive_resizing.old_hide_cursor = false;
/* Invalidate render pointers */
shm_unref(term->render.last_buf);
@ -3900,62 +3903,6 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
const uint32_t scrollback_lines = term->render.scrollback_lines;
/*
* Since text reflow is slow, dont do it *while* resizing. Only
* do it when done, or after pausing the resize for sufficiently
* long. We re-use the TIOCSWINSZ timer to handle this. See
* send_dimensions_to_client() and fdm_tiocswinsz().
*
* To be able to do the final reflow correctly, we need a copy of
* the original grid, before the resize started.
*/
if (term->window->is_resizing && term->interactive_resizing.grid == NULL) {
term_ptmx_pause(term);
/* Stash the current normal grid, as-is, to be used when
* doing the final reflow */
term->interactive_resizing.old_screen_rows = term->rows;
term->interactive_resizing.grid = xmalloc(sizeof(*term->interactive_resizing.grid));
*term->interactive_resizing.grid = term->normal;
/*
* Copy the current viewport to a new grid that will be used
* during the resize. For now, throw away sixels and OSC-8
* URLs. Theyll be "restored" when we do the final reflow.
*
* We use the alt screens row count, since we dont want to
* instantiate an unnecessarily large grid.
*
* TODO:
* - sixels?
* - OSC-8?
*/
xassert(1 << (32 - __builtin_clz(term->rows)) == term->alt.num_rows);
struct grid g = {
.num_rows = term->alt.num_rows,
.num_cols = term->cols,
.offset = 0,
.view = 0,
.cursor = term->normal.cursor,
.saved_cursor = term->normal.saved_cursor,
.rows = xcalloc(g.num_rows, sizeof(g.rows[0])),
.cur_row = NULL,
.scroll_damage = tll_init(),
.sixel_images = tll_init(),
.kitty_kbd = term->normal.kitty_kbd,
};
for (size_t i = 0, j = term->normal.view; i < term->rows;
i++, j = (j + 1) & (term->normal.num_rows - 1))
{
g.rows[i] = grid_row_alloc(term->cols, false);
memcpy(g.rows[i]->cells, term->normal.rows[j]->cells,
term->cols * sizeof(g.rows[i]->cells[0]));
}
term->normal = g;
}
/* Screen rows/cols before resize */
int old_cols = term->cols;
int old_rows = term->rows;
@ -3992,14 +3939,84 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
xassert(term->margins.top >= pad_y);
xassert(term->margins.bottom >= pad_y);
if (new_cols == old_cols && new_rows == old_rows &&
(term->interactive_resizing.grid == NULL || term->window->is_resizing))
{
if (new_cols == old_cols && new_rows == old_rows) {
LOG_DBG("grid layout unaffected; skipping reflow");
term->interactive_resizing.new_rows = new_normal_grid_rows;
goto damage_view;
}
/*
* Since text reflow is slow, dont do it *while* resizing. Only
* do it when done, or after pausing the resize for sufficiently
* long. We re-use the TIOCSWINSZ timer to handle this. See
* send_dimensions_to_client() and fdm_tiocswinsz().
*
* To be able to do the final reflow correctly, we need a copy of
* the original grid, before the resize started.
*/
if (term->window->is_resizing) {
if (term->interactive_resizing.grid == NULL) {
term_ptmx_pause(term);
/* Stash the current normal grid, as-is, to be used when
* doing the final reflow */
term->interactive_resizing.old_screen_rows = term->rows;
term->interactive_resizing.old_cols = term->cols;
term->interactive_resizing.old_hide_cursor = term->hide_cursor;
term->interactive_resizing.grid = xmalloc(sizeof(*term->interactive_resizing.grid));
*term->interactive_resizing.grid = term->normal;
} else {
/* Well replace the current temporary grid, with a new
* one (again based on the original grid) */
grid_free(&term->normal);
}
struct grid *orig = term->interactive_resizing.grid;
/*
* Copy the current viewport (of the original grid) to a new
* grid that will be used during the resize. For now, throw
* away sixels and OSC-8 URLs. Theyll be "restored" when we
* do the final reflow.
*
* Note that OSC-8 URLs are perfectly ok to throw away; they
* cannot be interacted with during the resize. And, even if
* url.osc8-underline=always, the underline attribute is
* part of the cell, not the URI struct (and thus our faked
* grid will still render OSC-8 links underlined).
*
* TODO:
* - sixels?
*/
struct grid g = {
.num_rows = 1 << (32 - __builtin_clz(term->interactive_resizing.old_screen_rows)),
.num_cols = term->interactive_resizing.old_cols,
.offset = 0,
.view = 0,
.cursor = orig->cursor,
.saved_cursor = orig->saved_cursor,
.rows = xcalloc(g.num_rows, sizeof(g.rows[0])),
.cur_row = NULL,
.scroll_damage = tll_init(),
.sixel_images = tll_init(),
.kitty_kbd = orig->kitty_kbd,
};
for (size_t i = 0, j = orig->view;
i < term->interactive_resizing.old_screen_rows;
i++, j = (j + 1) & (orig->num_rows - 1))
{
g.rows[i] = grid_row_alloc(g.num_cols, false);
memcpy(g.rows[i]->cells,
orig->rows[j]->cells,
g.num_cols * sizeof(g.rows[i]->cells[0]));
}
term->normal = g;
term->hide_cursor = true;
}
if (term->grid == &term->alt)
selection_cancel(term);
else {
@ -4028,7 +4045,8 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
term->interactive_resizing.new_rows = new_normal_grid_rows;
grid_resize_without_reflow(
&term->normal, new_alt_grid_rows, new_cols, old_rows, new_rows);
&term->normal, new_alt_grid_rows, new_cols,
term->interactive_resizing.old_screen_rows, new_rows);
} else {
/* Full text reflow */
@ -4040,11 +4058,14 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
term->normal = *term->interactive_resizing.grid;
free(term->interactive_resizing.grid);
term->hide_cursor = term->interactive_resizing.old_hide_cursor;
old_rows = term->interactive_resizing.old_screen_rows;
term->interactive_resizing.grid = NULL;
term->interactive_resizing.old_screen_rows = 0;
term->interactive_resizing.new_rows = 0;
term->interactive_resizing.old_hide_cursor = false;
term_ptmx_resume(term);
}

View file

@ -601,6 +601,8 @@ struct terminal {
struct {
struct grid *grid; /* Original normal grid, before resize started */
int old_screen_rows; /* term->rows before resize started */
int old_cols; /* term->cols before resize started */
int old_hide_cursor; /* term->hide_cursor before resize started */
int new_rows; /* New number of scrollback rows */
} interactive_resizing;