From bb74fe3f7de2b2e933eee9b0d36a811efabedb2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 22 Feb 2021 10:20:52 +0100 Subject: [PATCH 1/9] grid: add grid_free() --- grid.c | 14 ++++++++++++++ grid.h | 2 ++ terminal.c | 16 ++-------------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/grid.c b/grid.c index 21cad71f..771cf164 100644 --- a/grid.c +++ b/grid.c @@ -11,6 +11,20 @@ #include "util.h" #include "xmalloc.h" +void +grid_free(struct grid *grid) +{ + for (int r = 0; r < grid->num_rows; r++) + grid_row_free(grid->rows[r]); + + tll_foreach(grid->sixel_images, it) { + sixel_destroy(&it->item); + tll_remove(grid->sixel_images, it); + } + + free(grid->rows); +} + void grid_swap_row(struct grid *grid, int row_a, int row_b) { diff --git a/grid.h b/grid.h index 4cb339f2..c411b602 100644 --- a/grid.h +++ b/grid.h @@ -4,6 +4,8 @@ #include "debug.h" #include "terminal.h" +void grid_free(struct grid *grid); + void grid_swap_row(struct grid *grid, int row_a, int row_b); struct row *grid_row_alloc(int cols, bool initialize); void grid_row_free(struct row *row); diff --git a/terminal.c b/terminal.c index 18d5a947..57c9ae18 100644 --- a/terminal.c +++ b/terminal.c @@ -1437,12 +1437,8 @@ term_destroy(struct terminal *term) mtx_unlock(&term->render.workers.lock); free(term->vt.osc.data); - for (int row = 0; row < term->normal.num_rows; row++) - grid_row_free(term->normal.rows[row]); - free(term->normal.rows); - for (int row = 0; row < term->alt.num_rows; row++) - grid_row_free(term->alt.rows[row]); - free(term->alt.rows); + grid_free(&term->normal); + grid_free(&term->alt); tll_free(term->normal.scroll_damage); tll_free(term->alt.scroll_damage); @@ -1490,14 +1486,6 @@ term_destroy(struct terminal *term) tll_remove(term->ptmx_paste_buffers, it); } - tll_foreach(term->normal.sixel_images, it) { - sixel_destroy(&it->item); - tll_remove(term->normal.sixel_images, it); - } - tll_foreach(term->alt.sixel_images, it) { - sixel_destroy(&it->item); - tll_remove(term->alt.sixel_images, it); - } sixel_fini(term); urls_reset(term); From ae3ec52507c85395f90bf7c0ecc5b79affdc9b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 22 Feb 2021 10:21:58 +0100 Subject: [PATCH 2/9] grid: add grid_snapshot() This function deep copies a grid into a newly *allocated* grid struct. --- grid.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ grid.h | 1 + 2 files changed, 83 insertions(+) diff --git a/grid.c b/grid.c index 771cf164..70f288f5 100644 --- a/grid.c +++ b/grid.c @@ -8,9 +8,91 @@ #include "debug.h" #include "macros.h" #include "sixel.h" +#include "stride.h" #include "util.h" #include "xmalloc.h" +struct grid * +grid_snapshot(const struct grid *grid) +{ + struct grid *clone = xmalloc(sizeof(*clone)); + clone->num_rows = grid->num_rows; + clone->num_cols = grid->num_cols; + clone->offset = grid->offset; + clone->view = grid->view; + clone->cursor = grid->cursor; + clone->rows = xcalloc(grid->num_rows, sizeof(clone->rows[0])); + memset(&clone->scroll_damage, 0, sizeof(clone->scroll_damage)); + memset(&clone->sixel_images, 0, sizeof(clone->sixel_images)); + + for (int r = 0; r < grid->num_rows; r++) { + const struct row *row = grid->rows[r]; + + if (row == NULL) + continue; + + struct row *clone_row = xmalloc(sizeof(*row)); + clone->rows[r] = clone_row; + + clone_row->cells = xmalloc(grid->num_cols * sizeof(clone_row->cells[0])); + clone_row->linebreak = row->linebreak; + clone_row->dirty = row->dirty;; + + for (int c = 0; c < grid->num_cols; c++) { + clone_row->cells[c] = row->cells[c]; + clone_row->cells[c].attrs.clean = 0; + } + + if (row->extra != NULL) { + const struct row_data *extra = row->extra; + struct row_data *new_extra = xcalloc(1, sizeof(*new_extra)); + + tll_foreach(extra->uri_ranges, it) { + struct row_uri_range range = { + .start = it->item.start, + .end = it->item.end, + .id = it->item.id, + .uri = xstrdup(it->item.uri), + }; + + tll_push_back(new_extra->uri_ranges, range); + } + + clone_row->extra = new_extra; + } else + clone_row->extra = NULL; + } + + tll_foreach(grid->sixel_images, it) { + int width = it->item.width; + int height = it->item.height; + pixman_image_t *pix = it->item.pix; + pixman_format_code_t pix_fmt = pixman_image_get_format(pix); + int stride = stride_for_format_and_width(pix_fmt, width); + + size_t size = stride * height; + void *new_data = xmalloc(size); + memcpy(new_data, it->item.data, size); + + pixman_image_t *new_pix = pixman_image_create_bits_no_clear( + pix_fmt, width, height, new_data, stride); + + struct sixel six = { + .data = new_data, + .pix = new_pix, + .width = width, + .height = height, + .rows = it->item.rows, + .cols = it->item.cols, + .pos = it->item.pos, + }; + + tll_push_back(clone->sixel_images, six); + } + + return clone; +} + void grid_free(struct grid *grid) { diff --git a/grid.h b/grid.h index c411b602..1131ac46 100644 --- a/grid.h +++ b/grid.h @@ -4,6 +4,7 @@ #include "debug.h" #include "terminal.h" +struct grid *grid_snapshot(const struct grid *grid); void grid_free(struct grid *grid); void grid_swap_row(struct grid *grid, int row_a, int row_b); From 54b5ae95c1bb01c5103b3ce97a8faebe75e142bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 22 Feb 2021 10:22:41 +0100 Subject: [PATCH 3/9] url-mode: snapshot screen state when entering URL mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, we automatically exited URL mode whenever we received data on the PTY. This was done since we don’t know _what_ has changed on the screen, and we don’t want to display misleading jump labels. However, this becomes a problem in curses-like applications that periodically updates part of the screen. For example, a statusbar with a clock. This patch changes this behavior; instead of cancelling URL mode when receiving PTY data, we snapshot the grid when entering URL mode. When *rendering*, we use the snapshot:ed grid, while PTY updates modify the “real” grid. Snapshot:ing the grid means taking a full/deep copy of the current grid, including sixel images etc. Finally, it isn’t necessary to “damage” the entire view when *entering* URL mode, since we’re at that point the renderer is in sync with the grid. But we *do* need to damage the entire view when exiting URL mode, since the grid changes on the “real” grid hasn’t been tracked by the renderer. --- render.c | 16 ++++++++++++++++ terminal.c | 2 -- terminal.h | 2 ++ url-mode.c | 14 +++++++++++++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/render.c b/render.c index 1db813bc..aa172284 100644 --- a/render.c +++ b/render.c @@ -2675,6 +2675,12 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da term->render.pending.title = false; term->render.pending.urls = false; + struct grid *original_grid = term->grid; + if (urls_mode_is_active(term)) { + xassert(term->url_grid_snapshot != NULL); + term->grid = term->url_grid_snapshot; + } + if (csd && term->window->use_csd == CSD_YES) { quirk_weston_csd_on(term); render_csd(term); @@ -2697,6 +2703,8 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da if (it->item.kbd_focus == term) ime_update_cursor_rect(&it->item, term); } + + term->grid = original_grid; } static void @@ -3127,6 +3135,12 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data) term->render.refresh.urls = false; if (term->window->frame_callback == NULL) { + struct grid *original_grid = term->grid; + if (urls_mode_is_active(term)) { + xassert(term->url_grid_snapshot != NULL); + term->grid = term->url_grid_snapshot; + } + if (csd && term->window->use_csd == CSD_YES) { quirk_weston_csd_on(term); render_csd(term); @@ -3145,6 +3159,8 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data) if (it->item.kbd_focus == term) ime_update_cursor_rect(&it->item, term); } + + term->grid = original_grid; } else { /* Tells the frame callback to render again */ term->render.pending.grid |= grid; diff --git a/terminal.c b/terminal.c index 57c9ae18..ac127851 100644 --- a/terminal.c +++ b/terminal.c @@ -227,8 +227,6 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) cursor_blink_rearm_timer(term); } - urls_reset(term); - uint8_t buf[24 * 1024]; ssize_t count = sizeof(buf); diff --git a/terminal.h b/terminal.h index b1b7c391..2a6ae0b1 100644 --- a/terminal.h +++ b/terminal.h @@ -547,9 +547,11 @@ struct terminal { unsigned max_height; /* Maximum image height, in pixels */ } sixel; + /* TODO: wrap in a struct */ url_list_t urls; wchar_t url_keys[5]; bool urls_show_uri_on_jump_label; + struct grid *url_grid_snapshot; #if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED struct { diff --git a/url-mode.c b/url-mode.c index cea099e9..7fb60845 100644 --- a/url-mode.c +++ b/url-mode.c @@ -637,6 +637,9 @@ urls_render(struct terminal *term) tag_cells_for_url(term, &it->item, true); } + /* Snapshot the current grid */ + term->url_grid_snapshot = grid_snapshot(term->grid); + render_refresh_urls(term); render_refresh(term); } @@ -651,8 +654,15 @@ url_destroy(struct url *url) void urls_reset(struct terminal *term) { - if (likely(tll_length(term->urls) == 0)) + if (likely(tll_length(term->urls) == 0)) { + xassert(term->url_grid_snapshot == NULL); return; + } + + grid_free(term->url_grid_snapshot); + free(term->url_grid_snapshot); + term->url_grid_snapshot = NULL; + term->render.last_cursor.row = NULL; if (term->window != NULL) { tll_foreach(term->window->urls, it) { @@ -669,5 +679,7 @@ urls_reset(struct terminal *term) term->urls_show_uri_on_jump_label = false; memset(term->url_keys, 0, sizeof(term->url_keys)); + + term_damage_view(term); render_refresh(term); } From 2cb624ee43889e66632776496b02d39fbc7d4d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 22 Feb 2021 10:31:40 +0100 Subject: [PATCH 4/9] grid: grid_free(): free scroll damage list --- grid.c | 1 + terminal.c | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/grid.c b/grid.c index 70f288f5..6ea49352 100644 --- a/grid.c +++ b/grid.c @@ -105,6 +105,7 @@ grid_free(struct grid *grid) } free(grid->rows); + tll_free(grid->scroll_damage); } void diff --git a/terminal.c b/terminal.c index ac127851..dc210fbc 100644 --- a/terminal.c +++ b/terminal.c @@ -1438,9 +1438,6 @@ term_destroy(struct terminal *term) grid_free(&term->normal); grid_free(&term->alt); - tll_free(term->normal.scroll_damage); - tll_free(term->alt.scroll_damage); - free(term->composed); free(term->window_title); From a0f021b7dbf6b947d63087593610e1a53e13dbee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 22 Feb 2021 10:31:56 +0100 Subject: [PATCH 5/9] grid: snapshot: copy scroll damage --- grid.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grid.c b/grid.c index 6ea49352..f1391bfa 100644 --- a/grid.c +++ b/grid.c @@ -25,6 +25,9 @@ grid_snapshot(const struct grid *grid) memset(&clone->scroll_damage, 0, sizeof(clone->scroll_damage)); memset(&clone->sixel_images, 0, sizeof(clone->sixel_images)); + tll_foreach(grid->scroll_damage, it) + tll_push_back(clone->scroll_damage, it->item); + for (int r = 0; r < grid->num_rows; r++) { const struct row *row = grid->rows[r]; From aa10cd54eabf592d5ad79a972009887495841652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 23 Feb 2021 09:16:13 +0100 Subject: [PATCH 6/9] url-mode: no need to damage the entire view when exiting URL mode The renderer will simply apply *all* accumulated damage at once, when we render the real grid. --- url-mode.c | 1 - 1 file changed, 1 deletion(-) diff --git a/url-mode.c b/url-mode.c index 7fb60845..8b1b9795 100644 --- a/url-mode.c +++ b/url-mode.c @@ -680,6 +680,5 @@ urls_reset(struct terminal *term) term->urls_show_uri_on_jump_label = false; memset(term->url_keys, 0, sizeof(term->url_keys)); - term_damage_view(term); render_refresh(term); } From bc71e06d5f5b9f27aca373e523627c70670e979c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 23 Feb 2021 09:24:50 +0100 Subject: [PATCH 7/9] =?UTF-8?q?url-mode:=20dirty=20the=20=E2=80=9Clast=20c?= =?UTF-8?q?ursor=E2=80=9D=20cell=20before=20taking=20the=20snapshot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ensures the “last cursor” cell is re-drawn (without a cursor, if the cursor has moved), both in the snapshot:ed grid, and later, when we switch back to the real grid. We must also be careful and reset term->render.last_cursor.row both when *entering* and *leaving* URL mode, to ensure it doesn’t point to an invalid row. --- url-mode.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/url-mode.c b/url-mode.c index 8b1b9795..54edc524 100644 --- a/url-mode.c +++ b/url-mode.c @@ -637,6 +637,17 @@ urls_render(struct terminal *term) tag_cells_for_url(term, &it->item, true); } + /* Dirty the last cursor, to ensure it is erased */ + { + struct row *cursor_row = term->render.last_cursor.row; + if (cursor_row != NULL) { + struct cell *cell = &cursor_row->cells[term->render.last_cursor.col]; + cell->attrs.clean = 0; + cursor_row->dirty = true; + } + } + term->render.last_cursor.row = NULL; + /* Snapshot the current grid */ term->url_grid_snapshot = grid_snapshot(term->grid); @@ -662,6 +673,15 @@ urls_reset(struct terminal *term) grid_free(term->url_grid_snapshot); free(term->url_grid_snapshot); term->url_grid_snapshot = NULL; + + /* + * Make sure “last cursor” doesn’t point to a row in the just + * free:d snapshot grid. + * + * Note that it will still be erased properly (if hasn’t already), + * since we marked the cell as dirty *before* taking the grid + * snapshot. + */ term->render.last_cursor.row = NULL; if (term->window != NULL) { From 3c123425fbdf530c697096093268919e58221fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 23 Feb 2021 09:28:43 +0100 Subject: [PATCH 8/9] =?UTF-8?q?command:=20scrollback:=20don=E2=80=99t=20al?= =?UTF-8?q?low=20scrolling=20in=20URL=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- commands.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/commands.c b/commands.c index 01ad4e1e..e7dafdfc 100644 --- a/commands.c +++ b/commands.c @@ -7,6 +7,7 @@ #include "render.h" #include "selection.h" #include "terminal.h" +#include "url-mode.h" #include "util.h" void @@ -14,6 +15,8 @@ cmd_scrollback_up(struct terminal *term, int rows) { if (term->grid == &term->alt) return; + if (urls_mode_is_active(term)) + return; if (term->mouse_tracking != MOUSE_NONE) return; @@ -92,6 +95,8 @@ cmd_scrollback_down(struct terminal *term, int rows) { if (term->grid == &term->alt) return; + if (urls_mode_is_active(term)) + return; if (term->mouse_tracking != MOUSE_NONE) return; From ae0f20a53603c3e3657cbab5c548575d4d1147bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 24 Feb 2021 21:39:29 +0100 Subject: [PATCH 9/9] url-mode: damage current view before entering URL mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clear scroll damage and damage the entire viewport before entering URL mode. This will cause us to do a full screen redraw both when entering URL mode, and later when exiting it. Clearing the scroll damage is necessary to ensure we don’t apply it twice (once for the snapshot:ed grid, and later again for the real grid), as that would result in an incorrect pixmap. But, since we’ve cleared the scroll damage, we need to damage the entire view to ensure we redraw the contents correctly. --- url-mode.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/url-mode.c b/url-mode.c index 54edc524..791b4ce0 100644 --- a/url-mode.c +++ b/url-mode.c @@ -648,6 +648,14 @@ urls_render(struct terminal *term) } term->render.last_cursor.row = NULL; + /* Clear scroll damage, to ensure we don’t apply it twice (once on + * the snapshot:ed grid, and then later again on the real grid) */ + tll_free(term->grid->scroll_damage); + + /* Damage the entire view, to ensure a full screen redraw, both + * now, when entering URL mode, and later, when exiting it. */ + term_damage_view(term); + /* Snapshot the current grid */ term->url_grid_snapshot = grid_snapshot(term->grid);