Commit: cfe9a297ff93b377d8a78f04f36587d2367d15d2
Parent: b49aacb829b15c2bb4ffcfee5d42fc818af811ef
Author: Randy Palamar
Date: Sun, 23 Jun 2024 15:55:45 -0600
split incoming data to lines and blit some to screen
Diffstat:
M | build.sh | | | 2 | +- |
M | main.c | | | 18 | ++++++++++++++++-- |
A | terminal.c | | | 139 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | util.h | | | 32 | ++++++++++++++++++++++++++++++++ |
M | vtgl.c | | | 68 | +++++++++++++++++++++++++++++++++----------------------------------- |
5 files changed, 221 insertions(+), 38 deletions(-)
diff --git a/build.sh b/build.sh
@@ -8,7 +8,7 @@ ldflags="$ldflags -lfreetype $(pkg-config --static --libs glfw3 gl)"
# Hot Reloading/Debugging
cflags="$cflags -D_DEBUG -Wno-unused-function"
-libcflags="$cflags -fPIC"
+libcflags="$cflags -fPIC -Wno-unused-function"
libldflags="$ldflags -shared"
cc $libcflags vtgl.c -o vtgl.so $libldflags
diff --git a/main.c b/main.c
@@ -6,7 +6,8 @@
#include "util.h"
-#define BACKLOG_SIZE (16 * MEGABYTE)
+#define BACKLOG_SIZE (16 * MEGABYTE)
+#define BACKLOG_LINES (1024UL)
#ifndef _DEBUG
static void do_debug(GLCtx *gl) { }
@@ -231,6 +232,17 @@ check_shaders(GLCtx *gl, Arena a)
}
}
+static void
+line_buf_alloc(LineBuf *lb, Arena *a, u8 *start_position, size capacity)
+{
+ lb->cap = capacity;
+ lb->filled = 0;
+ lb->widx = 0;
+ lb->buf = alloc(a, Line, capacity);
+ lb->buf[0].start = start_position;
+ lb->buf[0].end = start_position;
+}
+
i32
main(void)
{
@@ -241,9 +253,11 @@ main(void)
"/usr/share/fonts/gofont/Go-Mono.ttf",
};
init_window(&term);
- init_fonts(&term, font_paths, ARRAY_COUNT(font_paths), 64, &memory);
+ init_fonts(&term, font_paths, ARRAY_COUNT(font_paths), 48, &memory);
os_alloc_ring_buffer(&term.log, BACKLOG_SIZE);
+ line_buf_alloc(&term.log_lines, &memory, term.log.buf, BACKLOG_LINES);
+
term.child = os_fork_child("/bin/sh");
do_debug(&term.gl);
diff --git a/terminal.c b/terminal.c
@@ -0,0 +1,139 @@
+#include <immintrin.h>
+
+static const u8 utf8overhangmask[32] = {
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static s8
+consume(s8 raw, size count)
+{
+ raw.data += count;
+ raw.len -= count;
+ return raw;
+}
+
+static u8
+peek(s8 raw, size i)
+{
+ ASSERT(i < raw.len);
+ return raw.data[i];
+}
+
+static u32
+get_ascii(s8 *raw)
+{
+ ASSERT(raw->len > 0);
+ u32 result = raw->data[0];
+ *raw = consume(*raw, 1);
+ return result;
+}
+
+static size
+line_length(Line *l)
+{
+ ASSERT(l->start <= l->end);
+ return l->end - l->start;
+}
+
+static void
+feed_line(LineBuf *lb, u8 *position, CursorState cursor_state)
+{
+ lb->buf[lb->widx++].end = position;
+ lb->widx = lb->widx >= lb->cap ? 0 : lb->widx;
+ lb->filled += lb->filled <= lb->widx;
+
+ lb->buf[lb->widx].start = position;
+ lb->buf[lb->widx].end = position;
+ lb->buf[lb->widx].has_unicode = 0;
+ lb->buf[lb->widx].cursor_state = cursor_state;
+}
+
+enum handle_escape_return {
+ HESC_NORMAL_RETURN,
+ HESC_NEEDS_MORE_BYTES,
+ HESC_CURSOR_MOVED,
+};
+static enum handle_escape_return
+handle_escape(s8 *raw)
+{
+ enum handle_escape_return result = HESC_NORMAL_RETURN;
+
+ return result;
+}
+
+static size
+split_raw_input_to_lines(Term *t, s8 raw)
+{
+ LineBuf *lb = &t->log_lines;
+ size parsed_lines = 0;
+ __m128i nl = _mm_set1_epi8('\n');
+ __m128i esc = _mm_set1_epi8(0x1B);
+ __m128i uni = _mm_set1_epi8(0x80);
+
+ #define SPLIT_LONG 4096
+ while (raw.len) {
+ __m128i hasutf8 = _mm_setzero_si128();
+ size count = raw.len > SPLIT_LONG ? SPLIT_LONG : raw.len;
+ u8 *data = raw.data;
+ while (count >= 16) {
+ __m128i vdat = _mm_loadu_si128((__m128i_u *)data);
+ __m128i hasnl = _mm_cmpeq_epi8(vdat, nl);
+ __m128i hasesc = _mm_cmpeq_epi8(vdat, esc);
+ __m128i hasuni = _mm_and_si128(vdat, uni);
+ __m128i hasproc = _mm_or_si128(hasuni, _mm_or_si128(hasnl, hasesc));
+ i32 needsproc = _mm_movemask_epi8(hasproc);
+
+ if (needsproc) {
+ u32 advance = _tzcnt_u32(needsproc);
+ __m128i utf8mask = _mm_loadu_si128((__m128i_u *)(utf8overhangmask + 16 - advance));
+ hasuni = _mm_and_si128(utf8mask, hasuni);
+ hasutf8 = _mm_or_si128(hasutf8, hasuni);
+ count -= advance;
+ data += advance;
+ break;
+ }
+
+ hasutf8 = _mm_or_si128(hasutf8, hasuni);
+ count -= 16;
+ data += 16;
+ }
+ lb->buf[lb->widx].has_unicode |= _mm_movemask_epi8(hasutf8);
+ raw = consume(raw, data - raw.data);
+
+ if (peek(raw, 0) == 0x1B) {
+ s8 old = raw;
+ raw = consume(raw, 1);
+ switch (handle_escape(&raw)) {
+ case HESC_NEEDS_MORE_BYTES:
+ t->unprocessed_bytes = old.len;
+ return parsed_lines;
+ case HESC_CURSOR_MOVED:
+ parsed_lines++;
+ feed_line(lb, old.data, t->cursor.state);
+ break;
+ default: break;
+ }
+ } else {
+ u32 c = get_ascii(&raw);
+ if (c == '\n') {
+ parsed_lines++;
+ feed_line(lb, raw.data, t->cursor.state);
+ } else if (c & 0x80) {
+ lb->buf[lb->widx].has_unicode = 1;
+ }
+ }
+
+ lb->buf[lb->widx].end = raw.data;
+ //if (tv->lines.buf[tv->lines.widx].end < tv->lines.buf[tv->lines.widx].start)
+ // tv->lines.buf[tv->lines.widx].end += tv->log.cap;
+ if (line_length(lb->buf + lb->widx) > SPLIT_LONG) {
+ parsed_lines++;
+ feed_line(lb, raw.data, t->cursor.state);
+ }
+ }
+ t->unprocessed_bytes = 0;
+ return parsed_lines;
+}
diff --git a/util.h b/util.h
@@ -69,6 +69,20 @@ typedef struct { u8 *beg, *end; } Arena;
typedef struct { size len; u8 *data; } s8;
#define s8(s) (s8){.len = ARRAY_COUNT(s) - 1, .data = (u8 *)s}
+enum CellAttr {
+ ATTR_NULL = 0,
+};
+
+typedef struct {
+ Colour fg, bg;
+ enum CellAttr attr;
+} CursorState;
+
+typedef struct {
+ u32 row, col;
+ CursorState state;
+} Cursor;
+
/* NOTE: virtual memory ring buffer */
typedef struct {
size cap;
@@ -77,6 +91,19 @@ typedef struct {
u8 *buf;
} RingBuf;
+typedef struct {
+ u8 *start, *end;
+ b32 has_unicode;
+ CursorState cursor_state;
+} Line;
+
+typedef struct {
+ size cap;
+ size filled;
+ size widx;
+ Line *buf;
+} LineBuf;
+
#define GL_UNIFORMS \
X(Pmat) \
X(charmap) \
@@ -149,12 +176,17 @@ typedef struct {
FontAtlas fa;
RingBuf log;
+ LineBuf log_lines;
+ size unprocessed_bytes;
os_child child;
+ Cursor cursor;
+
FT_Library ftlib;
} Term;
#include "font.c"
+#include "terminal.c"
#endif /* _UTIL_H_ */
diff --git a/vtgl.c b/vtgl.c
@@ -185,6 +185,30 @@ draw_cell(GLCtx *gl, u32 cp, Rect r, u32 bg, u32 fg)
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, 2);
}
+static s8
+line_to_s8(Line *l)
+{
+ ASSERT(l->start <= l->end);
+ s8 result = {.len = l->end - l->start, .data = l->start};
+ return result;
+}
+
+static void
+blit_lines(Term *t)
+{
+ size line_count = 12;
+ CLAMP(line_count, 0, t->log_lines.filled);
+
+ /* 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};
+ 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);
+ }
+}
/* NOTE: called when the window was resized */
static void
@@ -237,42 +261,16 @@ do_terminal(Term *t, Arena a)
if (os_child_data_available(t->child)) {
if (os_child_exited(t->child))
glfwSetWindowShouldClose(t->gl.window, GL_TRUE);
- os_read_from_child(t->child, &t->log);
- }
- /* TODO: actually parse this data */
- s8 text = {.len = t->log.widx, .data = t->log.buf};
- v2 cpos = { .x = 0, .y = t->gl.window_size.h/2 };
- draw_text(&t->gl, text, cpos, (Colour){.rgba = 0x1e9e33ff}, 1);
-
- Rect r1 = {.pos = {.x = 20, .y = 20}, .size = {.x = 200, .y = 100}};
- Rect r3 = {.pos = {.x = 100, .y = 600}, .size = {.x = 50, .y = 100}};
- static Rect r2 = {.pos = {.x = 50, .y = 300}, .size = {.x = 100, .y = 100}};
- static v2 r2dir = {.x = 1, .y = -1 };
-
- r2.pos.x += r2dir.x * t->gl.dt * 100;
- r2.pos.y += r2dir.y * t->gl.dt * 140;
- if (r2.pos.x + r2.size.x > (f32)t->gl.window_size.w) {
- r2.pos.x = (f32)t->gl.window_size.w - r2.size.x;
- r2dir.x *= -1;
- }
- if (r2.pos.x < 0) {
- r2.pos.x = 0;
- r2dir.x *= -1;
- }
-
- if (r2.pos.y + r2.size.y > (f32)t->gl.window_size.h) {
- r2.pos.y = (f32)t->gl.window_size.h - r2.size.y;
- r2dir.y *= -1;
- }
-
- if (r2.pos.y < 0) {
- r2.pos.y = 0;
- r2dir.y *= -1;
+ t->unprocessed_bytes += os_read_from_child(t->child, &t->log);
+ s8 raw = {
+ .len = t->unprocessed_bytes,
+ .data = t->log.buf + (t->log.widx - t->unprocessed_bytes)
+ };
+ size parsed_lines = split_raw_input_to_lines(t, raw);
+ /* TODO: think about only blitting update lines? */
+ (void)parsed_lines;
}
-
- draw_rectangle(&t->gl, r1, (Colour){.rgba = 0x7f0000ff});
- draw_cell(&t->gl, 'E', r2, 0x007f00ff, 0x000000ff);
- draw_rectangle(&t->gl, r3, (Colour){.rgba = 0x00007fff});
+ blit_lines(t);
{
s8 fps = s8alloc(&a, 64);