Compare commits
10 Commits
f9d1f14c7c
...
3d91a1f924
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d91a1f924 | ||
|
|
bb3859b537 | ||
|
|
ff17114860 | ||
|
|
c1c58d33b9 | ||
|
|
869af3f6dd | ||
|
|
6724814440 | ||
|
|
c8a4d19a72 | ||
|
|
13295a2fcd | ||
|
|
00777a24d5 | ||
|
|
c82eccc02a |
93
README.md
Normal file
93
README.md
Normal 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.
|
||||
86
build_file.c
86
build_file.c
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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[]){
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); }
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -15,3 +15,4 @@
|
||||
#include "core_platform.h"
|
||||
#include "core_hash_table.h"
|
||||
#include "core_ctx.h"
|
||||
#include "core_array.h"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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), ©, &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
24
src/core/core_array.h
Normal 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);
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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] == '/');
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
11
src/os/os.c
11
src/os/os.c
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
// }
|
||||
|
||||
@@ -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
73
src/prototype/parse_srt.c
Normal 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;
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
}
|
||||
190
src/prototype/transcript_browser.c
Normal file
190
src/prototype/transcript_browser.c
Normal 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();
|
||||
}
|
||||
|
||||
494
src/prototype/win32_transcript_browser.c
Normal file
494
src/prototype/win32_transcript_browser.c
Normal 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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
950
src/ui/buffer16.c
Normal 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
136
src/ui/buffer16.h
Normal 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);
|
||||
88
src/ui/ui.c
88
src/ui/ui.c
@@ -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);
|
||||
|
||||
@@ -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())
|
||||
|
||||
15
src/ui/ui.h
15
src/ui/ui.h
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
61
todo.txt
61
todo.txt
@@ -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
|
||||
Reference in New Issue
Block a user