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; diff --git a/grid.c b/grid.c index 21cad71f..f1391bfa 100644 --- a/grid.c +++ b/grid.c @@ -8,9 +8,109 @@ #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)); + + 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]; + + 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) +{ + 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); + tll_free(grid->scroll_damage); +} + void grid_swap_row(struct grid *grid, int row_a, int row_b) { diff --git a/grid.h b/grid.h index 4cb339f2..1131ac46 100644 --- a/grid.h +++ b/grid.h @@ -4,6 +4,9 @@ #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); struct row *grid_row_alloc(int cols, bool initialize); void grid_row_free(struct row *row); 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 18d5a947..dc210fbc 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); @@ -1437,15 +1435,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); - - tll_free(term->normal.scroll_damage); - tll_free(term->alt.scroll_damage); + grid_free(&term->normal); + grid_free(&term->alt); free(term->composed); @@ -1490,14 +1481,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); 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..791b4ce0 100644 --- a/url-mode.c +++ b/url-mode.c @@ -637,6 +637,28 @@ 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; + + /* 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); + render_refresh_urls(term); render_refresh(term); } @@ -651,8 +673,24 @@ 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; + + /* + * 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) { tll_foreach(term->window->urls, it) { @@ -669,5 +707,6 @@ urls_reset(struct terminal *term) term->urls_show_uri_on_jump_label = false; memset(term->url_keys, 0, sizeof(term->url_keys)); + render_refresh(term); }