vtgl

terminal emulator implemented in OpenGL
git clone anongit@rnpnr.xyz:vtgl.git
Log | Files | Refs | Feed | LICENSE

Commit: c20911f58d50f6a446c0c4959fc36a7b97018483
Parent: 750ff1000ec72f4797d48a4de5a11f7e4a1ee416
Author: Randy Palamar
Date:   Wed, 30 Oct 2024 05:56:05 -0600

prepack cell attributes into alpha colour bits

Foreground ones are needed for the layout shader and background
ones are used for the remaining attributes and will be ignored by
the shader.

Diffstat:
Mos_unix.c | 2--
Mterminal.c | 45++++++++++++++++++++++++++++++++-------------
Mtest.c | 36+++++++++++++++++-------------------
Mutil.h | 32+++++++++++++++++++-------------
Mvtgl.c | 49++++++++++++++++++++++++-------------------------
5 files changed, 92 insertions(+), 72 deletions(-)

diff --git a/os_unix.c b/os_unix.c @@ -204,8 +204,6 @@ os_alloc_ring_buffer(RingBuf *rb, size capacity) static void os_alloc_framebuffer(Framebuffer *fb, u32 rows, u32 cols) { - ASSERT(sizeof(Cell) == 16); - size pagesize = sysconf(_SC_PAGESIZE); size fb_needed_space = rows * cols * sizeof(Cell); diff --git a/terminal.c b/terminal.c @@ -15,20 +15,20 @@ get_word_around_cell(Term *t, iv2 cell) b32 isspace = ISSPACE(row[cell.x].cp); while (result.start.x > 0) { Cell nc = row[result.start.x - 1]; - if (!(nc.style.attr & ATTR_WDUMMY) && isspace != ISSPACE(nc.cp)) + if (!(nc.bg & ATTR_WDUMMY) && isspace != ISSPACE(nc.cp)) break; result.start.x--; } while (result.end.x < t->size.w - 1) { Cell nc = row[result.end.x + 1]; - if (!(nc.style.attr & ATTR_WDUMMY) && isspace != ISSPACE(nc.cp)) + if (!(nc.bg & ATTR_WDUMMY) && isspace != ISSPACE(nc.cp)) break; result.end.x++; } /* NOTE: ATTR_WDUMMY is invalid for start and end of range */ - if (row[result.start.x].style.attr & ATTR_WDUMMY) result.start.x++; - if (row[result.end.x].style.attr & ATTR_WDUMMY) result.end.x--; + if (row[result.start.x].bg & ATTR_WDUMMY) result.start.x++; + if (row[result.end.x].bg & ATTR_WDUMMY) result.end.x--; return result; } @@ -196,10 +196,13 @@ fb_clear_region(Term *t, u32 r1, u32 r2, u32 c1, u32 c2) Row *rows = t->views[t->view_idx].fb.rows; CellStyle cursor_style = t->cursor.style; + u32 fg = SHADER_PACK_FG(cursor_style.fg.rgba, cursor_style.attr); + u32 bg = SHADER_PACK_BG(cursor_style.bg.rgba, cursor_style.attr); for (u32 r = top_left.y; r <= bottom_right.y; r++) { for (u32 c = top_left.x; c <= bottom_right.x; c++) { - rows[r][c].style = cursor_style; - rows[r][c].cp = ' '; + rows[r][c].fg = fg; + rows[r][c].bg = bg; + rows[r][c].cp = ' '; } } @@ -209,11 +212,13 @@ fb_clear_region(Term *t, u32 r1, u32 r2, u32 c1, u32 c2) static void fb_scroll_down(Term *t, u32 top, u32 n) { + BEGIN_TIMED_BLOCK(); if (!BETWEEN(top, t->top, t->bot)) - return; + goto end; TermView *tv = t->views + t->view_idx; CLAMP(n, 0, t->bot - top + 1); + fb_clear_region(t, t->bot - n + 1, t->bot, 0, t->size.w); for (u32 i = t->bot; i >= top + n; i--) { Row tmp = tv->fb.rows[i]; @@ -221,23 +226,36 @@ fb_scroll_down(Term *t, u32 top, u32 n) tv->fb.rows[i - n] = tmp; } selection_scroll(t, top, n); +end: + END_TIMED_BLOCK(); } static void fb_scroll_up(Term *t, u32 top, u32 n) { + BEGIN_TIMED_BLOCK(); if (!BETWEEN(top, t->top, t->bot)) - return; + goto end; TermView *tv = t->views + t->view_idx; CLAMP(n, 0, t->bot - top + 1); + +#if 0 + size cell_count = (t->bot - n + 1) * t->size.w; + ASSERT(cell_count < tv->fb.cells_count); + mem_move(tv->fb.cells + t->size.w * n, tv->fb.cells, cell_count * sizeof(Cell)); + fb_clear_region(t, t->bot - n + 1, t->bot, 0, t->size.w); +#else fb_clear_region(t, top, top + n - 1, 0, t->size.w); for (u32 i = top; i <= t->bot - n; i++) { Row tmp = tv->fb.rows[i]; tv->fb.rows[i] = tv->fb.rows[i + n]; tv->fb.rows[i + n] = tmp; } +#endif selection_scroll(t, top, -n); +end: + END_TIMED_BLOCK(); } static void @@ -1301,16 +1319,17 @@ push_normal_cp(Term *t, TermView *tv, u32 cp) cursor_move_to(t, t->cursor.pos.y, t->size.w - width); } - Cell *c = &tv->fb.rows[t->cursor.pos.y][t->cursor.pos.x]; + Cell *c = &tv->fb.rows[t->cursor.pos.y][t->cursor.pos.x]; /* TODO: pack cell into ssbo */ - c->cp = cp; - c->style = t->cursor.style; + c->cp = cp; + c->fg = SHADER_PACK_FG(t->cursor.style.fg.rgba, t->cursor.style.attr); + c->bg = SHADER_PACK_BG(t->cursor.style.bg.rgba, t->cursor.style.attr); for (u32 w = width - 1; w; w--) { - c->style.attr |= ATTR_WIDE; + c->bg |= ATTR_WIDE; if (t->cursor.pos.x + w < t->size.w) { Cell *nc = c + w; - nc->style.attr |= ATTR_WDUMMY; + nc->bg |= ATTR_WDUMMY; } } diff --git a/test.c b/test.c @@ -119,10 +119,9 @@ copy_into_ringbuf(RingBuf *rb, s8 raw) static b32 check_cells_equal(Cell *a, Cell *b) { - b32 result = a->cp == b->cp && - a->style.bg.rgba == b->style.bg.rgba && - a->style.fg.rgba == b->style.fg.rgba && - a->style.attr == b->style.attr; + b32 result = a->cp == b->cp && + a->bg == b->bg && + a->fg == b->fg; return result; } @@ -155,16 +154,15 @@ static TEST_FN(csi_embedded_control) dump_csi(&term->csi); #endif - Cell c1 = { .cp = '1', .style = { - .bg = g_colours.data[g_colours.bgidx], - .fg = g_colours.data[g_colours.fgidx], - .attr = (ATTR_NULL), - }}; - Cell c2 = { .cp = '2', .style = { - .bg = (Colour){.r = 75, .g = 63, .b = 42, .a = 0xFF}, - .fg = g_colours.data[3], - .attr = (ATTR_INVISIBLE|ATTR_STRUCK), - }}; + Cell c1 = {.cp = '1', + .bg = SHADER_PACK_BG(g_colours.data[g_colours.bgidx].rgba, ATTR_NULL), + .fg = SHADER_PACK_FG(g_colours.data[g_colours.fgidx].rgba, ATTR_NULL), + }; + u32 attr = (ATTR_INVISIBLE|ATTR_STRUCK); + Cell c2 = {.cp = '2', + .bg = SHADER_PACK_BG(((Colour){.r = 75, .g = 63, .b = 42, .a = 0xFF}.rgba), attr), + .fg = SHADER_PACK_FG(g_colours.data[3].rgba, attr), + }; result.status = term->cursor.pos.y == 1 && term->cursor.pos.x == 3; result.status |= check_cells_equal(&c1, &term->views[term->view_idx].fb.rows[0][0]); result.status |= check_cells_equal(&c2, &term->views[term->view_idx].fb.rows[1][1]); @@ -186,11 +184,11 @@ static TEST_FN(colour_setting) handle_input(term, arena, raw); - Cell c = { .cp = 'A', .style = { - .bg = (Colour){.r = 75, .g = 63, .b = 42, .a = 0xFF}, - .fg = g_colours.data[3], - .attr = (ATTR_INVISIBLE|ATTR_STRUCK), - }}; + u32 attr = (ATTR_INVISIBLE|ATTR_STRUCK); + Cell c = {.cp = 'A', + .bg = SHADER_PACK_BG(((Colour){.r = 75, .g = 63, .b = 42, .a = 0xFF}.rgba), attr), + .fg = SHADER_PACK_FG(g_colours.data[3].rgba, attr), + }; result.status = check_cells_equal(&c, term->views[term->view_idx].fb.rows[0]); return result; diff --git a/util.h b/util.h @@ -119,25 +119,26 @@ enum cell_attribute { ATTR_NULL = 0, ATTR_BOLD = 1 << 0, ATTR_ITALIC = 1 << 1, - ATTR_FAINT = 1 << 2, - ATTR_UNDERLINED = 1 << 3, - ATTR_BLINK = 1 << 4, - ATTR_INVERSE = 1 << 5, - ATTR_INVISIBLE = 1 << 6, - ATTR_STRUCK = 1 << 7, - ATTR_WIDE = 1 << 8, - ATTR_WDUMMY = 1 << 9, /* NOTE: used to skip cells when copying */ + ATTR_WIDE = 1 << 2, + ATTR_WDUMMY = 1 << 3, /* NOTE: used to skip cells when copying */ + ATTR_FAINT = 1 << 4, + ATTR_UNDERLINED = 1 << 5, + ATTR_BLINK = 1 << 6, + ATTR_INVERSE = 1 << 7, + ATTR_INVISIBLE = 1 << 8, + ATTR_STRUCK = 1 << 9, }; #define ATTR_SHADER_MASK (ATTR_FAINT|ATTR_UNDERLINED|ATTR_BLINK|ATTR_INVERSE|ATTR_INVISIBLE|ATTR_STRUCK) +#define ATTR_STYLE_MASK (ATTR_BOLD|ATTR_ITALIC|ATTR_WIDE|ATTR_WDUMMY) -typedef struct { - Colour fg, bg; - u32 attr; -} CellStyle; +#define SHADER_PACK_ATTR(a) (((a) & ATTR_SHADER_MASK) >> 4) +#define SHADER_PACK_FG(c, a) (((c) & 0xFFFFFF00) | SHADER_PACK_ATTR(a)) +#define SHADER_PACK_BG(c, a) (((c) & 0xFFFFFF00) | ((a) & ATTR_STYLE_MASK)) typedef struct { u32 cp; - CellStyle style; + u32 fg; + u32 bg; } Cell; enum cursor_state { @@ -147,6 +148,11 @@ enum cursor_state { }; typedef struct { + Colour fg, bg; + u32 attr; +} CellStyle; + +typedef struct { iv2 pos; CellStyle style; enum cursor_state state; diff --git a/vtgl.c b/vtgl.c @@ -344,12 +344,11 @@ render_framebuffer(Term *t, RenderCell *render_buf) CachedGlyph *cg; rc->gpu_glyph = get_gpu_glyph_index(t->arena_for_frame, &t->gl, &t->fa, - c->cp, 0, c->style.attr & FS_MASK, &cg); + c->cp, 0, c->bg & FS_MASK, &cg); - u32 attr = (c->style.attr & ATTR_SHADER_MASK) >> 2; u32 rmask = (t->gl.mode & WIN_MODE_REVERSE)? REVERSE_VIDEO_MASK : 0; - rc->fg = ((c->style.fg.rgba & 0xFFFFFF00) | attr) ^ rmask; - rc->bg = ((c->style.bg.rgba & 0xFFFFFF00) | attr) ^ rmask; + rc->fg = c->fg ^ rmask; + rc->bg = c->bg ^ rmask; /* TODO: there is probably a better way to do this */ u32 tiles = cg->tile_count; @@ -374,10 +373,10 @@ render_framebuffer(Term *t, RenderCell *render_buf) for (u32 i = curs.x; i < t->size.w; i++) { Cell *c = &tv->fb.rows[curs.y][i]; RenderCell *rc = render_buf + curs.y * t->size.w + i; - rc->fg ^= (ATTR_INVERSE >> 2); - if (c->style.attr & ATTR_WIDE) { - for (u32 j = 1; c[j].style.attr & ATTR_WDUMMY; j++) { - rc[j].fg ^= (ATTR_INVERSE >> 2); + rc->fg ^= SHADER_PACK_ATTR(ATTR_INVERSE); + if (c->bg & ATTR_WIDE) { + for (u32 j = 1; c[j].bg & ATTR_WDUMMY; j++) { + rc[j].fg ^= SHADER_PACK_ATTR(ATTR_INVERSE); i++; } } @@ -388,10 +387,10 @@ render_framebuffer(Term *t, RenderCell *render_buf) for (u32 i = curs.x; i <= end.x; i++) { Cell *c = &tv->fb.rows[curs.y][i]; RenderCell *rc = render_buf + curs.y * t->size.w + i; - rc->fg ^= (ATTR_INVERSE >> 2); - if (c->style.attr & ATTR_WIDE) { - for (u32 j = 1; c[j].style.attr & ATTR_WDUMMY; j++) { - rc[j].fg ^= (ATTR_INVERSE >> 2); + rc->fg ^= SHADER_PACK_ATTR(ATTR_INVERSE); + if (c->bg & ATTR_WIDE) { + for (u32 j = 1; c[j].bg & ATTR_WDUMMY; j++) { + rc[j].fg ^= SHADER_PACK_ATTR(ATTR_INVERSE); i++; } } @@ -403,17 +402,17 @@ render_framebuffer(Term *t, RenderCell *render_buf) iv2 curs = t->cursor.pos; Cell *c = &tv->fb.rows[curs.y][curs.x]; RenderCell *rc = render_buf + curs.y * t->size.w + curs.x; - ASSERT(!(c->style.attr & ATTR_WDUMMY)); + ASSERT(!(c->bg & ATTR_WDUMMY)); - rc->fg ^= (ATTR_INVERSE >> 2); + rc->fg ^= SHADER_PACK_ATTR(ATTR_INVERSE); if ((t->mode & TM_ALTSCREEN) == 0) - rc->fg |= (ATTR_BLINK >> 2); + rc->fg |= SHADER_PACK_ATTR(ATTR_BLINK); - if (c->style.attr & ATTR_WIDE) { - for (u32 j = 1; c[j].style.attr & ATTR_WDUMMY; j++) { - rc[j].fg ^= (ATTR_INVERSE >> 2); + if (c->bg & ATTR_WIDE) { + for (u32 j = 1; c[j].bg & ATTR_WDUMMY; j++) { + rc[j].fg ^= SHADER_PACK_ATTR(ATTR_INVERSE); if ((t->mode & TM_ALTSCREEN) == 0) - rc[j].fg |= (ATTR_BLINK >> 2); + rc[j].fg |= SHADER_PACK_ATTR(ATTR_BLINK); } } } @@ -462,8 +461,8 @@ update_selection(Term *t) sel->last_mouse = mouse; iv2 new_p = mouse_to_cell_space(t, mouse); - if (t->views[t->view_idx].fb.rows[new_p.y][new_p.x].style.attr & ATTR_WDUMMY) { - ASSERT(t->views[t->view_idx].fb.rows[new_p.y][new_p.x - 1].style.attr & ATTR_WIDE); + if (t->views[t->view_idx].fb.rows[new_p.y][new_p.x].bg & ATTR_WDUMMY) { + ASSERT(t->views[t->view_idx].fb.rows[new_p.y][new_p.x - 1].bg & ATTR_WIDE); new_p.x--; } @@ -513,7 +512,7 @@ KEYBIND_FN(copy) for (; curs.y < end.y; curs.y++) { for (; curs.x < t->size.w && buf_curs != buf_size; curs.x++) { Cell c = tv->fb.rows[curs.y][curs.x]; - if (c.style.attr & ATTR_WDUMMY) + if (c.bg & ATTR_WDUMMY) continue; s8 enc = utf8_encode(c.cp); for (size i = 0; i < enc.len && buf_curs != buf_size; i++) @@ -529,7 +528,7 @@ KEYBIND_FN(copy) /* NOTE: do the last row */ for (; curs.x <= end.x && buf_curs != buf_size; curs.x++) { Cell c = tv->fb.rows[curs.y][curs.x]; - if (c.style.attr & ATTR_WDUMMY) + if (c.bg & ATTR_WDUMMY) continue; s8 enc = utf8_encode(c.cp); for (size i = 0; i < enc.len && buf_curs != buf_size; i++) @@ -730,8 +729,8 @@ mouse_button_callback(GLFWwindow *win, i32 btn, i32 act, i32 mod) t->selection.state++; iv2 cell = mouse_to_cell_space(t, t->selection.last_mouse); - if (t->views[t->view_idx].fb.rows[cell.y][cell.x].style.attr & ATTR_WDUMMY) { - ASSERT(t->views[t->view_idx].fb.rows[cell.y][cell.x - 1].style.attr & ATTR_WIDE); + if (t->views[t->view_idx].fb.rows[cell.y][cell.x].bg & ATTR_WDUMMY) { + ASSERT(t->views[t->view_idx].fb.rows[cell.y][cell.x - 1].bg & ATTR_WIDE); cell.x--; }