transcript browser wasm working
This commit is contained in:
19
build_file.c
19
build_file.c
@@ -6,7 +6,10 @@
|
|||||||
// don't ever include stuff that build_file will generate code for!
|
// don't ever include stuff that build_file will generate code for!
|
||||||
#define DONT_INCLUDE_GENERATED_MATH
|
#define DONT_INCLUDE_GENERATED_MATH
|
||||||
#include "src/core/core.h"
|
#include "src/core/core.h"
|
||||||
|
#include "src/os/os.h"
|
||||||
|
|
||||||
#include "src/core/core.c"
|
#include "src/core/core.c"
|
||||||
|
#include "src/os/os.c"
|
||||||
|
|
||||||
#define BUILD_TOOL_LIB
|
#define BUILD_TOOL_LIB
|
||||||
#define S8_String s8_t
|
#define S8_String s8_t
|
||||||
@@ -28,7 +31,7 @@ void build_testing_target(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (PLATFORM_WINDOWS) {
|
if (PLATFORM_WINDOWS) {
|
||||||
os_delete_file(s8("testing.pdb"));
|
os_delete(s8("testing.pdb"));
|
||||||
int ok = os_systemf(
|
int ok = os_systemf(
|
||||||
"cl ../src/testing/testing_main.c -Fe:testing.exe -Fd:testing.pdb"
|
"cl ../src/testing/testing_main.c -Fe:testing.exe -Fd:testing.pdb"
|
||||||
" -I ../src"
|
" -I ../src"
|
||||||
@@ -86,7 +89,7 @@ void build_prototype_wasm_target(void) {
|
|||||||
b32 html_code_modified = cache_code_modified(s8("../src/app/app_wasm.html"), s8("../package/index.html"));
|
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/prototype/main.c"), s8("main.wasm"));
|
b32 wasm_code_modified = cache_code_modified(s8("../src/prototype/main.c"), s8("main.wasm"));
|
||||||
if (html_code_modified) {
|
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) {
|
if (wasm_code_modified) {
|
||||||
int ok = os_systemf(
|
int ok = os_systemf(
|
||||||
@@ -97,7 +100,7 @@ void build_prototype_wasm_target(void) {
|
|||||||
" --target=wasm32 -nostdlib -mbulk-memory -msimd128"
|
" --target=wasm32 -nostdlib -mbulk-memory -msimd128"
|
||||||
" -Wl,-export-dynamic,--allow-undefined,--import-memory,--no-entry,--initial-memory=131072000,--max-memory=4294967296"
|
" -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);
|
if (ok != 0) exit(ok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,9 +131,9 @@ void generate_math_code(ma_arena_t *arena) {
|
|||||||
#define py "python3 "
|
#define py "python3 "
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
os_set_working_dir("../src/core");
|
os_set_cwd(s8("../src/core"));
|
||||||
os_systemf(py "core_math_gen.py");
|
os_systemf(py "core_math_gen.py");
|
||||||
os_set_working_dir("../../build");
|
os_set_cwd(s8("../../build"));
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
@@ -149,9 +152,9 @@ int main(int argc, char **argv) {
|
|||||||
generate_testing_code(&tcx->temp);
|
generate_testing_code(&tcx->temp);
|
||||||
|
|
||||||
b32 run_win32_app_base_target = true;
|
b32 run_win32_app_base_target = true;
|
||||||
b32 run_testing_target = true;
|
b32 run_testing_target = false;
|
||||||
b32 run_prototype_dll_target = true;
|
b32 run_prototype_dll_target = false;
|
||||||
b32 run_prototype_wasm_target = false;
|
b32 run_prototype_wasm_target = true;
|
||||||
b32 run_prototype_standalone_target = false;
|
b32 run_prototype_standalone_target = false;
|
||||||
b32 run_server = false;
|
b32 run_server = false;
|
||||||
|
|
||||||
|
|||||||
@@ -137,6 +137,9 @@ const wasm_app_imports = {
|
|||||||
ctx2d.rect(x, y, w, h);
|
ctx2d.rect(x, y, w, h);
|
||||||
ctx2d.clip();
|
ctx2d.clip();
|
||||||
},
|
},
|
||||||
|
wasm_open_link: (str, len) => {
|
||||||
|
window.open(mem.read_cstr(str, len));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
(async function main() {
|
(async function main() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ gb_wasm_export char wasm_temp_buff1[128] = {[127] = 0x13};
|
|||||||
gb_wasm_export i32 wasm_temp_buff1_len = 127;
|
gb_wasm_export i32 wasm_temp_buff1_len = 127;
|
||||||
gb_wasm_export char wasm_temp_buff2[128] = {[127] = 0x13};
|
gb_wasm_export char wasm_temp_buff2[128] = {[127] = 0x13};
|
||||||
gb_wasm_export i32 wasm_temp_buff2_len = 127;
|
gb_wasm_export i32 wasm_temp_buff2_len = 127;
|
||||||
|
fn_wasm_import void wasm_open_link(isize str, i32 len);
|
||||||
|
|
||||||
gb f32 wasm_dpr;
|
gb f32 wasm_dpr;
|
||||||
gb f32 wasm_delta_time;
|
gb f32 wasm_delta_time;
|
||||||
@@ -26,7 +27,7 @@ fn void wasm_add_event_ex(app_frame_t *frame, app_event_t *ev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn void wasm_add_event(app_event_t event) {
|
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 = event;
|
||||||
ev->id = ++wasm_event_ids;
|
ev->id = ++wasm_event_ids;
|
||||||
ev->alt = wasm_cached.alt;
|
ev->alt = wasm_cached.alt;
|
||||||
@@ -102,7 +103,7 @@ fn_wasm_export void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 m
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s8_t text = s8_copy(tcx->temp, key8);
|
s8_t text = s8_copy(&tcx->temp, key8);
|
||||||
wasm_add_event((app_event_t){
|
wasm_add_event((app_event_t){
|
||||||
.kind = app_event_kind_text,
|
.kind = app_event_kind_text,
|
||||||
.text = text,
|
.text = text,
|
||||||
@@ -146,7 +147,7 @@ fn_wasm_export b32 wasm_update(f64 time, f32 width, f32 height, f32 dpr) {
|
|||||||
}
|
}
|
||||||
b32 animating = app_update(tcx, &wasm_frame);
|
b32 animating = app_update(tcx, &wasm_frame);
|
||||||
|
|
||||||
ma_set0(tcx->temp);
|
ma_set0(&tcx->temp);
|
||||||
zero_struct(&wasm_frame);
|
zero_struct(&wasm_frame);
|
||||||
wasm_last_time = wasm_time;
|
wasm_last_time = wasm_time;
|
||||||
wasm_frame_counter += 1;
|
wasm_frame_counter += 1;
|
||||||
@@ -157,3 +158,8 @@ fn_wasm_export b32 wasm_update(f64 time, f32 width, f32 height, f32 dpr) {
|
|||||||
fn_wasm_export void wasm_init(void) {
|
fn_wasm_export void wasm_init(void) {
|
||||||
os_core_init();
|
os_core_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo: os?
|
||||||
|
fn void open_link(s8_t url) {
|
||||||
|
wasm_open_link((isize)url.str, (i32)url.len);
|
||||||
|
}
|
||||||
@@ -137,6 +137,9 @@ const wasm_app_imports = {
|
|||||||
ctx2d.rect(x, y, w, h);
|
ctx2d.rect(x, y, w, h);
|
||||||
ctx2d.clip();
|
ctx2d.clip();
|
||||||
},
|
},
|
||||||
|
wasm_open_link: (str, len) => {
|
||||||
|
window.open(mem.read_cstr(str, len));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
(async function main() {
|
(async function main() {
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ fn void *ma_push_size_ex(ma_arena_t *arena, usize size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (new_len > arena->commit) {
|
if (new_len > arena->commit) {
|
||||||
|
debugf("expression failed(new_len > arena->commit) len:%llu base_len:%llu reserve:%llu commit:%llu, align: %llu", arena->len, arena->base_len, arena->reserve, arena->commit, arena->align);
|
||||||
invalid_codepath;
|
invalid_codepath;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
gb_thread thread_ctx_t *tcx;
|
gb_thread thread_ctx_t *tcx;
|
||||||
|
|
||||||
fn void *ma_arena_alo_proc(alo_t alo, alokind_t kind, void *ptr, size_t size) {
|
fn void *ma_arena_alo_proc(alo_t alo, alokind_t kind, void *ptr, size_t size) {
|
||||||
ma_arena_t *ma = alo.object;
|
|
||||||
if (kind == alokind_alloc) {
|
if (kind == alokind_alloc) {
|
||||||
return ma_push_size(alo.object, size);
|
return ma_push_size(alo.object, size);
|
||||||
} else if (kind == alokind_dealloc) {
|
} else if (kind == alokind_dealloc) {
|
||||||
|
|||||||
@@ -59,8 +59,9 @@ fn void os_core_init(void) {
|
|||||||
perm.commit = perm.reserve = memory_size;
|
perm.commit = perm.reserve = memory_size;
|
||||||
os_core_small_init(&perm);
|
os_core_small_init(&perm);
|
||||||
tcx->perm = perm;
|
tcx->perm = perm;
|
||||||
tcx->temp = ma_push_arena(&perm, mib(2));
|
ma_push_arena_ex(&tcx->perm, &tcx->temp, mib(2));
|
||||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[0], mib(1));
|
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[1], kib(256));
|
||||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[2], kib(64));
|
ma_push_arena_ex(&tcx->perm, &tcx->scratch[2], kib(64));
|
||||||
|
debugf("memory size = %lld", memory_size);
|
||||||
}
|
}
|
||||||
@@ -487,6 +487,20 @@ fn u64 u64_from_s8(s8_t s, u64 base) {
|
|||||||
return acc;
|
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) {
|
fn f64 f64_from_s8(s8_t string) {
|
||||||
ma_temp_t scratch = ma_begin_scratch();
|
ma_temp_t scratch = ma_begin_scratch();
|
||||||
s8_t num_string = s8_copy(scratch.arena, string);
|
s8_t num_string = s8_copy(scratch.arena, string);
|
||||||
|
|||||||
@@ -555,8 +555,12 @@ fn_test void test_string16(void) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
s16_t a = s16("C:\\Program Files\\Memes");
|
s16_t a = s16("C:\\Program Files\\Memes");
|
||||||
s16_normalize_path_unsafe(a);
|
u16 memes[256] = {0};
|
||||||
assert(a.str[2] == '/');
|
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;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
OS_API S8_String os_cwd(MA_Arena *arena) {
|
OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) {
|
||||||
wchar_t wbuffer[1024];
|
wchar_t wbuffer[1024];
|
||||||
DWORD wsize = GetCurrentDirectoryW(MA_Lengthof(wbuffer), wbuffer);
|
DWORD wsize = GetCurrentDirectoryW(MA_Lengthof(wbuffer), wbuffer);
|
||||||
IO_Assert(wsize != 0);
|
IO_Assert(wsize != 0);
|
||||||
@@ -2277,7 +2277,7 @@ OS_API S8_String os_cwd(MA_Arena *arena) {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
OS_API void os_set_working_dir(char *path) {
|
OS_API void OS_SetWorkingDir(char *path) {
|
||||||
IO_Printf("cd %s\n", path);
|
IO_Printf("cd %s\n", path);
|
||||||
wchar_t wpath[1024];
|
wchar_t wpath[1024];
|
||||||
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path, S8_Length(path));
|
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;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
OS_API bool os_file_exists(S8_String path) {
|
OS_API bool OS_FileExists(S8_String path) {
|
||||||
wchar_t wbuff[1024];
|
wchar_t wbuff[1024];
|
||||||
UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len);
|
UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len);
|
||||||
DWORD attribs = GetFileAttributesW(wbuff);
|
DWORD attribs = GetFileAttributesW(wbuff);
|
||||||
@@ -2401,7 +2401,7 @@ OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path) {
|
|||||||
return it;
|
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);
|
IO_Printf("mkdir %s\n", path);
|
||||||
wchar_t wpath[1024];
|
wchar_t wpath[1024];
|
||||||
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path, S8_Length(path));
|
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;
|
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" : "";
|
const char *ow = overwrite ? "-n" : "";
|
||||||
IO_Printf("cp %s %s %s\n", ow, from, to);
|
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;
|
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));
|
IO_Printf("rm %.*s\n", S8_Expand(path));
|
||||||
wchar_t wpath[1024];
|
wchar_t wpath[1024];
|
||||||
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
|
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;
|
S8_Node *dirs_to_remove = 0;
|
||||||
for (S8_Node *it = list.first; it; it = it->next) {
|
for (S8_Node *it = list.first; it; it = it->next) {
|
||||||
if (!S8_EndsWith(it->string, S8_Lit("/"), S8_IgnoreCase)) {
|
if (!S8_EndsWith(it->string, S8_Lit("/"), S8_IgnoreCase)) {
|
||||||
os_delete_file(it->string);
|
OS_DeleteFile(it->string);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
S8_Node *node = S8_CreateNode(scratch.arena, it->string);
|
S8_Node *node = S8_CreateNode(scratch.arena, it->string);
|
||||||
@@ -2637,14 +2637,14 @@ OS_API S8_String OS_GetExeDir(MA_Arena *arena) {
|
|||||||
return copy;
|
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 *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX);
|
||||||
char *cwd = getcwd(buffer, PATH_MAX);
|
char *cwd = getcwd(buffer, PATH_MAX);
|
||||||
S8_String result = S8_MakeFromChar(cwd);
|
S8_String result = S8_MakeFromChar(cwd);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
OS_API void os_set_working_dir(char *path) {
|
OS_API void OS_SetWorkingDir(char *path) {
|
||||||
IO_Printf("cd %s\n", path);
|
IO_Printf("cd %s\n", path);
|
||||||
MA_Temp scratch = MA_GetScratch();
|
MA_Temp scratch = MA_GetScratch();
|
||||||
S8_String copy = S8_Copy(scratch.arena, S8_MakeFromChar(path));
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
OS_API bool os_file_exists(S8_String path) {
|
OS_API bool OS_FileExists(S8_String path) {
|
||||||
MA_Temp scratch = MA_GetScratch();
|
MA_Temp scratch = MA_GetScratch();
|
||||||
S8_String copy = S8_Copy(scratch.arena, path);
|
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;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
OS_API int os_systemf(const char *string, ...);
|
OS_API int OS_Systemf(const char *string, ...);
|
||||||
OS_API OS_Result os_make_dir(char *path) {
|
OS_API OS_Result OS_MakeDir(char *path) {
|
||||||
IO_Printf("mkdir %s\n", path);
|
IO_Printf("mkdir %s\n", path);
|
||||||
MA_Temp scratch = MA_GetScratch();
|
MA_Temp scratch = MA_GetScratch();
|
||||||
S8_String path8 = S8_Copy(scratch.arena, S8_MakeFromChar(path));
|
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;
|
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" : "";
|
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;
|
return result == 0 ? OS_SUCCESS : OS_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
OS_API OS_Result os_delete_file(S8_String path) {
|
OS_API OS_Result OS_DeleteFile(S8_String path) {
|
||||||
int result = os_systemf("rm %.*s", S8_Expand(path));
|
int result = OS_Systemf("rm %.*s", S8_Expand(path));
|
||||||
return result == 0 ? OS_SUCCESS : OS_FAILURE;
|
return result == 0 ? OS_SUCCESS : OS_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) {
|
OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) {
|
||||||
IO_Assert(flags & OS_RECURSIVE);
|
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;
|
return result == 0 ? OS_SUCCESS : OS_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2881,7 +2881,7 @@ OS_API OS_Result os_write_file(S8_String path, S8_String string) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if _WIN32 || __linux__ || __APPLE__ || __unix__
|
#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();
|
MA_Temp scratch = MA_GetScratch();
|
||||||
S8_FORMAT(scratch.arena, string, result);
|
S8_FORMAT(scratch.arena, string, result);
|
||||||
// IO_Printf("%.*s\n", S8_Expand(result));
|
// IO_Printf("%.*s\n", S8_Expand(result));
|
||||||
@@ -5052,7 +5052,7 @@ SRC_CacheEntry *cache_hash_file(S8_String file, char *parent_file) {
|
|||||||
bool cache_code_modified(S8_String file, S8_String artifact_path) {
|
bool cache_code_modified(S8_String file, S8_String artifact_path) {
|
||||||
double time_start = OS_GetTime();
|
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));
|
IO_Printf("FAILED File doesnt exist: %.*s\n", S8_Expand(file));
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
@@ -5062,7 +5062,7 @@ bool cache_code_modified(S8_String file, S8_String artifact_path) {
|
|||||||
|
|
||||||
bool modified = false;
|
bool modified = false;
|
||||||
if (artifact_path.len) {
|
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);
|
SRC_CacheEntry *in_memory = cache_hash_file(file, 0);
|
||||||
@@ -5117,8 +5117,8 @@ S8_String GCC_Debug = S8_ConstLit("-fsanitize=address -g");
|
|||||||
|
|
||||||
#if !defined(BUILD_TOOL_LIB)
|
#if !defined(BUILD_TOOL_LIB)
|
||||||
int main(int argument_count, char **arguments) {
|
int main(int argument_count, char **arguments) {
|
||||||
os_make_dir("build");
|
OS_MakeDir("build");
|
||||||
os_set_working_dir("build");
|
OS_SetWorkingDir("build");
|
||||||
S8_String working_dir = os_cwd(&Perm);
|
S8_String working_dir = os_cwd(&Perm);
|
||||||
|
|
||||||
S8_String cc = S8_Lit(IF_WINDOWS("cl") IF_MAC("clang") IF_LINUX("gcc"));
|
S8_String cc = S8_Lit(IF_WINDOWS("cl") IF_MAC("clang") IF_LINUX("gcc"));
|
||||||
@@ -5157,16 +5157,16 @@ int main(int argument_count, char **arguments) {
|
|||||||
time_build_file_compiled = OS_GetTime();
|
time_build_file_compiled = OS_GetTime();
|
||||||
int result = 0;
|
int result = 0;
|
||||||
if (S8_AreEqual(cc, S8_Lit("cl"), false)) {
|
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)) {
|
} 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 {
|
} 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) {
|
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);
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
time_build_file_compiled = OS_GetTime() - time_build_file_compiled;
|
time_build_file_compiled = OS_GetTime() - time_build_file_compiled;
|
||||||
@@ -5177,10 +5177,10 @@ int main(int argument_count, char **arguments) {
|
|||||||
double time = OS_GetTime();
|
double time = OS_GetTime();
|
||||||
if (build_file.str) {
|
if (build_file.str) {
|
||||||
exe_name = OS_GetAbsolutePath(&Perm, exe_name);
|
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) {
|
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);
|
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;
|
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) {
|
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;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1) (QUOTED_FORM) In the same directory as the file that contains the #include statement.
|
// 1) (QUOTED_FORM) In the same directory as the file that contains the #include statement.
|
||||||
if (!is_system_include && parent_file.len) {
|
if (!is_system_include && parent_file.len) {
|
||||||
s8_t path = s8_printf(arena, "%S/%S", s8_chop_last_slash(parent_file), filename);
|
s8_t path = s8_printf(arena, "%S/%S", s8_chop_last_slash(parent_file), filename);
|
||||||
if (os_file_exists(path)) {
|
if (os_exists(path)) {
|
||||||
return OS_GetAbsolutePath(&Perm, 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.
|
// 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) {
|
for (sb8_node_t *it = include_paths->first; it; it = it->next) {
|
||||||
s8_t path = s8_printf(arena, "%S/%S", it->string, filename);
|
s8_t path = s8_printf(arena, "%S/%S", it->string, filename);
|
||||||
if (os_file_exists(path)) {
|
if (os_exists(path)) {
|
||||||
return OS_GetAbsolutePath(&Perm, 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) {
|
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) {
|
if (content.len == 0) {
|
||||||
return;
|
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) {
|
fn mt_files_t mt_lex_files(ma_arena_t *arena, s8_t path, sb8_t *include_paths) {
|
||||||
mt_files_t files = {0};
|
mt_files_t files = {0};
|
||||||
path = OS_GetAbsolutePath(&Perm, path);
|
path = os_abs(arena, path);
|
||||||
mt__lex_files(arena, &files, path, include_paths);
|
mt__lex_files(arena, &files, path, include_paths);
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void mt_list_files_recursive(sb8_t *sb, s8_t path) {
|
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)) {
|
for (os_iter_t *iter = os_iter(sb->arena, path); iter->is_valid; os_advance(iter)) {
|
||||||
if (iter.is_directory) {
|
if (iter->is_directory) {
|
||||||
mt_list_files_recursive(sb, iter.absolute_path);
|
mt_list_files_recursive(sb, iter->abs);
|
||||||
} else {
|
} 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) {
|
fn sb8_t *mt_get_include_paths(ma_arena_t *arena) {
|
||||||
sb8_t *result = ma_push_type(arena, sb8_t);
|
sb8_t *result = ma_push_type(arena, sb8_t);
|
||||||
result->arena = arena;
|
result->arena = arena;
|
||||||
sb8_append(result, OS_GetAbsolutePath(&Perm, s8("../src")));
|
sb8_append(result, os_abs(arena, s8("../src")));
|
||||||
return result;
|
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);
|
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;
|
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
|
||||||
@@ -9,7 +9,10 @@
|
|||||||
#include "render/render.c"
|
#include "render/render.c"
|
||||||
#include "ui/ui.c"
|
#include "ui/ui.c"
|
||||||
|
|
||||||
|
#include "parse_srt.c"
|
||||||
|
#include "../build/entries.inc"
|
||||||
#include "transcript_browser.c"
|
#include "transcript_browser.c"
|
||||||
|
// #include "win32_transcript_browser.c"
|
||||||
#include "prototype.gen.c"
|
#include "prototype.gen.c"
|
||||||
|
|
||||||
// @todo: how to pack the data so it's available on wasm?
|
// @todo: how to pack the data so it's available on wasm?
|
||||||
@@ -18,6 +21,7 @@
|
|||||||
// @todo: selecting and writing is bugged
|
// @todo: selecting and writing is bugged
|
||||||
// @todo: make it possible without executable
|
// @todo: make it possible without executable
|
||||||
// https://drive.proton.me/urls/F9X0GWGH38#t7MaKdLbvOfN
|
// https://drive.proton.me/urls/F9X0GWGH38#t7MaKdLbvOfN
|
||||||
|
// @todo @ui scrolling when focused button goes out of screen
|
||||||
|
|
||||||
fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
|
fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
|
||||||
tcx = thread_ctx;
|
tcx = thread_ctx;
|
||||||
@@ -39,7 +43,7 @@ fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
|
|||||||
} else if (frame->first_event->kind == app_event_kind_reload) {
|
} else if (frame->first_event->kind == app_event_kind_reload) {
|
||||||
ui_reload();
|
ui_reload();
|
||||||
rn_reload();
|
rn_reload();
|
||||||
res_unload(tcx->user_ctx);
|
res_reload(tcx->user_ctx);
|
||||||
return true;
|
return true;
|
||||||
} else if (frame->first_event->kind == app_event_kind_unload) {
|
} else if (frame->first_event->kind == app_event_kind_unload) {
|
||||||
res_unload(tcx->user_ctx);
|
res_unload(tcx->user_ctx);
|
||||||
@@ -54,7 +58,3 @@ fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
|
|||||||
transcript_browser_update(frame, tweak_table, lengthof(tweak_table));
|
transcript_browser_update(frame, tweak_table, lengthof(tweak_table));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Answering one of your things. Maybe I went too much into idealism with that formulation, there are many solutions to the puzzle and they will differ by place, culture but you always need some kind of new elite, it would be a bit nicer if they gained support from actual hard work, communication, education etc. Back in the day you would do that for example by building the national, communal spirit through literature and poetry targetting peasentry, proletariatt. Today you have new digital media like youtube where you can reach big audiences, this seems to work pretty nicely in my country currently and it has a basis in historical polish ideas, "work at the base" where in one of the classics there is fantasy staged that polish nation is an organic unity where every part has to function and it is the responsibility of polish new elite to go down to the people learn about their circumstances, educate them, give them tools to overcome their circumstances because they will be the foundation of a new country. The interesting part is that the new elite has to be high on romanticism, always looking beyond. The assumption is they will be hated for it by everyone: the townspeople, the corrupt aristocracy who are all self-interested, divided and don't care about the nation as a unity. In the end the truly talented will be marginalized. The book ends with a sad, miserable ending where the main hero kills himself but leaves a note that not everything of him will die.
|
|
||||||
*/
|
|
||||||
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;
|
||||||
|
}
|
||||||
@@ -7,10 +7,5 @@ gb_read_only mt_tweak_t tweak_table[] = {
|
|||||||
|
|
||||||
};
|
};
|
||||||
void run_all_tests(void) {
|
void run_all_tests(void) {
|
||||||
test_string16();
|
|
||||||
test_hash_table();
|
|
||||||
test_intern_table();
|
|
||||||
test_array();
|
|
||||||
ui_test_text_replace();
|
ui_test_text_replace();
|
||||||
buffer16_test();
|
|
||||||
}// run_all_tests()
|
}// run_all_tests()
|
||||||
|
|||||||
@@ -1,6 +1,85 @@
|
|||||||
|
#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] == '.') {
|
||||||
|
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) {
|
void generate_prototype_code(ma_arena_t *arena) {
|
||||||
|
generate_transcript(s8("D:/videos/jiang"));
|
||||||
sb8_t *include_paths = sb8(arena);
|
sb8_t *include_paths = sb8(arena);
|
||||||
sb8_append(include_paths, OS_GetAbsolutePath(&Perm, s8("../src")));
|
sb8_append(include_paths, os_abs(arena, s8("../src")));
|
||||||
mt_files_t files = mt_lex_files(arena, s8("../src/prototype/main.c"), include_paths);
|
mt_files_t files = mt_lex_files(arena, s8("../src/prototype/main.c"), include_paths);
|
||||||
|
|
||||||
typedef struct cg_tweak_t cg_tweak_t;
|
typedef struct cg_tweak_t cg_tweak_t;
|
||||||
@@ -118,5 +197,5 @@ void generate_prototype_code(ma_arena_t *arena) {
|
|||||||
|
|
||||||
|
|
||||||
s8_t embeds = sb8_serial_end(arena, sb_embeds);
|
s8_t embeds = sb8_serial_end(arena, sb_embeds);
|
||||||
os_write_file(mt_cpath(arena), embeds);
|
os_write(mt_cpath(arena), embeds);
|
||||||
}
|
}
|
||||||
@@ -1,451 +1,73 @@
|
|||||||
///////////////////////////////
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
// parse srt
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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
|
|
||||||
long num = strtol(it->str, NULL, 10);
|
|
||||||
assert(section_number == num);
|
|
||||||
section_number += 1;
|
|
||||||
it = it->next;
|
|
||||||
|
|
||||||
// parse start of time interval
|
|
||||||
srt_entry_t entry = {0};
|
|
||||||
entry.hour = (u16)strtol(it->str, NULL, 10);
|
|
||||||
entry.minute = (u16)strtol(it->str + 3, NULL, 10);
|
|
||||||
entry.second = (u16)strtol(it->str + 6, NULL, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
// multithreaded resource loading
|
|
||||||
|
|
||||||
typedef array(s8_t) array_s8_t;
|
typedef array(s8_t) array_s8_t;
|
||||||
typedef struct res_parse_work_t res_parse_work_t;
|
|
||||||
typedef struct res_t res_t;
|
typedef struct res_t res_t;
|
||||||
struct res_parse_work_t {
|
|
||||||
s8_t srt;
|
|
||||||
res_t *res;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct res_t {
|
struct res_t {
|
||||||
ma_arena_t misc_arena;
|
|
||||||
array_s8_t error_messages;
|
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;
|
array_s8_t search_matches;
|
||||||
ui_text_input_t search_text_input;
|
ui_text_input_t search_text_input;
|
||||||
char search_prompt[256];
|
char search_prompt[256];
|
||||||
|
i64 search_idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
#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_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_unload(res_t *res) {
|
||||||
work_queue_stop(&res->work_queue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void res_reload(res_t *res) {
|
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) {
|
#define res_report(res, ...) array_add(&(res)->error_messages, s8_printf(&(tcx)->perm, __VA_ARGS__))
|
||||||
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 s8_t srt_find_matching_video(s8_t filename) {
|
fn void res_search_matches(res_t *res) {
|
||||||
s8_t no_srt = s8_chop_last_period(filename);
|
if (res->search_idx == 0) {
|
||||||
if (s8_ends_with(no_srt, s8(".en"))) {
|
res->search_matches.len = 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
if (res->search_text_input.len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const i64 search_iter = 4096*16;
|
||||||
i64 search_stop_counter = res->search_stop_counter;
|
for (i64 i = res->search_idx; i < res->search_idx + search_iter && i < the_text_itself.len; i += 1) {
|
||||||
mutex_lock(res->search_mutex);
|
s8_t text = s8_skip(the_text_itself, i);
|
||||||
res->search_matches.len = 0;
|
if (s8_starts_with(text, res->search_text_input.string)) {
|
||||||
mutex_unlock(res->search_mutex);
|
s8_t found = s8_make(text.str, res->search_text_input.len);
|
||||||
|
array_add(&res->search_matches, found);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
res->search_idx += search_iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void res_search_start(res_t *res) {
|
fn srt_entry_t res_find_entry(res_t *res, s8_t res_string) {
|
||||||
res->search_stop_counter += 1;
|
srt_entry_t result = {0};
|
||||||
work_queue_push(&res->work_queue, res, res_search_thread);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// update
|
||||||
|
|
||||||
fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i32 tweak_count) {
|
fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i32 tweak_count) {
|
||||||
ui_begin_frame(frame);
|
ui_begin_frame(frame);
|
||||||
rn_begin_frame(frame);
|
rn_begin_frame(frame);
|
||||||
|
|
||||||
res_t *res = (res_t *)tcx->user_ctx;
|
res_t *res = (res_t *)tcx->user_ctx;
|
||||||
|
res_search_matches(res);
|
||||||
|
|
||||||
for (app_event_t *ev = frame->first_event; ev; ev = ev->next) {
|
for (app_event_t *ev = frame->first_event; ev; ev = ev->next) {
|
||||||
ui_begin_build(UILOC, ev, window_rect_from_frame(frame));
|
ui_begin_build(UILOC, ev, window_rect_from_frame(frame));
|
||||||
|
|
||||||
@@ -462,11 +84,8 @@ fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
ma_temp_t scratch = ma_begin_scratch();
|
ma_temp_t scratch = ma_begin_scratch();
|
||||||
|
array_s8_t matches = res->search_matches;
|
||||||
|
|
||||||
array_s8_t matches = res_search_get(scratch.arena, res);
|
|
||||||
if (matches.len == 0 || res->search_text_input.len == 0) {
|
if (matches.len == 0 || res->search_text_input.len == 0) {
|
||||||
// @concurrency not sure if this is ok
|
|
||||||
matches = res->error_messages;
|
matches = res->error_messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,8 +111,8 @@ fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i
|
|||||||
ui_push_id((ui_id_t){i});
|
ui_push_id((ui_id_t){i});
|
||||||
s8_t it = matches.data[i];
|
s8_t it = matches.data[i];
|
||||||
|
|
||||||
uintptr_t begin_region = (uintptr_t)res->text_arena.data;
|
uintptr_t begin_region = (uintptr_t)the_text_itself.str;
|
||||||
uintptr_t end_region = (uintptr_t)res->text_arena.data + res->text_arena.len;
|
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 chars_per_line = (i64)(frame->window_size.x) / (i64)ui_dm(1);
|
||||||
|
|
||||||
@@ -514,47 +133,19 @@ fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i
|
|||||||
ui_box_t *box = ui_box(.string = string, .flags = {.draw_rect = true, .draw_text = true, .keyboard_nav = true});
|
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);
|
ui_signal_t sig = ui_signal_from_box(box);
|
||||||
if (sig.clicked && matches.data != res->error_messages.data) {
|
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;
|
||||||
// {
|
debugf("time = %u %u %u seconds = %d", entry.hour, entry.minute, entry.second, seconds);
|
||||||
// s8_t text = s8_make(res->text_arena.data, res->text_arena.len);
|
s8_t id = s8_get_postfix(entry.filepath, 13);
|
||||||
// sb8_t *sb = sb8_serial_begin(scratch.arena);
|
debugf("id = %S", id);
|
||||||
// sb8_printf(sb, "s8_t the_text_itself = s8(\"%S\");\n", text);
|
if (id.str[0] != '[') {
|
||||||
// sb8_printf(sb, "s8_t the_filenames[] = {\n");
|
fatalf("internal error: dear user, it appears that youtube id link is not composed of exactly 11 letters, quite a pickle, if you could report that to me would be very nice");
|
||||||
// array_srt_entry_t entries = res->mapping_entries;
|
|
||||||
// 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)) {
|
|
||||||
// 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;
|
|
||||||
// }
|
|
||||||
// sb8_printf(sb, "};\n");
|
|
||||||
// sb8_printf(sb, "srt_entry_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->string.str - text.str), it->string.len, it->filepath_index);
|
|
||||||
// }
|
|
||||||
// sb8_printf(sb, "};\n");
|
|
||||||
// s8_t text_entries = sb8_serial_end(scratch.arena, sb);
|
|
||||||
// os_write(s8("entries.inc"), text_entries);
|
|
||||||
// }
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
debugf("url = %S", url);
|
||||||
|
open_link(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_focus) {
|
if (set_focus) {
|
||||||
@@ -567,18 +158,18 @@ fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (text_input_sig.text_changed) {
|
if (text_input_sig.text_changed) {
|
||||||
res_search_start(res);
|
res->search_idx = 0;
|
||||||
set_focus = true;
|
set_focus = true;
|
||||||
}
|
}
|
||||||
if (text_input_sig.text_commit) {
|
if (text_input_sig.text_commit) {
|
||||||
if (os_is_abs(res->search_text_input.string)) {
|
// if (os_is_abs(res->search_text_input.string)) {
|
||||||
res_add_folder(res, res->search_text_input.string);
|
// res_add_folder(res, res->search_text_input.string);
|
||||||
ui_text_clear(&res->search_text_input);
|
// ui_text_clear(&res->search_text_input);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(res->error_messages.cap == 10000);
|
// assert(res->error_messages.cap == 10000);
|
||||||
assert(res->search_matches.cap == 10000000);
|
// assert(res->search_matches.cap == 10000000);
|
||||||
|
|
||||||
ui_end_scroller(scroller);
|
ui_end_scroller(scroller);
|
||||||
ui_end_build();
|
ui_end_build();
|
||||||
@@ -590,3 +181,4 @@ fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i
|
|||||||
rn_end();
|
rn_end();
|
||||||
ui_end_frame();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -21,8 +21,8 @@ struct rn_state_t {
|
|||||||
rn_state_t *rn;
|
rn_state_t *rn;
|
||||||
|
|
||||||
fn void rn_init(ma_arena_t *perm, f32 font_size, f32 dpr) {
|
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);
|
tcx->rn_ctx = ma_push_type(perm, rn_state_t);
|
||||||
rn = tcx->data[tcx_slot_rn];
|
rn = tcx->rn_ctx;
|
||||||
|
|
||||||
rn->main_font = ma_push_type(perm, rn_font_t);
|
rn->main_font = ma_push_type(perm, rn_font_t);
|
||||||
rn_reload_font(font_size, dpr);
|
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, ...) {
|
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);
|
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) {
|
fn void rn_begin_frame(app_frame_t *frame) {
|
||||||
rn = tcx->data[tcx_slot_rn];
|
rn = tcx->rn_ctx;
|
||||||
rn->frame = frame;
|
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) {
|
fn void run_tests(void) {
|
||||||
test_string16();
|
|
||||||
test_hash_table();
|
|
||||||
test_intern_table();
|
|
||||||
test_array();
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user