From: Brendan Hansen Date: Sun, 7 Nov 2021 22:08:40 +0000 (-0600) Subject: started event system; added font_get_text_width X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=fc19ce54a01444c2ec9f080ea562fd06b1629b26;p=heartbreak.git started event system; added font_get_text_width --- diff --git a/docs/todo.md b/docs/todo.md index 7a7d6fc..511ea5e 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -5,7 +5,7 @@ This is the short high-level list of things that need to be done: Window controls: -[ ] window_set_property(window, prop, value) +[ ] window_set_property(prop, value) prop is: .Fullscreen .Resizable @@ -13,9 +13,35 @@ Window controls: value can be whatever each property needs -[ ] window_get_property(window, prop) -> value +[ ] window_get_property(prop) -> value + +[ ] get desktop dimensions +[ ] get display count +[ ] get display names +[ ] get display orientations +[ ] get title +[ ] has focus +[ ] is maximized +[ ] is minimized +[ ] request attention +[ ] restore from minimized Event system: There is a little bit to consider here. Are event handlers going to be registered like in LOVE? -Do you define them in your main program? \ No newline at end of file +Do you define them in your main program? + +The problem currently with registing event handlers via something like, + register_event_handle :: (kind: EventKind, handler: (x: i32) -> void) +is that in Wasmer it is impossible to call the handler because of two missing functions: + wasm_table_get(...) + wasm_ref_as_func(...) +Once these functions are available in the shared library, then it would be possible to call the +WASM function dynamically from the GLFW event handler. + +In the meantime, it would be possible (and rather easy) to look for special exports in the binary +like __heartbreak_keydown which would then get registered as event handlers. In Onyx, this won't +create too much of a mess because in qhb.onyx there can simply be a #if #defined(keydown) to test +if a keydown handler was defined, then it would export that function as __heartbreak_keydown for +example. This will won't in the meantime while Wasmer gets their shit together and actually provides +the API that they claim to. diff --git a/include/gfx.h b/include/gfx.h index 5fe5c19..712880f 100644 --- a/include/gfx.h +++ b/include/gfx.h @@ -110,4 +110,5 @@ void gfx_image_bind(ImmediateRenderer *ir, Image *img, u32 texture_unit); void gfx_image_unbind(ImmediateRenderer *ir); Font* gfx_font_load(ImmediateRenderer *ir, char* font_path, i32 font_size); +f32 gfx_font_get_text_width(ImmediateRenderer *ir, char* msgptr, i32 msglen); #endif diff --git a/include/heartbreak.h b/include/heartbreak.h index cb76cf2..6ab0d32 100644 --- a/include/heartbreak.h +++ b/include/heartbreak.h @@ -10,8 +10,10 @@ // // WebAssembly State -extern wasm_memory_t* wasm_memory; -extern wasm_table_t* wasm_func_table; +extern wasm_memory_t* wasm_memory; +extern wasm_table_t* wasm_func_table; +extern wasm_module_t* wasm_module; +extern wasm_instance_t* wasm_instance; // // Global OpenGL / GLFW things diff --git a/misc/onyx/heartbreak.onyx b/misc/onyx/heartbreak.onyx index 8d32705..36b70f9 100644 --- a/misc/onyx/heartbreak.onyx +++ b/misc/onyx/heartbreak.onyx @@ -8,7 +8,7 @@ package heartbreak use package core {println} HeartbreakFuncs :: struct { - init : () -> void; + load : () -> void; update : (dt: f32) -> void; draw : () -> void; } @@ -19,7 +19,7 @@ run :: (use funcs: HeartbreakFuncs) { return; } - init(); + load(); while system.end_frame() { update(0); @@ -27,4 +27,4 @@ run :: (use funcs: HeartbreakFuncs) { } system.destroy(); -} \ No newline at end of file +} diff --git a/misc/onyx/heartbreak_graphics.onyx b/misc/onyx/heartbreak_graphics.onyx index 85bf8e0..eef9531 100644 --- a/misc/onyx/heartbreak_graphics.onyx +++ b/misc/onyx/heartbreak_graphics.onyx @@ -70,3 +70,6 @@ ImageProperty :: enum { getImageProperty :: (img: Image, prop: ImageProperty) -> i32 #foreign "heartbreak" "graphics_image_property" --- getImageDimensions :: (img: Image) => getImageProperty(img, .Width), getImageProperty(img, .Height); + + +getTextWidth :: (text: str) -> f32 #foreign "heartbreak" "graphics_get_text_width" --- diff --git a/misc/onyx/qhb.onyx b/misc/onyx/qhb.onyx index 10dd8ae..acd26a9 100644 --- a/misc/onyx/qhb.onyx +++ b/misc/onyx/qhb.onyx @@ -8,16 +8,23 @@ package main #private hb :: package heartbreak -#if !#defined((package main).init) { - init :: () {} +#private_file symbol_source :: package main + +#if !#defined(symbol_source.load) { + load :: () {} } -#if !#defined((package main).update) { +#if !#defined(symbol_source.update) { update :: (dt: f32) {} } -#if !#defined((package main).draw) { +#if !#defined(symbol_source.draw) { draw :: () {} } -main :: (_) => hb.run(.{ init, update, draw }); \ No newline at end of file +// This is one place where top-level macros would be nice. +#if #defined(symbol_source.keydown) { #export "__heartbreak_keydown" symbol_source.keydown } +#if #defined(symbol_source.keyup) { #export "__heartbreak_keyup" symbol_source.keyup } +#if #defined(symbol_source.resize) { #export "__heartbreak_resize" symbol_source.resize } + +main :: (_) => hb.run(.{ symbol_source.load, symbol_source.update, symbol_source.draw }); diff --git a/src/gfx.c b/src/gfx.c index b27b714..3c0ee21 100644 --- a/src/gfx.c +++ b/src/gfx.c @@ -563,3 +563,19 @@ Font* gfx_font_load(ImmediateRenderer *ir, char* font_path, i32 font_size) { return font; } + +f32 gfx_font_get_text_width(ImmediateRenderer *ir, char* msgptr, i32 msglen) { + stbtt_aligned_quad quad; + + f32 x=0, y=0; + while (msglen--) { + stbtt_GetPackedQuad(ir->active_font->char_data, + FONT_INTERNAL_IMAGE_SIZE, FONT_INTERNAL_IMAGE_SIZE, + *msgptr - ir->active_font->first_character, + &x, &y, &quad, 0); + + msgptr++; + } + + return x; +} diff --git a/src/heartbreak.c b/src/heartbreak.c index 50be7c0..11ea7f6 100644 --- a/src/heartbreak.c +++ b/src/heartbreak.c @@ -19,8 +19,10 @@ GLFWwindow* glfw_window = NULL; ImmediateRenderer renderer; -wasm_memory_t* wasm_memory; -wasm_table_t* wasm_func_table; +wasm_module_t* wasm_module; +wasm_instance_t* wasm_instance; +wasm_memory_t* wasm_memory; +wasm_table_t* wasm_func_table; void build_heartbreak_imports(WasmFuncDefinition** out_wfds, i32* out_count) { static WasmFuncDefinition* modules[] = { @@ -68,8 +70,6 @@ void run_wasm_file(bh_buffer wasm_bytes) { wasm_engine_t* engine = NULL; wasm_store_t* store = NULL; - wasm_module_t* module = NULL; - wasm_instance_t* instance = NULL; config = wasm_config_new(); if (!config) goto error_handling; @@ -95,11 +95,11 @@ void run_wasm_file(bh_buffer wasm_bytes) { wasm_data.size = wasm_bytes.length; wasm_data.data = wasm_bytes.data; - module = wasm_module_new(store, &wasm_data); - if (!module) goto error_handling; + wasm_module = wasm_module_new(store, &wasm_data); + if (!wasm_module) goto error_handling; wasmer_named_extern_vec_t wasi_imports; - wasi_get_unordered_imports(store, module, wasi_env, &wasi_imports); + wasi_get_unordered_imports(store, wasm_module, wasi_env, &wasi_imports); WasmFuncDefinition* defs; i32 defs_count; @@ -109,7 +109,7 @@ void run_wasm_file(bh_buffer wasm_bytes) { wasm_name_new_from_string(&heartbreak_name, "heartbreak"); // @Free wasm_importtype_vec_t module_imports; // @Free - wasm_module_imports(module, &module_imports); + wasm_module_imports(wasm_module, &module_imports); wasm_extern_vec_t imports; wasm_extern_vec_new_uninitialized(&imports, module_imports.size); // @Free @@ -165,13 +165,13 @@ void run_wasm_file(bh_buffer wasm_bytes) { wasm_trap_t* traps = NULL; - instance = wasm_instance_new(store, module, &imports, &traps); - if (!instance) goto error_handling; + wasm_instance = wasm_instance_new(store, wasm_module, &imports, &traps); + if (!wasm_instance) goto error_handling; - wasm_extern_t* start_extern = wasm_extern_lookup_by_name(module, instance, HEARTBREAK_WASM_START_FUNCTION_NAME); + wasm_extern_t* start_extern = wasm_extern_lookup_by_name(wasm_module, wasm_instance, HEARTBREAK_WASM_START_FUNCTION_NAME); wasm_func_t* start_func = wasm_extern_as_func(start_extern); - wasm_extern_t* memory_extern = wasm_extern_lookup_by_name(module, instance, HEARTBREAK_WASM_MEMORY_NAME); + wasm_extern_t* memory_extern = wasm_extern_lookup_by_name(wasm_module, wasm_instance, HEARTBREAK_WASM_MEMORY_NAME); wasm_memory = wasm_extern_as_memory(memory_extern); // This is storing the function table exported by WASM module in order to call functions passed by reference to the host. @@ -179,7 +179,7 @@ void run_wasm_file(bh_buffer wasm_bytes) { // There is no way to get a reference out of a table, nor turn a reference into a function object to call. This means // that even if I passed functions using reftypes, I still couldn't convert them to a wasm_func_t to call. Wasmer is // normally pretty good so I was disappointed to find this level of laziness in the implementation. - wasm_extern_t* func_table_extern = wasm_extern_lookup_by_name(module, instance, HEARTBREAK_WASM_FUNCTION_TABLE_NAME); + wasm_extern_t* func_table_extern = wasm_extern_lookup_by_name(wasm_module, wasm_instance, HEARTBREAK_WASM_FUNCTION_TABLE_NAME); wasm_func_table = wasm_extern_as_table(func_table_extern); wasm_val_vec_t args; @@ -200,8 +200,8 @@ error_handling: bh_printf("An error occured trying to run the WASM module...\n"); cleanup: - if (instance) wasm_instance_delete(instance); - if (module) wasm_module_delete(module); + if (wasm_instance) wasm_instance_delete(wasm_instance); + if (wasm_module) wasm_module_delete(wasm_module); if (store) wasm_store_delete(store); if (engine) wasm_engine_delete(engine); return; diff --git a/src/heartbreak_graphics.c b/src/heartbreak_graphics.c index 00c4656..6d85b11 100644 --- a/src/heartbreak_graphics.c +++ b/src/heartbreak_graphics.c @@ -308,6 +308,15 @@ HEARTBREAK_DEF(image_property, (WASM_I64, WASM_I32), (WASM_I32)) { return NULL; } +HEARTBREAK_DEF(get_text_width, (WASM_I32, WASM_I32), (WASM_F32)) { + char* str_data = wasm_memory_data(wasm_memory) + params->data[0].of.i32; + u32 str_size = params->data[1].of.i32; + + f32 res = gfx_font_get_text_width(&renderer, str_data, str_size); + results->data[0] = WASM_F32_VAL(res); + return NULL; +} + HEARTBREAK_MODULE { HEARTBREAK_FUNC(set_clear_color) HEARTBREAK_FUNC(clear) @@ -332,5 +341,7 @@ HEARTBREAK_MODULE { HEARTBREAK_FUNC(image_param) HEARTBREAK_FUNC(image_property) + HEARTBREAK_FUNC(get_text_width) + { NULL } }; diff --git a/src/heartbreak_system.c b/src/heartbreak_system.c index 1906048..2c467f1 100644 --- a/src/heartbreak_system.c +++ b/src/heartbreak_system.c @@ -3,10 +3,77 @@ #define HEARTBREAK_MODULE_NAME system +#define HEARTBREAK_EVENTS \ + HB_EVENT(keydown, (WASM_I32, WASM_I32)) \ + HB_EVENT(keyup, (WASM_I32)) \ + HB_EVENT(mousedown, ()) \ + HB_EVENT(mouseup, ()) \ + HB_EVENT(mousemove, ()) \ + HB_EVENT(resize, (WASM_I32, WASM_I32)) \ + +#define HB_EVENT(name, pt) static wasm_func_t *__heartbreak_event_##name; +HEARTBREAK_EVENTS +#undef HB_EVENT + +typedef struct HeartbreakEventType { + const char* name; + wasm_func_t** func; + WasmValkindBuffer params; +} HeartbreakEventType; + +#define HB_EVENT(name, pt) static HeartbreakEventType __heartbreak_event_type_##name = { #name, &__heartbreak_event_##name, _VALS pt }; +HEARTBREAK_EVENTS +#undef HB_EVENT + +#define HEARTBREAK_EVENT_CALL(name, ...) __heartbreak_event_call(&__heartbreak_event_type_##name, __VA_ARGS__); + +void __heartbreak_event_call(HeartbreakEventType *ev, ...) { + if (*ev->func == NULL) return; + + va_list args; + va_start(args, ev); + + wasm_val_vec_t wasm_args, wasm_results; + wasm_val_vec_new_uninitialized(&wasm_args, ev->params.count); + + fori (i, 0, (i32) ev->params.count) { + switch (ev->params.types[i]) { + case WASM_I32: wasm_args.data[i] = WASM_I32_VAL(va_arg(args, i32)); break; + case WASM_I64: wasm_args.data[i] = WASM_I64_VAL(va_arg(args, i64)); break; + case WASM_F32: wasm_args.data[i] = WASM_F32_VAL(va_arg(args, f64)); break; + case WASM_F64: wasm_args.data[i] = WASM_F64_VAL(va_arg(args, f64)); break; + default: assert(0); + } + } + + const wasm_trap_t* trap = wasm_func_call(*ev->func, &wasm_args, &wasm_results); + if (trap) { + wasm_message_t msg; + wasm_trap_message(trap, &msg); + + bh_printf("Error calling heartbreak event handler '%s': %b\n", ev->name, msg.data, msg.size); + } + + va_end(args); +} + static void __glfw_window_size_callback(GLFWwindow *w, i32 new_width, i32 new_height) { gfx_immediate_renderer_update_window_size(&renderer, (f32) new_width, (f32) new_height); + HEARTBREAK_EVENT_CALL(resize, new_width, new_height); } +static void __glfw_window_key_callback(GLFWwindow *w, i32 key, int scancode, int action, int mods) { + switch (action) { + case GLFW_PRESS: HEARTBREAK_EVENT_CALL(keydown, key, mods); break; + case GLFW_RELEASE: HEARTBREAK_EVENT_CALL(keyup, key); break; + } +} + +typedef struct HeartbreakEventLink { + const char *name; + wasm_func_t **func; +} HeartbreakEventLink; + HEARTBREAK_DEF(init, (), (WASM_I32)) { if (!glfwInit()) { bh_printf("Failed to initialize GLFW.\n"); @@ -25,6 +92,7 @@ HEARTBREAK_DEF(init, (), (WASM_I32)) { glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwMakeContextCurrent(glfw_window); glfwSetWindowSizeCallback(glfw_window, __glfw_window_size_callback); + glfwSetKeyCallback(glfw_window, __glfw_window_key_callback); glfwSwapInterval(1); @@ -38,6 +106,21 @@ HEARTBREAK_DEF(init, (), (WASM_I32)) { glfwGetWindowSize(glfw_window, &width, &height); gfx_immediate_renderer_update_window_size(&renderer, (f32) width, (f32) height); + #define HB_EVENT(name, pt) { "__heartbreak_" #name, &__heartbreak_event_##name }, + static HeartbreakEventLink events[] = { + HEARTBREAK_EVENTS + { NULL } + }; + #undef HB_EVENT + + wasm_extern_t* tmp_extern; + HeartbreakEventLink* ev = events; + while (ev->name) { + tmp_extern = wasm_extern_lookup_by_name(wasm_module, wasm_instance, ev->name); + if (tmp_extern) *ev->func = wasm_extern_as_func(tmp_extern); + ev++; + } + results->data[0] = WASM_I32_VAL(1); return NULL; } diff --git a/src/heartbreak_window.c b/src/heartbreak_window.c index 30ba079..385bf6d 100644 --- a/src/heartbreak_window.c +++ b/src/heartbreak_window.c @@ -26,11 +26,22 @@ HEARTBREAK_DEF(set_should_close, (WASM_I32), ()) { return NULL; } +HEARTBREAK_DEF(get_property, (WASM_I32), (WASM_I32)) { + return NULL; +} + +HEARTBREAK_DEF(set_property, (WASM_I32, WASM_I32), ()) { + return NULL; +} + HEARTBREAK_MODULE { HEARTBREAK_FUNC(get_width) HEARTBREAK_FUNC(get_height) HEARTBREAK_FUNC(set_dimensions) HEARTBREAK_FUNC(set_should_close) + HEARTBREAK_FUNC(get_property) + HEARTBREAK_FUNC(set_property) + { NULL } -}; \ No newline at end of file +}; diff --git a/tests/simp.onyx b/tests/simp.onyx index ddec249..6aca651 100644 --- a/tests/simp.onyx +++ b/tests/simp.onyx @@ -11,7 +11,7 @@ Square :: struct { r, g, b, a: f32; } -init :: () { +load :: () { w := hb.window.getWidth(); h := hb.window.getHeight(); printf("The window is {}x{}\n", w, h); @@ -24,9 +24,23 @@ init :: () { array.init(^squares); + printf("Width is: {}\n", hb.graphics.getTextWidth("Hello, World!")); + // hb.window.setDimensions(1200, 900); } +keydown :: (key, mods: i32) { + printf("Key down! {} {}\n", key, mods); +} + +keyup :: (key: i32) { + printf("Key up! {}\n", key); +} + +resize :: (nw, nh: i32) { + printf("The window is now {} by {}.\n", nw, nh); +} + t: f32 = 0; update :: (dt: f32) { t += 0.016;