diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 14:27:58 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 14:27:58 +0300 |
| commit | 5ab6b560e8528c27ba934b90f94b1828b7771e27 (patch) | |
| tree | 409c2a2e864529ccda0ff3e3b75b0280279e2a33 /src/core.c | |
| parent | 7633ab2c60b40bd5aa67ef391b2a0d149685742f (diff) | |
Encapsulate stream queue context and consolidate forwarding wrappers
- Create struct stream_emit_ctx packing stream queue arguments into 1 cache line
- Reduce emit_stream_bulk_wrapped from 13 parameters to 3
- Reduce flush_stream_queue from 10 parameters to 1
- Extract stream_queue_finish to eliminate duplicated cleanup in kernel instantiations
- Replace EMIT_STREAM_ROW macros with typed inline functions
- Move pending_wrap clearing directly into term_screen_erase, insert, delete, and scroll
- Remove sc_erase_display, sc_erase_line, sc_insert_chars, and sc_delete_chars wrappers
- Consolidate term_row and remove duplicated row_fill_gap helper
Diffstat (limited to 'src/core.c')
| -rw-r--r-- | src/core.c | 439 |
1 files changed, 232 insertions, 207 deletions
@@ -91,7 +91,7 @@ void term_sc_print_glyph(struct term *t, uint32_t cp, int width, uint16_t flags) int right = t->lr_margin_mode ? t->right_margin : cols - 1; if (TERM_EXPECT(width == 2 && cols > 1 && cx == right && t->autowrap, 0)) { - row_fill_gap(t, p, row, cx); + term_row_fill_gap(srow, cx); clear_wide_edges(row, cx, right + 1, srow->len); row[cx] = (struct cell){ ' ', TERM_COLOR_FG, TERM_COLOR_BG, CELL_WRAP_PAD, 0 }; srow->len = (uint16_t)cols; @@ -113,7 +113,7 @@ void term_sc_print_glyph(struct term *t, uint32_t cp, int width, uint16_t flags) } else { flags &= ~CELL_WIDE; } - row_fill_gap(t, p, row, cx); + term_row_fill_gap(srow, cx); if (TERM_EXPECT(t->insert_mode, 0)) { if (cx == srow->len) row[cx] = (struct cell){ ' ', TERM_COLOR_FG, TERM_COLOR_BG, 0, 0 }; @@ -333,13 +333,11 @@ void term_sc_tab(struct term *t, int n) void term_sc_scroll_region(struct term *t, int top, int bottom, int n, bool down) { - t->pending_wrap = false; term_screen_scroll_region(t, top, bottom, n, down, false); } void term_sc_scroll(struct term *t, int n, bool down) { - t->pending_wrap = false; term_screen_scroll_region(t, t->scroll_top, t->scroll_bottom, n, down, true); } @@ -587,67 +585,100 @@ static void flush_orig_rows(struct term *t, struct screen *s, } } -static void flush_stream_queue(struct term *t, struct screen *s, - struct hist_ascii_line *q, int *q_count, int q_cap, - int *orig_y, int *orig_rem, size_t *flushed_scrolled, - const struct pen_attr *attr, bool def_pen) +struct stream_emit_ctx { + struct term *term; + struct screen *screen; + struct hist_ascii_line *q; + const struct pen_attr *attr; + size_t flushed_scrolled; + int q_cap; + int q_count; + int orig_y; + int orig_rem; + int cols; + bool def_pen; +}; + +static void flush_stream_queue(struct stream_emit_ctx *ctx) { - if (*orig_rem > 0) - flush_orig_rows(t, s, orig_y, orig_rem, *orig_rem); - int flush_n = *q_count - q_cap; - (void)def_pen; - term_hist_push_ascii_lines(&t->hist, q, flush_n, attr); - if (q_cap > 0) { - if (flush_n >= q_cap) - memcpy(q, q + flush_n, (size_t)q_cap * sizeof(q[0])); + if (ctx->orig_rem > 0) + flush_orig_rows(ctx->term, ctx->screen, &ctx->orig_y, &ctx->orig_rem, ctx->orig_rem); + int flush_n = ctx->q_count - ctx->q_cap; + (void)ctx->def_pen; + term_hist_push_ascii_lines(&ctx->term->hist, ctx->q, flush_n, ctx->attr); + if (ctx->q_cap > 0) { + if (flush_n >= ctx->q_cap) + memcpy(ctx->q, ctx->q + flush_n, (size_t)ctx->q_cap * sizeof(ctx->q[0])); else - memmove(q, q + flush_n, (size_t)q_cap * sizeof(q[0])); + memmove(ctx->q, ctx->q + flush_n, (size_t)ctx->q_cap * sizeof(ctx->q[0])); } - *q_count = q_cap; - *flushed_scrolled += (size_t)flush_n; + ctx->q_count = ctx->q_cap; + ctx->flushed_scrolled += (size_t)flush_n; } -static inline void emit_stream_bulk_wrapped(struct term *t, struct screen *s, - const char *src, int bulk_lines, int cols, - const struct pen_attr *attr, bool def_pen, - struct hist_ascii_line *q, int q_cap, - int *q_count, int *orig_y, int *orig_rem, - size_t *flushed_scrolled) +static inline void emit_stream_bulk_wrapped(struct stream_emit_ctx *ctx, + const char *src, int bulk_lines) { + struct term *t = ctx->term; + struct screen *s = ctx->screen; + int cols = ctx->cols; if (TERM_EXPECT(t->pending_wrap, 0)) { row_set_wrapped(screen_get_row(s, s->rows - 1), true); t->pending_wrap = false; t->cursor_x = 0; term_screen_scroll_up(t); } - if (bulk_lines < q_cap) { + if (bulk_lines < ctx->q_cap) { const char *p = src; for (int r = 0; r < bulk_lines; r++, p += cols) { - if (*q_count == 256) { - flush_stream_queue(t, s, q, q_count, q_cap, orig_y, orig_rem, - flushed_scrolled, attr, def_pen); + if (ctx->q_count == 256) { + flush_stream_queue(ctx); } - q[(*q_count)++] = (struct hist_ascii_line){ p, (uint16_t)cols, 1 }; + ctx->q[ctx->q_count++] = (struct hist_ascii_line){ p, (uint16_t)cols, 1 }; } return; } - if (*orig_rem > 0) - flush_orig_rows(t, s, orig_y, orig_rem, *orig_rem); - if (*q_count > 0) { - term_hist_push_ascii_lines(&t->hist, q, *q_count, attr); - *flushed_scrolled += (size_t)*q_count; - *q_count = 0; + if (ctx->orig_rem > 0) + flush_orig_rows(t, s, &ctx->orig_y, &ctx->orig_rem, ctx->orig_rem); + if (ctx->q_count > 0) { + term_hist_push_ascii_lines(&t->hist, ctx->q, ctx->q_count, ctx->attr); + ctx->flushed_scrolled += (size_t)ctx->q_count; + ctx->q_count = 0; } - int hist_rows = bulk_lines - q_cap; + int hist_rows = bulk_lines - ctx->q_cap; if (hist_rows > 0) { - term_hist_push_ascii(&t->hist, src, hist_rows * cols, attr, true); - *flushed_scrolled += (size_t)hist_rows; + term_hist_push_ascii(&t->hist, src, hist_rows * cols, ctx->attr, true); + ctx->flushed_scrolled += (size_t)hist_rows; } const char *sp = src + (size_t)hist_rows * (size_t)cols; - for (int r = 0; r < q_cap; r++, sp += cols) - q[r] = (struct hist_ascii_line){ sp, (uint16_t)cols, 1 }; - *q_count = q_cap; + for (int r = 0; r < ctx->q_cap; r++, sp += cols) + ctx->q[r] = (struct hist_ascii_line){ sp, (uint16_t)cols, 1 }; + ctx->q_count = ctx->q_cap; +} + +static inline TERM_ALWAYS_INLINE void emit_stream_row_fast(struct stream_emit_ctx *ctx, + const char *row_ptr, + int row_len, bool row_wrapped) +{ + if (TERM_EXPECT(ctx->q_count == 256, 0)) + flush_stream_queue(ctx); + ctx->q[ctx->q_count++] = (struct hist_ascii_line){ + row_ptr, (uint16_t)row_len, (uint8_t)row_wrapped + }; +} + +static inline TERM_ALWAYS_INLINE void emit_stream_row(struct stream_emit_ctx *ctx, + const char *row_ptr, + int row_len, bool row_wrapped) +{ + if (TERM_EXPECT(ctx->term->pending_wrap, 0)) { + row_set_wrapped(screen_get_row(ctx->screen, ctx->screen->rows - 1), true); + ctx->term->pending_wrap = false; + ctx->term->cursor_x = 0; + term_screen_scroll_up(ctx->term); + } + emit_stream_row_fast(ctx, row_ptr, row_len, row_wrapped); } static TERM_NOINLINE size_t stream_ascii_wrapped(struct term *t, const char *buf, size_t len) @@ -877,6 +908,140 @@ static inline uint64_t line_masks(const char *buf, uint64_t *nl) return controls; } +static void stream_queue_finish(struct stream_emit_ctx *ctx) +{ + int cols = ctx->cols; + size_t queued_rows = 0; + for (int j = 0; j < ctx->q_count; j++) + queued_rows += ctx->q[j].wrapped ? 1 : rows_narrow(ctx->q[j].len, cols); + size_t loop_scrolled = ctx->flushed_scrolled + queued_rows; + if (loop_scrolled == 0) + return; + + struct term *t = ctx->term; + struct screen *s = ctx->screen; + int rows = s->rows; + + int push_orig = queued_rows < (size_t)ctx->orig_rem ? (int)queued_rows : ctx->orig_rem; + if (push_orig > 0) + flush_orig_rows(t, s, &ctx->orig_y, &ctx->orig_rem, push_orig); + + int needed = rows - 1 - ctx->orig_rem; + int tail_rows = 0; + int k = ctx->q_count; + while (k > 0 && tail_rows < needed) { + k--; + struct hist_ascii_line pr = ctx->q[k]; + int r = (pr.wrapped || pr.len == 0) ? 1 : (int)rows_narrow(pr.len, cols); + tail_rows += r; + } + + int flush_n = k; + if (flush_n > 0) { + term_hist_push_ascii_lines(&t->hist, ctx->q, flush_n, ctx->attr); + } + + int skip_rows = tail_rows - needed; + if (skip_rows > 0) { + struct hist_ascii_line pr = ctx->q[k]; + int skip_bytes = skip_rows * cols; + if (skip_bytes > (int)pr.len) skip_bytes = (int)pr.len; + term_hist_push_ascii(&t->hist, pr.ptr, skip_bytes, ctx->attr, true); + } + + if (ctx->orig_rem == 0) { + s->top = 0; + } else { + s->top += tail_rows; + if (s->top >= rows) + s->top -= rows; + } + /* Rows that stay on the screen become text rows. A row that + * continues the row above starts from its pen. */ + int cur_y = ctx->orig_rem; + bool prev_wrapped = false; + for (int j = k; j < ctx->q_count && cur_y < rows - 1; j++) { + struct hist_ascii_line pr = ctx->q[j]; + int p_len = pr.len; + const char *p_ptr = pr.ptr; + + if (p_len == 0) { + int p = phys_row(t, cur_y); + struct srow *dst = &s->slot[p]; + dst->uid = ++t->next_row_uid; + dst->version++; + uint8_t tail_padded = (dst->kind == ROW_CELLS && dst->len == 0) + ? (dst->flags & RF_TAIL_PADDED) : 0; + dst->kind = ROW_CELLS; + dst->len = 0; + dst->flags = tail_padded; + prev_wrapped = false; + cur_y++; + continue; + } + + int off_in_line = 0; + if (j == k && skip_rows > 0) { + off_in_line = skip_rows * cols; + prev_wrapped = true; + } + + while (off_in_line < p_len && cur_y < rows - 1) { + int chunk = p_len - off_in_line; + bool row_wraps = false; + if (chunk > cols) { + chunk = cols; + row_wraps = true; + } else if (pr.wrapped && off_in_line + chunk == p_len) { + row_wraps = true; + } + + int p = phys_row(t, cur_y); + struct srow *dst = &s->slot[p]; + dst->uid = ++t->next_row_uid; + dst->version++; + + struct pen_attr start = prev_wrapped ? *ctx->attr : pen_attr_default(); + dst->kind = ROW_TEXT; + dst->len = 0; + dst->flags = row_wraps ? RF_WRAPPED : 0; + text_row_start(&dst->text, &start); + dst->text.text = dst->ascii; + text_append(dst, (const uint8_t *)(p_ptr + off_in_line), + chunk, chunk, chunk, HLINE_NARROW, + ctx->attr->fg, ctx->attr->bg, pen_sl(ctx->attr)); + + prev_wrapped = row_wraps; + off_in_line += chunk; + cur_y++; + } + } + while (cur_y < rows - 1) { + int p = phys_row(t, cur_y); + struct srow *dst = &s->slot[p]; + dst->uid = ++t->next_row_uid; + dst->version++; + dst->kind = ROW_CELLS; + dst->len = 0; + dst->flags = 0; + cur_y++; + } + int bot_p = phys_row(t, rows - 1); + struct srow *bot = &s->slot[bot_p]; + uint8_t bot_tail = (bot->kind == ROW_CELLS && bot->len == 0) + ? (bot->flags & RF_TAIL_PADDED) : 0; + bot->uid = ++t->next_row_uid; + bot->version++; + bot->kind = ROW_CELLS; + bot->len = 0; + bot->flags = bot_tail; + if (t->prompt_y > 0) { + t->prompt_y = (size_t)t->prompt_y > loop_scrolled + ? t->prompt_y - (int)loop_scrolled : 0; + } + clamp_selection_after_scroll(t); +} + /* One body for every level. isa is a constant in each copy below, so each * copy keeps only its own kernels. */ static inline TERM_ALWAYS_INLINE size_t bulk_ascii_lines_body(struct term *t, const char *buf, @@ -894,7 +1059,6 @@ static inline TERM_ALWAYS_INLINE size_t bulk_ascii_lines_body(struct term *t, co bool autowrap = t->autowrap; bool lnm = term_lnm(t); int last_char = (int)t->last_char; - size_t flushed_scrolled = 0; struct pen_attr attr = { .fg = t->cur_fg, @@ -909,29 +1073,19 @@ static inline TERM_ALWAYS_INLINE size_t bulk_ascii_lines_body(struct term *t, co return 0; struct hist_ascii_line q[256]; - int q_cap = rows - 1; - int q_count = 0; - int orig_y = 0; - int orig_rem = q_cap; - -#define EMIT_STREAM_ROW_FAST(row_ptr, row_len, row_wrapped) do { \ - if (TERM_EXPECT(q_count == 256, 0)) \ - flush_stream_queue(t, s, q, &q_count, q_cap, &orig_y, &orig_rem, \ - &flushed_scrolled, &attr, def_pen); \ - q[q_count++] = (struct hist_ascii_line){ \ - (row_ptr), (uint16_t)(row_len), (uint8_t)(row_wrapped) \ - }; \ -} while (0) - -#define EMIT_STREAM_ROW(row_ptr, row_len, row_wrapped) do { \ - if (TERM_EXPECT(t->pending_wrap, 0)) { \ - row_set_wrapped(screen_get_row(s, rows - 1), true); \ - t->pending_wrap = false; \ - t->cursor_x = 0; \ - term_screen_scroll_up(t); \ - } \ - EMIT_STREAM_ROW_FAST(row_ptr, row_len, row_wrapped); \ -} while (0) + struct stream_emit_ctx ctx = { + .term = t, + .screen = s, + .q = q, + .attr = &attr, + .flushed_scrolled = 0, + .q_cap = rows - 1, + .q_count = 0, + .orig_y = 0, + .orig_rem = rows - 1, + .cols = cols, + .def_pen = def_pen, + }; while (i < len) { /* Scan each block once. Keep the unfinished line in the input buffer. */ @@ -953,7 +1107,7 @@ static inline TERM_ALWAYS_INLINE size_t bulk_ascii_lines_body(struct term *t, co goto narrow_done; if (end > i) last_char = (unsigned char)buf[end - 1]; - EMIT_STREAM_ROW_FAST(buf + i, (int)(end - i), false); + emit_stream_row_fast(&ctx, buf + i, (int)(end - i), false); i = end + 1; nl &= nl - 1; } @@ -979,9 +1133,7 @@ narrow_done: int bulk_lines = run / cols - (run % cols == 0); if (bulk_lines > 0 && autowrap && (i + run == len || ((unsigned char)buf[i + run] >= 0x20 && (unsigned char)buf[i + run] < 0x7f) || (run % cols == 0))) { - emit_stream_bulk_wrapped(t, s, buf + i, bulk_lines, cols, &attr, def_pen, - q, q_cap, &q_count, &orig_y, &orig_rem, - &flushed_scrolled); + emit_stream_bulk_wrapped(&ctx, buf + i, bulk_lines); size_t write_cells = (size_t)bulk_lines * (size_t)cols; last_char = (unsigned char)buf[i + write_cells - 1]; i += write_cells; @@ -998,7 +1150,7 @@ narrow_done: if (eol > 0) { last_char = (unsigned char)buf[i + limit - 1]; - EMIT_STREAM_ROW_FAST(buf + i, limit, false); + emit_stream_row_fast(&ctx, buf + i, limit, false); i += (size_t)(limit + eol); continue; } @@ -1008,7 +1160,7 @@ narrow_done: (unsigned char)buf[i + limit - 1]); if (!wrapped) break; last_char = (unsigned char)buf[i + limit - 1]; - EMIT_STREAM_ROW_FAST(buf + i, limit, true); + emit_stream_row_fast(&ctx, buf + i, limit, true); i += (size_t)limit; continue; } @@ -1030,7 +1182,7 @@ narrow_done: if (p1 > 0) last_char = (unsigned char)buf[i + p1 - 1]; - EMIT_STREAM_ROW_FAST(buf + i, p1, false); + emit_stream_row_fast(&ctx, buf + i, p1, false); int next_start = p1 + eol1; int last_consumed = next_start; @@ -1051,7 +1203,7 @@ narrow_done: if (eol2 > 0) { if (rel_p2 > 0) last_char = (unsigned char)buf[i + p2 - 1]; - EMIT_STREAM_ROW_FAST(buf + i + next_start, rel_p2, false); + emit_stream_row_fast(&ctx, buf + i + next_start, rel_p2, false); int next_start2 = p2 + eol2; last_consumed = next_start2; @@ -1072,7 +1224,7 @@ narrow_done: if (eol3 > 0) { if (rel_p3 > 0) last_char = (unsigned char)buf[i + p3 - 1]; - EMIT_STREAM_ROW_FAST(buf + i + next_start2, rel_p3, false); + emit_stream_row_fast(&ctx, buf + i + next_start2, rel_p3, false); last_consumed = p3 + eol3; } @@ -1101,7 +1253,7 @@ narrow_done: if (TERM_EXPECT(eol > 0, 1)) { if (run > 0) last_char = (unsigned char)buf[i + run - 1]; - EMIT_STREAM_ROW(buf + i, run, false); + emit_stream_row(&ctx, buf + i, run, false); i += (size_t)(run + eol); continue; } @@ -1109,7 +1261,7 @@ narrow_done: if (!run) { int neol = newline_bytes(buf + i, len - i); if (neol == 2 || (neol == 1 && lnm)) { - EMIT_STREAM_ROW(buf + i, 0, false); + emit_stream_row(&ctx, buf + i, 0, false); i += (size_t)neol; continue; } @@ -1128,150 +1280,23 @@ narrow_done: ((unsigned char)buf[i + full] >= 0x20 && (unsigned char)buf[i + full] < 0x7f) || full % cols == 0)) { - emit_stream_bulk_wrapped(t, s, buf + i, bulk_lines, cols, &attr, def_pen, - q, q_cap, &q_count, &orig_y, &orig_rem, - &flushed_scrolled); + emit_stream_bulk_wrapped(&ctx, buf + i, bulk_lines); size_t write_cells = (size_t)bulk_lines * (size_t)cols; last_char = (unsigned char)buf[i + write_cells - 1]; i += write_cells; continue; } last_char = (unsigned char)buf[i + run - 1]; - EMIT_STREAM_ROW(buf + i, run, true); + emit_stream_row(&ctx, buf + i, run, true); i += (size_t)run; } -#undef EMIT_STREAM_ROW -#undef EMIT_STREAM_ROW_FAST #if defined(TERM_KERNEL_AVX512) done: #endif if (i == 0) return 0; - size_t queued_rows = 0; - for (int j = 0; j < q_count; j++) - queued_rows += q[j].wrapped ? 1 : rows_narrow(q[j].len, cols); - size_t loop_scrolled = flushed_scrolled + queued_rows; - if (loop_scrolled > 0) { - int push_orig = queued_rows < (size_t)orig_rem ? (int)queued_rows : orig_rem; - if (push_orig > 0) - flush_orig_rows(t, s, &orig_y, &orig_rem, push_orig); - - int needed = rows - 1 - orig_rem; - int tail_rows = 0; - int k = q_count; - while (k > 0 && tail_rows < needed) { - k--; - struct hist_ascii_line pr = q[k]; - int r = (pr.wrapped || pr.len == 0) ? 1 : (int)rows_narrow(pr.len, cols); - tail_rows += r; - } - - int flush_n = k; - if (flush_n > 0) { - term_hist_push_ascii_lines(&t->hist, q, flush_n, &attr); - } - - int skip_rows = tail_rows - needed; - if (skip_rows > 0) { - struct hist_ascii_line pr = q[k]; - int skip_bytes = skip_rows * cols; - if (skip_bytes > (int)pr.len) skip_bytes = (int)pr.len; - term_hist_push_ascii(&t->hist, pr.ptr, skip_bytes, &attr, true); - } - - if (orig_rem == 0) { - s->top = 0; - } else { - s->top += tail_rows; - if (s->top >= rows) - s->top -= rows; - } - /* Rows that stay on the screen become text rows. A row that - * continues the row above starts from its pen. */ - int cur_y = orig_rem; - bool prev_wrapped = false; - for (int j = k; j < q_count && cur_y < rows - 1; j++) { - struct hist_ascii_line pr = q[j]; - int p_len = pr.len; - const char *p_ptr = pr.ptr; - - if (p_len == 0) { - int p = phys_row(t, cur_y); - struct srow *dst = &s->slot[p]; - dst->uid = ++t->next_row_uid; - dst->version++; - uint8_t tail_padded = (dst->kind == ROW_CELLS && dst->len == 0) - ? (dst->flags & RF_TAIL_PADDED) : 0; - dst->kind = ROW_CELLS; - dst->len = 0; - dst->flags = tail_padded; - prev_wrapped = false; - cur_y++; - continue; - } - - int off_in_line = 0; - if (j == k && skip_rows > 0) { - off_in_line = skip_rows * cols; - prev_wrapped = true; - } - - while (off_in_line < p_len && cur_y < rows - 1) { - int chunk = p_len - off_in_line; - bool row_wraps = false; - if (chunk > cols) { - chunk = cols; - row_wraps = true; - } else if (pr.wrapped && off_in_line + chunk == p_len) { - row_wraps = true; - } - - int p = phys_row(t, cur_y); - struct srow *dst = &s->slot[p]; - dst->uid = ++t->next_row_uid; - dst->version++; - - struct pen_attr start = prev_wrapped ? attr : pen_attr_default(); - dst->kind = ROW_TEXT; - dst->len = 0; - dst->flags = row_wraps ? RF_WRAPPED : 0; - text_row_start(&dst->text, &start); - dst->text.text = dst->ascii; - text_append(dst, (const uint8_t *)(p_ptr + off_in_line), - chunk, chunk, chunk, HLINE_NARROW, - attr.fg, attr.bg, pen_sl(&attr)); - - prev_wrapped = row_wraps; - off_in_line += chunk; - cur_y++; - } - } - while (cur_y < rows - 1) { - int p = phys_row(t, cur_y); - struct srow *dst = &s->slot[p]; - dst->uid = ++t->next_row_uid; - dst->version++; - dst->kind = ROW_CELLS; - dst->len = 0; - dst->flags = 0; - cur_y++; - } - int bot_p = phys_row(t, rows - 1); - struct srow *bot = &s->slot[bot_p]; - uint8_t bot_tail = (bot->kind == ROW_CELLS && bot->len == 0) - ? (bot->flags & RF_TAIL_PADDED) : 0; - bot->uid = ++t->next_row_uid; - bot->version++; - bot->kind = ROW_CELLS; - bot->len = 0; - bot->flags = bot_tail; - if (t->prompt_y > 0) { - t->prompt_y = (size_t)t->prompt_y > loop_scrolled - ? t->prompt_y - (int)loop_scrolled : 0; - } - clamp_selection_after_scroll(t); - } + stream_queue_finish(&ctx); t->last_char = (uint32_t)last_char; t->cursor_x = 0; |
