transcript browser working
This commit is contained in:
@@ -175,3 +175,11 @@ fn ma_temp_t ma_begin_scratch2(ma_arena_t *a, ma_arena_t *b) {
|
|||||||
ma_arena_t *conflicts[] = {a, b};
|
ma_arena_t *conflicts[] = {a, b};
|
||||||
return ma_begin_scratch_ex(conflicts, 2);
|
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);
|
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_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_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
|
// pop
|
||||||
|
|||||||
@@ -159,10 +159,13 @@ fn void os_advance(os_iter_t *it) {
|
|||||||
|
|
||||||
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
||||||
it->name = s8_from_s16(it->arena, s16_make(data->cFileName, str16_len(data->cFileName)));
|
it->name = s8_from_s16(it->arena, s16_make(data->cFileName, str16_len(data->cFileName)));
|
||||||
|
|
||||||
char *is_dir = it->is_directory ? "/" : "";
|
char *is_dir = it->is_directory ? "/" : "";
|
||||||
char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
|
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->rel = s8_printf(it->arena, "%S%s%S%s", it->path, separator, it->name, is_dir);
|
||||||
it->abs = os_abs(it->arena, it->rel);
|
it->abs = os_abs(it->arena, it->rel);
|
||||||
|
s8_normalize_path_unsafe(it->rel);
|
||||||
|
s8_normalize_path_unsafe(it->abs);
|
||||||
it->is_valid = true;
|
it->is_valid = true;
|
||||||
|
|
||||||
if (it->is_directory) {
|
if (it->is_directory) {
|
||||||
|
|||||||
@@ -20,9 +20,13 @@ fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
|
|||||||
mt_tweak_f32(font_size, 30, 4, 200);
|
mt_tweak_f32(font_size, 30, 4, 200);
|
||||||
mt_tweak_f32(_font_size, 30, 30, 30);
|
mt_tweak_f32(_font_size, 30, 30, 30);
|
||||||
|
|
||||||
ma_arena_t *perm = &tcx->perm;
|
rn_init(&tcx->perm, font_size, frame->dpr);
|
||||||
rn_init(perm, font_size, frame->dpr);
|
ui_init(&tcx->perm);
|
||||||
ui_init(perm);
|
|
||||||
|
global_res = ma_push_type(&tcx->perm, res_t);
|
||||||
|
res_init(global_res);
|
||||||
|
res_add_folder(global_res, s8("D:/videos/jiang"));
|
||||||
|
// res_add_folder(global_res, s8("D:/tools"));
|
||||||
return true;
|
return true;
|
||||||
} else if (frame->first_event->kind == app_event_kind_reload) {
|
} else if (frame->first_event->kind == app_event_kind_reload) {
|
||||||
ui_reload();
|
ui_reload();
|
||||||
|
|||||||
+369
-135
@@ -1,103 +1,8 @@
|
|||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
s8_t cmds[] = {
|
|
||||||
s8("show data tab"),
|
|
||||||
s8("show log tab"),
|
|
||||||
s8("show menus tab"),
|
|
||||||
s8("show menus tab2"),
|
|
||||||
s8("show menus tab3"),
|
|
||||||
s8("show menus tab4"),
|
|
||||||
s8("show menus tab5"),
|
|
||||||
s8("show menus tab6"),
|
|
||||||
s8("show menus tab8"),
|
|
||||||
s8("show menus tab7"),
|
|
||||||
s8("show menus tab9"),
|
|
||||||
s8("show menus tab0"),
|
|
||||||
s8("show menus tab11"),
|
|
||||||
s8("show menus tab22"),
|
|
||||||
s8("show menus tab33"),
|
|
||||||
s8("show menus tab44"),
|
|
||||||
s8("show menus tab55"),
|
|
||||||
s8("show menus tab66"),
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
locl char buff[128];
|
|
||||||
locl ui_text_input_t text_input;
|
|
||||||
ui_signal_t text_input_sig;
|
|
||||||
text_input.str = buff;
|
|
||||||
text_input.cap = lengthof(buff);
|
|
||||||
|
|
||||||
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(&text_input, .sim_even_if_no_focus = true, .keyboard_nav = false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ma_temp_t scratch = ma_begin_scratch();
|
|
||||||
s8_t needle = s8_make(text_input.str, text_input.len);
|
|
||||||
fuzzy_pair_t *pairs = s8_fuzzy_rate_array(scratch.arena, needle, cmds, lengthof(cmds));
|
|
||||||
if (text_input_sig.text_changed) {
|
|
||||||
set_focus = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)lengthof(cmds),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
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) {
|
|
||||||
s8_t text = cmds[pairs[i].index];
|
|
||||||
ui_box_t *box = ui_box(.string = text, .flags = { .draw_rect = true, .draw_text = true, .keyboard_nav = true });
|
|
||||||
ui_signal_from_box(box);
|
|
||||||
|
|
||||||
if (set_focus) {
|
|
||||||
ui->focus = box->id;
|
|
||||||
set_focus = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text_input_sig.text_commit) {
|
|
||||||
fuzzy_pair_t *pair = &pairs[0];
|
|
||||||
// ui_g_panel = (i32)pair->index + 1;
|
|
||||||
ui_text_clear(&text_input);
|
|
||||||
set_focus = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui_end_scroller(scroller);
|
|
||||||
ui_end_build();
|
|
||||||
ma_end_scratch(scratch);
|
|
||||||
}
|
|
||||||
|
|
||||||
rn_begin(white_color);
|
|
||||||
ui_draw();
|
|
||||||
rn_end();
|
|
||||||
ui_end_frame();
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
// work queue
|
// work queue
|
||||||
|
|
||||||
#define WORK_FUNCTION(name) void name(void *data)
|
#define FN_WORK(name) void name(void *data)
|
||||||
typedef WORK_FUNCTION(work_queue_callback_t);
|
typedef FN_WORK(work_queue_callback_t);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
work_queue_callback_t *callback;
|
work_queue_callback_t *callback;
|
||||||
@@ -106,32 +11,32 @@ typedef struct {
|
|||||||
|
|
||||||
typedef struct work_queue_t work_queue_t;
|
typedef struct work_queue_t work_queue_t;
|
||||||
struct work_queue_t {
|
struct work_queue_t {
|
||||||
int32_t thread_count;
|
i32 thread_count;
|
||||||
work_queue_entry_t entries[256];
|
work_queue_entry_t entries[1024];
|
||||||
int64_t volatile index_to_write;
|
i64 volatile index_to_write;
|
||||||
int64_t volatile index_to_read;
|
i64 volatile index_to_read;
|
||||||
int64_t volatile completion_index;
|
i64 volatile completion_index;
|
||||||
int64_t volatile completion_goal;
|
i64 volatile completion_goal;
|
||||||
void *semaphore;
|
void *semaphore;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct thread_startup_info_t thread_startup_info_t;
|
typedef struct thread_startup_info_t thread_startup_info_t;
|
||||||
struct thread_startup_info_t {
|
struct thread_startup_info_t {
|
||||||
uint32_t thread_id;
|
u32 thread_id;
|
||||||
int32_t thread_index;
|
i32 thread_index;
|
||||||
work_queue_t *queue;
|
work_queue_t *queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn int64_t atomic_increment(volatile int64_t *i) {
|
fn i64 atomic_increment(volatile i64 *i) {
|
||||||
return InterlockedIncrement64(i);
|
return InterlockedIncrement64(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int64_t atomic_compare_and_swap(volatile int64_t *dst, int64_t exchange, int64_t comperand) {
|
fn i64 atomic_compare_and_swap(volatile i64 *dst, i64 exchange, i64 comperand) {
|
||||||
return InterlockedCompareExchange64(dst, exchange, comperand);
|
return InterlockedCompareExchange64(dst, exchange, comperand);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void work_queue_push(work_queue_t *wq, void *data, work_queue_callback_t *callback) {
|
fn void work_queue_push(work_queue_t *wq, void *data, work_queue_callback_t *callback) {
|
||||||
uint32_t new_index = (wq->index_to_write + 1) % lengthof(wq->entries);
|
u32 new_index = (wq->index_to_write + 1) % lengthof(wq->entries);
|
||||||
assert(new_index != wq->index_to_read);
|
assert(new_index != wq->index_to_read);
|
||||||
|
|
||||||
work_queue_entry_t *entry = wq->entries + wq->index_to_write;
|
work_queue_entry_t *entry = wq->entries + wq->index_to_write;
|
||||||
@@ -146,10 +51,10 @@ fn void work_queue_push(work_queue_t *wq, void *data, work_queue_callback_t *cal
|
|||||||
|
|
||||||
fn b8 work_queue_try_doing_work(work_queue_t *wq) {
|
fn b8 work_queue_try_doing_work(work_queue_t *wq) {
|
||||||
b8 should_sleep = false;
|
b8 should_sleep = false;
|
||||||
int64_t original_index_to_read = wq->index_to_read;
|
i64 original_index_to_read = wq->index_to_read;
|
||||||
int64_t new_index_to_read = (original_index_to_read + 1) % lengthof(wq->entries);
|
i64 new_index_to_read = (original_index_to_read + 1) % lengthof(wq->entries);
|
||||||
if (original_index_to_read != wq->index_to_write) {
|
if (original_index_to_read != wq->index_to_write) {
|
||||||
int64_t index = atomic_compare_and_swap(&wq->index_to_read, new_index_to_read, original_index_to_read);
|
i64 index = atomic_compare_and_swap(&wq->index_to_read, new_index_to_read, original_index_to_read);
|
||||||
if (index == original_index_to_read) {
|
if (index == original_index_to_read) {
|
||||||
work_queue_entry_t *entry = wq->entries + index;
|
work_queue_entry_t *entry = wq->entries + index;
|
||||||
entry->callback(entry->data);
|
entry->callback(entry->data);
|
||||||
@@ -164,7 +69,8 @@ fn b8 work_queue_try_doing_work(work_queue_t *wq) {
|
|||||||
fn DWORD WINAPI work_queue_thread_entry(LPVOID param) {
|
fn DWORD WINAPI work_queue_thread_entry(LPVOID param) {
|
||||||
thread_startup_info_t *ti = (thread_startup_info_t *)param;
|
thread_startup_info_t *ti = (thread_startup_info_t *)param;
|
||||||
|
|
||||||
os_core_init();
|
tcx = &global_thread_context;
|
||||||
|
os_win32_register_crash_handler();
|
||||||
tcx->thread_index = ti->thread_index;
|
tcx->thread_index = ti->thread_index;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (work_queue_try_doing_work(ti->queue)) {
|
if (work_queue_try_doing_work(ti->queue)) {
|
||||||
@@ -173,7 +79,7 @@ fn DWORD WINAPI work_queue_thread_entry(LPVOID param) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void work_queue_init(work_queue_t *queue, uint32_t thread_count, thread_startup_info_t *info) {
|
fn void work_queue_init(work_queue_t *queue, thread_startup_info_t *info, u32 thread_count) {
|
||||||
queue->thread_count = thread_count;
|
queue->thread_count = thread_count;
|
||||||
queue->index_to_read = 0;
|
queue->index_to_read = 0;
|
||||||
queue->index_to_write = 0;
|
queue->index_to_write = 0;
|
||||||
@@ -182,7 +88,7 @@ fn void work_queue_init(work_queue_t *queue, uint32_t thread_count, thread_start
|
|||||||
queue->semaphore = CreateSemaphoreExA(0, 0, thread_count, 0, 0, SEMAPHORE_ALL_ACCESS);
|
queue->semaphore = CreateSemaphoreExA(0, 0, thread_count, 0, 0, SEMAPHORE_ALL_ACCESS);
|
||||||
assert(queue->semaphore != INVALID_HANDLE_VALUE);
|
assert(queue->semaphore != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < thread_count; i++) {
|
for (u32 i = 0; i < thread_count; i++) {
|
||||||
thread_startup_info_t *ti = info + i;
|
thread_startup_info_t *ti = info + i;
|
||||||
ti->thread_index = i;
|
ti->thread_index = i;
|
||||||
ti->queue = queue;
|
ti->queue = queue;
|
||||||
@@ -206,6 +112,52 @@ fn void work_queue_wait(work_queue_t *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
|
// mutex
|
||||||
|
|
||||||
@@ -244,18 +196,13 @@ fn void mutex_unlock(mutex_t mutex) {
|
|||||||
|
|
||||||
typedef struct srt_entry_t srt_entry_t;
|
typedef struct srt_entry_t srt_entry_t;
|
||||||
struct srt_entry_t {
|
struct srt_entry_t {
|
||||||
srt_entry_t *next;
|
|
||||||
u16 hour, minute, second;
|
u16 hour, minute, second;
|
||||||
s8_t string;
|
s8_t string;
|
||||||
|
s8_t filepath;
|
||||||
};
|
};
|
||||||
|
typedef array(srt_entry_t) array_srt_entry_t;
|
||||||
|
|
||||||
typedef struct srt_result_t srt_result_t;
|
fn array_srt_entry_t srt_parse(ma_arena_t *arena, s8_t filename) {
|
||||||
struct srt_result_t {
|
|
||||||
srt_entry_t *first;
|
|
||||||
srt_entry_t *last;
|
|
||||||
};
|
|
||||||
|
|
||||||
srt_result_t srt_parse(ma_arena_t *arena, s8_t filename) {
|
|
||||||
s8_t content = os_read(arena, filename);
|
s8_t content = os_read(arena, filename);
|
||||||
sb8_t lines = s8_split(arena, content, s8("\n"), s8_split_cleanup);
|
sb8_t lines = s8_split(arena, content, s8("\n"), s8_split_cleanup);
|
||||||
|
|
||||||
@@ -269,7 +216,8 @@ srt_result_t srt_parse(ma_arena_t *arena, s8_t filename) {
|
|||||||
// time interval
|
// time interval
|
||||||
// text
|
// text
|
||||||
int section_number = 1;
|
int section_number = 1;
|
||||||
srt_result_t result = {0};
|
array_srt_entry_t result = {0};
|
||||||
|
result.alo = malo(arena);
|
||||||
for (sb8_node_t *it = lines.first; it;) {
|
for (sb8_node_t *it = lines.first; it;) {
|
||||||
// parse section number
|
// parse section number
|
||||||
long num = strtol(it->str, NULL, 10);
|
long num = strtol(it->str, NULL, 10);
|
||||||
@@ -282,17 +230,16 @@ srt_result_t srt_parse(ma_arena_t *arena, s8_t filename) {
|
|||||||
entry.hour = (u16)strtol(it->str, NULL, 10);
|
entry.hour = (u16)strtol(it->str, NULL, 10);
|
||||||
entry.minute = (u16)strtol(it->str + 3, NULL, 10);
|
entry.minute = (u16)strtol(it->str + 3, NULL, 10);
|
||||||
entry.second = (u16)strtol(it->str + 6, NULL, 10);
|
entry.second = (u16)strtol(it->str + 6, NULL, 10);
|
||||||
|
entry.filepath = filename;
|
||||||
it = it->next;
|
it = it->next;
|
||||||
|
|
||||||
// parse text
|
// parse text
|
||||||
s8_t next_section_number = s8_printf(arena, "%d", section_number);
|
s8_t next_section_number = s8_printf(arena, "%d", section_number);
|
||||||
while (it && !s8_are_equal(next_section_number, it->string)) {
|
while (it && !s8_are_equal(next_section_number, it->string)) {
|
||||||
b8 duplicate = result.last && s8_are_equal(it->string, result.last->string);
|
b8 duplicate = result.len && s8_are_equal(it->string, array_last(&result).string);
|
||||||
if (!duplicate) {
|
if (!duplicate) {
|
||||||
srt_entry_t *entry_copy = ma_push_type(arena, srt_entry_t);
|
entry.string = it->string;
|
||||||
entry_copy[0] = entry;
|
array_add(&result, entry);
|
||||||
entry_copy->string = it->string;
|
|
||||||
SLLQ_APPEND(result.first, result.last, entry_copy);
|
|
||||||
}
|
}
|
||||||
it = it->next;
|
it = it->next;
|
||||||
}
|
}
|
||||||
@@ -301,14 +248,301 @@ srt_result_t srt_parse(ma_arena_t *arena, s8_t filename) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn_test void testing_out_things(void) {
|
///////////////////////////////
|
||||||
mutex_t mutex = mutex_create();
|
// multithreaded resource loading
|
||||||
mutex_lock(mutex);
|
|
||||||
mutex_unlock(mutex);
|
|
||||||
mutex_destroy(mutex);
|
|
||||||
|
|
||||||
|
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_init(res_t *res) {
|
||||||
|
work_queue_init(&res->work_queue, res->thread_info, lengthof(res->thread_info));
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
FN_WORK(res_parse_thread) {
|
||||||
|
res_parse_work_t *pair = (res_parse_work_t *)data;
|
||||||
ma_temp_t scratch = ma_begin_scratch();
|
ma_temp_t scratch = ma_begin_scratch();
|
||||||
srt_result_t result = srt_parse(scratch.arena, s8("D:\\videos\\zizek\\‘Hegel and the Spirit of Distrust’ Sven-Olov Wallenstein & Slavoj Žižek in Conversation [a7gEN-Rxr4c].en.srt"));
|
array_srt_entry_t srt = srt_parse(scratch.arena, pair->srt);
|
||||||
unused(result);
|
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->misc_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;
|
||||||
|
}
|
||||||
|
// @leak
|
||||||
|
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);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
res_t *global_res = NULL;
|
||||||
|
// @todo @core make temp arena normal pointer in ctx, dont init on win32 etc.
|
||||||
|
// @todo @core make it sure that not too much arenas are initialized for threads
|
||||||
|
// @todo @ui scrolling when focused button goes out of screen
|
||||||
|
fn_test void testing_out_things(void) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
// 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 = global_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_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) - strlen(res->search_prompt) * 2;
|
||||||
|
|
||||||
|
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);
|
||||||
|
debugf("%S\n", cmd);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user