Commit: 5cfafe14ace64e47eca65e3998ecc0391c915773
Parent: e357a4d259c41869292a4255dc5ec302d00d614d
Author: Randy Palamar
Date: Mon, 21 Apr 2025 18:47:22 -0600
cleanup code style
Diffstat:
M | build.sh | | | 2 | +- |
M | config.def.h | | | 48 | ++++++++++++++++++++++++------------------------ |
M | debug.c | | | 47 | ++++++++++++++++++++++++----------------------- |
M | debug.h | | | 2 | +- |
M | extern/stb_truetype.h | | | 4 | ++-- |
M | font.c | | | 36 | ++++++++++++++++++------------------ |
M | intrinsics.c | | | 12 | +++++++++--- |
A | os.h | | | 84 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | os_linux_aarch64.c | | | 175 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | os_linux_amd64.c | | | 165 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | os_linux_common.c | | | 559 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | os_linux_x11.c | | | 526 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | platform_linux_aarch64.c | | | 172 | ------------------------------------------------------------------------------- |
D | platform_linux_amd64.c | | | 162 | ------------------------------------------------------------------------------- |
D | platform_linux_common.c | | | 558 | ------------------------------------------------------------------------------- |
D | platform_linux_x11.c | | | 544 | ------------------------------------------------------------------------------- |
M | terminal.c | | | 234 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
M | tests/test-common.c | | | 69 | +++++++++++++++++++++++++++++++++------------------------------------ |
M | tests/test-fuzz.c | | | 2 | +- |
M | tests/test.c | | | 124 | ++++++++++++++++++++++++++++++++++++++++---------------------------------------- |
M | util.c | | | 217 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
M | util.h | | | 24 | ++++++++++++------------ |
M | vtgl.c | | | 332 | ++++++++++++++++++++++++++++++++++++++++--------------------------------------- |
M | vtgl.h | | | 356 | +++++++++++++++++++++++++++++++++---------------------------------------------- |
24 files changed, 2246 insertions(+), 2208 deletions(-)
diff --git a/build.sh b/build.sh
@@ -85,5 +85,5 @@ fuzz_results)
esac
[ ${build_lib} ] && ${cc} ${cflags} -fPIC vtgl.c -o vtgl.so ${ldflags} -shared
-${cc} ${cflags} -o vtgl platform_linux_x11.c ${ldflags}
+${cc} ${cflags} -o vtgl os_linux_x11.c ${ldflags}
${cc} ${testcflags} -O0 -o tests/test tests/test.c
diff --git a/config.def.h b/config.def.h
@@ -1,5 +1,5 @@
/* See LICENSE for copyright details */
-static FontDesc g_fonts[][FS_COUNT] = {
+global char *g_fonts[][FS_COUNT] = {
{
[FS_NORMAL] = "/usr/share/fonts/gofont/Go-Mono.ttf",
[FS_BOLD] = "/usr/share/fonts/gofont/Go-Mono-Bold.ttf",
@@ -17,25 +17,25 @@ static FontDesc g_fonts[][FS_COUNT] = {
},
};
-static i32 g_font_size = 28;
-static i32 g_ui_font_size = 20;
+global i32 g_font_size = 28;
+global i32 g_ui_font_size = 20;
/* NOTE: indices into the array above */
-static u32 g_ui_small_font_id = 2;
-static u32 g_ui_large_font_id = 3;
-static u32 g_ui_debug_font_id = 4;
+global u32 g_ui_small_font_id = 2;
+global u32 g_ui_large_font_id = 3;
+global u32 g_ui_debug_font_id = 4;
/* NOTE: terminal margin in pixels */
-static iv2 g_term_margin = {.w = 8, .h = 8};
+global iv2 g_term_margin = {.w = 8, .h = 8};
-static u8 g_tabstop = 8;
+global u8 g_tabstop = 8;
/* NOTE: number of blinks per second */
-static f32 g_blink_speed = 1.0f;
+global f32 g_blink_speed = 1.0f;
-static s8 g_shader_path_prefix = s8("");
+global s8 g_shader_path_prefix = s8("");
-static Colour base16_colours[16] = {
+global Colour base16_colours[16] = {
[0] = { .rgba = 0x000000ff }, /* black */
[1] = { .rgba = 0xaa0000ff }, /* red */
[2] = { .rgba = 0x00aa00ff }, /* green */
@@ -56,13 +56,13 @@ static Colour base16_colours[16] = {
[15] = { .rgba = 0xffffffff }, /* white */
};
-struct {
+global struct {
Colour *data;
u8 fgidx;
u8 bgidx;
} g_colours = {base16_colours, 7, 0};
-#define KEYBIND_FN(name) b32 name(Term *t, PlatformAPI *platform, Arg a)
+#define KEYBIND_FN(name) b32 name(Term *t, OS *os, Arg a)
typedef KEYBIND_FN(KeyBind_Fn);
/* NOTE: Bindable Functions */
@@ -76,19 +76,19 @@ KEYBIND_FN(zoom); /* arg: .i = font size increment */
#define ALTMOD (MOD_ALT|MOD_SHIFT)
//#define XXX (MOD_SUPER)
-struct hotkey {
+global struct hotkey {
u32 key;
KeyBind_Fn *fn;
Arg arg;
} g_hotkeys[] = {
- {ENCODE_KEY(ACT_PRESS, MODKEY, KEY_C), copy, {.i = CLIPBOARD_0}},
- {ENCODE_KEY(ACT_PRESS, MODKEY, KEY_V), paste, {.i = CLIPBOARD_0}},
- {ENCODE_KEY(ACT_PRESS, 0, KEY_PAGE_UP), scroll, {.i = +3}},
- {ENCODE_KEY(ACT_REPEAT, 0, KEY_PAGE_UP), scroll, {.i = +3}},
- {ENCODE_KEY(ACT_PRESS, 0, KEY_PAGE_DOWN), scroll, {.i = -3}},
- {ENCODE_KEY(ACT_REPEAT, 0, KEY_PAGE_DOWN), scroll, {.i = -3}},
- {ENCODE_KEY(ACT_PRESS, TERMMOD, KEY_MINUS), zoom, {.i = -1}},
- {ENCODE_KEY(ACT_REPEAT, TERMMOD, KEY_MINUS), zoom, {.i = -1}},
- {ENCODE_KEY(ACT_PRESS, TERMMOD, KEY_EQUAL), zoom, {.i = +1}},
- {ENCODE_KEY(ACT_REPEAT, TERMMOD, KEY_EQUAL), zoom, {.i = +1}},
+ {ENCODE_KEY(BUTTON_PRESS, MODKEY, KEY_C), copy, {.i = OS_CLIPBOARD_PRIMARY}},
+ {ENCODE_KEY(BUTTON_PRESS, MODKEY, KEY_V), paste, {.i = OS_CLIPBOARD_PRIMARY}},
+ {ENCODE_KEY(BUTTON_PRESS, 0, KEY_PAGE_UP), scroll, {.i = +3}},
+ {ENCODE_KEY(BUTTON_REPEAT, 0, KEY_PAGE_UP), scroll, {.i = +3}},
+ {ENCODE_KEY(BUTTON_PRESS, 0, KEY_PAGE_DOWN), scroll, {.i = -3}},
+ {ENCODE_KEY(BUTTON_REPEAT, 0, KEY_PAGE_DOWN), scroll, {.i = -3}},
+ {ENCODE_KEY(BUTTON_PRESS, TERMMOD, KEY_MINUS), zoom, {.i = -1}},
+ {ENCODE_KEY(BUTTON_REPEAT, TERMMOD, KEY_MINUS), zoom, {.i = -1}},
+ {ENCODE_KEY(BUTTON_PRESS, TERMMOD, KEY_EQUAL), zoom, {.i = +1}},
+ {ENCODE_KEY(BUTTON_REPEAT, TERMMOD, KEY_EQUAL), zoom, {.i = +1}},
};
diff --git a/debug.c b/debug.c
@@ -1,22 +1,23 @@
-static void
+/* See LICENSE for copyright details */
+function void
dump_lines_to_file(Term *t)
{
u8 buf[256];
- Stream fname = {.cap = sizeof(buf), .buf = buf};
+ Stream fname = {.capacity = sizeof(buf), .data = buf};
u64 current_time = os_get_time();
stream_push_u64(&fname, current_time);
stream_push_s8(&fname, s8("-lines.bin\0"));
/* TODO: just replace this with some giant buffer, this is debug code */
- iptr file = os_open(buf, FA_WRITE);
+ iptr file = os_open(buf, OS_FA_WRITE);
if (file == INVALID_FILE) return;
- fname.buf[fname.widx - 1] = '\n';
+ fname.data[fname.count - 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);
+ TermView *tv = t->views + t->view_idx;
+ iz line_count = MIN(256, tv->lines.filled);
Arena temp_arena = t->arena_for_frame;
Stream out = stream_alloc(&temp_arena, MB(1));
@@ -35,16 +36,16 @@ dump_lines_to_file(Term *t)
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);
+ iz file_offset = 0;
+ for (iz i = -(line_count - 1); i <= 0; i++) {
+ Line *line = tv->lines.data + get_line_idx(&tv->lines, i);
+ s8 l = line_to_s8(line);
stream_push_s8(&out, l);
if (out.errors) {
/* TODO: cleanup */
os_offset_write(file, stream_to_s8(&out), file_offset);
- file_offset += out.widx;
- out.widx = 0;
+ file_offset += out.count;
+ stream_reset(&out, 0);
stream_push_s8(&out, l);
}
}
@@ -55,7 +56,7 @@ dump_lines_to_file(Term *t)
os_close(file);
}
-static OpenDebugBlock *
+function OpenDebugBlock *
get_open_debug_block(DebugState *ds, DebugEvent *de)
{
OpenDebugBlock *result = ds->first_free_block;
@@ -70,7 +71,7 @@ get_open_debug_block(DebugState *ds, DebugEvent *de)
return result;
}
-static void
+function void
restart_collation(DebugState *ds, u32 invalid_record_index)
{
end_temp_arena(ds->temp_memory);
@@ -84,7 +85,7 @@ restart_collation(DebugState *ds, u32 invalid_record_index)
ds->open_record = 0;
}
-static void
+function void
coalesce_debug_events(DebugState *ds, u32 invalid_snapshot_index)
{
BEGIN_TIMED_BLOCK();
@@ -177,14 +178,14 @@ coalesce_debug_events(DebugState *ds, u32 invalid_snapshot_index)
END_TIMED_BLOCK();
}
-static void
+function void
refresh_collation(DebugState *ds)
{
restart_collation(ds, g_debug_table.snapshot_index);
coalesce_debug_events(ds, g_debug_table.snapshot_index);
}
-static void
+function void
draw_debug_bar_chart(Term *t, DebugState *ds, TerminalInput *input, RenderCtx *rc,
v2 bar_chart_top_left, f32 bar_chart_magnitude)
{
@@ -248,7 +249,7 @@ draw_debug_bar_chart(Term *t, DebugState *ds, TerminalInput *input, RenderCtx *r
v2 txt_s = measure_text(rc, g_ui_debug_font_id, stream_to_s8(&txt));
v2 txt_p = {.x = pos.x - txt_s.w - 10, .y = pos.y};
push_s8(rc, txt_p, fg, g_ui_debug_font_id, stream_to_s8(&txt));
- txt.widx = 0;
+ stream_reset(&txt, 0);
}
v4 target_colour = (v4){.g = .38, .b = .78, .a = 1};
@@ -267,7 +268,7 @@ draw_debug_bar_chart(Term *t, DebugState *ds, TerminalInput *input, RenderCtx *r
if (hot_region) {
DebugMetadata *txt_meta = hot_region->meta;
- txt.widx = 0;
+ stream_reset(&txt, 0);
stream_push_s8(&txt, c_str_to_s8(txt_meta->block_name));
stream_push_s8(&txt, s8(": "));
stream_push_f64(&txt, hot_region_secs * 1e3, 100);
@@ -288,7 +289,7 @@ draw_debug_bar_chart(Term *t, DebugState *ds, TerminalInput *input, RenderCtx *r
END_TIMED_BLOCK();
}
-static void
+function void
draw_debug_overlay(TerminalMemory *term_memory, TerminalInput *input, RenderCtx *rc)
{
Term *t = term_memory->memory;
@@ -351,7 +352,7 @@ draw_debug_overlay(TerminalMemory *term_memory, TerminalInput *input, RenderCtx
s8(" Recycles: "),
};
for (u32 i = 0; i < ARRAY_COUNT(glyph_stats.E); i++) {
- txt.widx = 0;
+ stream_reset(&txt, 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);
@@ -370,7 +371,7 @@ draw_debug_overlay(TerminalMemory *term_memory, TerminalInput *input, RenderCtx
END_TIMED_BLOCK();
}
-static void
+function void
debug_init(TerminalMemory *memory)
{
DebugState *ds = memory->debug_memory;
@@ -383,7 +384,7 @@ debug_init(TerminalMemory *memory)
restart_collation(ds, g_debug_table.snapshot_index);
}
-static void
+function void
debug_frame_end(TerminalMemory *memory, TerminalInput *input)
{
DebugState *debug_state = memory->debug_memory;
diff --git a/debug.h b/debug.h
@@ -102,7 +102,7 @@ typedef struct {
u32 metadata_count;
u32 snapshot_index;
} DebugTable;
-static DebugTable g_debug_table;
+global DebugTable g_debug_table;
#define RECORD_DEBUG_EVENT_COMMON(counter, event_type) \
u64 event_index = atomic_fetch_add(&g_debug_table.event_array_event_index, 1); \
diff --git a/extern/stb_truetype.h b/extern/stb_truetype.h
@@ -1451,8 +1451,8 @@ static int stbtt__GetGlyphShapeTT(Arena *a, const stbtt_fontinfo *info, int glyp
// Append vertices.
tmp = alloc(a, stbtt_vertex, num_vertices + comp_num_verts);
if (num_vertices > 0 && vertices)
- mem_copy(vertices, tmp, num_vertices * sizeof(stbtt_vertex));
- mem_copy(comp_verts, tmp + num_vertices, comp_num_verts * sizeof(stbtt_vertex));
+ mem_copy(tmp, vertices, num_vertices * sizeof(stbtt_vertex));
+ mem_copy(tmp + num_vertices, comp_verts, comp_num_verts * sizeof(stbtt_vertex));
vertices = tmp;
num_vertices += comp_num_verts;
}
diff --git a/font.c b/font.c
@@ -6,7 +6,7 @@
* "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", | p - w
* "│", "≤", "≥", "π", "≠", "£", "·", | x - ~
*/
-static u16 graphic_0[31] = {
+global u16 graphic_0[31] = {
0x25C6, 0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x00B0, 0x00B1,
0x2424, 0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA,
0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C,
@@ -17,7 +17,7 @@ static u16 graphic_0[31] = {
#define STB_STATIC
#include "extern/stb_truetype.h"
-static b32
+function b32
init_font(Font *f, char *font_path, i32 font_size)
{
b32 result = 0;
@@ -40,7 +40,7 @@ init_font(Font *f, char *font_path, i32 font_size)
return result;
}
-static u32
+function u32
compute_glyph_hash(GlyphCache *gc, u32 cp)
{
/* TODO: better hash function! */
@@ -48,7 +48,7 @@ compute_glyph_hash(GlyphCache *gc, u32 cp)
return result;
}
-static uv2
+function uv2
unpack_gpu_tile_coord(u32 gpu_index)
{
uv2 result;
@@ -57,7 +57,7 @@ unpack_gpu_tile_coord(u32 gpu_index)
return result;
}
-static void
+function void
cached_glyph_to_uv(FontAtlas *fa, CachedGlyph *cg, v2 *start, v2 *end, v2 scale)
{
v2 cs = {.w = fa->info.w, .h = fa->info.h};
@@ -72,7 +72,7 @@ cached_glyph_to_uv(FontAtlas *fa, CachedGlyph *cg, v2 *start, v2 *end, v2 scale)
}
-static void
+function void
recycle_cache(GlyphCache *gc)
{
CachedGlyph *sentinel = gc->glyphs + 0;
@@ -99,7 +99,7 @@ recycle_cache(GlyphCache *gc)
gc->stats.recycle_count++;
}
-static i32
+function i32
pop_free_glyph_entry(GlyphCache *gc)
{
CachedGlyph *sentinel = gc->glyphs + 0;
@@ -124,7 +124,7 @@ pop_free_glyph_entry(GlyphCache *gc)
return result;
}
-static u32
+function u32
get_glyph_entry_index(GlyphCache *gc, u32 cp)
{
u32 result = 0;
@@ -168,7 +168,7 @@ get_glyph_entry_index(GlyphCache *gc, u32 cp)
return result;
}
-static GlyphCacheStats
+function GlyphCacheStats
get_and_clear_glyph_cache_stats(GlyphCache *gc)
{
GlyphCacheStats result = gc->stats;
@@ -176,7 +176,7 @@ get_and_clear_glyph_cache_stats(GlyphCache *gc)
return result;
}
-static u32 *
+function u32 *
render_glyph(Arena *a, FontAtlas *fa, u32 cp, u32 font_id, enum face_style style, CachedGlyph **out_glyph)
{
BEGIN_TIMED_BLOCK();
@@ -323,19 +323,19 @@ end:
return rgba_bitmap;
}
-static FontInfo
+function FontInfo
compute_font_info(Font *font, u32 font_size)
{
FontInfo result = {0};
- static s8 ascii = s8(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"\
- "`abcdefghijklmnopqrstuvwxyz{|}~");
+ local_persist s8 ascii = s8(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
f32 scale = stbtt_ScaleForMappingEmToPixels(font->font_info, font_size);
i32 min_y = 0;
i32 max_height = 0;
f32 width = 0;
- for (size i = 0; i < ascii.len; i++) {
+ for (iz i = 0; i < ascii.len; i++) {
u32 glyph_idx = stbtt_FindGlyphIndex(font->font_info, ascii.data[i]);
i32 x0, y0, x1, y1, advance, left_bearing;
stbtt_GetGlyphBitmapBoxSubpixel(font->font_info, glyph_idx, scale, scale, 0, 0,
@@ -348,7 +348,7 @@ compute_font_info(Font *font, u32 font_size)
}
u32 graphic_0_count = 0;
- for (size i = 0; i < ARRAY_COUNT(graphic_0); i++) {
+ for (iz i = 0; i < ARRAY_COUNT(graphic_0); i++) {
if (graphic_0[i] == 0)
continue;
graphic_0_count++;
@@ -371,7 +371,7 @@ compute_font_info(Font *font, u32 font_size)
return result;
}
-static void
+function void
font_atlas_update(FontAtlas *fa, iv2 glyph_bitmap_dim)
{
GlyphCache *gc = &fa->glyph_cache;
@@ -396,7 +396,7 @@ font_atlas_update(FontAtlas *fa, iv2 glyph_bitmap_dim)
gc->glyphs[i].next_with_same_hash = i + 1;
}
-static void
+function void
shift_font_sizes(FontAtlas *fa, i32 size_delta)
{
g_font_size += size_delta;
@@ -416,7 +416,7 @@ shift_font_sizes(FontAtlas *fa, i32 size_delta)
}
}
-static void
+function void
init_fonts(FontAtlas *fa, Arena *a, iv2 glyph_bitmap_dim)
{
fa->nfonts = 0;
diff --git a/intrinsics.c b/intrinsics.c
@@ -1,6 +1,11 @@
#define FORCE_INLINE inline __attribute__((always_inline))
-static FORCE_INLINE u32
+#define atomic_and(ptr, n) __atomic_and_fetch(ptr, n, __ATOMIC_RELEASE);
+#define atomic_fetch_add(ptr, n) __atomic_fetch_add(ptr, n, __ATOMIC_RELEASE);
+#define atomic_load(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE)
+#define atomic_exchange_n(ptr, val) __atomic_exchange_n(ptr, val, __ATOMIC_SEQ_CST)
+
+function FORCE_INLINE u32
clz_u32(u32 a)
{
u32 result = 32;
@@ -8,7 +13,7 @@ clz_u32(u32 a)
return result;
}
-static FORCE_INLINE u32
+function FORCE_INLINE u32
ctz_u32(u32 a)
{
u32 result = 32;
@@ -20,13 +25,14 @@ ctz_u32(u32 a)
/* TODO? debuggers just loop here forever and need a manual PC increment (jump +1 in gdb) */
#define debugbreak() asm volatile ("brk 0xf000")
-static FORCE_INLINE u64
+function FORCE_INLINE u64
rdtsc(void)
{
register u64 cntvct asm("x0");
asm volatile ("mrs x0, cntvct_el0" : "=x"(cntvct));
return cntvct;
}
+
#elif __x86_64__
#include <immintrin.h>
diff --git a/os.h b/os.h
@@ -0,0 +1,84 @@
+/* See LICENSE for copyright details */
+#ifndef _OS_H_
+#define _OS_H_
+
+#define INVALID_FILE (-1)
+typedef enum {
+ OS_FA_READ = 1 << 0,
+ OS_FA_WRITE = 1 << 1,
+ OS_FA_APPEND = 1 << 2,
+} OSFileAttribute;
+
+/* NOTE(rnp): if your platform doesn't support secondary clipboard just push to primary */
+typedef enum {
+ OS_CLIPBOARD_PRIMARY,
+ OS_CLIPBOARD_SECONDARY,
+} OSClipboard;
+
+typedef struct { void *memory; iz size; } OSMemoryBlock;
+
+/* NOTE: virtual memory ring buffer */
+typedef struct {
+ iz capacity;
+ iz filled;
+ iz write_index;
+ u8 *data;
+} OSRingBuffer;
+
+/* NOTE: for now we will do the callback route but this will change if we do multithreading */
+#define OS_FILE_WATCH_CALLBACK_FN(name) void name(u8 *path, void *user_ctx)
+typedef OS_FILE_WATCH_CALLBACK_FN(os_file_watch_callback_fn);
+
+#define OS_ADD_FILE_WATCH_FN(name) void name(u8 *path, os_file_watch_callback_fn *fn, void *user_ctx)
+typedef OS_ADD_FILE_WATCH_FN(os_add_file_watch_fn);
+
+#define OS_ALLOCATE_RING_BUFFER_FN(name) OSRingBuffer name(iz capacity)
+typedef OS_ALLOCATE_RING_BUFFER_FN(os_allocate_ring_buffer_fn);
+
+#define OS_GET_CLIPBOARD_FN(name) u8 *name(OSClipboard clipboard)
+typedef OS_GET_CLIPBOARD_FN(os_get_clipboard_fn);
+
+#define OS_SET_CLIPBOARD_FN(name) b32 name(u8 *buffer, iz length, OSClipboard clipboard)
+typedef OS_SET_CLIPBOARD_FN(os_set_clipboard_fn);
+
+#define OS_READ_FILE_FN(name) s8 name(u8 *path, Arena *a)
+typedef OS_READ_FILE_FN(os_read_file_fn);
+
+/* TODO: this should possibly just take a stream buffer */
+#define OS_READ_FN(name) iz name(iptr file, s8 buffer)
+typedef OS_READ_FN(os_read_fn);
+
+#define OS_SET_TERMINAL_SIZE_FN(name) void name(iptr child, i32 rows, i32 columns, \
+ i32 window_width, i32 window_height)
+typedef OS_SET_TERMINAL_SIZE_FN(os_set_terminal_size_fn);
+
+#define OS_GET_WINDOW_TITLE_FN(name) u8 *name(void)
+typedef OS_GET_WINDOW_TITLE_FN(os_get_window_title_fn);
+
+#define OS_SET_WINDOW_TITLE_FN(name) void name(s8 title)
+typedef OS_SET_WINDOW_TITLE_FN(os_set_window_title_fn);
+
+#define OS_WRITE_FN(name) b32 name(iptr file, s8 raw)
+typedef OS_WRITE_FN(os_write_fn);
+
+#define OS_FUNCTIONS \
+ X(add_file_watch) \
+ X(allocate_ring_buffer) \
+ X(get_clipboard) \
+ X(set_clipboard) \
+ X(read_file) \
+ X(read) \
+ X(set_terminal_size) \
+ X(get_window_title) \
+ X(set_window_title) \
+ X(write)
+
+typedef struct {
+#define X(name) os_ ## name ## _fn *name;
+ OS_FUNCTIONS
+#undef X
+
+ u8 path_separator;
+} OS;
+
+#endif /* _OS_H_ */
diff --git a/os_linux_aarch64.c b/os_linux_aarch64.c
@@ -0,0 +1,175 @@
+/* TODO: generate this whole file with a metaprogram */
+
+/* See LICENSE for license details. */
+#ifndef asm
+#ifdef __asm
+#define asm __asm
+#else
+#define asm __asm__
+#endif
+#endif
+
+typedef enum {
+ SYS_dup3 = 24,
+ SYS_inotify_init1 = 26,
+ SYS_inotify_add_watch = 27,
+ SYS_inotify_rm_watch = 28,
+ SYS_ioctl = 29,
+ SYS_ftruncate = 46,
+ SYS_openat = 56,
+ SYS_close = 57,
+ SYS_read = 63,
+ SYS_write = 64,
+ SYS_pwrite64 = 68,
+ SYS_pselect6 = 72,
+ SYS_exit = 93,
+ SYS_exit_group = 94,
+ SYS_futex = 98,
+ SYS_clock_gettime = 113,
+ SYS_setsid = 157,
+ SYS_prctl = 167,
+ SYS_munmap = 215,
+ SYS_clone = 220,
+ SYS_execve = 221,
+ SYS_mmap = 222,
+ SYS_mprotect = 226,
+ SYS_madvise = 233,
+ SYS_wait4 = 260,
+ SYS_memfd_create = 279,
+ SYS_statx = 291,
+} AArch64Syscall;
+
+#define SIGCHLD 17
+
+/* NOTE(rnp): technically arm64 can have 4K, 16K or 64K pages but we will just assume 64K */
+#define PAGE_SIZE 65536
+
+/* TODO: check that this is equivalent */
+typedef u64 sys_fd_set[16];
+
+function FORCE_INLINE u64
+syscall0(AArch64Syscall n)
+{
+ register i64 x8 asm("x8") = n;
+ register u64 x0 asm("x0");
+ asm volatile ("svc 0"
+ : "=x"(x0)
+ : "x"(x8)
+ : "memory", "cc"
+ );
+ return x0;
+}
+
+function FORCE_INLINE u64
+syscall1(AArch64Syscall n, i64 a1)
+{
+ register i64 x8 asm("x8") = n;
+ register u64 x0 asm("x0") = a1;
+ asm volatile ("svc 0"
+ : "+x"(x0)
+ : "x"(x8)
+ : "memory", "cc"
+ );
+ return x0;
+}
+
+function FORCE_INLINE u64
+syscall2(AArch64Syscall n, i64 a1, i64 a2)
+{
+ register i64 x8 asm("x8") = n;
+ register u64 x0 asm("x0") = a1;
+ register i64 x1 asm("x1") = a2;
+ asm volatile ("svc 0"
+ : "+x"(x0)
+ : "x"(x8), "x"(x1)
+ : "memory", "cc"
+ );
+ return x0;
+}
+
+function FORCE_INLINE u64
+syscall3(AArch64Syscall n, i64 a1, i64 a2, i64 a3)
+{
+ register i64 x8 asm("x8") = n;
+ register u64 x0 asm("x0") = a1;
+ register i64 x1 asm("x1") = a2;
+ register i64 x2 asm("x2") = a3;
+ asm volatile ("svc 0"
+ : "+x"(x0)
+ : "x"(x8), "x"(x1), "x"(x2)
+ : "memory", "cc"
+ );
+ return x0;
+}
+
+function FORCE_INLINE u64
+syscall4(AArch64Syscall n, i64 a1, i64 a2, i64 a3, i64 a4)
+{
+ register i64 x8 asm("x8") = n;
+ register u64 x0 asm("x0") = a1;
+ register i64 x1 asm("x1") = a2;
+ register i64 x2 asm("x2") = a3;
+ register i64 x3 asm("x3") = a4;
+ asm volatile ("svc 0"
+ : "+x"(x0)
+ : "x"(x8), "x"(x1), "x"(x2), "x"(x3)
+ : "memory", "cc"
+ );
+ return x0;
+}
+
+function FORCE_INLINE u64
+syscall5(AArch64Syscall n, i64 a1, i64 a2, i64 a3, i64 a4, i64 a5)
+{
+ register i64 x8 asm("x8") = n;
+ register u64 x0 asm("x0") = a1;
+ register i64 x1 asm("x1") = a2;
+ register i64 x2 asm("x2") = a3;
+ register i64 x3 asm("x3") = a4;
+ register i64 x4 asm("x4") = a5;
+ asm volatile ("svc 0"
+ : "+x"(x0)
+ : "x"(x8), "x"(x1), "x"(x2), "x"(x3), "x"(x4)
+ : "memory", "cc"
+ );
+ return x0;
+}
+
+function FORCE_INLINE u64
+syscall6(AArch64Syscall n, i64 a1, i64 a2, i64 a3, i64 a4, i64 a5, i64 a6)
+{
+ register i64 x8 asm("x8") = n;
+ register u64 x0 asm("x0") = a1;
+ register i64 x1 asm("x1") = a2;
+ register i64 x2 asm("x2") = a3;
+ register i64 x3 asm("x3") = a4;
+ register i64 x4 asm("x4") = a5;
+ register i64 x5 asm("x5") = a6;
+ asm volatile ("svc 0"
+ : "+x"(x0)
+ : "x"(x8), "x"(x1), "x"(x2), "x"(x3), "x"(x4), "x"(x5)
+ : "memory", "cc"
+ );
+ return x0;
+}
+
+__attribute__((naked))
+function i64
+new_thread(void *stack_base)
+{
+ asm volatile (
+ "mov x8, #220\n" // SYS_clone
+ "mov x1, x0\n" // arg2 = new stack
+ "mov x0, #0xF00\n" // arg1 = clone flags (VM|FS|FILES|SIGHAND|THREAD|SYSVMEM)
+ "movk x0, #0x5, lsl #16\n" // no 32 bit immediates in general on arm
+ "svc 0\n"
+ "cbnz x0, 1f\n" // don't clobber syscall return in calling thread
+ "mov x0, sp\n"
+ "ldr x1, [sp]\n" // arm doesn't take the return address from the stack;
+ "blr x1\n" // we need to load it and branch to it
+ "1: ret"
+ ::: "x8", "x1", "memory", "cc"
+ );
+}
+
+#include "platform_linux_common.c"
diff --git a/os_linux_amd64.c b/os_linux_amd64.c
@@ -0,0 +1,165 @@
+/* See LICENSE for license details. */
+#ifndef asm
+#ifdef __asm
+#define asm __asm
+#else
+#define asm __asm__
+#endif
+#endif
+
+/* TODO: X macro that defines all of these with the appropriate function/macro */
+typedef enum {
+ SYS_read = 0,
+ SYS_write = 1,
+ SYS_close = 3,
+ SYS_mmap = 9,
+ SYS_mprotect = 10,
+ SYS_munmap = 11,
+ SYS_ioctl = 16,
+ SYS_pwrite64 = 18,
+ SYS_madvise = 28,
+ SYS_clone = 56,
+ SYS_execve = 59,
+ SYS_exit = 60,
+ SYS_wait4 = 61,
+ SYS_ftruncate = 77,
+ SYS_setsid = 112,
+ SYS_prctl = 157,
+ SYS_futex = 202,
+ SYS_getdents64 = 217,
+ SYS_clock_gettime = 228,
+ SYS_exit_group = 231,
+ SYS_inotify_add_watch = 254,
+ SYS_inotify_rm_watch = 255,
+ SYS_openat = 257,
+ SYS_pselect6 = 270,
+ SYS_dup3 = 292,
+ SYS_inotify_init1 = 294,
+ SYS_memfd_create = 319,
+ SYS_statx = 332,
+} AMD64Syscall;
+
+#define SIGCHLD 17
+
+#define PAGE_SIZE 4096
+
+typedef u64 sys_fd_set[16];
+
+#define DIRENT_RECLEN_OFF 16
+#define DIRENT_TYPE_OFF 18
+#define DIRENT_NAME_OFF 19
+
+function FORCE_INLINE u64
+syscall1(AMD64Syscall n, i64 a1)
+{
+ u64 result;
+ asm volatile ("syscall"
+ : "=a"(result)
+ : "a"(n), "D"(a1)
+ : "rcx", "r11", "memory"
+ );
+ return result;
+}
+
+function FORCE_INLINE u64
+syscall2(AMD64Syscall n, i64 a1, i64 a2)
+{
+ i64 result;
+ asm volatile ("syscall"
+ : "=a"(result)
+ : "a"(n), "D"(a1), "S"(a2)
+ : "rcx", "r11", "memory"
+ );
+ return result;
+}
+
+function FORCE_INLINE u64
+syscall3(AMD64Syscall n, i64 a1, i64 a2, i64 a3)
+{
+ u64 result;
+ asm volatile ("syscall"
+ : "=a"(result)
+ : "a"(n), "D"(a1), "S"(a2), "d"(a3)
+ : "rcx", "r11", "memory"
+ );
+ return result;
+}
+
+function FORCE_INLINE u64
+syscall4(AMD64Syscall n, i64 a1, i64 a2, i64 a3, i64 a4)
+{
+ u64 result;
+ register i64 r10 asm("r10") = a4;
+ asm volatile ("syscall"
+ : "=a"(result)
+ : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10)
+ : "rcx", "r11", "memory"
+ );
+ return result;
+}
+
+
+function FORCE_INLINE u64
+syscall5(AMD64Syscall n, i64 a1, i64 a2, i64 a3, i64 a4, i64 a5)
+{
+ u64 result;
+ register i64 r10 asm("r10") = a4;
+ register i64 r8 asm("r8") = a5;
+ asm volatile ("syscall"
+ : "=a"(result)
+ : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8)
+ : "rcx", "r11", "memory"
+ );
+ return result;
+}
+
+function FORCE_INLINE u64
+syscall6(AMD64Syscall n, i64 a1, i64 a2, i64 a3, i64 a4, i64 a5, i64 a6)
+{
+ i64 result;
+ register i64 r10 asm("r10") = a4;
+ register i64 r8 asm("r8") = a5;
+ register i64 r9 asm("r9") = a6;
+ asm volatile ("syscall"
+ : "=a"(result)
+ : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9)
+ : "rcx", "r11", "memory"
+ );
+ return result;
+}
+
+/* NOTE: based on code from nullprogram (Chris Wellons) */
+__attribute__((naked))
+function i64
+new_thread(void *stack_base)
+{
+ asm volatile (
+ "mov %%rdi, %%rsi\n" // arg2 = new stack
+ "mov $0x50F00, %%edi\n" // arg1 = clone flags (VM|FS|FILES|SIGHAND|THREAD|SYSVMEM)
+ "mov $56, %%eax\n" // SYS_clone
+ "syscall\n"
+ "test %%eax, %%eax\n" // don't mess with the calling thread's stack
+ "jne 1f\n"
+ "mov %%rsp, %%rdi\n"
+ "sub $8, %%rsp\n" // place a 0 return branch pointer on the child's stack
+ "push (%%rdi)\n" // push the entry point back onto the stack for use by ret
+ "1: ret\n"
+ ::: "rax", "rcx", "rsi", "rdi", "r11", "memory"
+ );
+}
+
+#include "os_linux_common.c"
+
+#if 0
+asm (
+ ".intel_syntax noprefix\n"
+ ".global _start\n"
+ "_start:\n"
+ " mov edi, DWORD PTR [rsp]\n"
+ " lea rsi, [rsp+8]\n"
+ " lea rdx, [rsi+rdi*8+8]\n"
+ " call linux_main\n"
+ " ud2\n"
+ ".att_syntax\n"
+);
+#endif
diff --git a/os_linux_common.c b/os_linux_common.c
@@ -0,0 +1,559 @@
+#define FUTEX_WAIT 0
+#define FUTEX_WAKE 1
+
+#define CLOCK_MONOTONIC 1
+
+#define PR_SET_NAME 15
+
+#define PROT_NONE 0x00
+#define PROT_READ 0x01
+#define PROT_RW 0x03
+
+#define MFD_CLOEXEC 0x01
+
+#define MAP_SHARED 0x01
+#define MAP_PRIVATE 0x02
+#define MAP_FIXED 0x10
+#define MAP_ANON 0x20
+
+#define MADV_FREE 8
+#define MADV_HUGEPAGE 14
+
+#define O_RDONLY 0x00000
+#define O_WRONLY 0x00001
+#define O_RDWR 0x00002
+#define O_CREAT 0x00040
+#define O_NOCTTY 0x00100
+#define O_APPEND 0x00400
+#define O_NONBLOCK 0x00800
+#define O_CLOEXEC 0x80000
+
+#define IN_CLOSE_WRITE 0x00000008
+#define IN_CLOSE_NOWRITE 0x00000010
+#define IN_MODIFY 0x00000002
+
+#define AT_EMPTY_PATH 0x1000
+#define AT_FDCWD (-100)
+
+#define LINUX_INOTIFY_MASK (IN_CLOSE_WRITE|IN_CLOSE_NOWRITE|IN_MODIFY)
+
+#define WNOHANG 1
+#define W_IF_EXITED(s) (!((s) & 0x7F))
+
+/* TODO: glibc/gcc indirectly include sys/select.h if you include immintrin.h. If that
+ * header is removed this can also be removed */
+#undef FD_SET
+#undef FD_ISSET
+
+#define FD_SET(d, s) ((s)[(d) / (8 * sizeof(*(s)))] |= (1ULL << ((d) % (8 * sizeof(*(s))))))
+#define FD_ISSET(d, s) ((s)[(d) / (8 * sizeof(*(s)))] & (1ULL << ((d) % (8 * sizeof(*(s))))))
+
+typedef __attribute__((aligned(16))) u8 statx_buffer[256];
+#define STATX_BUF_MEMBER(sb, t, off) (*(t *)((u8 *)(sb) + off))
+#define STATX_INODE(sb) STATX_BUF_MEMBER(sb, u64, 32)
+#define STATX_FILE_SIZE(sb) STATX_BUF_MEMBER(sb, u64, 40)
+
+#define STATX_INO 0x00000100U
+#define STATX_SIZE 0x00000200U
+
+#define TIOCSCTTY 0x540E
+#define TIOCSWINSZ 0x5414
+#define TIOCSPTLCK 0x40045431 /* (un)lock pty */
+#define TIOCGPTN 0x80045430 /* get pty number */
+
+#ifndef VERSION
+#define VERSION "unknown"
+#endif
+
+#define OS_MAP_READ PROT_READ
+#define OS_MAP_PRIVATE MAP_PRIVATE
+
+struct __attribute__((aligned(16))) stack_base {
+ void (*entry)(struct stack_base *stack);
+ Arena thread_arena;
+ void *window;
+ TerminalMemory *terminal_memory;
+ TerminalInput *input;
+ i32 work_futex;
+ b32 thread_asleep;
+};
+
+typedef struct {
+ os_file_watch_callback_fn *fn;
+ u8 *path;
+ void *user_ctx;
+ u64 inode;
+ i32 handle;
+} linux_file_watch;
+
+typedef struct linux_deferred_file_reload_queue {
+ struct linux_deferred_file_reload_queue *next;
+ struct linux_deferred_file_reload_queue *last;
+ i32 index;
+ i32 failures;
+} linux_deferred_file_reload_queue;
+
+typedef struct {
+ iptr handle;
+ iptr process_id;
+} linux_platform_process;
+
+typedef struct {
+ Arena platform_memory;
+ void *window;
+
+ TerminalMemory memory;
+ TerminalInput input;
+
+ Stream char_stream;
+
+ linux_platform_process child;
+ i32 inotify_fd;
+ i32 win_fd;
+
+ linux_deferred_file_reload_queue file_reload_queue;
+ linux_deferred_file_reload_queue *file_reload_free_list;
+ linux_file_watch file_watches[32];
+ i32 file_watch_count;
+
+ Stream error_stream;
+
+ struct stack_base *render_stack;
+
+#ifdef _DEBUG
+ void *library_handle;
+#endif
+} PlatformCtx;
+global PlatformCtx linux_ctx;
+
+function void
+os_write_err_msg(s8 msg)
+{
+ syscall3(SYS_write, 2, (iptr)msg.data, msg.len);
+}
+
+__attribute__((noreturn))
+function void
+os_fatal(s8 msg)
+{
+ os_write_err_msg(msg);
+ syscall1(SYS_exit_group, 1);
+ __builtin_unreachable();
+}
+
+function u32
+os_file_attribute_to_mode(u32 attr)
+{
+ u32 result = O_CREAT;
+ if (attr & OS_FA_READ && attr & OS_FA_WRITE) {
+ result |= O_RDWR;
+ } else if (attr & OS_FA_READ) {
+ result |= O_RDONLY;
+ } else if (attr & OS_FA_WRITE) {
+ result |= O_WRONLY;
+ }
+
+ if (attr & OS_FA_APPEND)
+ result |= O_APPEND;
+
+ return result;
+}
+
+function iptr
+os_open(u8 *name, u32 attr)
+{
+ u64 result = syscall4(SYS_openat, AT_FDCWD, (iptr)name, os_file_attribute_to_mode(attr), 0660);
+ if (result > -4096UL)
+ result = INVALID_FILE;
+ return result;
+}
+
+function b32
+os_offset_write(iptr file, s8 raw, iz offset)
+{
+ iz result = syscall4(SYS_pwrite64, file, (iptr)raw.data, raw.len, offset);
+ return result == raw.len;
+}
+
+function OS_WRITE_FN(os_write)
+{
+ iz result = syscall3(SYS_write, file, (iptr)raw.data, raw.len);
+ return result == raw.len;
+}
+
+function void
+os_close(iptr file)
+{
+ syscall1(SYS_close, file);
+}
+
+function OS_READ_FN(os_read)
+{
+ u64 r = 0, remaining = buffer.len, total_bytes_read = 0;
+
+ do {
+ remaining -= r;
+ total_bytes_read += r;
+ r = syscall3(SYS_read, file, (iptr)(buffer.data + total_bytes_read), remaining);
+ } while (r <= -4096UL && remaining != 0);
+
+ return total_bytes_read;
+}
+
+function OS_READ_FILE_FN(os_read_file)
+{
+ s8 result = {0};
+
+ statx_buffer sb;
+ u64 fd = syscall4(SYS_openat, AT_FDCWD, (iptr)path, O_RDONLY, 0);
+ u64 status = syscall5(SYS_statx, fd, 0, AT_EMPTY_PATH, STATX_SIZE, (iptr)sb);
+
+ if (fd <= -4096UL && status == 0) {
+ result = s8alloc(a, STATX_FILE_SIZE(sb));
+ iz rlen = os_read(fd, result);
+ syscall1(SYS_close, fd);
+ if (result.len != rlen)
+ result.len = 0;
+ }
+
+ return result;
+}
+
+function OSMemoryBlock
+os_block_alloc(iz requested_size)
+{
+ OSMemoryBlock result = {0};
+
+ /* TODO: query system for HUGETLB support and use those instead of page size */
+ iz alloc_size = requested_size;
+ if (alloc_size % PAGE_SIZE != 0)
+ alloc_size += PAGE_SIZE - alloc_size % PAGE_SIZE;
+
+ u64 memory = syscall6(SYS_mmap, 0, alloc_size, PROT_RW, MAP_ANON|MAP_PRIVATE, -1, 0);
+ if (memory <= -4096UL) {
+ result.memory = (void *)memory;
+ result.size = alloc_size;
+ syscall3(SYS_madvise, memory, alloc_size, MADV_HUGEPAGE);
+ }
+
+ return result;
+}
+
+function void
+os_release_memory_block(OSMemoryBlock memory)
+{
+ syscall3(SYS_madvise, (iptr)memory.memory, memory.size, MADV_FREE);
+ syscall3(SYS_mprotect, (iptr)memory.memory, memory.size, PROT_NONE);
+}
+
+function void
+os_release_ring_buffer(OSRingBuffer *rb)
+{
+ syscall2(SYS_munmap, (iptr)(rb->data - rb->capacity), rb->capacity * 3);
+}
+
+function f64
+os_get_time(void)
+{
+ i64 timespec[2];
+ syscall2(SYS_clock_gettime, CLOCK_MONOTONIC, (iptr)timespec);
+ f64 result = timespec[0] + ((f64)timespec[1]) * 1e-9;
+ return result;
+}
+
+function os_mapped_file
+os_map_file(char *path, i32 mode, i32 perm)
+{
+ os_mapped_file result = {0};
+
+ i32 open_mode = 0;
+ switch (mode) {
+ case OS_MAP_READ: open_mode = O_RDONLY; break;
+ default: ASSERT(0);
+ }
+
+ statx_buffer sb;
+ u64 fd = syscall4(SYS_openat, AT_FDCWD, (iptr)path, open_mode, 0);
+ u64 status = syscall5(SYS_statx, fd, 0, AT_EMPTY_PATH, STATX_SIZE, (iptr)sb);
+
+ if (fd <= -4096UL && status == 0) {
+ u64 memory = syscall6(SYS_mmap, 0, STATX_FILE_SIZE(sb), mode, perm, fd, 0);
+ if (memory <= -4096UL) {
+ result.data = (u8 *)memory;
+ result.len = STATX_FILE_SIZE(sb);
+ }
+ syscall1(SYS_close, fd);
+ }
+
+ return result;
+}
+
+function OS_ALLOCATE_RING_BUFFER_FN(os_allocate_ring_buffer)
+{
+ OSRingBuffer result = {0};
+ /* TODO: query system for HUGETLB support and use those instead of page size */
+ if (capacity % PAGE_SIZE != 0)
+ capacity += PAGE_SIZE - capacity % PAGE_SIZE;
+ ASSERT(capacity % PAGE_SIZE == 0);
+
+ u64 fd = syscall2(SYS_memfd_create, (iptr)"vtgl:rb", MFD_CLOEXEC);
+ if (fd > -4096UL) os_fatal(s8("os_alloc_ring_buffer: failed to open mem_fd\n"));
+ syscall2(SYS_ftruncate, fd, capacity);
+
+ result.capacity = capacity;
+ result.data = (u8 *)syscall6(SYS_mmap, 0, (iptr)(3 * capacity), 0, MAP_ANON|MAP_PRIVATE, -1, 0);
+ if ((u64)result.data > -4096UL)
+ os_fatal(s8("os_alloc_ring_buffer: initial mmap failed\n"));
+ syscall3(SYS_madvise, (iptr)result.data, 3 * capacity, MADV_HUGEPAGE);
+
+ for (i32 i = 0; i < 3; i++) {
+ u64 memory = syscall6(SYS_mmap, (iptr)(result.data + i * capacity), capacity,
+ PROT_RW, MAP_FIXED|MAP_SHARED, fd, 0);
+ if (memory > -4096UL) {
+ u8 buf[256];
+ Stream err = {.data = buf, .capacity = 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));
+ }
+ }
+ syscall1(SYS_close, fd);
+
+ /* NOTE: start in the middle page */
+ result.data += result.capacity;
+
+ return result;
+}
+
+function b32
+os_child_exited(iptr pid)
+{
+ i64 status;
+ i64 r = syscall4(SYS_wait4, pid, (iptr)&status, WNOHANG, 0);
+ return r == pid && W_IF_EXITED(status);
+}
+
+function linux_platform_process
+os_fork_child(s8 cmd, c8 **envp)
+{
+ i32 n = 0;
+
+ /* NOTE: we open in non-blocking mode so that we can try and fully drain the pipe
+ * before processing. Otherwise a single read will be limited to the page size */
+ u64 m = syscall4(SYS_openat, AT_FDCWD, (iptr)"/dev/ptmx", O_RDWR|O_NOCTTY|O_NONBLOCK|O_CLOEXEC, 0);
+ if (m > -4096UL) os_fatal(s8("os_fork_child: failed to open master terminal\n"));
+ /* NOTE: first unlock the tty, then get a valid pty number */
+ if (syscall3(SYS_ioctl, m, TIOCSPTLCK, (iptr)&n) || syscall3(SYS_ioctl, m, TIOCGPTN, (iptr)&n))
+ os_fatal(s8("os_fork_child: failed to get a pty number\n"));
+
+ u8 buffer[20] = {"/dev/pts/"};
+ Stream sbuf = {.data = buffer, .capacity = 20, .count = sizeof("/dev/pts/") - 1};
+ stream_push_i64(&sbuf, n);
+ stream_push_byte(&sbuf, 0);
+
+ u64 s = syscall4(SYS_openat, AT_FDCWD, (iptr)sbuf.data, O_RDWR|O_NOCTTY, 0);
+ if (s > -4096UL) os_fatal(s8("os_fork_child: failed to open slave terminal\n"));
+
+ u64 pid = syscall2(SYS_clone, SIGCHLD, 0);
+ if (pid > -4096UL) os_fatal(s8("os_fork_child: failed to fork a child\n"));
+
+ if (pid == 0) {
+ syscall1(SYS_setsid, 0);
+ syscall3(SYS_dup3, s, 0, 0);
+ syscall3(SYS_dup3, s, 1, 0);
+ syscall3(SYS_dup3, s, 2, 0);
+ syscall3(SYS_ioctl, s, TIOCSCTTY, 0);
+ if (s > 2) syscall1(SYS_close, s);
+ ASSERT(cmd.data[cmd.len] == 0);
+ u8 *argv[] = {cmd.data, 0};
+ syscall3(SYS_execve, (iptr)cmd.data, (iptr)argv, (iptr)envp);
+ __builtin_unreachable();
+ os_fatal(s8("failed to exec child\n"));
+ }
+ syscall1(SYS_close, s);
+
+ return (linux_platform_process){.process_id = pid, .handle = m};
+}
+
+function OS_SET_TERMINAL_SIZE_FN(os_set_terminal_size)
+{
+ u16 win_size[4];
+ win_size[0] = rows;
+ win_size[1] = columns;
+ win_size[2] = window_width;
+ win_size[3] = window_height;
+ if (syscall3(SYS_ioctl, child, TIOCSWINSZ, (iptr)win_size) > -4096UL)
+ os_write_err_msg(s8("os_set_term_size\n"));
+}
+
+function OS_ADD_FILE_WATCH_FN(linux_add_file_watch)
+{
+ u64 wd = syscall3(SYS_inotify_add_watch, linux_ctx.inotify_fd, (iptr)path, LINUX_INOTIFY_MASK);
+ if (wd <= -4096UL) {
+ statx_buffer sb;
+ syscall5(SYS_statx, AT_FDCWD, (iptr)path, 0, STATX_INO, (iptr)sb);
+
+ i32 idx = linux_ctx.file_watch_count++;
+ ASSERT(idx < ARRAY_COUNT(linux_ctx.file_watches));
+ linux_ctx.file_watches[idx].fn = fn;
+ linux_ctx.file_watches[idx].path = path;
+ linux_ctx.file_watches[idx].handle = wd;
+ linux_ctx.file_watches[idx].inode = STATX_INODE(sb);
+ linux_ctx.file_watches[idx].user_ctx = user_ctx;
+ }
+}
+
+function void
+try_deferred_file_loads(PlatformCtx *ctx)
+{
+ linux_deferred_file_reload_queue *file = ctx->file_reload_queue.next;
+ while (file) {
+ linux_file_watch *fw = ctx->file_watches + file->index;
+
+ statx_buffer sb;
+ syscall5(SYS_statx, AT_FDCWD, (iptr)fw->path, 0, STATX_INO, (iptr)sb);
+
+ fw->handle = syscall3(SYS_inotify_add_watch, ctx->inotify_fd, (iptr)fw->path,
+ LINUX_INOTIFY_MASK);
+ fw->inode = STATX_INODE(sb);
+
+ if ((u64)fw->handle <= -4096UL) {
+ fw->fn(fw->path, fw->user_ctx);
+ file->last->next = file->next;
+ file->next = ctx->file_reload_free_list;
+ ctx->file_reload_free_list = file;
+ file = file->last;
+ } else {
+ file->failures++;
+ #if 0
+ TODO
+ if (file->failures > MAX_FILE_RELOAD_TRIES) {
+ log
+ remove from list
+ }
+ #endif
+ }
+ file = file->next;
+ }
+}
+
+function b32
+defer_file_reload(PlatformCtx *ctx, i32 file_watch_index, statx_buffer *sb)
+{
+ b32 result = 1;
+ linux_file_watch *fw = ctx->file_watches + file_watch_index;
+
+ fw->inode = STATX_INODE(*sb);
+ fw->handle = syscall3(SYS_inotify_add_watch, ctx->inotify_fd, (iptr)fw->path, LINUX_INOTIFY_MASK);
+
+ if ((u64)fw->handle > -4096UL) {
+ result = 0;
+
+ linux_deferred_file_reload_queue *new = ctx->file_reload_free_list;
+ if (new) ctx->file_reload_free_list = new->next;
+ else new = push_struct(&ctx->platform_memory, typeof(*new));
+ new->index = file_watch_index;
+ new->failures = 0;
+ DLLPushDown(&ctx->file_reload_queue, new);
+ }
+
+ return result;
+}
+
+function void
+dispatch_file_watch_events(PlatformCtx *ctx)
+{
+ struct {
+ i32 wd;
+ u32 mask, cookie, len;
+ c8 name[];
+ } *ie;
+
+ u8 *mem = alloc_(&ctx->platform_memory, 4096, 64, 1);
+ s8 buf = {.len = 4096, .data = mem};
+
+ for (;;) {
+ iz rlen = syscall3(SYS_read, ctx->inotify_fd, (iptr)buf.data, buf.len);
+ if (rlen <= 0)
+ break;
+
+ for (u8 *data = buf.data; data < buf.data + rlen; data += sizeof(*ie) + ie->len) {
+ ie = (void *)data;
+ for (i32 i = 0; i < ctx->file_watch_count; i++) {
+ linux_file_watch *fw = ctx->file_watches + i;
+ if (fw->handle != ie->wd)
+ continue;
+
+ b32 file_changed = (ie->mask & IN_CLOSE_WRITE) != 0;
+ file_changed |= (ie->mask & IN_MODIFY) != 0;
+ /* NOTE: some editors and the compiler will rewrite a file
+ * completely and thus the inode will change; here we
+ * detect that and restart the watch */
+ statx_buffer sb;
+ u64 status = syscall5(SYS_statx, AT_FDCWD, (iptr)fw->path, 0, STATX_INO, (iptr)sb);
+
+ if (status > -4096UL || fw->inode != STATX_INODE(sb)) {
+ syscall2(SYS_inotify_rm_watch, ctx->inotify_fd, fw->handle);
+ fw->handle = INVALID_FILE;
+ file_changed = defer_file_reload(ctx, i, &sb);
+ }
+ if (file_changed)
+ fw->fn(fw->path, fw->user_ctx);
+ }
+ }
+ }
+}
+
+function struct stack_base *
+new_stack(iz capacity)
+{
+ u64 p = syscall6(SYS_mmap, 0, capacity, PROT_RW, MAP_ANON|MAP_PRIVATE, -1, 0);
+ if (p > -4096UL)
+ os_fatal(s8("new_stack: mmap failed\n"));
+ i64 count = capacity / sizeof(struct stack_base);
+ /* NOTE: remember the stack grows down; we want to start at the highest address */
+ struct stack_base *result = (struct stack_base *)p + count - 1;
+ return result;
+}
+
+function void
+usage(char *argv0, Stream *err)
+{
+ 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));
+}
+
+function s8
+get_default_cmd(char **envp)
+{
+ s8 result = envp_lookup(s8("SHELL="), envp);
+ if (result.len == 0)
+ result = s8("/bin/sh");
+ return result;
+}
+
+function SLLVariableVector
+parse_environment(Arena *a, char **envp)
+{
+ SLLVariableVector env = {0};
+ for (; *envp; envp++) {
+ s8 e = c_str_to_s8(*envp);
+ if (!s8_prefix_of(s8("TERM="), e)) {
+ Variable *var = push_struct(a, Variable);
+ var->type = VT_S8;
+ var->s8 = e;
+ SLLVariableVectorPush(a, &env, var);
+ }
+ }
+
+ Variable *var = push_struct(a, Variable);
+ var->type = VT_S8;
+ /* TODO: don't pretend to be xterm ? */
+ var->s8 = s8("TERM=xterm");
+ SLLVariableVectorPush(a, &env, var);
+
+ return env;
+}
diff --git a/os_linux_x11.c b/os_linux_x11.c
@@ -0,0 +1,526 @@
+/* See LICENSE for copyright details */
+#define GL_GLEXT_PROTOTYPES
+#include <GLFW/glfw3.h>
+
+/* TODO: fix glfw */
+typedef void *RROutput;
+typedef void *RRCrtc;
+typedef void *Display;
+typedef void *Window;
+
+#define GLFW_EXPOSE_NATIVE_X11
+#define GLFW_NATIVE_INCLUDE_NONE
+#include <GLFW/glfw3native.h>
+
+#include "vtgl.h"
+
+i32 XConnectionNumber(void *display);
+i32 XPending(void *display);
+
+#ifndef _DEBUG
+#define do_debug(...)
+#include "vtgl.c"
+#else
+#include <dlfcn.h>
+
+#define DEBUG_LIB_NAME "./vtgl.so"
+
+#define LIB_FNS \
+ X(vtgl_active_selection) \
+ X(vtgl_initialize) \
+ X(vtgl_render_frame) \
+ X(vtgl_handle_keys) \
+ X(vtgl_frame_step)
+
+#define X(name) static name ## _fn *name;
+LIB_FNS
+#undef X
+
+function OS_FILE_WATCH_CALLBACK_FN(debug_reload_library)
+{
+ PlatformCtx *ctx = user_ctx;
+
+ if (ctx->input.executable_reloaded)
+ return;
+
+ /* NOTE(rnp): spin until render thread finishes its work */
+ while (!ctx->render_stack->thread_asleep);
+
+ ctx->input.executable_reloaded = 1;
+ s8 nl = s8("\n");
+ /* NOTE: glibc sucks and will crash if this is NULL */
+ if (ctx->library_handle)
+ dlclose(ctx->library_handle);
+ ctx->library_handle = dlopen((c8 *)path, RTLD_NOW|RTLD_LOCAL);
+ if (!ctx->library_handle)
+ stream_push_s8s(&ctx->error_stream, 3,
+ (s8 []){s8("dlopen: "), c_str_to_s8(dlerror()), nl});
+
+ #define X(name) \
+ name = dlsym(ctx->library_handle, #name); \
+ if (!name) stream_push_s8s(&ctx->error_stream, 3, (s8 []){s8("dlsym: "), \
+ c_str_to_s8(dlerror()), nl});
+ LIB_FNS
+ #undef X
+
+ stream_push_s8(&ctx->error_stream, s8("Reloaded Main Program\n"));
+
+ os_write_err_msg(stream_to_s8(&ctx->error_stream));
+ stream_reset(&ctx->error_stream, 0);
+}
+#endif /* _DEBUG */
+
+function void
+glfw_error_callback(int code, const char *desc)
+{
+ u8 buf[1024];
+ Stream err = {.capacity = sizeof(buf), .data = buf};
+ stream_push_s8(&err, s8("GLFW Error (0x"));
+ stream_push_hex_u64(&err, code);
+ stream_push_s8(&err, s8("): "));
+ stream_push_s8(&err, c_str_to_s8((char *)desc));
+ stream_push_byte(&err, '\n');
+ os_write_err_msg(stream_to_s8(&err));
+}
+
+function void
+char_callback(GLFWwindow *win, u32 codepoint)
+{
+ PlatformCtx *ctx = glfwGetWindowUserPointer(win);
+ stream_push_s8(&ctx->char_stream, utf8_encode(codepoint));
+}
+
+/* NOTE: called when the window was resized */
+function void
+fb_callback(GLFWwindow *win, i32 w, i32 h)
+{
+ PlatformCtx *ctx = glfwGetWindowUserPointer(win);
+ ctx->input.window_size = (iv2){.w = w, .h = h};
+}
+
+function void
+scroll_callback(GLFWwindow *win, f64 xoff, f64 yoff)
+{
+ PlatformCtx *ctx = glfwGetWindowUserPointer(win);
+ ctx->input.mouse_scroll.x = xoff;
+ ctx->input.mouse_scroll.y = yoff;
+}
+
+function void
+key_callback(GLFWwindow *win, i32 key, i32 scancode, i32 action, i32 modifiers)
+{
+ PlatformCtx *ctx = glfwGetWindowUserPointer(win);
+ TerminalInput *input = &ctx->input;
+
+ /* TODO: base this on X11 keys directly */
+ switch (key) {
+ case GLFW_KEY_LEFT_SHIFT:
+ button_action(input->keys + KEY_LEFT_SHIFT, action == GLFW_PRESS);
+ if (action == GLFW_RELEASE) input->modifiers &= ~MOD_SHIFT;
+ else input->modifiers |= MOD_SHIFT;
+ break;
+ case GLFW_KEY_LEFT_CONTROL:
+ button_action(input->keys + KEY_LEFT_CONTROL, action == GLFW_PRESS);
+ if (action == GLFW_RELEASE) input->modifiers &= ~MOD_CONTROL;
+ else input->modifiers |= MOD_CONTROL;
+ break;
+ case GLFW_KEY_LEFT_ALT:
+ button_action(input->keys + KEY_LEFT_ALT, action == GLFW_PRESS);
+ if (action == GLFW_RELEASE) input->modifiers &= ~MOD_ALT;
+ else input->modifiers |= MOD_ALT;
+ break;
+ case GLFW_KEY_LEFT_SUPER:
+ button_action(input->keys + KEY_LEFT_SUPER, action == GLFW_PRESS);
+ if (action == GLFW_RELEASE) input->modifiers &= ~MOD_SUPER;
+ else input->modifiers |= MOD_SUPER;
+ break;
+ case GLFW_KEY_RIGHT_SHIFT:
+ button_action(input->keys + KEY_RIGHT_SHIFT, action == GLFW_PRESS);
+ if (action == GLFW_RELEASE) input->modifiers &= ~MOD_SHIFT;
+ else input->modifiers |= MOD_SHIFT;
+ break;
+ case GLFW_KEY_RIGHT_CONTROL:
+ button_action(input->keys + KEY_RIGHT_CONTROL, action == GLFW_PRESS);
+ if (action == GLFW_RELEASE) input->modifiers &= ~MOD_CONTROL;
+ else input->modifiers |= MOD_CONTROL;
+ break;
+ case GLFW_KEY_RIGHT_ALT:
+ button_action(input->keys + KEY_RIGHT_ALT, action == GLFW_PRESS);
+ if (action == GLFW_RELEASE) input->modifiers &= ~MOD_ALT;
+ else input->modifiers |= MOD_ALT;
+ break;
+ case GLFW_KEY_RIGHT_SUPER:
+ button_action(input->keys + KEY_RIGHT_SUPER, action == GLFW_PRESS);
+ if (action == GLFW_RELEASE) input->modifiers &= ~MOD_SUPER;
+ else input->modifiers |= MOD_SUPER;
+ break;
+ case GLFW_KEY_MENU:
+ button_action(input->keys + KEY_MENU, action == GLFW_PRESS);
+ break;
+ }
+ vtgl_handle_keys(&ctx->memory, &ctx->input, key, action, modifiers);
+}
+
+function void
+mouse_button_callback(GLFWwindow *win, i32 button, i32 action, i32 modifiers)
+{
+ PlatformCtx *ctx = glfwGetWindowUserPointer(win);
+ TerminalInput *input = &ctx->input;
+
+ switch (button) {
+ case GLFW_MOUSE_BUTTON_LEFT:
+ button_action(input->keys + MOUSE_LEFT, action == GLFW_PRESS);
+ break;
+ case GLFW_MOUSE_BUTTON_RIGHT:
+ button_action(input->keys + MOUSE_RIGHT, action == GLFW_PRESS);
+ break;
+ case GLFW_MOUSE_BUTTON_MIDDLE:
+ button_action(input->keys + MOUSE_MIDDLE, action == GLFW_PRESS);
+ break;
+ case GLFW_MOUSE_BUTTON_4:
+ button_action(input->keys + MOUSE_EXTENDED_0, action == GLFW_PRESS);
+ break;
+ case GLFW_MOUSE_BUTTON_5:
+ button_action(input->keys + MOUSE_EXTENDED_1, action == GLFW_PRESS);
+ break;
+ }
+}
+
+function void
+focus_callback(GLFWwindow *win, i32 focused)
+{
+ PlatformCtx *ctx = glfwGetWindowUserPointer(win);
+ ctx->input.window_focused = focused;
+ /* NOTE: force a refresh as well when the focus changes */
+ ctx->input.window_refreshed = 1;
+}
+
+function void
+refresh_callback(GLFWwindow *win)
+{
+ PlatformCtx *ctx = glfwGetWindowUserPointer(win);
+ ctx->input.window_refreshed = 1;
+}
+
+function GLFWwindow *
+init_window(PlatformCtx *ctx, iv2 window_size)
+{
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+ #ifdef _DEBUG
+ glfwWindowHint(GLFW_CONTEXT_DEBUG, GLFW_TRUE);
+ #endif
+
+ /* NOTE: we initially hide the window so that it can be freely resized behind the
+ * back of the window manager prior to showing */
+ glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
+
+ GLFWwindow *window = glfwCreateWindow(window_size.w, window_size.h, "vtgl", NULL, NULL);
+ if (!window) {
+ glfwTerminate();
+ os_fatal(s8("Failed to spawn GLFW window\n"));
+ }
+ glfwMakeContextCurrent(window);
+ glfwSetWindowUserPointer(window, ctx);
+
+ /* TODO: swap interval is not needed because we will sleep on waiting for terminal input */
+ glfwSwapInterval(1);
+
+ glfwSetCharCallback(window, char_callback);
+ glfwSetFramebufferSizeCallback(window, fb_callback);
+ glfwSetKeyCallback(window, key_callback);
+ glfwSetMouseButtonCallback(window, mouse_button_callback);
+ glfwSetScrollCallback(window, scroll_callback);
+ glfwSetWindowFocusCallback(window, focus_callback);
+ glfwSetWindowRefreshCallback(window, refresh_callback);
+
+ ctx->win_fd = XConnectionNumber(glfwGetX11Display());
+
+ return window;
+}
+
+function void
+update_input(PlatformCtx *ctx)
+{
+ TerminalInput *input = &ctx->input;
+ ctx->input.executable_reloaded = 0;
+ ctx->input.window_refreshed = 0;
+
+ /* NOTE: mouse */
+ input->last_mouse = input->mouse;
+ input->mouse_scroll = (v2){0};
+
+ f64 mouse_x, mouse_y;
+ glfwGetCursorPos(ctx->window, &mouse_x, &mouse_y);
+ input->mouse.x = mouse_x;
+ input->mouse.y = input->window_size.h - mouse_y;
+
+ for (u32 i = 0; i < ARRAY_COUNT(input->keys); i++)
+ input->keys[i].transitions = 0;
+
+ stream_reset(&ctx->char_stream, 0);
+
+ i64 timeout[2] = {0, 25e6};
+ if (input->pending_updates) {
+ timeout[1] = 0;
+ input->pending_updates = 0;
+ }
+
+ sys_fd_set rfd = {0};
+ FD_SET(ctx->child.handle, rfd);
+ FD_SET(ctx->inotify_fd, rfd);
+ FD_SET(ctx->win_fd, rfd);
+
+ i32 max_fd = MAX(ctx->inotify_fd, ctx->child.handle);
+ max_fd = MAX(max_fd, ctx->win_fd);
+ syscall6(SYS_pselect6, max_fd + 1, (iptr)rfd, 0, 0, (iptr)timeout, 0);
+
+ input->data_available = FD_ISSET(ctx->child.handle, rfd) != 0;
+
+ try_deferred_file_loads(ctx);
+ if (FD_ISSET(ctx->inotify_fd, rfd))
+ dispatch_file_watch_events(ctx);
+
+ glfwPollEvents();
+
+ /* NOTE: char_stream was filled in char callback */
+ input->character_input = stream_to_s8(&ctx->char_stream);
+}
+
+function OS_GET_CLIPBOARD_FN(x11_get_clipboard)
+{
+ u8 *text = 0;
+ switch (clipboard) {
+ case OS_CLIPBOARD_PRIMARY: text = (u8 *)glfwGetClipboardString(0); break;
+ case OS_CLIPBOARD_SECONDARY: text = (u8 *)glfwGetX11SelectionString(); break;
+ }
+ return text;
+}
+
+function OS_SET_CLIPBOARD_FN(x11_set_clipboard)
+{
+ switch (clipboard) {
+ case OS_CLIPBOARD_PRIMARY: glfwSetClipboardString(0, (c8 *)buffer); break;
+ case OS_CLIPBOARD_SECONDARY: glfwSetX11SelectionString((c8 *)buffer); break;
+ }
+ return 1;
+}
+
+function OS_GET_WINDOW_TITLE_FN(x11_get_window_title)
+{
+ u8 *title = (u8 *)glfwGetWindowTitle(linux_ctx.window);
+ return title;
+}
+
+function OS_SET_WINDOW_TITLE_FN(x11_set_window_title)
+{
+ glfwSetWindowTitle(linux_ctx.window, (c8 *)title.data);
+}
+
+function void
+linux_render_thread_entry(struct stack_base *stack)
+{
+ {
+ /* NOTE: set thread name */
+ char name[16] = "[render]";
+ syscall2(SYS_prctl, PR_SET_NAME, (iptr)name);
+ /* NOTE: halt until main thread is ready to hand off gl context */
+ syscall4(SYS_futex, (iptr)&stack->work_futex, FUTEX_WAIT, 0, 0);
+
+ glfwMakeContextCurrent(stack->window);
+ }
+
+ for (;;) {
+ stack->thread_asleep = 1;
+ syscall4(SYS_futex, (iptr)&stack->work_futex, FUTEX_WAIT, 0, 0);
+ stack->thread_asleep = 0;
+ vtgl_render_frame(stack->terminal_memory, stack->input, stack->thread_arena);
+ glfwSwapBuffers(stack->window);
+ }
+
+ __builtin_unreachable();
+}
+
+i32
+main(i32 argc, char *argv[], char *envp[])
+{
+ linux_ctx.platform_memory = arena_from_memory_block(os_block_alloc(MB(2)));
+ linux_ctx.error_stream = stream_alloc(&linux_ctx.platform_memory, KB(256));
+
+ iv2 cells = {.x = -1, .y = -1};
+
+ char *argv0 = *argv++;
+ argc--;
+ for (i32 i = 0; i < argc; i++) {
+ char *arg = argv[i];
+ if (!arg || !arg[0])
+ usage(argv0, &linux_ctx.error_stream);
+ if (arg[0] != '-')
+ break;
+ arg++;
+ switch (arg[0]) {
+ case 'g': {
+ if (!argv[i + 1])
+ usage(argv0, &linux_ctx.error_stream);
+ s8 g_arg = c_str_to_s8(argv[i + 1]);
+ struct conversion_result cres = s8_parse_i32_until(g_arg, 'x');
+ if (cres.status == CR_SUCCESS)
+ cells.w = cres.i;
+
+ if (cres.unparsed.len > 0 && cres.unparsed.data[0] == 'x') {
+ s8 remainder = {.len = cres.unparsed.len - 1,
+ .data = cres.unparsed.data + 1};
+ cres = s8_parse_i32(remainder);
+ if (cres.status == CR_SUCCESS)
+ cells.h = cres.i;
+ }
+
+ if (cells.w <= 0 || cells.h <= 0) {
+ stream_push_s8(&linux_ctx.error_stream, s8("ignoring malformed geometry: "));
+ stream_push_s8(&linux_ctx.error_stream, c_str_to_s8(argv[i + 1]));
+ stream_push_byte(&linux_ctx.error_stream, '\n');
+ }
+ argv++;
+ argc--;
+ } break;
+ case 'v':
+ stream_push_s8s(&linux_ctx.error_stream, 2,
+ (s8 []){c_str_to_s8(argv0), s8(" " VERSION "\n")});
+ os_fatal(stream_to_s8(&linux_ctx.error_stream));
+ default:
+ usage(argv0, &linux_ctx.error_stream);
+ }
+ }
+ if (linux_ctx.error_stream.count) {
+ os_write_err_msg(stream_to_s8(&linux_ctx.error_stream));
+ stream_reset(&linux_ctx.error_stream, 0);
+ }
+
+ linux_ctx.render_stack = new_stack(KB(256));
+ linux_ctx.render_stack->entry = linux_render_thread_entry;
+ linux_ctx.render_stack->thread_asleep = 1;
+ new_thread(linux_ctx.render_stack);
+
+ {
+ Arena tmp = linux_ctx.platform_memory;
+ SLLVariableVector environment_block = parse_environment(&tmp, envp);
+
+ /* TODO: build up argv for the child as well */
+ c8 **child_envp = construct_c_str_array(&tmp, environment_block);
+ linux_ctx.child = os_fork_child(get_default_cmd(envp), child_envp);
+ }
+
+ {
+ OSMemoryBlock terminal_memory = os_block_alloc(MB(32));
+ linux_ctx.memory.memory = terminal_memory.memory;
+ linux_ctx.memory.memory_size = terminal_memory.size;
+#ifdef _DEBUG
+ OSMemoryBlock debug_memory = os_block_alloc(MB(128));
+ linux_ctx.memory.debug_memory = debug_memory.memory;
+ linux_ctx.memory.debug_memory_size = debug_memory.size;
+#endif
+ }
+
+ linux_ctx.inotify_fd = syscall1(SYS_inotify_init1, O_NONBLOCK|O_CLOEXEC);
+
+#ifdef _DEBUG
+ debug_reload_library((u8 *)DEBUG_LIB_NAME, &linux_ctx);
+ linux_add_file_watch((u8 *)DEBUG_LIB_NAME, debug_reload_library, &linux_ctx);
+#endif
+
+ linux_ctx.memory.os.add_file_watch = linux_add_file_watch;
+ linux_ctx.memory.os.allocate_ring_buffer = os_allocate_ring_buffer;
+ linux_ctx.memory.os.get_clipboard = x11_get_clipboard;
+ linux_ctx.memory.os.set_clipboard = x11_set_clipboard;
+ linux_ctx.memory.os.read_file = os_read_file;
+ linux_ctx.memory.os.read = os_read;
+ linux_ctx.memory.os.set_terminal_size = os_set_terminal_size;
+ linux_ctx.memory.os.get_window_title = x11_get_window_title;
+ linux_ctx.memory.os.set_window_title = x11_set_window_title;
+ linux_ctx.memory.os.write = os_write;
+ linux_ctx.memory.os.path_separator = '/';
+
+ if (!glfwInit())
+ os_fatal(s8("Failed to init GLFW\n"));
+ glfwSetErrorCallback(glfw_error_callback);
+
+ GLFWmonitor *mon = glfwGetPrimaryMonitor();
+ if (!mon) {
+ glfwTerminate();
+ os_fatal(s8("Failed to find any monitors!\n"));
+ }
+ iv2 monitor_size;
+ glfwGetMonitorWorkarea(mon, NULL, NULL, &monitor_size.w, &monitor_size.h);
+
+ linux_ctx.char_stream = arena_stream(linux_ctx.platform_memory);
+
+ iv2 window_size = {.w = 1280, .h = 720};
+ linux_ctx.window = init_window(&linux_ctx, window_size);
+
+ iv2 requested_size = vtgl_initialize(&linux_ctx.memory, linux_ctx.child.handle, cells, monitor_size);
+ if (requested_size.w > 0 && requested_size.h > 0 &&
+ (requested_size.w != window_size.w || requested_size.h != window_size.h))
+ {
+ glfwSetWindowAttrib(linux_ctx.window, GLFW_FLOATING, GLFW_TRUE);
+ i32 x = ABS(window_size.w - requested_size.w) / 2;
+ i32 y = ABS(window_size.h - requested_size.h) / 2;
+ window_size = requested_size;
+ glfwSetWindowMonitor(linux_ctx.window, 0, x, y, window_size.w, window_size.h, GLFW_DONT_CARE);
+ /* NOTE: resizable must be set after the window is shown; otherwise tiling window
+ * managers will forcibly resize us even if we are supposed to be floating */
+ glfwShowWindow(linux_ctx.window);
+ glfwSetWindowAttrib(linux_ctx.window, GLFW_RESIZABLE, GLFW_TRUE);
+ } else {
+ /* NOTE: on the other hand we should let the window be resized if no size was
+ * explicitly requested */
+ glfwSetWindowAttrib(linux_ctx.window, GLFW_RESIZABLE, GLFW_TRUE);
+ glfwSetWindowPos(linux_ctx.window,
+ ABS(monitor_size.w - window_size.w) / 2,
+ ABS(monitor_size.h - window_size.h) / 2);
+ glfwShowWindow(linux_ctx.window);
+ }
+ glfwMakeContextCurrent(0);
+
+ linux_ctx.input.window_size = window_size;
+
+ linux_ctx.render_stack->input = &linux_ctx.input;
+ linux_ctx.render_stack->terminal_memory = &linux_ctx.memory;
+ linux_ctx.render_stack->thread_arena = arena_from_memory_block(os_block_alloc(MB(8)));
+ linux_ctx.render_stack->window = linux_ctx.window;
+ syscall3(SYS_futex, (iptr)&linux_ctx.render_stack->work_futex, FUTEX_WAKE, 1);
+
+ Range last_sel = {0};
+ f64 last_time = os_get_time();
+ while (!glfwWindowShouldClose(linux_ctx.window)) {
+ if (os_child_exited(linux_ctx.child.process_id))
+ break;
+
+ /* TODO: cpu time excluding waiting for the vblank */
+ f64 current_time = os_get_time();
+ linux_ctx.input.dt = current_time - last_time;
+ last_time = current_time;
+
+ update_input(&linux_ctx);
+ if (vtgl_frame_step(&linux_ctx.memory, &linux_ctx.input))
+ syscall3(SYS_futex, (iptr)&linux_ctx.render_stack->work_futex, FUTEX_WAKE, 1);
+
+ Range current_sel = vtgl_active_selection(&linux_ctx.memory, 0);
+ if (is_valid_range(current_sel) && !equal_range(current_sel, last_sel)) {
+ Stream buf = arena_stream(linux_ctx.platform_memory);
+ vtgl_active_selection(&linux_ctx.memory, &buf);
+ stream_push_byte(&buf, 0);
+ if (!buf.errors)
+ glfwSetX11SelectionString((c8 *)buf.data);
+ last_sel = current_sel;
+ }
+ }
+
+ syscall1(SYS_exit_group, 0);
+ __builtin_unreachable();
+
+ return 0;
+}
diff --git a/platform_linux_aarch64.c b/platform_linux_aarch64.c
@@ -1,172 +0,0 @@
-/* TODO: generate this whole file with a metaprogram */
-
-/* See LICENSE for license details. */
-#ifndef asm
-#ifdef __asm
-#define asm __asm
-#else
-#define asm __asm__
-#endif
-#endif
-
-#define SYS_dup3 24
-#define SYS_inotify_init1 26
-#define SYS_inotify_add_watch 27
-#define SYS_inotify_rm_watch 28
-#define SYS_ioctl 29
-#define SYS_ftruncate 46
-#define SYS_openat 56
-#define SYS_close 57
-#define SYS_read 63
-#define SYS_write 64
-#define SYS_pwrite64 68
-#define SYS_pselect6 72
-#define SYS_exit_group 94
-#define SYS_futex 98
-#define SYS_clock_gettime 113
-#define SYS_setsid 157
-#define SYS_prctl 167
-#define SYS_munmap 215
-#define SYS_clone 220
-#define SYS_execve 221
-#define SYS_mmap 222
-#define SYS_mprotect 226
-#define SYS_madvise 233
-#define SYS_wait4 260
-#define SYS_memfd_create 279
-#define SYS_statx 291
-
-#define SIGCHLD 17
-
-/* NOTE(rnp): technically arm64 can have 4K, 16K or 64K pages but we will just assume 64K */
-#define PAGE_SIZE 65536
-
-/* TODO: check that this is equivalent */
-typedef u64 sys_fd_set[16];
-
-static FORCE_INLINE u64
-syscall0(i64 n)
-{
- register i64 x8 asm("x8") = n;
- register u64 x0 asm("x0");
- asm volatile ("svc 0"
- : "=x"(x0)
- : "x"(x8)
- : "memory", "cc"
- );
- return x0;
-}
-
-static FORCE_INLINE u64
-syscall1(i64 n, i64 a1)
-{
- register i64 x8 asm("x8") = n;
- register u64 x0 asm("x0") = a1;
- asm volatile ("svc 0"
- : "+x"(x0)
- : "x"(x8)
- : "memory", "cc"
- );
- return x0;
-}
-
-static FORCE_INLINE u64
-syscall2(i64 n, i64 a1, i64 a2)
-{
- register i64 x8 asm("x8") = n;
- register u64 x0 asm("x0") = a1;
- register i64 x1 asm("x1") = a2;
- asm volatile ("svc 0"
- : "+x"(x0)
- : "x"(x8), "x"(x1)
- : "memory", "cc"
- );
- return x0;
-}
-
-static FORCE_INLINE u64
-syscall3(i64 n, i64 a1, i64 a2, i64 a3)
-{
- register i64 x8 asm("x8") = n;
- register u64 x0 asm("x0") = a1;
- register i64 x1 asm("x1") = a2;
- register i64 x2 asm("x2") = a3;
- asm volatile ("svc 0"
- : "+x"(x0)
- : "x"(x8), "x"(x1), "x"(x2)
- : "memory", "cc"
- );
- return x0;
-}
-
-static FORCE_INLINE u64
-syscall4(i64 n, i64 a1, i64 a2, i64 a3, i64 a4)
-{
- register i64 x8 asm("x8") = n;
- register u64 x0 asm("x0") = a1;
- register i64 x1 asm("x1") = a2;
- register i64 x2 asm("x2") = a3;
- register i64 x3 asm("x3") = a4;
- asm volatile ("svc 0"
- : "+x"(x0)
- : "x"(x8), "x"(x1), "x"(x2), "x"(x3)
- : "memory", "cc"
- );
- return x0;
-}
-
-static FORCE_INLINE u64
-syscall5(i64 n, i64 a1, i64 a2, i64 a3, i64 a4, i64 a5)
-{
- register i64 x8 asm("x8") = n;
- register u64 x0 asm("x0") = a1;
- register i64 x1 asm("x1") = a2;
- register i64 x2 asm("x2") = a3;
- register i64 x3 asm("x3") = a4;
- register i64 x4 asm("x4") = a5;
- asm volatile ("svc 0"
- : "+x"(x0)
- : "x"(x8), "x"(x1), "x"(x2), "x"(x3), "x"(x4)
- : "memory", "cc"
- );
- return x0;
-}
-
-static FORCE_INLINE u64
-syscall6(i64 n, i64 a1, i64 a2, i64 a3, i64 a4, i64 a5, i64 a6)
-{
- register i64 x8 asm("x8") = n;
- register u64 x0 asm("x0") = a1;
- register i64 x1 asm("x1") = a2;
- register i64 x2 asm("x2") = a3;
- register i64 x3 asm("x3") = a4;
- register i64 x4 asm("x4") = a5;
- register i64 x5 asm("x5") = a6;
- asm volatile ("svc 0"
- : "+x"(x0)
- : "x"(x8), "x"(x1), "x"(x2), "x"(x3), "x"(x4), "x"(x5)
- : "memory", "cc"
- );
- return x0;
-}
-
-__attribute__((naked))
-static i64
-new_thread(void *stack_base)
-{
- asm volatile (
- "mov x8, #220\n" // SYS_clone
- "mov x1, x0\n" // arg2 = new stack
- "mov x0, #0xF00\n" // arg1 = clone flags (VM|FS|FILES|SIGHAND|THREAD|SYSVMEM)
- "movk x0, #0x5, lsl #16\n" // no 32 bit immediates in general on arm
- "svc 0\n"
- "cbnz x0, 1f\n" // don't clobber syscall return in calling thread
- "mov x0, sp\n"
- "ldr x1, [sp]\n" // arm doesn't take the return address from the stack;
- "blr x1\n" // we need to load it and branch to it
- "1: ret"
- ::: "x8", "x1", "memory", "cc"
- );
-}
-
-#include "platform_linux_common.c"
diff --git a/platform_linux_amd64.c b/platform_linux_amd64.c
@@ -1,162 +0,0 @@
-/* See LICENSE for license details. */
-#ifndef asm
-#ifdef __asm
-#define asm __asm
-#else
-#define asm __asm__
-#endif
-#endif
-
-/* TODO: X macro that defines all of these with the appropriate function/macro */
-#define SYS_read 0
-#define SYS_write 1
-#define SYS_close 3
-#define SYS_mmap 9
-#define SYS_mprotect 10
-#define SYS_munmap 11
-#define SYS_ioctl 16
-#define SYS_pwrite64 18
-#define SYS_madvise 28
-#define SYS_clone 56
-#define SYS_execve 59
-#define SYS_wait4 61
-#define SYS_ftruncate 77
-#define SYS_setsid 112
-#define SYS_prctl 157
-#define SYS_futex 202
-#define SYS_getdents64 217
-#define SYS_clock_gettime 228
-#define SYS_exit_group 231
-#define SYS_inotify_add_watch 254
-#define SYS_inotify_rm_watch 255
-#define SYS_openat 257
-#define SYS_pselect6 270
-#define SYS_dup3 292
-#define SYS_inotify_init1 294
-#define SYS_memfd_create 319
-#define SYS_statx 332
-
-#define SIGCHLD 17
-
-#define PAGE_SIZE 4096
-
-typedef u64 sys_fd_set[16];
-
-#define DIRENT_RECLEN_OFF 16
-#define DIRENT_TYPE_OFF 18
-#define DIRENT_NAME_OFF 19
-
-static FORCE_INLINE u64
-syscall1(i64 n, i64 a1)
-{
- u64 result;
- asm volatile ("syscall"
- : "=a"(result)
- : "a"(n), "D"(a1)
- : "rcx", "r11", "memory"
- );
- return result;
-}
-
-static FORCE_INLINE u64
-syscall2(i64 n, i64 a1, i64 a2)
-{
- i64 result;
- asm volatile ("syscall"
- : "=a"(result)
- : "a"(n), "D"(a1), "S"(a2)
- : "rcx", "r11", "memory"
- );
- return result;
-}
-
-static FORCE_INLINE u64
-syscall3(i64 n, i64 a1, i64 a2, i64 a3)
-{
- u64 result;
- asm volatile ("syscall"
- : "=a"(result)
- : "a"(n), "D"(a1), "S"(a2), "d"(a3)
- : "rcx", "r11", "memory"
- );
- return result;
-}
-
-static FORCE_INLINE u64
-syscall4(i64 n, i64 a1, i64 a2, i64 a3, i64 a4)
-{
- u64 result;
- register i64 r10 asm("r10") = a4;
- asm volatile ("syscall"
- : "=a"(result)
- : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10)
- : "rcx", "r11", "memory"
- );
- return result;
-}
-
-
-static FORCE_INLINE u64
-syscall5(i64 n, i64 a1, i64 a2, i64 a3, i64 a4, i64 a5)
-{
- u64 result;
- register i64 r10 asm("r10") = a4;
- register i64 r8 asm("r8") = a5;
- asm volatile ("syscall"
- : "=a"(result)
- : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8)
- : "rcx", "r11", "memory"
- );
- return result;
-}
-
-static FORCE_INLINE u64
-syscall6(i64 n, i64 a1, i64 a2, i64 a3, i64 a4, i64 a5, i64 a6)
-{
- i64 result;
- register i64 r10 asm("r10") = a4;
- register i64 r8 asm("r8") = a5;
- register i64 r9 asm("r9") = a6;
- asm volatile ("syscall"
- : "=a"(result)
- : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9)
- : "rcx", "r11", "memory"
- );
- return result;
-}
-
-/* NOTE: based on code from nullprogram (Chris Wellons) */
-__attribute__((naked))
-static i64
-new_thread(void *stack_base)
-{
- asm volatile (
- "mov %%rdi, %%rsi\n" // arg2 = new stack
- "mov $0x50F00, %%edi\n" // arg1 = clone flags (VM|FS|FILES|SIGHAND|THREAD|SYSVMEM)
- "mov $56, %%eax\n" // SYS_clone
- "syscall\n"
- "test %%eax, %%eax\n" // don't mess with the calling thread's stack
- "jne 1f\n"
- "mov %%rsp, %%rdi\n"
- "sub $8, %%rsp\n" // place a 0 return branch pointer on the child's stack
- "push (%%rdi)\n" // push the entry point back onto the stack for use by ret
- "1: ret\n"
- ::: "rax", "rcx", "rsi", "rdi", "r11", "memory"
- );
-}
-
-#include "platform_linux_common.c"
-
-#if 0
-asm (
- ".intel_syntax noprefix\n"
- ".global _start\n"
- "_start:\n"
- " mov edi, DWORD PTR [rsp]\n"
- " lea rsi, [rsp+8]\n"
- " lea rdx, [rsi+rdi*8+8]\n"
- " call linux_main\n"
- " ud2\n"
- ".att_syntax\n"
-);
-#endif
diff --git a/platform_linux_common.c b/platform_linux_common.c
@@ -1,558 +0,0 @@
-#define FUTEX_WAIT 0
-#define FUTEX_WAKE 1
-
-#define CLOCK_MONOTONIC 1
-
-#define PR_SET_NAME 15
-
-#define PROT_NONE 0x00
-#define PROT_READ 0x01
-#define PROT_RW 0x03
-
-#define MFD_CLOEXEC 0x01
-
-#define MAP_SHARED 0x01
-#define MAP_PRIVATE 0x02
-#define MAP_FIXED 0x10
-#define MAP_ANON 0x20
-
-#define MADV_FREE 8
-#define MADV_HUGEPAGE 14
-
-#define O_RDONLY 0x00000
-#define O_WRONLY 0x00001
-#define O_RDWR 0x00002
-#define O_CREAT 0x00040
-#define O_NOCTTY 0x00100
-#define O_APPEND 0x00400
-#define O_NONBLOCK 0x00800
-#define O_CLOEXEC 0x80000
-
-#define IN_CLOSE_WRITE 0x00000008
-#define IN_CLOSE_NOWRITE 0x00000010
-#define IN_MODIFY 0x00000002
-
-#define AT_EMPTY_PATH 0x1000
-#define AT_FDCWD (-100)
-
-#define LINUX_INOTIFY_MASK (IN_CLOSE_WRITE|IN_CLOSE_NOWRITE|IN_MODIFY)
-
-#define WNOHANG 1
-#define W_IF_EXITED(s) (!((s) & 0x7F))
-
-/* TODO: glibc/gcc indirectly include sys/select.h if you include immintrin.h. If that
- * header is removed this can also be removed */
-#undef FD_SET
-#undef FD_ISSET
-
-#define FD_SET(d, s) ((s)[(d) / (8 * sizeof(*(s)))] |= (1ULL << ((d) % (8 * sizeof(*(s))))))
-#define FD_ISSET(d, s) ((s)[(d) / (8 * sizeof(*(s)))] & (1ULL << ((d) % (8 * sizeof(*(s))))))
-
-typedef __attribute__((aligned(16))) u8 statx_buffer[256];
-#define STATX_BUF_MEMBER(sb, t, off) (*(t *)((u8 *)(sb) + off))
-#define STATX_INODE(sb) STATX_BUF_MEMBER(sb, u64, 32)
-#define STATX_FILE_SIZE(sb) STATX_BUF_MEMBER(sb, u64, 40)
-
-#define STATX_INO 0x00000100U
-#define STATX_SIZE 0x00000200U
-
-#define TIOCSCTTY 0x540E
-#define TIOCSWINSZ 0x5414
-#define TIOCSPTLCK 0x40045431 /* (un)lock pty */
-#define TIOCGPTN 0x80045430 /* get pty number */
-
-#ifndef VERSION
-#define VERSION "unknown"
-#endif
-
-#define OS_MAP_READ PROT_READ
-#define OS_MAP_PRIVATE MAP_PRIVATE
-
-struct __attribute__((aligned(16))) stack_base {
- void (*entry)(struct stack_base *stack);
- Arena thread_arena;
- void *window;
- TerminalMemory *terminal_memory;
- TerminalInput *input;
- i32 work_futex;
- b32 thread_asleep;
-};
-
-typedef struct {
- platform_file_watch_callback_fn *fn;
- u8 *path;
- void *user_ctx;
- u64 inode;
- i32 handle;
-} linux_file_watch;
-
-typedef struct linux_deferred_file_reload_queue {
- struct linux_deferred_file_reload_queue *next;
- struct linux_deferred_file_reload_queue *last;
- i32 index;
- i32 failures;
-} linux_deferred_file_reload_queue;
-
-typedef struct {
- iptr handle;
- iptr process_id;
-} linux_platform_process;
-
-typedef struct {
- Arena platform_memory;
- void *window;
-
- TerminalMemory memory;
- TerminalInput input;
-
- Stream char_stream;
-
- linux_platform_process child;
- i32 inotify_fd;
- i32 win_fd;
-
- linux_deferred_file_reload_queue file_reload_queue;
- linux_deferred_file_reload_queue *file_reload_free_list;
- linux_file_watch file_watches[32];
- i32 file_watch_count;
-
- Stream error_stream;
-
- struct stack_base *render_stack;
-
-#ifdef _DEBUG
- void *library_handle;
-#endif
-} PlatformCtx;
-static PlatformCtx linux_ctx;
-
-static void
-os_write_err_msg(s8 msg)
-{
- syscall3(SYS_write, 2, (iptr)msg.data, msg.len);
-}
-
-__attribute__((noreturn))
-static void
-os_fatal(s8 msg)
-{
- os_write_err_msg(msg);
- syscall1(SYS_exit_group, 1);
- __builtin_unreachable();
-}
-
-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)
-{
- u64 result = syscall4(SYS_openat, AT_FDCWD, (iptr)name, os_file_attribute_to_mode(attr), 0660);
- if (result > -4096UL)
- result = INVALID_FILE;
- return result;
-}
-
-static b32
-os_offset_write(iptr file, s8 raw, size offset)
-{
- size result = syscall4(SYS_pwrite64, file, (iptr)raw.data, raw.len, offset);
- return result == raw.len;
-}
-
-static PLATFORM_WRITE_FN(os_write)
-{
- size result = syscall3(SYS_write, file, (iptr)raw.data, raw.len);
- return result == raw.len;
-}
-
-static void
-os_close(iptr file)
-{
- syscall1(SYS_close, file);
-}
-
-static PLATFORM_READ_FN(os_read)
-{
- u64 r = 0, remaining = buffer.len, total_bytes_read = 0;
-
- do {
- remaining -= r;
- total_bytes_read += r;
- r = syscall3(SYS_read, file, (iptr)(buffer.data + total_bytes_read), remaining);
- } while (r <= -4096UL && remaining != 0);
-
- return total_bytes_read;
-}
-
-static PLATFORM_READ_FILE_FN(os_read_file)
-{
- s8 result = {0};
-
- statx_buffer sb;
- u64 fd = syscall4(SYS_openat, AT_FDCWD, (iptr)path, O_RDONLY, 0);
- u64 status = syscall5(SYS_statx, fd, 0, AT_EMPTY_PATH, STATX_SIZE, (iptr)sb);
-
- if (fd <= -4096UL && status == 0) {
- result = s8alloc(a, STATX_FILE_SIZE(sb));
- size rlen = os_read(fd, result);
- syscall1(SYS_close, fd);
- if (result.len != rlen)
- result.len = 0;
- }
-
- return result;
-}
-
-static MemoryBlock
-os_block_alloc(size requested_size)
-{
- MemoryBlock result = {0};
-
- /* TODO: query system for HUGETLB support and use those instead of page size */
- size alloc_size = requested_size;
- if (alloc_size % PAGE_SIZE != 0)
- alloc_size += PAGE_SIZE - alloc_size % PAGE_SIZE;
-
- u64 memory = syscall6(SYS_mmap, 0, alloc_size, PROT_RW, MAP_ANON|MAP_PRIVATE, -1, 0);
- if (memory <= -4096UL) {
- result.memory = (void *)memory;
- result.size = alloc_size;
- syscall3(SYS_madvise, memory, alloc_size, MADV_HUGEPAGE);
- }
-
- return result;
-}
-
-static void
-os_release_memory_block(MemoryBlock memory)
-{
- syscall3(SYS_madvise, (iptr)memory.memory, memory.size, MADV_FREE);
- syscall3(SYS_mprotect, (iptr)memory.memory, memory.size, PROT_NONE);
-}
-
-static void
-os_release_ring_buffer(RingBuf *rb)
-{
- syscall2(SYS_munmap, (iptr)(rb->buf - rb->cap), rb->cap * 3);
-}
-
-static f64
-os_get_time(void)
-{
- i64 timespec[2];
- syscall2(SYS_clock_gettime, CLOCK_MONOTONIC, (iptr)timespec);
- f64 result = timespec[0] + ((f64)timespec[1]) * 1e-9;
- return result;
-}
-
-static os_mapped_file
-os_map_file(char *path, i32 mode, i32 perm)
-{
- os_mapped_file result = {0};
-
- i32 open_mode = 0;
- switch (mode) {
- case OS_MAP_READ: open_mode = O_RDONLY; break;
- default: ASSERT(0);
- }
-
- statx_buffer sb;
- u64 fd = syscall4(SYS_openat, AT_FDCWD, (iptr)path, open_mode, 0);
- u64 status = syscall5(SYS_statx, fd, 0, AT_EMPTY_PATH, STATX_SIZE, (iptr)sb);
-
- if (fd <= -4096UL && status == 0) {
- u64 memory = syscall6(SYS_mmap, 0, STATX_FILE_SIZE(sb), mode, perm, fd, 0);
- if (memory <= -4096UL) {
- result.data = (u8 *)memory;
- result.len = STATX_FILE_SIZE(sb);
- }
- syscall1(SYS_close, fd);
- }
-
- return result;
-}
-
-static PLATFORM_ALLOCATE_RING_BUFFER_FN(os_allocate_ring_buffer)
-{
- /* TODO: query system for HUGETLB support and use those instead of page size */
- if (capacity % PAGE_SIZE != 0)
- capacity += PAGE_SIZE - capacity % PAGE_SIZE;
- ASSERT(capacity % PAGE_SIZE == 0);
-
- u64 fd = syscall2(SYS_memfd_create, (iptr)"vtgl:rb", MFD_CLOEXEC);
- if (fd > -4096UL) os_fatal(s8("os_alloc_ring_buffer: failed to open mem_fd\n"));
- syscall2(SYS_ftruncate, fd, capacity);
-
- rb->widx = 0;
- rb->filled = 0;
- rb->cap = capacity;
- rb->buf = (u8 *)syscall6(SYS_mmap, 0, (iptr)(3 * rb->cap), 0, MAP_ANON|MAP_PRIVATE, -1, 0);
- if ((u64)rb->buf > -4096UL)
- os_fatal(s8("os_alloc_ring_buffer: initial mmap failed\n"));
- syscall3(SYS_madvise, (iptr)rb->buf, 3 * rb->cap, MADV_HUGEPAGE);
-
- for (i32 i = 0; i < 3; i++) {
- u64 memory = syscall6(SYS_mmap, (iptr)(rb->buf + i * rb->cap), rb->cap, PROT_RW,
- MAP_FIXED|MAP_SHARED, fd, 0);
- if (memory > -4096UL) {
- 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));
- }
- }
- syscall1(SYS_close, fd);
-
- /* NOTE: start in the middle page */
- rb->buf += rb->cap;
-}
-
-static b32
-os_child_exited(iptr pid)
-{
- i64 status;
- i64 r = syscall4(SYS_wait4, pid, (iptr)&status, WNOHANG, 0);
- return r == pid && W_IF_EXITED(status);
-}
-
-static linux_platform_process
-os_fork_child(s8 cmd, c8 **envp)
-{
- i32 n = 0;
-
- /* NOTE: we open in non-blocking mode so that we can try and fully drain the pipe
- * before processing. Otherwise a single read will be limited to the page size */
- u64 m = syscall4(SYS_openat, AT_FDCWD, (iptr)"/dev/ptmx", O_RDWR|O_NOCTTY|O_NONBLOCK|O_CLOEXEC, 0);
- if (m > -4096UL) os_fatal(s8("os_fork_child: failed to open master terminal\n"));
- /* NOTE: first unlock the tty, then get a valid pty number */
- if (syscall3(SYS_ioctl, m, TIOCSPTLCK, (iptr)&n) || syscall3(SYS_ioctl, m, TIOCGPTN, (iptr)&n))
- os_fatal(s8("os_fork_child: failed to get a pty number\n"));
-
- u8 buffer[20] = {"/dev/pts/"};
- Stream sbuf = {.buf = buffer, .cap = 20, .widx = sizeof("/dev/pts/") - 1};
- stream_push_i64(&sbuf, n);
- stream_push_byte(&sbuf, 0);
-
- u64 s = syscall4(SYS_openat, AT_FDCWD, (iptr)sbuf.buf, O_RDWR|O_NOCTTY, 0);
- if (s > -4096UL) os_fatal(s8("os_fork_child: failed to open slave terminal\n"));
-
- u64 pid = syscall2(SYS_clone, SIGCHLD, 0);
- if (pid > -4096UL) os_fatal(s8("os_fork_child: failed to fork a child\n"));
-
- if (pid == 0) {
- syscall1(SYS_setsid, 0);
- syscall3(SYS_dup3, s, 0, 0);
- syscall3(SYS_dup3, s, 1, 0);
- syscall3(SYS_dup3, s, 2, 0);
- syscall3(SYS_ioctl, s, TIOCSCTTY, 0);
- if (s > 2) syscall1(SYS_close, s);
- ASSERT(cmd.data[cmd.len] == 0);
- u8 *argv[] = {cmd.data, 0};
- syscall3(SYS_execve, (iptr)cmd.data, (iptr)argv, (iptr)envp);
- __builtin_unreachable();
- os_fatal(s8("failed to exec child\n"));
- }
- syscall1(SYS_close, s);
-
- return (linux_platform_process){.process_id = pid, .handle = m};
-}
-
-static PLATFORM_SET_TERMINAL_SIZE_FN(os_set_terminal_size)
-{
- u16 win_size[4];
- win_size[0] = rows;
- win_size[1] = columns;
- win_size[2] = window_width;
- win_size[3] = window_height;
- if (syscall3(SYS_ioctl, child, TIOCSWINSZ, (iptr)win_size) > -4096UL)
- os_write_err_msg(s8("os_set_term_size\n"));
-}
-
-static PLATFORM_ADD_FILE_WATCH_FN(linux_add_file_watch)
-{
- u64 wd = syscall3(SYS_inotify_add_watch, linux_ctx.inotify_fd, (iptr)path, LINUX_INOTIFY_MASK);
- if (wd <= -4096UL) {
- statx_buffer sb;
- syscall5(SYS_statx, AT_FDCWD, (iptr)path, 0, STATX_INO, (iptr)sb);
-
- i32 idx = linux_ctx.file_watch_count++;
- ASSERT(idx < ARRAY_COUNT(linux_ctx.file_watches));
- linux_ctx.file_watches[idx].fn = fn;
- linux_ctx.file_watches[idx].path = path;
- linux_ctx.file_watches[idx].handle = wd;
- linux_ctx.file_watches[idx].inode = STATX_INODE(sb);
- linux_ctx.file_watches[idx].user_ctx = user_ctx;
- }
-}
-
-static void
-try_deferred_file_loads(PlatformCtx *ctx)
-{
- linux_deferred_file_reload_queue *file = ctx->file_reload_queue.next;
- while (file) {
- linux_file_watch *fw = ctx->file_watches + file->index;
-
- statx_buffer sb;
- syscall5(SYS_statx, AT_FDCWD, (iptr)fw->path, 0, STATX_INO, (iptr)sb);
-
- fw->handle = syscall3(SYS_inotify_add_watch, ctx->inotify_fd, (iptr)fw->path,
- LINUX_INOTIFY_MASK);
- fw->inode = STATX_INODE(sb);
-
- if ((u64)fw->handle <= -4096UL) {
- fw->fn(fw->path, fw->user_ctx);
- file->last->next = file->next;
- file->next = ctx->file_reload_free_list;
- ctx->file_reload_free_list = file;
- file = file->last;
- } else {
- file->failures++;
- #if 0
- TODO
- if (file->failures > MAX_FILE_RELOAD_TRIES) {
- log
- remove from list
- }
- #endif
- }
- file = file->next;
- }
-}
-
-static b32
-defer_file_reload(PlatformCtx *ctx, i32 file_watch_index, statx_buffer *sb)
-{
- b32 result = 1;
- linux_file_watch *fw = ctx->file_watches + file_watch_index;
-
- fw->inode = STATX_INODE(*sb);
- fw->handle = syscall3(SYS_inotify_add_watch, ctx->inotify_fd, (iptr)fw->path, LINUX_INOTIFY_MASK);
-
- if ((u64)fw->handle > -4096UL) {
- result = 0;
-
- linux_deferred_file_reload_queue *new = ctx->file_reload_free_list;
- if (new) ctx->file_reload_free_list = new->next;
- else new = push_struct(&ctx->platform_memory, typeof(*new));
- new->index = file_watch_index;
- new->failures = 0;
- DLLPushDown(&ctx->file_reload_queue, new);
- }
-
- return result;
-}
-
-static void
-dispatch_file_watch_events(PlatformCtx *ctx)
-{
- struct {
- i32 wd;
- u32 mask, cookie, len;
- c8 name[];
- } *ie;
-
- u8 *mem = alloc_(&ctx->platform_memory, 4096, 64, 1);
- s8 buf = {.len = 4096, .data = mem};
-
- for (;;) {
- size rlen = syscall3(SYS_read, ctx->inotify_fd, (iptr)buf.data, buf.len);
- if (rlen <= 0)
- break;
-
- for (u8 *data = buf.data; data < buf.data + rlen; data += sizeof(*ie) + ie->len) {
- ie = (void *)data;
- for (i32 i = 0; i < ctx->file_watch_count; i++) {
- linux_file_watch *fw = ctx->file_watches + i;
- if (fw->handle != ie->wd)
- continue;
-
- b32 file_changed = (ie->mask & IN_CLOSE_WRITE) != 0;
- file_changed |= (ie->mask & IN_MODIFY) != 0;
- /* NOTE: some editors and the compiler will rewrite a file
- * completely and thus the inode will change; here we
- * detect that and restart the watch */
- statx_buffer sb;
- u64 status = syscall5(SYS_statx, AT_FDCWD, (iptr)fw->path, 0, STATX_INO, (iptr)sb);
-
- if (status > -4096UL || fw->inode != STATX_INODE(sb)) {
- syscall2(SYS_inotify_rm_watch, ctx->inotify_fd, fw->handle);
- fw->handle = INVALID_FILE;
- file_changed = defer_file_reload(ctx, i, &sb);
- }
- if (file_changed)
- fw->fn(fw->path, fw->user_ctx);
- }
- }
- }
-}
-
-static struct stack_base *
-new_stack(size capacity)
-{
- u64 p = syscall6(SYS_mmap, 0, capacity, PROT_RW, MAP_ANON|MAP_PRIVATE, -1, 0);
- if (p > -4096UL)
- os_fatal(s8("new_stack: mmap failed\n"));
- i64 count = capacity / sizeof(struct stack_base);
- /* NOTE: remember the stack grows down; we want to start at the highest address */
- struct stack_base *result = (struct stack_base *)p + count - 1;
- return result;
-}
-
-static void
-usage(char *argv0, Stream *err)
-{
- 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));
-}
-
-static s8
-get_default_cmd(char **envp)
-{
- s8 result = envp_lookup(s8("SHELL="), envp);
- if (result.len == 0)
- result = s8("/bin/sh");
- return result;
-}
-
-static SLLVariableVector
-parse_environment(Arena *a, char **envp)
-{
- SLLVariableVector env = {0};
- for (; *envp; envp++) {
- s8 e = c_str_to_s8(*envp);
- if (!s8_prefix_of(s8("TERM="), e)) {
- Variable *var = push_struct(a, Variable);
- var->type = VT_S8;
- var->s8 = e;
- SLLVariableVectorPush(a, &env, var);
- }
- }
-
- Variable *var = push_struct(a, Variable);
- var->type = VT_S8;
- /* TODO: don't pretend to be xterm ? */
- var->s8 = s8("TERM=xterm");
- SLLVariableVectorPush(a, &env, var);
-
- return env;
-}
diff --git a/platform_linux_x11.c b/platform_linux_x11.c
@@ -1,544 +0,0 @@
-/* See LICENSE for copyright details */
-#define GL_GLEXT_PROTOTYPES
-#include <GLFW/glfw3.h>
-
-/* TODO: fix glfw */
-typedef void *RROutput;
-typedef void *RRCrtc;
-typedef void *Display;
-typedef void *Window;
-
-#define GLFW_EXPOSE_NATIVE_X11
-#define GLFW_NATIVE_INCLUDE_NONE
-#include <GLFW/glfw3native.h>
-
-#include "vtgl.h"
-
-i32 XConnectionNumber(void *display);
-i32 XPending(void *display);
-
-#ifndef _DEBUG
-#define do_debug(...)
-#include "vtgl.c"
-#else
-#include <dlfcn.h>
-
-#define DEBUG_LIB_NAME "./vtgl.so"
-
-#define LIB_FNS \
- X(vtgl_active_selection) \
- X(vtgl_initialize) \
- X(vtgl_render_frame) \
- X(vtgl_handle_keys) \
- X(vtgl_frame_step)
-
-#define X(name) static name ## _fn *name;
-LIB_FNS
-#undef X
-
-static PLATFORM_FILE_WATCH_CALLBACK_FN(debug_reload_library)
-{
- PlatformCtx *ctx = user_ctx;
-
- if (ctx->input.executable_reloaded)
- return;
-
- /* NOTE(rnp): spin until render thread finishes its work */
- while (!ctx->render_stack->thread_asleep);
-
- ctx->input.executable_reloaded = 1;
- s8 nl = s8("\n");
- /* NOTE: glibc sucks and will crash if this is NULL */
- if (ctx->library_handle)
- dlclose(ctx->library_handle);
- ctx->library_handle = dlopen((c8 *)path, RTLD_NOW|RTLD_LOCAL);
- if (!ctx->library_handle)
- stream_push_s8s(&ctx->error_stream, 3,
- (s8 []){s8("dlopen: "), c_str_to_s8(dlerror()), nl});
-
- #define X(name) \
- name = dlsym(ctx->library_handle, #name); \
- if (!name) stream_push_s8s(&ctx->error_stream, 3, (s8 []){s8("dlsym: "), \
- c_str_to_s8(dlerror()), nl});
- LIB_FNS
- #undef X
-
- stream_push_s8(&ctx->error_stream, s8("Reloaded Main Program\n"));
-
- os_write_err_msg(stream_to_s8(&ctx->error_stream));
- ctx->error_stream.widx = 0;
-}
-#endif /* _DEBUG */
-
-static void
-glfw_error_callback(int code, const char *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 void
-char_callback(GLFWwindow *win, u32 codepoint)
-{
- PlatformCtx *ctx = glfwGetWindowUserPointer(win);
- stream_push_s8(&ctx->char_stream, utf8_encode(codepoint));
-}
-
-/* NOTE: called when the window was resized */
-static void
-fb_callback(GLFWwindow *win, i32 w, i32 h)
-{
- PlatformCtx *ctx = glfwGetWindowUserPointer(win);
- ctx->input.window_size = (iv2){.w = w, .h = h};
-}
-
-static void
-scroll_callback(GLFWwindow *win, f64 xoff, f64 yoff)
-{
- PlatformCtx *ctx = glfwGetWindowUserPointer(win);
- ctx->input.mouse_scroll.x = xoff;
- ctx->input.mouse_scroll.y = yoff;
-}
-
-static void
-key_callback(GLFWwindow *win, i32 key, i32 scancode, i32 action, i32 modifiers)
-{
- PlatformCtx *ctx = glfwGetWindowUserPointer(win);
- TerminalInput *input = &ctx->input;
-
- /* TODO: base this on X11 keys directly */
- switch (key) {
- case GLFW_KEY_LEFT_SHIFT:
- button_action(input->keys + KEY_LEFT_SHIFT, action == GLFW_PRESS);
- if (action == GLFW_RELEASE) input->modifiers &= ~MOD_SHIFT;
- else input->modifiers |= MOD_SHIFT;
- break;
- case GLFW_KEY_LEFT_CONTROL:
- button_action(input->keys + KEY_LEFT_CONTROL, action == GLFW_PRESS);
- if (action == GLFW_RELEASE) input->modifiers &= ~MOD_CONTROL;
- else input->modifiers |= MOD_CONTROL;
- break;
- case GLFW_KEY_LEFT_ALT:
- button_action(input->keys + KEY_LEFT_ALT, action == GLFW_PRESS);
- if (action == GLFW_RELEASE) input->modifiers &= ~MOD_ALT;
- else input->modifiers |= MOD_ALT;
- break;
- case GLFW_KEY_LEFT_SUPER:
- button_action(input->keys + KEY_LEFT_SUPER, action == GLFW_PRESS);
- if (action == GLFW_RELEASE) input->modifiers &= ~MOD_SUPER;
- else input->modifiers |= MOD_SUPER;
- break;
- case GLFW_KEY_RIGHT_SHIFT:
- button_action(input->keys + KEY_RIGHT_SHIFT, action == GLFW_PRESS);
- if (action == GLFW_RELEASE) input->modifiers &= ~MOD_SHIFT;
- else input->modifiers |= MOD_SHIFT;
- break;
- case GLFW_KEY_RIGHT_CONTROL:
- button_action(input->keys + KEY_RIGHT_CONTROL, action == GLFW_PRESS);
- if (action == GLFW_RELEASE) input->modifiers &= ~MOD_CONTROL;
- else input->modifiers |= MOD_CONTROL;
- break;
- case GLFW_KEY_RIGHT_ALT:
- button_action(input->keys + KEY_RIGHT_ALT, action == GLFW_PRESS);
- if (action == GLFW_RELEASE) input->modifiers &= ~MOD_ALT;
- else input->modifiers |= MOD_ALT;
- break;
- case GLFW_KEY_RIGHT_SUPER:
- button_action(input->keys + KEY_RIGHT_SUPER, action == GLFW_PRESS);
- if (action == GLFW_RELEASE) input->modifiers &= ~MOD_SUPER;
- else input->modifiers |= MOD_SUPER;
- break;
- case GLFW_KEY_MENU:
- button_action(input->keys + KEY_MENU, action == GLFW_PRESS);
- break;
- }
- vtgl_handle_keys(&ctx->memory, &ctx->input, key, action, modifiers);
-}
-
-static void
-mouse_button_callback(GLFWwindow *win, i32 button, i32 action, i32 modifiers)
-{
- PlatformCtx *ctx = glfwGetWindowUserPointer(win);
- TerminalInput *input = &ctx->input;
-
- switch (button) {
- case GLFW_MOUSE_BUTTON_LEFT:
- button_action(input->keys + MOUSE_LEFT, action == GLFW_PRESS);
- break;
- case GLFW_MOUSE_BUTTON_RIGHT:
- button_action(input->keys + MOUSE_RIGHT, action == GLFW_PRESS);
- break;
- case GLFW_MOUSE_BUTTON_MIDDLE:
- button_action(input->keys + MOUSE_MIDDLE, action == GLFW_PRESS);
- break;
- case GLFW_MOUSE_BUTTON_4:
- button_action(input->keys + MOUSE_EXTENDED_0, action == GLFW_PRESS);
- break;
- case GLFW_MOUSE_BUTTON_5:
- button_action(input->keys + MOUSE_EXTENDED_1, action == GLFW_PRESS);
- break;
- }
-}
-
-static void
-focus_callback(GLFWwindow *win, i32 focused)
-{
- PlatformCtx *ctx = glfwGetWindowUserPointer(win);
- ctx->input.window_focused = focused;
- /* NOTE: force a refresh as well when the focus changes */
- ctx->input.window_refreshed = 1;
-}
-
-static void
-refresh_callback(GLFWwindow *win)
-{
- PlatformCtx *ctx = glfwGetWindowUserPointer(win);
- ctx->input.window_refreshed = 1;
-}
-
-static GLFWwindow *
-init_window(PlatformCtx *ctx, iv2 window_size)
-{
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
- glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-
- #ifdef _DEBUG
- glfwWindowHint(GLFW_CONTEXT_DEBUG, GLFW_TRUE);
- #endif
-
- /* NOTE: we initially hide the window so that it can be freely resized behind the
- * back of the window manager prior to showing */
- glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
- glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
-
- GLFWwindow *window = glfwCreateWindow(window_size.w, window_size.h, "vtgl", NULL, NULL);
- if (!window) {
- glfwTerminate();
- os_fatal(s8("Failed to spawn GLFW window\n"));
- }
- glfwMakeContextCurrent(window);
- glfwSetWindowUserPointer(window, ctx);
-
- /* TODO: swap interval is not needed because we will sleep on waiting for terminal input */
- glfwSwapInterval(1);
-
- glfwSetCharCallback(window, char_callback);
- glfwSetFramebufferSizeCallback(window, fb_callback);
- glfwSetKeyCallback(window, key_callback);
- glfwSetMouseButtonCallback(window, mouse_button_callback);
- glfwSetScrollCallback(window, scroll_callback);
- glfwSetWindowFocusCallback(window, focus_callback);
- glfwSetWindowRefreshCallback(window, refresh_callback);
-
- ctx->win_fd = XConnectionNumber(glfwGetX11Display());
-
- return window;
-}
-
-static void
-update_input(PlatformCtx *ctx)
-{
- TerminalInput *input = &ctx->input;
- ctx->input.executable_reloaded = 0;
- ctx->input.window_refreshed = 0;
-
- /* NOTE: mouse */
- input->last_mouse = input->mouse;
- input->mouse_scroll = (v2){0};
-
- f64 mouse_x, mouse_y;
- glfwGetCursorPos(ctx->window, &mouse_x, &mouse_y);
- input->mouse.x = mouse_x;
- input->mouse.y = input->window_size.h - mouse_y;
-
- for (u32 i = 0; i < ARRAY_COUNT(input->keys); i++)
- input->keys[i].transitions = 0;
-
- ctx->char_stream.widx = 0;
-
- i64 timeout[2] = {0, 25e6};
- if (input->pending_updates) {
- timeout[1] = 0;
- input->pending_updates = 0;
- }
-
- sys_fd_set rfd = {0};
- FD_SET(ctx->child.handle, rfd);
- FD_SET(ctx->inotify_fd, rfd);
- FD_SET(ctx->win_fd, rfd);
-
- i32 max_fd = MAX(ctx->inotify_fd, ctx->child.handle);
- max_fd = MAX(max_fd, ctx->win_fd);
- syscall6(SYS_pselect6, max_fd + 1, (iptr)rfd, 0, 0, (iptr)timeout, 0);
-
- input->data_available = FD_ISSET(ctx->child.handle, rfd) != 0;
-
- try_deferred_file_loads(ctx);
- if (FD_ISSET(ctx->inotify_fd, rfd))
- dispatch_file_watch_events(ctx);
-
- glfwPollEvents();
-
- /* NOTE: char_stream was filled in char callback */
- input->character_input = stream_to_s8(&ctx->char_stream);
-}
-
-static PLATFORM_CLIPBOARD_FN(x11_get_clipboard)
-{
- /* NOTE: this does a bunch of extra copying and other garbage. both GLFW and X11 are
- * at fault. The API is designed to do what the terminal wants and not be constrained
- * by GLFW and X11 garbage */
- ASSERT(buffer);
- char *text = 0;
- switch (clipboard) {
- case CLIPBOARD_0: text = (c8 *)glfwGetClipboardString(0); break;
- case CLIPBOARD_1: text = (c8 *)glfwGetX11SelectionString(); break;
- }
- if (text) {
- /* TODO: we may need to replace '\n' with '\r\n' */
- stream_push_s8(buffer, c_str_to_s8(text));
- }
-
- return !buffer->errors;
-}
-
-static PLATFORM_CLIPBOARD_FN(x11_set_clipboard)
-{
- ASSERT(buffer);
- stream_push_byte(buffer, 0);
-
- if (!buffer->errors) {
- switch (clipboard) {
- case CLIPBOARD_0: glfwSetClipboardString(0, (c8 *)buffer->buf); break;
- case CLIPBOARD_1: glfwSetX11SelectionString((c8 *)buffer->buf); break;
- }
- }
- return !buffer->errors;
-}
-
-static PLATFORM_WINDOW_TITLE_FN(x11_get_window_title)
-{
- ASSERT(buffer);
- char *title = (c8 *)glfwGetWindowTitle(linux_ctx.window);
- if (title) stream_push_s8(buffer, c_str_to_s8(title));
-}
-
-static PLATFORM_WINDOW_TITLE_FN(x11_set_window_title)
-{
- ASSERT(buffer);
- stream_push_byte(buffer, 0);
- if (!buffer->errors)
- glfwSetWindowTitle(linux_ctx.window, (c8 *)buffer->buf);
-}
-
-static void
-linux_render_thread_entry(struct stack_base *stack)
-{
- {
- /* NOTE: set thread name */
- char name[16] = "[render]";
- syscall2(SYS_prctl, PR_SET_NAME, (iptr)name);
- /* NOTE: halt until main thread is ready to hand off gl context */
- syscall4(SYS_futex, (iptr)&stack->work_futex, FUTEX_WAIT, 0, 0);
-
- glfwMakeContextCurrent(stack->window);
- }
-
- for (;;) {
- stack->thread_asleep = 1;
- syscall4(SYS_futex, (iptr)&stack->work_futex, FUTEX_WAIT, 0, 0);
- stack->thread_asleep = 0;
- vtgl_render_frame(stack->terminal_memory, stack->input, stack->thread_arena);
- glfwSwapBuffers(stack->window);
- }
-
- __builtin_unreachable();
-}
-
-i32
-main(i32 argc, char *argv[], char *envp[])
-{
- linux_ctx.platform_memory = arena_from_memory_block(os_block_alloc(MB(2)));
- linux_ctx.error_stream = stream_alloc(&linux_ctx.platform_memory, KB(256));
-
- iv2 cells = {.x = -1, .y = -1};
-
- char *argv0 = *argv++;
- argc--;
- for (i32 i = 0; i < argc; i++) {
- char *arg = argv[i];
- if (!arg || !arg[0])
- usage(argv0, &linux_ctx.error_stream);
- if (arg[0] != '-')
- break;
- arg++;
- switch (arg[0]) {
- case 'g': {
- if (!argv[i + 1])
- usage(argv0, &linux_ctx.error_stream);
- s8 g_arg = c_str_to_s8(argv[i + 1]);
- struct conversion_result cres = s8_parse_i32_until(g_arg, 'x');
- if (cres.status == CR_SUCCESS)
- cells.w = cres.i;
-
- if (cres.unparsed.len > 0 && cres.unparsed.data[0] == 'x') {
- s8 remainder = {.len = cres.unparsed.len - 1,
- .data = cres.unparsed.data + 1};
- cres = s8_parse_i32(remainder);
- if (cres.status == CR_SUCCESS)
- cells.h = cres.i;
- }
-
- if (cells.w <= 0 || cells.h <= 0) {
- stream_push_s8(&linux_ctx.error_stream, s8("ignoring malformed geometry: "));
- stream_push_s8(&linux_ctx.error_stream, c_str_to_s8(argv[i + 1]));
- stream_push_byte(&linux_ctx.error_stream, '\n');
- }
- argv++;
- argc--;
- } break;
- case 'v':
- stream_push_s8s(&linux_ctx.error_stream, 2,
- (s8 []){c_str_to_s8(argv0), s8(" " VERSION "\n")});
- os_fatal(stream_to_s8(&linux_ctx.error_stream));
- default:
- usage(argv0, &linux_ctx.error_stream);
- }
- }
- if (linux_ctx.error_stream.widx) {
- os_write_err_msg(stream_to_s8(&linux_ctx.error_stream));
- linux_ctx.error_stream.widx = 0;
- }
-
- linux_ctx.render_stack = new_stack(KB(256));
- linux_ctx.render_stack->entry = linux_render_thread_entry;
- linux_ctx.render_stack->thread_asleep = 1;
- new_thread(linux_ctx.render_stack);
-
- {
- Arena tmp = linux_ctx.platform_memory;
- SLLVariableVector environment_block = parse_environment(&tmp, envp);
-
- /* TODO: build up argv for the child as well */
- c8 **child_envp = construct_c_str_array(&tmp, environment_block);
- linux_ctx.child = os_fork_child(get_default_cmd(envp), child_envp);
- }
-
- {
- MemoryBlock terminal_memory = os_block_alloc(MB(32));
- linux_ctx.memory.memory = terminal_memory.memory;
- linux_ctx.memory.memory_size = terminal_memory.size;
-#ifdef _DEBUG
- MemoryBlock debug_memory = os_block_alloc(MB(128));
- linux_ctx.memory.debug_memory = debug_memory.memory;
- linux_ctx.memory.debug_memory_size = debug_memory.size;
-#endif
- }
-
- linux_ctx.inotify_fd = syscall1(SYS_inotify_init1, O_NONBLOCK|O_CLOEXEC);
-
-#ifdef _DEBUG
- debug_reload_library((u8 *)DEBUG_LIB_NAME, &linux_ctx);
- linux_add_file_watch((u8 *)DEBUG_LIB_NAME, debug_reload_library, &linux_ctx);
-#endif
-
- linux_ctx.memory.platform_api.add_file_watch = linux_add_file_watch;
- linux_ctx.memory.platform_api.allocate_ring_buffer = os_allocate_ring_buffer;
- linux_ctx.memory.platform_api.get_clipboard = x11_get_clipboard;
- linux_ctx.memory.platform_api.set_clipboard = x11_set_clipboard;
- linux_ctx.memory.platform_api.read_file = os_read_file;
- linux_ctx.memory.platform_api.read = os_read;
- linux_ctx.memory.platform_api.set_terminal_size = os_set_terminal_size;
- linux_ctx.memory.platform_api.get_window_title = x11_get_window_title;
- linux_ctx.memory.platform_api.set_window_title = x11_set_window_title;
- linux_ctx.memory.platform_api.write = os_write;
- linux_ctx.memory.platform_api.path_separator = '/';
-
- if (!glfwInit())
- os_fatal(s8("Failed to init GLFW\n"));
- glfwSetErrorCallback(glfw_error_callback);
-
- GLFWmonitor *mon = glfwGetPrimaryMonitor();
- if (!mon) {
- glfwTerminate();
- os_fatal(s8("Failed to find any monitors!\n"));
- }
- iv2 monitor_size;
- glfwGetMonitorWorkarea(mon, NULL, NULL, &monitor_size.w, &monitor_size.h);
-
- linux_ctx.char_stream = arena_stream(linux_ctx.platform_memory);
-
- iv2 window_size = {.w = 1280, .h = 720};
- linux_ctx.window = init_window(&linux_ctx, window_size);
-
- iv2 requested_size = vtgl_initialize(&linux_ctx.memory, linux_ctx.child.handle, cells, monitor_size);
- if (requested_size.w > 0 && requested_size.h > 0 &&
- (requested_size.w != window_size.w || requested_size.h != window_size.h))
- {
- glfwSetWindowAttrib(linux_ctx.window, GLFW_FLOATING, GLFW_TRUE);
- i32 x = ABS(window_size.w - requested_size.w) / 2;
- i32 y = ABS(window_size.h - requested_size.h) / 2;
- window_size = requested_size;
- glfwSetWindowMonitor(linux_ctx.window, 0, x, y, window_size.w, window_size.h, GLFW_DONT_CARE);
- /* NOTE: resizable must be set after the window is shown; otherwise tiling window
- * managers will forcibly resize us even if we are supposed to be floating */
- glfwShowWindow(linux_ctx.window);
- glfwSetWindowAttrib(linux_ctx.window, GLFW_RESIZABLE, GLFW_TRUE);
- } else {
- /* NOTE: on the other hand we should let the window be resized if no size was
- * explicitly requested */
- glfwSetWindowAttrib(linux_ctx.window, GLFW_RESIZABLE, GLFW_TRUE);
- glfwSetWindowPos(linux_ctx.window,
- ABS(monitor_size.w - window_size.w) / 2,
- ABS(monitor_size.h - window_size.h) / 2);
- glfwShowWindow(linux_ctx.window);
- }
- glfwMakeContextCurrent(0);
-
- linux_ctx.input.window_size = window_size;
-
- linux_ctx.render_stack->input = &linux_ctx.input;
- linux_ctx.render_stack->terminal_memory = &linux_ctx.memory;
- linux_ctx.render_stack->thread_arena = arena_from_memory_block(os_block_alloc(MB(8)));
- linux_ctx.render_stack->window = linux_ctx.window;
- syscall3(SYS_futex, (iptr)&linux_ctx.render_stack->work_futex, FUTEX_WAKE, 1);
-
- Range last_sel = {0};
- f64 last_time = os_get_time();
- while (!glfwWindowShouldClose(linux_ctx.window)) {
- if (os_child_exited(linux_ctx.child.process_id))
- break;
-
- /* TODO: cpu time excluding waiting for the vblank */
- f64 current_time = os_get_time();
- linux_ctx.input.dt = current_time - last_time;
- last_time = current_time;
-
- update_input(&linux_ctx);
- if (vtgl_frame_step(&linux_ctx.memory, &linux_ctx.input))
- syscall3(SYS_futex, (iptr)&linux_ctx.render_stack->work_futex, FUTEX_WAKE, 1);
-
- Range current_sel = vtgl_active_selection(&linux_ctx.memory, 0);
- if (is_valid_range(current_sel) && !equal_range(current_sel, last_sel)) {
- Stream buf = arena_stream(linux_ctx.platform_memory);
- vtgl_active_selection(&linux_ctx.memory, &buf);
- stream_push_byte(&buf, 0);
- if (!buf.errors)
- glfwSetX11SelectionString((c8 *)buf.buf);
- last_sel = current_sel;
- }
- }
-
- syscall1(SYS_exit_group, 0);
- __builtin_unreachable();
-
- return 0;
-}
diff --git a/terminal.c b/terminal.c
@@ -3,7 +3,7 @@
/* TODO: build own wide char tables */
i32 wcwidth(u32 cp);
-static const u8 utf8overhangmask[32] = {
+global 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,
@@ -12,11 +12,11 @@ static const u8 utf8overhangmask[32] = {
#define SPLIT_LONG 4096L
-static iv2
+function iv2
initialize_framebuffer(Framebuffer *fb, iv2 term_size)
{
- size cell_memory_size = sizeof(Cell) * term_size.h * term_size.w;
- size rows_memory_size = sizeof(Row) * term_size.h;
+ iz cell_memory_size = sizeof(Cell) * term_size.h * term_size.w;
+ iz rows_memory_size = sizeof(Row) * term_size.h;
/* NOTE: make sure cell memory size is a multiple of pointer size */
cell_memory_size += (sizeof(void *) - cell_memory_size % sizeof(void *));
@@ -33,7 +33,7 @@ initialize_framebuffer(Framebuffer *fb, iv2 term_size)
return term_size;
}
-static Range
+function Range
get_word_around_cell(Term *t, iv2 cell)
{
Range result = {.start = cell, .end = cell};
@@ -56,7 +56,7 @@ get_word_around_cell(Term *t, iv2 cell)
return result;
}
-static Range
+function Range
get_char_around_cell(Term *t, iv2 cell)
{
Range result = {.start = cell, .end = cell};
@@ -79,26 +79,26 @@ get_char_around_cell(Term *t, iv2 cell)
return result;
}
-static s8
-consume(s8 raw, size count)
+function s8
+consume(s8 raw, iz count)
{
raw.data += count;
raw.len -= count;
return raw;
}
-static u8
-peek(s8 raw, size i)
+function u8
+peek(s8 raw, iz i)
{
ASSERT(i < raw.len);
return raw.data[i];
}
-static u32
+function u32
get_utf8(s8 *raw)
{
u32 state = 0, cp;
- size off = 0;
+ iz off = 0;
while (off < raw->len) {
if (!utf8_decode(&state, &cp, raw->data[off++])) {
*raw = consume(*raw, off);
@@ -108,7 +108,7 @@ get_utf8(s8 *raw)
return (u32)-1;
}
-static u32
+function u32
get_ascii(s8 *raw)
{
ASSERT(raw->len > 0);
@@ -117,15 +117,15 @@ get_ascii(s8 *raw)
return result;
}
-static size
+function iz
line_length(Line *l)
{
ASSERT(l->start <= l->end);
return l->end - l->start;
}
-static s8
-line_to_s8(Line *l, RingBuf *rb)
+function s8
+line_to_s8(Line *l)
{
ASSERT(l->start <= l->end);
@@ -133,7 +133,7 @@ line_to_s8(Line *l, RingBuf *rb)
return result;
}
-static void
+function void
init_line(Line *l, u8 *position, CellStyle cursor_state)
{
l->start = position;
@@ -142,28 +142,28 @@ init_line(Line *l, u8 *position, CellStyle cursor_state)
l->cursor_state = cursor_state;
}
-static size
-feed_line(LineBuf *lb, u8 *position, CellStyle cursor_state)
+function iz
+feed_line(LineBuffer *lb, u8 *position, CellStyle cursor_state)
{
- size result = 0;
- if (lb->buf[lb->widx].start != position) {
- lb->buf[lb->widx++].end = position;
- lb->widx = lb->widx >= lb->cap ? 0 : lb->widx;
- lb->filled += lb->filled <= lb->widx;
- init_line(lb->buf + lb->widx, position, cursor_state);
+ iz result = 0;
+ if (lb->data[lb->count].start != position) {
+ lb->data[lb->count++].end = position;
+ lb->count = lb->count >= lb->capacity ? 0 : lb->count;
+ lb->filled += lb->filled <= lb->count;
+ init_line(lb->data + lb->count, position, cursor_state);
result = 1;
}
return result;
}
-static void
+function void
selection_clear(Selection *s)
{
s->range.end = INVALID_RANGE_END;
s->state = SS_NONE;
}
-static void
+function void
selection_scroll(Term *t, i32 origin, i32 n)
{
Selection *s = &t->selection;
@@ -183,7 +183,7 @@ selection_scroll(Term *t, i32 origin, i32 n)
}
}
-static b32
+function b32
is_selected(Selection *s, i32 x, i32 y)
{
if (!is_valid_range(s->range))
@@ -195,7 +195,7 @@ is_selected(Selection *s, i32 x, i32 y)
return result;
}
-static b32
+function b32
selection_intersects_region(Selection *s, iv2 tl, iv2 br)
{
/* TODO: maybe this can be further simplified (eg. with a k-map) */
@@ -210,7 +210,7 @@ selection_intersects_region(Selection *s, iv2 tl, iv2 br)
return result;
}
-static void
+function void
fb_clear_region(Term *t, i32 r1, i32 r2, i32 c1, i32 c2)
{
BEGIN_TIMED_BLOCK();
@@ -250,7 +250,7 @@ fb_clear_region(Term *t, i32 r1, i32 r2, i32 c1, i32 c2)
END_TIMED_BLOCK();
}
-static void
+function void
fb_scroll_down(Term *t, i32 top, i32 n)
{
BEGIN_TIMED_BLOCK();
@@ -271,7 +271,7 @@ end:
END_TIMED_BLOCK();
}
-static void
+function void
fb_scroll_up(Term *t, i32 top, i32 n)
{
BEGIN_TIMED_BLOCK();
@@ -299,14 +299,14 @@ end:
END_TIMED_BLOCK();
}
-static void
+function void
swap_screen(Term *t)
{
t->mode.term ^= TM_ALTSCREEN;
t->view_idx = !!(t->mode.term & TM_ALTSCREEN);
}
-static void
+function void
cursor_reset(Term *t)
{
//(Colour){.rgba = 0x1e9e33ff};
@@ -315,7 +315,7 @@ cursor_reset(Term *t)
t->cursor.style.attr = ATTR_NULL;
}
-static void
+function void
cursor_move_to(Term *t, i32 row, i32 col)
{
i32 minr = 0, maxr = t->size.h - 1;
@@ -328,7 +328,7 @@ cursor_move_to(Term *t, i32 row, i32 col)
t->cursor.state &= ~CURSOR_WRAP_NEXT;
}
-static void
+function void
cursor_move_abs_to(Term *t, i32 row, i32 col)
{
if (t->cursor.state & CURSOR_ORIGIN)
@@ -336,7 +336,7 @@ cursor_move_abs_to(Term *t, i32 row, i32 col)
cursor_move_to(t, row, col);
}
-static void
+function void
cursor_alt(Term *t, b32 save)
{
i32 mode = t->view_idx;
@@ -349,7 +349,7 @@ cursor_alt(Term *t, b32 save)
}
/* NOTE: advance the cursor by <n> cells; handles reverse movement */
-static void
+function void
cursor_step_column(Term *t, i32 n)
{
i32 col = t->cursor.pos.x + n;
@@ -364,7 +364,7 @@ cursor_step_column(Term *t, i32 n)
}
/* NOTE: steps the cursor without causing a scroll */
-static void
+function void
cursor_step_raw(Term *t, i32 step, i32 rows, i32 cols)
{
rows *= step;
@@ -374,7 +374,7 @@ cursor_step_raw(Term *t, i32 step, i32 rows, i32 cols)
cursor_move_to(t, t->cursor.pos.y + rows, t->cursor.pos.x + cols);
}
-static void
+function void
cursor_up(Term *t, i32 requested_count)
{
i32 cursor_y = t->cursor.pos.y;
@@ -383,7 +383,7 @@ cursor_up(Term *t, i32 requested_count)
cursor_move_to(t, t->cursor.pos.y - count, t->cursor.pos.x);
}
-static void
+function void
cursor_down(Term *t, i32 requested_count)
{
i32 cursor_y = t->cursor.pos.y;
@@ -392,7 +392,7 @@ cursor_down(Term *t, i32 requested_count)
cursor_move_to(t, t->cursor.pos.y + count, t->cursor.pos.x);
}
-static i32
+function i32
next_tab_position(Term *t, b32 backwards)
{
static_assert(ARRAY_COUNT(t->tabs) == 8 * sizeof(*t->tabs),
@@ -422,7 +422,7 @@ next_tab_position(Term *t, b32 backwards)
return result;
}
-static void
+function void
term_tab_col(Term *t, i32 col, b32 set)
{
ASSERT(col < t->size.w);
@@ -434,7 +434,7 @@ term_tab_col(Term *t, i32 col, b32 set)
else t->tabs[idx] &= ~mask;
}
-static void
+function void
reset_tabs(Term *t, u32 column_step)
{
for (i32 i = 0; i < ARRAY_COUNT(t->tabs); i++)
@@ -443,7 +443,7 @@ reset_tabs(Term *t, u32 column_step)
term_tab_col(t, i, 1);
}
-static void
+function void
term_reset(Term *t)
{
i32 mode = t->mode.term & TM_ALTSCREEN;
@@ -466,11 +466,11 @@ term_reset(Term *t)
t->mode.term = mode|TM_AUTO_WRAP|TM_UTF8;
}
-static void
+function void
stream_push_csi(Stream *s, CSI *csi)
{
stream_push_s8(s, s8("ESC ["));
- for (size i = 0; i < csi->raw.len; i++) {
+ for (iz i = 0; i < csi->raw.len; i++) {
u8 c = csi->raw.data[i];
if (ISPRINT(c)) {
stream_push_byte(s, csi->raw.data[i]);
@@ -504,7 +504,7 @@ stream_push_csi(Stream *s, CSI *csi)
}
/* ED/DECSED: Erase in Display */
-static void
+function void
erase_in_display(Term *t, CSI *csi)
{
iv2 cpos = t->cursor.pos;
@@ -530,7 +530,7 @@ erase_in_display(Term *t, CSI *csi)
}
/* EL/DECSEL: Erase in Line */
-static void
+function void
erase_in_line(Term *t, CSI *csi)
{
iv2 cpos = t->cursor.pos;
@@ -549,21 +549,21 @@ erase_in_line(Term *t, CSI *csi)
}
/* IL: Insert <count> blank lines */
-static void
+function void
insert_blank_lines(Term *t, i32 count)
{
fb_scroll_down(t, t->cursor.pos.y, count);
}
/* DL: Erase <count> lines */
-static void
+function void
erase_lines(Term *t, i32 count)
{
fb_scroll_up(t, t->cursor.pos.y, count);
}
/* DCH: Delete <count> Characters */
-static void
+function void
delete_characters(Term *t, i32 requested_count)
{
iv2 cpos = t->cursor.pos;
@@ -597,7 +597,7 @@ delete_characters(Term *t, i32 requested_count)
}
/* ECH: Erase <count> Characters */
-static void
+function void
erase_characters(Term *t, i32 count)
{
iv2 cpos = t->cursor.pos;
@@ -605,7 +605,7 @@ erase_characters(Term *t, i32 count)
}
/* TBC: Tabulation Clear */
-static void
+function void
clear_term_tab(Term *t, i32 arg)
{
/* TODO: case 1, 2? */
@@ -622,12 +622,12 @@ clear_term_tab(Term *t, i32 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;
+ stream_reset(&t->error_stream, 0);
}
}
/* SM/DECSET: Set Mode & RM/DECRST Reset Mode */
-static void
+function void
set_mode(Term *t, CSI *csi, b32 set, ModeState src, ModeState *dest)
{
BEGIN_TIMED_BLOCK();
@@ -720,7 +720,7 @@ set_mode(Term *t, CSI *csi, b32 set, ModeState src, ModeState *dest)
stream_push_s8(&t->error_stream, s8("set_mode: unhandled mode: "));
stream_push_csi(&t->error_stream, csi);
os_write_err_msg(stream_to_s8(&t->error_stream));
- t->error_stream.widx = 0;
+ stream_reset(&t->error_stream, 0);
}
}
#undef PRIV
@@ -728,7 +728,7 @@ set_mode(Term *t, CSI *csi, b32 set, ModeState src, ModeState *dest)
}
/* NOTE: adapted from the perl script 256colres.pl in xterm src */
-static Colour
+function Colour
indexed_colour(i32 index)
{
Colour result;
@@ -751,7 +751,7 @@ indexed_colour(i32 index)
return result;
}
-static struct conversion_result
+function struct conversion_result
direct_colour(i32 *argv, i32 argc, i32 *idx, Stream *err)
{
struct conversion_result result = {.status = CR_FAILURE};
@@ -810,7 +810,7 @@ direct_colour(i32 *argv, i32 argc, i32 *idx, Stream *err)
}
/* SGR: Select Graphic Rendition */
-static void
+function void
set_colours(Term *t, CSI *csi)
{
BEGIN_TIMED_BLOCK();
@@ -842,7 +842,7 @@ set_colours(Term *t, CSI *csi)
stream_push_s8(&t->error_stream, s8("set_colours: "));
stream_push_csi(&t->error_stream, csi);
os_write_err_msg(stream_to_s8(&t->error_stream));
- t->error_stream.widx = 0;
+ stream_reset(&t->error_stream, 0);
}
break;
@@ -856,7 +856,7 @@ set_colours(Term *t, CSI *csi)
stream_push_s8(&t->error_stream, s8("set_colours: "));
stream_push_csi(&t->error_stream, csi);
os_write_err_msg(stream_to_s8(&t->error_stream));
- t->error_stream.widx = 0;
+ stream_reset(&t->error_stream, 0);
}
break;
@@ -877,14 +877,14 @@ set_colours(Term *t, CSI *csi)
stream_push_byte(&t->error_stream, '\n');
stream_push_csi(&t->error_stream, csi);
os_write_err_msg(stream_to_s8(&t->error_stream));
- t->error_stream.widx = 0;
+ stream_reset(&t->error_stream, 0);
}
}
}
END_TIMED_BLOCK();
}
-static void
+function void
set_top_bottom_margins(Term *t, i32 requested_top, i32 requested_bottom)
{
i32 top = MIN(MAX(1, requested_top), t->size.h);
@@ -896,23 +896,29 @@ set_top_bottom_margins(Term *t, i32 requested_top, i32 requested_bottom)
}
}
-static void
+function void
window_manipulation(Term *t, CSI *csi)
{
switch (csi->argv[0]) {
- case 22: t->platform->get_window_title(&t->saved_title); break;
- case 23: t->platform->set_window_title(&t->saved_title); break;
+ case 22: {
+ u8 *title = t->os->get_window_title();
+ stream_reset(&t->saved_title, 0);
+ stream_push_s8(&t->saved_title, c_str_to_s8((char *)title));
+ /* TODO(rnp): ensure termination */
+ stream_push_byte(&t->saved_title, 0);
+ } break;
+ case 23: t->os->set_window_title(stream_to_s8(&t->saved_title)); break;
default:
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');
stream_push_csi(&t->error_stream, csi);
os_write_err_msg(stream_to_s8(&t->error_stream));
- t->error_stream.widx = 0;
+ stream_reset(&t->error_stream, 0);
}
}
-static void
+function void
push_newline(Term *t, b32 move_to_first_col)
{
i32 row = t->cursor.pos.y;
@@ -923,7 +929,7 @@ push_newline(Term *t, b32 move_to_first_col)
cursor_move_to(t, row, move_to_first_col? 0 : t->cursor.pos.x);
}
-static void
+function void
push_tab(Term *t, i32 n)
{
u32 end = ABS(n);
@@ -931,7 +937,7 @@ push_tab(Term *t, i32 n)
cursor_move_to(t, t->cursor.pos.y, next_tab_position(t, n < 0));
}
-static b32
+function b32
parse_csi(s8 *r, CSI *csi)
{
BEGIN_TIMED_BLOCK();
@@ -970,7 +976,7 @@ end:
return result;
}
-static void
+function void
handle_csi(Term *t, CSI *csi)
{
BEGIN_TIMED_BLOCK();
@@ -1012,7 +1018,7 @@ handle_csi(Term *t, CSI *csi)
case 'n': {
switch (csi->argv[0]) {
case 5: /* NOTE: DSR V-1: Operating Status */
- t->platform->write(t->child, s8("\x1B[0n"));
+ t->os->write(t->child, s8("\x1B[0n"));
break;
case 6: /* NOTE: DSR V-2: Cursor Position */ {
iv2 cpos = t->cursor.pos;
@@ -1027,7 +1033,7 @@ handle_csi(Term *t, CSI *csi)
stream_push_byte(&buf, ';');
stream_push_i64(&buf, cpos.x + 1);
stream_push_byte(&buf, 'R');
- t->platform->write(t->child, stream_to_s8(&buf));
+ t->os->write(t->child, stream_to_s8(&buf));
} break;
default: goto unknown;
}
@@ -1071,12 +1077,12 @@ handle_csi(Term *t, CSI *csi)
stream_push_s8(&t->error_stream, s8("unknown csi: "));
stream_push_csi(&t->error_stream, csi);
os_write_err_msg(stream_to_s8(&t->error_stream));
- t->error_stream.widx = 0;
+ stream_reset(&t->error_stream, 0);
}
END_TIMED_BLOCK();
}
-static b32
+function b32
parse_osc(s8 *raw, OSC *osc)
{
BEGIN_TIMED_BLOCK();
@@ -1121,18 +1127,18 @@ parse_osc(s8 *raw, OSC *osc)
return result;
}
-static void
+function void
reset_csi(CSI *csi, s8 *raw)
{
*csi = (CSI){0};
csi->raw.data = raw->data;
}
-static void
+function void
dump_osc(OSC *osc, Stream *err)
{
stream_push_s8(err, s8("ESC]"));
- for (size i = 0; i < osc->raw.len; i++) {
+ for (iz i = 0; i < osc->raw.len; i++) {
u8 cp = osc->raw.data[i];
if (ISPRINT(cp)) {
stream_push_byte(err, cp);
@@ -1153,10 +1159,10 @@ dump_osc(OSC *osc, Stream *err)
stream_push_i64(err, osc->arg.len);
stream_push_s8(err, s8("}\n"));
os_write_err_msg(stream_to_s8(err));
- err->widx = 0;
+ stream_reset(err, 0);
}
-static void
+function void
handle_osc(Term *t, s8 *raw, Arena a)
{
BEGIN_TIMED_BLOCK();
@@ -1166,9 +1172,13 @@ handle_osc(Term *t, s8 *raw, Arena a)
Stream buffer = arena_stream(a);
switch (osc.cmd) {
- case 0: stream_push_s8(&buffer, osc.arg); t->platform->set_window_title(&buffer); break;
+ case 0:
+ case 2: {
+ stream_push_s8(&buffer, osc.arg);
+ stream_push_byte(&buffer, 0);
+ t->os->set_window_title(stream_to_s8(&buffer));
+ } break;
case 1: break; /* IGNORED: set icon name */
- case 2: stream_push_s8(&buffer, osc.arg); t->platform->set_window_title(&buffer); break;
default:
unknown:
stream_push_s8(&t->error_stream, s8("unhandled osc cmd: "));
@@ -1178,7 +1188,7 @@ handle_osc(Term *t, s8 *raw, Arena a)
END_TIMED_BLOCK();
}
-static i32
+function i32
handle_escape(Term *t, s8 *raw, Arena a)
{
BEGIN_TIMED_BLOCK();
@@ -1216,7 +1226,7 @@ handle_escape(Term *t, s8 *raw, Arena a)
stream_push_byte(&t->error_stream, cs);
stream_push_byte(&t->error_stream, '\n');
os_write_err_msg(stream_to_s8(&t->error_stream));
- t->error_stream.widx = 0;
+ stream_reset(&t->error_stream, 0);
break;
}
}
@@ -1257,14 +1267,14 @@ handle_escape(Term *t, s8 *raw, Arena a)
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;
+ stream_reset(&t->error_stream, 0);
break;
}
END_TIMED_BLOCK();
return result;
}
-static i32
+function i32
push_control(Term *t, s8 *line, u32 cp, Arena a)
{
i32 result = 0;
@@ -1289,7 +1299,7 @@ push_control(Term *t, s8 *line, u32 cp, Arena a)
stream_push_hex_u64(&t->error_stream, cp);
stream_push_byte(&t->error_stream, '\n');
os_write_err_msg(stream_to_s8(&t->error_stream));
- t->error_stream.widx = 0;
+ stream_reset(&t->error_stream, 0);
break;
}
if (cp != 0x1B) {
@@ -1298,7 +1308,7 @@ push_control(Term *t, s8 *line, u32 cp, Arena a)
return result;
}
-static void
+function void
push_normal_cp(Term *t, TermView *tv, u32 cp)
{
BEGIN_TIMED_BLOCK();
@@ -1349,13 +1359,13 @@ push_normal_cp(Term *t, TermView *tv, u32 cp)
END_TIMED_BLOCK();
}
-static void
+function void
push_line(Term *t, Line *line, Arena a)
{
BEGIN_TIMED_BLOCK();
TermView *tv = t->views + t->view_idx;
- s8 l = line_to_s8(line, &tv->log);
+ s8 l = line_to_s8(line);
t->cursor.style = line->cursor_state;
while (l.len) {
@@ -1382,17 +1392,17 @@ push_line(Term *t, Line *line, Arena a)
END_TIMED_BLOCK();
}
-static size
-get_line_idx(LineBuf *lb, size off)
+function iz
+get_line_idx(LineBuffer *lb, iz off)
{
ASSERT(-off <= lb->filled);
- size result = lb->widx + off;
+ iz result = lb->count + off;
if (result < 0)
result += lb->filled;
return result;
}
-static void
+function void
blit_lines(Term *t, Arena a)
{
BEGIN_TIMED_BLOCK();
@@ -1400,13 +1410,13 @@ blit_lines(Term *t, Arena a)
ASSERT(t->gl.flags & NEEDS_REFILL);
term_reset(t);
- TermView *tv = t->views + t->view_idx;
- size line_count = t->size.h - 1;
- size off = t->scroll_offset;
+ TermView *tv = t->views + t->view_idx;
+ iz line_count = t->size.h - 1;
+ iz off = t->scroll_offset;
CLAMP(line_count, 0, tv->lines.filled);
- for (size idx = -line_count; idx <= 0; idx++) {
- size line_idx = get_line_idx(&tv->lines, idx - off);
- push_line(t, tv->lines.buf + line_idx, a);
+ for (iz idx = -line_count; idx <= 0; idx++) {
+ iz line_idx = get_line_idx(&tv->lines, idx - off);
+ push_line(t, tv->lines.data + line_idx, a);
}
t->gl.flags &= ~NEEDS_REFILL;
@@ -1414,7 +1424,7 @@ blit_lines(Term *t, Arena a)
END_TIMED_BLOCK();
}
-static void
+function void
handle_input(Term *t, Arena a, s8 raw)
{
BEGIN_TIMED_BLOCK();
@@ -1423,7 +1433,7 @@ handle_input(Term *t, Arena a, s8 raw)
/* TODO: SIMD look ahead */
while (raw.len) {
- size start_len = raw.len;
+ iz start_len = raw.len;
u32 cp = peek(raw, 0);
/* TODO: this could be a performance issue; may need seperate code path for
* terminal when not in UTF8 mode */
@@ -1436,7 +1446,7 @@ handle_input(Term *t, Arena a, s8 raw)
/* NOTE(rnp): invalid/garbage cp; treat as ASCII control char */
cp = get_ascii(&raw);
} else if (cp != (u32)-1) {
- tv->lines.buf[tv->lines.widx].has_unicode = 1;
+ tv->lines.data[tv->lines.count].has_unicode = 1;
}
} else {
cp = get_ascii(&raw);
@@ -1467,12 +1477,12 @@ handle_input(Term *t, Arena a, s8 raw)
ASSERT(*old == 0x1B);
feed_line(&tv->lines, old, t->cursor.style);
TermView *nv = t->views + t->view_idx;
- size nstart = nv->log.widx;
- mem_copy(raw.data, nv->log.buf + nstart, raw.len);
+ iz nstart = nv->log.write_index;
+ mem_copy(nv->log.data + nstart, raw.data, raw.len);
commit_to_rb(tv, -raw.len);
commit_to_rb(nv, raw.len);
- raw.data = nv->log.buf + nstart;
- init_line(nv->lines.buf + nv->lines.widx, raw.data,
+ raw.data = nv->log.data + nstart;
+ init_line(nv->lines.data + nv->lines.count, raw.data,
t->cursor.style);
tv = nv;
} else if (t->cursor.pos.y != old_curs_y) {
@@ -1486,13 +1496,13 @@ handle_input(Term *t, Arena a, s8 raw)
}
end:
- tv->lines.buf[tv->lines.widx].end = raw.data;
+ tv->lines.data[tv->lines.count].end = raw.data;
/* TODO: this shouldn't be needed */
- if (tv->lines.buf[tv->lines.widx].end < tv->lines.buf[tv->lines.widx].start)
- tv->lines.buf[tv->lines.widx].start -= tv->log.cap;
+ if (tv->lines.data[tv->lines.count].end < tv->lines.data[tv->lines.count].start)
+ tv->lines.data[tv->lines.count].start -= tv->log.capacity;
- if (!t->escape && line_length(tv->lines.buf + tv->lines.widx) > SPLIT_LONG)
+ if (!t->escape && line_length(tv->lines.data + tv->lines.count) > SPLIT_LONG)
feed_line(&tv->lines, raw.data, t->cursor.style);
t->unprocessed_bytes = raw.len;
diff --git a/tests/test-common.c b/tests/test-common.c
@@ -3,7 +3,7 @@
#include "config.h"
/* NOTE: stubs for stuff we aren't testing */
-static void get_gpu_glyph_index(Arena, void *, void *, u32, u32, u32, CachedGlyph **);
+function void get_gpu_glyph_index(Arena, void *, void *, u32, u32, u32, CachedGlyph **);
KEYBIND_FN(copy) { return 0; }
KEYBIND_FN(paste) { return 0; }
@@ -13,7 +13,7 @@ KEYBIND_FN(zoom) { return 0; }
#include "font.c"
#include "terminal.c"
-static PLATFORM_WRITE_FN(test_write)
+function OS_WRITE_FN(test_write)
{
/* NOTE(rnp): for testing the caller will provide a stream via the platform
* child handle. Then this function writes into it and the caller can compare
@@ -23,70 +23,67 @@ static PLATFORM_WRITE_FN(test_write)
return !s->errors;
}
-static PLATFORM_WINDOW_TITLE_FN(test_get_window_title)
+function OS_GET_WINDOW_TITLE_FN(test_get_window_title)
{
- ASSERT(buffer);
- stream_push_s8(buffer, s8("test_title"));
+ return (u8 *)"test_title";
}
-static PLATFORM_WINDOW_TITLE_FN(test_set_window_title)
+function OS_SET_WINDOW_TITLE_FN(test_set_window_title)
{
- ASSERT(buffer);
- stream_push_byte(buffer, 0);
+ ASSERT(title.len);
}
-static size
-copy_into_ringbuf(RingBuf *rb, s8 raw)
+function iz
+copy_into_ringbuf(OSRingBuffer *rb, s8 raw)
{
- ASSERT(raw.len < rb->cap);
- for (size i = 0; i < raw.len; i++)
- rb->buf[rb->widx + i] = raw.data[i];
+ ASSERT(raw.len < rb->capacity);
+ mem_copy(rb->data + rb->write_index, raw.data, raw.len);
- rb->widx += raw.len;
- rb->filled += raw.len;
+ rb->write_index += raw.len;
+ rb->filled += raw.len;
- CLAMP(rb->filled, 0, rb->cap);
- if (rb->widx >= rb->cap)
- rb->widx -= rb->cap;
+ CLAMP(rb->filled, 0, rb->capacity);
+ if (rb->write_index >= rb->capacity)
+ rb->write_index -= rb->capacity;
ASSERT(rb->filled >= 0);
- ASSERT(rb->widx >= 0 && rb->widx < rb->cap);
+ ASSERT(rb->write_index >= 0 && rb->write_index < rb->capacity);
return raw.len;
}
-static s8
+function s8
launder_static_string(Term *term, s8 static_str)
{
- RingBuf *rb = &term->views[term->view_idx].log;
+ OSRingBuffer *rb = &term->views[term->view_idx].log;
term->unprocessed_bytes += copy_into_ringbuf(rb, static_str);
s8 raw = {
.len = term->unprocessed_bytes,
- .data = rb->buf + (rb->widx - term->unprocessed_bytes)
+ .data = rb->data + (rb->write_index - term->unprocessed_bytes)
};
return raw;
}
-static Term *
-place_term_into_memory(MemoryBlock memory, i32 rows, i32 columns)
+function Term *
+place_term_into_memory(OSMemoryBlock memory, i32 rows, i32 columns)
{
Arena tmp = arena_from_memory_block(memory);
Term *t = push_struct(&tmp, Term);
t->size = (iv2){.w = 80, .h = 24};
- t->platform = push_struct(&tmp, typeof(*t->platform));
- t->platform->set_window_title = test_set_window_title;
- t->platform->get_window_title = test_get_window_title;
- t->platform->write = test_write;
+ t->os = push_struct(&tmp, typeof(*t->os));
+ t->os->set_window_title = test_set_window_title;
+ t->os->get_window_title = test_get_window_title;
+ t->os->write = test_write;
t->arena_for_frame = tmp;
- os_allocate_ring_buffer(&t->views[0].log, MB(2));
- line_buf_alloc(&t->views[0].lines, &t->arena_for_frame, t->views[0].log.buf, t->cursor.style,
- BACKLOG_LINES);
+ t->views[0].log = os_allocate_ring_buffer(MB(2));
+ t->views[0].lines = line_buffer_alloc(&t->arena_for_frame, t->views[0].log.data,
+ t->cursor.style, BACKLOG_LINES);
- os_allocate_ring_buffer(&t->views[1].log, MB(2));
- line_buf_alloc(&t->views[1].lines, &t->arena_for_frame, t->views[1].log.buf, t->cursor.style,
- ALT_BACKLOG_LINES);
+ t->views[1].log = os_allocate_ring_buffer(MB(2));
+ t->views[1].lines = line_buffer_alloc(&t->arena_for_frame, t->views[1].log.data,
+ t->cursor.style, ALT_BACKLOG_LINES);
t->views[0].fb.backing_store = memory_block_from_arena(&t->arena_for_frame, MB(1));
t->views[1].fb.backing_store = memory_block_from_arena(&t->arena_for_frame, MB(1));
@@ -103,8 +100,8 @@ place_term_into_memory(MemoryBlock memory, i32 rows, i32 columns)
void *malloc(size_t);
void free(void *);
-static void
-release_term_memory(MemoryBlock backing)
+function void
+release_term_memory(OSMemoryBlock backing)
{
Term *t = backing.memory;
os_release_ring_buffer(&t->views[0].log);
diff --git a/tests/test-fuzz.c b/tests/test-fuzz.c
@@ -1,7 +1,7 @@
/* See LICENSE for copyright details */
#include "test-common.c"
-static void
+function void
fuzz_entry_point(s8 data, Stream error_stream)
{
MemoryBlock term_backing = {.memory = malloc(MB(4)), .size = MB(4)};
diff --git a/tests/test.c b/tests/test.c
@@ -74,7 +74,7 @@ GHOSTTY_TESTS
#undef X
#define X(f) {.implementation = f, .name = s8(#f)},
-static Test tests[] = {
+global Test tests[] = {
TESTS
GHOSTTY_TESTS
};
@@ -83,11 +83,11 @@ static Test tests[] = {
#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");
-static s8 unsupported_string = s8("\x1B[33mUNSUPPORTED\x1B[0m\n");
+global s8 failure_string = s8("\x1B[31mFAILURE\x1B[0m\n");
+global s8 success_string = s8("\x1B[32mSUCCESS\x1B[0m\n");
+global s8 unsupported_string = s8("\x1B[33mUNSUPPORTED\x1B[0m\n");
-static b32
+function b32
check_cells_equal(Cell *a, Cell *b)
{
b32 result = a->cp == b->cp &&
@@ -96,7 +96,7 @@ check_cells_equal(Cell *a, Cell *b)
return result;
}
-static TEST_FN(csi_embedded_control)
+function TEST_FN(csi_embedded_control)
{
/* NOTE: print a '1' with default style then start changing the colour,
* but backspace within the escape sequence so the cursor is now over the
@@ -131,13 +131,13 @@ static TEST_FN(csi_embedded_control)
result &= check_cells_equal(&c1, &term->views[term->view_idx].fb.rows[0][0]);
result &= check_cells_equal(&c2, &term->views[term->view_idx].fb.rows[1][1]);
/* NOTE: we also want to ensure that we cannot split a line in the middle of a CSI */
- LineBuf *lb = &term->views[0].lines;
- result &= lb->filled == 0 && *lb->buf[lb->widx].start != '2';
+ LineBuffer *lb = &term->views[0].lines;
+ result &= lb->filled == 0 && *lb->data[lb->count].start != '2';
return result;
}
-static TEST_FN(colour_setting)
+function TEST_FN(colour_setting)
{
launder_static_string(term, CSI(8m));
launder_static_string(term, CSI(4m));
@@ -159,7 +159,7 @@ static TEST_FN(colour_setting)
return result;
}
-static TEST_FN(cursor_at_line_boundary)
+function TEST_FN(cursor_at_line_boundary)
{
/* NOTE: Test that lines are not split in the middle of utf-8 characters */
s8 long_line = s8alloc(&arena, 8192);
@@ -179,8 +179,8 @@ static TEST_FN(cursor_at_line_boundary)
s8 raw = launder_static_string(term, (s8){.len = SPLIT_LONG + 1, .data = long_line.data});
long_line = consume(long_line, SPLIT_LONG + 1);
- LineBuf *lb = &term->views[term->view_idx].lines;
- size line_count = lb->filled;
+ LineBuffer *lb = &term->views[term->view_idx].lines;
+ iz line_count = lb->filled;
handle_input(term, arena, raw);
/* NOTE: ensure line didn't split on red dragon */
@@ -201,12 +201,12 @@ static TEST_FN(cursor_at_line_boundary)
raw = launder_static_string(term, long_line);
handle_input(term, arena, raw);
- result &= line_length(lb->buf) > SPLIT_LONG;
+ result &= line_length(lb->data) > SPLIT_LONG;
return result;
}
-static TEST_FN(cursor_tabs)
+function TEST_FN(cursor_tabs)
{
/* NOTE: first test advancing to a tabstop */
s8 raw = launder_static_string(term, s8("123\t"));
@@ -226,7 +226,7 @@ static TEST_FN(cursor_tabs)
return result;
}
-static TEST_FN(cursor_tabs_across_boundary)
+function TEST_FN(cursor_tabs_across_boundary)
{
/* NOTE: clear tabstops then set one beyond multiple boundaries */
launder_static_string(term, CSI(3g));
@@ -271,12 +271,12 @@ static TEST_FN(cursor_tabs_across_boundary)
return result;
}
-static TEST_FN(working_ringbuffer)
+function TEST_FN(working_ringbuffer)
{
- RingBuf *rb = &term->views[term->view_idx].log;
- rb->buf[0] = 0xFE;
- TestResult result = (rb->buf[0] == rb->buf[ rb->cap]) &&
- (rb->buf[0] == rb->buf[-rb->cap]);
+ OSRingBuffer *rb = &term->views[term->view_idx].log;
+ rb->data[0] = 0xFE;
+ TestResult result = (rb->data[0] == rb->data[ rb->capacity]) &&
+ (rb->data[0] == rb->data[-rb->capacity]);
return result;
}
@@ -285,7 +285,7 @@ static TEST_FN(working_ringbuffer)
/***********************************************/
/* NOTE: CBT V-1: Left Beyond First Column */
-static TEST_FN(cursor_backwards_tabulation_v1)
+function TEST_FN(cursor_backwards_tabulation_v1)
{
launder_static_string(term, s8("\n"));
launder_static_string(term, CSI(?5W));
@@ -301,7 +301,7 @@ static TEST_FN(cursor_backwards_tabulation_v1)
}
/* NOTE: CBT V-2: Left Starting After Tab Stop */
-static TEST_FN(cursor_backwards_tabulation_v2)
+function TEST_FN(cursor_backwards_tabulation_v2)
{
launder_static_string(term, CSI(?5W));
launder_static_string(term, CSI(1;10H));
@@ -318,7 +318,7 @@ static TEST_FN(cursor_backwards_tabulation_v2)
}
/* NOTE: CBT V-3: Left Starting on Tabstop */
-static TEST_FN(cursor_backwards_tabulation_v3)
+function TEST_FN(cursor_backwards_tabulation_v3)
{
launder_static_string(term, CSI(?5W));
launder_static_string(term, CSI(1;9H));
@@ -337,7 +337,7 @@ static TEST_FN(cursor_backwards_tabulation_v3)
}
/* NOTE: CBT V-4: Left Margin with Origin Mode */
-static TEST_FN(cursor_backwards_tabulation_v4)
+function TEST_FN(cursor_backwards_tabulation_v4)
{
TestResult result = UNSUPPORTED;
@@ -363,10 +363,10 @@ static TEST_FN(cursor_backwards_tabulation_v4)
}
/* NOTE: CUB V-1: Pending Wrap is Unset */
-static TEST_FN(cursor_backwards_v1)
+function TEST_FN(cursor_backwards_v1)
{
u8 buffer_store[32];
- Stream buffer = {.buf = buffer_store, .cap = sizeof(buffer_store)};
+ Stream buffer = {.data = buffer_store, .capacity = sizeof(buffer_store)};
stream_push_s8(&buffer, s8("\x1B["));
stream_push_u64(&buffer, term->size.w);
stream_push_byte(&buffer, 'G');
@@ -387,7 +387,7 @@ static TEST_FN(cursor_backwards_v1)
}
/* NOTE: CUB V-2: Leftmost Boundary with Reverse Wrap Disabled */
-static TEST_FN(cursor_backwards_v2)
+function TEST_FN(cursor_backwards_v2)
{
launder_static_string(term, CSI(?45l));
launder_static_string(term, s8("A\r\n"));
@@ -404,7 +404,7 @@ static TEST_FN(cursor_backwards_v2)
}
/* NOTE: CUB V-3: Reverse Wrap */
-static TEST_FN(cursor_backwards_v3)
+function TEST_FN(cursor_backwards_v3)
{
TestResult result = UNSUPPORTED;
@@ -434,7 +434,7 @@ static TEST_FN(cursor_backwards_v3)
}
/* NOTE: CUB V-4: Extended Reverse Wrap Single Line */
-static TEST_FN(cursor_backwards_v4)
+function TEST_FN(cursor_backwards_v4)
{
TestResult result = UNSUPPORTED;
@@ -458,7 +458,7 @@ static TEST_FN(cursor_backwards_v4)
}
/* NOTE: CUB V-5: Extended Reverse Wrap Wraps to Bottom */
-static TEST_FN(cursor_backwards_v5)
+function TEST_FN(cursor_backwards_v5)
{
TestResult result = UNSUPPORTED;
@@ -491,7 +491,7 @@ static TEST_FN(cursor_backwards_v5)
}
/* NOTE: CUB V-6: Reverse Wrap Outside of Margins */
-static TEST_FN(cursor_backwards_v6)
+function TEST_FN(cursor_backwards_v6)
{
TestResult result = UNSUPPORTED;
@@ -510,7 +510,7 @@ static TEST_FN(cursor_backwards_v6)
}
/* NOTE: CUB V-7: Reverse Wrap with Pending Wrap State */
-static TEST_FN(cursor_backwards_v7)
+function TEST_FN(cursor_backwards_v7)
{
TestResult result = UNSUPPORTED;
@@ -543,7 +543,7 @@ static TEST_FN(cursor_backwards_v7)
}
/* NOTE: CUD V-1: Cursor Down */
-static TEST_FN(cursor_down_v1)
+function TEST_FN(cursor_down_v1)
{
launder_static_string(term, s8("A"));
launder_static_string(term, CSI(2B));
@@ -559,7 +559,7 @@ static TEST_FN(cursor_down_v1)
}
/* NOTE: CUD V-2: Cursor Down Above Bottom Margin */
-static TEST_FN(cursor_down_v2)
+function TEST_FN(cursor_down_v2)
{
launder_static_string(term, CSI(1;3r));
launder_static_string(term, s8("A"));
@@ -576,7 +576,7 @@ static TEST_FN(cursor_down_v2)
}
/* NOTE: CUD V-3: Cursor Down Below Bottom Margin */
-static TEST_FN(cursor_down_v3)
+function TEST_FN(cursor_down_v3)
{
launder_static_string(term, CSI(1;3r));
launder_static_string(term, s8("A"));
@@ -594,7 +594,7 @@ static TEST_FN(cursor_down_v3)
}
/* NOTE: CUP V-1: Normal Usage */
-static TEST_FN(cursor_position_v1)
+function TEST_FN(cursor_position_v1)
{
launder_static_string(term, CSI(2;3H));
s8 raw = launder_static_string(term, s8("A"));
@@ -608,7 +608,7 @@ static TEST_FN(cursor_position_v1)
}
/* NOTE: CUP V-2: Off the Screen */
-static TEST_FN(cursor_position_v2)
+function TEST_FN(cursor_position_v2)
{
launder_static_string(term, CSI(500;500H));
s8 raw = launder_static_string(term, s8("A"));
@@ -624,7 +624,7 @@ static TEST_FN(cursor_position_v2)
}
/* NOTE: CUP V-3: Relative to Origin */
-static TEST_FN(cursor_position_v3)
+function TEST_FN(cursor_position_v3)
{
launder_static_string(term, CSI(2;3r));
launder_static_string(term, CSI(?6h));
@@ -638,7 +638,7 @@ static TEST_FN(cursor_position_v3)
}
/* NOTE: CUP V-4: Relative to Origin with Left/Right Margins */
-static TEST_FN(cursor_position_v4)
+function TEST_FN(cursor_position_v4)
{
TestResult result = UNSUPPORTED;
@@ -658,7 +658,7 @@ static TEST_FN(cursor_position_v4)
}
/* NOTE: CUP V-5: Limits with Scroll Region and Origin Mode */
-static TEST_FN(cursor_position_v5)
+function TEST_FN(cursor_position_v5)
{
TestResult result = UNSUPPORTED;
@@ -678,10 +678,10 @@ static TEST_FN(cursor_position_v5)
}
/* NOTE: CUP V-6: Pending Wrap is Unset */
-static TEST_FN(cursor_position_v6)
+function TEST_FN(cursor_position_v6)
{
u8 buffer_store[32];
- Stream buffer = {.buf = buffer_store, .cap = sizeof(buffer_store)};
+ Stream buffer = {.data = buffer_store, .capacity = sizeof(buffer_store)};
stream_push_s8(&buffer, s8("\x1B["));
stream_push_u64(&buffer, term->size.w);
stream_push_byte(&buffer, 'G');
@@ -701,7 +701,7 @@ static TEST_FN(cursor_position_v6)
}
/* NOTE: CUU V-1: Cursor Up */
-static TEST_FN(cursor_up_v1)
+function TEST_FN(cursor_up_v1)
{
launder_static_string(term, CSI(3;1H));
launder_static_string(term, s8("A"));
@@ -718,7 +718,7 @@ static TEST_FN(cursor_up_v1)
}
/* NOTE: CUU V-2: Cursor Up Below Top Margin */
-static TEST_FN(cursor_up_v2)
+function TEST_FN(cursor_up_v2)
{
launder_static_string(term, CSI(2;4r));
launder_static_string(term, CSI(3;1H));
@@ -736,7 +736,7 @@ static TEST_FN(cursor_up_v2)
}
/* NOTE: CUU V-3: Cursor Up Above Top Margin */
-static TEST_FN(cursor_up_v3)
+function TEST_FN(cursor_up_v3)
{
launder_static_string(term, CSI(3;5r));
launder_static_string(term, CSI(3;1H));
@@ -756,7 +756,7 @@ static TEST_FN(cursor_up_v3)
/* NOTE: DCH V-1: Simple Delete Character */
-static TEST_FN(delete_characters_v1)
+function TEST_FN(delete_characters_v1)
{
launder_static_string(term, s8("ABC123"));
launder_static_string(term, CSI(3G));
@@ -776,7 +776,7 @@ static TEST_FN(delete_characters_v1)
}
/* NOTE: DCH V-2: SGR State */
-static TEST_FN(delete_characters_v2)
+function TEST_FN(delete_characters_v2)
{
launder_static_string(term, s8("ABC123"));
launder_static_string(term, CSI(3G));
@@ -802,7 +802,7 @@ static TEST_FN(delete_characters_v2)
}
/* NOTE: DCH V-3: Outside Left/Right Scroll Region */
-static TEST_FN(delete_characters_v3)
+function TEST_FN(delete_characters_v3)
{
TestResult result = UNSUPPORTED;
@@ -828,7 +828,7 @@ static TEST_FN(delete_characters_v3)
}
/* NOTE: DCH V-4: Inside Left/Right Scroll Region */
-static TEST_FN(delete_characters_v4)
+function TEST_FN(delete_characters_v4)
{
TestResult result = UNSUPPORTED;
@@ -854,7 +854,7 @@ static TEST_FN(delete_characters_v4)
}
/* NOTE: DCH V-5: Split Wide Character */
-static TEST_FN(delete_characters_v5)
+function TEST_FN(delete_characters_v5)
{
launder_static_string(term, s8("A橋123"));
launder_static_string(term, CSI(3G));
@@ -873,7 +873,7 @@ static TEST_FN(delete_characters_v5)
}
/* NOTE: DECSTBM V-1: Full Screen */
-static TEST_FN(set_top_bottom_margins_v1)
+function TEST_FN(set_top_bottom_margins_v1)
{
launder_static_string(term, s8("ABC\r\nDEF\r\nGHI\r\n"));
launder_static_string(term, CSI(r));
@@ -900,7 +900,7 @@ static TEST_FN(set_top_bottom_margins_v1)
}
/* NOTE: DECSTBM V-2: Top Only */
-static TEST_FN(set_top_bottom_margins_v2)
+function TEST_FN(set_top_bottom_margins_v2)
{
launder_static_string(term, s8("ABC\r\nDEF\r\nGHI\r\n"));
launder_static_string(term, CSI(2r));
@@ -926,7 +926,7 @@ static TEST_FN(set_top_bottom_margins_v2)
}
/* NOTE: DECSTBM V-3: Top and Bottom */
-static TEST_FN(set_top_bottom_margins_v3)
+function TEST_FN(set_top_bottom_margins_v3)
{
launder_static_string(term, s8("ABC\r\nDEF\r\nGHI\r\n"));
launder_static_string(term, CSI(1;2r));
@@ -949,7 +949,7 @@ static TEST_FN(set_top_bottom_margins_v3)
}
/* NOTE: DECSTBM V-4: Top Equal to Bottom */
-static TEST_FN(set_top_bottom_margins_v4)
+function TEST_FN(set_top_bottom_margins_v4)
{
launder_static_string(term, s8("ABC\r\nDEF\r\nGHI\r\n"));
launder_static_string(term, CSI(2;2r));
@@ -975,24 +975,24 @@ static TEST_FN(set_top_bottom_margins_v4)
}
/* NOTE: DSR V-1: Operating Status */
-static TEST_FN(device_status_report_v1)
+function TEST_FN(device_status_report_v1)
{
- Stream buffer = {.buf = malloc(KB(1)), .cap = KB(1)};
+ Stream buffer = {.data = malloc(KB(1)), .capacity = KB(1)};
term->child = (iptr)&buffer;
s8 raw = launder_static_string(term, CSI(5n));
handle_input(term, arena, raw);
TestResult result = s8_equal(s8("\x1B[0n"), stream_to_s8(&buffer));
- free(buffer.buf);
+ free(buffer.data);
return result;
}
/* NOTE: DSR V-2: Cursor Position */
-static TEST_FN(device_status_report_v2)
+function TEST_FN(device_status_report_v2)
{
- Stream buffer = {.buf = malloc(KB(1)), .cap = KB(1)};
+ Stream buffer = {.data = malloc(KB(1)), .capacity = KB(1)};
term->child = (iptr)&buffer;
launder_static_string(term, CSI(2;4H));
@@ -1001,7 +1001,7 @@ static TEST_FN(device_status_report_v2)
TestResult result = s8_equal(s8("\x1B[2;4R"), stream_to_s8(&buffer));
- free(buffer.buf);
+ free(buffer.data);
return result;
}
@@ -1023,13 +1023,13 @@ main(void)
u32 failure_count = 0;
for (u32 i = 0; i < ARRAY_COUNT(tests); i++) {
- MemoryBlock term_backing = {.memory = malloc(MB(4)), .size = MB(4)};
+ OSMemoryBlock term_backing = {.memory = malloc(MB(4)), .size = MB(4)};
/* TODO(rnp): term sizes as part of the tests */
Term *term = place_term_into_memory(term_backing, 24, 80);
TestResult result = tests[i].implementation(term, term->arena_for_frame);
stream_push_s8(&log, tests[i].name);
stream_push_s8(&log, s8(":"));
- size count = tests[i].name.len;
+ iz count = tests[i].name.len;
while (count < max_name_len) { stream_push_byte(&log, ' '); count++; }
switch (result) {
case FAILURE: stream_push_s8(&log, failure_string); failure_count++; break;
diff --git a/util.c b/util.c
@@ -1,21 +1,21 @@
/* See LICENSE for copyright details */
/* NOTE: avoids braindead standards committee UB when n is negative or shift is > 31 */
-static u32
+function u32
safe_left_shift(u32 n, u32 shift)
{
u64 result = (u64)n << (shift & 63);
return result & 0xFFFFFFFF;
}
-static u32
+function u32
round_down_power_of_2(u32 a)
{
u32 result = 0x80000000UL >> clz_u32(a);
return result;
}
-static v2
+function v2
sub_v2(v2 a, v2 b)
{
v2 result;
@@ -24,49 +24,49 @@ sub_v2(v2 a, v2 b)
return result;
}
-static f32
+function f32
length_v2(v2 a)
{
f32 result = a.x * a.x + a.y * a.y;
return result;
}
-static b32
+function b32
equal_iv2(iv2 a, iv2 b)
{
b32 result = a.x == b.x && a.y == b.y;
return result;
}
-static b32
+function b32
equal_uv2(uv2 a, uv2 b)
{
b32 result = a.x == b.x && a.y == b.y;
return result;
}
-static v2
+function v2
v2_from_iv2(iv2 a)
{
v2 result = {.x = a.x, .y = a.y};
return result;
}
-static b32
+function b32
is_valid_range(Range r)
{
b32 result = !equal_iv2(r.end, INVALID_RANGE_END);
return result;
}
-static b32
+function b32
equal_range(Range a, Range b)
{
b32 result = equal_iv2(a.start, b.start) && equal_iv2(a.end, b.end);
return result;
}
-static b32
+function b32
point_in_rect(v2 point, Rect rect)
{
v2 max = {.x = rect.pos.x + rect.size.w, .y = rect.pos.y + rect.size.h};
@@ -74,7 +74,7 @@ point_in_rect(v2 point, Rect rect)
return result;
}
-static Range
+function Range
normalize_range(Range r)
{
Range result;
@@ -92,7 +92,7 @@ normalize_range(Range r)
/* NOTE(rnp): based on nullprogram's lock-free, concurrent,
* generic queue in 32 bits */
-static i32
+function i32
work_queue_push(u32 *q, u32 capacity)
{
ASSERT(ISPOWEROFTWO(capacity));
@@ -106,13 +106,13 @@ work_queue_push(u32 *q, u32 capacity)
return next == tail ? -1 : head;
}
-static void
+function void
work_queue_push_commit(u32 *q)
{
atomic_fetch_add(q, 1);
}
-static i32
+function i32
work_queue_pop(u32 *q, u32 capacity)
{
ASSERT(ISPOWEROFTWO(capacity));
@@ -123,13 +123,13 @@ work_queue_pop(u32 *q, u32 capacity)
return head == tail ? -1 : tail;
}
-static void
+function void
work_queue_pop_commit(u32 *q)
{
atomic_fetch_add(q, 0x10000u);
}
-static b32
+function b32
work_queue_empty(u32 *q, u32 capacity)
{
ASSERT(ISPOWEROFTWO(capacity));
@@ -140,7 +140,7 @@ work_queue_empty(u32 *q, u32 capacity)
return head == tail;
}
-static void
+function void
work_queue_insert(Term *t, u32 type, void *ctx)
{
i32 index = work_queue_push(&t->work_queue, t->work_queue_capacity);
@@ -151,8 +151,8 @@ work_queue_insert(Term *t, u32 type, void *ctx)
t->work_queue_items[index].ctx = ctx;
}
-static void
-mem_copy(void *restrict src, void *restrict dest, size len)
+function void
+mem_copy(void *restrict dest, void *restrict src, iz len)
{
ASSERT(len >= 0);
u8 *s = src, *d = dest;
@@ -160,8 +160,8 @@ mem_copy(void *restrict src, void *restrict dest, size len)
}
#define zero_struct(s) mem_clear(s, 0, sizeof(typeof(*s)))
-static void *
-mem_clear(void *p_, u8 c, size len)
+function void *
+mem_clear(void *p_, u8 c, iz len)
{
u8 *p = p_;
while (len) p[--len] = c;
@@ -170,11 +170,11 @@ mem_clear(void *p_, u8 c, size len)
#define push_struct(a, t) alloc(a, t, 1)
#define alloc(a, t, n) (t *)alloc_(a, sizeof(t), _Alignof(t), n)
-static void *
-alloc_(Arena *a, size len, size align, size count)
+function void *
+alloc_(Arena *a, iz len, iz align, iz count)
{
- size padding = -(uintptr_t)a->beg & (align - 1);
- size available = a->end - a->beg - padding;
+ iz padding = -(uintptr_t)a->beg & (align - 1);
+ iz available = a->end - a->beg - padding;
if (available <= 0 || available / len < count) {
ASSERT(0);
}
@@ -184,8 +184,8 @@ alloc_(Arena *a, size len, size align, size count)
return mem_clear(p, 0, count * len);
}
-static Arena
-arena_from_memory_block(MemoryBlock memory)
+function Arena
+arena_from_memory_block(OSMemoryBlock memory)
{
Arena result;
result.beg = memory.memory;
@@ -193,17 +193,17 @@ arena_from_memory_block(MemoryBlock memory)
return result;
}
-static MemoryBlock
-memory_block_from_arena(Arena *a, size requested_size)
+function OSMemoryBlock
+memory_block_from_arena(Arena *a, iz requested_size)
{
- MemoryBlock result;
+ OSMemoryBlock result;
result.memory = alloc_(a, requested_size, 64, 1);
result.size = requested_size;
return result;
}
-static Arena
-sub_arena(Arena *a, size size)
+function Arena
+sub_arena(Arena *a, iz size)
{
Arena result = {0};
result.beg = alloc_(a, size, 64, 1);
@@ -211,7 +211,7 @@ sub_arena(Arena *a, size size)
return result;
}
-static TempArena
+function TempArena
begin_temp_arena(Arena *a)
{
TempArena result;
@@ -220,7 +220,7 @@ begin_temp_arena(Arena *a)
return result;
}
-static void
+function void
end_temp_arena(TempArena ta)
{
Arena *a = ta.arena;
@@ -230,63 +230,67 @@ end_temp_arena(TempArena ta)
/* NOTE: This performs wrapping of the ring buffer as needed; since a line could be in
* progress this must also adjust the start and end of the current line */
-static void
-commit_to_rb(TermView *tv, size len)
+function void
+commit_to_rb(TermView *tv, iz len)
{
- ASSERT(ABS(len) <= tv->log.cap);
+ ASSERT(ABS(len) <= tv->log.capacity);
- tv->log.widx += len;
- tv->log.filled += len;
+ tv->log.write_index += len;
+ tv->log.filled += len;
- CLAMP(tv->log.filled, 0, tv->log.cap);
- if (tv->log.widx >= tv->log.cap) {
- tv->log.widx -= tv->log.cap;
+ CLAMP(tv->log.filled, 0, tv->log.capacity);
+ if (tv->log.write_index >= tv->log.capacity) {
+ tv->log.write_index -= tv->log.capacity;
- size line = tv->lines.widx;
- tv->lines.buf[line].start -= tv->log.cap;
- tv->lines.buf[line].end -= tv->log.cap;
+ iz line = tv->lines.count;
+ tv->lines.data[line].start -= tv->log.capacity;
+ tv->lines.data[line].end -= tv->log.capacity;
}
ASSERT(tv->log.filled >= 0);
- ASSERT(tv->log.widx >= 0 && tv->log.widx < tv->log.cap);
+ ASSERT(tv->log.write_index >= 0 && tv->log.write_index < tv->log.capacity);
}
-static void
-line_buf_alloc(LineBuf *lb, Arena *a, u8 *start_position, CellStyle state, size capacity)
+function LineBuffer
+line_buffer_alloc(Arena *a, u8 *start_position, CellStyle state, iz 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;
+ LineBuffer result = {0};
+ result.capacity = capacity;
+ result.data = alloc(a, Line, capacity);
+ result.data[0].start = start_position;
+ result.data[0].end = start_position;
+ result.data[0].cursor_state = state;
+ return result;
}
-static s8
-s8alloc(Arena *a, size len)
+function s8
+s8alloc(Arena *a, iz len)
{
return (s8){.len = len, .data = alloc(a, u8, len)};
}
-static s8
+function s8
c_str_to_s8(char *s)
{
s8 result = {.data = (u8 *)s};
- while (*s) { result.len++; s++; }
+ if (s) {
+ char *end = s;
+ while (*end) end++;
+ result.len = end - s;
+ }
return result;
}
-static b32
+function b32
s8_equal(s8 a, s8 b)
{
b32 result = a.len == b.len;
- for (size i = 0; result && i < a.len; i++)
+ for (iz i = 0; result && i < a.len; i++)
result = a.data[i] == b.data[i];
return result;
}
-static b32
+function b32
s8_prefix_of(s8 s, s8 match)
{
b32 result = 0;
@@ -297,10 +301,10 @@ s8_prefix_of(s8 s, s8 match)
return result;
}
-static s8
+function s8
s8_chop_at(s8 raw, u8 delim)
{
- size i;
+ iz i;
for (i = 0; i < raw.len; i++) {
if (raw.data[i] == delim)
break;
@@ -309,12 +313,12 @@ s8_chop_at(s8 raw, u8 delim)
return result;
}
-static void
+function void
s8_parse_i32_accum(struct conversion_result *result, s8 raw)
{
result->status = CR_SUCCESS;
- size i = 0;
+ iz i = 0;
i32 scale = 1;
if (raw.len && raw.data[0] == '-') {
scale = -1;
@@ -342,7 +346,7 @@ s8_parse_i32_accum(struct conversion_result *result, s8 raw)
result->i *= scale;
}
-static struct conversion_result
+function struct conversion_result
s8_parse_i32(s8 raw)
{
struct conversion_result result = {0};
@@ -350,7 +354,7 @@ s8_parse_i32(s8 raw)
return result;
}
-static struct conversion_result
+function struct conversion_result
s8_parse_i32_until(s8 raw, u8 delim)
{
s8 chopped = s8_chop_at(raw, delim);
@@ -359,51 +363,58 @@ s8_parse_i32_until(s8 raw, u8 delim)
return result;
}
-static Stream
+function Stream
arena_stream(Arena a)
{
- Stream result = {0};
- result.cap = a.end - a.beg;
- result.buf = (typeof(result.buf))a.beg;
+ Stream result = {0};
+ result.capacity = a.end - a.beg;
+ result.data = a.beg;
return result;
}
-static Stream
-stream_alloc(Arena *a, u32 cap)
+function void
+stream_reset(Stream *s, iz index)
+{
+ s->errors = index < 0 || index > s->capacity;
+ if (!s->errors) s->count = index;
+}
+
+function Stream
+stream_alloc(Arena *a, i32 capacity)
{
- Stream result = {0};
- result.cap = cap;
- result.buf = alloc(a, typeof(*result.buf), cap);
+ Stream result = {0};
+ result.capacity = capacity;
+ result.data = alloc(a, u8, capacity);
return result;
}
-static s8
+function s8
stream_to_s8(Stream *s)
{
- s8 result = {.len = s->widx, .data = s->buf};
+ s8 result = {.len = s->count, .data = s->data};
return result;
}
-static void
+function void
stream_push_byte(Stream *s, u8 cp)
{
- s->errors |= !(s->cap - s->widx);
+ s->errors |= !(s->capacity - s->count);
if (!s->errors)
- s->buf[s->widx++] = cp;
+ s->data[s->count++] = cp;
}
-static void
+function void
stream_push_s8(Stream *s, s8 str)
{
- s->errors |= (s->cap - s->widx) < str.len;
+ s->errors |= (s->capacity - s->count) < str.len;
if (!s->errors) {
- for (size i = 0; i < str.len; i++)
- s->buf[s->widx++] = str.data[i];
+ mem_copy(s->data + s->count, str.data, str.len);
+ s->count += str.len;
}
}
/* NOTE: how can he get away with not using a library for this */
-static void
+function void
stream_push_s8_left_padded(Stream *s, s8 str, u32 width)
{
for (u32 i = str.len; i < width; i++)
@@ -411,13 +422,13 @@ stream_push_s8_left_padded(Stream *s, s8 str, u32 width)
stream_push_s8(s, str);
}
-static void
+function void
stream_push_s8s(Stream *s, u32 count, s8 *strs)
{
while (count) { stream_push_s8(s, *strs++); count--; }
}
-static void
+function void
stream_push_hex_u64(Stream *s, u64 n)
{
if (!s->errors) {
@@ -435,7 +446,7 @@ stream_push_hex_u64(Stream *s, u64 n)
}
}
-static void
+function void
stream_push_u64_padded(Stream *s, u64 n, u32 width)
{
if (!s->errors) {
@@ -451,13 +462,13 @@ stream_push_u64_padded(Stream *s, u64 n, u32 width)
}
}
-static void
+function void
stream_push_u64(Stream *s, u64 n)
{
stream_push_u64_padded(s, n, 0);
}
-static void
+function void
stream_push_i64(Stream *s, i64 n)
{
s->errors |= n == I64_MIN;
@@ -470,7 +481,7 @@ stream_push_i64(Stream *s, i64 n)
}
}
-static void
+function void
stream_push_iv2(Stream *s, iv2 v)
{
stream_push_byte(s, '{');
@@ -480,7 +491,7 @@ stream_push_iv2(Stream *s, iv2 v)
stream_push_byte(s, '}');
}
-static void
+function void
stream_push_f64(Stream *s, f64 f, i64 prec)
{
if (f < 0) {
@@ -506,7 +517,7 @@ stream_push_f64(Stream *s, f64 f, i64 prec)
}
}
-static SelectionIterator
+function SelectionIterator
selection_iterator(Range s, Row *rows, u32 term_width)
{
SelectionIterator result;
@@ -518,7 +529,7 @@ selection_iterator(Range s, Row *rows, u32 term_width)
return result;
}
-static Cell *
+function Cell *
selection_next(SelectionIterator *s)
{
Cell *result = 0;
@@ -535,7 +546,7 @@ selection_next(SelectionIterator *s)
return result;
}
-static s8
+function s8
envp_lookup(s8 name, char **e)
{
ASSERT(name.data[name.len - 1] == '=');
@@ -556,7 +567,7 @@ envp_lookup(s8 name, char **e)
return result;
}
-static c8 **
+function c8 **
construct_c_str_array(Arena *a, SLLVariableVector vars)
{
c8 **result = alloc(a, c8 *, vars.count + 1);
@@ -571,7 +582,7 @@ construct_c_str_array(Arena *a, SLLVariableVector vars)
return result;
}
-static b32
+function b32
any_mouse_down(TerminalInput *input)
{
b32 result = input->keys[MOUSE_LEFT].ended_down ||
@@ -580,7 +591,7 @@ any_mouse_down(TerminalInput *input)
return result;
}
-static b32
+function b32
all_mouse_up(TerminalInput *input)
{
b32 result = !input->keys[MOUSE_LEFT].ended_down &&
@@ -589,7 +600,7 @@ all_mouse_up(TerminalInput *input)
return result;
}
-static void
+function void
button_action(ButtonState *button, b32 pressed)
{
if (pressed != button->ended_down)
@@ -597,14 +608,14 @@ button_action(ButtonState *button, b32 pressed)
button->ended_down = pressed;
}
-static b32
+function b32
pressed_last_frame(ButtonState *button)
{
b32 result = (button->transitions > 1) || (button->ended_down && button->transitions == 1);
return result;
}
-static s8
+function s8
utf8_encode(u32 cp)
{
static u8 buf[4];
diff --git a/util.h b/util.h
@@ -138,7 +138,7 @@ typedef Cell *Row;
typedef struct {
Cell *cells;
Row *rows;
- MemoryBlock backing_store;
+ OSMemoryBlock backing_store;
} Framebuffer;
typedef struct {
@@ -148,16 +148,16 @@ typedef struct {
} Line;
typedef struct {
- size cap;
- size filled;
- size widx;
- Line *buf;
-} LineBuf;
+ iz capacity;
+ iz filled;
+ iz count;
+ Line *data;
+} LineBuffer;
typedef struct {
- RingBuf log;
- LineBuf lines;
- Framebuffer fb;
+ OSRingBuffer log;
+ LineBuffer lines;
+ Framebuffer fb;
} TermView;
enum terminal_mode {
@@ -471,7 +471,7 @@ typedef struct Term {
i32 view_idx;
i32 scroll_offset;
- size unprocessed_bytes;
+ iz unprocessed_bytes;
iptr child;
@@ -490,9 +490,9 @@ typedef struct Term {
Stream saved_title;
Stream error_stream;
- PlatformAPI *platform;
+ OS *os;
} Term;
-static f32 dt_for_frame;
+global f32 dt_for_frame;
#endif /* _UTIL_H_ */
diff --git a/vtgl.c b/vtgl.c
@@ -1,5 +1,15 @@
/* See LICENSE for copyright details */
+
+/* TODO(rnp):
+ * [ ]: refactor: instead of having a callback for key input it is probably better for
+ * the platform to pass an in order list of key events last frame
+ * [ ]: refactor: remove os_... includes from vtgl.h
+ * [ ]: refactor: stream_ensure_terminator
+ * [ ]: refactor: push_s8 (into arena); rename push_s8 to draw_s8
+ */
+
/* TODO: define this ourselves since we should be loading it at runtime */
+/* TODO(rnp): this is only valid on platforms where we can directly link libGL */
#define GL_GLEXT_PROTOTYPES
#include <GL/glcorearb.h>
@@ -34,7 +44,7 @@
" gl_Position = u_Pmat * vec4(vertex_position, 0.0, 1.0);\n" \
"}\n"
-static void
+function void
set_projection_matrix(GLCtx *gl, u32 stage)
{
f32 w = gl->window_size.w;
@@ -50,7 +60,7 @@ set_projection_matrix(GLCtx *gl, u32 stage)
glProgramUniformMatrix4fv(gl->programs[stage], SHADER_PMAT_LOC, 1, GL_TRUE, pmat);
}
-static u32
+function u32
compile_shader(Arena a, u32 type, s8 shader)
{
u32 sid = glCreateShader(type);
@@ -74,7 +84,7 @@ compile_shader(Arena a, u32 type, s8 shader)
return sid;
}
-static u32
+function u32
program_from_shader_text(s8 vertex, s8 fragment, Arena a)
{
u32 pid = glCreateProgram();
@@ -110,7 +120,7 @@ typedef struct {
u32 stage;
} queue_shader_reload_ctx;
-static void
+function void
update_uniforms(GLCtx *gl, enum shader_stages stage)
{
switch (stage) {
@@ -126,10 +136,10 @@ update_uniforms(GLCtx *gl, enum shader_stages stage)
}
}
-static void
-reload_shader(GLCtx *gl, PlatformAPI *platform, u8 *path, u32 stage, s8 info, Arena a)
+function void
+reload_shader(GLCtx *gl, OS *os, u8 *path, u32 stage, s8 info, Arena a)
{
- s8 fs_text = platform->read_file(path, &a);
+ s8 fs_text = os->read_file(path, &a);
if (fs_text.len) {
u32 program = program_from_shader_text(s8(VERTEX_SHADER_TEXT), fs_text, a);
if (program) {
@@ -142,45 +152,45 @@ reload_shader(GLCtx *gl, PlatformAPI *platform, u8 *path, u32 stage, s8 info, Ar
if (info.len) os_write_err_msg(info);
}
-static s8 fs_name[SHADER_COUNT] = {
+global s8 fs_name[SHADER_COUNT] = {
[SHADER_RENDER] = s8("frag_render.glsl"),
[SHADER_RECTS] = s8("frag_for_rects.glsl"),
[SHADER_POST] = s8("frag_post.glsl"),
};
-static void
-reload_all_shaders(GLCtx *gl, PlatformAPI *platform, Arena a)
+function void
+reload_all_shaders(GLCtx *gl, OS *os, Arena a)
{
Stream fs_path = stream_alloc(&a, KB(4));
stream_push_s8(&fs_path, g_shader_path_prefix);
- if (fs_path.widx && fs_path.buf[fs_path.widx - 1] != platform->path_separator)
- stream_push_byte(&fs_path, platform->path_separator);
+ if (fs_path.count && fs_path.data[fs_path.count - 1] != os->path_separator)
+ stream_push_byte(&fs_path, os->path_separator);
- i32 sidx = fs_path.widx;
+ i32 sidx = fs_path.count;
for (u32 i = 0; i < SHADER_COUNT; i++) {
stream_push_s8(&fs_path, fs_name[i]);
stream_push_byte(&fs_path, 0);
- reload_shader(gl, platform, fs_path.buf, i, (s8){0}, a);
- fs_path.widx = sidx;
+ reload_shader(gl, os, fs_path.data, i, (s8){0}, a);
+ stream_reset(&fs_path, sidx);
}
os_write_err_msg(s8("Reloaded Shaders\n"));
}
-static PLATFORM_FILE_WATCH_CALLBACK_FN(queue_shader_reload)
+function OS_FILE_WATCH_CALLBACK_FN(queue_shader_reload)
{
queue_shader_reload_ctx *ctx = user_ctx;
ctx->path = path;
work_queue_insert(ctx->t, WQ_RELOAD_SHADER, ctx);
}
-static v4
+function v4
normalize_colour(Colour c)
{
return (v4){.r = c.r / 255.0f, .g = c.g / 255.0f, .b = c.b / 255.0f, .a = c.a / 255.0f};
}
-static void
+function void
clear_colour(void)
{
Colour c = g_colours.data[g_colours.bgidx];
@@ -189,14 +199,14 @@ clear_colour(void)
glClear(GL_COLOR_BUFFER_BIT);
}
-static v2
+function v2
get_cell_size(FontAtlas *fa)
{
v2 result = {.w = fa->info.w, .h = fa->info.h};
return result;
}
-static v2
+function v2
get_occupied_size(Term *t)
{
v2 cs = get_cell_size(&t->fa);
@@ -204,7 +214,7 @@ get_occupied_size(Term *t)
return result;
}
-static v2
+function v2
get_terminal_top_left(Term *t)
{
v2 os = get_occupied_size(t);
@@ -213,8 +223,8 @@ get_terminal_top_left(Term *t)
return result;
}
-static void
-resize_terminal(Term *t, PlatformAPI *platform, iv2 window_size)
+function void
+resize_terminal(Term *t, OS *os, iv2 window_size)
{
v2 ws = v2_from_iv2(window_size);
ws.w -= 2 * g_term_margin.w;
@@ -231,7 +241,7 @@ resize_terminal(Term *t, PlatformAPI *platform, iv2 window_size)
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;
+ stream_reset(&t->error_stream, 0);
}
if (!equal_iv2(old_size, t->size)) {
@@ -240,14 +250,14 @@ resize_terminal(Term *t, PlatformAPI *platform, iv2 window_size)
t->gl.flags |= NEEDS_REFILL;
}
- platform->set_terminal_size(t->child, t->size.h, t->size.w, ws.w, ws.h);
+ os->set_terminal_size(t->child, t->size.h, t->size.w, ws.w, ws.h);
t->gl.flags |= RESIZE_RENDERER;
t->gl.flags &= ~NEEDS_RESIZE;
}
-static void
-resize(Term *t, PlatformAPI *platform, iv2 window_size)
+function void
+resize(Term *t, OS *os, iv2 window_size)
{
GLCtx *gl = &t->gl;
gl->window_size = window_size;
@@ -292,7 +302,7 @@ resize(Term *t, PlatformAPI *platform, iv2 window_size)
gl->flags &= ~RESIZE_RENDERER;
}
-static RenderCtx
+function RenderCtx
make_render_ctx(Arena *a, GLCtx *gl, FontAtlas *fa)
{
RenderCtx result;
@@ -303,7 +313,7 @@ make_render_ctx(Arena *a, GLCtx *gl, FontAtlas *fa)
return result;
}
-static iv2
+function iv2
get_gpu_texture_position(v2 cs, u32 gpu_tile_index)
{
uv2 gpu_position = {.x = gpu_tile_index & 0xFFFF, .y = gpu_tile_index >> 16};
@@ -311,7 +321,7 @@ get_gpu_texture_position(v2 cs, u32 gpu_tile_index)
return result;
}
-static u32
+function u32
get_gpu_glyph_index(Arena a, GLCtx *gl, FontAtlas *fa, u32 codepoint, u32 font_id,
enum face_style style, CachedGlyph **out)
{
@@ -334,7 +344,7 @@ get_gpu_glyph_index(Arena a, GLCtx *gl, FontAtlas *fa, u32 codepoint, u32 font_i
}
/* NOTE: this function assumes we are drawing quads */
-static void
+function void
flush_render_push_buffer(RenderCtx *rc)
{
BEGIN_TIMED_BLOCK();
@@ -359,7 +369,7 @@ flush_render_push_buffer(RenderCtx *rc)
END_TIMED_BLOCK();
}
-static u32
+function u32
get_render_push_buffer_idx(RenderCtx *rc, u32 count)
{
if (rc->rpb->count + count > RENDER_PUSH_BUFFER_CAP)
@@ -369,7 +379,7 @@ get_render_push_buffer_idx(RenderCtx *rc, u32 count)
return result;
}
-static void
+function void
push_rect_full(RenderCtx *rc, Rect r, v4 colour, v2 min_tex_coord, v2 max_tex_coord)
{
BEGIN_TIMED_BLOCK();
@@ -397,7 +407,7 @@ push_rect_full(RenderCtx *rc, Rect r, v4 colour, v2 min_tex_coord, v2 max_tex_co
END_TIMED_BLOCK();
}
-static void
+function void
push_rect_textured(RenderCtx *rc, Rect r, v4 colour, b32 flip_texture)
{
v2 min_tex_coord, max_tex_coord;
@@ -411,7 +421,7 @@ push_rect_textured(RenderCtx *rc, Rect r, v4 colour, b32 flip_texture)
push_rect_full(rc, r, colour, min_tex_coord, max_tex_coord);
}
-static void
+function void
push_rect(RenderCtx *rc, Rect r, v4 colour)
{
f32 max_x = 1.0f / rc->gl->glyph_bitmap_dim.x;
@@ -419,7 +429,7 @@ push_rect(RenderCtx *rc, Rect r, v4 colour)
push_rect_full(rc, r, colour, (v2){0}, (v2){.x = max_x, .y = max_y});
}
-static v2
+function v2
push_s8(RenderCtx *rc, v2 pos, v4 colour, u32 font_id, s8 s)
{
BEGIN_TIMED_BLOCK();
@@ -458,7 +468,7 @@ push_s8(RenderCtx *rc, v2 pos, v4 colour, u32 font_id, s8 s)
return text_size;
}
-static v2
+function v2
measure_text(RenderCtx *rc, u32 font_id, s8 text)
{
BEGIN_TIMED_BLOCK();
@@ -489,7 +499,7 @@ measure_text(RenderCtx *rc, u32 font_id, s8 text)
* to the screen as a full window quad. Therefore render_framebuffer must take care
* to handle all necessary padding (window and cell). Outside of here everyone should
* simply care about the terminal in terms of rows and columns (t->size). */
-static void
+function void
render_framebuffer(Term *t, RenderCell *render_buf, TerminalInput *input, Arena arena)
{
BEGIN_TIMED_BLOCK();
@@ -530,7 +540,7 @@ render_framebuffer(Term *t, RenderCell *render_buf, TerminalInput *input, Arena
END_TIMED_BLOCK();
}
-static void
+function void
render_cursor(Term *t, b32 focused, Arena a)
{
BEGIN_TIMED_BLOCK();
@@ -539,9 +549,9 @@ render_cursor(Term *t, b32 focused, Arena a)
Cell *c = &t->views[t->view_idx].fb.rows[curs.y][curs.x];
RenderCell *rc = alloc(&a, RenderCell, 3);
- size rc_off = 1;
- size length = sizeof(RenderCell);
- size offset = sizeof(RenderCell) * (curs.y * t->size.w + curs.x);
+ iz rc_off = 1;
+ iz length = sizeof(RenderCell);
+ iz offset = sizeof(RenderCell) * (curs.y * t->size.w + curs.x);
CachedGlyph *cg;
rc[1].gpu_glyph = get_gpu_glyph_index(a, &t->gl, &t->fa, c->cp, 0, c->bg & FS_MASK, &cg);
@@ -574,7 +584,7 @@ render_cursor(Term *t, b32 focused, Arena a)
END_TIMED_BLOCK();
}
-static iv2
+function iv2
mouse_to_cell_space(Term *t, v2 mouse)
{
v2 cell_size = get_cell_size(&t->fa);
@@ -590,7 +600,7 @@ mouse_to_cell_space(Term *t, v2 mouse)
return result;
}
-static void
+function void
stream_push_selection(Stream *s, Row *rows, Range sel, u32 term_width)
{
s->errors |= !is_valid_range(sel);
@@ -604,18 +614,18 @@ stream_push_selection(Stream *s, Row *rows, Range sel, u32 term_width)
stream_push_s8(s, utf8_encode(c->cp));
if (!ISSPACE(c->cp))
- last_non_space_idx = s->widx;
+ last_non_space_idx = s->count;
if (si.next.y != si.cursor.y) {
- s->widx = last_non_space_idx;
+ stream_reset(s, last_non_space_idx);
stream_push_byte(s, '\n');
}
}
- s->widx = last_non_space_idx;
+ stream_reset(s, last_non_space_idx);
}
-static void
+function void
begin_selection(Term *t, u32 click_count, v2 mouse)
{
Selection *sel = &t->selection;
@@ -633,7 +643,7 @@ begin_selection(Term *t, u32 click_count, v2 mouse)
t->gl.queued_render = 1;
}
-static void
+function void
update_selection(Term *t, TerminalInput *input)
{
if (!input->keys[MOUSE_LEFT].ended_down)
@@ -676,21 +686,22 @@ KEYBIND_FN(copy)
{
Stream buf = arena_stream(t->arena_for_frame);
stream_push_selection(&buf, t->views[t->view_idx].fb.rows, t->selection.range, t->size.w);
- platform->set_clipboard(&buf, a.i);
+ stream_push_byte(&buf, 0);
+ os->set_clipboard(buf.data, buf.count - 1, a.i);
return 1;
}
KEYBIND_FN(paste)
{
- Stream buf = arena_stream(t->arena_for_frame);
b32 bracketed = t->mode.win & WM_BRACKPASTE;
- if (bracketed) stream_push_s8(&buf, s8("\033[200~"));
- b32 success = platform->get_clipboard(&buf, a.i);
- if (bracketed) stream_push_s8(&buf, s8("\033[201~"));
-
- if (success)
- platform->write(t->child, stream_to_s8(&buf));
+ /* TODO(rnp): we may need to replace '\n' with '\r\n' */
+ s8 text = c_str_to_s8((char *)os->get_clipboard(a.i));
+ if (text.len) {
+ if (bracketed) os->write(t->child, s8("\033[200~"));
+ os->write(t->child, text);
+ if (bracketed) os->write(t->child, s8("\033[201~"));
+ }
return 1;
}
@@ -722,7 +733,7 @@ KEYBIND_FN(zoom)
return 1;
}
-static void
+function void
report_mouse(Term *t, TerminalInput *input, b32 released, b32 beginning)
{
if ((t->mode.win & WM_MOUSE_X10) && released)
@@ -785,10 +796,10 @@ report_mouse(Term *t, TerminalInput *input, b32 released, b32 beginning)
return;
}
- t->platform->write(t->child, stream_to_s8(&buf));
+ t->os->write(t->child, stream_to_s8(&buf));
}
-static void
+function void
begin_terminal_interaction(Term *t, TerminalInput *input, u32 click_count)
{
if (t->mode.win & WM_MOUSE_MASK) {
@@ -799,8 +810,8 @@ begin_terminal_interaction(Term *t, TerminalInput *input, u32 click_count)
}
}
-static b32
-terminal_interaction(Term *t, PlatformAPI *platform, TerminalInput *input, u32 click_count)
+function b32
+terminal_interaction(Term *t, OS *os, TerminalInput *input, u32 click_count)
{
b32 should_end_interaction = all_mouse_up(input);
@@ -810,24 +821,24 @@ terminal_interaction(Term *t, PlatformAPI *platform, TerminalInput *input, u32 c
} else {
update_selection(t, input);
if (pressed_last_frame(input->keys + MOUSE_MIDDLE))
- paste(t, platform, (Arg){.i = CLIPBOARD_1});
+ paste(t, os, (Arg){.i = OS_CLIPBOARD_SECONDARY});
b32 shift_down = input->modifiers & MOD_SHIFT;
if (input->mouse_scroll.y) {
if (t->mode.term & TM_ALTSCREEN) {
iptr child = t->child;
if (input->mouse_scroll.y > 0) {
- if (shift_down) platform->write(child, s8("\x1B[5;2~"));
- else platform->write(child, s8("\x19"));
+ if (shift_down) os->write(child, s8("\x1B[5;2~"));
+ else os->write(child, s8("\x19"));
} else {
- if (shift_down) platform->write(child, s8("\x1B[6;2~"));
- else platform->write(child, s8("\x05"));
+ if (shift_down) os->write(child, s8("\x1B[6;2~"));
+ else os->write(child, s8("\x05"));
}
} else {
Arg a = {.i = (i32)input->mouse_scroll.y};
if (shift_down)
a.i *= 5;
- scroll(t, platform, a);
+ scroll(t, os, a);
}
}
}
@@ -837,22 +848,22 @@ terminal_interaction(Term *t, PlatformAPI *platform, TerminalInput *input, u32 c
DEBUG_EXPORT VTGL_HANDLE_KEYS_FN(vtgl_handle_keys)
{
- Term *t = memory->memory;
- PlatformAPI *platform = &memory->platform_api;
- iptr child = t->child;
+ Term *t = memory->memory;
+ OS *os = &memory->os;
+ iptr child = t->child;
#ifdef _DEBUG
- if (key == KEY_F1 && action == ACT_PRESS) {
+ if (key == KEY_F1 && action == BUTTON_PRESS) {
dump_lines_to_file(t);
return;
}
- if (key == KEY_F11 && action == ACT_PRESS) {
+ if (key == KEY_F11 && action == BUTTON_PRESS) {
/* TODO: probably move this into the debug frame start */
DebugState *ds = memory->debug_memory;
ds->paused = !ds->paused;
return;
}
- if (key == KEY_F12 && action == ACT_PRESS) {
+ if (key == KEY_F12 && action == BUTTON_PRESS) {
t->gl.flags ^= DRAW_DEBUG_OVERLAY;
input->window_refreshed = 1;
return;
@@ -864,88 +875,80 @@ DEBUG_EXPORT VTGL_HANDLE_KEYS_FN(vtgl_handle_keys)
for (u32 i = 0; i < ARRAY_COUNT(g_hotkeys); i++) {
struct hotkey *hk = g_hotkeys + i;
if (hk->key == enc) {
- b32 handled = hk->fn(t, &memory->platform_api, hk->arg);
+ b32 handled = hk->fn(t, &memory->os, hk->arg);
if (handled)
return;
}
}
/* NOTE: send control sequences */
- if (modifiers & MOD_CONTROL && action != ACT_RELEASE) {
+ if (modifiers & MOD_CONTROL && action != BUTTON_RELEASE) {
/* TODO: this is wrong. look up where 8-bit modifiers should be sent */
if (0 && t->mode.win & WM_8BIT) {
if (key < 0x7F) {
- platform->write(child, utf8_encode(key | 0x80));
+ os->write(child, utf8_encode(key | 0x80));
return;
}
} else if (BETWEEN(key, 0x40, 0x5F)) {
- platform->write(child, utf8_encode(key - 0x40));
+ os->write(child, utf8_encode(key - 0x40));
return;
}
}
/* TODO: construct a hash table of bound keys */
switch (ENCODE_KEY(action, 0, key)) {
- case ENCODE_KEY(ACT_PRESS, 0, KEY_ESCAPE):
- case ENCODE_KEY(ACT_REPEAT, 0, KEY_ESCAPE):
- platform->write(child, s8("\x1B"));
- break;
- case ENCODE_KEY(ACT_PRESS, 0, KEY_TAB):
- case ENCODE_KEY(ACT_REPEAT, 0, KEY_TAB):
- platform->write(child, s8("\t"));
- break;
- case ENCODE_KEY(ACT_PRESS, 0, KEY_ENTER):
- case ENCODE_KEY(ACT_REPEAT, 0, KEY_ENTER):
- platform->write(child, s8("\r"));
- break;
- case ENCODE_KEY(ACT_PRESS, 0, KEY_BACKSPACE):
- case ENCODE_KEY(ACT_REPEAT, 0, KEY_BACKSPACE):
- platform->write(child, s8("\x7F"));
- break;
- case ENCODE_KEY(ACT_PRESS, 0, KEY_UP):
- case ENCODE_KEY(ACT_REPEAT, 0, KEY_UP):
- if (t->mode.win & WM_APPCURSOR)
- platform->write(child, s8("\x1BOA"));
- else
- platform->write(child, s8("\x1B[A"));
- break;
- case ENCODE_KEY(ACT_PRESS, 0, KEY_DOWN):
- case ENCODE_KEY(ACT_REPEAT, 0, KEY_DOWN):
- if (t->mode.win & WM_APPCURSOR)
- platform->write(child, s8("\x1BOB"));
- else
- platform->write(child, s8("\x1B[B"));
- break;
- case ENCODE_KEY(ACT_PRESS, 0, KEY_RIGHT):
- case ENCODE_KEY(ACT_REPEAT, 0, KEY_RIGHT):
- if (t->mode.win & WM_APPCURSOR)
- platform->write(child, s8("\x1BOC"));
- else
- platform->write(child, s8("\x1B[C"));
- break;
- case ENCODE_KEY(ACT_PRESS, 0, KEY_LEFT):
- case ENCODE_KEY(ACT_REPEAT, 0, KEY_LEFT):
- if (t->mode.win & WM_APPCURSOR)
- platform->write(child, s8("\x1BOD"));
- else
- platform->write(child, s8("\x1B[D"));
- break;
- case ENCODE_KEY(ACT_PRESS, 0, KEY_PAGE_UP):
- case ENCODE_KEY(ACT_REPEAT, 0, KEY_PAGE_UP):
- if (modifiers & MOD_CONTROL) platform->write(child, s8("\x1B[5;5~"));
- else if (modifiers & MOD_SHIFT) platform->write(child, s8("\x1B[5;2~"));
- else platform->write(child, s8("\x1B[5~"));
- break;
- case ENCODE_KEY(ACT_PRESS, 0, KEY_PAGE_DOWN):
- case ENCODE_KEY(ACT_REPEAT, 0, KEY_PAGE_DOWN):
- if (modifiers & MOD_CONTROL) platform->write(child, s8("\x1B[6;5~"));
- else if (modifiers & MOD_SHIFT) platform->write(child, s8("\x1B[6;2~"));
- else platform->write(child, s8("\x1B[6~"));
- break;
+ case ENCODE_KEY(BUTTON_PRESS, 0, KEY_ESCAPE):
+ case ENCODE_KEY(BUTTON_REPEAT, 0, KEY_ESCAPE): {
+ os->write(child, s8("\x1B"));
+ } break;
+ case ENCODE_KEY(BUTTON_PRESS, 0, KEY_TAB):
+ case ENCODE_KEY(BUTTON_REPEAT, 0, KEY_TAB): {
+ os->write(child, s8("\t"));
+ } break;
+ case ENCODE_KEY(BUTTON_PRESS, 0, KEY_ENTER):
+ case ENCODE_KEY(BUTTON_REPEAT, 0, KEY_ENTER): {
+ os->write(child, s8("\r"));
+ } break;
+ case ENCODE_KEY(BUTTON_PRESS, 0, KEY_BACKSPACE):
+ case ENCODE_KEY(BUTTON_REPEAT, 0, KEY_BACKSPACE): {
+ os->write(child, s8("\x7F"));
+ } break;
+ case ENCODE_KEY(BUTTON_PRESS, 0, KEY_UP):
+ case ENCODE_KEY(BUTTON_REPEAT, 0, KEY_UP): {
+ if (t->mode.win & WM_APPCURSOR) os->write(child, s8("\x1BOA"));
+ else os->write(child, s8("\x1B[A"));
+ } break;
+ case ENCODE_KEY(BUTTON_PRESS, 0, KEY_DOWN):
+ case ENCODE_KEY(BUTTON_REPEAT, 0, KEY_DOWN): {
+ if (t->mode.win & WM_APPCURSOR) os->write(child, s8("\x1BOB"));
+ else os->write(child, s8("\x1B[B"));
+ } break;
+ case ENCODE_KEY(BUTTON_PRESS, 0, KEY_RIGHT):
+ case ENCODE_KEY(BUTTON_REPEAT, 0, KEY_RIGHT): {
+ if (t->mode.win & WM_APPCURSOR) os->write(child, s8("\x1BOC"));
+ else os->write(child, s8("\x1B[C"));
+ } break;
+ case ENCODE_KEY(BUTTON_PRESS, 0, KEY_LEFT):
+ case ENCODE_KEY(BUTTON_REPEAT, 0, KEY_LEFT): {
+ if (t->mode.win & WM_APPCURSOR) os->write(child, s8("\x1BOD"));
+ else os->write(child, s8("\x1B[D"));
+ } break;
+ case ENCODE_KEY(BUTTON_PRESS, 0, KEY_PAGE_UP):
+ case ENCODE_KEY(BUTTON_REPEAT, 0, KEY_PAGE_UP): {
+ if (modifiers & MOD_CONTROL) os->write(child, s8("\x1B[5;5~"));
+ else if (modifiers & MOD_SHIFT) os->write(child, s8("\x1B[5;2~"));
+ else os->write(child, s8("\x1B[5~"));
+ } break;
+ case ENCODE_KEY(BUTTON_PRESS, 0, KEY_PAGE_DOWN):
+ case ENCODE_KEY(BUTTON_REPEAT, 0, KEY_PAGE_DOWN): {
+ if (modifiers & MOD_CONTROL) os->write(child, s8("\x1B[6;5~"));
+ else if (modifiers & MOD_SHIFT) os->write(child, s8("\x1B[6;2~"));
+ else os->write(child, s8("\x1B[6~"));
+ } break;
}
}
-static b32
+function b32
should_start_interaction(TerminalInput *input)
{
b32 result = input->mouse_scroll.y || input->mouse_scroll.x ||
@@ -955,7 +958,7 @@ should_start_interaction(TerminalInput *input)
return result;
}
-static void
+function void
begin_interaction(Term *t, InteractionState *is, TerminalInput *input)
{
is->click_count++;
@@ -983,14 +986,14 @@ begin_interaction(Term *t, InteractionState *is, TerminalInput *input)
}
}
-static void
+function void
end_interaction(InteractionState *is, TerminalInput *input)
{
is->active = (Interaction){.type = IS_NONE};
}
-static void
-handle_interactions(Term *t, TerminalInput *input, PlatformAPI *platform)
+function void
+handle_interactions(Term *t, TerminalInput *input, OS *os)
{
InteractionState *is = &t->interaction;
@@ -1016,13 +1019,13 @@ handle_interactions(Term *t, TerminalInput *input, PlatformAPI *platform)
case IS_DRAG: /* TODO */ break;
case IS_DEBUG: /* TODO */ break;
case IS_TERM: {
- if (terminal_interaction(t, platform, input, is->click_count))
+ if (terminal_interaction(t, os, input, is->click_count))
end_interaction(is, input);
} break;
}
}
-static void
+function 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;
@@ -1038,10 +1041,10 @@ gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, i32 len, const char *msg, co
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;
+ stream_reset(err, 0);
}
-static u32
+function u32
gen_2D_texture(iv2 size, u32 format, u32 filter, u32 *rgba)
{
/* TODO: logging */
@@ -1059,15 +1062,15 @@ DEBUG_EXPORT VTGL_INITIALIZE_FN(vtgl_initialize)
Term *t = (Term *)memory->memory;
Arena a = {.beg = (u8 *)(t + 1), .end = memory->memory + memory->memory_size};
- t->platform = &memory->platform_api;
+ t->os = &memory->os;
t->cursor.state = CURSOR_NORMAL;
cursor_reset(t);
- memory->platform_api.allocate_ring_buffer(&t->views[0].log, BACKLOG_SIZE);
- line_buf_alloc(&t->views[0].lines, &a, t->views[0].log.buf, t->cursor.style, BACKLOG_LINES);
- memory->platform_api.allocate_ring_buffer(&t->views[1].log, ALT_BACKLOG_SIZE);
- line_buf_alloc(&t->views[1].lines, &a, t->views[1].log.buf, t->cursor.style, ALT_BACKLOG_LINES);
+ t->views[0].log = memory->os.allocate_ring_buffer(BACKLOG_SIZE);
+ t->views[0].lines = line_buffer_alloc(&a, t->views[0].log.data, t->cursor.style, BACKLOG_LINES);
+ t->views[1].log = memory->os.allocate_ring_buffer(ALT_BACKLOG_SIZE);
+ t->views[1].lines = line_buffer_alloc(&a, t->views[1].log.data, t->cursor.style, ALT_BACKLOG_LINES);
t->views[0].fb.backing_store = memory_block_from_arena(&a, MB(2));
t->views[1].fb.backing_store = memory_block_from_arena(&a, MB(2));
@@ -1101,8 +1104,8 @@ DEBUG_EXPORT VTGL_INITIALIZE_FN(vtgl_initialize)
for (u32 i = 0; i < SHADER_COUNT; i++) {
Stream path = arena_stream(a);
stream_push_s8(&path, g_shader_path_prefix);
- if (path.widx && path.buf[path.widx - 1] != memory->platform_api.path_separator)
- stream_push_byte(&path, memory->platform_api.path_separator);
+ if (path.count && path.data[path.count - 1] != memory->os.path_separator)
+ stream_push_byte(&path, memory->os.path_separator);
queue_shader_reload_ctx *src = reload_ctxs + i;
src->info = shader_infos[i];
@@ -1110,8 +1113,8 @@ DEBUG_EXPORT VTGL_INITIALIZE_FN(vtgl_initialize)
src->t = t;
stream_push_s8(&path, fs_name[i]);
stream_push_byte(&path, 0);
- memory->platform_api.add_file_watch(path.buf, queue_shader_reload, src);
- a.beg = path.buf + path.widx;
+ memory->os.add_file_watch(path.data, queue_shader_reload, src);
+ a.beg = path.data + path.count;
}
t->error_stream = stream_alloc(&a, KB(256));
@@ -1198,7 +1201,7 @@ DEBUG_EXPORT VTGL_INITIALIZE_FN(vtgl_initialize)
glActiveTexture(GL_TEXTURE0);
- reload_all_shaders(&t->gl, &memory->platform_api, a);
+ reload_all_shaders(&t->gl, &memory->os, a);
return requested_size;
}
@@ -1228,11 +1231,10 @@ DEBUG_EXPORT VTGL_RENDER_FRAME_FN(vtgl_render_frame)
switch (entry->type) {
case WQ_RELOAD_SHADER: {
queue_shader_reload_ctx *ctx = entry->ctx;
- reload_shader(&t->gl, &memory->platform_api, ctx->path, ctx->stage,
- ctx->info, arena);
+ reload_shader(&t->gl, &memory->os, ctx->path, ctx->stage, ctx->info, arena);
} break;
case WQ_RELOAD_ALL_SHADERS: {
- reload_all_shaders(&t->gl, &memory->platform_api, arena);
+ reload_all_shaders(&t->gl, &memory->os, arena);
} break;
default: INVALID_CODE_PATH;
}
@@ -1259,7 +1261,7 @@ DEBUG_EXPORT VTGL_RENDER_FRAME_FN(vtgl_render_frame)
while (atomic_exchange_n(&t->resize_lock, 1) != 0);
if (t->gl.flags & RESIZE_RENDERER)
- resize(t, &memory->platform_api, input->window_size);
+ resize(t, &memory->os, input->window_size);
if (t->gl.queued_render) {
t->gl.queued_render = 0;
@@ -1331,7 +1333,7 @@ DEBUG_EXPORT VTGL_FRAME_STEP_FN(vtgl_frame_step)
/* NOTE(rnp): we skip the resize this time through so that we don't add
* input latency waiting for the render thread to release this lock */
if (atomic_exchange_n(&t->resize_lock, 1) == 0) {
- resize_terminal(t, &memory->platform_api, input->window_size);
+ resize_terminal(t, &memory->os, input->window_size);
t->resize_lock = 0;
} else {
input->pending_updates = 1;
@@ -1348,10 +1350,10 @@ DEBUG_EXPORT VTGL_FRAME_STEP_FN(vtgl_frame_step)
t->scroll_offset = 0;
t->gl.flags |= NEEDS_REFILL;
}
- memory->platform_api.write(t->child, input->character_input);
+ memory->os.write(t->child, input->character_input);
}
- handle_interactions(t, input, &memory->platform_api);
+ handle_interactions(t, input, &memory->os);
END_NAMED_BLOCK(mouse_and_keyboard_input);
if (t->gl.flags & NEEDS_REFILL) {
@@ -1361,17 +1363,17 @@ DEBUG_EXPORT VTGL_FRAME_STEP_FN(vtgl_frame_step)
BEGIN_NAMED_BLOCK(input_from_child);
if (input->data_available) {
- RingBuf *rb = &t->views[t->view_idx].log;
- s8 buffer = {.len = rb->cap - t->unprocessed_bytes, .data = rb->buf + rb->widx};
+ OSRingBuffer *rb = &t->views[t->view_idx].log;
+ s8 buffer = {.len = rb->capacity - t->unprocessed_bytes, .data = rb->data + rb->write_index};
- size bytes_read = memory->platform_api.read(t->child, buffer);
- ASSERT(bytes_read <= rb->cap);
+ iz bytes_read = memory->os.read(t->child, buffer);
+ ASSERT(bytes_read <= rb->capacity);
commit_to_rb(t->views + t->view_idx, bytes_read);
t->unprocessed_bytes += bytes_read;
s8 raw = {
.len = t->unprocessed_bytes,
- .data = rb->buf + (rb->widx - t->unprocessed_bytes)
+ .data = rb->data + (rb->write_index - t->unprocessed_bytes)
};
handle_input(t, t->arena_for_frame, raw);
t->gl.queued_render = 1;
diff --git a/vtgl.h b/vtgl.h
@@ -17,17 +17,16 @@
#define static_assert _Static_assert
#endif
-#define atomic_and(ptr, n) __atomic_and_fetch(ptr, n, __ATOMIC_RELEASE);
-#define atomic_fetch_add(ptr, n) __atomic_fetch_add(ptr, n, __ATOMIC_RELEASE);
-#define atomic_load(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE)
-#define atomic_exchange_n(ptr, val) __atomic_exchange_n(ptr, val, __ATOMIC_SEQ_CST)
+#define function static
+#define global static
+#define local_persist static
#define PI 3.1415926535897932384f
#define KB(a) ((a) << 10ULL)
#define MB(a) ((a) << 20ULL)
-#define ARRAY_COUNT(a) (size)(sizeof(a) / sizeof(*a))
+#define ARRAY_COUNT(a) (iz)(sizeof(a) / sizeof(*a))
#define ABS(a) ((a) < 0 ? (-a) : (a))
#define BETWEEN(x, a, b) ((x) >= (a) && (x) <= (b))
#define CLAMP(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x))
@@ -70,8 +69,8 @@ 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 ptrdiff_t iz;
+typedef size_t uz;
#include "intrinsics.c"
@@ -83,31 +82,21 @@ typedef size_t usize;
#define DEBUG_EXPORT static
#endif
-typedef struct { void *memory; size size; } MemoryBlock;
-
typedef struct { u8 *beg, *end; } Arena;
typedef struct { Arena *arena; u8 *old_beg; } TempArena;
-typedef struct { size len; u8 *data; } s8;
+typedef struct { iz len; u8 *data; } s8;
#define s8(s) (s8){.len = ARRAY_COUNT(s) - 1, .data = (u8 *)s}
typedef s8 os_mapped_file;
typedef struct {
- u8 *buf;
- u32 cap;
- u32 widx;
- b32 errors;
+ u8 *data;
+ iz capacity;
+ iz count;
+ b32 errors;
} Stream;
-/* NOTE: virtual memory ring buffer */
-typedef struct {
- size cap;
- size filled;
- size widx;
- u8 *buf;
-} RingBuf;
-
typedef union {
struct { i32 x, y; };
struct { i32 w, h; };
@@ -123,186 +112,133 @@ typedef union {
typedef struct { iv2 start, end; } Range;
#define INVALID_RANGE_END (iv2){.x = -1, .y = -1}
-#define INVALID_FILE (-1)
-enum file_attribute {
- FA_READ = 1 << 0,
- FA_WRITE = 1 << 1,
- FA_APPEND = 1 << 2,
-};
-
-/* NOTE: for now we will do the callback route but this will change if we do multithreading */
-#define PLATFORM_FILE_WATCH_CALLBACK_FN(name) void name(u8 *path, void *user_ctx)
-typedef PLATFORM_FILE_WATCH_CALLBACK_FN(platform_file_watch_callback_fn);
-
-#define PLATFORM_ADD_FILE_WATCH_FN(name) void name(u8 *path, platform_file_watch_callback_fn *fn, \
- void *user_ctx)
-typedef PLATFORM_ADD_FILE_WATCH_FN(platform_add_file_watch_fn);
-
-#define PLATFORM_ALLOCATE_RING_BUFFER_FN(name) void name(RingBuf *rb, size capacity)
-typedef PLATFORM_ALLOCATE_RING_BUFFER_FN(platform_allocate_ring_buffer_fn);
-
-#define PLATFORM_CLIPBOARD_FN(name) b32 name(Stream *buffer, u32 clipboard)
-typedef PLATFORM_CLIPBOARD_FN(platform_clipboard_fn);
-
-#define PLATFORM_READ_FILE_FN(name) s8 name(u8 *path, Arena *a)
-typedef PLATFORM_READ_FILE_FN(platform_read_file_fn);
-
-/* TODO: this should possibly just take a stream buffer */
-#define PLATFORM_READ_FN(name) size name(iptr file, s8 buffer)
-typedef PLATFORM_READ_FN(platform_read_fn);
-
-#define PLATFORM_SET_TERMINAL_SIZE_FN(name) void name(iptr child, i32 rows, i32 columns, \
- i32 window_width, i32 window_height)
-typedef PLATFORM_SET_TERMINAL_SIZE_FN(platform_set_terminal_size_fn);
-
-#define PLATFORM_WINDOW_TITLE_FN(name) void name(Stream *buffer)
-typedef PLATFORM_WINDOW_TITLE_FN(platform_window_title_fn);
-
-#define PLATFORM_WRITE_FN(name) b32 name(iptr file, s8 raw)
-typedef PLATFORM_WRITE_FN(platform_write_fn);
-
-typedef struct {
- platform_add_file_watch_fn *add_file_watch;
- platform_allocate_ring_buffer_fn *allocate_ring_buffer;
- platform_clipboard_fn *get_clipboard;
- platform_clipboard_fn *set_clipboard;
- platform_read_file_fn *read_file;
- platform_read_fn *read;
- platform_set_terminal_size_fn *set_terminal_size;
- platform_window_title_fn *get_window_title;
- platform_window_title_fn *set_window_title;
- platform_write_fn *write;
-
- u8 path_separator;
-} PlatformAPI;
-
-/* NOTE: CLIPBOARD_1 need not be supported on all platforms */
-enum {
- CLIPBOARD_0,
- CLIPBOARD_1,
-};
+#include "os.h"
/* TODO: for now these are just based on the GLFW keycodes directly. It might
* be better for the platform to define these values themselves */
-#define ACT_RELEASE 0
-#define ACT_PRESS 1
-#define ACT_REPEAT 2
-
-#define KEY_SPACE 32
-#define KEY_APOSTROPHE 39 /* ' */
-#define KEY_COMMA 44 /* , */
-#define KEY_MINUS 45 /* - */
-#define KEY_PERIOD 46 /* . */
-#define KEY_SLASH 47 /* / */
-#define KEY_0 48
-#define KEY_1 49
-#define KEY_2 50
-#define KEY_3 51
-#define KEY_4 52
-#define KEY_5 53
-#define KEY_6 54
-#define KEY_7 55
-#define KEY_8 56
-#define KEY_9 57
-#define KEY_SEMICOLON 59 /* ; */
-#define KEY_EQUAL 61 /* = */
-#define KEY_A 65
-#define KEY_B 66
-#define KEY_C 67
-#define KEY_D 68
-#define KEY_E 69
-#define KEY_F 70
-#define KEY_G 71
-#define KEY_H 72
-#define KEY_I 73
-#define KEY_J 74
-#define KEY_K 75
-#define KEY_L 76
-#define KEY_M 77
-#define KEY_N 78
-#define KEY_O 79
-#define KEY_P 80
-#define KEY_Q 81
-#define KEY_R 82
-#define KEY_S 83
-#define KEY_T 84
-#define KEY_U 85
-#define KEY_V 86
-#define KEY_W 87
-#define KEY_X 88
-#define KEY_Y 89
-#define KEY_Z 90
-#define KEY_LEFT_BRACKET 91 /* [ */
-#define KEY_BACKSLASH 92 /* \ */
-#define KEY_RIGHT_BRACKET 93 /* ] */
-#define KEY_GRAVE_ACCENT 96 /* ` */
-#define KEY_WORLD_1 161 /* non-US #1 */
-#define KEY_WORLD_2 162 /* non-US #2 */
-
-/* Function keys */
-#define KEY_ESCAPE 256
-#define KEY_ENTER 257
-#define KEY_TAB 258
-#define KEY_BACKSPACE 259
-#define KEY_INSERT 260
-#define KEY_DELETE 261
-#define KEY_RIGHT 262
-#define KEY_LEFT 263
-#define KEY_DOWN 264
-#define KEY_UP 265
-#define KEY_PAGE_UP 266
-#define KEY_PAGE_DOWN 267
-#define KEY_HOME 268
-#define KEY_END 269
-#define KEY_CAPS_LOCK 280
-#define KEY_SCROLL_LOCK 281
-#define KEY_NUM_LOCK 282
-#define KEY_PRINT_SCREEN 283
-#define KEY_PAUSE 284
-#define KEY_F1 290
-#define KEY_F2 291
-#define KEY_F3 292
-#define KEY_F4 293
-#define KEY_F5 294
-#define KEY_F6 295
-#define KEY_F7 296
-#define KEY_F8 297
-#define KEY_F9 298
-#define KEY_F10 299
-#define KEY_F11 300
-#define KEY_F12 301
-#define KEY_F13 302
-#define KEY_F14 303
-#define KEY_F15 304
-#define KEY_F16 305
-#define KEY_F17 306
-#define KEY_F18 307
-#define KEY_F19 308
-#define KEY_F20 309
-#define KEY_F21 310
-#define KEY_F22 311
-#define KEY_F23 312
-#define KEY_F24 313
-#define KEY_F25 314
-#define KEY_KP_0 320
-#define KEY_KP_1 321
-#define KEY_KP_2 322
-#define KEY_KP_3 323
-#define KEY_KP_4 324
-#define KEY_KP_5 325
-#define KEY_KP_6 326
-#define KEY_KP_7 327
-#define KEY_KP_8 328
-#define KEY_KP_9 329
-#define KEY_KP_DECIMAL 330
-#define KEY_KP_DIVIDE 331
-#define KEY_KP_MULTIPLY 332
-#define KEY_KP_SUBTRACT 333
-#define KEY_KP_ADD 334
-#define KEY_KP_ENTER 335
-#define KEY_KP_EQUAL 336
-
-enum input_keys {
+typedef enum {
+ BUTTON_RELEASE,
+ BUTTON_PRESS,
+ BUTTON_REPEAT,
+} ButtonAction;
+
+typedef enum {
+ KEY_SPACE = 32,
+ KEY_APOSTROPHE = 39, /* ' */
+ KEY_COMMA = 44, /* , */
+ KEY_MINUS = 45, /* - */
+ KEY_PERIOD = 46, /* . */
+ KEY_SLASH = 47, /* / */
+ KEY_0 = 48,
+ KEY_1 = 49,
+ KEY_2 = 50,
+ KEY_3 = 51,
+ KEY_4 = 52,
+ KEY_5 = 53,
+ KEY_6 = 54,
+ KEY_7 = 55,
+ KEY_8 = 56,
+ KEY_9 = 57,
+ KEY_SEMICOLON = 59, /* ; */
+ KEY_EQUAL = 61, /* = */
+ KEY_A = 65,
+ KEY_B = 66,
+ KEY_C = 67,
+ KEY_D = 68,
+ KEY_E = 69,
+ KEY_F = 70,
+ KEY_G = 71,
+ KEY_H = 72,
+ KEY_I = 73,
+ KEY_J = 74,
+ KEY_K = 75,
+ KEY_L = 76,
+ KEY_M = 77,
+ KEY_N = 78,
+ KEY_O = 79,
+ KEY_P = 80,
+ KEY_Q = 81,
+ KEY_R = 82,
+ KEY_S = 83,
+ KEY_T = 84,
+ KEY_U = 85,
+ KEY_V = 86,
+ KEY_W = 87,
+ KEY_X = 88,
+ KEY_Y = 89,
+ KEY_Z = 90,
+ KEY_LEFT_BRACKET = 91, /* [ */
+ KEY_BACKSLASH = 92, /* \ */
+ KEY_RIGHT_BRACKET = 93, /* ] */
+ KEY_GRAVE_ACCENT = 96, /* ` */
+ KEY_WORLD_1 = 161, /* non-US #1 */
+ KEY_WORLD_2 = 162, /* non-US #2 */
+
+ /* Function keys */
+ KEY_ESCAPE = 256,
+ KEY_ENTER = 257,
+ KEY_TAB = 258,
+ KEY_BACKSPACE = 259,
+ KEY_INSERT = 260,
+ KEY_DELETE = 261,
+ KEY_RIGHT = 262,
+ KEY_LEFT = 263,
+ KEY_DOWN = 264,
+ KEY_UP = 265,
+ KEY_PAGE_UP = 266,
+ KEY_PAGE_DOWN = 267,
+ KEY_HOME = 268,
+ KEY_END = 269,
+ KEY_CAPS_LOCK = 280,
+ KEY_SCROLL_LOCK = 281,
+ KEY_NUM_LOCK = 282,
+ KEY_PRINT_SCREEN = 283,
+ KEY_PAUSE = 284,
+ KEY_F1 = 290,
+ KEY_F2 = 291,
+ KEY_F3 = 292,
+ KEY_F4 = 293,
+ KEY_F5 = 294,
+ KEY_F6 = 295,
+ KEY_F7 = 296,
+ KEY_F8 = 297,
+ KEY_F9 = 298,
+ KEY_F10 = 299,
+ KEY_F11 = 300,
+ KEY_F12 = 301,
+ KEY_F13 = 302,
+ KEY_F14 = 303,
+ KEY_F15 = 304,
+ KEY_F16 = 305,
+ KEY_F17 = 306,
+ KEY_F18 = 307,
+ KEY_F19 = 308,
+ KEY_F20 = 309,
+ KEY_F21 = 310,
+ KEY_F22 = 311,
+ KEY_F23 = 312,
+ KEY_F24 = 313,
+ KEY_F25 = 314,
+ KEY_KP_0 = 320,
+ KEY_KP_1 = 321,
+ KEY_KP_2 = 322,
+ KEY_KP_3 = 323,
+ KEY_KP_4 = 324,
+ KEY_KP_5 = 325,
+ KEY_KP_6 = 326,
+ KEY_KP_7 = 327,
+ KEY_KP_8 = 328,
+ KEY_KP_9 = 329,
+ KEY_KP_DECIMAL = 330,
+ KEY_KP_DIVIDE = 331,
+ KEY_KP_MULTIPLY = 332,
+ KEY_KP_SUBTRACT = 333,
+ KEY_KP_ADD = 334,
+ KEY_KP_ENTER = 335,
+ KEY_KP_EQUAL = 336,
+} KeyboardKey;
+
+typedef enum {
MOUSE_LEFT,
MOUSE_RIGHT,
MOUSE_MIDDLE,
@@ -320,21 +256,21 @@ enum input_keys {
KEY_MENU,
INPUT_KEY_COUNT,
-};
+} InputKey;
-enum modifiers {
+typedef enum {
MOD_SHIFT = (1 << 0),
MOD_CONTROL = (1 << 1),
MOD_ALT = (1 << 2),
MOD_SUPER = (1 << 3),
MOD_MASK = (MOD_SHIFT|MOD_CONTROL|MOD_ALT|MOD_SUPER),
-};
+} InputModifier;
typedef struct {
/* TODO: is this even supported or does GLFW only call you once per poll? */
- u16 transitions;
- u16 ended_down;
+ u8 transitions;
+ u8 ended_down;
} ButtonState;
typedef struct TerminalInput {
@@ -348,7 +284,7 @@ typedef struct TerminalInput {
b32 window_focused;
b32 pending_updates;
- u32 modifiers;
+ InputModifier modifiers;
v2 mouse;
v2 last_mouse;
@@ -367,7 +303,7 @@ typedef struct TerminalMemory {
u64 debug_memory_size;
void *debug_memory;
- PlatformAPI platform_api;
+ OS os;
} TerminalMemory;
/************************************************************/
@@ -386,18 +322,20 @@ typedef VTGL_RENDER_FRAME_FN(vtgl_render_frame_fn);
typedef VTGL_ACTIVE_SELECTION_FN(vtgl_active_selection_fn);
#define VTGL_HANDLE_KEYS_FN(name) void name(TerminalMemory *memory, TerminalInput *input, \
- i32 key, i32 action, u32 modifiers)
+ KeyboardKey key, ButtonAction action, InputModifier modifiers)
typedef VTGL_HANDLE_KEYS_FN(vtgl_handle_keys_fn);
+#include "os.h"
+
#include "util.h"
#include "util.c"
#include "debug.h"
#ifdef __ARM_ARCH_ISA_A64
-#include "platform_linux_aarch64.c"
+#include "os_linux_aarch64.c"
#elif defined(__linux__) && (defined(__x86_64__) || defined(_M_X64))
-#include "platform_linux_amd64.c"
+#include "os_linux_amd64.c"
#else
#error Unsupported Platform!
#endif