unsigned int tsm_symbol_get_width(struct tsm_symbol_table *tbl, tsm_symbol_t sym) { const uint32_t *ch; size_t len; if (!tbl) return 0; ch = tsm_symbol_get(tbl, &sym, &len); if (len == 0) return 0; return tsm_ucs4_get_width(*ch); }
SHL_EXPORT unsigned int tsm_symbol_get_width(struct tsm_symbol_table *tbl, tsm_symbol_t sym) { int ret; const uint32_t *ch; size_t len; if (!tbl) tbl = tsm_symbol_table_default; if (!tbl) { ret = tsm_symbol_table_new(&tbl); if (ret) return sym; tsm_symbol_table_default = tbl; } ch = tsm_symbol_get(tbl, &sym, &len); if (len == 0) return 0; return tsm_ucs4_get_width(*ch); }
SHL_EXPORT tsm_age_t tsm_screen_draw(struct tsm_screen *con, tsm_screen_draw_cb draw_cb, void *data) { unsigned int cur_x, cur_y; unsigned int i, j, k; struct line *iter, *line = NULL; struct cell *cell, empty; struct tsm_screen_attr attr; int ret, warned = 0; const uint32_t *ch; size_t len; bool in_sel = false, sel_start = false, sel_end = false; bool was_sel = false; tsm_age_t age; if (!con || !draw_cb) return 0; screen_cell_init(con, &empty); cur_x = con->cursor_x; if (con->cursor_x >= con->size_x) cur_x = con->size_x - 1; cur_y = con->cursor_y; if (con->cursor_y >= con->size_y) cur_y = con->size_y - 1; /* push each character into rendering pipeline */ iter = con->sb_pos; k = 0; if (con->sel_active) { if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP) in_sel = !in_sel; if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) in_sel = !in_sel; if (con->sel_start.line && (!iter || con->sel_start.line->sb_id < iter->sb_id)) in_sel = !in_sel; if (con->sel_end.line && (!iter || con->sel_end.line->sb_id < iter->sb_id)) in_sel = !in_sel; } for (i = 0; i < con->size_y; ++i) { if (iter) { line = iter; iter = iter->next; } else { line = con->lines[k]; k++; } if (con->sel_active) { if (con->sel_start.line == line || (!con->sel_start.line && con->sel_start.y == k - 1)) sel_start = true; else sel_start = false; if (con->sel_end.line == line || (!con->sel_end.line && con->sel_end.y == k - 1)) sel_end = true; else sel_end = false; was_sel = false; } for (j = 0; j < con->size_x; ++j) { if (j < line->size) cell = &line->cells[j]; else cell = ∅ memcpy(&attr, &cell->attr, sizeof(attr)); if (con->sel_active) { if (sel_start && j == con->sel_start.x) { was_sel = in_sel; in_sel = !in_sel; } if (sel_end && j == con->sel_end.x) { was_sel = in_sel; in_sel = !in_sel; } } if (k == cur_y + 1 && j == cur_x && !(con->flags & TSM_SCREEN_HIDE_CURSOR)) attr.underline = !attr.underline; //attr.inverse = !attr.inverse; /* TODO: do some more sophisticated inverse here. When * INVERSE mode is set, we should instead just select * inverse colors instead of switching background and * foreground */ if (con->flags & TSM_SCREEN_INVERSE) attr.inverse = !attr.inverse; if (in_sel || was_sel) { was_sel = false; attr.inverse = !attr.inverse; } if (con->age_reset) { age = 0; } else { age = cell->age; if (line->age > age) age = line->age; if (con->age > age) age = con->age; } ch = tsm_symbol_get(con->sym_table, &cell->ch, &len); if (cell->ch == ' ' || cell->ch == 0) len = 0; ret = draw_cb(con, cell->ch, ch, len, cell->width, j, i, &attr, age, data); if (ret && warned++ < 3) { llog_debug(con, "cannot draw glyph at %ux%u via text-renderer", j, i); if (warned == 3) llog_debug(con, "suppressing further warnings during this rendering round"); } } } if (con->age_reset) { con->age_reset = 0; return 0; } else { return con->age_cnt; } }
tsm_symbol_t tsm_symbol_append(struct tsm_symbol_table *tbl, tsm_symbol_t sym, uint32_t ucs4) { uint32_t buf[TSM_UCS4_MAXLEN + 1], nsym, *nval; const uint32_t *ptr; size_t s; bool res; int ret; if (!tbl) return sym; if (ucs4 > TSM_UCS4_MAX) return sym; ptr = tsm_symbol_get(tbl, &sym, &s); if (s >= TSM_UCS4_MAXLEN) return sym; memcpy(buf, ptr, s * sizeof(uint32_t)); buf[s++] = ucs4; buf[s++] = TSM_UCS4_MAX + 1; res = shl_htable_lookup(&tbl->symbols, buf, hash_ucs4(buf, NULL), (void**)&nval); if (res) { /* key is prefixed with actual value */ return *--nval; } /* We save the key in nval and prefix it with the new ID. Note that * the prefix is hidden, we actually store "++nval" in the htable. */ nval = malloc(sizeof(uint32_t) * (s + 1)); if (!nval) return sym; ++nval; memcpy(nval, buf, s * sizeof(uint32_t)); nsym = tbl->next_id + 1; /* Out of IDs; we actually have 2 Billion IDs so this seems * very unlikely but lets be safe here */ if (nsym <= tbl->next_id++) goto err_id; /* store ID hidden before the key */ *(nval - 1) = nsym; ret = shl_htable_insert(&tbl->symbols, nval, hash_ucs4(nval, NULL)); if (ret) goto err_id; ret = shl_array_push(tbl->index, &nval); if (ret) goto err_symbol; return nsym; err_symbol: shl_htable_remove(&tbl->symbols, nval, hash_ucs4(nval, NULL), NULL); err_id: --tbl->next_id; free(nval); return sym; }
SHL_EXPORT tsm_symbol_t tsm_symbol_append(struct tsm_symbol_table *tbl, tsm_symbol_t sym, uint32_t ucs4) { uint32_t buf[TSM_UCS4_MAXLEN + 1], nsym, *nval; const uint32_t *ptr; size_t s; void *tmp; bool res; int ret; if (!tbl) tbl = tsm_symbol_table_default; if (!tbl) { ret = tsm_symbol_table_new(&tbl); if (ret) return sym; tsm_symbol_table_default = tbl; } if (ucs4 > TSM_UCS4_MAX) return sym; ptr = tsm_symbol_get(tbl, &sym, &s); if (s >= TSM_UCS4_MAXLEN) return sym; memcpy(buf, ptr, s * sizeof(uint32_t)); buf[s++] = ucs4; buf[s++] = TSM_UCS4_MAX + 1; res = shl_hashtable_find(tbl->symbols, &tmp, buf); if (res) return (uint32_t)(long)tmp; nval = malloc(sizeof(uint32_t) * s); if (!nval) return sym; memcpy(nval, buf, s * sizeof(uint32_t)); nsym = tbl->next_id + 1; /* Out of IDs; we actually have 2 Billion IDs so this seems * very unlikely but lets be safe here */ if (nsym <= tbl->next_id++) goto err_id; ret = shl_hashtable_insert(tbl->symbols, nval, (void*)(long)nsym); if (ret) goto err_id; ret = shl_array_push(tbl->index, &nval); if (ret) goto err_symbol; return nsym; err_symbol: shl_hashtable_remove(tbl->symbols, nval); err_id: --tbl->next_id; free(nval); return sym; }
void tsm_screen_draw(struct tsm_screen *con, tsm_screen_prepare_cb prepare_cb, tsm_screen_draw_cb draw_cb, tsm_screen_render_cb render_cb, void *data) { unsigned int cur_x, cur_y; unsigned int i, j, k; struct line *iter, *line = NULL; struct cell *cell; struct tsm_screen_attr attr; bool cursor_done = false; int ret, warned = 0; uint64_t time_prep = 0, time_draw = 0, time_rend = 0; const uint32_t *ch; size_t len; struct cell empty; if (!con || !draw_cb) return; cell_init(con, &empty); cur_x = con->cursor_x; if (con->cursor_x >= con->size_x) cur_x = con->size_x - 1; cur_y = con->cursor_y; if (con->cursor_y >= con->size_y) cur_y = con->size_y - 1; /* render preparation */ if (prepare_cb) { if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) shl_timer_reset(con->timer); ret = prepare_cb(con, data); if (ret) { llog_warning(con, "cannot prepare text-renderer for rendering"); return; } if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) time_prep = shl_timer_elapsed(con->timer); } else { time_prep = 0; } /* push each character into rendering pipeline */ if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) shl_timer_reset(con->timer); iter = con->sb_pos; k = 0; for (i = 0; i < con->size_y; ++i) { if (iter) { line = iter; iter = iter->next; } else { line = con->lines[k]; k++; } for (j = 0; j < con->size_x; ++j) { if (j < line->size) cell = &line->cells[j]; else cell = ∅ memcpy(&attr, &cell->attr, sizeof(attr)); if (k == cur_y + 1 && j == cur_x) { cursor_done = true; if (!(con->flags & TSM_SCREEN_HIDE_CURSOR)) attr.inverse = !attr.inverse; } /* TODO: do some more sophisticated inverse here. When * INVERSE mode is set, we should instead just select * inverse colors instead of switching background and * foreground */ if (con->flags & TSM_SCREEN_INVERSE) attr.inverse = !attr.inverse; ch = tsm_symbol_get(NULL, &cell->ch, &len); if (cell->ch == ' ' || cell->ch == 0) len = 0; ret = draw_cb(con, cell->ch, ch, len, j, i, &attr, data); if (ret && warned++ < 3) { llog_debug(con, "cannot draw glyph at %ux%u via text-renderer", j, i); if (warned == 3) llog_debug(con, "suppressing further warnings during this rendering round"); } } if (k == cur_y + 1 && !cursor_done) { cursor_done = true; if (!(con->flags & TSM_SCREEN_HIDE_CURSOR)) { if (!(con->flags & TSM_SCREEN_INVERSE)) attr.inverse = !attr.inverse; draw_cb(con, 0, NULL, 0, cur_x, i, &attr, data); } } } if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) time_draw = shl_timer_elapsed(con->timer); /* perform final rendering steps */ if (render_cb) { if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) shl_timer_reset(con->timer); ret = render_cb(con, data); if (ret) llog_warning(con, "cannot render via text-renderer"); if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) time_rend = shl_timer_elapsed(con->timer); } else { time_rend = 0; } if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) llog_debug(con, "timing: sum: %" PRIu64 " prepare: %" PRIu64 " draw: %" PRIu64 " render: %" PRIu64, time_prep + time_draw + time_rend, time_prep, time_draw, time_rend); }