From c8d03542f81f70b53e6892ba31a139eb0efe5292 Mon Sep 17 00:00:00 2001 From: Dannii Willis Date: Mon, 4 Mar 2024 13:06:04 +1000 Subject: [PATCH] Receive GlkOte events Fix more WASM ABI issues --- remglk/src/glkapi/mod.rs | 16 ++++++++++--- remglk/src/glkapi/objects.rs | 16 +++++++++---- remglk_capi/src/dispatch.rs | 10 ++++---- remglk_capi/src/glk/support.c | 28 ++++++++++++++++++++++ remglk_capi/src/glk/support.h | 6 +++++ remglk_capi/src/systems/emglken.rs | 16 +++++++++++-- remglk_capi/src/systems/library_emglken.js | 22 +++++++++++++---- 7 files changed, 97 insertions(+), 17 deletions(-) diff --git a/remglk/src/glkapi/mod.rs b/remglk/src/glkapi/mod.rs index 727c8d2..a75accb 100644 --- a/remglk/src/glkapi/mod.rs +++ b/remglk/src/glkapi/mod.rs @@ -20,6 +20,7 @@ mod windows; use std::cmp::min; use std::mem; +use mem::MaybeUninit; use std::ops::DerefMut; use std::str; use std::time::SystemTime; @@ -1563,8 +1564,16 @@ where S: Default + GlkSystem { pub fn retain_array(&self, buf: &GlkBuffer) -> DispatchRock { match buf { - GlkBuffer::U8(buf) => (self.retain_array_callbacks_u8.as_ref().unwrap().retain)(buf.as_ptr(), buf.len() as u32, "&+#!Cn".as_ptr()), - GlkBuffer::U32(buf) => (self.retain_array_callbacks_u32.as_ref().unwrap().retain)(buf.as_ptr(), buf.len() as u32, "&+#!Iu".as_ptr()), + GlkBuffer::U8(buf) => { + let mut disprock: MaybeUninit = MaybeUninit::uninit(); + (self.retain_array_callbacks_u8.as_ref().unwrap().retain)(buf.as_ptr(), buf.len() as u32, "&+#!Cn".as_ptr(), disprock.as_mut_ptr()); + unsafe {disprock.assume_init()} + }, + GlkBuffer::U32(buf) => { + let mut disprock: MaybeUninit = MaybeUninit::uninit(); + (self.retain_array_callbacks_u32.as_ref().unwrap().retain)(buf.as_ptr(), buf.len() as u32, "&+#!Iu".as_ptr(), disprock.as_mut_ptr()); + unsafe {disprock.assume_init()} + }, } } @@ -1609,7 +1618,8 @@ pub struct GlkDate { } // Retained array callbacks -pub type RetainArrayCallback = extern fn(*const T, u32, *const u8) -> DispatchRock; +// The WASM ABI means that we can't return a DispatchRock, so it must be set through an out parameter +pub type RetainArrayCallback = extern fn(*const T, u32, *const u8, *mut DispatchRock); pub type UnretainArrayCallback = extern fn(*const T, u32, *const u8, DispatchRock); pub struct RetainArrayCallbacks { diff --git a/remglk/src/glkapi/objects.rs b/remglk/src/glkapi/objects.rs index 3f1fbc6..2b5a16a 100644 --- a/remglk/src/glkapi/objects.rs +++ b/remglk/src/glkapi/objects.rs @@ -9,6 +9,7 @@ https://github.com/curiousdannii/remglk-rs */ +use std::mem::MaybeUninit; use std::ops::{Deref, DerefMut}; use std::sync::{Arc, Mutex, Weak}; @@ -128,8 +129,8 @@ where T: Default + GlkObjectClass, GlkObject: Default + Eq { obj.id = self.counter; obj.rock = rock; self.counter += 1; - if let Some(register_cb) = self.register_cb { - obj.disprock = Some(register_cb(obj_ptr, self.object_class)); + if let Some(_) = self.register_cb { + obj.disprock = Some(self.register_callback(obj_ptr, self.object_class)); } match self.first.as_ref() { None => { @@ -147,13 +148,19 @@ where T: Default + GlkObjectClass, GlkObject: Default + Eq { }; } + fn register_callback(&self, obj_ptr: *const Mutex>, object_class: u32) -> DispatchRock { + let mut disprock: MaybeUninit = MaybeUninit::uninit(); + self.register_cb.unwrap()(obj_ptr, object_class, disprock.as_mut_ptr()); + unsafe {disprock.assume_init()} + } + pub fn set_callbacks(&mut self, register_cb: DispatchRegisterCallback, unregister_cb: DispatchUnregisterCallback) { self.register_cb = Some(register_cb); self.unregister_cb = Some(unregister_cb); for obj in self.store.iter() { let obj_ptr = obj.as_ptr(); let mut obj = obj.lock().unwrap(); - obj.disprock = Some(register_cb(obj_ptr, self.object_class)); + obj.disprock = Some(self.register_callback(obj_ptr, self.object_class)); } } @@ -253,7 +260,8 @@ pub union DispatchRock { ptr: *const DispatchRockPtr, } -pub type DispatchRegisterCallback = fn(*const Mutex>, u32) -> DispatchRock; +// The WASM ABI means that we can't return a DispatchRock, so it must be set through an out parameter +pub type DispatchRegisterCallback = fn(*const Mutex>, u32, *mut DispatchRock); pub type DispatchUnregisterCallback = fn(*const Mutex>, u32, DispatchRock); pub trait GlkObjectClass { diff --git a/remglk_capi/src/dispatch.rs b/remglk_capi/src/dispatch.rs index 64a2450..bb15ec8 100644 --- a/remglk_capi/src/dispatch.rs +++ b/remglk_capi/src/dispatch.rs @@ -19,11 +19,12 @@ use super::*; use common::*; use glkapi::*; -type RegisterCallbackGeneric = extern fn(*const c_void, u32) -> DispatchRock; +// The WASM ABI means that we can't return a DispatchRock, so it must be set through an out parameter +type RegisterCallbackGeneric = extern fn(*const c_void, u32, *mut DispatchRock); type UnregisterCallbackGeneric = extern fn(*const c_void, u32, DispatchRock); #[no_mangle] -pub unsafe extern "C" fn gidispatch_set_object_registry(register_cb: RegisterCallbackGeneric, unregister_cb: UnregisterCallbackGeneric) { +pub unsafe extern "C" fn gidispatch_set_object_registry_rs(register_cb: RegisterCallbackGeneric, unregister_cb: UnregisterCallbackGeneric) { let mut glkapi = glkapi().lock().unwrap(); let register = mem::transmute::>(register_cb); let unregister = mem::transmute::>(unregister_cb); @@ -59,11 +60,12 @@ pub extern "C" fn gidispatch_get_objrock_window(ptr: WindowPtr, dispatchrock_ptr write_ptr(dispatchrock_ptr, obj.disprock.unwrap()); } -type RetainArrayCallbackGeneric = extern fn(*const c_void, u32, *const c_char) -> DispatchRock; +// The WASM ABI means that we can't return a DispatchRock, so it must be set through an out parameter +type RetainArrayCallbackGeneric = extern fn(*const c_void, u32, *const c_char, *mut DispatchRock); type UnretainArrayCallbackGeneric = extern fn(*const c_void, u32, *const c_char, DispatchRock); #[no_mangle] -pub unsafe extern "C" fn gidispatch_set_retained_registry(register_cb: RetainArrayCallbackGeneric, unregister_cb: UnretainArrayCallbackGeneric) { +pub unsafe extern "C" fn gidispatch_set_retained_registry_rs(register_cb: RetainArrayCallbackGeneric, unregister_cb: UnretainArrayCallbackGeneric) { let mut glkapi = glkapi().lock().unwrap(); let retain = mem::transmute::>(register_cb); let unretain = mem::transmute::>(unregister_cb); diff --git a/remglk_capi/src/glk/support.c b/remglk_capi/src/glk/support.c index e18d06a..2d82206 100644 --- a/remglk_capi/src/glk/support.c +++ b/remglk_capi/src/glk/support.c @@ -13,6 +13,9 @@ MIT licenced #include "glk.h" #include "support.h" +gidispatch_rock_t (*gli_register_arr)(void *array, glui32 len, char *typecode) = NULL; +gidispatch_rock_t (*gli_register_obj)(void *obj, glui32 objclass) = NULL; + gidispatch_rock_t gidispatch_get_objrock(void *obj, glui32 objclass) { gidispatch_rock_t rock; switch (objclass) { @@ -30,6 +33,31 @@ gidispatch_rock_t gidispatch_get_objrock(void *obj, glui32 objclass) { } } +// Because of a WASM ABI issue, we call the VM's registry functions indirectly +void gidispatch_call_array_register(void *array, glui32 len, char *typecode, gidispatch_rock_t *rock_ptr) { + gidispatch_rock_t rock = gli_register_arr(array, len, typecode); + *rock_ptr = rock; +} +void gidispatch_call_object_register(void *obj, glui32 objclass, gidispatch_rock_t *rock_ptr) { + gidispatch_rock_t rock = gli_register_obj(obj, objclass); + *rock_ptr = rock; +} + +void gidispatch_set_object_registry( + gidispatch_rock_t (*regi)(void *obj, glui32 objclass), + void (*unregi)(void *obj, glui32 objclass, gidispatch_rock_t objrock)) +{ + gli_register_obj = regi; + gidispatch_set_object_registry_rs(gidispatch_call_object_register, unregi); +} +void gidispatch_set_retained_registry( + gidispatch_rock_t (*regi)(void *array, glui32 len, char *typecode), + void (*unregi)(void *array, glui32 len, char *typecode, gidispatch_rock_t objrock)) +{ + gli_register_arr = regi; + gidispatch_set_retained_registry_rs(gidispatch_call_array_register, unregi); +} + glkunix_argumentlist_t *glkunix_arguments_addr(void) { return glkunix_arguments; } \ No newline at end of file diff --git a/remglk_capi/src/glk/support.h b/remglk_capi/src/glk/support.h index 7a8ba88..8f58067 100644 --- a/remglk_capi/src/glk/support.h +++ b/remglk_capi/src/glk/support.h @@ -7,5 +7,11 @@ extern void gidispatch_get_objrock_fileref(void *obj, gidispatch_rock_t *rock_ptr); extern void gidispatch_get_objrock_stream(void *obj, gidispatch_rock_t *rock_ptr); extern void gidispatch_get_objrock_window(void *obj, gidispatch_rock_t *rock_ptr); +extern void gidispatch_set_object_registry_rs( + void (*regi)(void *obj, glui32 objclass, gidispatch_rock_t *rock_ptr), + void (*unregi)(void *obj, glui32 objclass, gidispatch_rock_t objrock)); +extern void gidispatch_set_retained_registry_rs( + void (*regi)(void *array, glui32 len, char *typecode, gidispatch_rock_t *rock_ptr), + void (*unregi)(void *array, glui32 len, char *typecode, gidispatch_rock_t objrock)); #endif /* REMGLK_RS_SUPPORT_START_H */ \ No newline at end of file diff --git a/remglk_capi/src/systems/emglken.rs b/remglk_capi/src/systems/emglken.rs index db863fc..20c019e 100644 --- a/remglk_capi/src/systems/emglken.rs +++ b/remglk_capi/src/systems/emglken.rs @@ -20,6 +20,7 @@ use glkapi::protocol::{Event, SystemFileRef, Update}; extern "C" { fn emglken_fileref_exists(filename_ptr: *const u8, filename_len: usize) -> bool; fn emglken_fileref_read(filename_ptr: *const u8, filename_len: usize, buffer: &mut EmglkenBuffer) -> bool; + fn emglken_get_glkote_event(buffer: &mut EmglkenBuffer); } #[repr(C)] @@ -72,7 +73,7 @@ impl GlkSystem for EmglkenSystem { }; let result = unsafe {emglken_fileref_read(fileref.filename.as_ptr(), fileref.filename.len(), &mut buf)}; if result { - return unsafe {Some(Box::from_raw(slice::from_raw_parts_mut(buf.ptr, buf.len)))}; + return Some(buffer_to_boxed_slice(&buf)); } None } @@ -91,10 +92,21 @@ impl GlkSystem for EmglkenSystem { } fn get_glkote_event(&mut self) -> Option { - unimplemented!() + let mut buf = EmglkenBuffer { + ptr: ptr::null_mut(), + len: 0, + }; + unsafe {emglken_get_glkote_event(&mut buf)}; + let data = buffer_to_boxed_slice(&buf); + let event: Event = serde_json::from_slice(&data).unwrap(); + return Some(event); } fn send_glkote_update(&mut self, _update: Update) { unimplemented!() } +} + +fn buffer_to_boxed_slice(buffer: &EmglkenBuffer) -> Box<[u8]> { + unsafe {Box::from_raw(slice::from_raw_parts_mut(buffer.ptr, buffer.len))} } \ No newline at end of file diff --git a/remglk_capi/src/systems/library_emglken.js b/remglk_capi/src/systems/library_emglken.js index c4f42d2..3d153b7 100644 --- a/remglk_capi/src/systems/library_emglken.js +++ b/remglk_capi/src/systems/library_emglken.js @@ -18,15 +18,29 @@ addToLibrary({ return false }, + emglken_fileref_read__deps: ['$writeBuffer'], emglken_fileref_read(filename_ptr, filename_len, buffer) { const name = UTF8ToString(filename_ptr, filename_len) if (name === storyfile_name) { - const ptr = _malloc(storyfile_data.length) - HEAP8.set(storyfile_data, ptr) - {{{ makeSetValue('buffer', 0, 'ptr', 'i32') }}} - {{{ makeSetValue('buffer', 4, 'storyfile_data.length', 'i32') }}} + writeBuffer(buffer, storyfile_data) return true } return false }, + + emglken_get_glkote_event__async: true, + emglken_get_glkote_event__deps: ['$writeBuffer'], + emglken_get_glkote_event(buffer) { + return Asyncify.handleAsync(async () => { + await new Promise(resolve => { glkote_event_ready = resolve }) + writeBuffer(buffer, glkote_event_data) + }) + }, + + $writeBuffer(buffer, data) { + const ptr = _malloc(data.length) + HEAP8.set(data, ptr) + {{{ makeSetValue('buffer', 0, 'ptr', 'i32') }}} + {{{ makeSetValue('buffer', 4, 'data.length', 'i32') }}} + } }) \ No newline at end of file