Commit: afeabe75acbd1a6d5284e085b051930d9fc129f5
Parent: cfe9a297ff93b377d8a78f04f36587d2367d15d2
Author: Randy Palamar
Date: Sun, 23 Jun 2024 17:41:12 -0600
start parsing colour escapes
Diffstat:
M | main.c | | | 7 | +++++-- |
M | terminal.c | | | 146 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
M | util.h | | | 14 | +++++++++++++- |
M | vtgl.c | | | 57 | ++++++++++++++++++++++++++++++++++++++++++++++----------- |
4 files changed, 207 insertions(+), 17 deletions(-)
diff --git a/main.c b/main.c
@@ -233,7 +233,7 @@ check_shaders(GLCtx *gl, Arena a)
}
static void
-line_buf_alloc(LineBuf *lb, Arena *a, u8 *start_position, size capacity)
+line_buf_alloc(LineBuf *lb, Arena *a, u8 *start_position, CursorState state, size capacity)
{
lb->cap = capacity;
lb->filled = 0;
@@ -241,6 +241,7 @@ line_buf_alloc(LineBuf *lb, Arena *a, u8 *start_position, size capacity)
lb->buf = alloc(a, Line, capacity);
lb->buf[0].start = start_position;
lb->buf[0].end = start_position;
+ lb->buf[0].cursor_state = state;
}
i32
@@ -255,8 +256,10 @@ main(void)
init_window(&term);
init_fonts(&term, font_paths, ARRAY_COUNT(font_paths), 48, &memory);
+ cursor_reset(&term);
+
os_alloc_ring_buffer(&term.log, BACKLOG_SIZE);
- line_buf_alloc(&term.log_lines, &memory, term.log.buf, BACKLOG_LINES);
+ line_buf_alloc(&term.log_lines, &memory, term.log.buf, term.cursor.state, BACKLOG_LINES);
term.child = os_fork_child("/bin/sh");
diff --git a/terminal.c b/terminal.c
@@ -1,5 +1,15 @@
#include <immintrin.h>
+#define ESC_ARG_SIZ 6
+
+typedef struct {
+ s8 raw;
+ i32 argv[ESC_ARG_SIZ];
+ i32 argc;
+ u8 mode;
+ u8 priv;
+} CSI;
+
static const u8 utf8overhangmask[32] = {
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
@@ -7,6 +17,17 @@ static const u8 utf8overhangmask[32] = {
0, 0, 0, 0, 0, 0, 0, 0
};
+static v2
+get_cell_size(Term *t)
+{
+ /* TODO: Make padding configurable */
+ v2 result = {
+ .w = t->fa.size.w + 0,
+ .h = t->fa.size.h + 8,
+ };
+ return result;
+}
+
static s8
consume(s8 raw, size count)
{
@@ -51,16 +72,135 @@ feed_line(LineBuf *lb, u8 *position, CursorState cursor_state)
lb->buf[lb->widx].cursor_state = cursor_state;
}
+static void
+cursor_reset(Term *t)
+{
+ t->cursor.state.fg = (Colour){.rgba = 0x1e9e33ff};
+ t->cursor.state.bg = (Colour){.r = 20, .g = 20, .b = 20, .a = 0xff};
+ t->cursor.state.attr = ATTR_NULL;
+}
+
+static void
+dump_csi(CSI *csi)
+{
+ fputs("raw: ESC[", stderr);
+ for (size i = 0; i < csi->raw.len; i++) {
+ u8 c = csi->raw.data[i];
+ if (ISPRINT(c))
+ fputc(c, stderr);
+ else if (c == '\n')
+ fputs("(\\n)", stderr);
+ else if (c == '\r')
+ fputs("(\\r)", stderr);
+ else
+ fprintf(stderr, "(0x%02X)", c);
+ }
+ fprintf(stderr, "\n\tparsed = { .priv = %d, .mode = ", csi->priv);
+ if (ISPRINT(csi->mode))
+ fputc(csi->mode, stderr);
+ else
+ fprintf(stderr, "(0x%02X)", csi->mode);
+ fprintf(stderr, ", .argc = %d, .argv = {", csi->argc);
+ for (i32 i = 0; i < csi->argc; i++)
+ fprintf(stderr, " %d", csi->argv[i]);
+ fputs(" } }\n", stderr);
+}
+
+/* SGR: Select Graphic Rendition */
+static void
+set_colours(Term *t, CSI *csi)
+{
+ CursorState *cs = &t->cursor.state;
+ for (i32 i = 0; i < csi->argc; i++) {
+ switch (csi->argv[i]) {
+ case 0: cursor_reset(t); break;
+ case 1: cs->attr |= ATTR_BOLD; break;
+ case 2: cs->attr |= ATTR_FAINT; break;
+ case 3: cs->attr |= ATTR_ITALIC; break;
+ case 4: cs->attr |= ATTR_UNDERLINED; break;
+ case 5: cs->attr |= ATTR_BLINK; break;
+ case 7: cs->attr |= ATTR_INVERSE; break;
+ case 8: cs->attr |= ATTR_INVISIBLE; break;
+ case 9: cs->attr |= ATTR_STRUCK; break;
+ default:
+ fprintf(stderr, "unhandled colour arg: %d\n", csi->argv[i]);
+ dump_csi(csi);
+ }
+ }
+}
+
+static CSI
+parse_csi(s8 *r)
+{
+ CSI csi = {0};
+ csi.raw.data = r->data;
+
+ if (peek(*r, 0) == '?') {
+ csi.priv = 1;
+ csi.raw.len++;
+ get_ascii(r);
+ }
+
+ while (r->len) {
+ u32 cp = get_ascii(r);
+ csi.raw.len++;
+ if (BETWEEN(cp, '0', '9')) {
+ /* 123 */
+ csi.argv[csi.argc] *= 10;
+ csi.argv[csi.argc] += cp - '0';
+ continue;
+ }
+ csi.argc++;
+ if (cp != ';' || csi.argc == ESC_ARG_SIZ) {
+ if (cp == ';') {
+ csi.mode = get_ascii(r);
+ csi.raw.len++;
+ } else {
+ csi.mode = cp;
+ }
+ return csi;
+ }
+ }
+ /* TODO: error case: needs more chars! */
+ return csi;
+}
+
+static void
+handle_csi(Term *t, s8 *raw)
+{
+ CSI csi = parse_csi(raw);
+
+ switch (csi.mode) {
+ case 'm': set_colours(t, &csi); break;
+ default:
+ fputs("unknown csi: ", stderr);
+ dump_csi(&csi);
+ }
+}
+
enum handle_escape_return {
HESC_NORMAL_RETURN,
HESC_NEEDS_MORE_BYTES,
HESC_CURSOR_MOVED,
};
static enum handle_escape_return
-handle_escape(s8 *raw)
+handle_escape(Term *t, s8 *raw)
{
enum handle_escape_return result = HESC_NORMAL_RETURN;
-
+ u32 cp = get_ascii(raw);
+ switch(cp) {
+ case '[': handle_csi(t, raw); break;
+ case '(': /* GZD4 -- set primary charset G0 */
+ case ')': /* G1D4 -- set secondary charset G1 */
+ case '*': /* G2D4 -- set tertiary charset G2 */
+ case '+': /* G3D4 -- set quaternary charset G3 */
+ case '%': /* utf-8 mode */
+ get_ascii(raw);
+ break;
+ default:
+ //fprintf(stderr, "unknown escape sequence: ESC %c (0x%02x)\n", cp, cp);
+ break;
+ }
return result;
}
@@ -106,7 +246,7 @@ split_raw_input_to_lines(Term *t, s8 raw)
if (peek(raw, 0) == 0x1B) {
s8 old = raw;
raw = consume(raw, 1);
- switch (handle_escape(&raw)) {
+ switch (handle_escape(t, &raw)) {
case HESC_NEEDS_MORE_BYTES:
t->unprocessed_bytes = old.len;
return parsed_lines;
diff --git a/util.h b/util.h
@@ -25,6 +25,8 @@
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
#define CLAMP(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x))
+#define ISPRINT(c) BETWEEN((c), ' ', '~')
+
typedef float f32;
typedef double f64;
typedef uint8_t u8;
@@ -47,6 +49,7 @@ typedef union {
typedef union {
struct { f32 x, y; };
+ struct { f32 w, h; };
f32 E[2];
} v2;
@@ -70,7 +73,15 @@ typedef struct { size len; u8 *data; } s8;
#define s8(s) (s8){.len = ARRAY_COUNT(s) - 1, .data = (u8 *)s}
enum CellAttr {
- ATTR_NULL = 0,
+ 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,
};
typedef struct {
@@ -115,6 +126,7 @@ typedef struct {
enum gl_flags {
NEEDS_RESIZE = 1 << 0,
+ NEEDS_BLIT = 1 << 1,
UPDATE_UNIFORMS = 1 << 30,
};
diff --git a/vtgl.c b/vtgl.c
@@ -141,16 +141,17 @@ draw_rectangle(GLCtx *gl, Rect r, Colour colour)
}
static void
-draw_cell(GLCtx *gl, u32 cp, Rect r, u32 bg, u32 fg)
+push_cell(Term *t, u32 cp, Rect r, u32 bg, u32 fg)
{
+ GLCtx *gl = &t->gl;
Glyph g;
i32 depth_idx = get_gpu_glyph_index(gl, cp, &g);
v2 texscale[2];
texscale[0] = (v2){0};
texscale[1] = (v2){
- .x = g.size.w / 128.0f,
- .y = g.size.h / 128.0f,
+ .x = g.size.w / MAX_FONT_SIZE,
+ .y = g.size.h / MAX_FONT_SIZE,
};
@@ -164,8 +165,8 @@ draw_cell(GLCtx *gl, u32 cp, Rect r, u32 bg, u32 fg)
vertoff[0] = r.pos;
v2 texture_position;
- texture_position.x = (r.size.x - g.size.w) * 0.5 + r.pos.x;// + g.delta.x;
- texture_position.y = (r.size.y - g.size.h) * 0.5 + r.pos.y;// + g.delta.y + t->fa.deltay;
+ texture_position.x = r.pos.x + g.delta.x;
+ texture_position.y = r.pos.y + g.delta.y + t->fa.deltay;
vertscale[1] = (v2){.x = g.size.w, .y = g.size.h};
vertoff[1] = texture_position;
@@ -194,19 +195,53 @@ line_to_s8(Line *l)
}
static void
+push_line(Term *t, Line *line, v2 start_pos)
+{
+ s8 l = line_to_s8(line);
+
+ t->cursor.state = line->cursor_state;
+
+ v2 cs = get_cell_size(t);
+ Rect cr = {.pos = start_pos, .size = cs};
+ while (l.len) {
+ /* TODO: handle unicode case */
+ u32 cp = get_ascii(&l);
+ switch (cp) {
+ case 0x1B: handle_escape(t, &l); break;
+ default:
+ /* TODO properly make characters are printable */
+ CLAMP(cp, ' ', '~');
+ push_cell(t, cp, cr, t->cursor.state.bg.rgba, t->cursor.state.fg.rgba);
+ cr.pos.w += cs.w;
+ /* TODO: properly advance cursor */
+ t->cursor.col++;
+ }
+ }
+ /* TODO: properly advance cursor */
+ t->cursor.row++;
+}
+
+static void
blit_lines(Term *t)
{
- size line_count = 12;
+ size line_count = 16;
CLAMP(line_count, 0, t->log_lines.filled);
-
+ v2 cs = get_cell_size(t);
/* TODO: handle case where widx has wrapped around */
ASSERT(t->log_lines.widx >= line_count);
size line_off = t->log_lines.widx - line_count;
- v2 pos = {.y = t->gl.window_size.h - t->fa.size.h - 8};
+
+ /* TODO: properly set cursor */
+ t->cursor.row = 0;
+
for (size i = 0; i <= line_count; i++) {
- s8 line = line_to_s8(t->log_lines.buf + line_off + i);
- draw_text(&t->gl, line, pos, (Colour){.rgba = 0x1e9e33ff}, 1);
- pos.y -= (t->fa.size.h + 8);
+ /* TODO: properly set cursor */
+ t->cursor.col = 0;
+ v2 pos = {
+ .x = t->cursor.col * cs.w,
+ .y = t->gl.window_size.h - (t->cursor.row + 1) * cs.h
+ };
+ push_line(t, t->log_lines.buf + line_off + i, pos);
}
}