Compare commits

...

10 Commits

Author SHA1 Message Date
Krzosa Karol
3d91a1f924 couple commits 2026-03-20 00:09:50 +01:00
Krzosa Karol
bb3859b537 transcript browser wasm working 2025-08-03 12:03:28 +02:00
Krzosa Karol
ff17114860 small core changes 2025-08-01 08:26:55 +02:00
Krzosa Karol
c1c58d33b9 transcript browser working 2025-07-28 14:12:39 +02:00
Krzosa Karol
869af3f6dd srt, work_queue, mutex 2025-07-27 16:35:15 +02:00
Krzosa Karol
6724814440 begin prototype, transcript browser 2025-07-27 10:18:54 +02:00
Krzosa Karol
c8a4d19a72 buffer16 api work 2025-04-10 22:07:14 +02:00
Krzosa Karol
13295a2fcd text editor 2025-04-10 11:01:32 +02:00
Krzosa Karol
00777a24d5 text editor 2025-04-09 23:48:44 +02:00
Krzosa Karol
c82eccc02a porting text editor 2025-04-07 08:37:06 +02:00
55 changed files with 2739 additions and 651 deletions

93
README.md Normal file
View File

@@ -0,0 +1,93 @@
# wasm_transcript_browser
This project generates a searchable transcript browser from local `.srt` files and ships it as a WebAssembly web app.
Type a word or phrase, click a match, and it opens the exact timestamp in the matching YouTube video. You can also copy a direct timestamped link.
## What it does
- Parses subtitle files (`.srt`) from a local folder.
- Generates a packed transcript index (`build/entries.inc`) at build time.
- Compiles a C codebase to `main.wasm` and serves it with a small HTML/JS shell.
- Provides instant text search over all transcript text.
- Creates timestamped YouTube links from search hits.
## How the pipeline works
1. `build_file.c` defines a hard-coded source folder:
- `folder_to_create_transcript_for`
2. During build, `src/prototype/prototype.meta.c`:
- scans `.srt` files in that folder,
- parses subtitle entries,
- normalizes text (lowercase, removes punctuation, turns `-` into spaces),
- writes generated data to `build/entries.inc`.
3. `src/prototype/main.c` includes that generated file and compiles to WASM.
4. The browser app (`package/index.html` + `package/main.wasm`) renders a custom UI and handles link open/copy actions.
## Transcript filename format
To build correct YouTube links, filenames are expected to include the 11-char YouTube ID wrapped in one character on each side at the end of the name (commonly brackets).
Example:
- `My Video Title [dQw4w9WgXcQ].en.srt`
- `My Video Title [dQw4w9WgXcQ].srt`
The app extracts the video ID from the ending token and creates links like:
- `https://youtu.be/<id>?feature=shared&t=<seconds>`
## Build
### Prerequisites
- `clang` (or `gcc`/MSVC depending on your platform)
- Python 3 (for local static server)
### Linux
```bash
./build.sh
```
### Windows
```bat
build.bat
```
Build output of interest:
- `build/entries.inc` (generated transcript index)
- `package/index.html`
- `package/main.wasm`
## Run locally
From `package/`:
```bash
python3 -m http.server 8080
```
Then open:
- `http://localhost:8080`
Windows helper script:
- `package/run_server.bat`
## Configuration notes
- Update transcript source folder in `build_file.c` before building.
- The current build file also contains hard-coded deploy commands (`ssh`/`scp`) in `build_prototype_wasm_target`; remove or update those for your own environment.
## Project status
This is an older personal project with a custom C build/codegen stack. The rough edges are expected, but the core idea works: local transcript ingestion + fast search + one-click timestamped YouTube links.

View File

@@ -6,7 +6,10 @@
// don't ever include stuff that build_file will generate code for!
#define DONT_INCLUDE_GENERATED_MATH
#include "src/core/core.h"
#include "src/os/os.h"
#include "src/core/core.c"
#include "src/os/os.c"
#define BUILD_TOOL_LIB
#define S8_String s8_t
@@ -16,20 +19,29 @@
#include "src/meta/meta_table_format.c"
#include "src/meta/meta_cfiles.c"
s8_t folder_to_create_transcript_for = s8("D:/videos/jblow");
#include "src/app/app.meta.c"
#include "src/ui/ui.meta.c"
#include "src/render/render.meta.c"
#include "src/wasm_app/wasm_app.meta.c"
#include "src/text_editor/text_editor.meta.c"
#include "src/prototype/prototype.meta.c"
#include "src/testing/testing.meta.c"
b32 run_win32_app_base_target = false;
b32 run_testing_target = false;
b32 run_prototype_dll_target = false;
b32 run_prototype_standalone_target = false;
b32 run_prototype_wasm_target = true;
b32 run_server = false;
void build_testing_target(void) {
if (!cache_code_modified(s8("../src/testing/testing_main.c"), s8("testing.exe"))) {
return;
}
if (PLATFORM_WINDOWS) {
os_delete_file(s8("testing.pdb"));
os_delete(s8("testing.pdb"));
int ok = os_systemf(
"cl ../src/testing/testing_main.c -Fe:testing.exe -Fd:testing.pdb"
" -I ../src"
@@ -69,26 +81,12 @@ void build_win32_app_base_target(void) {
if (ok != 0) exit(ok);
}
void build_text_editor_dll_target(void) {
if (!cache_code_modified(s8("../src/text_editor/text_editor_main.c"), s8("app.dll"))) {
return;
}
int ok = os_systemf(
"cl ../src/text_editor/text_editor_main.c -Fe:app.dll -Fd:app.pdb"
" -I ../src /DAPP_IS_DLL"
" /Zi /FC /nologo /Oi"
" /WX /W3 /wd4200 /diagnostics:column"
" /link /incremental:no /DEBUG:FULL /DLL"
);
if (ok != 0) exit(ok);
}
void build_prototype_dll_target(void) {
if (!cache_code_modified(s8("../src/wasm_app/main.c"), s8("app.dll"))) {
if (!cache_code_modified(s8("../src/prototype/main.c"), s8("app.dll"))) {
return;
}
int ok = os_systemf(
"cl ../src/wasm_app/main.c -Fe:app.dll -Fd:app.pdb"
"cl ../src/prototype/main.c -Fe:app.dll -Fd:app.pdb"
" -I ../src /DAPP_IS_DLL"
" /Zi /FC /nologo /Oi"
" /WX /W3 /wd4200 /diagnostics:column"
@@ -99,31 +97,38 @@ void build_prototype_dll_target(void) {
void build_prototype_wasm_target(void) {
b32 html_code_modified = cache_code_modified(s8("../src/app/app_wasm.html"), s8("../package/index.html"));
b32 wasm_code_modified = cache_code_modified(s8("../src/wasm_app/main.c"), s8("main.wasm"));
b32 wasm_code_modified = cache_code_modified(s8("../src/prototype/main.c"), s8("main.wasm"));
if (html_code_modified) {
os_copy("../src/app/app_wasm.html", "../package/index.html", os_copy_overwrite);
os_copy(s8("../src/app/app_wasm.html"), s8("../package/index.html"), os_copy_overwrite);
}
if (wasm_code_modified) {
int ok = os_systemf(
"clang ../src/wasm_app/main.c -o main.wasm"
"clang ../src/prototype/main.c -o main.wasm"
" -Oz -g -I../src"
" -Wall -Wno-missing-braces -Wno-single-bit-bitfield-constant-conversion -Wno-unsequenced -Wno-initializer-overrides"
" -fdiagnostics-absolute-paths -fdiagnostics-format=msvc"
" --target=wasm32 -nostdlib -mbulk-memory -msimd128"
" -Wl,-export-dynamic,--allow-undefined,--import-memory,--no-entry,--initial-memory=131072000,--max-memory=4294967296"
);
os_copy("main.wasm", "../package/main.wasm", os_copy_overwrite);
os_copy(s8("main.wasm"), s8("../package/main.wasm"), os_copy_overwrite);
if (ok != 0) exit(ok);
// os_systemf("ssh root@65.21.244.51 rm /website/index.html /website/main.wasm");
// os_systemf("scp ../package/index.html root@65.21.244.51:/website/index.html");
// os_systemf("scp ../package/main.wasm root@65.21.244.51:/website/main.wasm");
os_systemf("ssh root@157.90.144.237 rm /var/www/html/jiang/index.html /var/www/html/jiang/main.wasm");
os_systemf("scp ../package/index.html root@157.90.144.237:/var/www/html/jiang/index.html");
os_systemf("scp ../package/main.wasm root@157.90.144.237:/var/www/html/jiang/main.wasm");
}
}
void build_prototype_standalone_target(void) {
if (!cache_code_modified(s8("../src/wasm_app/main.c"), s8("standalone_app.exe"))) {
if (!cache_code_modified(s8("../src/prototype/main.c"), s8("standalone_app.exe"))) {
return;
}
int ok = os_systemf(
"cl ../src/wasm_app/main.c -Fe:standalone_app.exe -Fd:standalone_app.pdb"
"cl ../src/prototype/main.c -Fe:standalone_app.exe -Fd:standalone_app.pdb"
" -I ../src"
" /Zi /FC /nologo /Oi"
" /WX /W3 /wd4200 /diagnostics:column"
@@ -143,34 +148,25 @@ void generate_math_code(ma_arena_t *arena) {
#define py "python3 "
#endif
os_set_working_dir("../src/core");
os_set_cwd(s8("../src/core"));
os_systemf(py "core_math_gen.py");
os_set_working_dir("../../build");
os_set_cwd(s8("../../build"));
}
int main(int argc, char **argv) {
core_init();
os_core_init();
int ok = 0;
SRC_SearchPaths.include_path = (char*[]){OS_GetAbsolutePath(&Perm, s8("../src")).str};
SRC_SearchPaths.include_path_count = 1;
cache_init(&Perm, s8("cache_build_file"));
generate_math_code(tcx->temp);
generate_ui_code(tcx->temp);
generate_app_code(tcx->temp);
generate_wasm_app_code(tcx->temp);
generate_render_code(tcx->temp);
generate_testing_code(tcx->temp);
generate_text_editor_code(tcx->temp);
b32 run_win32_app_base_target = true;
b32 run_testing_target = true;
b32 run_text_editor_dll_target = true;
b32 run_prototype_dll_target = false;
b32 run_prototype_wasm_target = false;
b32 run_prototype_standalone_target = false;
b32 run_server = false;
generate_math_code(&tcx->temp);
generate_ui_code(&tcx->temp);
generate_app_code(&tcx->temp);
generate_prototype_code(&tcx->temp);
generate_render_code(&tcx->temp);
generate_testing_code(&tcx->temp);
if (run_server) {
os_systemf("start /D ..\\package ..\\package\\run_server.bat");
@@ -192,10 +188,6 @@ int main(int argc, char **argv) {
build_win32_app_base_target();
}
if (run_text_editor_dll_target) {
build_text_editor_dll_target();
}
if (run_prototype_wasm_target) {
build_prototype_wasm_target();
}

View File

@@ -5,7 +5,7 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<title>Hello traveller!</title>
</head>
<body>
@@ -60,6 +60,20 @@ class stream_t {
}
}
function unsecuredCopyToClipboard(text) {
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Unable to copy to clipboard', err);
}
document.body.removeChild(textArea);
}
class memory_t {
exports = null; // this is set after wasm module created
constructor(wasm_memory) {
@@ -108,6 +122,9 @@ const wasm_app_imports = {
wasm_trap: () => { throw new Error(); },
wasm_write_to_console: (str, len) => { console.log(mem.read_cstr(str, len)); },
wasm_open_link: (str, len) => { window.open(mem.read_cstr(str, len)); },
wasm_set_clipboard: (str, len) => { unsecuredCopyToClipboard(mem.read_cstr(str, len)); },
// gfx
wasm_draw_text: (str, len, x, y, font_str, font_len, font_size, r, g, b, a) => {
ctx2d.font = `${font_size}px ${mem.read_cstr(font_str, font_len)}`;
@@ -137,6 +154,15 @@ const wasm_app_imports = {
ctx2d.rect(x, y, w, h);
ctx2d.clip();
},
wasm_set_cursor: (cursor_num) => {
let cursor = 'auto';
if (cursor_num == 0) {
cursor = 'text';
} else if (cursor_num == 1) {
cursor = 'pointer';
}
document.getElementById("canvas").style.cursor = cursor;
},
};
(async function main() {
@@ -198,14 +224,20 @@ const wasm_app_imports = {
}
});
addEventListener("contextmenu", (event) => { event.preventDefault(); return false; })
addEventListener("resize", (event) => { wake_up(); });
addEventListener("keydown", (event) => {
if (["F1", "F2", "F3", "p"].includes(event.key)) event.preventDefault();
wasm_exports["wasm_key_down"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
let memory = wasm_exports["wasm_temp_alloc"](64);
mem.write_string_into_cmemory(memory, 64, event.key);
wasm_exports["wasm_key_down"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wake_up();
});
addEventListener("keyup", (event) => {
wasm_exports["wasm_key_up"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
let memory = wasm_exports["wasm_temp_alloc"](64);
mem.write_string_into_cmemory(memory, 64, event.key);
wasm_exports["wasm_key_up"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wake_up();
});
addEventListener("mousemove", (event) => {
@@ -213,7 +245,7 @@ const wasm_app_imports = {
wake_up();
});
addEventListener("mousedown", (event) => {
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey, event.detail);
wake_up();
});
addEventListener("mouseup", (event) => {

View File

@@ -266,13 +266,14 @@ type_t type__app_event_t = { type_kind_struct, s8_const("app_event_t"), sizeof(a
{.name = s8_const("key"), .type = &type__app_key_t, .offset = offsetof(app_event_t, key), .dont_serialize = 0},
{.name = s8_const("text"), .type = &type__s8_t, .offset = offsetof(app_event_t, text), .dont_serialize = 0},
{.name = s8_const("mouse_wheel_delta"), .type = &type__v3f32_t, .offset = offsetof(app_event_t, mouse_wheel_delta), .dont_serialize = 0},
{.name = s8_const("clicks"), .type = &type__i8, .offset = offsetof(app_event_t, clicks), .dont_serialize = 0},
{.name = s8_const("ctrl"), .type = &type__b8, .offset = offsetof(app_event_t, ctrl), .dont_serialize = 0},
{.name = s8_const("shift"), .type = &type__b8, .offset = offsetof(app_event_t, shift), .dont_serialize = 0},
{.name = s8_const("alt"), .type = &type__b8, .offset = offsetof(app_event_t, alt), .dont_serialize = 0},
{.name = s8_const("mouse_pos"), .type = &type__v2f32_t, .offset = offsetof(app_event_t, mouse_pos), .dont_serialize = 0},
{.name = s8_const("mouse_delta"), .type = &type__v2f32_t, .offset = offsetof(app_event_t, mouse_delta), .dont_serialize = 0},
},
.count = 12,
.count = 13,
};
type_t type__app_frame_t = { type_kind_struct, s8_const("app_frame_t"), sizeof(app_frame_t),
.members = (type_member_t[]){

View File

@@ -104,6 +104,7 @@ struct app_event_t {
app_key_t key;
s8_t text;
v3f32_t mouse_wheel_delta;
i8 clicks;
b8 ctrl;
b8 shift;
b8 alt;

View File

@@ -170,6 +170,7 @@ void generate_app_code(ma_arena_t *arena) {
v3f32_t mouse_wheel_delta; // @mouse_wheel
// always present data
i8 clicks;
b8 ctrl;
b8 shift;
b8 alt;

View File

@@ -1,8 +1,3 @@
gb_wasm_export char wasm_temp_buff1[128] = {[127] = 0x13};
gb_wasm_export i32 wasm_temp_buff1_len = 127;
gb_wasm_export char wasm_temp_buff2[128] = {[127] = 0x13};
gb_wasm_export i32 wasm_temp_buff2_len = 127;
gb f32 wasm_dpr;
gb f32 wasm_delta_time;
gb f32 wasm_time;
@@ -26,7 +21,7 @@ fn void wasm_add_event_ex(app_frame_t *frame, app_event_t *ev) {
}
fn void wasm_add_event(app_event_t event) {
app_event_t *ev = ma_push_type(tcx->temp, app_event_t);
app_event_t *ev = ma_push_type(&tcx->temp, app_event_t);
*ev = event;
ev->id = ++wasm_event_ids;
ev->alt = wasm_cached.alt;
@@ -54,7 +49,7 @@ fn_wasm_export void wasm_mouse_move(f32 x, f32 y, b32 ctrl, b32 shift, b32 alt,
wasm_set_cached_buttons(ctrl, shift, alt);
}
fn_wasm_export void wasm_mouse_down(f32 x, f32 y, i32 button, b32 ctrl, b32 shift, b32 alt, b32 meta) {
fn_wasm_export void wasm_mouse_down(f32 x, f32 y, i32 button, b32 ctrl, b32 shift, b32 alt, b32 meta, i32 clicks) {
button += 1;
assert(button >= app_mouse_button_left && button <= app_mouse_button_right);
wasm_set_cached_buttons(ctrl, shift, alt);
@@ -62,6 +57,7 @@ fn_wasm_export void wasm_mouse_down(f32 x, f32 y, i32 button, b32 ctrl, b32 shif
wasm_add_event((app_event_t){
.kind = app_event_kind_mouse_down,
.mouse_button = button,
.clicks = clicks,
});
}
@@ -87,8 +83,6 @@ fn_wasm_export void wasm_mouse_wheel(f32 x, f32 y, f32 delta_x, f32 delta_y, f32
fn_wasm_export void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 meta) {
wasm_set_cached_buttons(ctrl, shift, alt);
assert(wasm_temp_buff1[127] == 0x13); // make sure we didn't overwrite memory in JS
assert(wasm_temp_buff2[127] == 0x13);
s8_t key8 = s8_from_char(key);
wasm_key_map_t map = wasm_map_key_string_to_app_key(key8);
if (map.key != app_key_null) {
@@ -102,7 +96,7 @@ fn_wasm_export void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 m
return;
}
s8_t text = s8_copy(tcx->temp, key8);
s8_t text = s8_copy(&tcx->temp, key8);
wasm_add_event((app_event_t){
.kind = app_event_kind_text,
.text = text,
@@ -111,8 +105,6 @@ fn_wasm_export void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 m
fn_wasm_export void wasm_key_up(char *key, b32 ctrl, b32 shift, b32 alt, b32 meta) {
wasm_set_cached_buttons(ctrl, shift, alt);
assert(wasm_temp_buff1[127] == 0x13); // make sure we didn't overwrite memory in JS
assert(wasm_temp_buff2[127] == 0x13);
s8_t key8 = s8_from_char(key);
wasm_key_map_t map = wasm_map_key_string_to_app_key(key8);
@@ -146,7 +138,7 @@ fn_wasm_export b32 wasm_update(f64 time, f32 width, f32 height, f32 dpr) {
}
b32 animating = app_update(tcx, &wasm_frame);
ma_set0(tcx->temp);
ma_set0(&tcx->temp);
zero_struct(&wasm_frame);
wasm_last_time = wasm_time;
wasm_frame_counter += 1;
@@ -155,5 +147,18 @@ fn_wasm_export b32 wasm_update(f64 time, f32 width, f32 height, f32 dpr) {
}
fn_wasm_export void wasm_init(void) {
core_init();
os_core_init();
}
fn_wasm_export isize wasm_temp_alloc(isize size) {
return (isize)ma_push_size(&tcx->temp, size);
}
fn_wasm_import void wasm_open_link(isize str, i32 len);
fn_wasm_import void wasm_set_clipboard(isize str, i32 len);
fn_wasm_import void wasm_set_cursor(i32 cursor_num);
// @todo: os?
fn void open_link(s8_t url) { wasm_open_link((isize)url.str, (i32)url.len); }
fn void set_clipboard(s8_t url) { wasm_set_clipboard((isize)url.str, (i32)url.len); }
fn void set_cursor(i32 cursor_num) { wasm_set_cursor(cursor_num); }

View File

@@ -5,7 +5,7 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<title>Hello traveller!</title>
</head>
<body>
@@ -60,6 +60,20 @@ class stream_t {
}
}
function unsecuredCopyToClipboard(text) {
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Unable to copy to clipboard', err);
}
document.body.removeChild(textArea);
}
class memory_t {
exports = null; // this is set after wasm module created
constructor(wasm_memory) {
@@ -108,6 +122,9 @@ const wasm_app_imports = {
wasm_trap: () => { throw new Error(); },
wasm_write_to_console: (str, len) => { console.log(mem.read_cstr(str, len)); },
wasm_open_link: (str, len) => { window.open(mem.read_cstr(str, len)); },
wasm_set_clipboard: (str, len) => { unsecuredCopyToClipboard(mem.read_cstr(str, len)); },
// gfx
wasm_draw_text: (str, len, x, y, font_str, font_len, font_size, r, g, b, a) => {
ctx2d.font = `${font_size}px ${mem.read_cstr(font_str, font_len)}`;
@@ -137,6 +154,15 @@ const wasm_app_imports = {
ctx2d.rect(x, y, w, h);
ctx2d.clip();
},
wasm_set_cursor: (cursor_num) => {
let cursor = 'auto';
if (cursor_num == 0) {
cursor = 'text';
} else if (cursor_num == 1) {
cursor = 'pointer';
}
document.getElementById("canvas").style.cursor = cursor;
},
};
(async function main() {
@@ -198,14 +224,20 @@ const wasm_app_imports = {
}
});
addEventListener("contextmenu", (event) => { event.preventDefault(); return false; })
addEventListener("resize", (event) => { wake_up(); });
addEventListener("keydown", (event) => {
if (["F1", "F2", "F3", "p"].includes(event.key)) event.preventDefault();
wasm_exports["wasm_key_down"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
let memory = wasm_exports["wasm_temp_alloc"](8);
mem.write_string_into_cmemory(memory, 8, event.key);
wasm_exports["wasm_key_down"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wake_up();
});
addEventListener("keyup", (event) => {
wasm_exports["wasm_key_up"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
let memory = wasm_exports["wasm_temp_alloc"](8);
mem.write_string_into_cmemory(memory, 8, event.key);
wasm_exports["wasm_key_up"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wake_up();
});
addEventListener("mousemove", (event) => {
@@ -213,7 +245,7 @@ const wasm_app_imports = {
wake_up();
});
addEventListener("mousedown", (event) => {
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey, event.detail);
wake_up();
});
addEventListener("mouseup", (event) => {

View File

@@ -307,12 +307,12 @@ fn void w32_try_reloading_library(w32_library_t *lib, app_frame_t frame) {
u64 random_value = get_random_u64(&lib->seed);
s8_t base_name = s8_chop_last_period(lib->dll_name);
s8_t new_dll_name = s8_printf(tcx->temp, "%S_temp_%u.dll", base_name, random_value);
s8_t new_dll_name = s8_printf(&tcx->temp, "%S_temp_%u.dll", base_name, random_value);
b32 ok = os_copy(lib->dll_name, new_dll_name, true);
if (!ok) debugf("failed to copy from %S to %S", lib->dll_name, new_dll_name);
s8_t pdb_name = s8_printf(tcx->temp, "%S.pdb", base_name);
s8_t new_pdb_name = s8_printf(tcx->temp, "%S_temp_%u.pdb", base_name, random_value);
s8_t pdb_name = s8_printf(&tcx->temp, "%S.pdb", base_name);
s8_t new_pdb_name = s8_printf(&tcx->temp, "%S_temp_%u.pdb", base_name, random_value);
ok = os_copy(pdb_name, new_pdb_name, true);
if (!ok) debugf("failed to copy from %S to %S", pdb_name, new_pdb_name);
@@ -343,7 +343,7 @@ end_of_lib_load:;
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
core_init();
os_core_init();
// Delete temporary dll files
{
@@ -463,7 +463,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int n
w32_library_t lib = {0};
lib.dll_name = s8("app.dll");
lib.update_fn_name = s8("app_update");
tcx->data[tcx_slot_app] = w32_load_opengl_fn;
tcx->app_ctx = w32_load_opengl_fn;
// Set VSync
if (wglSwapIntervalEXT) {
@@ -487,7 +487,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int n
for (;;) {
app_frame_t frame = {0};
b32 waited = w32_get_events(tcx->temp, &frame, wait_for_events);
b32 waited = w32_get_events(&tcx->temp, &frame, wait_for_events);
if (waited) {
frame.delta = 0.00001;
}
@@ -526,7 +526,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int n
SwapBuffers(w32_dc);
ma_set0(tcx->temp);
ma_set0(&tcx->temp);
///////////////////////////////
// end of frame timings

View File

@@ -15,3 +15,4 @@
#include "core_platform.h"
#include "core_hash_table.h"
#include "core_ctx.h"
#include "core_array.h"

View File

@@ -170,3 +170,16 @@ fn ma_temp_t ma_begin_scratch1(ma_arena_t *conflict) {
ma_arena_t *conflicts[] = {conflict};
return ma_begin_scratch_ex(conflicts, 1);
}
fn ma_temp_t ma_begin_scratch2(ma_arena_t *a, ma_arena_t *b) {
ma_arena_t *conflicts[] = {a, b};
return ma_begin_scratch_ex(conflicts, 2);
}
fn void *ma_push__copy(ma_arena_t *arena, void *data, u64 size) {
void *result = ma_push_size_ex(arena, size);
if (result) {
memory_copy(result, data, size);
}
return result;
}

View File

@@ -32,6 +32,8 @@ fn ma_arena_t *ma_push_arena(ma_arena_t *allocator, usize size);
fn void ma_push_arena_ex(ma_arena_t *allocator, ma_arena_t *result, usize size);
#define ma_push_type(arena, Type) (Type *)ma_push_size((arena), sizeof(Type))
#define ma_push_array(arena, Type, count) (Type *)ma_push_size((arena), sizeof(Type) * (count))
#define ma_push_type_copy(ma, data) ma_push__copy((ma), (data), sizeof(*(data)))
#define ma_push_array_copy(ma, data, count) ma_push__copy((ma), (data), (count) * sizeof(*(data)))
//
// pop

View File

@@ -1,98 +1,116 @@
typedef struct array_header_t array_header_t;
struct array_header_t {
ma_arena_t *arena;
i64 len;
i64 cap;
};
#define array_header(arr) ((array_header_t *)(arr) - 1)
#define array_len(arr) (array_header(arr)->len)
#define array_cap(arr) (array_header(arr)->cap)
#define array_add(arr, x) (array_grow(&(arr), sizeof(*(arr)), 1), (arr)[array_len(arr)++] = (x))
#define array_addn(arr, count) (array_grow(&(arr), sizeof(*(arr)), (count)), array_len(arr) += (count), &(arr)[array_len(arr) - (count)])
#define array_set_len(arr, x) (array_len(arr) = (x))
#define array_set_cap(arr, x) (array__set_cap(&(arr), sizeof(*(arr)), (x)))
#define array_pop(arr) ((arr)[--array_len(arr)])
#define array_swapdel(arr, i) (assert_expr((i) < array_len(arr)), (arr)[(i)] = (arr)[--array_len(arr)])
#define array_create(arena, type, count) (type *)(array__create((arena), sizeof(type), (count)) + 1)
fn array_header_t *array__create(ma_arena_t *arena, i64 item_size, i64 item_count) {
array_header_t *hdr = (array_header_t *)ma_push_size(arena, item_size * item_count + sizeof(array_header_t));
hdr->arena = arena;
hdr->cap = item_count;
return hdr;
fn void array__init(alo_t alo, array_void_t *this, size_t item_size, i64 count) {
assert(this->data == NULL);
this->data = alloc_size(alo, item_size * count);
this->cap = count;
this->len = 0;
this->alo = alo;
}
fn void array__set_cap(void **arr, i64 item_size, i64 item_count) {
array_header_t *hdr = array_header(*arr);
array_header_t *new_hdr = array__create(hdr->arena, item_size, item_count);
new_hdr->len = CLAMP(hdr->len, 0, item_count);
memory_copy(new_hdr + 1, hdr + 1, new_hdr->len * item_size);
*arr = new_hdr + 1;
fn void array__copy(alo_t alo, array_void_t *dst, array_void_t *src, size_t item_size) {
dst->data = alloc_size(alo, item_size * src->cap);
memory_copy(dst->data, src->data, item_size * src->len);
dst->cap = src->cap;
dst->len = src->len;
dst->alo = alo;
}
fn void array_grow(void **arr, i64 item_size, i64 increment) {
assert(increment >= 1);
array_header_t *hdr = array_header(*arr);
if (hdr->len + increment > hdr->cap) {
array_header_t *new_hdr = array__create(hdr->arena, item_size, (hdr->cap + increment - 1) * 2);
memory_copy(new_hdr + 1, hdr + 1, hdr->len * item_size);
new_hdr->len = hdr->len;
*arr = new_hdr + 1;
// dealloc(hdr);
fn void array__grow(array_void_t *this, size_t item_size, i64 item_count) {
if (this->len + 1 > this->cap) {
i64 new_cap = this->cap == 0 ? 16 : (this->cap + item_count - 1) * 2;
void *new_data = alloc_size(this->alo, new_cap * item_size);
if (this->data) {
memory_copy(new_data, this->data, this->len*item_size);
dealloc(this->alo, this->data);
}
this->data = new_data;
this->cap = new_cap;
}
}
fn void array__del(array_void_t *this, size_t item_size, i64 idx, i64 count) {
if (count == 0) return;
assert(idx >= 0 && idx < this->len);
assert((idx + count) > 0 && (idx + count) <= this->len);
i64 right_len = this->len - idx - count;
u8 *data = (u8 *)this->data;
memory_move(data + idx * item_size, data + (idx + count) * item_size, right_len * item_size);
this->len -= count;
}
fn void array__insert(array_void_t *this, size_t item_size, i64 idx) {
array__grow(this, item_size, 1);
assert(idx >= 0);
assert(idx <= this->len);
assert(this->alo.object);
u8 *data = (u8 *)this->data;
i64 right_len = this->len - idx;
memory_move(data + (idx + 1) * item_size, data + idx * item_size, item_size * right_len);
this->len += 1;
}
fn_test void test_array(void) {
ma_temp_t scratch = ma_begin_scratch();
int *arr = array_create(scratch.arena, int, 2);
for (int i = 0; i < 512; i += 1) {
array_add(arr, i);
}
for (int i = 0; i < 512; i += 1) {
assert(arr[i] == i);
}
assert(array_len(arr) == 512);
assert(array_cap(arr) == 512);
for (int i = 511; i >= 0; i -= 1) {
int a = array_pop(arr);
assert(a == i);
}
assert(array_len(arr) == 0);
{
int *a = array_addn(arr, 4);
assert(arr == a);
assert(array_header(arr)->len == 4);
int *b = array_addn(arr, 12);
assert(arr + 4 == b);
assert(array_header(arr)->len == 16);
array_set_len(arr, 0);
}
{
array_i64_t arr = (array_i64_t){.alo = malot(scratch)};
for (int i = 0; i < 512; i += 1) {
array_add(arr, i);
array_add(&arr, i);
}
for (int i = 0; i < 512; i += 1) {
assert(arr.data[i] == i);
}
assert(arr.len == 512);
assert(arr.cap == 512);
for (int i = 511; i >= 0; i -= 1) {
i64 a = array_pop(&arr);
assert(a == i);
}
assert(arr.len == 0);
array_dealloc(&arr);
}
{
array_i64_t arr = (array_i64_t){.alo = malot(scratch)};
i64 *i = array_addn(&arr, 10);
assert(arr.len == 10 && arr.cap == 16);
i64 *j = array_addn(&arr, 2);
assert(i == arr.data);
assert(j == arr.data + 10);
array_dealloc(&arr);
}
{
array_i64_t arr = (array_i64_t){.alo = malot(scratch)};
for (int i = 0; i < 512; i += 1) {
array_add(&arr, i);
}
array_swapdel(arr, 3);
assert(arr[3] == 511);
assert(array_len(arr) == 511);
array_insert(&arr, 256, 111);
assert(arr.data[256] == 111);
assert(arr.data[255] == 255);
assert(arr.data[257] == 256);
assert(arr.len == 513);
array_set_len(arr, 0);
}
array_insert(&arr, 4, 222);
assert(arr.len == 514);
assert(arr.data[4] == 222);
assert(arr.data[3] == 3);
assert(arr.data[5] == 4);
{
for (int i = 0; i < 512; i += 1) {
array_add(arr, i);
array_swapdel(&arr, 2);
assert(arr.len == 513);
assert(arr.data[2] == 511);
assert(arr.data[1] == 1);
assert(arr.data[3] == 3);
array_i64_t copy = {0};
array_copy(malot(scratch), &copy, &arr);
for (i64 i = 0; i < arr.len; i += 1) {
assert(arr.data[i] == copy.data[i]);
}
array_set_cap(arr, 256);
assert(array_len(arr) == 256);
assert(array_cap(arr) == 256);
}
assert(copy.len == arr.len);
}
ma_end_scratch(scratch);
}

24
src/core/core_array.h Normal file
View File

@@ -0,0 +1,24 @@
#define array(type) struct { alo_t alo; type *data; i64 len; i64 cap; }
typedef array(void) array_void_t;
typedef array(i64) array_i64_t;
#define arrisz(x) sizeof((x)->data[0])
#define arrcst(x) ((array_void_t *)(x))
#define array_init(a, this, count) (array__init((a), arrcst(this), arrisz(this), (count)))
#define array_add(this, ...) (array__grow(arrcst(this), arrisz(this), 1), (this)->data[(this)->len++] = __VA_ARGS__)
#define array_pop(this) (assert_expr((this)->len > 0), (this)->data[--(this)->len])
#define array_dealloc(this) (dealloc((this)->alo, (this)->data))
#define array_addn(this, n) (array__grow(arrcst(this), arrisz(this), (n)), (this)->len += (n), &(this)->data[(this)->len - (n)])
#define array_insert(this, i, item) (array__insert(arrcst(this), arrisz(this), (i)), (this)->data[(i)] = (item))
#define array_swapdel(this, i) (assert_expr((i) < (this)->len), (this)->data[(i)] = (this)->data[--(this)->len])
#define array_del(this, i) (array__del(arrcst(this), arrisz(this), (i), 1))
#define array_deln(this, i, count) (array__del(arrcst(this), arrisz(this), (i), (count)))
#define array_copy(alo, dst, src) (array__copy((alo), arrcst(dst), (array_void_t *)(src), arrisz(dst)))
#define array_last(x) ((x)->data[(x)->len - 1])
#define array_for(type, it, array) for (type *it = (array)->data; it < (array)->data + (array)->len; it += 1)
fn void array__init(alo_t alo, array_void_t *this, size_t item_size, i64 count);
fn void array__copy(alo_t alo, array_void_t *dst, array_void_t *src, size_t item_size);
fn void array__grow(array_void_t *this, size_t item_size, i64 item_count);
fn void array__del(array_void_t *this, size_t item_size, i64 idx, i64 count);
fn void array__insert(array_void_t *this, size_t item_size, i64 idx);

View File

@@ -115,6 +115,7 @@ union convert_f32_i32_t { f32 f; i32 i; };
#define PASTE_(a, b) a##b
#define PASTE(a, b) PASTE_(a, b)
#define SWAP(t, a, b) do { t PASTE(temp__, __LINE__) = a; a = b; b = PASTE(temp__, __LINE__); } while(0)
#define SWAP_PTR(t, a, b) do { t PASTE(temp__, __LINE__) = *a; *a = *b; *b = PASTE(temp__, __LINE__); } while(0)
#define CODE(...) #__VA_ARGS__
#if PLATFORM_CL || (PLATFORM_CLANG && PLATFORM_WINDOWS)

View File

@@ -1,14 +1,27 @@
gb_thread thread_ctx_t global_thread_context = {
.log = {
.break_on_fatal = true,
.break_on_error = true,
.break_on_warning = true,
.print_file_and_line = true,
.log_proc = default_log_proc,
}
};
gb_thread thread_ctx_t *tcx;
fn void *ma_arena_alo_proc(alo_t alo, alokind_t kind, void *ptr, size_t size) {
if (kind == alokind_alloc) {
return ma_push_size(alo.object, size);
} else if (kind == alokind_dealloc) {
return NULL;
} else_is_invalid;
return NULL;
}
fn alo_t malo(ma_arena_t *arena) {
return (alo_t){arena, ma_arena_alo_proc};
}
fn alo_t malot(ma_temp_t temp) {
return malo(temp.arena);
}
fn void dealloc(alo_t alo, void *ptr) {
alo.proc(alo, alokind_dealloc, ptr, 0);
}
fn void *alloc_size(alo_t alo, size_t size) {
return alo.proc(alo, alokind_alloc, NULL, size);
}
fn void core_init(void) {
tcx = &global_thread_context;
os_core_init();
}

View File

@@ -1,7 +1,7 @@
typedef struct thread_ctx_t thread_ctx_t;
struct thread_ctx_t {
ma_arena_t scratch[3];
ma_arena_t *temp; // application specific arena
ma_arena_t temp; // application specific arena
// I probably want to discourage using it implicitly like: tcx.perm, instead just pass it around
// to functions that allocate pernament memory. temp and scratch very nicely create reusable code
@@ -9,14 +9,33 @@ struct thread_ctx_t {
// and which functions have state
ma_arena_t perm;
void *data[32];
void *app_ctx;
void *rn_ctx;
void *ui_ctx;
void *te_ctx;
void *user_ctx;
logger_t log;
int thread_index;
};
enum {
tcx_slot_rn,
tcx_slot_ui,
tcx_slot_app,
typedef enum {
alokind_alloc,
alokind_dealloc,
} alokind_t;
typedef struct alo_t alo_t;
struct alo_t {
void *object;
void *(*proc)(alo_t alo, alokind_t kind, void *ptr, size_t size);
};
gb_thread thread_ctx_t *tcx;
#define alloc_type(alo, type) (type *)alloc_size((aloc), sizeof(type))
#define alloc_array(alo, type, count) (type *)alloc_size((alo), sizeof(type) * (count))
fn alo_t malo(ma_arena_t *arena);
fn alo_t malot(ma_temp_t temp);
fn void dealloc(alo_t alo, void *ptr);
fn void *alloc_size(alo_t alo, size_t size);
fn int tcx_alloc_id(void);
extern gb_thread thread_ctx_t *tcx;

View File

@@ -27,7 +27,7 @@ fn void log_basef(log_level_t level, s8_t file_and_line, const char *str, ...);
#if PLATFORM_DEBUG_ASSERT
#define assert_expr(x) (!(x) && (os_error_box(FILE_AND_LINE ": assertion failed: " #x "\n"), debug_break()))
#define assert(x) do { static int once = 1; for (;once;once=0) assert_expr(x); } while(0)
#define assert(x) do { assert_expr(x); } while(0)
#else
#define assert_expr(x) (void)(x)
#define assert(x) do { (void)(x); } while(0)

View File

@@ -370,8 +370,8 @@ fn r2f32_t r2f32_get_bottom(const r2f32_t *r, f32 value) { /* Y is down */
fn_inline r2f32_t r2f32_expand(r2f32_t rect, v2f32_t value) { return (r2f32_t){ v2f32_sub(rect.min, value), v2f32_add(rect.max, value) }; }
fn_inline r2f32_t r2f32_shrink(r2f32_t rect, v2f32_t value) { return (r2f32_t){ v2f32_add(rect.min, value), v2f32_sub(rect.max, value) }; }
fn_inline r2f32_t r2f32_shrinks(r2f32_t rect, f32 value) { return (r2f32_t){ v2f32_adds(rect.min, value), v2f32_subs(rect.max, value) }; }
fn_inline v2f32_t r2f32_get_size(r2f32_t r) { return (v2f32_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
fn_inline v2f32_t r2f32_get_mid(r2f32_t r) { return v2f32_add(r.min, v2f32_divs(r2f32_get_size(r), 2)); }
fn_inline v2f32_t r2f32_size(r2f32_t r) { return (v2f32_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
fn_inline v2f32_t r2f32_get_mid(r2f32_t r) { return v2f32_add(r.min, v2f32_divs(r2f32_size(r), 2)); }
fn_inline b32 r2f32_contains(r2f32_t rec, v2f32_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); }
fn_inline r2f32_t r2f32_intersect(r2f32_t a, r2f32_t b) { return (r2f32_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; }
fn_inline r2f32_t r2f32_union(r2f32_t a, r2f32_t b) { return (r2f32_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; }
@@ -381,8 +381,16 @@ fn_inline r1f32_t r1f32(f32 min, f32 max) { return (r1f32_t){min,max}; }
fn_inline r1f32_t r1f32_auto(f32 a, f32 b) { return (r1f32_t){MIN(a,b),MAX(a,b)}; }
fn_inline r1f32_t r1f32s(f32 a) { return (r1f32_t){a,a}; }
fn_inline r1f32_t r1f32_clamp(r1f32_t a, f32 min, f32 max) { return (r1f32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
fn_inline b32 r1f32_contains(r1f32_t rec, f32 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); }
fn_inline b32 r1f32_are_equal(r1f32_t a, r1f32_t b) { return a.min == b.min && a.max == b.max; }
gb_read_only r1f32_t r1f32_null;
fn b32 r1f32_overlap(r1f32_t a, r1f32_t b) {
b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
return r1 || r2;
}
fn v2f32_t v2f32_clamps(v2f32_t v, f32 min, f32 max) {
v2f32_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) };
return result;
@@ -515,8 +523,8 @@ fn r2f64_t r2f64_get_bottom(const r2f64_t *r, f64 value) { /* Y is down */
fn_inline r2f64_t r2f64_expand(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_sub(rect.min, value), v2f64_add(rect.max, value) }; }
fn_inline r2f64_t r2f64_shrink(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_add(rect.min, value), v2f64_sub(rect.max, value) }; }
fn_inline r2f64_t r2f64_shrinks(r2f64_t rect, f64 value) { return (r2f64_t){ v2f64_adds(rect.min, value), v2f64_subs(rect.max, value) }; }
fn_inline v2f64_t r2f64_get_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
fn_inline v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_get_size(r), 2)); }
fn_inline v2f64_t r2f64_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
fn_inline v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_size(r), 2)); }
fn_inline b32 r2f64_contains(r2f64_t rec, v2f64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); }
fn_inline r2f64_t r2f64_intersect(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; }
fn_inline r2f64_t r2f64_union(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; }
@@ -526,8 +534,16 @@ fn_inline r1f64_t r1f64(f64 min, f64 max) { return (r1f64_t){min,max}; }
fn_inline r1f64_t r1f64_auto(f64 a, f64 b) { return (r1f64_t){MIN(a,b),MAX(a,b)}; }
fn_inline r1f64_t r1f64s(f64 a) { return (r1f64_t){a,a}; }
fn_inline r1f64_t r1f64_clamp(r1f64_t a, f64 min, f64 max) { return (r1f64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
fn_inline b32 r1f64_contains(r1f64_t rec, f64 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); }
fn_inline b32 r1f64_are_equal(r1f64_t a, r1f64_t b) { return a.min == b.min && a.max == b.max; }
gb_read_only r1f64_t r1f64_null;
fn b32 r1f64_overlap(r1f64_t a, r1f64_t b) {
b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
return r1 || r2;
}
fn v2f64_t v2f64_clamps(v2f64_t v, f64 min, f64 max) {
v2f64_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) };
return result;
@@ -660,8 +676,8 @@ fn r2i32_t r2i32_get_bottom(const r2i32_t *r, i32 value) { /* Y is down */
fn_inline r2i32_t r2i32_expand(r2i32_t rect, v2i32_t value) { return (r2i32_t){ v2i32_sub(rect.min, value), v2i32_add(rect.max, value) }; }
fn_inline r2i32_t r2i32_shrink(r2i32_t rect, v2i32_t value) { return (r2i32_t){ v2i32_add(rect.min, value), v2i32_sub(rect.max, value) }; }
fn_inline r2i32_t r2i32_shrinks(r2i32_t rect, i32 value) { return (r2i32_t){ v2i32_adds(rect.min, value), v2i32_subs(rect.max, value) }; }
fn_inline v2i32_t r2i32_get_size(r2i32_t r) { return (v2i32_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
fn_inline v2i32_t r2i32_get_mid(r2i32_t r) { return v2i32_add(r.min, v2i32_divs(r2i32_get_size(r), 2)); }
fn_inline v2i32_t r2i32_size(r2i32_t r) { return (v2i32_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
fn_inline v2i32_t r2i32_get_mid(r2i32_t r) { return v2i32_add(r.min, v2i32_divs(r2i32_size(r), 2)); }
fn_inline b32 r2i32_contains(r2i32_t rec, v2i32_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); }
fn_inline r2i32_t r2i32_intersect(r2i32_t a, r2i32_t b) { return (r2i32_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; }
fn_inline r2i32_t r2i32_union(r2i32_t a, r2i32_t b) { return (r2i32_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; }
@@ -671,8 +687,16 @@ fn_inline r1i32_t r1i32(i32 min, i32 max) { return (r1i32_t){min,max}; }
fn_inline r1i32_t r1i32_auto(i32 a, i32 b) { return (r1i32_t){MIN(a,b),MAX(a,b)}; }
fn_inline r1i32_t r1i32s(i32 a) { return (r1i32_t){a,a}; }
fn_inline r1i32_t r1i32_clamp(r1i32_t a, i32 min, i32 max) { return (r1i32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
fn_inline b32 r1i32_contains(r1i32_t rec, i32 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); }
fn_inline b32 r1i32_are_equal(r1i32_t a, r1i32_t b) { return a.min == b.min && a.max == b.max; }
gb_read_only r1i32_t r1i32_null;
fn b32 r1i32_overlap(r1i32_t a, r1i32_t b) {
b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
return r1 || r2;
}
fn v2i32_t v2i32_clamps(v2i32_t v, i32 min, i32 max) {
v2i32_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) };
return result;
@@ -805,8 +829,8 @@ fn r2i64_t r2i64_get_bottom(const r2i64_t *r, i64 value) { /* Y is down */
fn_inline r2i64_t r2i64_expand(r2i64_t rect, v2i64_t value) { return (r2i64_t){ v2i64_sub(rect.min, value), v2i64_add(rect.max, value) }; }
fn_inline r2i64_t r2i64_shrink(r2i64_t rect, v2i64_t value) { return (r2i64_t){ v2i64_add(rect.min, value), v2i64_sub(rect.max, value) }; }
fn_inline r2i64_t r2i64_shrinks(r2i64_t rect, i64 value) { return (r2i64_t){ v2i64_adds(rect.min, value), v2i64_subs(rect.max, value) }; }
fn_inline v2i64_t r2i64_get_size(r2i64_t r) { return (v2i64_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
fn_inline v2i64_t r2i64_get_mid(r2i64_t r) { return v2i64_add(r.min, v2i64_divs(r2i64_get_size(r), 2)); }
fn_inline v2i64_t r2i64_size(r2i64_t r) { return (v2i64_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
fn_inline v2i64_t r2i64_get_mid(r2i64_t r) { return v2i64_add(r.min, v2i64_divs(r2i64_size(r), 2)); }
fn_inline b32 r2i64_contains(r2i64_t rec, v2i64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); }
fn_inline r2i64_t r2i64_intersect(r2i64_t a, r2i64_t b) { return (r2i64_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; }
fn_inline r2i64_t r2i64_union(r2i64_t a, r2i64_t b) { return (r2i64_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; }
@@ -816,8 +840,16 @@ fn_inline r1i64_t r1i64(i64 min, i64 max) { return (r1i64_t){min,max}; }
fn_inline r1i64_t r1i64_auto(i64 a, i64 b) { return (r1i64_t){MIN(a,b),MAX(a,b)}; }
fn_inline r1i64_t r1i64s(i64 a) { return (r1i64_t){a,a}; }
fn_inline r1i64_t r1i64_clamp(r1i64_t a, i64 min, i64 max) { return (r1i64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
fn_inline b32 r1i64_contains(r1i64_t rec, i64 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); }
fn_inline b32 r1i64_are_equal(r1i64_t a, r1i64_t b) { return a.min == b.min && a.max == b.max; }
gb_read_only r1i64_t r1i64_null;
fn b32 r1i64_overlap(r1i64_t a, r1i64_t b) {
b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
return r1 || r2;
}
fn v2i64_t v2i64_clamps(v2i64_t v, i64 min, i64 max) {
v2i64_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) };
return result;

View File

@@ -318,8 +318,8 @@ fn r2f64_t r2f64_get_bottom(const r2f64_t *r, f64 value) { /* Y is down */
fn_inline r2f64_t r2f64_expand(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_sub(rect.min, value), v2f64_add(rect.max, value) }; }
fn_inline r2f64_t r2f64_shrink(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_add(rect.min, value), v2f64_sub(rect.max, value) }; }
fn_inline r2f64_t r2f64_shrinks(r2f64_t rect, f64 value) { return (r2f64_t){ v2f64_adds(rect.min, value), v2f64_subs(rect.max, value) }; }
fn_inline v2f64_t r2f64_get_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
fn_inline v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_get_size(r), 2)); }
fn_inline v2f64_t r2f64_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
fn_inline v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_size(r), 2)); }
fn_inline b32 r2f64_contains(r2f64_t rec, v2f64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); }
fn_inline r2f64_t r2f64_intersect(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; }
fn_inline r2f64_t r2f64_union(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; }
@@ -329,8 +329,16 @@ fn_inline r1f64_t r1f64(f64 min, f64 max) { return (r1f64_t){min,max}; }
fn_inline r1f64_t r1f64_auto(f64 a, f64 b) { return (r1f64_t){MIN(a,b),MAX(a,b)}; }
fn_inline r1f64_t r1f64s(f64 a) { return (r1f64_t){a,a}; }
fn_inline r1f64_t r1f64_clamp(r1f64_t a, f64 min, f64 max) { return (r1f64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
fn_inline b32 r1f64_contains(r1f64_t rec, f64 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); }
fn_inline b32 r1f64_are_equal(r1f64_t a, r1f64_t b) { return a.min == b.min && a.max == b.max; }
gb_read_only r1f64_t r1f64_null;
fn b32 r1f64_overlap(r1f64_t a, r1f64_t b) {
b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
return r1 || r2;
}
fn v2f64_t v2f64_clamps(v2f64_t v, f64 min, f64 max) {
v2f64_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) };
return result;

View File

@@ -6,4 +6,4 @@ fn b32 os_vmem_commit(void *p, usize size);
fn b32 os_vmem_release(void *p);
fn b32 os_vmem_decommit(void *p, usize size);
fn void core_init(void); // required in wasm
fn void os_core_init(void);

View File

@@ -54,8 +54,18 @@ fn void os_unix_register_crash_handler() {
sigaction(SIGABRT, &sa, NULL);
}
fn void os_core_init(void) {
tcx->temp = ma_create(ma_default_reserve_size);
ma_init(&tcx->perm, ma_default_reserve_size);
fn void os_core_small_init(ma_arena_t *arena) {
tcx = ma_push_type(arena, thread_ctx_t);
tcx->log.break_on_error = true;
tcx->log.break_on_fatal = true;
tcx->log.break_on_warning = true;
tcx->log.print_file_and_line = true;
tcx->log.log_proc = default_log_proc;
os_unix_register_crash_handler();
}
fn void os_core_init(void) {
ma_arena_t perm = {0};
os_core_small_init(&perm);
tcx->perm = perm;
}

View File

@@ -40,18 +40,28 @@ fn f64 os_parse_float(char *str) {
return wasm_parse_float((isize)str, str_len((char *)str));
}
fn void os_core_small_init(ma_arena_t *arena) {
tcx = ma_push_type(arena, thread_ctx_t);
tcx->log.break_on_error = true;
tcx->log.break_on_fatal = true;
tcx->log.break_on_warning = true;
tcx->log.print_file_and_line = true;
tcx->log.log_proc = default_log_proc;
}
fn void os_core_init(void) {
ma_arena_t perm = {0};
isize page_size = kib(64);
isize page_count = __builtin_wasm_memory_size(0);
u8 *memory = (u8 *)&__heap_base;
usize memory_size = page_count * (page_size) - (isize)memory;
tcx->perm.data = memory;
tcx->perm.commit = tcx->perm.reserve = memory_size;
ma_arena_t *perm = &tcx->perm;
ma_push_arena_ex(&tcx->perm, &tcx->scratch[0], mib(1));
perm.data = memory;
perm.commit = perm.reserve = memory_size;
os_core_small_init(&perm);
tcx->perm = perm;
ma_push_arena_ex(&tcx->perm, &tcx->temp, mib(2));
ma_push_arena_ex(&tcx->perm, &tcx->scratch[0], mib(2));
ma_push_arena_ex(&tcx->perm, &tcx->scratch[1], kib(256));
ma_push_arena_ex(&tcx->perm, &tcx->scratch[2], kib(64));
tcx->temp = ma_push_arena(perm, mib(2));
debugf("memory size = %lld", memory_size);
}

View File

@@ -45,8 +45,18 @@ fn void os_win32_register_crash_handler() {
signal(SIGABRT, os_win32_crash_handler);
}
fn void os_core_init(void) {
tcx->temp = ma_create(ma_default_reserve_size);
ma_init(&tcx->perm, ma_default_reserve_size);
fn void os_core_small_init(ma_arena_t *arena) {
tcx = ma_push_type(arena, thread_ctx_t);
tcx->log.break_on_error = true;
tcx->log.break_on_fatal = true;
tcx->log.break_on_warning = true;
tcx->log.print_file_and_line = true;
tcx->log.log_proc = default_log_proc;
os_win32_register_crash_handler();
}
}
fn void os_core_init(void) {
ma_arena_t perm = {0};
os_core_small_init(&perm);
tcx->perm = perm;
}

View File

@@ -45,6 +45,21 @@ fn b32 char_is_alphanumeric(char a) {
return result;
}
fn b32 char_is_symbol(char w) {
b32 result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~');
return result;
}
fn b32 char_is_non_word(char w) {
b32 result = w != '_' && (char_is_symbol(w) || char_is_whitespace(w));
return result;
}
fn b32 char_is_word(char w) {
b32 result = !char_is_non_word(w);
return result;
}
fn s8_t s8_from_range(char *begin, char *end) {
assert(end >= begin);
intptr_t size = (intptr_t)end - (intptr_t)begin;
@@ -414,7 +429,14 @@ fn sb8_t s8_split(ma_arena_t *ma, s8_t string, s8_t find, s8_split_t flags) {
s8_seek_t find_flag = flags & s8_split_ignore_case ? s8_seek_ignore_case : s8_seek_none;
while (s8_seek(string, find, find_flag, &index)) {
s8_t before_match = s8_make(string.str, index);
sb8_append(&result, before_match);
if (flags & s8_split_cleanup) {
before_match = s8_trim(before_match);
if (before_match.len) {
sb8_append(&result, before_match);
}
} else {
sb8_append(&result, before_match);
}
if (flags & s8_split_inclusive) {
s8_t match = s8_make(string.str + index, find.len);
sb8_append(&result, match);
@@ -465,6 +487,20 @@ fn u64 u64_from_s8(s8_t s, u64 base) {
return acc;
}
fn u64 u64_from_s8_loose(s8_t s, u64 base) {
assert(base >= 2 && base <= 16);
u64 acc = 0;
for (i64 i = 0; i < s.len; i++) {
u64 num = u64_from_hexchar(s.str[i]);
if (num >= base) {
break;
}
acc *= base;
acc += num;
}
return acc;
}
fn f64 f64_from_s8(s8_t string) {
ma_temp_t scratch = ma_begin_scratch();
s8_t num_string = s8_copy(scratch.arena, string);

View File

@@ -35,6 +35,7 @@ enum {
s8_split_none = 0,
s8_split_ignore_case = 1,
s8_split_inclusive = 2,
s8_split_cleanup = 4,
};
enum { s8_ignore_case = 1 };

View File

@@ -5,38 +5,53 @@ fn i64 str16_len(u16 *string) {
return len;
}
fn u16 u16_to_lower_case(u16 a) {
fn u16 char16_to_lower_case(u16 a) {
if (a >= 'A' && a <= 'Z') a += 32;
return a;
}
fn u16 u16_to_upper_case(u16 a) {
fn u16 char16_to_upper_case(u16 a) {
if (a >= 'a' && a <= 'z') a -= 32;
return a;
}
fn b32 u16_is_whitespace(u16 w) {
fn b32 char16_is_whitespace(u16 w) {
b32 result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
return result;
}
fn b32 u16_is_alphabetic(u16 a) {
fn b32 char16_is_alphabetic(u16 a) {
b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
return result;
}
fn b32 u16_is_ident(u16 a) {
fn b32 char16_is_ident(u16 a) {
b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
return result;
}
fn b32 u16_is_digit(u16 a) {
fn b32 char16_is_digit(u16 a) {
b32 result = a >= '0' && a <= '9';
return result;
}
fn b32 u16_is_alphanumeric(u16 a) {
b32 result = u16_is_digit(a) || u16_is_alphabetic(a);
fn b32 char16_is_symbol(u16 w) {
b32 result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~');
return result;
}
fn b32 char16_is_non_word(u16 w) {
b32 result = w != '_' && (char16_is_symbol(w) || char16_is_whitespace(w));
return result;
}
fn b32 char16_is_word(u16 w) {
b32 result = !char16_is_non_word(w);
return result;
}
fn b32 char16_is_alphanumeric(u16 a) {
b32 result = char16_is_digit(a) || char16_is_alphabetic(a);
return result;
}
@@ -73,8 +88,8 @@ fn b32 s16_are_equal_ex(s16_t a, s16_t b, b32 ignore_case) {
u16 A = a.str[i];
u16 B = b.str[i];
if (ignore_case) {
A = u16_to_lower_case(A);
B = u16_to_lower_case(B);
A = char16_to_lower_case(A);
B = char16_to_lower_case(B);
}
if (A != B)
return false;
@@ -206,14 +221,14 @@ fn s16_t s16_trim(s16_t string) {
i64 whitespace_begin = 0;
for (; whitespace_begin < string.len; whitespace_begin++) {
if (!u16_is_whitespace(string.str[whitespace_begin])) {
if (!char16_is_whitespace(string.str[whitespace_begin])) {
break;
}
}
i64 whitespace_end = string.len;
for (; whitespace_end != whitespace_begin; whitespace_end--) {
if (!u16_is_whitespace(string.str[whitespace_end - 1])) {
if (!char16_is_whitespace(string.str[whitespace_end - 1])) {
break;
}
}
@@ -231,7 +246,7 @@ fn s16_t s16_trim(s16_t string) {
fn s16_t s16_trim_end(s16_t string) {
i64 whitespace_end = string.len;
for (; whitespace_end != 0; whitespace_end--) {
if (!u16_is_whitespace(string.str[whitespace_end - 1])) {
if (!char16_is_whitespace(string.str[whitespace_end - 1])) {
break;
}
}
@@ -314,7 +329,7 @@ fn s16_t s16_get_name_no_ext(s16_t s) {
fn s16_t s16_to_lower_case(ma_arena_t *ma, s16_t s) {
s16_t copy = s16_copy(ma, s);
for (int64_t i = 0; i < copy.len; i++) {
copy.str[i] = u16_to_lower_case(copy.str[i]);
copy.str[i] = char16_to_lower_case(copy.str[i]);
}
return copy;
}
@@ -322,7 +337,7 @@ fn s16_t s16_to_lower_case(ma_arena_t *ma, s16_t s) {
fn s16_t s16_to_upper_case(ma_arena_t *ma, s16_t s) {
s16_t copy = s16_copy(ma, s);
for (int64_t i = 0; i < copy.len; i++) {
copy.str[i] = u16_to_upper_case(copy.str[i]);
copy.str[i] = char16_to_upper_case(copy.str[i]);
}
return copy;
}
@@ -474,6 +489,15 @@ fn sb16_t s16_split(ma_arena_t *ma, s16_t string, s16_t find, s16_split_t flags)
return result;
}
fn s16_t s16_copy_ex(alo_t ma, s16_t string) {
i64 byte_size = sizeof(u16) * string.len;
u16 *copy = (u16 *)alloc_size(ma, byte_size + sizeof(u16));
memory_copy(copy, string.str, byte_size);
copy[string.len] = 0;
s16_t result = s16_make(copy, string.len);
return result;
}
fn_test void test_string16(void) {
ma_temp_t scratch = ma_begin_scratch();
@@ -491,10 +515,10 @@ fn_test void test_string16(void) {
}
{
assert(u16_to_upper_case('a') == char16('A'));
assert(u16_to_upper_case('a') == 'A');
assert(u16_to_upper_case(char16('a')) == 'A');
assert(u16_is_digit('3'));
assert(char16_to_upper_case('a') == char16('A'));
assert(char16_to_upper_case('a') == 'A');
assert(char16_to_upper_case(char16('a')) == 'A');
assert(char16_is_digit('3'));
}
{
@@ -531,8 +555,12 @@ fn_test void test_string16(void) {
{
s16_t a = s16("C:\\Program Files\\Memes");
s16_normalize_path_unsafe(a);
assert(a.str[2] == '/');
u16 memes[256] = {0};
memory_copy(memes, a.str, a.len * 2 + 2);
s16_t b = s16_make(memes, a.len);
s16_normalize_path_unsafe(b);
assert(b.str[2] == '/');
}
{

View File

@@ -2264,7 +2264,7 @@ OS_API S8_String OS_GetExeDir(MA_Arena *arena) {
return path;
}
OS_API S8_String os_cwd(MA_Arena *arena) {
OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) {
wchar_t wbuffer[1024];
DWORD wsize = GetCurrentDirectoryW(MA_Lengthof(wbuffer), wbuffer);
IO_Assert(wsize != 0);
@@ -2277,7 +2277,7 @@ OS_API S8_String os_cwd(MA_Arena *arena) {
return path;
}
OS_API void os_set_working_dir(char *path) {
OS_API void OS_SetWorkingDir(char *path) {
IO_Printf("cd %s\n", path);
wchar_t wpath[1024];
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path, S8_Length(path));
@@ -2296,7 +2296,7 @@ OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) {
return path;
}
OS_API bool os_file_exists(S8_String path) {
OS_API bool OS_FileExists(S8_String path) {
wchar_t wbuff[1024];
UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len);
DWORD attribs = GetFileAttributesW(wbuff);
@@ -2401,7 +2401,7 @@ OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path) {
return it;
}
OS_API OS_Result os_make_dir(char *path) {
OS_API OS_Result OS_MakeDir(char *path) {
IO_Printf("mkdir %s\n", path);
wchar_t wpath[1024];
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path, S8_Length(path));
@@ -2420,7 +2420,7 @@ OS_API OS_Result os_make_dir(char *path) {
return result;
}
OS_API OS_Result os_copy(char *from, char *to, bool overwrite) {
OS_API OS_Result OS_Copy(char *from, char *to, bool overwrite) {
const char *ow = overwrite ? "-n" : "";
IO_Printf("cp %s %s %s\n", ow, from, to);
@@ -2439,7 +2439,7 @@ OS_API OS_Result os_copy(char *from, char *to, bool overwrite) {
return result;
}
OS_API OS_Result os_delete_file(S8_String path) {
OS_API OS_Result OS_DeleteFile(S8_String path) {
IO_Printf("rm %.*s\n", S8_Expand(path));
wchar_t wpath[1024];
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
@@ -2460,7 +2460,7 @@ OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) {
S8_Node *dirs_to_remove = 0;
for (S8_Node *it = list.first; it; it = it->next) {
if (!S8_EndsWith(it->string, S8_Lit("/"), S8_IgnoreCase)) {
os_delete_file(it->string);
OS_DeleteFile(it->string);
}
else {
S8_Node *node = S8_CreateNode(scratch.arena, it->string);
@@ -2637,14 +2637,14 @@ OS_API S8_String OS_GetExeDir(MA_Arena *arena) {
return copy;
}
OS_API S8_String os_cwd(MA_Arena *arena) {
OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) {
char *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX);
char *cwd = getcwd(buffer, PATH_MAX);
S8_String result = S8_MakeFromChar(cwd);
return result;
}
OS_API void os_set_working_dir(char *path) {
OS_API void OS_SetWorkingDir(char *path) {
IO_Printf("cd %s\n", path);
MA_Temp scratch = MA_GetScratch();
S8_String copy = S8_Copy(scratch.arena, S8_MakeFromChar(path));
@@ -2664,7 +2664,7 @@ OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) {
return result;
}
OS_API bool os_file_exists(S8_String path) {
OS_API bool OS_FileExists(S8_String path) {
MA_Temp scratch = MA_GetScratch();
S8_String copy = S8_Copy(scratch.arena, path);
@@ -2747,8 +2747,8 @@ OS_API OS_FileIter OS_IterateFiles(MA_Arena *arena, S8_String path) {
return it;
}
OS_API int os_systemf(const char *string, ...);
OS_API OS_Result os_make_dir(char *path) {
OS_API int OS_Systemf(const char *string, ...);
OS_API OS_Result OS_MakeDir(char *path) {
IO_Printf("mkdir %s\n", path);
MA_Temp scratch = MA_GetScratch();
S8_String path8 = S8_Copy(scratch.arena, S8_MakeFromChar(path));
@@ -2757,20 +2757,20 @@ OS_API OS_Result os_make_dir(char *path) {
return error == 0 ? OS_SUCCESS : OS_FAILURE;
}
OS_API OS_Result os_copy(char *from, char *to, bool overwrite) {
OS_API OS_Result OS_Copy(char *from, char *to, bool overwrite) {
const char *ow = overwrite ? "-n" : "";
int result = os_systemf("cp %s %s %s", ow, from, to);
int result = OS_Systemf("cp %s %s %s", ow, from, to);
return result == 0 ? OS_SUCCESS : OS_FAILURE;
}
OS_API OS_Result os_delete_file(S8_String path) {
int result = os_systemf("rm %.*s", S8_Expand(path));
OS_API OS_Result OS_DeleteFile(S8_String path) {
int result = OS_Systemf("rm %.*s", S8_Expand(path));
return result == 0 ? OS_SUCCESS : OS_FAILURE;
}
OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) {
IO_Assert(flags & OS_RECURSIVE);
int result = os_systemf("rm -r %.*s", S8_Expand(path));
int result = OS_Systemf("rm -r %.*s", S8_Expand(path));
return result == 0 ? OS_SUCCESS : OS_FAILURE;
}
@@ -2881,10 +2881,10 @@ OS_API OS_Result os_write_file(S8_String path, S8_String string) {
#endif
#if _WIN32 || __linux__ || __APPLE__ || __unix__
OS_API int os_systemf(const char *string, ...) {
OS_API int OS_Systemf(const char *string, ...) {
MA_Temp scratch = MA_GetScratch();
S8_FORMAT(scratch.arena, string, result);
IO_Printf("%.*s\n", S8_Expand(result));
// IO_Printf("%.*s\n", S8_Expand(result));
int error_code = system(result.str);
MA_ReleaseScratch(scratch);
return error_code;
@@ -4479,8 +4479,11 @@ CL_PRIVATE_FUNCTION void CL_DefaultTokenize(CL_Lexer *T, CL_Token *token) {
CL_Advance(T);
}
uint64_t len = T->stream - token->str;
CL_ASSERT(len > 2);
token->u64 = CL_ParseInteger(T, token, token->str + 2, len - 2, 16);
if (len <= 2) {
CL_ReportError(T, token, "Invalid hex number");
} else {
token->u64 = CL_ParseInteger(T, token, token->str + 2, len - 2, 16);
}
break;
}
}
@@ -5052,7 +5055,7 @@ SRC_CacheEntry *cache_hash_file(S8_String file, char *parent_file) {
bool cache_code_modified(S8_String file, S8_String artifact_path) {
double time_start = OS_GetTime();
if (os_file_exists(file) == false) {
if (OS_FileExists(file) == false) {
IO_Printf("FAILED File doesnt exist: %.*s\n", S8_Expand(file));
exit(0);
}
@@ -5062,7 +5065,7 @@ bool cache_code_modified(S8_String file, S8_String artifact_path) {
bool modified = false;
if (artifact_path.len) {
modified = os_file_exists(artifact_path) == false;
modified = OS_FileExists(artifact_path) == false;
}
SRC_CacheEntry *in_memory = cache_hash_file(file, 0);
@@ -5117,8 +5120,8 @@ S8_String GCC_Debug = S8_ConstLit("-fsanitize=address -g");
#if !defined(BUILD_TOOL_LIB)
int main(int argument_count, char **arguments) {
os_make_dir("build");
os_set_working_dir("build");
OS_MakeDir("build");
OS_SetWorkingDir("build");
S8_String working_dir = os_cwd(&Perm);
S8_String cc = S8_Lit(IF_WINDOWS("cl") IF_MAC("clang") IF_LINUX("gcc"));
@@ -5157,16 +5160,16 @@ int main(int argument_count, char **arguments) {
time_build_file_compiled = OS_GetTime();
int result = 0;
if (S8_AreEqual(cc, S8_Lit("cl"), false)) {
result = os_systemf("cl \"%.*s\" -nologo -Zi -WX -W3 -wd4200 -diagnostics:column /Fe:%.*s /Fd:%.*s.pdb", S8_Expand(build_file), S8_Expand(exe_name), S8_Expand(name_no_ext));
result = OS_Systemf("cl \"%.*s\" -nologo -Zi -WX -W3 -wd4200 -diagnostics:column /Fe:%.*s /Fd:%.*s.pdb", S8_Expand(build_file), S8_Expand(exe_name), S8_Expand(name_no_ext));
} else if (S8_AreEqual(cc, S8_Lit("clang"), false)) {
result = os_systemf("clang \"%.*s\" -o %.*s -ldl -g -Wno-single-bit-bitfield-constant-conversion -fdiagnostics-absolute-paths -Wno-writable-strings -Wno-unsequenced %s", S8_Expand(build_file), S8_Expand(exe_name), IF_LINUX_ELSE("-lm", ""));
result = OS_Systemf("clang \"%.*s\" -o %.*s -ldl -g -Wno-single-bit-bitfield-constant-conversion -fdiagnostics-absolute-paths -Wno-writable-strings -Wno-unsequenced %s", S8_Expand(build_file), S8_Expand(exe_name), IF_LINUX_ELSE("-lm", ""));
} else {
result = os_systemf("gcc \"%.*s\" -o %.*s -ldl -g -Wno-single-bit-bitfield-constant-conversion -Wno-write-strings %s", S8_Expand(build_file), S8_Expand(exe_name), IF_LINUX_ELSE("-lm", ""));
result = OS_Systemf("gcc \"%.*s\" -o %.*s -ldl -g -Wno-single-bit-bitfield-constant-conversion -Wno-write-strings %s", S8_Expand(build_file), S8_Expand(exe_name), IF_LINUX_ELSE("-lm", ""));
}
if (result != 0) {
IO_Printf("ERROR: failed to compile build file: %.*s, exitcode(%d), %f, deleting cache\n", S8_Expand(build_file), result, OS_GetTime() - time_build_file_compiled);
os_delete_file(S8_Lit("build_tool.cache"));
OS_DeleteFile(S8_Lit("build_tool.cache"));
return 1;
}
time_build_file_compiled = OS_GetTime() - time_build_file_compiled;
@@ -5177,10 +5180,10 @@ int main(int argument_count, char **arguments) {
double time = OS_GetTime();
if (build_file.str) {
exe_name = OS_GetAbsolutePath(&Perm, exe_name);
int result = os_systemf("%.*s %.*s", S8_Expand(exe_name), S8_Expand(cmd_args_merged));
int result = OS_Systemf("%.*s %.*s", S8_Expand(exe_name), S8_Expand(cmd_args_merged));
if (result != 0) {
IO_Printf("ERROR: error during build_file execution: %.*s, exitcode(%d), %f, deleting cache\n", S8_Expand(build_file), result, OS_GetTime() - time);
os_delete_file(S8_Lit("build_tool.cache"));
OS_DeleteFile(S8_Lit("build_tool.cache"));
return 1;
}
}

View File

@@ -13,15 +13,15 @@ struct mt_files_t {
};
fn s8_t mt_resolve_path(ma_arena_t *arena, sb8_t *include_paths, s8_t filename, s8_t parent_file, b32 is_system_include) {
if (OS_IsAbsolute(filename) && os_file_exists(filename)) {
if (os_is_abs(filename) && os_exists(filename)) {
return filename;
}
// 1) (QUOTED_FORM) In the same directory as the file that contains the #include statement.
if (!is_system_include && parent_file.len) {
s8_t path = s8_printf(arena, "%S/%S", s8_chop_last_slash(parent_file), filename);
if (os_file_exists(path)) {
return OS_GetAbsolutePath(&Perm, path);
if (os_exists(path)) {
return os_abs(arena, path);
}
}
@@ -31,8 +31,8 @@ fn s8_t mt_resolve_path(ma_arena_t *arena, sb8_t *include_paths, s8_t filename,
// 3) (BOTH FORMS) Along the path that's specified by each /I (or INCLUDE enviroment variable) compiler option.
for (sb8_node_t *it = include_paths->first; it; it = it->next) {
s8_t path = s8_printf(arena, "%S/%S", it->string, filename);
if (os_file_exists(path)) {
return OS_GetAbsolutePath(&Perm, path);
if (os_exists(path)) {
return os_abs(arena, path);
}
}
@@ -54,7 +54,7 @@ fn mt_file_t *mt_find_file(mt_files_t *root, s8_t name) {
}
fn void mt__lex_files(ma_arena_t *arena, mt_files_t *root, s8_t path, sb8_t *include_paths) {
s8_t content = OS_ReadFile(&Perm, path);
s8_t content = os_read(arena, path);
if (content.len == 0) {
return;
}
@@ -82,17 +82,17 @@ fn void mt__lex_files(ma_arena_t *arena, mt_files_t *root, s8_t path, sb8_t *inc
fn mt_files_t mt_lex_files(ma_arena_t *arena, s8_t path, sb8_t *include_paths) {
mt_files_t files = {0};
path = OS_GetAbsolutePath(&Perm, path);
path = os_abs(arena, path);
mt__lex_files(arena, &files, path, include_paths);
return files;
}
fn void mt_list_files_recursive(sb8_t *sb, s8_t path) {
for (OS_FileIter iter = OS_IterateFiles(&Perm, path); OS_IsValid(iter); OS_Advance(&iter)) {
if (iter.is_directory) {
mt_list_files_recursive(sb, iter.absolute_path);
for (os_iter_t *iter = os_iter(sb->arena, path); iter->is_valid; os_advance(iter)) {
if (iter->is_directory) {
mt_list_files_recursive(sb, iter->abs);
} else {
sb8_append(sb, iter.absolute_path);
sb8_append(sb, iter->abs);
}
}
}
@@ -134,6 +134,6 @@ fn s8_t mt__main_path(ma_arena_t *arena, s8_t file) {
fn sb8_t *mt_get_include_paths(ma_arena_t *arena) {
sb8_t *result = ma_push_type(arena, sb8_t);
result->arena = arena;
sb8_append(result, OS_GetAbsolutePath(&Perm, s8("../src")));
sb8_append(result, os_abs(arena, s8("../src")));
return result;
}

View File

@@ -12,3 +12,14 @@ fn s8_t os_format_date(ma_arena_t *arena, os_date_t date) {
s8_t result = s8_printf(arena, "%04u-%02u-%02u %02u:%02u:%02u.%03u", date.year, date.month, date.day, date.hour, date.min, date.sec, date.ms);
return result;
}
#if PLATFORM_WINDOWS || PLATFORM_POSIX
fn int os_systemf(const char *string, ...) {
ma_temp_t scratch = ma_begin_scratch();
S8_FMT(scratch.arena, string, result);
int error_code = system(result.str);
ma_end_scratch(scratch);
return error_code;
}
#endif

View File

@@ -44,6 +44,8 @@ fn os_date_t os_universal_time(void);
fn s8_t os_format_date(ma_arena_t *arena, os_date_t date);
fn f64 os_seconds(void);
fn f64 os_milliseconds(void);
fn f64 os_microseconds(void);
fn b32 os_copy(s8_t from, s8_t to, b32 overwrite);
fn b32 os_delete(s8_t path);

View File

@@ -25,11 +25,11 @@ fn os_date_t os_universal_time(void) {
return result;
}
fn u64 os_get_microseconds(void) {
fn f64 os_microseconds(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
u64 result = t.tv_sec*million(1) + (t.tv_nsec/thousand(1));
return result;
return (f64)result;
}
fn f64 os_get_milliseconds(void) {

View File

@@ -28,23 +28,30 @@ fn os_date_t os_universal_time(void) {
return result;
}
static int64_t win32_counts_per_second;
fn f64 os_seconds(void) {
static int64_t counts_per_second;
if (counts_per_second == 0) {
if (win32_counts_per_second == 0) {
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
counts_per_second = freq.QuadPart;
win32_counts_per_second = freq.QuadPart;
}
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
f64 result = (f64)time.QuadPart / (f64)counts_per_second;
f64 result = (f64)time.QuadPart / (f64)win32_counts_per_second;
return result;
}
// @todo: revise these conversions
fn f64 os_microseconds(void) {
f64 secs = os_seconds();
f64 result = secs * 1000000.0;
return result;
}
fn f64 os_milliseconds(void) {
f64 secs = os_seconds();
f64 result = secs * 1000;
f64 result = secs * 1000.0;
return result;
}
@@ -152,10 +159,13 @@ fn void os_advance(os_iter_t *it) {
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
it->name = s8_from_s16(it->arena, s16_make(data->cFileName, str16_len(data->cFileName)));
char *is_dir = it->is_directory ? "/" : "";
char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
it->rel = s8_printf(it->arena, "%S%s%S%s", it->path, separator, it->name, is_dir);
it->abs = os_abs(it->arena, it->rel);
s8_normalize_path_unsafe(it->rel);
s8_normalize_path_unsafe(it->abs);
it->is_valid = true;
if (it->is_directory) {
@@ -348,7 +358,7 @@ fn i32 os_thread_join(os_thread_t *thread) {
// @todo:
// This will probably create 16 arenas or more XD
// fn i32 os__thread_log(void *data) {
// core_init();
// os_core_init();
// debugf("testing");
// return 0;
// }

View File

@@ -9,26 +9,42 @@
#include "render/render.c"
#include "ui/ui.c"
#include "wasm_app.gen.c"
#include "parse_srt.c"
#include "../build/entries.inc"
#include "transcript_browser.c"
// #include "win32_transcript_browser.c"
#include "prototype.gen.c"
// @todo: scrolling when clicked on bar bugs out and is slow
// @todo: copy paste
// @todo @ui scrolling when focused button goes out of screen
fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
tcx = thread_ctx;
if (frame->first_event->kind == app_event_kind_init) {
run_all_tests();
mt_tweak_f32(font_size, 30, 4, 200);
mt_tweak_f32(_font_size, 30, 30, 30);
ma_arena_t *perm = &tcx->perm;
rn_init(perm, font_size, frame->dpr);
ui_init(perm);
rn_init(&tcx->perm, font_size, frame->dpr);
ui_init(&tcx->perm);
tcx->user_ctx = (void *)ma_push_type(&tcx->perm, res_t);
res_init(tcx->user_ctx);
res_t *res = tcx->user_ctx;
res_report(res, "type something!");
return true;
} else if (frame->first_event->kind == app_event_kind_reload) {
ui_reload();
rn_reload();
res_reload(tcx->user_ctx);
return true;
} else if (frame->first_event->kind == app_event_kind_unload) {
res_unload(tcx->user_ctx);
return true;
}
@@ -36,6 +52,7 @@ fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
_font_size = font_size;
rn_reload_font(font_size, frame->dpr);
}
ui_demo_update(frame, tweak_table, lengthof(tweak_table));
// ui_demo_update(frame, tweak_table, lengthof(tweak_table));
transcript_browser_update(frame, tweak_table, lengthof(tweak_table));
return true;
}
}

73
src/prototype/parse_srt.c Normal file
View File

@@ -0,0 +1,73 @@
typedef struct srt_entry_t srt_entry_t;
struct srt_entry_t {
u16 hour, minute, second;
s8_t string;
s8_t filepath;
i32 filepath_index;
};
typedef array(srt_entry_t) array_srt_entry_t;
#if PLATFORM_WASM
fn s8_t os_read(ma_arena_t *arena, s8_t filename);
#endif
fn array_srt_entry_t srt_parse(ma_arena_t *arena, s8_t filename) {
s8_t content = os_read(arena, filename);
sb8_t lines = s8_split(arena, content, s8("\n"), s8_split_cleanup);
// srt format looks like this:
// 1
// 00:00:01,000 --> 00:00:05,000
// This is the first subtitle.
//
// or:
// section number
// time interval
// text
int section_number = 1;
array_srt_entry_t result = {0};
result.alo = malo(arena);
for (sb8_node_t *it = lines.first; it;) {
// parse section number
u64 num = u64_from_s8_loose(it->string, 10);
assert(section_number == num);
section_number += 1;
it = it->next;
// parse start of time interval
srt_entry_t entry = {0};
entry.hour = (u16)u64_from_s8_loose(it->string, 10);
entry.minute = (u16)u64_from_s8_loose(s8_skip(it->string, 3), 10);
entry.second = (u16)u64_from_s8_loose(s8_skip(it->string, 6), 10);
entry.filepath = filename;
it = it->next;
// parse text
s8_t next_section_number = s8_printf(arena, "%d", section_number);
while (it && !s8_are_equal(next_section_number, it->string)) {
b8 duplicate = result.len && s8_are_equal(it->string, array_last(&result).string);
if (!duplicate) {
entry.string = it->string;
array_add(&result, entry);
}
it = it->next;
}
}
return result;
}
fn s8_t srt_find_matching_video(s8_t filename) {
s8_t no_srt = s8_chop_last_period(filename);
if (s8_ends_with(no_srt, s8(".en"))) {
no_srt = s8_chop_last_period(no_srt);
}
char *ext[] = {"mp4", "webm", "mkv"};
for (i32 i = 0; i < lengthof(ext); i += 1) {
s8_t video = s8_printf(&tcx->temp, "%S.%s", no_srt, ext[i]);
if (os_exists(video)) {
return video;
}
}
return s8_null;
}

View File

@@ -1,4 +1,4 @@
// automatically generated using: C:\dev\wasm\src/wasm_app/wasm_app.meta.c
// automatically generated using: C:\dev\wasm\src/prototype/prototype.meta.c
gb f32 font_size = 30;
gb f32 _font_size = 30;
gb_read_only mt_tweak_t tweak_table[] = {
@@ -7,9 +7,5 @@ gb_read_only mt_tweak_t tweak_table[] = {
};
void run_all_tests(void) {
test_string16();
test_hash_table();
test_intern_table();
test_array();
ui_test_text_replace();
}// run_all_tests()

View File

@@ -1,7 +1,87 @@
void generate_wasm_app_code(ma_arena_t *arena) {
#include "../prototype/parse_srt.c"
void generate_transcript(s8_t folder) {
ma_temp_t scratch = ma_begin_scratch();
ma_arena_t *arena = ma_create(ma_default_reserve_size);
arena->align = 0;
array_srt_entry_t entries = {malo(scratch.arena)};
for (os_iter_t *iter = os_iter(scratch.arena, folder); iter->is_valid; os_advance(iter)) {
if (iter->is_directory) {
continue;
}
if (!s8_ends_with(iter->abs, s8(".srt"))) {
continue;
}
array_srt_entry_t srt = srt_parse(scratch.arena, iter->abs);
for (i64 i = 0; i < srt.len; i += 1) {
srt_entry_t *it = srt.data + i;
char *temp_string = ma_push_array(scratch.arena, char, it->string.len + 1);
i64 i1 = 0;
for (i64 i0 = 0; i0 < it->string.len; i0 += 1) {
if (it->string.str[i0] == '"' || it->string.str[i0] == '\'' ||
it->string.str[i0] == ',' || it->string.str[i0] == '.' ||
it->string.str[i0] == '\\') {
continue;
} else if (it->string.str[i0] == '-') {
temp_string[i1++] = ' ';
continue;
}
temp_string[i1++] = char_to_lower_case(it->string.str[i0]);
}
it->string = s8_copy(arena, s8_make(temp_string, i1));
it->string.str[it->string.len] = ' ';
array_add(&entries, *it);
}
}
{
s8_t text = s8_make(arena->data + arena->base_len, arena->len - arena->base_len);
sb8_t *sb = sb8_serial_begin(scratch.arena);
sb8_printf(sb, "s8_t the_text_itself = s8(\"%S\");\n", text);
sb8_printf(sb, "typedef struct srt_packed_t srt_packed_t;\n");
sb8_printf(sb, "struct srt_packed_t {\n");
sb8_printf(sb, " u16 hour, minute, second;\n");
sb8_printf(sb, " u16 file_idx;\n");
sb8_printf(sb, " u32 start, len;\n");
sb8_printf(sb, "};\n");
sb8_printf(sb, "s8_t the_filenames[] = {\n");
i32 idx = 0;
for (i64 i = 1; i < entries.len; i += 1) {
srt_entry_t *prev_it = entries.data + i - 1;
srt_entry_t *it = entries.data + i;
if (!s8_are_equal(it->filepath, prev_it->filepath) || i == 1) {
sb8_printf(sb, "s8(\"%S\"),\n", s8_chop_last_period(s8_chop_last_period(s8_skip_to_last_slash(it->filepath))));
idx += 1;
}
it->filepath_index = idx - 1;
}
entries.data[0].filepath_index = 1 - 1;
sb8_printf(sb, "};\n");
sb8_printf(sb, "srt_packed_t all_the_entries[] = {\n");
for (i64 i = 0; i < entries.len; i += 1) {
srt_entry_t *it = entries.data + i;
sb8_printf(sb, "{%u, %u, %u, %u, %u, %d},\n", it->hour, it->minute, it->second, it->filepath_index, (it->string.str - text.str), it->string.len);
}
sb8_printf(sb, "};\n");
s8_t text_entries = sb8_serial_end(scratch.arena, sb);
os_write(s8("entries.inc"), text_entries);
}
ma_destroy(arena);
ma_end_scratch(scratch);
}
void generate_prototype_code(ma_arena_t *arena) {
generate_transcript(folder_to_create_transcript_for);
sb8_t *include_paths = sb8(arena);
sb8_append(include_paths, OS_GetAbsolutePath(&Perm, s8("../src")));
mt_files_t files = mt_lex_files(arena, s8("../src/wasm_app/main.c"), include_paths);
sb8_append(include_paths, os_abs(arena, s8("../src")));
mt_files_t files = mt_lex_files(arena, s8("../src/prototype/main.c"), include_paths);
typedef struct cg_tweak_t cg_tweak_t;
struct cg_tweak_t {
@@ -118,5 +198,5 @@ void generate_wasm_app_code(ma_arena_t *arena) {
s8_t embeds = sb8_serial_end(arena, sb_embeds);
os_write_file(mt_cpath(arena), embeds);
os_write(mt_cpath(arena), embeds);
}

View File

@@ -0,0 +1,190 @@
typedef array(s8_t) array_s8_t;
typedef struct res_t res_t;
struct res_t {
array_s8_t error_messages;
array_s8_t search_matches;
ui_text_input_t search_text_input;
char search_prompt[256];
i64 search_idx;
};
fn void res_init(res_t *res) {
array_init(malo(&tcx->perm), &res->search_matches, 1000);
array_init(malo(&tcx->perm), &res->error_messages, 100);
}
fn void res_unload(res_t *res) {
}
fn void res_reload(res_t *res) {
}
#define res_report(res, ...) array_add(&(res)->error_messages, s8_printf(&(tcx)->perm, __VA_ARGS__))
fn void res_search_matches(res_t *res) {
if (res->search_idx == 0) {
res->search_matches.len = 0;
}
if (res->search_text_input.len == 0) {
return;
}
const i64 search_iter = 4096*16;
for (i64 i = res->search_idx; i < res->search_idx + search_iter && i < the_text_itself.len; i += 1) {
s8_t text = s8_skip(the_text_itself, i);
if (s8_starts_with_ex(text, res->search_text_input.string, true)) {
s8_t found = s8_make(text.str, res->search_text_input.len);
array_add(&res->search_matches, found);
}
}
res->search_idx += search_iter;
}
fn srt_entry_t res_find_entry(res_t *res, s8_t res_string) {
srt_entry_t result = {0};
for (i64 i = 0; i < lengthof(all_the_entries); i += 1) {
srt_packed_t *it = all_the_entries + i;
s8_t string = s8_make(the_text_itself.str + it->start, it->len);
usize begin = (usize)(string.str);
usize end = (usize)(string.str + string.len);
usize needle = (usize)(res_string.str);
if (needle >= begin && needle < end) {
debugf("file idx = %u", it->file_idx);
result.string = string;
result.filepath = the_filenames[it->file_idx];
result.hour = it->hour;
result.minute = it->minute;
result.second = it->second;
}
}
return result;
}
///////////////////////////////
// update
fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i32 tweak_count) {
ui_begin_frame(frame);
rn_begin_frame(frame);
res_t *res = (res_t *)tcx->user_ctx;
res_search_matches(res);
for (app_event_t *ev = frame->first_event; ev; ev = ev->next) {
ui_begin_build(UILOC, ev, window_rect_from_frame(frame));
locl b8 set_focus;
ui_signal_t text_input_sig;
res->search_text_input.str = res->search_prompt;
res->search_text_input.cap = lengthof(res->search_prompt);
ui_box_t *top_box = ui_box(.null_id = true, .rect = r2f32_cut_top(ui_top_rectp(), ui_em(1.0f)), .flags = {.draw_rect = true, .clip_rect = true});
ui_set_text_align(ui_text_align_left)
ui_set_lop(ui_lop_cut_top)
ui_set_top(top_box) {
text_input_sig = ui_text_input(&res->search_text_input, .sim_even_if_no_focus = true, .keyboard_nav = false);
}
ma_temp_t scratch = ma_begin_scratch();
array_s8_t matches = res->search_matches;
if (matches.len == 0 || res->search_text_input.len == 0) {
matches = res->error_messages;
}
ui_box_t *lister = ui_box(.null_id = true, .rect = r2f32_cut_top(ui_top_rectp(), ui_max), .flags = {.draw_rect = true, .clip_rect = true});
locl f32 verti_scroller_value;
ui_scroller_t scroller = ui_begin_scroller(UILOC, (ui_scroller_params_t){
.parent = lister,
.verti = {
.enabled = true,
.value = &verti_scroller_value,
.item_pixels = ui_em(1),
.item_count = (i32)matches.len,
},
});
ui_set_top(lister)
ui_set_string_pos_offset(ui_dm(1)) {
ui_cut_top_scroller_offset(scroller);
for (i32 i = scroller.verti.istart; i < scroller.verti.iend; i += 1) {
if (i >= matches.len) {
break;
}
ui_push_id((ui_id_t){i});
s8_t it = matches.data[i];
uintptr_t begin_region = (uintptr_t)the_text_itself.str;
uintptr_t end_region = (uintptr_t)the_text_itself.str + the_text_itself.len;
u64 chars_per_line = (i64)(frame->window_size.x) / (i64)ui_dm(1);
u64 begin = (uintptr_t)(it.str - chars_per_line / 2);
u64 end = (uintptr_t)(it.str + it.len + chars_per_line / 2);
u64 a = CLAMP(begin, begin_region, end_region);
u64 b = CLAMP((uintptr_t)it.str, begin_region, end_region);
s8_t left = {(char *)a, (i64)(b - a)};
u64 c = CLAMP((uintptr_t)(it.str + it.len), begin_region, end_region);
u64 d = CLAMP(end, begin_region, end_region);
s8_t right = {(char *)c, (i64)(d - c)};
s8_t middle = it;
s8_t string = s8_printf(scratch.arena, "%S**%S**%S", left, middle, right);
ui_box_t *box = ui_box(.string = string, .flags = {.set_click_cursor = res->search_text_input.len != 0, .draw_rect = true, .draw_text = true, .keyboard_nav = true});
ui_signal_t sig = ui_signal_from_box(box);
if (sig.clicked && matches.data != res->error_messages.data) {
srt_entry_t entry = res_find_entry(res, middle);
int seconds = entry.hour * 60 * 60 + entry.minute * 60 + entry.second;
s8_t id = s8_get_postfix(entry.filepath, 13);
id = s8_skip(id, 1);
id = s8_chop(id, 1);
s8_t url = s8_printf(scratch.arena, "https://youtu.be/%S?feature=shared&t=%d", id, seconds);
open_link(url);
}
b32 right_clicked = (ui->hot.value == box->id.value && ev->kind == app_event_kind_mouse_down && ev->mouse_button == app_mouse_button_right);
b32 copied = (ui->focus.value == box->id.value && ev->kind == app_event_kind_key_down && ev->key == app_key_c && ev->ctrl);
if (right_clicked || copied) {
srt_entry_t entry = res_find_entry(res, middle);
int seconds = entry.hour * 60 * 60 + entry.minute * 60 + entry.second;
s8_t id = s8_get_postfix(entry.filepath, 13);
id = s8_skip(id, 1);
id = s8_chop(id, 1);
s8_t url = s8_printf(scratch.arena, "https://youtu.be/%S?feature=shared&t=%d %S%S%S", id, seconds, left, middle, right);
set_clipboard(url);
}
if (set_focus) {
ui->focus = box->id;
set_focus = false;
}
ui_pop_id();
}
}
if (text_input_sig.text_changed) {
res->search_idx = 0;
set_focus = true;
}
if (text_input_sig.text_commit) {
// if (os_is_abs(res->search_text_input.string)) {
// res_add_folder(res, res->search_text_input.string);
// ui_text_clear(&res->search_text_input);
// }
}
// assert(res->error_messages.cap == 10000);
// assert(res->search_matches.cap == 10000000);
ui_end_scroller(scroller);
ui_end_build();
ma_end_scratch(scratch);
}
rn_begin(white_color);
ui_draw();
rn_end();
ui_end_frame();
}

View File

@@ -0,0 +1,494 @@
///////////////////////////////
// work queue
#define FN_WORK(name) void name(void *data)
typedef FN_WORK(work_queue_callback_t);
typedef struct {
work_queue_callback_t *callback;
void *data;
} work_queue_entry_t;
typedef struct work_queue_t work_queue_t;
struct work_queue_t {
b32 running;
i32 thread_count;
work_queue_entry_t entries[1024];
i64 volatile index_to_write;
i64 volatile index_to_read;
i64 volatile completion_index;
i64 volatile completion_goal;
void *semaphore;
};
typedef struct thread_startup_info_t thread_startup_info_t;
struct thread_startup_info_t {
u32 thread_id;
i32 thread_index;
ma_arena_t *arena;
work_queue_t *queue;
};
fn i64 atomic_increment(volatile i64 *i) {
return InterlockedIncrement64(i);
}
fn i64 atomic_compare_and_swap(volatile i64 *dst, i64 exchange, i64 comperand) {
return InterlockedCompareExchange64(dst, exchange, comperand);
}
fn void work_queue_push(work_queue_t *wq, void *data, work_queue_callback_t *callback) {
u32 new_index = (wq->index_to_write + 1) % lengthof(wq->entries);
assert(new_index != wq->index_to_read);
work_queue_entry_t *entry = wq->entries + wq->index_to_write;
entry->data = data;
entry->callback = callback;
wq->completion_goal += 1;
_WriteBarrier();
wq->index_to_write = new_index;
ReleaseSemaphore(wq->semaphore, 1, 0);
}
fn b8 work_queue_try_doing_work(work_queue_t *wq) {
b8 should_sleep = false;
i64 original_index_to_read = wq->index_to_read;
i64 new_index_to_read = (original_index_to_read + 1) % lengthof(wq->entries);
if (original_index_to_read != wq->index_to_write) {
i64 index = atomic_compare_and_swap(&wq->index_to_read, new_index_to_read, original_index_to_read);
if (index == original_index_to_read) {
work_queue_entry_t *entry = wq->entries + index;
entry->callback(entry->data);
atomic_increment(&wq->completion_index);
}
} else {
should_sleep = true;
}
return should_sleep;
}
fn DWORD WINAPI work_queue_thread_entry(LPVOID param) {
thread_startup_info_t *ti = (thread_startup_info_t *)param;
os_core_small_init(ti->arena);
tcx->thread_index = ti->thread_index;
for (;ti->queue->running;) {
if (work_queue_try_doing_work(ti->queue)) {
WaitForSingleObject(ti->queue->semaphore, INFINITE);
}
}
ExitThread(0);
}
fn void work_queue_init(work_queue_t *queue, thread_startup_info_t *info, u32 thread_count) {
queue->running = true;
queue->thread_count = thread_count;
queue->index_to_read = 0;
queue->index_to_write = 0;
queue->completion_index = 0;
queue->completion_goal = 0;
queue->semaphore = CreateSemaphoreExA(0, 0, thread_count, 0, 0, SEMAPHORE_ALL_ACCESS);
assert(queue->semaphore != INVALID_HANDLE_VALUE);
for (u32 i = 0; i < thread_count; i++) {
thread_startup_info_t *ti = info + i;
ti->arena = &tcx->perm;
ti->thread_index = i;
ti->queue = queue;
DWORD thread_id = 0;
HANDLE thread_handle = CreateThread(0, 0, work_queue_thread_entry, ti, 0, &thread_id);
assert(thread_handle != INVALID_HANDLE_VALUE);
ti->thread_id = thread_id;
CloseHandle(thread_handle);
}
}
fn b8 work_queue_is_complete(work_queue_t *wq) {
b8 result = wq->completion_goal == wq->completion_index;
return result;
}
fn void work_queue_wait(work_queue_t *wq) {
while (!work_queue_is_complete(wq)) {
work_queue_try_doing_work(wq);
}
}
fn void work_queue_stop(work_queue_t *wq) {
wq->running = false;
work_queue_wait(wq);
}
///////////////////////////////
// process
typedef struct process_t process_t;
struct process_t {
b8 is_valid;
char platform[32];
};
fn process_t process_run(s8_t args) {
ma_temp_t scratch = ma_begin_scratch();
wchar_t *application_name = NULL;
wchar_t *cmd = s16_from_s8(scratch.arena, args).str;
BOOL inherit_handles = FALSE;
DWORD creation_flags = 0;
void *enviroment = NULL;
wchar_t *working_dir = NULL;
STARTUPINFOW startup_info = {};
startup_info.cb = sizeof(STARTUPINFOW);
process_t result = {};
assert(sizeof(result.platform) >= sizeof(PROCESS_INFORMATION));
PROCESS_INFORMATION *process_info = (PROCESS_INFORMATION *)result.platform;
BOOL success = CreateProcessW(application_name, cmd, NULL, NULL, inherit_handles, creation_flags, enviroment, working_dir, &startup_info, process_info);
result.is_valid = true;
if (!success) {
result.is_valid = false;
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
char *buff = (char *)lpMsgBuf;
size_t buffLen = strlen((const char *)buff);
if (buffLen > 0 && buff[buffLen - 1] == '\n') {
buff[buffLen - 1] = 0;
}
debugf("failed to create process | message: %s | cmd: %s", (char *)lpMsgBuf, args);
LocalFree(lpMsgBuf);
}
ma_end_scratch(scratch);
return result;
}
///////////////////////////////
// mutex
typedef struct mutex_t mutex_t;
struct mutex_t {
void *platform;
};
fn mutex_t mutex_create(void) {
HANDLE handle = CreateMutex(NULL, FALSE, NULL);
assert(handle);
mutex_t result = (mutex_t){(void *)handle};
return result;
}
fn void mutex_destroy(mutex_t mutex) {
BOOL result = CloseHandle(mutex.platform);
assert(result != 0);
unused(result);
}
fn void mutex_lock(mutex_t mutex) {
DWORD result = WaitForSingleObject(mutex.platform, INFINITE);
assert(result == WAIT_OBJECT_0);
unused(result);
}
fn void mutex_unlock(mutex_t mutex) {
BOOL result = ReleaseMutex(mutex.platform);
assert(result != 0);
unused(result);
}
///////////////////////////////
// multithreaded resource loading
typedef array(s8_t) array_s8_t;
typedef struct res_parse_work_t res_parse_work_t;
typedef struct res_t res_t;
struct res_parse_work_t {
s8_t srt;
res_t *res;
};
struct res_t {
ma_arena_t misc_arena;
array_s8_t error_messages;
mutex_t mutex;
ma_arena_t text_arena;
ma_arena_t node_arena;
array_srt_entry_t mapping_entries;
thread_startup_info_t thread_info[16];
work_queue_t work_queue;
mutex_t log_mutex;
i64 search_stop_counter;
mutex_t search_mutex;
array_s8_t search_matches;
ui_text_input_t search_text_input;
char search_prompt[256];
};
#define res_report(res, ...) (mutex_lock((res)->log_mutex), array_add(&(res)->error_messages, s8_printf(&(res)->misc_arena, __VA_ARGS__)), mutex_unlock((res)->log_mutex))
fn void res_unload(res_t *res) {
work_queue_stop(&res->work_queue);
}
fn void res_reload(res_t *res) {
work_queue_init(&res->work_queue, res->thread_info, lengthof(res->thread_info));
}
fn void res_init(res_t *res) {
res_reload(res);
ma_init(&res->text_arena, ma_default_reserve_size);
ma_init(&res->node_arena, ma_default_reserve_size);
ma_init(&res->misc_arena, ma_default_reserve_size);
res->log_mutex = mutex_create();
res->mutex = mutex_create();
res->search_mutex = mutex_create();
array_init(malo(&res->misc_arena), &res->search_matches, 10000000);
array_init(malo(&res->misc_arena), &res->error_messages, 10000);
res->text_arena.align = 0;
}
FN_WORK(res_parse_thread) {
res_parse_work_t *pair = (res_parse_work_t *)data;
ma_temp_t scratch = ma_begin_scratch();
array_srt_entry_t srt = srt_parse(scratch.arena, pair->srt);
mutex_lock(pair->res->mutex);
pair->res->mapping_entries.alo = malo(&pair->res->node_arena);
for (i64 i = 0; i < srt.len; i += 1) {
srt_entry_t it = srt.data[i];
it.filepath = s8_copy(&pair->res->node_arena, it.filepath);
it.string = s8_copy(&pair->res->text_arena, it.string);
it.string.str[it.string.len] = ' ';
array_add(&pair->res->mapping_entries, it);
}
mutex_unlock(pair->res->mutex);
ma_end_scratch(scratch);
if (srt.len) {
res_report(pair->res, "%S", pair->srt);
} else {
res_report(pair->res, "failed to load: %S", pair->srt);
}
}
fn void res_add_folder(res_t *res, s8_t folder) {
ma_temp_t scratch = ma_begin_scratch();
if (!os_is_dir(folder)) {
res_report(res, "failed to find folder: %S", folder);
}
for (os_iter_t *iter = os_iter(scratch.arena, folder); iter->is_valid; os_advance(iter)) {
if (iter->is_directory) {
continue;
}
if (!s8_ends_with(iter->abs, s8(".srt"))) {
continue;
}
res_parse_work_t *work = ma_push_type(&res->misc_arena, res_parse_work_t);
work->srt = iter->abs;
work->res = res;
work_queue_push(&res->work_queue, work, res_parse_thread);
}
ma_end_scratch(scratch);
}
fn srt_entry_t *res_find_entry(res_t *res, s8_t res_string) {
srt_entry_t *result = NULL;
mutex_lock(res->mutex);
for (srt_entry_t *it = res->mapping_entries.data; it != res->mapping_entries.data + res->mapping_entries.len; it += 1) {
u64 begin = (u64)(it->string.str);
u64 end = (u64)(it->string.str + it->string.len);
u64 needle = (u64)(res_string.str);
if (needle >= begin && needle < end) {
result = it;
break;
}
}
mutex_unlock(res->mutex);
return result;
}
fn s8_t res_get_string(res_t *res) {
mutex_lock(res->mutex);
s8_t result = (s8_t){res->text_arena.data, res->text_arena.len};
mutex_unlock(res->mutex);
return result;
}
///////////////////////////////
// search thread
FN_WORK(res_search_thread) {
res_t *res = (res_t *)data;
if (res->search_text_input.len == 0) {
return;
}
i64 search_stop_counter = res->search_stop_counter;
mutex_lock(res->search_mutex);
res->search_matches.len = 0;
mutex_unlock(res->search_mutex);
s8_t buffer = res_get_string(res);
s8_t find = res->search_text_input.string;
i64 index = 0;
while (s8_seek(buffer, find, s8_seek_ignore_case, &index)) {
if (search_stop_counter != res->search_stop_counter) {
break;
}
assert(res->search_matches.cap == 10000000);
s8_t found = s8_make(buffer.str + index, find.len);
array_add(&res->search_matches, found);
buffer = s8_skip(buffer, index + find.len);
}
}
fn void res_search_start(res_t *res) {
res->search_stop_counter += 1;
work_queue_push(&res->work_queue, res, res_search_thread);
}
fn array_s8_t res_search_get(ma_arena_t *arena, res_t *res) {
mutex_lock(res->search_mutex);
array_s8_t copy = res->search_matches;
copy.data = ma_push_array_copy(arena, copy.data, copy.len);
mutex_unlock(res->search_mutex);
return copy;
}
// @todo @ui scrolling when focused button goes out of screen
///////////////////////////////
// update
fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i32 tweak_count) {
ui_begin_frame(frame);
rn_begin_frame(frame);
res_t *res = (res_t *)tcx->user_ctx;
for (app_event_t *ev = frame->first_event; ev; ev = ev->next) {
ui_begin_build(UILOC, ev, window_rect_from_frame(frame));
locl b8 set_focus;
ui_signal_t text_input_sig;
res->search_text_input.str = res->search_prompt;
res->search_text_input.cap = lengthof(res->search_prompt);
ui_box_t *top_box = ui_box(.null_id = true, .rect = r2f32_cut_top(ui_top_rectp(), ui_em(1.0f)), .flags = {.draw_rect = true, .clip_rect = true});
ui_set_text_align(ui_text_align_left)
ui_set_lop(ui_lop_cut_top)
ui_set_top(top_box) {
text_input_sig = ui_text_input(&res->search_text_input, .sim_even_if_no_focus = true, .keyboard_nav = false);
}
ma_temp_t scratch = ma_begin_scratch();
array_s8_t matches = res_search_get(scratch.arena, res);
if (matches.len == 0 || res->search_text_input.len == 0) {
// @concurrency not sure if this is ok
matches = res->error_messages;
}
ui_box_t *lister = ui_box(.null_id = true, .rect = r2f32_cut_top(ui_top_rectp(), ui_max), .flags = {.draw_rect = true, .clip_rect = true});
locl f32 verti_scroller_value;
ui_scroller_t scroller = ui_begin_scroller(UILOC, (ui_scroller_params_t){
.parent = lister,
.verti = {
.enabled = true,
.value = &verti_scroller_value,
.item_pixels = ui_em(1),
.item_count = (i32)matches.len,
},
});
ui_set_top(lister)
ui_set_string_pos_offset(ui_dm(1)) {
ui_cut_top_scroller_offset(scroller);
for (i32 i = scroller.verti.istart; i < scroller.verti.iend; i += 1) {
if (i >= matches.len) {
break;
}
ui_push_id((ui_id_t){i});
s8_t it = matches.data[i];
uintptr_t begin_region = (uintptr_t)res->text_arena.data;
uintptr_t end_region = (uintptr_t)res->text_arena.data + res->text_arena.len;
u64 chars_per_line = (i64)(frame->window_size.x) / (i64)ui_dm(1);
u64 begin = (uintptr_t)(it.str - chars_per_line / 2);
u64 end = (uintptr_t)(it.str + it.len + chars_per_line / 2);
u64 a = CLAMP(begin, begin_region, end_region);
u64 b = CLAMP((uintptr_t)it.str, begin_region, end_region);
s8_t left = {(char *)a, (i64)(b - a)};
u64 c = CLAMP((uintptr_t)(it.str + it.len), begin_region, end_region);
u64 d = CLAMP(end, begin_region, end_region);
s8_t right = {(char *)c, (i64)(d - c)};
s8_t middle = it;
s8_t string = s8_printf(scratch.arena, "%S**%S**%S", left, middle, right);
ui_box_t *box = ui_box(.string = string, .flags = {.draw_rect = true, .draw_text = true, .keyboard_nav = true});
ui_signal_t sig = ui_signal_from_box(box);
if (sig.clicked && matches.data != res->error_messages.data) {
srt_entry_t *entry = res_find_entry(res, middle);
s8_t video = srt_find_matching_video(entry->filepath);
if (video.len == 0) {
res_report(res, "failed to find video for: %S", entry->filepath);
} else {
int seconds = entry->hour * 60 * 60 + entry->minute * 60 + entry->second;
for (i64 i = 0; i < video.len; i += 1) {
if (video.str[i] == '/') video.str[i] = '\\';
}
s8_t cmd = s8_printf(scratch.arena, "\"C:/Program Files/VideoLAN/VLC/vlc.exe\" --start-time %d \"%S\"", seconds, video);
process_run(cmd);
}
}
if (set_focus) {
ui->focus = box->id;
set_focus = false;
}
ui_pop_id();
}
}
if (text_input_sig.text_changed) {
res_search_start(res);
set_focus = true;
}
if (text_input_sig.text_commit) {
if (os_is_abs(res->search_text_input.string)) {
res_add_folder(res, res->search_text_input.string);
ui_text_clear(&res->search_text_input);
}
}
assert(res->error_messages.cap == 10000);
assert(res->search_matches.cap == 10000000);
ui_end_scroller(scroller);
ui_end_build();
ma_end_scratch(scratch);
}
rn_begin(white_color);
ui_draw();
rn_end();
ui_end_frame();
}

View File

@@ -10,7 +10,7 @@
#define STBTT_acos(x) (f64_acos(x))
#define STBTT_fabs(x) (f64_abs(x))
#define STBTT_assert(x) assert(x)
#define STBTT_malloc(x,u) (ma_push_size(tcx->temp, x))
#define STBTT_malloc(x,u) (ma_push_size(&tcx->temp, x))
#define STBTT_free(x,u)
#define STBTT_strlen(x) (str_len(x))
#define STBTT_memcpy memory_copy

View File

@@ -8,7 +8,7 @@ fn rn_cmd_t *rn_get_cmd(rn_cmd_kind_t kind) {
rn_cmd_t *result = rn->last_cmd;
if (alloc_new) {
result = ma_push_type(tcx->temp, rn_cmd_t);
result = ma_push_type(&tcx->temp, rn_cmd_t);
result->kind = kind;
SLLQ_APPEND(rn->first_cmd, rn->last_cmd, result);
@@ -115,7 +115,7 @@ fn v2f32_t rn_draw_string(rn_font_t *font, v2f32_t pos, v4f32_t color, s8_t stri
}
fn v2f32_t rn_draw_stringf(rn_font_t *font, v2f32_t pos, v4f32_t color, char *str, ...) {
S8_FMT(tcx->temp, str, result);
S8_FMT(&tcx->temp, str, result);
return rn_draw_string(font, pos, color, result);
}

View File

@@ -56,14 +56,14 @@ fn void rn_reload_font(f32 font_size, f32 dpr) {
}
fn void rn_reload(void) {
if (!gladLoadGLLoader((GLADloadproc)tcx->data[tcx_slot_app])) {
if (!gladLoadGLLoader((GLADloadproc)tcx->app_ctx)) {
fatalf("couldn't load opengl!");
}
}
fn void rn_init(ma_arena_t *perm, f32 font_size, f32 dpr) {
tcx->data[tcx_slot_rn] = ma_push_type(perm, rn_state_t);
rn = tcx->data[tcx_slot_rn];
tcx->rn_ctx = ma_push_type(perm, rn_state_t);
rn = tcx->rn_ctx;
rn_reload();
@@ -140,7 +140,7 @@ fn void rn_init(ma_arena_t *perm, f32 font_size, f32 dpr) {
}
void rn_begin_frame(app_frame_t *frame) {
rn = tcx->data[tcx_slot_rn];
rn = tcx->rn_ctx;
rn->frame = frame;
}

View File

@@ -21,8 +21,8 @@ struct rn_state_t {
rn_state_t *rn;
fn void rn_init(ma_arena_t *perm, f32 font_size, f32 dpr) {
tcx->data[tcx_slot_rn] = ma_push_type(perm, rn_state_t);
rn = tcx->data[tcx_slot_rn];
tcx->rn_ctx = ma_push_type(perm, rn_state_t);
rn = tcx->rn_ctx;
rn->main_font = ma_push_type(perm, rn_font_t);
rn_reload_font(font_size, dpr);
@@ -60,7 +60,7 @@ fn v2f32_t rn_draw_string(rn_font_t *font, v2f32_t pos, v4f32_t color, s8_t stri
}
fn v2f32_t rn_draw_stringf(rn_font_t *font, v2f32_t pos, v4f32_t color, char *str, ...) {
S8_FMT(tcx->temp, str, string);
S8_FMT(&tcx->temp, str, string);
return rn_draw_string(font, pos, color, string);
}
@@ -69,7 +69,7 @@ fn void rn_set_clip(r2f32_t rect) {
}
fn void rn_begin_frame(app_frame_t *frame) {
rn = tcx->data[tcx_slot_rn];
rn = tcx->rn_ctx;
rn->frame = frame;
}

View File

@@ -1,10 +1,2 @@
void test_string16(void);
void test_hash_table(void);
void test_intern_table(void);
void test_array(void);
fn void run_tests(void) {
test_string16();
test_hash_table();
test_intern_table();
test_array();
}

View File

@@ -11,12 +11,12 @@ fn void os_test(void) {
os_date_t local_time = os_local_time();
os_date_t universal_time = os_universal_time();
unused(universal_time); unused(local_time);
// debugf("OS local_time = %S | universal_time = %S", os_format_date(tcx->temp, local_time), os_format_date(tcx->temp, universal_time));
// debugf("OS local_time = %S | universal_time = %S", os_format_date(&tcx->temp, local_time), os_format_date(&tcx->temp, universal_time));
s8_t exe_dir = os_exe_dir(tcx->temp);
s8_t exe_dir = os_exe_dir(&tcx->temp);
assert(exe_dir.str[exe_dir.len - 1] == '/');
s8_t exe = os_exe(tcx->temp);
s8_t cwd = os_cwd(tcx->temp);
s8_t exe = os_exe(&tcx->temp);
s8_t cwd = os_cwd(&tcx->temp);
assert(cwd.str[cwd.len - 1] == '/');
assert(os_is_dir(exe_dir));
assert(os_is_file(exe));
@@ -30,7 +30,7 @@ fn void os_test(void) {
assert(!os_is_abs(s8("../path/")));
// debugf("OS paths %S %S %S", exe_dir, exe, cwd);
s8_t file = os_read(tcx->temp, s8("../.gitignore"));
s8_t file = os_read(&tcx->temp, s8("../.gitignore"));
assert(file.str != 0);
assert(file.len != 0);
@@ -140,9 +140,9 @@ fn void test_s8(void) {
}
int main() {
core_init();
os_core_init();
run_tests();
test_s8();
os_test();
return 0;
}
}

View File

@@ -1,14 +0,0 @@
// automatically generated using: C:\dev\wasm\src/text_editor/text_editor.meta.c
gb f32 font_size = 30;
gb f32 _font_size = 30;
gb_read_only mt_tweak_t tweak_table[] = {
{type(f32), s8_const("font_size"), &font_size, 4, 200},
{type(f32), s8_const("_font_size"), &_font_size, 30, 30},
};
void run_all_tests(void) {
test_string16();
test_hash_table();
test_intern_table();
test_array();
}// run_all_tests()

View File

@@ -1,122 +0,0 @@
void generate_text_editor_code(ma_arena_t *arena) {
sb8_t *include_paths = sb8(arena);
sb8_append(include_paths, OS_GetAbsolutePath(&Perm, s8("../src")));
mt_files_t files = mt_lex_files(arena, s8("../src/text_editor/text_editor_main.c"), include_paths);
typedef struct cg_tweak_t cg_tweak_t;
struct cg_tweak_t {
cg_tweak_t *next;
type_t *type;
s8_t name;
s8_t value;
s8_t min, max;
};
cg_tweak_t *first_tweak = NULL;
cg_tweak_t *last_tweak = NULL;
sb8_t *tests = sb8_serial_begin(arena);
sb8_t *sb_embeds = sb8_serial_begin(arena);
sb8_printf(sb_embeds, "// automatically generated using: " __FILE__ "\n");
for (mt_file_t *it = files.first; it; it = it->next) {
parser_t *par = parser_make(arena, it->tokens.data);
for (;par->at->kind != lex_kind_eof;) {
b32 matched = false;
if (par->at->inside_macro == false && parser_matchi(par, s8("fn_test"))) {
parser_expecti(par, s8("void"));
lex_t *ident = parser_match(par, lex_kind_ident);
sb8_append(tests, ident->string);
matched = true;
}
if (par->at->inside_macro == false && parser_matchi(par, s8("mt_embed_file"))) {
parser_expect(par, lex_kind_open_paren);
lex_t *var_name = parser_expect(par, lex_kind_ident);
parser_expect(par, lex_kind_comma);
lex_t *path = parser_expect(par, lex_kind_string);
parser_expect(par, lex_kind_close_paren);
s8_t relpath = s8_printf(arena, "../%S", path->string);
s8_t content = OS_ReadFile(&Perm, relpath);
sb8_printf(sb_embeds, "gb_read_only ");
mt_serial_to_cbyte_array_ex(sb_embeds, content, s8_printf(arena, "%S_data", var_name->string));
sb8_printf(sb_embeds, "gb_read_only s8_t %S = s8_array_lit(%S_data);\n", var_name->string, var_name->string);
matched = true;
}
if (par->at->inside_macro == false && parser_matchi(par, s8("mt_tweak_b32"))) {
cg_tweak_t *tweak = ma_push_type(arena, cg_tweak_t);
tweak->type = type(b32);
tweak->min = tweak->max = s8("0");
parser_expect(par, lex_kind_open_paren);
tweak->name = parser_expect(par, lex_kind_ident)->string;
parser_expect(par, lex_kind_comma);
tweak->value = parser_next(par)->string;
parser_expect(par, lex_kind_close_paren);
SLLQ_APPEND(first_tweak, last_tweak, tweak);
matched = true;
}
if (par->at->inside_macro == false && parser_matchi(par, s8("mt_tweak_f32"))) {
cg_tweak_t *tweak = ma_push_type(arena, cg_tweak_t);
tweak->type = type(f32);
parser_expect(par, lex_kind_open_paren);
tweak->name = parser_expect(par, lex_kind_ident)->string;
parser_expect(par, lex_kind_comma);
tweak->value = parser_next(par)->string;
parser_expect(par, lex_kind_comma);
tweak->min = parser_next(par)->string;
parser_expect(par, lex_kind_comma);
tweak->max = parser_next(par)->string;
parser_expect(par, lex_kind_close_paren);
SLLQ_APPEND(first_tweak, last_tweak, tweak);
matched = true;
}
if (par->at->inside_macro == false && parser_matchi(par, s8("mt_tweak_color"))) {
cg_tweak_t *tweak = ma_push_type(arena, cg_tweak_t);
tweak->type = type(v4f32_t);
tweak->min = s8("0.0f");
tweak->max = s8("1.0f");
parser_expect(par, lex_kind_open_paren);
tweak->name = parser_expect(par, lex_kind_ident)->string;
parser_expect(par, lex_kind_comma);
tweak->value = parser_next(par)->string;
while (par->at->kind != lex_kind_close_paren && par->at->kind != lex_kind_eof) parser_next(par);
lex_t *end = parser_expect(par, lex_kind_close_paren);
tweak->value.len = (i64)(end->str - tweak->value.str);
SLLQ_APPEND(first_tweak, last_tweak, tweak);
matched = true;
}
if (!matched) {
parser_next(par);
}
}
}
if (first_tweak != NULL) {
for (cg_tweak_t *it = first_tweak; it; it = it->next) {
sb8_printf(sb_embeds, "gb %S %S = %S;\n", it->type->name, it->name, it->value);
}
sb8_printf(sb_embeds, "gb_read_only mt_tweak_t tweak_table[] = {\n");
for (cg_tweak_t *it = first_tweak; it; it = it->next) {
sb8_printf(sb_embeds, " {type(%S), s8_const(\"%S\"), &%S, %S, %S},\n", it->type->name, it->name, it->name, it->min, it->max);
}
sb8_printf(sb_embeds, "\n};\n");
}
sb8_printf(sb_embeds, "void run_all_tests(void) {\n");
for (sb8_node_t *it = tests->first; it; it = it->next) {
sb8_printf(sb_embeds, " %S();\n", it->string);
}
sb8_printf(sb_embeds, "}// run_all_tests()\n");
s8_t embeds = sb8_serial_end(arena, sb_embeds);
os_write_file(mt_cpath(arena), embeds);
}

View File

@@ -1,89 +0,0 @@
#include "core/core.h"
#include "os/os.h"
#include "app/app.h"
// #include "ui/ui.h"
#include "core/core.c"
#include "os/os.c"
#include "app/app.c"
#include "render/render.c"
// #include "ui/ui.c"
#include "text_editor.gen.c"
/*todo:
* OK Create a complete string16 library
* OK (Win32) Win32 SDL_GetPrefPath, create configuration directory, get path
* Array
*
*
*
* Win32 upload icon
**/
typedef struct caret_t caret_t;
struct caret_t {
i64 ifront;
union {
r1i64_t range;
i64 pos[2];
};
};
typedef struct xy_t xy_t;
struct xy_t {
i64 col;
i64 line;
};
typedef struct buffer_id_t buffer_id_t;
struct buffer_id_t { i64 e; };
typedef struct buffer_t buffer_t;
struct buffer_t {
buffer_id_t id;
union {
s16_t string;
struct {
u16 *data;
i64 len;
};
};
i64 cap;
i64 *line_starts;
i64 line_count;
};
fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
tcx = thread_ctx;
if (frame->first_event->kind == app_event_kind_init) {
run_all_tests();
mt_tweak_f32(font_size, 30, 4, 200);
mt_tweak_f32(_font_size, 30, 30, 30);
rn_init(&tcx->perm, font_size, frame->dpr);
return true;
} else if (frame->first_event->kind == app_event_kind_reload) {
rn_reload();
return true;
} else if (frame->first_event->kind == app_event_kind_unload) {
return true;
}
if (!f32_are_equal(font_size, _font_size)) {
_font_size = font_size;
rn_reload_font(font_size, frame->dpr);
}
rn_begin_frame(frame);
rn_begin(white_color);
rn_end();
return true;
}

950
src/ui/buffer16.c Normal file
View File

@@ -0,0 +1,950 @@
#define BUFFER_DEBUG 1
gb i64 buffer_raw_ids;
fn void buffer16_dealloc_history_entries(buffer16_t *buffer, array_history16_t *entries);
fn void buffer16_dealloc_history_array(buffer16_t *buffer, array_history16_t *entries);
fn void buffer16_multi_cursor_apply_edits(buffer16_t *buffer, array_edit16_t edits);
///////////////////////////////
// caret helpers
///////////////////////////////
fn i64 caret_get_front(caret_t caret) {
i64 result = caret.pos[caret.ifront];
return result;
}
fn i64 caret_get_back(caret_t caret) {
i64 result = caret.pos[(caret.ifront + 1) % 2];
return result;
}
fn caret_t caret_make(i64 front, i64 back) {
caret_t result = {0};
if (front >= back) {
result.range.min = back;
result.range.max = front;
result.ifront = 1;
} else {
result.range.min = front;
result.range.max = back;
result.ifront = 0;
}
return result;
}
fn caret_t caret_set_front(caret_t caret, i64 pos) {
i64 back = caret_get_back(caret);
caret_t result = caret_make(pos, back);
return result;
}
fn caret_t caret_set_back(caret_t caret, i64 pos) {
i64 front = caret_get_front(caret);
caret_t result = caret_make(front, pos);
return result;
}
fn b32 carets_are_equal(caret_t a, caret_t b) {
b32 result = r1i64_are_equal(a.range, b.range) && a.ifront == b.ifront;
return result;
}
fn b32 carets_overlap(caret_t a, caret_t b) {
b32 result = r1i64_overlap(a.range, b.range);
return result;
}
///////////////////////////////
// buffer helpers
///////////////////////////////
fn i64 buffer16_pos_to_line(buffer16_t *buffer, i64 pos) {
// @todo: refactor this to not modify line_starts
assert(buffer->flags.line_starts);
array_add(&buffer->line_starts, buffer->len + 1);
// binary search
i64 low = 0;
// -2 here because we use 2 indices and combine them into one line range so we
// don't want to access last item since that would index past array.
i64 high = buffer->line_starts.len - 2;
i64 result = 0;
while (low <= high) {
i64 mid = low + (high - low) / 2;
r1i64_t range = {buffer->line_starts.data[mid], buffer->line_starts.data[mid + 1]};
if (pos >= range.min && pos < range.max) {
result = mid;
break;
}
if (range.min < pos) {
low = mid + 1;
} else {
high = mid - 1;
}
}
array_pop(&buffer->line_starts);
return result;
}
fn r1i64_t buffer16_get_line_range_full(buffer16_t *buffer, i64 line, i64 *eof) {
assert(line < buffer->line_starts.len);
r1i64_t result = {buffer->line_starts.data[line], buffer->len};
if (line + 1 < buffer->line_starts.len) {
result.max = buffer->line_starts.data[line + 1];
} else {
*eof = 1;
}
return result;
}
fn r1i64_t buffer16_get_line_range(buffer16_t *buffer, i64 line) {
i64 eof = 0;
r1i64_t line_range = buffer16_get_line_range_full(buffer, line, &eof);
line_range.max = line_range.max - 1 + eof;
return line_range;
}
fn xy_t buffer16_pos_to_xy(buffer16_t *buffer, i64 pos) {
i64 eof = 0;
i64 line = buffer16_pos_to_line(buffer, pos);
r1i64_t line_range = buffer16_get_line_range_full(buffer, line, &eof);
i64 col = pos - line_range.min;
xy_t result = {col, line};
return result;
}
fn i64 buffer16_xy_to_pos(buffer16_t *buffer, xy_t xy) {
xy.line = CLAMP(xy.line, 0, buffer->line_starts.len - 1);
i64 eof = 0;
r1i64_t line_range = buffer16_get_line_range_full(buffer, xy.line, &eof);
i64 pos = CLAMP(xy.col + line_range.min, line_range.min, line_range.max);
return pos;
}
fn i64 buffer16_xy_to_pos_without_new_line(buffer16_t *buffer, xy_t xy) {
xy.line = CLAMP(xy.line, 0, buffer->line_starts.len - 1);
i64 eof = 0;
r1i64_t line_range = buffer16_get_line_range_full(buffer, xy.line, &eof);
i64 pos = CLAMP(xy.col + line_range.min, line_range.min, line_range.max - 1 + eof);
return pos;
}
fn s16_t buffer16_get_string(buffer16_t *buffer, r1i64_t range) {
range = r1i64_clamp(range, 0, buffer->len);
s16_t result = s16_make(buffer->str + range.min, r1i64_size(range));
return result;
}
fn s16_t buffer16_get_line_string(buffer16_t *buffer, i64 line) {
r1i64_t range = buffer16_get_line_range(buffer, line);
s16_t string = buffer16_get_string(buffer, range);
return string;
}
fn r1i64_t buffer16_get_range_end(buffer16_t *buffer) {
r1i64_t result = {buffer->len, buffer->len};
return result;
}
fn r1i64_t buffer16_get_range(buffer16_t *buffer) {
r1i64_t result = {0, buffer->len};
return result;
}
fn i64 buffer16_clamp_pos(buffer16_t *buffer, i64 pos) {
i64 result = CLAMP(pos, 0, buffer->len);
return result;
}
fn r1i64_t buffer16_clamp_range(buffer16_t *buffer, r1i64_t range) {
r1i64_t result = {buffer16_clamp_pos(buffer, range.min), buffer16_clamp_pos(buffer, range.max)};
return result;
}
fn i64 buffer16_get_word_start(buffer16_t *buffer, i64 pos) {
pos = CLAMP(pos, (i64)0, buffer->len);
for (i64 i = pos - 1; i >= 0; i -= 1) {
if (!char16_is_word(buffer->str[i]))
break;
pos = i;
}
return pos;
}
fn i64 buffer16_get_word_end(buffer16_t *buffer, i64 pos) {
pos = CLAMP(pos, (i64)0, buffer->len);
for (i64 i = pos;; i += 1) {
pos = i;
// this is because buffer end terminates the loop
// too early and we cannot establish the proper range
// semantics - proper max is one past last index
if (!(i < buffer->len))
break;
if (!char16_is_word(buffer->str[i]))
break;
}
return pos;
}
fn i64 buffer16_get_next_word_end(buffer16_t *buffer, i64 pos) {
pos = CLAMP(pos, (i64)0, buffer->len);
u16 prev = 0;
for (i64 i = pos;; i += 1) {
pos = i;
// this is because buffer end terminates the loop
// too early and we cannot establish the proper range
// semantics - proper max is one past last index
if (!(i < buffer->len))
break;
if (prev == L'\n' || (prev && prev != buffer->str[i]) || char16_is_word(buffer->str[i])) {
break;
}
prev = buffer->str[i];
}
i64 result = prev == L'\n' ? pos : buffer16_get_word_end(buffer, pos);
return result;
}
fn i64 buffer16_get_prev_word_start(buffer16_t *buffer, i64 pos) {
pos = CLAMP(pos, (i64)0, buffer->len);
u16 prev = 0;
i64 i = pos - 1;
for (; i >= 0; i -= 1) {
if (prev == L'\n' || (prev && prev != buffer->str[i]) || char16_is_word(buffer->str[i])) {
break;
}
pos = i;
prev = buffer->str[i];
}
b32 new_line = prev == L'\n';
i64 result = new_line ? pos : buffer16_get_word_start(buffer, pos);
return result;
}
fn i64 buffer16_get_line_start(buffer16_t *buffer, i64 pos, i64 *eof) {
pos = CLAMP(pos, (i64)0, buffer->len);
i64 line = buffer16_pos_to_line(buffer, pos);
r1i64_t range = buffer16_get_line_range_full(buffer, line, eof);
return range.min;
}
fn i64 buffer16_get_line_end(buffer16_t *buffer, i64 pos, i64 *eof) {
pos = CLAMP(pos, (i64)0, buffer->len);
i64 line = buffer16_pos_to_line(buffer, pos);
r1i64_t range = buffer16_get_line_range_full(buffer, line, eof);
return range.max;
}
fn i64 buffer16_get_line_start_full(buffer16_t *buffer, i64 pos) {
pos = CLAMP(pos, (i64)0, buffer->len);
i64 line = buffer16_pos_to_line(buffer, pos);
r1i64_t range = buffer16_get_line_range(buffer, line);
return range.min;
}
fn i64 buffer16_get_line_end_full(buffer16_t *buffer, i64 pos) {
pos = CLAMP(pos, (i64)0, buffer->len);
i64 line = buffer16_pos_to_line(buffer, pos);
r1i64_t range = buffer16_get_line_range(buffer, line);
return range.max;
}
fn r1i64_t buffer16_enclose_word(buffer16_t *buffer, i64 pos) {
r1i64_t result = {buffer16_get_word_start(buffer, pos), buffer16_get_word_end(buffer, pos)};
return result;
}
fn u16 buffer16_get_char(buffer16_t *buffer, i64 pos) {
if (pos >= 0 && pos < buffer->len) {
return buffer->str[pos];
}
return 0;
}
fn i64 buffer16_offset_pos_by_line(buffer16_t *buffer, i64 pos, i64 line_offset) {
xy_t xy = buffer16_pos_to_xy(buffer, pos);
i64 result = buffer16_xy_to_pos_without_new_line(buffer, (xy_t){xy.col, xy.line + line_offset});
return result;
}
///////////////////////////////
// raw buffer operations
///////////////////////////////
fn void buffer16_raw_grow(buffer16_t *buffer, i64 change_size) {
i64 new_size = buffer->len + change_size;
if (new_size > buffer->cap) {
i64 outside = new_size - buffer->cap;
i64 new_cap = (buffer->cap + outside) * 2;
u16 *new_array = alloc_array(buffer->alo, u16, new_cap);
memory_copy(new_array, buffer->data, buffer->len * sizeof(u16));
dealloc(buffer->alo, buffer->data);
buffer->cap = new_cap;
buffer->data = new_array;
}
}
fn void buffer16_raw_offset_all_lines_forward(buffer16_t *buffer, i64 line, i64 *_offset) {
i64 offset = *_offset;
*_offset = 0;
if (offset == 0) return;
for (i64 i = line; i < buffer->line_starts.len; i += 1) {
buffer->line_starts.data[i] += offset;
}
}
fn void buffer16_raw_update_lines(buffer16_t *buffer, r1i64_t range, s16_t string) {
if (buffer->flags.line_starts == false) {
return;
}
i64 min_line_number = buffer16_pos_to_line(buffer, range.min);
assert(min_line_number < buffer->line_starts.len);
// Update lines remove
{
i64 line_offset = 0;
i64 lines_to_remove = min_line_number + 1;
i64 lines_to_remove_count = 0;
for (i64 i = range.min; i < range.max; i += 1) {
u16 c = buffer->data[i];
if (c == '\n') {
lines_to_remove_count += 1;
}
line_offset -= 1;
}
array_deln(&buffer->line_starts, lines_to_remove, lines_to_remove_count);
buffer16_raw_offset_all_lines_forward(buffer, min_line_number + 1, &line_offset);
}
// Update lines add
i64 line_offset = 0;
i64 nl = min_line_number + 1;
for (i64 i = 0; i < string.len; i += 1) {
nl = min_line_number + 1;
b32 next_line_valid = nl < buffer->line_starts.len;
if (string.str[i] == L'\n') {
i64 new_line_pos = range.min + i + 1;
line_offset += 1;
array_insert(&buffer->line_starts, nl, new_line_pos);
buffer16_raw_offset_all_lines_forward(buffer, nl + 1, &line_offset);
min_line_number = nl;
} else if (next_line_valid) {
line_offset += 1;
}
}
buffer16_raw_offset_all_lines_forward(buffer, nl, &line_offset);
}
fn void buffer16_raw_validate_line_starts(buffer16_t *buffer) {
i64 line = 0;
for (i64 i = 0; i < buffer->len; i += 1) {
i64 l = buffer16_pos_to_line(buffer, i);
assert(l == line);
if (buffer->data[i] == L'\n') line += 1;
}
}
fn void buffer16_raw_replace_text(buffer16_t *buffer, r1i64_t range, s16_t string) {
assert(range.max >= range.min);
assert(range.max >= 0 && range.max <= buffer->len);
assert(range.min >= 0 && range.min <= buffer->len);
buffer->flags.dirty = true;
buffer->change_id += 1;
i64 size_to_remove = range.max - range.min;
i64 size_to_add = string.len;
i64 change_size = size_to_add - size_to_remove;
assert(change_size + buffer->len >= 0);
buffer16_raw_grow(buffer, change_size);
i64 range_size = range.max - range.min;
u16 *begin_remove = buffer->data + range.min;
u16 *end_remove = begin_remove + range_size;
i64 remain_len = buffer->len - (range.min + range_size);
buffer16_raw_update_lines(buffer, range, string);
u16 *begin_add = begin_remove;
u16 *end_add = begin_add + string.len;
memory_move(end_add, end_remove, remain_len * sizeof(u16));
memory_copy(begin_add, string.str, string.len * sizeof(u16));
buffer->len = buffer->len + change_size;
#if BUFFER_DEBUG
buffer16_raw_validate_line_starts(buffer);
#endif
}
///////////////////////////////
// buffer multicursor + history
///////////////////////////////
fn void buffer16_init(alo_t alo, buffer16_t *buffer, s8_t name, i64 size) {
buffer->id = (buffer16_id_t){++buffer_raw_ids};
buffer->name = name;
buffer->alo = alo;
buffer->cap = size;
buffer->data = alloc_array(alo, u16, size);
array_init(alo, &buffer->line_starts, 128);
array_init(alo, &buffer->undo_stack, 128);
array_init(alo, &buffer->redo_stack, 128);
buffer->flags.line_starts = true;
buffer->flags.history = true;
array_add(&buffer->line_starts, 0);
}
fn void buffer16_deinit(buffer16_t *buffer) {
// @todo: verify this works
array_dealloc(&buffer->line_starts);
dealloc(buffer->alo, buffer->data);
buffer16_dealloc_history_array(buffer, &buffer->undo_stack);
buffer16_dealloc_history_array(buffer, &buffer->redo_stack);
}
fn void buffer16_save_history_before_merge_cursor(buffer16_t *buffer, array_history16_t *stack, array_caret_t *carets) {
if (!buffer->flags.history) {
return;
}
history16_t node = {0};
array_copy(carets->alo, &node.carets, carets);
array_add(stack, node);
}
fn void buffer16_save_history_before_apply_edits(buffer16_t *buffer, array_history16_t *stack, array_edit16_t *edits) {
if (!buffer->flags.history) {
return;
}
history16_t *entry = &array_last(stack);
array_copy(buffer->alo, &entry->edits, edits);
// make reverse edits
array_for(edit16_t, it, &entry->edits) {
r1i64_t new_range = {it->range.min, it->range.min + it->string.len};
s16_t string = buffer16_get_string(buffer, it->range);
it->string = s16_copy_ex(buffer->alo, string);
it->range = new_range;
}
ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object);
array_edit16_t temp_edit = {0};
array_copy(malot(scratch), &temp_edit, &entry->edits);
// fix reverse edits
array_for(edit16_t, edit, edits) {
i64 remove_size = r1i64_size(edit->range);
i64 insert_size = edit->string.len;
i64 offset = insert_size - remove_size;
for (i64 i = 0; i < entry->edits.len; i += 1) {
edit16_t *new_edit = entry->edits.data + i;
edit16_t *old_edit = temp_edit.data + i;
if (old_edit->range.min > edit->range.min) {
new_edit->range.min += offset;
new_edit->range.max += offset;
}
}
}
ma_end_scratch(scratch);
}
fn void buffer16_redo_edit(buffer16_t *buffer, array_caret_t *carets) {
if (!buffer->flags.history || buffer->redo_stack.len <= 0) {
return;
}
history16_t entry = array_pop(&buffer->redo_stack);
buffer16_save_history_before_merge_cursor(buffer, &buffer->undo_stack, carets);
buffer16_save_history_before_apply_edits(buffer, &buffer->undo_stack, &entry.edits);
buffer16_multi_cursor_apply_edits(buffer, entry.edits);
array_dealloc(carets);
*carets = entry.carets;
array_for(edit16_t, it, &entry.edits) {
dealloc(buffer->alo, it->string.str);
}
array_dealloc(&entry.edits);
}
fn void buffer16_undo_edit(buffer16_t *buffer, array_caret_t *carets) {
if (!buffer->flags.history || buffer->undo_stack.len <= 0) {
return;
}
history16_t entry = array_pop(&buffer->undo_stack);
buffer16_save_history_before_merge_cursor(buffer, &buffer->redo_stack, carets);
buffer16_save_history_before_apply_edits(buffer, &buffer->redo_stack, &entry.edits);
buffer16_multi_cursor_apply_edits(buffer, entry.edits);
array_dealloc(carets);
*carets = entry.carets;
array_for(edit16_t, it, &entry.edits) {
dealloc(buffer->alo, it->string.str);
}
array_dealloc(&entry.edits);
}
fn void buffer16__apply_edits(buffer16_t *buffer, array_edit16_t *edits) {
assert(buffer->edit_phase == 1);
buffer->edit_phase += 1;
buffer16_save_history_before_apply_edits(buffer, &buffer->undo_stack, edits);
buffer16_multi_cursor_apply_edits(buffer, *edits);
}
fn void buffer16_dealloc_history_entries(buffer16_t *buffer, array_history16_t *entries) {
for (history16_t *it = entries->data; it < entries->data + entries->len; it += 1) {
array_dealloc(&it->carets);
for (edit16_t *edit_it = it->edits.data; edit_it < it->edits.data + it->edits.len; edit_it += 1) {
dealloc(buffer->alo, edit_it->string.str);
}
array_dealloc(&it->edits);
}
entries->len = 0;
}
fn void buffer16_dealloc_history_array(buffer16_t *buffer, array_history16_t *entries) {
buffer16_dealloc_history_entries(buffer, entries);
array_dealloc(entries);
}
// @note: !!
// We can invoke this before caret altering commands to save caret history
// and then call some editing command to edit which is not going to save carets
fn array_edit16_t buffer16_begin_edit(alo_t alo, buffer16_t *buffer, array_caret_t *carets) {
assert(buffer->edit_phase == 0 || buffer->edit_phase == 1);
if (buffer->edit_phase == 0) {
buffer->edit_phase += 1;
assert(carets->len);
buffer16_save_history_before_merge_cursor(buffer, &buffer->undo_stack, carets);
buffer16_dealloc_history_entries(buffer, &buffer->redo_stack);
}
array_edit16_t edits = (array_edit16_t){.alo = alo};
return edits;
}
fn void buffer16_pre_begin_edit_save_caret_history(buffer16_t *buffer, array_caret_t *carets) {
buffer16_begin_edit(buffer->alo, buffer, carets);
}
fn void caret_assert_ranges(array_caret_t carets) {
array_for(caret_t, it, &carets) {
assert(it->range.max >= it->range.min);
}
}
fn void buffer16_adjust_carets(array_edit16_t *edits, array_caret_t *carets) {
ma_temp_t scratch = ma_begin_scratch1(edits->alo.object);
assert(edits->alo.object == carets->alo.object);
array_caret_t new_carets = {0};
array_copy(malot(scratch), &new_carets, carets);
array_for(edit16_t, it, edits) {
i64 remove_size = r1i64_size(it->range);
i64 insert_size = it->string.len;
i64 offset = insert_size - remove_size;
for (i64 i = 0; i < carets->len; i += 1) {
caret_t *old_caret = carets->data + i;
caret_t *new_caret = new_carets.data + i;
if (old_caret->range.min > it->range.min) {
new_caret->range.min += offset;
new_caret->range.max += offset;
}
}
}
for (i64 i = 0; i < carets->len; i += 1) {
carets->data[i] = new_carets.data[i];
}
ma_end_scratch(scratch);
}
fn void buffer16_end_edit(buffer16_t *buffer, array_edit16_t *edits, array_caret_t *carets, b32 kill_selection) {
buffer16__apply_edits(buffer, edits);
assert(buffer->edit_phase == 2);
buffer->edit_phase -= 1;
#if BUFFER_DEBUG
if (buffer->flags.history) {
history16_t *entry = &array_last(&buffer->undo_stack);
assert(entry->carets.len);
assert(entry->edits.len);
for (i64 i = 0; i < edits->len - 1; i += 1) {
assert(edits->data[i].range.min <= edits->data[i + 1].range.min);
}
}
#endif
// Adjust carets
// this one also moves the carets forward if they are aligned with edit
ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object);
assert(buffer->alo.object == carets->alo.object);
assert(buffer->alo.object == edits->alo.object);
array_caret_t new_carets = {0};
array_copy(malot(scratch), &new_carets, carets);
array_for(edit16_t, it, edits) {
i64 remove_size = r1i64_size(it->range);
i64 insert_size = it->string.len;
i64 offset = insert_size - remove_size;
for (i64 i = 0; i < carets->len; i += 1) {
caret_t *old_caret = carets->data + i;
caret_t *new_caret = new_carets.data + i;
if (old_caret->range.min == it->range.min) {
new_caret->range.min += insert_size;
} else if (old_caret->range.min > it->range.min) {
new_caret->range.min += offset;
}
if (old_caret->range.max == it->range.max) {
new_caret->range.max += insert_size;
} else if (old_caret->range.max > it->range.max) {
new_caret->range.max += offset;
}
assert(new_caret->range.max >= new_caret->range.min);
}
}
for (i64 i = 0; i < carets->len; i += 1) {
carets->data[i] = new_carets.data[i];
if (kill_selection) {
carets->data[i].range.max = carets->data[i].range.min;
}
}
ma_end_scratch(scratch);
}
fn void caret_merge_sort(i64 Count, caret_t *First, caret_t *Temp) {
// SortKey = range.min
if (Count == 1) {
// NOTE(casey): No work to do.
} else if (Count == 2) {
caret_t *EntryA = First;
caret_t *EntryB = First + 1;
if (EntryA->range.min > EntryB->range.min) {
SWAP_PTR(caret_t, EntryA, EntryB);
}
} else {
i64 Half0 = Count / 2;
i64 Half1 = Count - Half0;
assert(Half0 >= 1);
assert(Half1 >= 1);
caret_t *InHalf0 = First;
caret_t *InHalf1 = First + Half0;
caret_t *End = First + Count;
caret_merge_sort(Half0, InHalf0, Temp);
caret_merge_sort(Half1, InHalf1, Temp);
caret_t *ReadHalf0 = InHalf0;
caret_t *ReadHalf1 = InHalf1;
caret_t *Out = Temp;
for (i64 Index = 0;
Index < Count;
++Index) {
if (ReadHalf0 == InHalf1) {
*Out++ = *ReadHalf1++;
} else if (ReadHalf1 == End) {
*Out++ = *ReadHalf0++;
} else if (ReadHalf0->range.min < ReadHalf1->range.min) {
*Out++ = *ReadHalf0++;
} else {
*Out++ = *ReadHalf1++;
}
}
assert(Out == (Temp + Count));
assert(ReadHalf0 == InHalf1);
assert(ReadHalf1 == End);
// TODO(casey): Not really necessary if we ping-pong
for (i64 Index = 0;
Index < Count;
++Index) {
First[Index] = Temp[Index];
}
}
}
fn void edit16_merge_sort(i64 Count, edit16_t *First, edit16_t *Temp) {
// SortKey = range.min
if (Count == 1) {
// NOTE(casey): No work to do.
} else if (Count == 2) {
edit16_t *EntryA = First;
edit16_t *EntryB = First + 1;
if (EntryA->range.min > EntryB->range.min) {
SWAP_PTR(edit16_t, EntryA, EntryB);
}
} else {
i64 Half0 = Count / 2;
i64 Half1 = Count - Half0;
assert(Half0 >= 1);
assert(Half1 >= 1);
edit16_t *InHalf0 = First;
edit16_t *InHalf1 = First + Half0;
edit16_t *End = First + Count;
edit16_merge_sort(Half0, InHalf0, Temp);
edit16_merge_sort(Half1, InHalf1, Temp);
edit16_t *ReadHalf0 = InHalf0;
edit16_t *ReadHalf1 = InHalf1;
edit16_t *Out = Temp;
for (i64 Index = 0;
Index < Count;
++Index) {
if (ReadHalf0 == InHalf1) {
*Out++ = *ReadHalf1++;
} else if (ReadHalf1 == End) {
*Out++ = *ReadHalf0++;
} else if (ReadHalf0->range.min < ReadHalf1->range.min) {
*Out++ = *ReadHalf0++;
} else {
*Out++ = *ReadHalf1++;
}
}
assert(Out == (Temp + Count));
assert(ReadHalf0 == InHalf1);
assert(ReadHalf1 == End);
// TODO(casey): Not really necessary if we ping-pong
for (i64 Index = 0;
Index < Count;
++Index) {
First[Index] = Temp[Index];
}
}
}
fn void buffer16_multi_cursor_apply_edits(buffer16_t *buffer, array_edit16_t edits) {
#if BUFFER_DEBUG
assert(buffer->line_starts.len);
assert(edits.len);
array_for(edit16_t, it, &edits) {
assert(it->range.min >= 0);
assert(it->range.max >= it->range.min);
assert(it->range.max <= buffer->len);
}
array_for(edit16_t, it1, &edits) {
array_for(edit16_t, it2, &edits) {
if (it1 == it2) continue;
b32 a2_inside = it2->range.min >= it1->range.min && it2->range.min < it1->range.max;
assert(!a2_inside);
b32 b2_inside = it2->range.max > it1->range.min && it2->range.max <= it1->range.max;
assert(!b2_inside);
}
}
#endif
// We need to sort from lowest to highest based on range.min
{
ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object);
array_edit16_t edits_copy = {0};
array_copy(malot(scratch), &edits_copy, &edits);
if (edits.len > 1) edit16_merge_sort(edits.len, edits_copy.data, edits.data);
edits = edits_copy;
ma_end_scratch(scratch);
}
#if BUFFER_DEBUG
for (i64 i = 0; i < edits.len - 1; i += 1) {
assert(edits.data[i].range.min <= edits.data[i + 1].range.min);
}
#endif
// @optimize: we can do all edits in one go with less memory copies probably
// or something else entirely
i64 offset = 0;
array_for(edit16_t, it, &edits) {
it->range.min += offset;
it->range.max += offset;
offset += it->string.len - r1i64_size(it->range);
buffer16_raw_replace_text(buffer, it->range, it->string);
}
}
fn void buffer16_add_edit(array_edit16_t *edits, r1i64_t range, s16_t string) {
array_add(edits, (edit16_t){range, string});
}
// Merge carets that overlap, this needs to be handled before any edits to
// make sure overlapping edits won't happen.
//
// mouse_selection_anchor is special case for mouse handling !
fn void buffer16_merge_carets(buffer16_t *buffer, array_caret_t *carets) {
array_for(caret_t, it, carets) {
it->range = buffer16_clamp_range(buffer, it->range);
}
caret_t first_caret = carets->data[0];
ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object);
array_caret_t c1 = {0};
array_copy(malot(scratch), &c1, carets);
if (carets->len > 1) {
caret_merge_sort(carets->len, c1.data, carets->data);
}
carets->len = 0;
i64 first_caret_index = 0;
array_add(carets, c1.data[0]);
for (i64 i = 1; i < c1.len; i += 1) {
caret_t *it = c1.data + i;
caret_t *last = &array_last(carets);
if (r1i64_overlap(it->range, last->range)) {
last->range.max = MAX(last->range.max, it->range.max);
} else {
array_add(carets, *it);
}
if (carets_are_equal(*it, first_caret)) {
first_caret_index = carets->len - 1;
}
}
SWAP(caret_t, carets->data[first_caret_index], carets->data[0]);
ma_end_scratch(scratch);
}
fn_test void buffer16_test(void) {
ma_temp_t scratch = ma_begin_scratch();
{
s16_t string = s16("thing itself");
buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t);
buffer16_init(malot(scratch), buffer, S8_FILE_AND_LINE, 16);
buffer16_raw_replace_text(buffer, r1i64_null, string);
assert(s16_are_equal(buffer->string, string));
assert(buffer->cap == 16);
assert(buffer->line_starts.len == 1);
buffer16_raw_replace_text(buffer, r1i64(5, 6), s16("|||"));
assert(s16_are_equal(buffer->string, s16("thing|||itself")));
assert(buffer->line_starts.len == 1);
buffer16_raw_replace_text(buffer, buffer16_get_range_end(buffer), s16("|||MEMES|||AndStuff"));
assert(s16_are_equal(buffer->string, s16("thing|||itself|||MEMES|||AndStuff")));
assert(buffer->cap > 16);
assert(buffer->line_starts.len == 1);
buffer16_raw_replace_text(buffer, buffer16_get_range_end(buffer), s16("\nnext line"));
assert(buffer->line_starts.len == 2);
buffer16_raw_replace_text(buffer, r1i64(4, 4), s16("\nnext line"));
buffer16_raw_replace_text(buffer, r1i64(20, 20), s16("\nnext line"));
buffer16_raw_replace_text(buffer, r1i64(14, 15), s16("\nnext line"));
buffer16_raw_replace_text(buffer, r1i64(0, 0), s16("\nnext line"));
buffer16_raw_replace_text(buffer, r1i64(9, 10), s16("\nnext line"));
assert(buffer->line_starts.len == 7);
buffer16_raw_replace_text(buffer, r1i64(8, 12), s16_null);
assert(buffer->line_starts.len == 6);
}
{
s16_t string = s16("thing itself_and meme");
buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t);
buffer16_init(malot(scratch), buffer, S8_FILE_AND_LINE, 10);
buffer16_raw_replace_text(buffer, r1i64_null, string);
r1i64_t range = buffer16_enclose_word(buffer, 18);
buffer16_raw_replace_text(buffer, range, s16("cat"));
assert(s16_are_equal(buffer->string, s16("thing itself_and cat")));
i64 eof = 0;
r1i64_t rng = buffer16_get_line_range_full(buffer, 0, &eof);
s16_t string_of_range = buffer16_get_string(buffer, rng);
assert(eof == 1);
assert(s16_are_equal(string_of_range, s16("thing itself_and cat")));
}
{
buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t);
buffer16_init(malot(scratch), buffer, S8_FILE_AND_LINE, 16);
for (int i = 0; i < 16; i += 1) {
buffer16_raw_replace_text(buffer, r1i64_null, s16("line of memes\n"));
}
}
{
s16_t string = s16("some thing or another and stuff like that");
s16_t meme_string = s16("meme meme or another and stuff like that");
buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t);
buffer16_init(malot(scratch), buffer, S8_FILE_AND_LINE, 128);
buffer16_raw_replace_text(buffer, r1i64_null, string);
{
array_caret_t carets = {0};
array_init(malot(scratch), &carets, 32);
array_add(&carets, caret_make(0, 4));
array_add(&carets, caret_make(3, 8));
array_add(&carets, caret_make(3, 8));
array_add(&carets, caret_make(3, 8));
array_add(&carets, caret_make(3, 8));
array_add(&carets, caret_make(3, 6));
array_add(&carets, caret_make(3, 6));
buffer16_merge_carets(buffer, &carets);
assert(carets.len == 1);
assert(carets.data[0].range.min == 0 && carets.data[0].range.max == 8);
}
{
array_caret_t carets = {0};
array_init(malot(scratch), &carets, 32);
array_add(&carets, caret_make(0, 4));
array_add(&carets, caret_make(0, 3));
array_add(&carets, caret_make(5, 10));
array_edit16_t edits = buffer16_begin_edit(malot(scratch), buffer, &carets);
buffer16_merge_carets(buffer, &carets);
assert(carets.len == 2);
array_for(caret_t, it, &carets) {
buffer16_add_edit(&edits, it->range, s16("meme"));
}
buffer16_end_edit(buffer, &edits, &carets, kill_selection);
assert(s16_are_equal(buffer->string, meme_string));
for (i32 i = 0; i < 32; i += 1) {
buffer16_undo_edit(buffer, &carets);
assert(s16_are_equal(buffer->string, string));
buffer16_redo_edit(buffer, &carets);
assert(s16_are_equal(buffer->string, meme_string));
}
}
}
ma_end_scratch(scratch);
}

136
src/ui/buffer16.h Normal file
View File

@@ -0,0 +1,136 @@
typedef struct caret_t caret_t;
struct caret_t {
i64 ifront;
union {
r1i64_t range;
i64 pos[2];
};
};
typedef struct xy_t xy_t;
struct xy_t {
i64 col;
i64 line;
};
typedef struct edit16_t edit16_t;
struct edit16_t {
r1i64_t range;
s16_t string;
};
// @todo: redo tree
typedef array(edit16_t) array_edit16_t;
typedef array(caret_t) array_caret_t;
typedef struct history16_t history16_t;
struct history16_t {
array_edit16_t edits;
array_caret_t carets;
};
typedef array(history16_t) array_history16_t;
typedef struct buffer16_id_t buffer16_id_t;
struct buffer16_id_t { i64 e; };
typedef struct buffer16_t buffer16_t;
struct buffer16_t {
s8_t name;
buffer16_id_t id;
i32 change_id;
i8 edit_phase;
struct {
b8 dirty: 1;
b8 history: 1;
b8 line_starts: 1;
} flags;
union {
s16_t string;
u16 *data;
struct {
u16 *str;
i64 len;
};
};
i64 cap;
array_i64_t line_starts;
array_history16_t undo_stack;
array_history16_t redo_stack;
alo_t alo;
};
typedef struct view_id_t view_id_t;
struct view_id_t { i64 e; };
typedef struct view_t view_t;
struct view_t {
view_id_t id;
buffer16_id_t active_buffer;
v2i32_t scroll;
array_caret_t carets;
caret_t main_caret_on_begin_frame;
b32 update_scroll;
};
const b32 dont_kill_selection = false;
const b32 kill_selection = true;
///////////////////////////////
// caret helpers
fn caret_t caret_make(i64 front, i64 back);
fn i64 caret_get_front(caret_t caret);
fn i64 caret_get_back(caret_t caret);
fn caret_t caret_set_front(caret_t caret, i64 pos);
fn caret_t caret_set_back(caret_t caret, i64 pos);
fn b32 carets_are_equal(caret_t a, caret_t b);
fn b32 carets_overlap(caret_t a, caret_t b);
///////////////////////////////
// buffer helpers
fn i64 buffer16_pos_to_line(buffer16_t *buffer, i64 pos);
fn r1i64_t buffer16_get_line_range(buffer16_t *buffer, i64 line);
fn r1i64_t buffer16_get_line_range_full(buffer16_t *buffer, i64 line, i64 *eof);
fn xy_t buffer16_pos_to_xy(buffer16_t *buffer, i64 pos);
fn i64 buffer16_xy_to_pos(buffer16_t *buffer, xy_t xy);
fn i64 buffer16_xy_to_pos_without_new_line(buffer16_t *buffer, xy_t xy);
fn s16_t buffer16_get_string(buffer16_t *buffer, r1i64_t range);
fn s16_t buffer16_get_line_string(buffer16_t *buffer, i64 line);
fn r1i64_t buffer16_get_range_end(buffer16_t *buffer);
fn r1i64_t buffer16_get_range(buffer16_t *buffer);
fn i64 buffer16_clamp_pos(buffer16_t *buffer, i64 pos);
fn r1i64_t buffer16_clamp_range(buffer16_t *buffer, r1i64_t range);
fn i64 buffer16_get_word_start(buffer16_t *buffer, i64 pos);
fn i64 buffer16_get_word_end(buffer16_t *buffer, i64 pos);
fn i64 buffer16_get_next_word_end(buffer16_t *buffer, i64 pos);
fn i64 buffer16_get_prev_word_start(buffer16_t *buffer, i64 pos);
fn i64 buffer16_get_line_start(buffer16_t *buffer, i64 pos, i64 *eof);
fn i64 buffer16_get_line_end(buffer16_t *buffer, i64 pos, i64 *eof);
fn i64 buffer16_get_line_start_full(buffer16_t *buffer, i64 pos);
fn i64 buffer16_get_line_end_full(buffer16_t *buffer, i64 pos);
fn r1i64_t buffer16_enclose_word(buffer16_t *buffer, i64 pos);
fn u16 buffer16_get_char(buffer16_t *buffer, i64 pos);
fn i64 buffer16_offset_pos_by_line(buffer16_t *buffer, i64 pos, i64 line_offset);
///////////////////////////////
// buffer raw textural operations. no history
fn void buffer16_raw_grow(buffer16_t *buffer, i64 change_size);
fn void buffer16_raw_replace_text(buffer16_t *buffer, r1i64_t range, s16_t string);
///////////////////////////////
// buffer multicursor + history
fn void buffer16_init(alo_t alo, buffer16_t *buffer, s8_t name, i64 size);
fn void buffer16_deinit(buffer16_t *buffer);
fn array_edit16_t buffer16_begin_edit(alo_t alo, buffer16_t *buffer, array_caret_t *carets);
fn void buffer16_add_edit(array_edit16_t *edits, r1i64_t range, s16_t string);
fn void buffer16_end_edit(buffer16_t *buffer, array_edit16_t *edits, array_caret_t *carets, b32 kill_selection);
fn void buffer16_merge_carets(buffer16_t *buffer, array_caret_t *carets);
fn void buffer16_adjust_carets(array_edit16_t *edits, array_caret_t *carets);
fn void buffer16_pre_begin_edit_save_caret_history(buffer16_t *buffer, array_caret_t *carets);
fn void buffer16_redo_edit(buffer16_t *buffer, array_caret_t *carets);
fn void buffer16_undo_edit(buffer16_t *buffer, array_caret_t *carets);

View File

@@ -1,4 +1,5 @@
#include "ui.gen.c"
#include "buffer16.c"
fn ui_caret_t ui_caret_clamp(ui_caret_t c, i32 min, i32 max) {
return (ui_caret_t){CLAMP(c.e[0],min,max), CLAMP(c.e[1],min,max), c.ifront};
@@ -38,7 +39,7 @@ fn ui_caret_t ui_carets(i32 x) {
}
fn s8_t ui_tprint_loc(ui_code_loc_t loc) {
return s8_printf(tcx->temp, "%s(%d)", loc.file, loc.line);
return s8_printf(&tcx->temp, "%s(%d)", loc.file, loc.line);
}
fn s8_t ui_get_display_string(s8_t string) {
@@ -105,7 +106,7 @@ fn ui_id_t ui_id(s8_t string) {
}
fn ui_id_t ui_idf(char *str, ...) {
S8_FMT(tcx->temp, str, string);
S8_FMT(&tcx->temp, str, string);
return ui_id(string);
}
@@ -367,7 +368,7 @@ fn ui_signal_t ui_signal_from_box(ui_box_t *box) {
}
fn v2f32_t ui_aligned_text_pos(f32 offset, ui_text_align_t text_align, r2f32_t rect, s8_t string, v2f32_t string_size) {
v2f32_t rect_size = r2f32_get_size(rect);
v2f32_t rect_size = r2f32_size(rect);
v2f32_t rect_string_diff = v2f32_sub(rect_size, string_size);
v2f32_t center_pos = v2f32_divs(rect_string_diff, 2);
v2f32_t pos_in_rect = v2f32(0, center_pos.y);
@@ -390,7 +391,7 @@ struct ui_draw_compute_t {
fn r2f32_t ui_get_appear_rect(ui_box_t *box) {
r2f32_t result = box->rect;
if (box->flags.animate_appear && !ui_id_is_null(box->id)) {
v2f32_t size = v2f32_muls(r2f32_get_size(result), 0.15f);
v2f32_t size = v2f32_muls(r2f32_size(result), 0.15f);
r2f32_t smaller_rect = r2f32_shrink(result, size);
f32 appear_t = f32_ease_out_n(f32_clamp01(box->appear_t * 2), 10);
result = r2f32_lerp(smaller_rect, result, appear_t);
@@ -401,6 +402,12 @@ fn r2f32_t ui_get_appear_rect(ui_box_t *box) {
fn ui_draw_compute_t ui_draw_compute(ui_box_t *box) {
ui_draw_compute_t co = {0};
if (ui->set_cursor_for_frame == false && box->flags.set_click_cursor &&
(ui->hot.value == box->id.value || ui->active.value == box->id.value)) {
set_cursor(1);
ui->set_cursor_for_frame = true;
}
co.rect = ui_get_appear_rect(box);
co.background_color = box->background_color;
co.text_color = box->text_color;
@@ -430,6 +437,10 @@ fn ui_draw_compute_t ui_draw_compute(ui_box_t *box) {
}
fn void ui_text_input_draw(ui_box_t *box) {
if (ui->set_cursor_for_frame == false && (ui->hot.value == box->id.value || ui->active.value == box->id.value)) {
set_cursor(0);
ui->set_cursor_for_frame = true;
}
ui_draw_compute_t co = ui_draw_compute(box);
rn_draw_rect(co.rect, co.background_color);
@@ -453,7 +464,9 @@ fn void ui_text_input_draw(ui_box_t *box) {
r2f32_t caret_rect = r2f32(co.rect.min.x + size_front.x, co.rect.min.y, co.rect.min.x + size_front.x + 1, co.rect.min.y + ui_em(1));
rn_draw_rect(caret_rect, co.text_color);
}
rn_draw_rect_border(co.rect, co.border_color, box->border_thickness);
// rn_draw_rect_border(co.rect, co.border_color, 1);
r2f32_t bottom = r2f32_cut_bottom(&co.rect, box->border_thickness);
rn_draw_rect(bottom, co.text_color);
}
fn void ui_default_draw_box(ui_box_t *box) {
@@ -517,26 +530,22 @@ fn_test void ui_test_text_replace(void) {
assert(s8_are_equal(ti.string, s8("qaer")));
}
#define ui_text_input(...) ui__text_input(UILOC, __VA_ARGS__)
fn ui_signal_t ui__text_input(ui_code_loc_t loc, ui_text_input_t *ti, b32 sim_even_if_no_focus) {
ui_box_t *box = ui_box(.loc = loc, .string = s8("text_input"), .flags = { .draw_border = true, .draw_rect = true, .draw_text = true, .keyboard_nav = true });
#define ui_text_input(ti, ...) ui__text_input(UILOC, ti, (ui_box_flags_t){ .draw_border = true, .draw_rect = true, .draw_text = true, .keyboard_nav = true, __VA_ARGS__ })
fn ui_signal_t ui__text_input(ui_code_loc_t loc, ui_text_input_t *ti, ui_box_flags_t flags) {
ui_box_t *box = ui_box(.loc = loc, .string = s8("text_input"), .flags = flags);
box->text_input = ti;
box->custom_draw = ui_text_input_draw;
ui_signal_t signal = ui_signal_from_box(box);
b32 sim = sim_even_if_no_focus || ui_is_focused_box(box);
b32 sim = flags.sim_even_if_no_focus || ui_is_focused_box(box);
if (sim) {
app_event_t *ev = ui->event;
i32 sel_size = r1i32_size(ti->caret.range);
if (ev->kind == app_event_kind_text) {
signal.text_changed = true;
ui_text_replace(ti, ti->caret.range, ev->text);
if (sel_size) {
ti->caret = ui_carets(ti->caret.range.min);
} else {
ti->caret = ui_carets(ti->caret.range.min + 1);
}
ti->caret = ui_carets(ti->caret.range.min + 1);
} else if (ev->kind == app_event_kind_key_down) {
if (ev->key == app_key_backspace) {
signal.text_changed = true;
@@ -561,6 +570,7 @@ fn ui_signal_t ui__text_input(ui_code_loc_t loc, ui_text_input_t *ti, b32 sim_ev
} else {
ui_text_replace(ti, r1i32(ti->caret.e[0], ti->caret.e[0] + 1), s8_null);
}
} else if (ev->key == app_key_left) {
signal.text_changed = true;
if (ev->shift) {
@@ -577,13 +587,20 @@ fn ui_signal_t ui__text_input(ui_code_loc_t loc, ui_text_input_t *ti, b32 sim_ev
}
} else if (ev->key == app_key_enter) {
signal.text_commit = true;
} else if (ev->key == app_key_a && ev->ctrl) {
ti->caret = ui_caret(0, ti->len);
}
}
v2f32_t size = rn_measure_string(rn->main_font, s8("_"));
v2f32_t pos = v2f32_sub(ev->mouse_pos, box->final_rect.min);
i32 p = (i32)f32_round(pos.x / size.x);
if (ev->kind == app_event_kind_mouse_down && ev->mouse_button == app_mouse_button_left) {
ti->caret = ui_carets(p);
if (ev->clicks == 2) {
// ti->caret = ui_caret(0, ti->len);
} else {
ti->caret = ui_carets(p);
}
}
if (signal.dragging) {
ti->caret = ui_caret_set_front(ti->caret, p);
@@ -595,7 +612,7 @@ fn ui_signal_t ui__text_input(ui_code_loc_t loc, ui_text_input_t *ti, b32 sim_ev
#define ui_button(...) ui__button(UILOC, __VA_ARGS__)
fn ui_signal_t ui__button(ui_code_loc_t loc, char *str, ...) {
S8_FMT(tcx->temp, str, string);
S8_FMT(&tcx->temp, str, string);
ui_box_t *box = ui_box(.loc = loc, .string = string, .flags = { .draw_border = true, .draw_rect = true, .draw_text = true, .keyboard_nav = true });
ui_signal_t signal = ui_signal_from_box(box);
return signal;
@@ -603,7 +620,7 @@ fn ui_signal_t ui__button(ui_code_loc_t loc, char *str, ...) {
#define ui_radio_button(...) ui__radio_button(UILOC, __VA_ARGS__)
fn ui_signal_t ui__radio_button(ui_code_loc_t loc, i32 *value, i32 value_clicked, char *str, ...) {
S8_FMT(tcx->temp, str, string);
S8_FMT(&tcx->temp, str, string);
ui_box_t *box = ui_box(.loc = loc, .string = string, .flags = { .draw_border = true, .draw_rect = true, .draw_text = true, .keyboard_nav = true });
ui_signal_t signal = ui_signal_from_box(box);
if (signal.clicked) *value = value_clicked;
@@ -613,7 +630,7 @@ fn ui_signal_t ui__radio_button(ui_code_loc_t loc, i32 *value, i32 value_clicked
#define ui_label_button(...) ui__label_button(UILOC, __VA_ARGS__)
fn ui_signal_t ui__label_button(ui_code_loc_t loc, char *str, ...) {
S8_FMT(tcx->temp, str, string);
S8_FMT(&tcx->temp, str, string);
ui_box_t *box = ui_box(.loc = loc, .string = string, .flags = {.draw_text = true, .keyboard_nav = true});
ui_signal_t signal = ui_signal_from_box(box);
return signal;
@@ -621,7 +638,7 @@ fn ui_signal_t ui__label_button(ui_code_loc_t loc, char *str, ...) {
#define ui_label(...) ui__label(UILOC, __VA_ARGS__)
fn ui_box_t *ui__label(ui_code_loc_t loc, char *str, ...) {
S8_FMT(tcx->temp, str, string);
S8_FMT(&tcx->temp, str, string);
ui_box_t *box = ui_box(.loc = loc, .string = string, .flags = {.draw_text = true});
return box;
}
@@ -634,7 +651,7 @@ fn void ui_tree_table_begin(ui_code_loc_t loc) {
fn void ui_tree_table_end(void) {
ui_box_t *box = ui_pop_top();
ui_set_children_sums(box);
ui_next_rect(ui_top_lop(), ui_top_rectp(), r2f32_get_size(box->rect));
ui_next_rect(ui_top_lop(), ui_top_rectp(), r2f32_size(box->rect));
}
#define ui_tree_table_expandable(...) defer_if (ui_tree_table_push_expandable(UILOC, __VA_ARGS__).clicked, ui_tree_table_pop_expandable())
@@ -646,7 +663,7 @@ fn void ui_tree_table_pop_expandable(void) {
ui_pop_id();
}
fn ui_signal_t ui_tree_table_push_expandable(ui_code_loc_t loc, char *str, ...) {
S8_FMT(tcx->temp, str, string);
S8_FMT(&tcx->temp, str, string);
ui_id_t id = ui_id(ui_get_hash_string(string));
ui_push_id(id);
ui_tree_table_begin(loc);
@@ -665,8 +682,8 @@ fn ui_signal_t ui_tree_table_push_expandable(ui_code_loc_t loc, char *str, ...)
if (button.clicked == false) {
ui_tree_table_pop_expandable();
}
if ( button.box->expanded) button.box->string = s8_printf(tcx->temp, "* %S", button.box->string);
if (!button.box->expanded) button.box->string = s8_printf(tcx->temp, "> %S", button.box->string);
if ( button.box->expanded) button.box->string = s8_printf(&tcx->temp, "* %S", button.box->string);
if (!button.box->expanded) button.box->string = s8_printf(&tcx->temp, "> %S", button.box->string);
return button;
}
@@ -998,7 +1015,7 @@ struct ui_scroller_t {
fn void ui_scroller_calc_vertical(ui_scroller_t s) {
ui_set_top(s.verti.box) {
f32 scroller_rect_pixels = r2f32_get_size(s.verti.box->rect).y;
f32 scroller_rect_pixels = r2f32_size(s.verti.box->rect).y;
f32 all_items_size = s.p.verti.max_size;
f32 scroller_size = f32_clamp01(s.verti.item_box_pixels / (all_items_size + s.verti.item_box_pixels));
f32 scrollable_space = (1 - scroller_size);
@@ -1023,7 +1040,7 @@ fn void ui_scroller_calc_vertical(ui_scroller_t s) {
}
if (upper_box_signal.dragging || down_box_signal.dragging) {
s.p.verti.value[0] = (ev->mouse_pos.y - upper_box->rect.min.y) * coef;
s.p.verti.value[0] -= (r2f32_get_size(slider_box->rect).y / 2) * coef;
s.p.verti.value[0] -= (r2f32_size(slider_box->rect).y / 2) * coef;
}
if (ev->kind == app_event_kind_mouse_wheel) {
s.p.verti.value[0] -= ev->mouse_wheel_delta.y;
@@ -1038,7 +1055,7 @@ fn ui_scroller_t ui_begin_scroller(ui_code_loc_t loc, ui_scroller_params_t p) {
if (p.verti.enabled) {
s.verti.box = ui_box(.loc = loc, .rect = r2f32_cut_right(&p.parent->rect, ui_dm(0.5f)), .flags = {.draw_rect = true});
r2f32_cut_bottom(&s.verti.box->rect, ui_dm(0.5f));
s.verti.item_box_pixels = r2f32_get_size(p.parent->rect).y;
s.verti.item_box_pixels = r2f32_size(p.parent->rect).y;
if (p.verti.item_count) {
s.p.verti.max_size = s.p.verti.item_pixels * s.p.verti.item_count;
ui_scroller_calc_vertical(s);
@@ -1077,7 +1094,7 @@ fn void ui_end_scroller(ui_scroller_t s) {
if (s.p.hori.enabled) {
ui_set_top(s.hori.box) {
f32 scroller_rect_pixels = r2f32_get_size(s.hori.box->rect).x;
f32 scroller_rect_pixels = r2f32_size(s.hori.box->rect).x;
f32 scroller_button_size_norm = f32_clamp01(scroller_rect_pixels / s.p.hori.max_size);
f32 scrollable_space = (1.f - scroller_button_size_norm);
@@ -1101,7 +1118,7 @@ fn void ui_end_scroller(ui_scroller_t s) {
}
if (left_box_sig.dragging || right_box_sig.dragging) {
s.p.hori.value[0] = (ev->mouse_pos.x - left_box->rect.min.x) * coef;
s.p.hori.value[0] -= (r2f32_get_size(slider_box->rect).x / 2) * coef;
s.p.hori.value[0] -= (r2f32_size(slider_box->rect).x / 2) * coef;
}
if (ev->kind == app_event_kind_mouse_wheel) {
s.p.hori.value[0] -= ev->mouse_wheel_delta.x;
@@ -1198,8 +1215,9 @@ fn void ui_draw(void) {
}
fn void ui_begin_frame(app_frame_t *frame) {
ui = tcx->data[tcx_slot_ui];
ui = tcx->ui_ctx;
ui->frame = frame;
ui->set_cursor_for_frame = false;
}
fn void ui_end_frame(void) {
@@ -1227,6 +1245,10 @@ fn void ui_end_frame(void) {
ui->active = ui_null_id;
}
}
if (ui->set_cursor_for_frame == false) {
set_cursor(32323);
}
}
fn void ui_reload(void) {
@@ -1234,8 +1256,8 @@ fn void ui_reload(void) {
}
fn void ui_init(ma_arena_t *arena) {
tcx->data[tcx_slot_ui] = ma_push_type(arena, ui_t);
ui = tcx->data[tcx_slot_ui];
tcx->ui_ctx = ma_push_type(arena, ui_t);
ui = tcx->ui_ctx;
ui->box_arena = ma_push_arena(arena, mib(1));
ui_reload();
}
@@ -1288,7 +1310,7 @@ fn void ui_demo_everything_lister(void) {
locl ui_text_input_t text_input;
text_input.str = buff;
text_input.cap = lengthof(buff);
ui_signal_t ti_sig = ui_text_input(&text_input, true);
ui_signal_t ti_sig = ui_text_input(&text_input, .sim_even_if_no_focus = true);
if (lister_just_opened) text_input.len = 0;
ma_temp_t scratch = ma_begin_scratch();
@@ -1399,7 +1421,7 @@ fn void ui_demo_update(app_frame_t *frame, mt_tweak_t *tweak_table, i32 tweak_co
ui_serial_type(UILOC, &ui_test_event, type(app_event_t));
r2f32_t scroll_rect = r2f32_fix(ui_top_rect());
scroller.p.verti.max_size = r2f32_get_size(scroll_rect).y;
scroller.p.verti.max_size = r2f32_size(scroll_rect).y;
}
ui_end_scroller(scroller);

View File

@@ -46,63 +46,63 @@ ui_color_table[ui_color_scroller] = ui_color_table[ui_color_text];
ui_color_table[ui_color_scroller_hot] = v4f32_hsla_to_rgba((v4f32_t){0.1f, 0.2f, 0.7f, 1.0f});
ui_color_table[ui_color_scroller_active] = v4f32_hsla_to_rgba((v4f32_t){0.1f, 0.2f, 0.5f, 1.0f});
}
fn void ui_push_id(ui_id_t v) { ui_id_node_t *n = ma_push_type(tcx->temp, ui_id_node_t); n->value = v; SLLS_PUSH(ui->id_stack, n); }
fn void ui_push_id(ui_id_t v) { ui_id_node_t *n = ma_push_type(&tcx->temp, ui_id_node_t); n->value = v; SLLS_PUSH(ui->id_stack, n); }
fn void ui_pop_id(void) { SLLS_POP(ui->id_stack); }
fn ui_id_t ui_top_id(void) { return ui->id_stack->value; }
#define ui_set_id(x) defer_block(ui_push_id(x), ui_pop_id())
fn void ui_push_lop(ui_lop_t v) { ui_lop_node_t *n = ma_push_type(tcx->temp, ui_lop_node_t); n->value = v; SLLS_PUSH(ui->lop_stack, n); }
fn void ui_push_lop(ui_lop_t v) { ui_lop_node_t *n = ma_push_type(&tcx->temp, ui_lop_node_t); n->value = v; SLLS_PUSH(ui->lop_stack, n); }
fn void ui_pop_lop(void) { SLLS_POP(ui->lop_stack); }
fn ui_lop_t ui_top_lop(void) { return ui->lop_stack->value; }
#define ui_set_lop(x) defer_block(ui_push_lop(x), ui_pop_lop())
fn void ui_push_border_thickness(f32 v) { ui_f32_node_t *n = ma_push_type(tcx->temp, ui_f32_node_t); n->value = v; SLLS_PUSH(ui->border_thickness_stack, n); }
fn void ui_push_border_thickness(f32 v) { ui_f32_node_t *n = ma_push_type(&tcx->temp, ui_f32_node_t); n->value = v; SLLS_PUSH(ui->border_thickness_stack, n); }
fn void ui_pop_border_thickness(void) { SLLS_POP(ui->border_thickness_stack); }
fn f32 ui_top_border_thickness(void) { return ui->border_thickness_stack->value; }
#define ui_set_border_thickness(x) defer_block(ui_push_border_thickness(x), ui_pop_border_thickness())
fn void ui_push_text_align(ui_text_align_t v) { ui_text_align_node_t *n = ma_push_type(tcx->temp, ui_text_align_node_t); n->value = v; SLLS_PUSH(ui->text_align_stack, n); }
fn void ui_push_text_align(ui_text_align_t v) { ui_text_align_node_t *n = ma_push_type(&tcx->temp, ui_text_align_node_t); n->value = v; SLLS_PUSH(ui->text_align_stack, n); }
fn void ui_pop_text_align(void) { SLLS_POP(ui->text_align_stack); }
fn ui_text_align_t ui_top_text_align(void) { return ui->text_align_stack->value; }
#define ui_set_text_align(x) defer_block(ui_push_text_align(x), ui_pop_text_align())
fn void ui_push_required_size(f32 v) { ui_f32_node_t *n = ma_push_type(tcx->temp, ui_f32_node_t); n->value = v; SLLS_PUSH(ui->required_size_stack, n); }
fn void ui_push_required_size(f32 v) { ui_f32_node_t *n = ma_push_type(&tcx->temp, ui_f32_node_t); n->value = v; SLLS_PUSH(ui->required_size_stack, n); }
fn void ui_pop_required_size(void) { SLLS_POP(ui->required_size_stack); }
fn f32 ui_top_required_size(void) { return ui->required_size_stack->value; }
#define ui_set_required_size(x) defer_block(ui_push_required_size(x), ui_pop_required_size())
fn void ui_push_padding(f32 v) { ui_f32_node_t *n = ma_push_type(tcx->temp, ui_f32_node_t); n->value = v; SLLS_PUSH(ui->padding_stack, n); }
fn void ui_push_padding(f32 v) { ui_f32_node_t *n = ma_push_type(&tcx->temp, ui_f32_node_t); n->value = v; SLLS_PUSH(ui->padding_stack, n); }
fn void ui_pop_padding(void) { SLLS_POP(ui->padding_stack); }
fn f32 ui_top_padding(void) { return ui->padding_stack->value; }
#define ui_set_padding(x) defer_block(ui_push_padding(x), ui_pop_padding())
fn void ui_push_string_pos_offset(f32 v) { ui_f32_node_t *n = ma_push_type(tcx->temp, ui_f32_node_t); n->value = v; SLLS_PUSH(ui->string_pos_offset_stack, n); }
fn void ui_push_string_pos_offset(f32 v) { ui_f32_node_t *n = ma_push_type(&tcx->temp, ui_f32_node_t); n->value = v; SLLS_PUSH(ui->string_pos_offset_stack, n); }
fn void ui_pop_string_pos_offset(void) { SLLS_POP(ui->string_pos_offset_stack); }
fn f32 ui_top_string_pos_offset(void) { return ui->string_pos_offset_stack->value; }
#define ui_set_string_pos_offset(x) defer_block(ui_push_string_pos_offset(x), ui_pop_string_pos_offset())
fn void ui_push_rect(r2f32_t v) { ui_r2f32_node_t *n = ma_push_type(tcx->temp, ui_r2f32_node_t); n->value = v; SLLS_PUSH(ui->rect_stack, n); }
fn void ui_push_rect(r2f32_t v) { ui_r2f32_node_t *n = ma_push_type(&tcx->temp, ui_r2f32_node_t); n->value = v; SLLS_PUSH(ui->rect_stack, n); }
fn void ui_pop_rect(void) { SLLS_POP(ui->rect_stack); }
fn r2f32_t ui_top_rect(void) { return ui->rect_stack->value; }
#define ui_set_rect(x) defer_block(ui_push_rect(x), ui_pop_rect())
fn void ui_push_background_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->background_color_stack, n); }
fn void ui_push_background_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(&tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->background_color_stack, n); }
fn void ui_pop_background_color(void) { SLLS_POP(ui->background_color_stack); }
fn v4f32_t ui_top_background_color(void) { return ui->background_color_stack->value; }
#define ui_set_background_color(x) defer_block(ui_push_background_color(x), ui_pop_background_color())
fn void ui_push_bg_hot_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->bg_hot_color_stack, n); }
fn void ui_push_bg_hot_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(&tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->bg_hot_color_stack, n); }
fn void ui_pop_bg_hot_color(void) { SLLS_POP(ui->bg_hot_color_stack); }
fn v4f32_t ui_top_bg_hot_color(void) { return ui->bg_hot_color_stack->value; }
#define ui_set_bg_hot_color(x) defer_block(ui_push_bg_hot_color(x), ui_pop_bg_hot_color())
fn void ui_push_bg_active_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->bg_active_color_stack, n); }
fn void ui_push_bg_active_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(&tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->bg_active_color_stack, n); }
fn void ui_pop_bg_active_color(void) { SLLS_POP(ui->bg_active_color_stack); }
fn v4f32_t ui_top_bg_active_color(void) { return ui->bg_active_color_stack->value; }
#define ui_set_bg_active_color(x) defer_block(ui_push_bg_active_color(x), ui_pop_bg_active_color())
fn void ui_push_border_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->border_color_stack, n); }
fn void ui_push_border_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(&tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->border_color_stack, n); }
fn void ui_pop_border_color(void) { SLLS_POP(ui->border_color_stack); }
fn v4f32_t ui_top_border_color(void) { return ui->border_color_stack->value; }
#define ui_set_border_color(x) defer_block(ui_push_border_color(x), ui_pop_border_color())
fn void ui_push_text_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->text_color_stack, n); }
fn void ui_push_text_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(&tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->text_color_stack, n); }
fn void ui_pop_text_color(void) { SLLS_POP(ui->text_color_stack); }
fn v4f32_t ui_top_text_color(void) { return ui->text_color_stack->value; }
#define ui_set_text_color(x) defer_block(ui_push_text_color(x), ui_pop_text_color())
fn void ui_push_text_hot_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->text_hot_color_stack, n); }
fn void ui_push_text_hot_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(&tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->text_hot_color_stack, n); }
fn void ui_pop_text_hot_color(void) { SLLS_POP(ui->text_hot_color_stack); }
fn v4f32_t ui_top_text_hot_color(void) { return ui->text_hot_color_stack->value; }
#define ui_set_text_hot_color(x) defer_block(ui_push_text_hot_color(x), ui_pop_text_hot_color())
fn void ui_push_text_active_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->text_active_color_stack, n); }
fn void ui_push_text_active_color(v4f32_t v) { ui_v4f32_node_t *n = ma_push_type(&tcx->temp, ui_v4f32_node_t); n->value = v; SLLS_PUSH(ui->text_active_color_stack, n); }
fn void ui_pop_text_active_color(void) { SLLS_POP(ui->text_active_color_stack); }
fn v4f32_t ui_top_text_active_color(void) { return ui->text_active_color_stack->value; }
#define ui_set_text_active_color(x) defer_block(ui_push_text_active_color(x), ui_pop_text_active_color())

View File

@@ -1,3 +1,5 @@
#include "buffer16.h"
typedef struct ui_code_loc_t ui_code_loc_t;
struct ui_code_loc_t {
char *file;
@@ -16,12 +18,15 @@ struct ui_box_flags_t {
b8 draw_border: 1;
b8 draw_text: 1;
b8 clip_rect: 1;
b8 set_click_cursor : 1;
b8 animate_appear: 1;
b8 children_sum_x: 1;
b8 children_sum_y: 1;
b8 keyboard_nav: 1;
b8 sim_even_if_no_focus: 1;
};
typedef enum {
@@ -66,6 +71,13 @@ struct ui_text_input_t {
ui_caret_t caret;
};
// typedef struct ui_styled_string_t ui_styled_string_t;
// struct ui_styled_string_t {
// ui_styled_string_t *next;
// s8_t string;
// v4f32_t color;
// };
typedef struct ui_box_t ui_box_t;
typedef void ui_custom_draw_t(ui_box_t *);
@@ -101,6 +113,7 @@ struct ui_box_t {
b32 expanded;
ui_text_input_t *text_input;
view_id_t view;
};
typedef struct ui_signal_t ui_signal_t;
@@ -142,6 +155,8 @@ struct ui_t {
// drawing
r2f32_t clip_rect;
b32 set_cursor_for_frame;
};
gb ui_t *ui;

View File

@@ -102,7 +102,7 @@ fn void mt_ui_stacks(ma_arena_t *arena, sb8_t *c, sb8_t *h) {
///////////////////////////////
// generate stack functions
for (ast_t *it = table->first; it; it = it->next) {
mt_sbprintf(c, it, "fn void ui_push_@name(@type v) { @node *n = ma_push_type(tcx->temp, @node); n->value = v; SLLS_PUSH(ui->@stack, n); }\n"
mt_sbprintf(c, it, "fn void ui_push_@name(@type v) { @node *n = ma_push_type(&tcx->temp, @node); n->value = v; SLLS_PUSH(ui->@stack, n); }\n"
"fn void ui_pop_@name(void) { SLLS_POP(ui->@stack); }\n"
"fn @type ui_top_@name(void) { return ui->@stack->value; }\n"
"#define ui_set_@name(x) defer_block(ui_push_@name(x), ui_pop_@name())\n");

View File

@@ -1,61 +0,0 @@
[ ] app
[ ] event playback
[ ] win32
[ ] hot reload / plugins
[ ] tests using yield
[ ] touchpad gestures: https://learn.microsoft.com/en-us/windows/win32/wintouch/windows-touch-gestures-overview
[ ] linux
[ ] wasm
[ ] drag and drop
[ ] open file dialog
[ ] os
[?] wasm (explore)
[ ] linux
[ ] render
[ ] wasm (maybe try first without opengl and then opengl)
[ ] opengl
[ ] maybe copy package stuff to build?
[ ] ui
[ ] sleep when not animating
[ ] concept of active panel
[ ] separate root so can't focus off with keyboard
[ ] clicking away from panel, closes it or changes focus
[ ] hot to move between panels?
[ ] table
[ ] scrolling with keyboard
[ ] context menu
[ ] maybe a separate root for context menu?
[ ] hover tooltips
[ ] separate root which gets drawn on top of all ui
[ ] upper left menu dynamics
[ ] text input
[ ] ctrl backspace, ctrl delete
[ ] set mouse cursor on hover
[ ] maybe copy over text editor code from other project?
[ ] everything lister (with edits etc.)
[ ] pernament storage?
[ ] push pop flag
[ ] how to do colors?
- css like based on ids and pre built palettes
- fat box with stacks (now)
[ ] demo style, with different buttons and controls, headings
[ ] color picker
[ ] slider
[ ] draw image in box ui
[ ] push flags?
[ ] core
[ ] ast
[ ] move to core layer at some point as the abstract format for different types of serialization
[ ] use bit fields instead of ast_flag
[ ] mt_tag syntax // f32 value; mt_tag(min = 0, min = 10) // mt_tag(something)
[ ] remove most flags
[ ] explore metadata flags for common formats aka (key_value, list etc.. will
[ ] json format
[ ] ini format
[ ] build system
[ ] use threads
[ ] meta
[ ] s8_bin