From 0ac0d0647ae996bcef119d885cbd2d22a9940d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 17 Oct 2022 18:49:57 +0200 Subject: [PATCH] interactive resize: improve user experience MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- render.c | 141 ++++++++++++++++++++++++++++++----------------------- terminal.h | 2 + 2 files changed, 83 insertions(+), 60 deletions(-) diff --git a/render.c b/render.c index d50606cb..19ab5ef8 100644 --- a/render.c +++ b/render.c @@ -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, don’t 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. They’ll be "restored" when we do the final reflow. - * - * We use the ‘alt’ screen’s row count, since we don’t 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, don’t 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 { + /* We’ll 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. They’ll 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); } diff --git a/terminal.h b/terminal.h index d5ed1ba2..ec5560cd 100644 --- a/terminal.h +++ b/terminal.h @@ -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;