From ae3c0cf60f69e37aa3bc760c829de71500ff1d79 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Fri, 12 Jun 2026 21:14:26 +0200 Subject: [PATCH] Add a new draw2 interface Remove the callback, and use a simple array. Rework the attributes, to take only 12 bytes per cell. So for a 80x24 Terminal, it's only 23KB Signed-off-by: Jocelyn Falempe --- src/tsm/libtsm-int.h | 4 ++ src/tsm/libtsm.h | 27 ++++++++++++ src/tsm/libtsm.sym | 5 +++ src/tsm/tsm-render.c | 102 ++++++++++++++++++++++++++++++++++++++++++- src/tsm/tsm-screen.c | 1 + 5 files changed, 138 insertions(+), 1 deletion(-) diff --git a/src/tsm/libtsm-int.h b/src/tsm/libtsm-int.h index f167158..16b7b88 100644 --- a/src/tsm/libtsm-int.h +++ b/src/tsm/libtsm-int.h @@ -155,6 +155,10 @@ struct tsm_screen { bool sel_active; struct selection_pos sel_start; struct selection_pos sel_end; + + /* draw2 interface */ + struct tsm_screen_cell *cells; + unsigned int cells_count; }; void screen_cell_init(struct tsm_screen *con, struct cell *cell); diff --git a/src/tsm/libtsm.h b/src/tsm/libtsm.h index 48f67ad..6349d8f 100644 --- a/src/tsm/libtsm.h +++ b/src/tsm/libtsm.h @@ -172,6 +172,31 @@ struct tsm_screen_attr { unsigned int blink : 1; /* blinking character */ }; +/* Attributes that alter the glyph shape */ +typedef union{ + struct { + uint8_t bold : 1; + uint8_t italic : 1; + uint8_t underline : 1; + uint8_t blink : 1; + uint8_t reserved : 5; + }; + uint8_t u8; +} tsm_screen_attr2_t; + +struct tsm_screen_color { + uint8_t r; /* red */ + uint8_t g; /* green */ + uint8_t b; /* blue */ +}; + +struct tsm_screen_cell { + uint32_t ch; /* character */ + struct tsm_screen_color fg; /* foreground color */ + struct tsm_screen_color bg; /* background color */ + tsm_screen_attr2_t attr2; /* glyph attributes */ +}; + typedef int (*tsm_screen_draw_cb) (struct tsm_screen *con, uint64_t id, const uint32_t *ch, @@ -268,6 +293,8 @@ int tsm_screen_selection_copy(struct tsm_screen *con, char **out); tsm_age_t tsm_screen_draw(struct tsm_screen *con, tsm_screen_draw_cb draw_cb, void *data); +const struct tsm_screen_cell *tsm_screen_draw2(struct tsm_screen *con); + /** @} */ /** diff --git a/src/tsm/libtsm.sym b/src/tsm/libtsm.sym index 3456191..021de6c 100644 --- a/src/tsm/libtsm.sym +++ b/src/tsm/libtsm.sym @@ -148,3 +148,8 @@ global: tsm_vte_set_bell_cb; tsm_vte_set_led_cb; } LIBTSM_4_4; + +LIBTSM_4_6 { +global: + tsm_screen_draw2; +} LIBTSM_4_5; diff --git a/src/tsm/tsm-render.c b/src/tsm/tsm-render.c index 0429e7d..de9ce68 100644 --- a/src/tsm/tsm-render.c +++ b/src/tsm/tsm-render.c @@ -30,7 +30,6 @@ * yourself. */ -#include #include #include #include @@ -193,3 +192,104 @@ tsm_age_t tsm_screen_draw(struct tsm_screen *con, tsm_screen_draw_cb draw_cb, return con->age_cnt; } } + +/* + * tsm_screen_draw2 returns a pointer to a table of tsm_screen_cell, one for each cell on the screen. + * It uses a bit more memory, but should be much faster, as each cell is only 12 bytes. + * The caller can read the table, until the next call to tsm_screen_draw2, or until con is destroyed. + */ +SHL_EXPORT +const struct tsm_screen_cell *tsm_screen_draw2(struct tsm_screen *con) +{ + unsigned int i, j, k; + struct line *line, *next_line = NULL; + struct cell *cell, empty; + struct tsm_screen_cell *out; + bool in_sel = false, sel_start = false, sel_end = false; + bool inverse = false; + + if (!con) + return NULL; + + if (con->cells_count < con->size_x * con->size_y) { + free(con->cells); + con->cells_count = 0; + con->cells = malloc(con->size_x * con->size_y * sizeof(struct tsm_screen_cell)); + if (!con->cells) + return NULL; + con->cells_count = con->size_x * con->size_y; + memset(con->cells, 0, con->cells_count * sizeof(struct tsm_screen_cell)); + } + + screen_cell_init(con, &empty); + + /* push each character into rendering pipeline */ + k = 0; + next_line = con->sb.pos; + + if (con->sel_active) { + if (!con->sel_start.line && con->sel_end.line) + in_sel = true; + + if (is_in_scrollback(&con->sel_start) + && (!con->sb.pos || con->sel_start.line->sb_id < con->sb.pos->sb_id)) + in_sel = !in_sel; + if (is_in_scrollback(&con->sel_end) + && (!con->sb.pos || con->sel_end.line->sb_id < con->sb.pos->sb_id)) + in_sel = !in_sel; + } + + for (i = 0; i < con->size_y; ++i) { + if (next_line) { + line = next_line; + next_line = shl_dlist_next(next_line, &con->sb.list, list); + } else { + line = con->lines[k]; + k++; + } + + if (con->sel_active) { + sel_start = (con->sel_start.line == line); + sel_end = (con->sel_end.line == line); + } + + for (j = 0; j < con->size_x; ++j) { + out = &con->cells[i * con->size_x + j]; + /* don't handle multiple codepoints yet */ + if (j < line->size && line->cells[j].ch <= TSM_UCS4_MAX) + cell = &line->cells[j]; + else + cell = ∅ + out->ch = cell->ch ? cell->ch : ' '; + out->attr2.bold = cell->attr.bold; + out->attr2.italic = cell->attr.italic; + out->attr2.underline = cell->attr.underline; + out->attr2.blink = cell->attr.blink; + + if (sel_start && j == con->sel_start.x) + in_sel = !in_sel; + + /* Inverse logic, cell attribute, selection, and whole screen inverse */ + inverse = cell->attr.inverse ^ in_sel ^ (con->flags & TSM_SCREEN_INVERSE); + + if (inverse) { + out->fg.r = cell->attr.br; + out->fg.g = cell->attr.bg; + out->fg.b = cell->attr.bb; + out->bg.r = cell->attr.fr; + out->bg.g = cell->attr.fg; + out->bg.b = cell->attr.fb; + } else { + out->fg.r = cell->attr.fr; + out->fg.g = cell->attr.fg; + out->fg.b = cell->attr.fb; + out->bg.r = cell->attr.br; + out->bg.g = cell->attr.bg; + out->bg.b = cell->attr.bb; + } + if (sel_end && j == con->sel_end.x) + in_sel = !in_sel; + } + } + return con->cells; +} diff --git a/src/tsm/tsm-screen.c b/src/tsm/tsm-screen.c index 3247242..2ed815d 100644 --- a/src/tsm/tsm-screen.c +++ b/src/tsm/tsm-screen.c @@ -543,6 +543,7 @@ void tsm_screen_unref(struct tsm_screen *con) free(con->alt_lines); free(con->tab_ruler); tsm_symbol_table_unref(con->sym_table); + free(con->cells); free(con); }