2023-04-12 14:21:03 +00:00
|
|
|
/************************************************************//**
|
|
|
|
*
|
|
|
|
* @file: main.c
|
|
|
|
* @author: Martin Fouilleul
|
|
|
|
* @date: 11/04/2023
|
|
|
|
*
|
|
|
|
*****************************************************************/
|
|
|
|
#include<stdio.h>
|
2023-04-14 09:48:36 +00:00
|
|
|
#include<errno.h>
|
2023-04-12 14:21:03 +00:00
|
|
|
#include<pthread.h>
|
|
|
|
#include<math.h>
|
|
|
|
|
|
|
|
#define MG_INCLUDE_GL_API
|
|
|
|
#include"milepost.h"
|
2023-04-26 12:57:12 +00:00
|
|
|
#include"graphics_common.h"
|
2023-04-12 14:21:03 +00:00
|
|
|
|
2023-06-11 16:21:04 +00:00
|
|
|
#include"orca_app.h"
|
2023-04-17 16:13:07 +00:00
|
|
|
|
|
|
|
#include"memory_impl.c"
|
2023-06-11 16:21:04 +00:00
|
|
|
#include"io_impl.c"
|
2023-04-12 14:21:03 +00:00
|
|
|
|
2023-04-18 12:33:47 +00:00
|
|
|
|
2023-06-11 16:21:04 +00:00
|
|
|
#define LOG_SUBSYSTEM "Orca"
|
2023-04-14 09:48:36 +00:00
|
|
|
|
2023-04-18 08:45:16 +00:00
|
|
|
int orca_assert(const char* file, const char* function, int line, const char* src, const char* note)
|
2023-04-17 16:13:07 +00:00
|
|
|
{
|
2023-04-18 08:45:16 +00:00
|
|
|
mem_arena* scratch = mem_scratch();
|
|
|
|
str8 msg = str8_pushf(scratch,
|
|
|
|
"Assertion failed in function %s() in file \"%s\", line %i:\n%s\nNote: %s\n",
|
|
|
|
function,
|
|
|
|
file,
|
|
|
|
line,
|
|
|
|
src,
|
|
|
|
note);
|
|
|
|
|
|
|
|
const char* msgCStr = str8_to_cstring(scratch, msg);
|
2023-04-18 16:06:47 +00:00
|
|
|
log_error(msgCStr);
|
2023-04-18 08:45:16 +00:00
|
|
|
|
|
|
|
const char* options[] = {"OK"};
|
|
|
|
mp_alert_popup("Assertion Failed", msgCStr, 1, options);
|
|
|
|
|
|
|
|
//TODO: should terminate more gracefully...
|
|
|
|
exit(-1);
|
2023-04-17 16:13:07 +00:00
|
|
|
}
|
|
|
|
|
2023-04-19 09:28:53 +00:00
|
|
|
mg_font orca_font_create(const char* resourcePath)
|
2023-04-14 09:48:36 +00:00
|
|
|
{
|
|
|
|
//NOTE(martin): create default font
|
2023-06-09 15:28:33 +00:00
|
|
|
str8 fontPath = path_executable_relative(mem_scratch(), STR8(resourcePath));
|
2023-04-14 09:48:36 +00:00
|
|
|
char* fontPathCString = str8_to_cstring(mem_scratch(), fontPath);
|
|
|
|
|
|
|
|
FILE* fontFile = fopen(fontPathCString, "r");
|
|
|
|
if(!fontFile)
|
|
|
|
{
|
2023-04-18 16:06:47 +00:00
|
|
|
log_error("Could not load font file '%s': %s\n", fontPathCString, strerror(errno));
|
2023-04-14 09:48:36 +00:00
|
|
|
return(mg_font_nil());
|
|
|
|
}
|
|
|
|
unsigned char* fontData = 0;
|
|
|
|
fseek(fontFile, 0, SEEK_END);
|
|
|
|
u32 fontDataSize = ftell(fontFile);
|
|
|
|
rewind(fontFile);
|
|
|
|
fontData = (unsigned char*)malloc(fontDataSize);
|
|
|
|
fread(fontData, 1, fontDataSize, fontFile);
|
|
|
|
fclose(fontFile);
|
|
|
|
|
|
|
|
unicode_range ranges[5] = {UNICODE_RANGE_BASIC_LATIN,
|
|
|
|
UNICODE_RANGE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
|
|
|
|
UNICODE_RANGE_LATIN_EXTENDED_A,
|
|
|
|
UNICODE_RANGE_LATIN_EXTENDED_B,
|
|
|
|
UNICODE_RANGE_SPECIALS};
|
|
|
|
|
|
|
|
mg_font font = mg_font_create_from_memory(fontDataSize, fontData, 5, ranges);
|
|
|
|
|
|
|
|
free(fontData);
|
|
|
|
|
|
|
|
return(font);
|
|
|
|
}
|
|
|
|
|
2023-04-19 09:28:53 +00:00
|
|
|
mg_font mg_font_create_default()
|
|
|
|
{
|
|
|
|
return(orca_font_create("../resources/OpenSansLatinSubset.ttf"));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-11 16:21:04 +00:00
|
|
|
orca_app __orcaApp = {0};
|
2023-04-12 14:21:03 +00:00
|
|
|
|
2023-06-11 16:21:04 +00:00
|
|
|
orca_app* orca_app_get()
|
2023-04-19 09:28:53 +00:00
|
|
|
{
|
2023-06-11 16:21:04 +00:00
|
|
|
return(&__orcaApp);
|
|
|
|
}
|
2023-04-19 09:28:53 +00:00
|
|
|
|
2023-06-11 16:21:04 +00:00
|
|
|
orca_runtime* orca_runtime_get()
|
2023-04-12 14:21:03 +00:00
|
|
|
{
|
2023-06-11 16:21:04 +00:00
|
|
|
return(&__orcaApp.runtime);
|
|
|
|
}
|
2023-05-09 16:44:23 +00:00
|
|
|
|
2023-04-20 13:47:18 +00:00
|
|
|
void orca_log(log_level level,
|
|
|
|
int fileLen,
|
|
|
|
char* file,
|
|
|
|
int functionLen,
|
|
|
|
char* function,
|
|
|
|
int line,
|
|
|
|
int msgLen,
|
|
|
|
char* msg)
|
|
|
|
{
|
|
|
|
orca_debug_overlay* debug = &__orcaApp.debugOverlay;
|
|
|
|
|
|
|
|
//NOTE: recycle first entry if we exceeded the max entry count
|
|
|
|
debug->entryCount++;
|
|
|
|
if(debug->entryCount > debug->maxEntries)
|
|
|
|
{
|
|
|
|
log_entry* e = list_pop_entry(&debug->logEntries, log_entry, listElt);
|
|
|
|
if(e)
|
|
|
|
{
|
|
|
|
list_push(&debug->logFreeList, &e->listElt);
|
|
|
|
debug->entryCount--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
u64 cap = sizeof(log_entry)+fileLen+functionLen+msgLen;
|
|
|
|
|
|
|
|
//NOTE: allocate a new entry
|
|
|
|
//TODO: should probably use a buddy allocator over the arena or something
|
|
|
|
log_entry* entry = 0;
|
|
|
|
for_list(&debug->logFreeList, elt, log_entry, listElt)
|
|
|
|
{
|
|
|
|
if(elt->cap >= cap)
|
|
|
|
{
|
|
|
|
list_remove(&debug->logFreeList, &elt->listElt);
|
|
|
|
entry = elt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!entry)
|
|
|
|
{
|
|
|
|
char* mem = mem_arena_alloc(&debug->logArena, cap);
|
|
|
|
entry = (log_entry*)mem;
|
|
|
|
entry->cap = cap;
|
|
|
|
}
|
|
|
|
char* payload = (char*)entry + sizeof(log_entry);
|
|
|
|
|
|
|
|
entry->file.len = fileLen;
|
|
|
|
entry->file.ptr = payload;
|
|
|
|
payload += entry->file.len;
|
|
|
|
|
|
|
|
entry->function.len = functionLen;
|
|
|
|
entry->function.ptr = payload;
|
|
|
|
payload += entry->function.len;
|
|
|
|
|
|
|
|
entry->msg.len = msgLen;
|
|
|
|
entry->msg.ptr = payload;
|
|
|
|
payload += entry->msg.len;
|
|
|
|
|
|
|
|
memcpy(entry->file.ptr, file, fileLen);
|
2023-04-21 08:02:17 +00:00
|
|
|
memcpy(entry->function.ptr, function, functionLen);
|
2023-04-20 13:47:18 +00:00
|
|
|
memcpy(entry->msg.ptr, msg, msgLen);
|
|
|
|
|
|
|
|
entry->level = level;
|
|
|
|
entry->line = line;
|
|
|
|
entry->recordIndex = debug->logEntryTotalCount;
|
|
|
|
debug->logEntryTotalCount++;
|
|
|
|
|
|
|
|
list_push_back(&debug->logEntries, &entry->listElt);
|
|
|
|
|
|
|
|
log_push(level,
|
|
|
|
str8_from_buffer(fileLen, file),
|
|
|
|
str8_from_buffer(functionLen, function),
|
|
|
|
line,
|
|
|
|
"%.*s\n",
|
|
|
|
msgLen,
|
|
|
|
msg);
|
|
|
|
}
|
|
|
|
|
2023-04-26 12:57:12 +00:00
|
|
|
mg_surface orca_surface_main(void)
|
|
|
|
{
|
|
|
|
return(__orcaApp.surface);
|
|
|
|
}
|
|
|
|
|
|
|
|
void orca_surface_render_commands(mg_surface surface,
|
|
|
|
mg_color clearColor,
|
|
|
|
u32 primitiveCount,
|
|
|
|
mg_primitive* primitives,
|
|
|
|
u32 eltCount,
|
|
|
|
mg_path_elt* elements)
|
|
|
|
{
|
|
|
|
orca_app* app = &__orcaApp;
|
|
|
|
|
|
|
|
char* memBase = app->runtime.wasmMemory.ptr;
|
|
|
|
u32 memSize = app->runtime.wasmMemory.committed;
|
|
|
|
if( ((char*)primitives > memBase)
|
|
|
|
&&((char*)primitives + primitiveCount*sizeof(mg_primitive) - memBase <= memSize)
|
|
|
|
&&((char*)elements > memBase)
|
|
|
|
&&((char*)elements + eltCount*sizeof(mg_path_elt) - memBase <= memSize))
|
|
|
|
{
|
|
|
|
mg_surface_render_commands(surface,
|
|
|
|
clearColor,
|
|
|
|
primitiveCount,
|
|
|
|
primitives,
|
|
|
|
eltCount,
|
|
|
|
elements);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-20 13:47:18 +00:00
|
|
|
void debug_overlay_toggle(orca_debug_overlay* overlay)
|
|
|
|
{
|
|
|
|
overlay->show = !overlay->show;
|
|
|
|
mg_surface_set_hidden(overlay->surface, !overlay->show);
|
|
|
|
|
|
|
|
if(overlay->show)
|
|
|
|
{
|
|
|
|
overlay->logScrollToLast = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void log_entry_ui(orca_debug_overlay* overlay, log_entry* entry)
|
|
|
|
{
|
|
|
|
static const char* levelNames[] = {"Error: ", "Warning: ", "Info: "};
|
|
|
|
static const mg_color levelColors[] = {{0.8, 0, 0, 1},
|
|
|
|
{1, 0.5, 0, 1},
|
|
|
|
{0, 0.8, 0, 1}};
|
|
|
|
|
|
|
|
static const mg_color bgColors[3][2] = {//errors
|
|
|
|
{{0.6, 0, 0, 0.5}, {0.8, 0, 0, 0.5}},
|
|
|
|
//warning
|
|
|
|
{{0.4, 0.4, 0.4, 0.5}, {0.5, 0.5, 0.5, 0.5}},
|
|
|
|
//info
|
|
|
|
{{0.4, 0.4, 0.4, 0.5}, {0.5, 0.5, 0.5, 0.5}}};
|
|
|
|
|
|
|
|
ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1},
|
|
|
|
.size.height = {UI_SIZE_CHILDREN},
|
|
|
|
.layout.axis = UI_AXIS_Y,
|
|
|
|
.layout.margin.x = 10,
|
|
|
|
.layout.margin.y = 5,
|
|
|
|
.bgColor = bgColors[entry->level][entry->recordIndex & 1]},
|
|
|
|
UI_STYLE_SIZE
|
|
|
|
|UI_STYLE_LAYOUT_AXIS
|
|
|
|
|UI_STYLE_LAYOUT_MARGINS
|
|
|
|
|UI_STYLE_BG_COLOR);
|
|
|
|
|
|
|
|
str8 key = str8_pushf(mem_scratch(), "%ull", entry->recordIndex);
|
|
|
|
|
|
|
|
ui_container_str8(key, UI_FLAG_DRAW_BACKGROUND)
|
|
|
|
{
|
|
|
|
ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1},
|
|
|
|
.size.height = {UI_SIZE_CHILDREN},
|
|
|
|
.layout.axis = UI_AXIS_X},
|
|
|
|
UI_STYLE_SIZE
|
|
|
|
|UI_STYLE_LAYOUT_AXIS);
|
|
|
|
|
|
|
|
ui_container("header", 0)
|
|
|
|
{
|
|
|
|
ui_style_next(&(ui_style){.color = levelColors[entry->level],
|
|
|
|
.font = overlay->fontBold},
|
|
|
|
UI_STYLE_COLOR
|
|
|
|
|UI_STYLE_FONT);
|
|
|
|
ui_label(levelNames[entry->level]);
|
|
|
|
|
|
|
|
str8 loc = str8_pushf(mem_scratch(),
|
|
|
|
"%.*s() in %.*s:%i:",
|
|
|
|
str8_ip(entry->file),
|
|
|
|
str8_ip(entry->function),
|
|
|
|
entry->line);
|
|
|
|
ui_label_str8(loc);
|
|
|
|
}
|
|
|
|
ui_label_str8(entry->msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-12 14:21:03 +00:00
|
|
|
char m3_type_to_tag(M3ValueType type)
|
|
|
|
{
|
|
|
|
switch(type)
|
|
|
|
{
|
|
|
|
case c_m3Type_none:
|
|
|
|
return('v');
|
|
|
|
case c_m3Type_i32:
|
|
|
|
return('i');
|
|
|
|
case c_m3Type_i64:
|
|
|
|
return('I');
|
|
|
|
case c_m3Type_f32:
|
|
|
|
return('f');
|
|
|
|
case c_m3Type_f64:
|
|
|
|
return('d');
|
|
|
|
|
|
|
|
case c_m3Type_unknown:
|
2023-06-17 18:10:11 +00:00
|
|
|
default:
|
2023-04-12 14:21:03 +00:00
|
|
|
return('!');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-17 16:13:07 +00:00
|
|
|
void orca_runtime_init(orca_runtime* runtime)
|
2023-04-12 14:21:03 +00:00
|
|
|
{
|
2023-04-17 16:13:07 +00:00
|
|
|
memset(runtime, 0, sizeof(orca_runtime));
|
2023-04-12 14:21:03 +00:00
|
|
|
mem_base_allocator* allocator = mem_base_allocator_default();
|
2023-04-17 16:13:07 +00:00
|
|
|
runtime->wasmMemory.committed = 0;
|
|
|
|
runtime->wasmMemory.reserved = 4ULL<<30;
|
|
|
|
runtime->wasmMemory.ptr = mem_base_reserve(allocator, runtime->wasmMemory.reserved);
|
2023-04-12 14:21:03 +00:00
|
|
|
}
|
|
|
|
|
2023-04-20 13:47:18 +00:00
|
|
|
#include"bindgen_core_api.c"
|
|
|
|
#include"canvas_api_bind.c"
|
2023-05-09 16:44:23 +00:00
|
|
|
#include"io_api_bind_gen.c"
|
|
|
|
|
2023-04-20 13:47:18 +00:00
|
|
|
#include"bindgen_gles_api.c"
|
|
|
|
#include"manual_gles_api.c"
|
|
|
|
|
2023-04-12 14:21:03 +00:00
|
|
|
void* orca_runloop(void* user)
|
|
|
|
{
|
2023-04-17 16:13:07 +00:00
|
|
|
orca_app* app = &__orcaApp;
|
|
|
|
|
|
|
|
orca_runtime_init(&app->runtime);
|
2023-04-12 14:21:03 +00:00
|
|
|
|
|
|
|
//NOTE: loads wasm module
|
|
|
|
const char* bundleNameCString = "module";
|
2023-06-09 15:28:33 +00:00
|
|
|
str8 modulePath = path_executable_relative(mem_scratch(), STR8("../app/wasm/module.wasm"));
|
2023-04-12 14:21:03 +00:00
|
|
|
const char* modulePathCString = str8_to_cstring(mem_scratch(), modulePath);
|
|
|
|
|
|
|
|
FILE* file = fopen(modulePathCString, "rb");
|
|
|
|
if(!file)
|
|
|
|
{
|
2023-04-18 16:06:47 +00:00
|
|
|
log_error("Couldn't load wasm module at %s\n", modulePathCString);
|
2023-06-19 12:17:40 +00:00
|
|
|
|
|
|
|
char* options[] = {"OK"};
|
|
|
|
mp_alert_popup("Error",
|
|
|
|
"The application couldn't load: web assembly module not found",
|
|
|
|
1,
|
|
|
|
options);
|
|
|
|
|
|
|
|
mp_request_quit();
|
2023-04-12 14:21:03 +00:00
|
|
|
return((void*)-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
u64 wasmSize = ftell(file);
|
|
|
|
rewind(file);
|
|
|
|
|
2023-04-17 16:13:07 +00:00
|
|
|
app->runtime.wasmBytecode.len = wasmSize;
|
|
|
|
app->runtime.wasmBytecode.ptr = malloc_array(char, wasmSize);
|
|
|
|
fread(app->runtime.wasmBytecode.ptr, 1, app->runtime.wasmBytecode.len, file);
|
2023-04-12 14:21:03 +00:00
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
u32 stackSize = 65536;
|
2023-04-17 16:13:07 +00:00
|
|
|
app->runtime.m3Env = m3_NewEnvironment();
|
2023-04-12 14:21:03 +00:00
|
|
|
|
2023-04-17 16:13:07 +00:00
|
|
|
app->runtime.m3Runtime = m3_NewRuntime(app->runtime.m3Env, stackSize, NULL);
|
|
|
|
m3_RuntimeSetMemoryCallbacks(app->runtime.m3Runtime, wasm_memory_resize_callback, wasm_memory_free_callback, &app->runtime.wasmMemory);
|
2023-04-12 14:21:03 +00:00
|
|
|
//NOTE: host memory will be freed when runtime is freed.
|
|
|
|
|
|
|
|
//TODO check errors
|
2023-04-17 16:13:07 +00:00
|
|
|
m3_ParseModule(app->runtime.m3Env, &app->runtime.m3Module, (u8*)app->runtime.wasmBytecode.ptr, app->runtime.wasmBytecode.len);
|
|
|
|
m3_LoadModule(app->runtime.m3Runtime, app->runtime.m3Module);
|
|
|
|
m3_SetModuleName(app->runtime.m3Module, bundleNameCString);
|
2023-04-12 14:21:03 +00:00
|
|
|
|
2023-06-09 15:28:33 +00:00
|
|
|
mem_arena_clear(mem_scratch());
|
2023-04-12 14:21:03 +00:00
|
|
|
|
|
|
|
//NOTE: bind orca APIs
|
2023-04-17 16:13:07 +00:00
|
|
|
bindgen_link_core_api(app->runtime.m3Module);
|
|
|
|
bindgen_link_canvas_api(app->runtime.m3Module);
|
2023-05-09 16:44:23 +00:00
|
|
|
bindgen_link_io_api(app->runtime.m3Module);
|
2023-04-17 16:13:07 +00:00
|
|
|
bindgen_link_gles_api(app->runtime.m3Module);
|
|
|
|
manual_link_gles_api(app->runtime.m3Module);
|
2023-04-12 14:21:03 +00:00
|
|
|
|
|
|
|
//NOTE: compile
|
2023-04-17 16:13:07 +00:00
|
|
|
M3Result res = m3_CompileModule(app->runtime.m3Module);
|
2023-04-12 14:21:03 +00:00
|
|
|
if(res)
|
|
|
|
{
|
2023-06-16 18:07:26 +00:00
|
|
|
M3ErrorInfo errInfo = {0};
|
2023-04-17 16:13:07 +00:00
|
|
|
m3_GetErrorInfo(app->runtime.m3Runtime, &errInfo);
|
2023-04-12 14:21:03 +00:00
|
|
|
|
2023-04-18 16:06:47 +00:00
|
|
|
log_error("wasm error: %s\n", errInfo.message);
|
2023-06-19 12:17:40 +00:00
|
|
|
|
|
|
|
char* options[] = {"OK"};
|
|
|
|
mp_alert_popup("Error",
|
|
|
|
"The application couldn't load: can't compile web assembly module",
|
|
|
|
1,
|
|
|
|
options);
|
|
|
|
|
|
|
|
mp_request_quit();
|
2023-04-17 16:13:07 +00:00
|
|
|
return((void*)-1);
|
2023-04-12 14:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//NOTE: Find and type check event handlers.
|
|
|
|
for(int i=0; i<G_EVENT_COUNT; i++)
|
|
|
|
{
|
|
|
|
const g_event_handler_desc* desc = &G_EVENT_HANDLER_DESC[i];
|
|
|
|
IM3Function handler = 0;
|
2023-04-17 16:13:07 +00:00
|
|
|
m3_FindFunction(&handler, app->runtime.m3Runtime, desc->name.ptr);
|
2023-04-12 14:21:03 +00:00
|
|
|
|
|
|
|
if(handler)
|
|
|
|
{
|
|
|
|
bool checked = false;
|
|
|
|
|
|
|
|
//NOTE: check function signature
|
|
|
|
int retCount = m3_GetRetCount(handler);
|
|
|
|
int argCount = m3_GetArgCount(handler);
|
|
|
|
if(retCount == desc->retTags.len && argCount == desc->argTags.len)
|
|
|
|
{
|
|
|
|
checked = true;
|
|
|
|
for(int retIndex = 0; retIndex < retCount; retIndex++)
|
|
|
|
{
|
|
|
|
M3ValueType m3Type = m3_GetRetType(handler, retIndex);
|
|
|
|
char tag = m3_type_to_tag(m3Type);
|
|
|
|
|
|
|
|
if(tag != desc->retTags.ptr[retIndex])
|
|
|
|
{
|
|
|
|
checked = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(checked)
|
|
|
|
{
|
|
|
|
for(int argIndex = 0; argIndex < argCount; argIndex++)
|
|
|
|
{
|
|
|
|
M3ValueType m3Type = m3_GetArgType(handler, argIndex);
|
|
|
|
char tag = m3_type_to_tag(m3Type);
|
|
|
|
|
|
|
|
if(tag != desc->argTags.ptr[argIndex])
|
|
|
|
{
|
|
|
|
checked = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(checked)
|
|
|
|
{
|
2023-04-17 16:13:07 +00:00
|
|
|
app->runtime.eventHandlers[i] = handler;
|
2023-04-12 14:21:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-04-18 16:06:47 +00:00
|
|
|
log_error("type mismatch for event handler %.*s\n", (int)desc->name.len, desc->name.ptr);
|
2023-04-12 14:21:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-11 16:21:04 +00:00
|
|
|
//NOTE: preopen the app local root dir
|
|
|
|
{
|
|
|
|
str8 localRootPath = path_executable_relative(mem_scratch(), STR8("../app/data"));
|
|
|
|
|
|
|
|
io_req req = {.op = IO_OP_OPEN_AT,
|
2023-06-16 18:07:26 +00:00
|
|
|
.open.rights = FILE_ACCESS_READ,
|
2023-06-11 16:21:04 +00:00
|
|
|
.size = localRootPath.len,
|
|
|
|
.buffer = localRootPath.ptr};
|
|
|
|
io_cmp cmp = io_wait_single_req_with_table(&req, &app->fileTable);
|
|
|
|
app->rootDir = cmp.handle;
|
|
|
|
}
|
2023-04-12 14:21:03 +00:00
|
|
|
|
|
|
|
//NOTE: prepare GL surface
|
|
|
|
mg_surface_prepare(app->surface);
|
|
|
|
|
2023-04-17 16:13:07 +00:00
|
|
|
IM3Function* eventHandlers = app->runtime.eventHandlers;
|
|
|
|
|
2023-04-12 14:21:03 +00:00
|
|
|
//NOTE: call init handler
|
|
|
|
if(eventHandlers[G_EVENT_START])
|
|
|
|
{
|
|
|
|
m3_Call(eventHandlers[G_EVENT_START], 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(eventHandlers[G_EVENT_FRAME_RESIZE])
|
|
|
|
{
|
|
|
|
mp_rect frame = mg_surface_get_frame(app->surface);
|
|
|
|
u32 width = (u32)frame.w;
|
|
|
|
u32 height = (u32)frame.h;
|
|
|
|
const void* args[2] = {&width, &height};
|
|
|
|
m3_Call(eventHandlers[G_EVENT_FRAME_RESIZE], 2, args);
|
|
|
|
}
|
|
|
|
|
2023-04-19 09:28:53 +00:00
|
|
|
ui_set_context(&app->debugOverlay.ui);
|
|
|
|
|
2023-04-12 14:21:03 +00:00
|
|
|
while(!mp_should_quit())
|
|
|
|
{
|
2023-04-19 14:19:13 +00:00
|
|
|
mp_event* event = 0;
|
|
|
|
while((event = mp_next_event(mem_scratch())) != 0)
|
2023-04-12 14:21:03 +00:00
|
|
|
{
|
2023-04-19 09:28:53 +00:00
|
|
|
if(app->debugOverlay.show)
|
|
|
|
{
|
|
|
|
ui_process_event(event);
|
|
|
|
}
|
|
|
|
|
2023-04-19 14:19:13 +00:00
|
|
|
switch(event->type)
|
2023-04-12 14:21:03 +00:00
|
|
|
{
|
|
|
|
case MP_EVENT_WINDOW_CLOSE:
|
|
|
|
{
|
|
|
|
mp_request_quit();
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case MP_EVENT_WINDOW_RESIZE:
|
|
|
|
{
|
2023-04-19 14:19:13 +00:00
|
|
|
mp_rect frame = {0, 0, event->frame.rect.w, event->frame.rect.h};
|
2023-04-19 09:28:53 +00:00
|
|
|
mg_surface_set_frame(app->surface, frame);
|
2023-04-14 09:48:36 +00:00
|
|
|
|
2023-04-20 13:47:18 +00:00
|
|
|
mg_surface_set_frame(app->debugOverlay.surface, frame);
|
|
|
|
|
2023-04-12 14:21:03 +00:00
|
|
|
if(eventHandlers[G_EVENT_FRAME_RESIZE])
|
|
|
|
{
|
2023-04-19 14:19:13 +00:00
|
|
|
u32 width = (u32)event->frame.rect.w;
|
|
|
|
u32 height = (u32)event->frame.rect.h;
|
2023-04-12 14:21:03 +00:00
|
|
|
const void* args[2] = {&width, &height};
|
|
|
|
m3_Call(eventHandlers[G_EVENT_FRAME_RESIZE], 2, args);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case MP_EVENT_MOUSE_BUTTON:
|
|
|
|
{
|
2023-04-19 14:19:13 +00:00
|
|
|
if(event->key.action == MP_KEY_PRESS)
|
2023-04-12 14:21:03 +00:00
|
|
|
{
|
|
|
|
if(eventHandlers[G_EVENT_MOUSE_DOWN])
|
|
|
|
{
|
2023-04-19 14:19:13 +00:00
|
|
|
int key = event->key.code;
|
2023-04-12 14:21:03 +00:00
|
|
|
const void* args[1] = {&key};
|
|
|
|
m3_Call(eventHandlers[G_EVENT_MOUSE_DOWN], 1, args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(eventHandlers[G_EVENT_MOUSE_UP])
|
|
|
|
{
|
2023-04-19 14:19:13 +00:00
|
|
|
int key = event->key.code;
|
2023-04-12 14:21:03 +00:00
|
|
|
const void* args[1] = {&key};
|
|
|
|
m3_Call(eventHandlers[G_EVENT_MOUSE_UP], 1, args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case MP_EVENT_MOUSE_MOVE:
|
|
|
|
{
|
|
|
|
if(eventHandlers[G_EVENT_MOUSE_MOVE])
|
|
|
|
{
|
2023-04-19 14:19:13 +00:00
|
|
|
const void* args[4] = {&event->move.x, &event->move.y, &event->move.deltaX, &event->move.deltaY};
|
2023-04-12 14:21:03 +00:00
|
|
|
m3_Call(eventHandlers[G_EVENT_MOUSE_MOVE], 4, args);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case MP_EVENT_KEYBOARD_KEY:
|
|
|
|
{
|
2023-04-19 14:19:13 +00:00
|
|
|
if(event->key.action == MP_KEY_PRESS)
|
2023-04-12 14:21:03 +00:00
|
|
|
{
|
2023-04-19 14:19:13 +00:00
|
|
|
if(event->key.code == MP_KEY_D && (event->key.mods & (MP_KEYMOD_SHIFT | MP_KEYMOD_CMD)))
|
2023-04-19 09:28:53 +00:00
|
|
|
{
|
2023-06-19 15:13:56 +00:00
|
|
|
#if 0 // EPILEPSY WARNING! on windows this has a bug which causes a pretty strong stroboscopic effect
|
2023-04-20 13:47:18 +00:00
|
|
|
debug_overlay_toggle(&app->debugOverlay);
|
2023-06-19 15:13:56 +00:00
|
|
|
#endif
|
2023-04-19 09:28:53 +00:00
|
|
|
}
|
|
|
|
|
2023-04-12 14:21:03 +00:00
|
|
|
if(eventHandlers[G_EVENT_KEY_DOWN])
|
|
|
|
{
|
2023-04-19 14:19:13 +00:00
|
|
|
const void* args[1] = {&event->key.code};
|
2023-04-12 14:21:03 +00:00
|
|
|
m3_Call(eventHandlers[G_EVENT_KEY_DOWN], 1, args);
|
|
|
|
}
|
|
|
|
}
|
2023-04-19 14:19:13 +00:00
|
|
|
else if(event->key.action == MP_KEY_RELEASE)
|
2023-04-12 14:21:03 +00:00
|
|
|
{
|
|
|
|
if(eventHandlers[G_EVENT_KEY_UP])
|
|
|
|
{
|
2023-04-19 14:19:13 +00:00
|
|
|
const void* args[1] = {&event->key.code};
|
2023-04-12 14:21:03 +00:00
|
|
|
m3_Call(eventHandlers[G_EVENT_KEY_UP], 1, args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-26 12:57:12 +00:00
|
|
|
if(eventHandlers[G_EVENT_FRAME_REFRESH])
|
|
|
|
{
|
|
|
|
m3_Call(eventHandlers[G_EVENT_FRAME_REFRESH], 0, 0);
|
|
|
|
}
|
2023-04-14 09:48:36 +00:00
|
|
|
|
2023-04-19 09:28:53 +00:00
|
|
|
if(app->debugOverlay.show)
|
|
|
|
{
|
|
|
|
ui_style debugUIDefaultStyle = {.bgColor = {0},
|
|
|
|
.color = {1, 1, 1, 1},
|
2023-04-20 13:47:18 +00:00
|
|
|
.font = app->debugOverlay.fontReg,
|
2023-04-19 09:28:53 +00:00
|
|
|
.fontSize = 16,
|
|
|
|
.borderColor = {1, 0, 0, 1},
|
|
|
|
.borderSize = 2};
|
|
|
|
|
|
|
|
ui_style_mask debugUIDefaultMask = UI_STYLE_BG_COLOR
|
|
|
|
| UI_STYLE_COLOR
|
|
|
|
| UI_STYLE_BORDER_COLOR
|
|
|
|
| UI_STYLE_BORDER_SIZE
|
|
|
|
| UI_STYLE_FONT
|
|
|
|
| UI_STYLE_FONT_SIZE;
|
|
|
|
|
2023-04-26 12:57:12 +00:00
|
|
|
mp_rect frameRect = mg_surface_get_frame(app->debugOverlay.surface);
|
|
|
|
vec2 frameSize = {frameRect.w, frameRect.h};
|
|
|
|
|
|
|
|
ui_frame(frameSize, &debugUIDefaultStyle, debugUIDefaultMask)
|
2023-04-19 09:28:53 +00:00
|
|
|
{
|
|
|
|
ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1},
|
|
|
|
.size.height = {UI_SIZE_PARENT, 1, 1}},
|
|
|
|
UI_STYLE_SIZE);
|
|
|
|
|
|
|
|
ui_container("overlay area", 0)
|
|
|
|
{
|
|
|
|
//...
|
|
|
|
}
|
|
|
|
|
|
|
|
ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1},
|
|
|
|
.size.height = {UI_SIZE_PARENT, 0.4},
|
2023-04-20 13:47:18 +00:00
|
|
|
.layout.axis = UI_AXIS_Y,
|
2023-04-19 09:28:53 +00:00
|
|
|
.bgColor = {0, 0, 0, 0.5}},
|
|
|
|
UI_STYLE_SIZE
|
2023-04-20 13:47:18 +00:00
|
|
|
|UI_STYLE_LAYOUT_AXIS
|
2023-04-19 09:28:53 +00:00
|
|
|
|UI_STYLE_BG_COLOR);
|
|
|
|
|
|
|
|
ui_container("log console", UI_FLAG_DRAW_BACKGROUND)
|
|
|
|
{
|
|
|
|
ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1},
|
2023-04-20 13:47:18 +00:00
|
|
|
.size.height = {UI_SIZE_CHILDREN},
|
|
|
|
.layout.axis = UI_AXIS_X,
|
|
|
|
.layout.spacing = 10,
|
|
|
|
.layout.margin.x = 10,
|
|
|
|
.layout.margin.y = 10},
|
|
|
|
UI_STYLE_SIZE
|
|
|
|
|UI_STYLE_LAYOUT);
|
|
|
|
|
|
|
|
ui_container("log toolbar", 0)
|
|
|
|
{
|
|
|
|
ui_style buttonStyle = {.layout.margin.x = 4,
|
|
|
|
.layout.margin.y = 4,
|
|
|
|
.roundness = 2,
|
|
|
|
.bgColor = {0, 0, 0, 0.5},
|
|
|
|
.color = {1, 1, 1, 1}};
|
|
|
|
|
|
|
|
ui_style_mask buttonStyleMask = UI_STYLE_LAYOUT_MARGINS
|
|
|
|
| UI_STYLE_ROUNDNESS
|
|
|
|
| UI_STYLE_BG_COLOR
|
|
|
|
| UI_STYLE_COLOR;
|
|
|
|
|
|
|
|
ui_style_match_after(ui_pattern_all(), &buttonStyle, buttonStyleMask);
|
|
|
|
if(ui_button("Clear").clicked)
|
|
|
|
{
|
|
|
|
for_list_safe(&app->debugOverlay.logEntries, entry, log_entry, listElt)
|
|
|
|
{
|
|
|
|
list_remove(&app->debugOverlay.logEntries, &entry->listElt);
|
|
|
|
list_push(&app->debugOverlay.logFreeList, &entry->listElt);
|
|
|
|
app->debugOverlay.entryCount--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1},
|
|
|
|
.size.height = {UI_SIZE_PARENT, 1, 1}},
|
2023-04-19 09:28:53 +00:00
|
|
|
UI_STYLE_SIZE);
|
|
|
|
|
2023-04-20 13:47:18 +00:00
|
|
|
//TODO: this is annoying to have to do that. Basically there's another 'contents' box inside ui_panel,
|
|
|
|
// and we need to change that to size according to its parent (whereas the default is sizing according
|
|
|
|
// to its children)
|
|
|
|
ui_pattern pattern = {0};
|
|
|
|
ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_OWNER});
|
|
|
|
ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = STR8("contents")});
|
|
|
|
ui_style_match_after(pattern, &(ui_style){.size.width = {UI_SIZE_PARENT, 1}}, UI_STYLE_SIZE_WIDTH);
|
|
|
|
|
|
|
|
ui_box* panel = ui_box_lookup("log view");
|
|
|
|
f32 scrollY = 0;
|
|
|
|
if(panel)
|
|
|
|
{
|
|
|
|
scrollY = panel->scroll.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
ui_panel("log view", UI_FLAG_SCROLL_WHEEL_Y)
|
2023-04-19 09:28:53 +00:00
|
|
|
{
|
2023-04-20 13:47:18 +00:00
|
|
|
panel = ui_box_top();
|
|
|
|
|
2023-04-19 09:28:53 +00:00
|
|
|
ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1},
|
2023-04-20 13:47:18 +00:00
|
|
|
.size.height = {UI_SIZE_CHILDREN},
|
|
|
|
.layout.axis = UI_AXIS_Y,
|
|
|
|
.layout.margin.y = 5},
|
|
|
|
UI_STYLE_SIZE
|
|
|
|
|UI_STYLE_LAYOUT_AXIS);
|
2023-04-19 09:28:53 +00:00
|
|
|
|
|
|
|
ui_container("contents", 0)
|
|
|
|
{
|
2023-04-20 13:47:18 +00:00
|
|
|
for_list(&app->debugOverlay.logEntries, entry, log_entry, listElt)
|
|
|
|
{
|
|
|
|
log_entry_ui(&app->debugOverlay, entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(app->debugOverlay.logScrollToLast)
|
|
|
|
{
|
|
|
|
if(panel->scroll.y >= scrollY)
|
|
|
|
{
|
|
|
|
panel->scroll.y = ClampLowBound(panel->childrenSum[1] - panel->rect.h, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
app->debugOverlay.logScrollToLast = false;
|
2023-04-19 09:28:53 +00:00
|
|
|
}
|
|
|
|
}
|
2023-04-20 13:47:18 +00:00
|
|
|
else if(panel->scroll.y >= (panel->childrenSum[1] - panel->rect.h) - 1)
|
|
|
|
{
|
|
|
|
app->debugOverlay.logScrollToLast = true;
|
|
|
|
}
|
2023-04-19 09:28:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-26 12:57:12 +00:00
|
|
|
mg_surface_prepare(app->debugOverlay.surface);
|
|
|
|
mg_canvas_set_current(app->debugOverlay.canvas);
|
2023-04-19 09:28:53 +00:00
|
|
|
ui_draw();
|
|
|
|
/*
|
|
|
|
mg_set_font(app->debugOverlay.font);
|
|
|
|
mg_set_font_size(32);
|
|
|
|
mg_set_color_rgba(0.2, 0.2, 0.2, 1);
|
|
|
|
mg_move_to(30, 30);
|
|
|
|
mg_text_outlines(STR8("Debug Overlay"));
|
|
|
|
mg_fill();
|
|
|
|
*/
|
2023-04-26 12:57:12 +00:00
|
|
|
mg_render(app->debugOverlay.surface, app->debugOverlay.canvas);
|
|
|
|
mg_surface_present(app->debugOverlay.surface);
|
2023-04-19 09:28:53 +00:00
|
|
|
}
|
2023-06-09 15:28:33 +00:00
|
|
|
mem_arena_clear(mem_scratch());
|
2023-04-12 14:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char** argv)
|
|
|
|
{
|
2023-04-18 12:33:47 +00:00
|
|
|
log_set_level(LOG_LEVEL_INFO);
|
2023-04-12 14:21:03 +00:00
|
|
|
|
|
|
|
mp_init();
|
|
|
|
mp_clock_init();
|
|
|
|
|
2023-04-19 09:28:53 +00:00
|
|
|
orca_app* orca = &__orcaApp;
|
|
|
|
|
2023-06-11 16:21:04 +00:00
|
|
|
//NOTE: create window and surfaces
|
2023-04-12 14:21:03 +00:00
|
|
|
mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610};
|
2023-04-19 09:28:53 +00:00
|
|
|
orca->window = mp_window_create(windowRect, "orca", 0);
|
2023-04-26 12:57:12 +00:00
|
|
|
orca->surface = mg_surface_create_for_window(orca->window, MG_CANVAS);
|
|
|
|
orca->canvas = mg_canvas_create();
|
2023-04-19 09:28:53 +00:00
|
|
|
|
|
|
|
mg_surface_swap_interval(orca->surface, 1);
|
2023-04-12 14:21:03 +00:00
|
|
|
|
2023-04-19 09:28:53 +00:00
|
|
|
orca->debugOverlay.show = false;
|
2023-04-26 12:57:12 +00:00
|
|
|
orca->debugOverlay.surface = mg_surface_create_for_window(orca->window, MG_CANVAS);
|
|
|
|
orca->debugOverlay.canvas = mg_canvas_create();
|
2023-04-20 13:47:18 +00:00
|
|
|
orca->debugOverlay.fontReg = orca_font_create("../resources/Menlo.ttf");
|
|
|
|
orca->debugOverlay.fontBold = orca_font_create("../resources/Menlo Bold.ttf");
|
2023-04-21 08:02:17 +00:00
|
|
|
orca->debugOverlay.maxEntries = 200;
|
2023-04-20 13:47:18 +00:00
|
|
|
mem_arena_init(&orca->debugOverlay.logArena);
|
2023-04-12 14:21:03 +00:00
|
|
|
|
2023-04-19 09:28:53 +00:00
|
|
|
mg_surface_set_hidden(orca->debugOverlay.surface, true);
|
|
|
|
|
|
|
|
//WARN: this is a workaround to avoid stalling the first few times we acquire drawables from
|
|
|
|
// the surfaces... This should probably be fixed in the implementation of mtl_surface!
|
|
|
|
for(int i=0; i<4; i++)
|
2023-04-12 14:21:03 +00:00
|
|
|
{
|
2023-04-26 12:57:12 +00:00
|
|
|
mg_surface_prepare(orca->surface);
|
|
|
|
mg_canvas_set_current(orca->canvas);
|
|
|
|
mg_render(orca->surface, orca->canvas);
|
|
|
|
mg_surface_present(orca->surface);
|
|
|
|
|
|
|
|
mg_surface_prepare(orca->debugOverlay.surface);
|
|
|
|
mg_canvas_set_current(orca->debugOverlay.canvas);
|
|
|
|
mg_render(orca->debugOverlay.surface, orca->debugOverlay.canvas);
|
|
|
|
mg_surface_present(orca->debugOverlay.surface);
|
2023-04-12 14:21:03 +00:00
|
|
|
}
|
|
|
|
|
2023-04-19 09:28:53 +00:00
|
|
|
ui_init(&orca->debugOverlay.ui);
|
2023-04-14 09:48:36 +00:00
|
|
|
|
2023-04-19 09:28:53 +00:00
|
|
|
//NOTE: show window and start runloop
|
|
|
|
mp_window_bring_to_front(orca->window);
|
|
|
|
mp_window_focus(orca->window);
|
2023-04-21 08:02:17 +00:00
|
|
|
mp_window_center(orca->window);
|
2023-04-12 14:21:03 +00:00
|
|
|
|
|
|
|
pthread_t runloopThread;
|
2023-04-17 16:13:07 +00:00
|
|
|
pthread_create(&runloopThread, 0, orca_runloop, 0);
|
2023-04-12 14:21:03 +00:00
|
|
|
|
|
|
|
while(!mp_should_quit())
|
|
|
|
{
|
|
|
|
mp_pump_events(0);
|
|
|
|
//TODO: what to do with mem scratch here?
|
|
|
|
}
|
|
|
|
|
|
|
|
void* res;
|
|
|
|
pthread_join(runloopThread, &res);
|
|
|
|
|
2023-04-19 09:28:53 +00:00
|
|
|
mg_canvas_destroy(orca->canvas);
|
|
|
|
mg_surface_destroy(orca->surface);
|
|
|
|
mp_window_destroy(orca->window);
|
2023-04-12 14:21:03 +00:00
|
|
|
|
|
|
|
mp_terminate();
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef LOG_SUBSYSTEM
|