Commit: 40937aa3f7a2b6f9175480bdb5df5541fc13bf5b
Parent: 7f3c9e87be9ee8366bddcb10efa7cb6119a3bbd8
Author: Randy Palamar
Date: Tue, 29 Oct 2024 06:42:56 -0600
remove stdio usage
Diffstat:
M | debug.c | | | 80 | +++++++++++++++++++++++++++++++++++++------------------------------------------ |
M | debug.h | | | 1 | + |
M | main.c | | | 114 | ++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------- |
M | os_unix.c | | | 49 | ++++++++++++++++++++++++++++++++++++++++++++++--- |
M | terminal.c | | | 157 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
M | test.c | | | 23 | +++++++++++++++++++---- |
M | util.c | | | 133 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | util.h | | | 18 | ++++++++++++++++++ |
M | vtgl.c | | | 48 | ++++++++++++++++++++++++++++++------------------ |
9 files changed, 459 insertions(+), 164 deletions(-)
diff --git a/debug.c b/debug.c
@@ -33,63 +33,57 @@ simulate_line(Term *t, Line *line)
}
static void
-fput_cursor_info(FILE *f, Cursor c)
-{
- fprintf(f, "\tFG: 0x%08x\n", c.style.fg.rgba);
- fprintf(f, "\tBG: 0x%08x\n", c.style.bg.rgba);
- fprintf(f, "\tAttr: 0x%08x\n", c.style.attr);
- fprintf(f, "\tPos: {%d, %d}\n", c.pos.y, c.pos.x);
-}
-
-static void
-fput_line_info(FILE *f, Term *t, Line *l)
-{
- Cursor start = t->cursor;
- start.style = l->cursor_state;
- fprintf(f, "Line Info:\n");
- fprintf(f, "\tLength: %ld\n", line_length(l));
- fprintf(f, "\tHas Unicode: %d\n", l->has_unicode);
- fput_cursor_info(f, start);
- Cursor end = simulate_line(t, l);
- fprintf(f, "After Line Cursor State:\n");
- fput_cursor_info(f, end);
-}
-
-static void
dump_lines_to_file(Term *t)
{
- char buf[256];
+ u8 buf[256];
+ Stream fname = {.cap = sizeof(buf), .buf = buf};
u64 current_time = os_get_time();
- snprintf(buf, sizeof(buf), "%zu-lines.bin", current_time);
+ stream_push_u64(&fname, current_time);
+ stream_push_s8(&fname, s8("-lines.bin\0"));
- FILE *f = fopen(buf, "w");
- if (!f) return;
+ iptr file = os_open(buf, FA_WRITE);
+ if (file == INVALID_FILE) return;
- printf("dumping lines to %s\n", buf);
+ fname.buf[fname.widx - 1] = '\n';
+ os_write_err_msg(s8("dumping lines to "));
+ os_write_err_msg(stream_to_s8(&fname));
TermView *tv = t->views + t->view_idx;
size line_count = MIN(256, tv->lines.filled);
- fputs("Term Info:\n", f);
- fprintf(f, " Size: %ux%u\n", t->size.w, t->size.h);
- fprintf(f, " Window Size: %ux%u\n", (u32)t->gl.window_size.w, (u32)t->gl.window_size.h);
- fprintf(f, " Mode: 0x%X\n", t->mode);
- fprintf(f, " Window Mode: 0x%X\n", t->gl.mode);
- fprintf(f, " Scroll Region:\n");
- fprintf(f, " Top: %u\n", t->top);
- fprintf(f, " Bottom: %u\n", t->bot);
- fprintf(f, " Offset: %d\n", t->scroll_offset);
- fprintf(f, "Raw Line Count: %u\n", (u32)line_count);
- fputs("==============================\n", f);
-
+ Arena temp_arena = t->arena_for_frame;
+ Stream out = stream_alloc(&temp_arena, 1 * MEGABYTE);
+
+ iv2 tsize = {.w = t->size.w, .h = t->size.h};
+ iv2 wsize = {.w = t->gl.window_size.w, .h = t->gl.window_size.h};
+ stream_push_s8(&out, s8("Term Info:"));
+ stream_push_s8(&out, s8("\n Size: ")); stream_push_iv2(&out, tsize);
+ stream_push_s8(&out, s8("\n Window Size: ")); stream_push_iv2(&out, wsize);
+ stream_push_s8(&out, s8("\n Mode: 0x")); stream_push_hex_u64(&out, t->mode);
+ stream_push_s8(&out, s8("\n Window Mode: 0x")); stream_push_hex_u64(&out, t->gl.mode);
+ stream_push_s8(&out, s8("\n Scroll Region:"));
+ stream_push_s8(&out, s8("\n Top: ")); stream_push_u64(&out, t->top);
+ stream_push_s8(&out, s8("\n Bottom: ")); stream_push_u64(&out, t->bot);
+ stream_push_s8(&out, s8("\n Offset: ")); stream_push_i64(&out, t->scroll_offset);
+ stream_push_s8(&out, s8("\nRaw Line Count: ")); stream_push_u64(&out, (u32)line_count);
+ stream_push_s8(&out, s8("\n==============================\n"));
+
+ size file_offset = 0;
for (size i = -(line_count - 1); i <= 0; i++) {
Line *line = tv->lines.buf + get_line_idx(&tv->lines, i);
s8 l = line_to_s8(line, &tv->log);
- fwrite(l.data, 1, l.len, f);
+ stream_push_s8(&out, l);
+ if (out.errors) {
+ os_write(file, stream_to_s8(&out), file_offset);
+ file_offset += out.widx;
+ out.widx = 0;
+ stream_push_s8(&out, l);
+ }
}
- fputc('\n', f);
+ stream_push_byte(&out, '\n');
- fclose(f);
+ os_write(file, stream_to_s8(&out), file_offset);
+ os_close(file);
}
static void
diff --git a/debug.h b/debug.h
@@ -11,6 +11,7 @@
#else
#define ASSERT(c) do { if (!(c)) asm("int3; nop"); } while(0)
+#define INVALID_CODE_PATH ASSERT(0)
#define DEBUG_EXPORT
typedef struct {
diff --git a/main.c b/main.c
@@ -28,27 +28,28 @@ typedef iv2 init_term_fn(Term *, Arena *, iv2);
static init_term_fn *init_term;
static void
-load_library(const char *lib)
+load_library(const char *lib, Stream *err)
{
+ s8 nl = s8("\n");
/* NOTE: glibc sucks and will crash if this is NULL */
if (libhandle)
dlclose(libhandle);
libhandle = dlopen(lib, RTLD_NOW|RTLD_LOCAL);
if (!libhandle)
- fprintf(stderr, "do_debug: dlopen: %s\n", dlerror());
+ stream_push_s8s(err, 3, (s8 []){s8("do_debug: dlopen: "), c_str_to_s8(dlerror()), nl});
do_terminal = dlsym(libhandle, "do_terminal");
if (!do_terminal)
- fprintf(stderr, "do_debug: dlsym: %s\n", dlerror());
+ stream_push_s8s(err, 3, (s8 []){s8("do_debug: dlsym: "), c_str_to_s8(dlerror()), nl});
init_callbacks = dlsym(libhandle, "init_callbacks");
if (!init_callbacks)
- fprintf(stderr, "do_debug: dlsym: %s\n", dlerror());
+ stream_push_s8s(err, 3, (s8 []){s8("do_debug: dlsym: "), c_str_to_s8(dlerror()), nl});
init_term = dlsym(libhandle, "init_term");
if (!init_term)
- fprintf(stderr, "do_debug: dlsym: %s\n", dlerror());
+ stream_push_s8s(err, 3, (s8 []){s8("do_debug: dlsym: "), c_str_to_s8(dlerror()), nl});
}
static void
-do_debug(GLCtx *gl)
+do_debug(GLCtx *gl, Stream *err)
{
static os_file_stats updated;
os_file_stats test = os_get_file_stats(libname);
@@ -58,11 +59,16 @@ do_debug(GLCtx *gl)
/* NOTE: sucks but seems to be easiest reliable way to make sure lib is written */
struct timespec sleep_time = { .tv_nsec = 100e6 };
nanosleep(&sleep_time, &sleep_time);
- load_library(libname);
+ load_library(libname, err);
if (gl) {
init_callbacks(gl);
}
- os_write_err_msg(s8("Reloaded Main Program\n"));
+ stream_push_s8(err, s8("Reloaded Main Program\n"));
+ }
+
+ if (err->widx) {
+ os_write_err_msg(stream_to_s8(err));
+ err->widx = 0;
}
}
@@ -71,24 +77,34 @@ do_debug(GLCtx *gl)
static void
gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, i32 len, const char *msg, const void *data)
{
- (void)src; (void)type; (void)id; (void)data;
- os_write_err_msg(s8("[GL Error "));
+ (void)src; (void)type; (void)id;
+ Stream *err = (Stream *)data;
+ stream_push_s8(err, s8("[GL Error "));
switch (lvl) {
- case GL_DEBUG_SEVERITY_HIGH: os_write_err_msg(s8("HIGH]: ")); break;
- case GL_DEBUG_SEVERITY_MEDIUM: os_write_err_msg(s8("MEDIUM]: ")); break;
- case GL_DEBUG_SEVERITY_LOW: os_write_err_msg(s8("LOW]: ")); break;
- case GL_DEBUG_SEVERITY_NOTIFICATION: os_write_err_msg(s8("NOTIFICATION]: ")); break;
- default: os_write_err_msg(s8("INVALID]: ")); break;
+ case GL_DEBUG_SEVERITY_HIGH: stream_push_s8(err, s8("HIGH]: ")); break;
+ case GL_DEBUG_SEVERITY_MEDIUM: stream_push_s8(err, s8("MEDIUM]: ")); break;
+ case GL_DEBUG_SEVERITY_LOW: stream_push_s8(err, s8("LOW]: ")); break;
+ case GL_DEBUG_SEVERITY_NOTIFICATION: stream_push_s8(err, s8("NOTIFICATION]: ")); break;
+ default: stream_push_s8(err, s8("INVALID]: ")); break;
}
- os_write_err_msg((s8){.len = len, .data = (u8 *)msg});
- os_write_err_msg(s8("\n"));
+ stream_push_s8(err, (s8){.len = len, .data = (u8 *)msg});
+ stream_push_byte(err, '\n');
+ os_write_err_msg(stream_to_s8(err));
+ err->widx = 0;
}
static void
error_callback(int code, const char *desc)
{
- fprintf(stderr, "GLFW Error (0x%04X): %s\n", code, desc);
+ u8 buf[256];
+ Stream err = {.cap = sizeof(buf), .buf = buf};
+ stream_push_s8(&err, s8("GLFW Error (0x"));
+ stream_push_hex_u64(&err, code);
+ stream_push_s8(&err, s8("): "));
+ os_write_err_msg(stream_to_s8(&err));
+ os_write_err_msg(c_str_to_s8((char *)desc));
+ os_write_err_msg(s8("\n"));
}
static u32
@@ -262,7 +278,7 @@ program_from_shader_text(s8 vertex, s8 fragment, Arena a)
}
static void
-check_shaders(GLCtx *gl, Arena a)
+check_shaders(GLCtx *gl, Arena a, Stream *err)
{
static char *fs_name[SHADER_LAST] = {
"frag_render.glsl",
@@ -277,12 +293,12 @@ check_shaders(GLCtx *gl, Arena a)
static os_file_stats fs_stats[SHADER_LAST], vs_stats[SHADER_LAST];
static struct {
- char *name;
- u32 flag;
+ s8 name;
+ u32 flag;
} map[SHADER_LAST] = {
- [SHADER_RENDER] = {.name = "Render", .flag = 0},
- [SHADER_RECTS] = {.name = "Rects", .flag = 0},
- [SHADER_POST] = {.name = "Post", .flag = UPDATE_POST_UNIFORMS},
+ [SHADER_RENDER] = {.name = s8("Render"), .flag = 0},
+ [SHADER_RECTS] = {.name = s8("Rects"), .flag = 0},
+ [SHADER_POST] = {.name = s8("Post"), .flag = UPDATE_POST_UNIFORMS},
};
for (u32 i = 0; i < SHADER_LAST; i++) {
@@ -293,7 +309,7 @@ check_shaders(GLCtx *gl, Arena a)
!os_filetime_changed(vs_test.timestamp, vs_stats[i].timestamp))
continue;
- fprintf(stderr, "Reloading %s Shader!\n", map[i].name);
+ stream_push_s8s(err, 3, (s8 []){s8("Reloading "), map[i].name, s8(" Shader!\n")});
fs_stats[i] = fs_test;
vs_stats[i] = vs_test;
@@ -307,24 +323,32 @@ check_shaders(GLCtx *gl, Arena a)
glDeleteProgram(gl->programs[i]);
gl->programs[i] = program;
gl->flags |= map[i].flag;
- fprintf(stderr, "%s Program Updated!\n", map[i].name);
+ stream_push_s8s(err, 2, (s8 []){map[i].name, s8(" Program Updated!\n")});
+ }
+
+ if (err->widx) {
+ os_write_err_msg(stream_to_s8(err));
+ err->widx = 0;
}
}
static void
-usage(char *argv0)
+usage(char *argv0, Stream *err)
{
- os_write_err_msg(s8("usage: "));
- os_write_err_msg(c_str_to_s8(argv0));
- os_fatal(s8(" [-v] [-g COLxROW]\n"));
+ stream_push_s8(err, s8("usage: "));
+ stream_push_s8(err, c_str_to_s8(argv0));
+ stream_push_s8(err, s8(" [-v] [-g COLxROW]\n"));
+ os_fatal(stream_to_s8(err));
}
i32
-main(i32 argc, char *argv[])
+main(i32 argc, char *argv[], char *envp[])
{
Arena memory = os_new_arena(16 * MEGABYTE);
Term term = {0};
+ term.error_stream = stream_alloc(&memory, MEGABYTE / 4);
+
iv2 cells = {.x = -1, .y = -1};
char *argv0 = *argv++;
@@ -333,34 +357,42 @@ main(i32 argc, char *argv[])
for (i32 i = 0; i < argc; i++) {
char *arg = argv[i];
if (!arg || !arg[0])
- usage(argv0);
+ usage(argv0, &term.error_stream);
if (arg[0] != '-')
break;
arg++;
switch (arg[0]) {
case 'g':
if (!argv[i + 1])
- usage(argv0);
+ usage(argv0, &term.error_stream);
cres = i32_from_cstr(argv[i + 1], 'x');
if (cres.status == CR_SUCCESS)
cells.w = cres.i;
cres = i32_from_cstr(cres.unparsed, 0);
if (cres.status == CR_SUCCESS)
cells.h = cres.i;
- if (cells.w <= 0 || cells.h <= 0)
- fprintf(stderr, "ignoring malformed geometry: %s\n", argv[i + 1]);
+ if (cells.w <= 0 || cells.h <= 0) {
+ stream_push_s8(&term.error_stream, s8("ignoring malformed geometry: "));
+ stream_push_s8(&term.error_stream, c_str_to_s8(argv[i + 1]));
+ stream_push_byte(&term.error_stream, '\n');
+ }
argv++;
argc--;
break;
case 'v':
- os_write_err_msg(c_str_to_s8(argv0));
- os_fatal(s8(" " VERSION "\n"));
+ stream_push_s8s(&term.error_stream, 2,
+ (s8 []){c_str_to_s8(argv0), s8(" " VERSION "\n")});
+ os_fatal(stream_to_s8(&term.error_stream));
default:
- usage(argv0);
+ usage(argv0, &term.error_stream);
}
}
+ if (term.error_stream.widx) {
+ os_write_err_msg(stream_to_s8(&term.error_stream));
+ term.error_stream.widx = 0;
+ }
- do_debug(NULL);
+ do_debug(NULL, &term.error_stream);
if (!glfwInit())
os_fatal(s8("Failed to init GLFW\n"));
@@ -397,8 +429,8 @@ main(i32 argc, char *argv[])
f32 last_time = 0;
while (!glfwWindowShouldClose(term.gl.window)) {
- do_debug(&term.gl);
- check_shaders(&term.gl, memory);
+ do_debug(&term.gl, &term.error_stream);
+ check_shaders(&term.gl, memory, &term.error_stream);
f32 current_time = (f32)glfwGetTime();
f32 dt = current_time - last_time;
diff --git a/os_unix.c b/os_unix.c
@@ -2,7 +2,6 @@
#include <fcntl.h>
#include <pty.h>
#include <pwd.h>
-#include <stdio.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/stat.h>
@@ -79,6 +78,46 @@ os_filetime_changed(os_filetime a, os_filetime b)
return ((a.tv_sec - b.tv_sec) + (a.tv_nsec - b.tv_nsec)) != 0;
}
+static u32
+os_file_attribute_to_mode(u32 attr)
+{
+ u32 result = O_CREAT;
+ if (attr & FA_READ && attr & FA_WRITE) {
+ result |= O_RDWR;
+ } else if (attr & FA_READ) {
+ result |= O_RDONLY;
+ } else if (attr & FA_WRITE) {
+ result |= O_WRONLY;
+ }
+
+ if (attr & FA_APPEND)
+ result |= O_APPEND;
+
+ return result;
+}
+
+static iptr
+os_open(u8 *name, u32 attr)
+{
+ iptr result = open((char *)name, os_file_attribute_to_mode(attr), 0660);
+ if (result < 0)
+ result = INVALID_FILE;
+ return result;
+}
+
+static b32
+os_write(iptr file, s8 raw, size offset)
+{
+ size result = pwrite(file, raw.data, raw.len, offset);
+ return result == raw.len;
+}
+
+static void
+os_close(iptr file)
+{
+ close(file);
+}
+
static s8
os_read_file(Arena *a, char *name, size filesize)
{
@@ -148,8 +187,12 @@ os_alloc_ring_buffer(RingBuf *rb, size capacity)
void *ret = mmap(rb->buf + i * rb->cap, rb->cap, PROT_READ|PROT_WRITE,
MAP_FIXED|MAP_SHARED, fd, 0);
if (ret == MAP_FAILED) {
- fprintf(stderr, "os_alloc_ring_buffer: mmap(%d) failed\n", i);
- _exit(1);
+ u8 buf[256];
+ Stream err = {.buf = buf, .cap = sizeof(buf)};
+ stream_push_s8(&err, s8("os_alloc_ring_buffer: mmap("));
+ stream_push_u64(&err, i);
+ stream_push_s8(&err, s8(") failed\n"));
+ os_fatal(stream_to_s8(&err));
}
}
close(fd);
diff --git a/terminal.c b/terminal.c
@@ -365,31 +365,42 @@ term_reset(Term *t)
}
static void
-dump_csi(CSI *csi)
+dump_csi(CSI *csi, Stream *err)
{
- os_write_err_msg(s8("raw: ESC["));
+ stream_push_s8(err, s8("raw: ESC["));
for (size i = 0; i < csi->raw.len; i++) {
u8 c = csi->raw.data[i];
- if (ISPRINT(c))
- os_write_err_msg((s8){.len = 1, .data = csi->raw.data + i});
- else if (c == '\n')
- os_write_err_msg(s8("\\n"));
- else if (c == '\r')
- os_write_err_msg(s8("\\r"));
- else
- fprintf(stderr, "\\x%02X", c);
+ if (ISPRINT(c)) {
+ stream_push_byte(err, csi->raw.data[i]);
+ } else if (c == '\n') {
+ stream_push_s8(err, s8("\\n"));
+ } else if (c == '\r') {
+ stream_push_s8(err, s8("\\r"));
+ } else {
+ stream_push_s8(err, s8("\\x"));
+ stream_push_hex_u64(err, c);
+ }
}
- fprintf(stderr, "\n\tparsed = { .priv = %d, .mode = ", csi->priv);
+ stream_push_s8(err, s8("\n\tparsed = { .priv = "));
+ stream_push_u64(err, csi->priv);
+ stream_push_s8(err, s8(" .mode = "));
if (ISPRINT(csi->mode)) {
- u8 buf[1] = {csi->mode};
- os_write_err_msg((s8){.len = 1, .data = buf});
+ stream_push_byte(err, csi->mode);
} else {
- fprintf(stderr, "\\x%02X", csi->mode);
+ stream_push_s8(err, s8("\\x"));
+ stream_push_hex_u64(err, csi->mode);
+ }
+ stream_push_s8(err, s8(", .argc = "));
+ stream_push_u64(err, csi->argc);
+ stream_push_s8(err, s8(", .argv = {"));
+ for (i32 i = 0; i < csi->argc; i++) {
+ stream_push_byte(err, ' ');
+ stream_push_i64(err, csi->argv[i]);
}
- fprintf(stderr, ", .argc = %d, .argv = {", csi->argc);
- for (i32 i = 0; i < csi->argc; i++)
- fprintf(stderr, " %d", csi->argv[i]);
- os_write_err_msg(s8(" } }\n"));
+
+ stream_push_s8(err, s8(" } }\n"));
+ os_write_err_msg(stream_to_s8(err));
+ err->widx = 0;
}
/* ED/DECSED: Erase in Display */
@@ -474,7 +485,11 @@ clear_term_tab(Term *t, i32 arg)
t->tabs[i] = 0;
break;
default:
- fprintf(stderr, "clear_term_tab: unhandled arg: %d\n", arg);
+ stream_push_s8(&t->error_stream, s8("clear_term_tab: unhandled arg: "));
+ stream_push_i64(&t->error_stream, arg);
+ stream_push_byte(&t->error_stream, '\n');
+ os_write_err_msg(stream_to_s8(&t->error_stream));
+ t->error_stream.widx = 0;
}
}
@@ -552,7 +567,7 @@ set_mode(Term *t, CSI *csi, b32 set, b32 simulate)
break;
default:
os_write_err_msg(s8("set_mode: unhandled mode: "));
- dump_csi(csi);
+ dump_csi(csi, &t->error_stream);
}
}
#undef PRIV
@@ -584,13 +599,15 @@ indexed_colour(i32 index)
}
static struct conversion_result
-direct_colour(i32 *argv, i32 argc, i32 *idx)
+direct_colour(i32 *argv, i32 argc, i32 *idx, Stream *err)
{
struct conversion_result result = {.status = CR_FAILURE};
switch (argv[*idx + 1]) {
case 2: /* NOTE: defined RGB colour */
if (*idx + 4 >= argc) {
- fprintf(stderr, "direct_colour: wrong paramater count: %d\n", argc);
+ stream_push_s8(err, s8("direct_colour: wrong parameter count: "));
+ stream_push_u64(err, argc);
+ stream_push_byte(err, '\n');
break;
}
u32 r = (u32)argv[*idx + 2];
@@ -598,7 +615,13 @@ direct_colour(i32 *argv, i32 argc, i32 *idx)
u32 b = (u32)argv[*idx + 4];
*idx += 4;
if (r > 0xFF || g > 0xFF || b > 0xFF) {
- fprintf(stderr, "direct_colour: bad rgb colour: (%u, %u, %u)\n", r, g, b);
+ stream_push_s8(err, s8("direct_colour: bad rgb colour: ("));
+ stream_push_u64(err, r);
+ stream_push_s8(err, s8(", "));
+ stream_push_u64(err, g);
+ stream_push_s8(err, s8(", "));
+ stream_push_u64(err, b);
+ stream_push_s8(err, s8(")\n"));
break;
}
result.colour = (Colour){.r = r, .g = g, .b = b, .a = 0xFF};
@@ -606,13 +629,16 @@ direct_colour(i32 *argv, i32 argc, i32 *idx)
break;
case 5: /* NOTE: indexed colour */
if (*idx + 2 >= argc) {
- fprintf(stderr, "direct_colour: wrong paramater count: %d\n", argc);
+ stream_push_s8(err, s8("direct_colour: wrong parameter count: "));
+ stream_push_u64(err, argc);
+ stream_push_byte(err, '\n');
break;
}
*idx += 2;
if (!BETWEEN(argv[*idx], 0, 255)) {
- fprintf(stderr, "direct_colour: index parameter out of range: %d\n",
- argv[*idx]);
+ stream_push_s8(err, s8("direct_colour: index parameter out of range: "));
+ stream_push_i64(err, argv[*idx]);
+ stream_push_byte(err, '\n');
break;
}
if (BETWEEN(argv[*idx], 0, 16))
@@ -622,8 +648,11 @@ direct_colour(i32 *argv, i32 argc, i32 *idx)
result.status = CR_SUCCESS;
break;
default:
- fprintf(stderr, "define_colour: unknown argument: %d\n", argv[*idx + 1]);
+ stream_push_s8(err, s8("direct_colour: unknown argument: "));
+ stream_push_i64(err, argv[*idx + 1]);
+ stream_push_byte(err, '\n');
}
+
return result;
}
@@ -653,24 +682,24 @@ set_colours(Term *t, CSI *csi)
case 28: cs->attr &= ~ATTR_INVISIBLE; break;
case 29: cs->attr &= ~ATTR_STRUCK; break;
case 38:
- dcr = direct_colour(csi->argv, csi->argc, &i);
+ dcr = direct_colour(csi->argv, csi->argc, &i, &t->error_stream);
if (dcr.status == CR_SUCCESS) {
cs->fg = dcr.colour;
} else {
- os_write_err_msg(s8("set_colours: "));
- dump_csi(csi);
+ stream_push_s8(&t->error_stream, s8("set_colours: "));
+ dump_csi(csi, &t->error_stream);
}
break;
case 39: cs->fg = g_colours.data[g_colours.fgidx]; break;
case 48:
- dcr = direct_colour(csi->argv, csi->argc, &i);
+ dcr = direct_colour(csi->argv, csi->argc, &i, &t->error_stream);
if (dcr.status == CR_SUCCESS) {
cs->bg = dcr.colour;
} else {
- os_write_err_msg(s8("set_colours: "));
- dump_csi(csi);
+ stream_push_s8(&t->error_stream, s8("set_colours: "));
+ dump_csi(csi, &t->error_stream);
}
break;
@@ -686,8 +715,10 @@ set_colours(Term *t, CSI *csi)
} else if (BETWEEN(csi->argv[i], 100, 107)) {
cs->bg = g_colours.data[csi->argv[i] - 92];
} else {
- fprintf(stderr, "unhandled colour arg: %d\n", csi->argv[i]);
- dump_csi(csi);
+ stream_push_s8(&t->error_stream, s8("unhandled colour arg: "));
+ stream_push_i64(&t->error_stream, csi->argv[i]);
+ stream_push_byte(&t->error_stream, '\n');
+ dump_csi(csi, &t->error_stream);
}
}
}
@@ -724,8 +755,10 @@ window_manipulation(Term *t, CSI *csi)
break;
case 23: glfwSetWindowTitle(t->gl.window, t->saved_title); break;
default:
- fprintf(stderr, "unhandled xtwinops: %d\n", csi->argv[0]);
- dump_csi(csi);
+ stream_push_s8(&t->error_stream, s8("unhandled xtwinops: "));
+ stream_push_i64(&t->error_stream, csi->argv[0]);
+ stream_push_byte(&t->error_stream, '\n');
+ dump_csi(csi, &t->error_stream);
}
}
@@ -845,8 +878,8 @@ handle_csi(Term *t, CSI *csi)
goto unknown;
default:
unknown:
- os_write_err_msg(s8("unknown csi: "));
- dump_csi(csi);
+ stream_push_s8(&t->error_stream, s8("unknown csi: "));
+ dump_csi(csi, &t->error_stream);
}
END_TIMED_BLOCK();
}
@@ -910,23 +943,31 @@ reset_csi(CSI *csi, s8 *raw)
}
static void
-dump_osc(OSC *osc)
+dump_osc(OSC *osc, Stream *err)
{
- os_write_err_msg(s8("ESC]"));
+ stream_push_s8(err, s8("ESC]"));
for (size i = 0; i < osc->raw.len; i++) {
u8 cp = osc->raw.data[i];
- if (ISPRINT(cp))
- os_write_err_msg((s8){.len = 1, .data = osc->raw.data + i});
- else if (cp == '\n')
- os_write_err_msg(s8("\\n"));
- else if (cp == '\r')
- os_write_err_msg(s8("\\r"));
- else if (cp == '\a')
- os_write_err_msg(s8("\\a"));
- else
- fprintf(stderr, "\\x%02X", cp);
+ if (ISPRINT(cp)) {
+ stream_push_byte(err, cp);
+ } else if (cp == '\n') {
+ stream_push_s8(err, s8("\\n"));
+ } else if (cp == '\r') {
+ stream_push_s8(err, s8("\\r"));
+ } else if (cp == '\a') {
+ stream_push_s8(err, s8("\\a"));
+ } else {
+ stream_push_s8(err, s8("\\x"));
+ stream_push_hex_u64(err, cp);
+ }
}
- fprintf(stderr, "\n\t.cmd = %d, .arg = {.len = %zd}\n", osc->cmd, osc->arg.len);
+ stream_push_s8(err, s8("\n\t.cmd = "));
+ stream_push_u64(err, osc->cmd);
+ stream_push_s8(err, s8(", .arg = {.len = "));
+ stream_push_i64(err, osc->arg.len);
+ stream_push_s8(err, s8("}\n"));
+ os_write_err_msg(stream_to_s8(err));
+ err->widx = 0;
}
static void
@@ -942,8 +983,8 @@ handle_osc(Term *t, s8 *raw, Arena a)
case 1: /* IGNORED: set icon name */ break;
case 2: set_window_title(t->gl.window, a, osc.arg); break;
default:
- os_write_err_msg(s8("unhandled osc cmd: "));
- dump_osc(&osc);
+ stream_push_s8(&t->error_stream, s8("unhandled osc cmd: "));
+ dump_osc(&osc, &t->error_stream);
break;
}
END_TIMED_BLOCK();
@@ -996,7 +1037,13 @@ handle_escape(Term *t, s8 *raw, Arena a)
cursor_alt(t, 0);
break;
default:
- fprintf(stderr, "unknown escape sequence: ESC %c (0x%02x)\n", cp, cp);
+ stream_push_s8(&t->error_stream, s8("unknown escape sequence: ESC "));
+ stream_push_byte(&t->error_stream, cp);
+ stream_push_s8(&t->error_stream, s8(" (0x"));
+ stream_push_hex_u64(&t->error_stream, cp);
+ stream_push_s8(&t->error_stream, s8(")\n"));
+ os_write_err_msg(stream_to_s8(&t->error_stream));
+ t->error_stream.widx = 0;
break;
}
END_TIMED_BLOCK();
diff --git a/test.c b/test.c
@@ -49,6 +49,9 @@ get_gpu_glyph_index(Arena a, void *b, void *c, u32 cp, u32 d, u32 e, CachedGlyph
#define ESC(a) s8("\x1B"#a)
#define CSI(a) ESC([a)
+static s8 failure_string = s8("\x1B[31mFAILURE\x1B[0m\n");
+static s8 success_string = s8("\x1B[32mSUCCESS\x1B[0m\n");
+
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);
@@ -298,9 +301,10 @@ DebugRecord debug_records[__COUNTER__];
int
main(void)
{
- Arena memory = os_new_arena(16 * MEGABYTE);
+ Arena memory = os_new_arena(32 * MEGABYTE);
Term term = {0};
+ Stream log = stream_alloc(&memory, 4 * MEGABYTE);
for (u32 i = 0; i < ARRAY_COUNT(term.saved_cursors); i++) {
cursor_reset(&term);
cursor_move_to(&term, 0, 0);
@@ -323,12 +327,23 @@ main(void)
buffer_reset(&term);
term_reset(&term);
struct test_result result = tests[i](&term, memory);
+ s8 fn = c_str_to_s8((char *)result.info);
+ stream_push_s8(&log, fn);
+ stream_push_s8(&log, s8(":"));
+ size count = fn.len;
+ while (count < 26) { stream_push_byte(&log, ' '); count++; }
if (result.status == 0) {
failure_count++;
- printf("TEST FAILED: [%u/%lu] %s\n", i, ARRAY_COUNT(tests), result.info);
+ stream_push_s8(&log, failure_string);
+ } else {
+ stream_push_s8(&log, success_string);
}
}
- printf("FINISHED: [%lu/%lu] Succeeded\n", ARRAY_COUNT(tests) - failure_count,
- ARRAY_COUNT(tests));
+ stream_push_s8(&log, s8("FINISHED: ["));
+ stream_push_u64(&log, ARRAY_COUNT(tests) - failure_count);
+ stream_push_byte(&log, '/');
+ stream_push_u64(&log, ARRAY_COUNT(tests));
+ stream_push_s8(&log, s8("] Succeeded\n"));
+ os_write_err_msg(stream_to_s8(&log));
return 0;
}
diff --git a/util.c b/util.c
@@ -180,6 +180,139 @@ i32_from_cstr(char *s, char delim)
return ret;
}
+static Stream
+stream_alloc(Arena *a, u32 cap)
+{
+ Stream result = {0};
+ result.cap = cap;
+ result.buf = alloc(a, typeof(*result.buf), cap);
+ return result;
+}
+
+static s8
+stream_to_s8(Stream *s)
+{
+ s8 result = {.len = s->widx, .data = s->buf};
+ return result;
+}
+
+static void
+stream_push_byte(Stream *s, u8 cp)
+{
+ s->errors |= !(s->cap - s->widx);
+ if (!s->errors)
+ s->buf[s->widx++] = cp;
+}
+
+static void
+stream_push_s8(Stream *s, s8 str)
+{
+ s->errors |= (s->cap - s->widx) < str.len;
+ if (!s->errors) {
+ for (size i = 0; i < str.len; i++)
+ s->buf[s->widx++] = str.data[i];
+ }
+}
+
+/* NOTE: how can he get away with not using a library for this */
+static void
+stream_push_s8_left_padded(Stream *s, s8 str, u32 width)
+{
+ for (u32 i = str.len; i < width; i++)
+ stream_push_byte(s, ' ');
+ stream_push_s8(s, str);
+}
+
+static void
+stream_push_s8s(Stream *s, u32 count, s8 *strs)
+{
+ while (count) { stream_push_s8(s, *strs++); count--; }
+}
+
+static void
+stream_push_hex_u64(Stream *s, u64 n)
+{
+ static u8 hex[16] = {"0123456789abcdef"};
+ u8 buf[16];
+ u8 *end = buf + sizeof(buf);
+ u8 *beg = end;
+ while (n) {
+ *--beg = hex[n & 0x0F];
+ n >>= 4;
+ }
+ if (beg == end) {
+ *--beg = '0';
+ *--beg = '0';
+ }
+ stream_push_s8(s, (s8){.len = end - beg, .data = beg});
+}
+
+static void
+stream_push_u64_padded(Stream *s, u64 n, u32 width)
+{
+ u8 tmp[64];
+ u8 *end = tmp + sizeof(tmp);
+ u8 *beg = end;
+ do { *--beg = '0' + (n % 10); } while (n /= 10);
+
+ s8 str = {.len = end - beg, .data = beg};
+ for (u32 i = str.len; i < width; i++)
+ stream_push_byte(s, ' ');
+ stream_push_s8(s, str);
+}
+
+static void
+stream_push_u64(Stream *s, u64 n)
+{
+ stream_push_u64_padded(s, n, 0);
+}
+
+static void
+stream_push_i64(Stream *s, i64 n)
+{
+ if (n < 0) {
+ stream_push_byte(s, '-');
+ n = -n;
+ }
+ stream_push_u64_padded(s, n, 0);
+}
+
+static void
+stream_push_iv2(Stream *s, iv2 v)
+{
+ stream_push_byte(s, '{');
+ stream_push_i64(s, v.x);
+ stream_push_byte(s, ',');
+ stream_push_i64(s, v.y);
+ stream_push_byte(s, '}');
+}
+
+static void
+stream_push_f64(Stream *s, f64 f, i64 prec)
+{
+ if (f < 0) {
+ stream_push_byte(s, '-');
+ f *= -1;
+ }
+
+ /* NOTE: round last digit */
+ f += 0.5f / prec;
+
+ if (f >= (f64)(-1UL >> 1)) {
+ stream_push_s8(s, s8("inf"));
+ } else {
+ u64 integral = f;
+ u64 fraction = (f - integral) * prec;
+ stream_push_u64(s, integral);
+ stream_push_byte(s, '.');
+ for (u64 i = prec / 10; i > 1; i /= 10) {
+ if (i > fraction)
+ stream_push_byte(s, '0');
+ }
+ stream_push_u64(s, fraction);
+ }
+}
+
static s8
utf8_encode(u32 cp)
{
diff --git a/util.h b/util.h
@@ -55,7 +55,9 @@ typedef uint32_t u32;
typedef uint32_t b32;
typedef int64_t i64;
typedef uint64_t u64;
+typedef ptrdiff_t iptr;
typedef ptrdiff_t size;
+typedef size_t usize;
typedef union {
struct { i32 x, y; };
@@ -97,6 +99,20 @@ typedef struct { size len; u8 *data; } s8;
typedef struct { iv2 start, end; } Range;
#define INVALID_RANGE_END (iv2){.x = -1, .y = -1}
+typedef struct {
+ u8 *buf;
+ u32 cap;
+ u32 widx;
+ b32 errors;
+} Stream;
+
+#define INVALID_FILE (-1)
+enum file_attribute {
+ FA_READ = 1 << 0,
+ FA_WRITE = 1 << 1,
+ FA_APPEND = 1 << 2,
+};
+
#include "debug.h"
enum cell_attribute {
@@ -453,6 +469,8 @@ typedef struct {
u32 tabs[32];
char saved_title[1024];
+
+ Stream error_stream;
} Term;
static f32 dt_for_frame;
diff --git a/vtgl.c b/vtgl.c
@@ -92,7 +92,11 @@ resize(Term *t)
if (t->size.w > ARRAY_COUNT(t->tabs) * 32) {
t->size.w = ARRAY_COUNT(t->tabs) * 32u;
- fprintf(stderr, "resize: max terminal width is %u; clamping\n", t->size.w);
+ stream_push_s8(&t->error_stream, s8("resize: max terminal width is "));
+ stream_push_u64(&t->error_stream, t->size.w);
+ stream_push_s8(&t->error_stream, s8("; clamping\n"));
+ os_write_err_msg(stream_to_s8(&t->error_stream));
+ t->error_stream.widx = 0;
}
if (!equal_uv2(old_size, t->size)) {
@@ -946,7 +950,6 @@ draw_debug_overlay(Term *t, RenderCtx *rc)
return;
v2 ws = t->gl.window_size;
- s8 buf = s8alloc(&t->arena_for_frame, 1024);
static GlyphCacheStats glyph_stats;
static Rect r;
@@ -964,12 +967,14 @@ draw_debug_overlay(Term *t, RenderCtx *rc)
f32 line_pad = 5;
v2 txt_pos = {.x = r.pos.x, .y = ws.h - 20};
+ Stream txt = stream_alloc(&t->arena_for_frame, 1024);
{
- s8 txt = buf;
- txt.len = snprintf((char *)txt.data, buf.len, "Render Time: %0.02f ms/f", dt_for_frame * 1e3);
- v2 ts = measure_text(rc, font_id, txt);
+ stream_push_s8(&txt, s8("Render Time: "));
+ stream_push_f64(&txt, dt_for_frame * 1e3, 100);
+ stream_push_s8(&txt, s8(" ms/f"));
+ v2 ts = measure_text(rc, font_id, stream_to_s8(&txt));
txt_pos.y = (u32)(txt_pos.y - ts.h - line_pad);
- push_s8(rc, txt_pos, fg, font_id, txt);
+ push_s8(rc, txt_pos, fg, font_id, stream_to_s8(&txt));
if (ts.w > max_text_width) max_text_width = ts.w;
if (ts.h > line_height) line_height = ts.h;
@@ -981,12 +986,18 @@ draw_debug_overlay(Term *t, RenderCtx *rc)
DebugRecord *dr = debug_records + i;
- s8 txt = buf;
- txt.len = snprintf((char *)txt.data, buf.len, "%29s: %9lu cycs %5u %4s %11.02f cycs/hit",
- dr->function_name, cycs[i], hits[i], hits[i] > 1? "hits" : "hit",
- (f32)cycs[i]/(f32)hits[i]);
+ txt.widx = 0;
+ stream_push_s8_left_padded(&txt, c_str_to_s8(dr->function_name), 29);
+ stream_push_byte(&txt, ':');
+ stream_push_u64_padded(&txt, cycs[i], 10);
+ stream_push_s8(&txt, s8(" cycs "));
+ stream_push_u64_padded(&txt, hits[i], 5);
+ stream_push_s8_left_padded(&txt, hits[i] > 1? s8("hits") : s8("hit"), 5);
+ stream_push_s8(&txt, s8(" "));
+ stream_push_f64(&txt, (f32)cycs[i]/(f32)hits[i], 100);
+ stream_push_s8_left_padded(&txt, s8("cycs/hit"), 80 - txt.widx);
txt_pos.y = (u32)(txt_pos.y - line_height - line_pad);
- v2 ts = push_s8(rc, txt_pos, fg, font_id, txt);
+ v2 ts = push_s8(rc, txt_pos, fg, font_id, stream_to_s8(&txt));
if (ts.w > max_text_width) max_text_width = ts.w;
if (ts.h > line_height) line_height = ts.h;
@@ -1001,16 +1012,17 @@ draw_debug_overlay(Term *t, RenderCtx *rc)
v2 ts = push_s8(rc, txt_pos, fg, font_id, header);
/* TODO: This doesn't really work when we are redrawing on every frame */
- static char *fmts[ARRAY_COUNT(glyph_stats.E)] = {
- " Hits: %u",
- " Misses: %u",
- " Recycles: %u",
+ static s8 fmts[ARRAY_COUNT(glyph_stats.E)] = {
+ s8(" Hits: "),
+ s8(" Misses: "),
+ s8(" Recycles: "),
};
for (u32 i = 0; i < ARRAY_COUNT(glyph_stats.E); i++) {
- s8 txt = buf;
- txt.len = snprintf((char *)txt.data, buf.len, fmts[i], glyph_stats.E[i]);
+ txt.widx = 0;
+ stream_push_s8(&txt, fmts[i]);
+ stream_push_u64(&txt, glyph_stats.E[i]);
txt_pos.y = (u32)(txt_pos.y - line_height - line_pad);
- ts = push_s8(rc, txt_pos, fg, font_id, txt);
+ ts = push_s8(rc, txt_pos, fg, font_id, stream_to_s8(&txt));
if (ts.w > max_text_width) max_text_width = ts.w;
if (ts.h > line_height) line_height = ts.h;