diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/screen.c | 18 | ||||
| -rw-r--r-- | src/snapshot.c | 59 |
2 files changed, 59 insertions, 18 deletions
diff --git a/src/screen.c b/src/screen.c index bd65564..1712faf 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1236,6 +1236,14 @@ static bool term_resize_internal(struct term *t, int cols, int rows) int old_cols = active_scr->cols; int old_rows = active_scr->rows; + struct screen new_scr; + if (!resize_target(t, &new_scr, cols, rows, active_scr)) { + term_gstore_unpin(t->gstore); + return false; + } + + /* Allocate the replacement before scrolling rows into history. A + * failed height shrink must leave the active screen and cursor alone. */ if (rows < old_rows && t->cursor_y >= rows) { int shift = t->cursor_y - rows + 1; for (int i = 0; i < shift; i++) @@ -1243,12 +1251,10 @@ static bool term_resize_internal(struct term *t, int cols, int rows) t->cursor_y -= shift; t->saved.y = t->saved.y >= shift ? t->saved.y - shift : 0; } - - struct screen new_scr; - if (!resize_target(t, &new_scr, cols, rows, active_scr)) { - term_gstore_unpin(t->gstore); - return false; - } + /* Scrolling can advance the screen's history mapping when there is + * no history buffer, so capture its final value after the scroll. */ + new_scr.top_id = active_scr->top_id; + new_scr.top_seg = active_scr->top_seg; int pull = 0; if (rows > old_rows && !t->alternate) diff --git a/src/snapshot.c b/src/snapshot.c index 29ed98c..6578c9a 100644 --- a/src/snapshot.c +++ b/src/snapshot.c @@ -73,6 +73,20 @@ static bool reserve(struct term_snapshot *s, int cols, int rows) return true; } +/* The public contract makes every failed update an empty, retryable snapshot. */ +static void invalidate(struct term_snapshot *s) +{ + s->view = (struct term_view){ .palette = s->palette }; + s->source = 0; + s->arena_len = 0; + s->arena_dead = 0; + if (s->row_cap > 0) { + memset(s->keys, 0, (size_t)s->row_cap * sizeof(*s->keys)); + memset(s->row_clusters, 0, (size_t)s->row_cap * sizeof(*s->row_clusters)); + memset(s->selection, 0, (size_t)s->row_cap * sizeof(*s->selection)); + } +} + /* Attribute contributions of the low and high byte of cell flags. */ static uint16_t lo_attrs[256], hi_attrs[256]; static uint8_t hi_underline[256]; @@ -100,7 +114,8 @@ void term_snapshot_tables_init(void) } } -static uint32_t arena_add(struct term_snapshot *s, const struct gstore *g, uint32_t handle) +static uint32_t arena_add(struct term_snapshot *s, const struct gstore *g, uint32_t handle, + bool *ok) { uint32_t cps[GRAPHEME_MAX_LEN]; int n = term_gstore_expand(g, handle, cps, GRAPHEME_MAX_LEN); @@ -110,8 +125,10 @@ static uint32_t arena_add(struct term_snapshot *s, const struct gstore *g, uint3 uint32_t cap = term_grow_cap32(s->arena_cap ? s->arena_cap * 2 : 256, s->arena_len + (uint32_t)n + 1); uint32_t *a = realloc(s->arena, cap * sizeof(*a)); - if (!a) - return cps[0]; + if (!a) { + *ok = false; + return 0; + } s->arena = a; s->arena_cap = cap; } @@ -125,7 +142,7 @@ static uint32_t arena_add(struct term_snapshot *s, const struct gstore *g, uint3 /* Convert one stored row; returns the number of arena words it added. */ static uint32_t convert_row(struct term_snapshot *s, const struct gstore *g, struct term_cell *dst, - const struct cell *src, int cols) + const struct cell *src, int cols, bool *ok) { uint32_t before = s->arena_len; for (int x = 0; x < cols; x++) { @@ -138,8 +155,11 @@ static uint32_t convert_row(struct term_snapshot *s, const struct gstore *g, uint32_t text = c->c; if (TERM_EXPECT(text < 0x20, 0)) text = text ? ' ' : 0; - else if (TERM_EXPECT(grapheme_is_handle(text), 0)) - text = arena_add(s, g, text); + else if (TERM_EXPECT(grapheme_is_handle(text), 0)) { + text = arena_add(s, g, text, ok); + if (!*ok) + return 0; + } dst[x] = (struct term_cell){ .text = text, .fg = c->fg, @@ -158,8 +178,7 @@ static bool term_snapshot_update_inner(struct term_snapshot *s, struct term *t) return false; int cols = t->cols, rows = t->rows; if (!reserve(s, cols, rows)) { - s->view.size = (struct term_vector2i){ 0 }; - s->source = 0; + invalidate(s); return false; } @@ -175,13 +194,17 @@ static bool term_snapshot_update_inner(struct term_snapshot *s, struct term *t) bool changed = all; term_hist_prepare_rows(&t->hist, cols); - if (!term_hist_view_sync(&t->view, &t->hist, cols)) + if (!term_hist_view_sync(&t->view, &t->hist, cols)) { + invalidate(s); return false; + } term_hist_view_refine(&t->view, &t->hist, cols, rows); uint64_t stack_keys[256]; uint64_t *new_keys = rows <= 256 ? stack_keys : malloc((size_t)rows * sizeof(uint64_t)); - if (!new_keys) + if (!new_keys) { + invalidate(s); return false; + } for (int y = 0; y < rows; y++) new_keys[y] = term_screen_view_row_key(t, y); @@ -230,6 +253,8 @@ static bool term_snapshot_update_inner(struct term_snapshot *s, struct term *t) if (scrolled > 0) { int k = scrolled; + for (int y = 0; y < k; y++) + s->arena_dead += s->row_clusters[y]; memmove(s->cells, s->cells + (size_t)k * cols, (size_t)(rows - k) * cols * sizeof(struct term_cell)); memmove(s->row_clusters, s->row_clusters + k, @@ -247,7 +272,15 @@ static bool term_snapshot_update_inner(struct term_snapshot *s, struct term *t) if (!all) s->arena_dead += s->row_clusters[y]; const struct cell *src = term_screen_view_row(t, y); - s->row_clusters[y] = convert_row(s, t->gstore, s->cells + (size_t)y * cols, src, cols); + bool converted = true; + s->row_clusters[y] = convert_row(s, t->gstore, s->cells + (size_t)y * cols, + src, cols, &converted); + if (!converted) { + if (new_keys != stack_keys) + free(new_keys); + invalidate(s); + return false; + } s->keys[y] = key; changed = true; } @@ -256,8 +289,10 @@ static bool term_snapshot_update_inner(struct term_snapshot *s, struct term *t) int stack[2][256]; int *c0 = rows <= 256 ? stack[0] : malloc((size_t)rows * 2 * sizeof(int)); - if (!c0) + if (!c0) { + invalidate(s); return false; + } int *c1 = rows <= 256 ? stack[1] : c0 + rows; term_selection_view_spans(t, c0, c1); for (int y = 0; y < rows; y++) { |
