vtgl

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

Commit: 400dd63d5a02cf6426bc56279de1017eb4cc017a
Parent: 21b5b3ee81f2061fa0184037cfc9768c560613e2
Author: Randy Palamar
Date:   Mon,  8 Jul 2024 20:54:46 -0600

add initial test program and fix 1 bug

On initial start up the framebuffer would only get allocated if
the window resizes. This is fine in my setup with a tiling wm but
it would have immediately crashed in a floating wm.

Diffstat:
Mbuild.sh | 5+++++
Mmain.c | 15---------------
Atest.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mutil.c | 12++++++++++++
Mutil.h | 8++++++++
Mvtgl.c | 7++++++-
6 files changed, 125 insertions(+), 16 deletions(-)

diff --git a/build.sh b/build.sh @@ -12,7 +12,12 @@ cflags="$cflags -D_DEBUG -Wno-unused-function -Wno-undefined-internal" libcflags="$cflags -fPIC -Wno-unused-function" libldflags="$ldflags -shared" +testcflags="-march=native -ggdb -O0 -Wall $(pkg-config --cflags freetype2)" +testcflags="$testcflags -D_DEBUG -Wno-unused-function -Wno-undefined-internal" +testldflags="-lfreetype" + if ! [ -s "./config.h" ]; then cp ./config.def.h ./config.h; fi cc $libcflags vtgl.c -o vtgl.so $libldflags cc $cflags -o vtgl main.c $ldflags +cc $testcflags -o test test.c $testldflags diff --git a/main.c b/main.c @@ -6,9 +6,6 @@ #include "util.h" -#define BACKLOG_SIZE (16 * MEGABYTE) -#define BACKLOG_LINES (1024UL) - #ifndef _DEBUG static void do_debug(GLCtx *gl) { } #include "vtgl.c" @@ -264,18 +261,6 @@ check_shaders(GLCtx *gl, Arena a) } } -static void -line_buf_alloc(LineBuf *lb, Arena *a, u8 *start_position, CellStyle state, 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; - lb->buf[0].cursor_state = state; -} - i32 main(void) { diff --git a/test.c b/test.c @@ -0,0 +1,94 @@ +/* NOTE: stubs for stuff we aren't testing */ +typedef void *GLFWwindow; +static void glfwSetWindowTitle(GLFWwindow *w, const char *s) {}; +static const char *glfwGetWindowTitle(GLFWwindow *w) { return "test"; }; + +#include "util.h" + +#define ESC(a) "\x1B"#a +#define CSI(a) ESC([a) + +struct test_result { b32 status; const char *info; }; +#define TEST_FN(name) struct test_result name(Term *term, Arena arena) +typedef TEST_FN(Test_Fn); + +static size +copy_into_ringbuf(RingBuf *rb, s8 raw) +{ + ASSERT(raw.len < rb->cap); + for (size i = 0; i < raw.len; i++) + rb->buf[rb->widx + i] = raw.data[i]; + + rb->widx += raw.len; + rb->filled += raw.len; + + CLAMP(rb->filled, 0, rb->cap); + if (rb->widx >= rb->cap) + rb->widx -= rb->cap; + + ASSERT(rb->filled >= 0); + ASSERT(rb->widx >= 0 && rb->widx < rb->cap); + return raw.len; +} + +static s8 +launder_static_string(Term *term, s8 static_str) +{ + term->unprocessed_bytes += copy_into_ringbuf(&term->log, static_str); + s8 raw = { + .len = term->unprocessed_bytes, + .data = term->log.buf + (term->log.widx - term->unprocessed_bytes) + }; + return raw; +} + +static TEST_FN(test_cursor_movement) +{ + struct test_result result = {.info = __FUNCTION__}; + + s8 raw = launder_static_string(term, s8(CSI(17;2H))); + split_raw_input_to_lines(term, raw); + blit_lines(term, arena); + + raw = launder_static_string(term, s8(CSI(7G))); + split_raw_input_to_lines(term, raw); + blit_lines(term, arena); + + result.status = term->cursor.row == 16 && term->cursor.col == 6; + + return result; +} + +static Test_Fn *tests[] = { + test_cursor_movement, +}; + +static u32 failure_count; + +int +main(void) +{ + Arena memory = os_new_arena(16 * MEGABYTE); + Term term = {0}; + + cursor_reset(&term); + + os_alloc_ring_buffer(&term.log, BACKLOG_SIZE); + line_buf_alloc(&term.log_lines, &memory, term.log.buf, term.cursor.state, BACKLOG_LINES); + + /* TODO: should probably be some odd size */ + term.size = (uv2){.w = 80, .h = 24}; + os_alloc_framebuffer(&term.fb, term.size.h, term.size.w); + + for (u32 i = 0; i < ARRAY_COUNT(tests); i++) { + cursor_reset(&term); + struct test_result result = tests[i](&term, memory); + if (result.status == 0) { + failure_count++; + printf("TEST FAILED: [%u/%lu] %s\n", i, ARRAY_COUNT(tests), result.info); + } + } + printf("FINISHED: [%lu/%lu] Succeeded\n", ARRAY_COUNT(tests) - failure_count, + ARRAY_COUNT(tests)); + return 0; +} diff --git a/util.c b/util.c @@ -38,6 +38,18 @@ alloc_(Arena *a, size len, size align, size count) return mem_clear(p, 0, count * len); } +static void +line_buf_alloc(LineBuf *lb, Arena *a, u8 *start_position, CellStyle state, 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; + lb->buf[0].cursor_state = state; +} + static s8 s8alloc(Arena *a, size len) { diff --git a/util.h b/util.h @@ -5,6 +5,10 @@ #include <stdint.h> #include <stddef.h> +#ifndef asm +#define asm __asm__ +#endif + #ifdef _DEBUG #define ASSERT(c) do { if (!(c)) asm("int3; nop"); } while(0) #define DEBUG_EXPORT @@ -30,6 +34,9 @@ /* NOTE: GLFW does not sequentially number keys so switch statement will never be optimized */ #define ENCODE_KEY(action, mod, key) (((action) << 24) | ((mod) << 16) | ((key) & 0xFFFF)) +#define BACKLOG_SIZE (16 * MEGABYTE) +#define BACKLOG_LINES (1024UL) + typedef float f32; typedef double f64; typedef uint8_t u8; @@ -158,6 +165,7 @@ typedef struct { X(vertscale) enum gl_flags { + NEEDS_RESIZE = 1 << 0, UPDATE_RENDER_UNIFORMS = 1 << 29, UPDATE_POST_UNIFORMS = 1 << 30, }; diff --git a/vtgl.c b/vtgl.c @@ -58,6 +58,7 @@ resize(Term *t) v2 ws = t->gl.window_size; os_alloc_framebuffer(&t->fb, t->size.h, t->size.w); os_set_term_size(t->child, t->size.h, t->size.w, ws.w, ws.h); + t->gl.flags &= ~NEEDS_RESIZE; } static void @@ -289,7 +290,7 @@ fb_callback(GLFWwindow *win, i32 w, i32 h) set_projection_matrix(&t->gl); - resize(t); + t->gl.flags |= NEEDS_RESIZE; } static void @@ -376,6 +377,10 @@ do_terminal(Term *t, Arena a) { static f32 last_frame_time; f32 frame_start_time = (f32)glfwGetTime(); + + if (t->gl.flags & NEEDS_RESIZE) + resize(t); + if (t->gl.flags & UPDATE_RENDER_UNIFORMS) update_uniforms(t, SHADER_RENDER); if (t->gl.flags & UPDATE_POST_UNIFORMS)