From 95d3296dd9e9a6e37eef39ac5a8d1967cd24d5bb Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Thu, 20 Mar 2025 17:25:48 -0500 Subject: [PATCH] wota now encodes at the C level; update dmon for macos 13; clean up many warnings --- meson.build | 8 +- scripts/core/engine.js | 25 +- source/cute_aseprite.h | 9 +- source/dmon.h | 86 ++- source/jsffi.c | 259 +------ source/nota.h | 2 +- source/prosperon.c | 926 +++++++++++++++++++++--- source/prosperon.h | 32 +- source/qjs_dmon.h | 2 +- source/qjs_enet.c | 3 +- source/qjs_imgui.cpp | 2 +- source/qjs_tracy.c | 2 +- source/qjs_wota.c | 55 +- source/qjs_wota.h | 1 + source/render.h | 2 - source/sprite.c | 2 +- source/sprite.h | 2 +- source/thirdparty/imgui/GraphEditor.cpp | 4 - source/thirdparty/par/par_shapes.h | 26 +- source/transform.h | 2 +- source/wota.h | 2 - tests/delay.js | 12 + 22 files changed, 1021 insertions(+), 443 deletions(-) create mode 100644 tests/delay.js diff --git a/meson.build b/meson.build index c971c615..d4421bef 100644 --- a/meson.build +++ b/meson.build @@ -32,8 +32,9 @@ add_project_arguments( add_project_arguments('-Wno-incompatible-pointer-types', language: 'c') add_project_arguments('-Wno-narrowing', language: 'cpp') add_project_arguments('-Wno-missing-braces', language:'c') -add_project_arguments('-Wl,--disable-new-dtags', language:'cpp') -add_project_arguments('-Wl,--disable-new-dtags', language:'c') +add_project_arguments('-Wno-strict-prototypes', language:'c') +add_project_arguments('-Wno-unused-command-line-argument', language: 'c') +add_project_arguments('-Wno-unused-command-line-argument', language: 'cpp') deps = [] @@ -236,7 +237,8 @@ tests = [ 'wota', 'portalspawner', 'overling', - 'send' + 'send', + 'delay' ] foreach file : tests diff --git a/scripts/core/engine.js b/scripts/core/engine.js index 8edaa5d0..e5d56b66 100644 --- a/scripts/core/engine.js +++ b/scripts/core/engine.js @@ -575,7 +575,6 @@ var enet = use('enet') var util = use('util') var math = use('math') var crypto = use('crypto') -var wota = use('wota') var nota = use('nota') var HEADER = Symbol() @@ -671,6 +670,10 @@ var portal_fn = undefined var local_address = undefined var local_port = undefined +var service_delay = 0.01 + + + $_.portal = function(fn, port) { if (portal) throw new Error(`Already started a portal listening on ${portal.port}`) console.log(`starting a portal on port ${port}`) @@ -680,6 +683,7 @@ $_.portal = function(fn, port) { local_port = port portal_fn = fn console.log(`I am now ${$_}`) + // kick off servicing } $_.portal[prosperon.DOC] = "starts a public address that performs introduction services..." @@ -744,7 +748,6 @@ $_.start[prosperon.DOC] = "The start function creates a new actor..." $_.stop = function(actor) { if (!actor) { destroyself() - return } @@ -791,7 +794,7 @@ function actor_send(actor, message) { } if (actor.__ACTORDATA__.id && os.mailbox_exist(actor.__ACTORDATA__.id)) { - os.mailbox_push(actor.__ACTORDATA__.id, wota.encode(message)) + os.mailbox_push(actor.__ACTORDATA__.id, message) return } @@ -847,7 +850,7 @@ cmd.process(prosperon.argv) if (!prosperon.args.id) prosperon.id = util.guid() else prosperon.id = prosperon.args.id -os.register_actor(prosperon.id, handle_local); +os.register_actor(prosperon.id, handle_message); $_.__ACTORDATA__.id = prosperon.id @@ -860,8 +863,8 @@ if (prosperon.args.program) actor.spawn(prosperon.args.program) function destroyself() { console.log(`Got the message to destroy self.`) - if (overling) actor_send(overling, { type: "stopped", id: prosperon.id }) - os.destroy(prosperon.id); +// if (overling) actor_send(overling, { type: "stopped", id: prosperon.id }) + os.destroy(); } function handle_actor_disconnect(id) { @@ -875,17 +878,11 @@ function handle_actor_disconnect(id) { delete peers[id] } -function handle_local(msg) { - console.print("LOCAL!!!"); - console.log("LOCAL MESSAGE"); - handle_message(wota.decode(msg)) -} - function handle_message(msg) { console.log(`message was ${json.encode(msg)}`) if (msg.target) { if (msg.target !== prosperon.id) { - os.mailbox_push(msg.target, wota.encode(msg)) + os.mailbox_push(msg.target, msg) return } } @@ -933,6 +930,4 @@ function handle_message(msg) { } }; -console.log('finished running main') - })() diff --git a/source/cute_aseprite.h b/source/cute_aseprite.h index 08e582a1..6ace0cf1 100644 --- a/source/cute_aseprite.h +++ b/source/cute_aseprite.h @@ -306,7 +306,7 @@ struct ase_t void* mem_ctx; }; -const char *aseprite_GetError(); +const char *aseprite_GetError(void); #endif // CUTE_ASEPRITE_H @@ -317,11 +317,11 @@ const char *aseprite_GetError(); #define ASEPRITE_ERROR_MAX 256 char aseprite_error[ASEPRITE_ERROR_MAX] = {0}; -const char *aseprite_GetError() { +const char *aseprite_GetError(void) { return aseprite_error; } -void aseprite_clear_error() { +void aseprite_clear_error(void) { aseprite_error[0] = 0; } @@ -726,7 +726,6 @@ static int s_inflate(const void* in, int in_bytes, void* out, int out_bytes, voi s->out_end = s->out + out_bytes; s->begin = (char*)out; - int count = 0; uint32_t bfinal; do { @@ -740,8 +739,6 @@ static int s_inflate(const void* in, int in_bytes, void* out, int out_bytes, voi case 2: s_dynamic(s); CUTE_ASEPRITE_CALL(s_block(s)); break; case 3: CUTE_ASEPRITE_CHECK(0, "Detected unknown block type within input stream."); } - - ++count; } while (!bfinal); diff --git a/source/dmon.h b/source/dmon.h index ead58241..b75c836d 100644 --- a/source/dmon.h +++ b/source/dmon.h @@ -399,7 +399,7 @@ typedef struct dmon__watch_state { typedef struct dmon__state { int num_watches; dmon__watch_state* watches[DMON_MAX_WATCHES]; - int freelist[DMON_MAX_WATCHES]; + int freelist[DMON_MAX_WATCHES]; HANDLE thread_handle; CRITICAL_SECTION mutex; volatile LONG modify_watches; @@ -589,7 +589,7 @@ DMON_API_IMPL void dmon_init(void) _dmon.thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_dmon_thread, NULL, 0, NULL); DMON_ASSERT(_dmon.thread_handle); - for (int i = 0; i < DMON_MAX_WATCHES; i++) + for (int i = 0; i < DMON_MAX_WATCHES; i++) _dmon.freelist[i] = DMON_MAX_WATCHES - i - 1; _dmon_init = true; @@ -627,7 +627,7 @@ DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, const char* oldname, void* user), uint32_t flags, void* user_data) { - DMON_ASSERT(_dmon_init); + DMON_ASSERT(_dmon_init); DMON_ASSERT(watch_cb); DMON_ASSERT(rootdir && rootdir[0]); @@ -706,7 +706,7 @@ DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, DMON_API_IMPL void dmon_unwatch(dmon_watch_id id) { - DMON_ASSERT(_dmon_init); + DMON_ASSERT(_dmon_init); DMON_ASSERT(id.id > 0); int index = id.id - 1; DMON_ASSERT(index < DMON_MAX_WATCHES); @@ -761,7 +761,7 @@ typedef struct dmon__watch_state { typedef struct dmon__state { dmon__watch_state* watches[DMON_MAX_WATCHES]; - int freelist[DMON_MAX_WATCHES]; + int freelist[DMON_MAX_WATCHES]; dmon__inotify_event* events; int num_watches; pthread_t thread_handle; @@ -891,9 +891,7 @@ _DMON_PRIVATE void _dmon_inotify_process_events(void) ev->skip = true; break; } else if ((ev->mask & IN_ISDIR) && (check_ev->mask & (IN_ISDIR|IN_MODIFY))) { - // in some cases, particularly when created files under sub directories - // there can be two modify events for a single subdir one with trailing slash and one without - // remove trailing slash from both cases and test + // handle trailing slashes int l1 = (int)strlen(ev->filepath); int l2 = (int)strlen(check_ev->filepath); if (ev->filepath[l1-1] == '/') ev->filepath[l1-1] = '\0'; @@ -910,14 +908,12 @@ _DMON_PRIVATE void _dmon_inotify_process_events(void) for (j = i + 1; j < c && !loop_break; j++) { dmon__inotify_event* check_ev = &_dmon.events[j]; if ((check_ev->mask & IN_MOVED_FROM) && strcmp(ev->filepath, check_ev->filepath) == 0) { - // there is a case where some programs (like gedit): - // when we save, it creates a temp file, and moves it to the file being modified - // search for these cases and remove all of them + // check for rename sequences int k; for (k = j + 1; k < c; k++) { dmon__inotify_event* third_ev = &_dmon.events[k]; if (third_ev->mask & IN_MOVED_TO && check_ev->cookie == third_ev->cookie) { - third_ev->mask = IN_MODIFY; // change to modified + third_ev->mask = IN_MODIFY; // treat as a modify ev->skip = check_ev->skip = true; loop_break = true; break; @@ -925,7 +921,6 @@ _DMON_PRIVATE void _dmon_inotify_process_events(void) } } else if ((check_ev->mask & IN_MODIFY) && strcmp(ev->filepath, check_ev->filepath) == 0) { // Another case is that file is copied. CREATE and MODIFY happens sequentially - // so we ignore MODIFY event check_ev->skip = true; } } @@ -939,10 +934,7 @@ _DMON_PRIVATE void _dmon_inotify_process_events(void) break; } } - - // in some environments like nautilus file explorer: - // when a file is deleted, it is moved to recycle bin - // so if the destination of the move is not valid, it's probably DELETE + // If destination is not valid, treat as delete if (!move_valid) { ev->mask = IN_DELETE; } @@ -956,10 +948,7 @@ _DMON_PRIVATE void _dmon_inotify_process_events(void) break; } } - - // in some environments like nautilus file explorer: - // when a file is deleted, it is moved to recycle bin, on undo it is moved back it - // so if the destination of the move is not valid, it's probably CREATE + // If source is not valid, treat as create if (!move_valid) { ev->mask = IN_CREATE; } @@ -967,7 +956,6 @@ _DMON_PRIVATE void _dmon_inotify_process_events(void) int j; for (j = i + 1; j < c; j++) { dmon__inotify_event* check_ev = &_dmon.events[j]; - // if the file is DELETED and then MODIFIED after, just ignore the modify event if ((check_ev->mask & IN_MODIFY) && strcmp(ev->filepath, check_ev->filepath) == 0) { check_ev->skip = true; break; @@ -1009,10 +997,9 @@ _DMON_PRIVATE void _dmon_inotify_process_events(void) stb_sb_push(watch->subdirs, subdir); stb_sb_push(watch->wds, wd); - // some directories may be already created, for instance, with the command: mkdir -p - // so we will enumerate them manually and add them to the events + // gather newly created subdirs _dmon_gather_recursive(watch, watchdir); - ev = &_dmon.events[i]; // gotta refresh the pointer because it may be relocated + ev = &_dmon.events[i]; } } watch->watch_cb(ev->watch_id, DMON_ACTION_CREATE, watch->rootdir, ev->filepath, NULL, watch->user_data); @@ -1172,7 +1159,7 @@ DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, const char* oldname, void* user), uint32_t flags, void* user_data) { - DMON_ASSERT(_dmon_init); + DMON_ASSERT(_dmon_init); DMON_ASSERT(watch_cb); DMON_ASSERT(rootdir && rootdir[0]); @@ -1256,7 +1243,7 @@ DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, return _dmon_make_id(0); } dmon__watch_subdir subdir; - _dmon_strcpy(subdir.rootdir, sizeof(subdir.rootdir), ""); // root dir is just a dummy entry + _dmon_strcpy(subdir.rootdir, sizeof(subdir.rootdir), ""); // root dir is a dummy entry stb_sb_push(watch->subdirs, subdir); stb_sb_push(watch->wds, wd); @@ -1273,7 +1260,7 @@ DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, DMON_API_IMPL void dmon_unwatch(dmon_watch_id id) { - DMON_ASSERT(_dmon_init); + DMON_ASSERT(_dmon_init); DMON_ASSERT(id.id > 0); int index = id.id - 1; DMON_ASSERT(index < DMON_MAX_WATCHES); @@ -1320,7 +1307,7 @@ typedef struct dmon__watch_state { typedef struct dmon__state { dmon__watch_state* watches[DMON_MAX_WATCHES]; - int freelist[DMON_MAX_WATCHES]; + int freelist[DMON_MAX_WATCHES]; dmon__fsevent_event* events; int num_watches; volatile int modify_watches; @@ -1369,7 +1356,7 @@ _DMON_PRIVATE void _dmon_fsevent_process_events(void) continue; } - // remove redundant modify events on a single file + // remove redundant modifies on a single file if (ev->event_flags & kFSEventStreamEventFlagItemModified) { int j; for (j = i + 1; j < c; j++) { @@ -1380,7 +1367,8 @@ _DMON_PRIVATE void _dmon_fsevent_process_events(void) break; } } - } else if ((ev->event_flags & kFSEventStreamEventFlagItemRenamed) && !ev->move_valid) { + } + else if ((ev->event_flags & kFSEventStreamEventFlagItemRenamed) && !ev->move_valid) { int j; for (j = i + 1; j < c; j++) { dmon__fsevent_event* check_ev = &_dmon.events[j]; @@ -1391,10 +1379,7 @@ _DMON_PRIVATE void _dmon_fsevent_process_events(void) } } - // in some environments like finder file explorer: - // when a file is deleted, it is moved to recycle bin - // so if the destination of the move is not valid, it's probably DELETE or CREATE - // decide CREATE if file exists + // if not valid rename, treat as remove or create if (!ev->move_valid) { ev->event_flags &= ~kFSEventStreamEventFlagItemRenamed; @@ -1429,10 +1414,10 @@ _DMON_PRIVATE void _dmon_fsevent_process_events(void) watch->watch_cb(ev->watch_id, DMON_ACTION_CREATE, watch->rootdir_unmod, ev->filepath, NULL, watch->user_data); } - if (ev->event_flags & kFSEventStreamEventFlagItemModified) { watch->watch_cb(ev->watch_id, DMON_ACTION_MODIFY, watch->rootdir_unmod, ev->filepath, NULL, watch->user_data); - } else if (ev->event_flags & kFSEventStreamEventFlagItemRenamed) { + } + else if (ev->event_flags & kFSEventStreamEventFlagItemRenamed) { int j; for (j = i + 1; j < c; j++) { dmon__fsevent_event* check_ev = &_dmon.events[j]; @@ -1442,7 +1427,8 @@ _DMON_PRIVATE void _dmon_fsevent_process_events(void) break; } } - } else if (ev->event_flags & kFSEventStreamEventFlagItemRemoved) { + } + else if (ev->event_flags & kFSEventStreamEventFlagItemRemoved) { watch->watch_cb(ev->watch_id, DMON_ACTION_DELETE, watch->rootdir_unmod, ev->filepath, NULL, watch->user_data); } @@ -1478,8 +1464,19 @@ _DMON_PRIVATE void* _dmon_thread(void* arg) dmon__watch_state* watch = _dmon.watches[i]; if (!watch->init) { DMON_ASSERT(watch->fsev_stream_ref); - FSEventStreamScheduleWithRunLoop(watch->fsev_stream_ref, _dmon.cf_loop_ref, kCFRunLoopDefaultMode); - FSEventStreamStart(watch->fsev_stream_ref); + // Modified block: Use dispatch queue if macOS >= 13, else run loop + #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 130000 + { + dispatch_queue_t queue = dispatch_queue_create("com.dmon.fsevents", DISPATCH_QUEUE_SERIAL); + FSEventStreamSetDispatchQueue(watch->fsev_stream_ref, queue); + FSEventStreamStart(watch->fsev_stream_ref); + } + #else + { + FSEventStreamScheduleWithRunLoop(watch->fsev_stream_ref, _dmon.cf_loop_ref, kCFRunLoopDefaultMode); + FSEventStreamStart(watch->fsev_stream_ref); + } + #endif watch->init = true; } @@ -1587,8 +1584,8 @@ _DMON_PRIVATE void _dmon_fsevent_callback(ConstFSEventStreamRef stream_ref, void _dmon_strcpy(abs_filepath, sizeof(abs_filepath), filepath); _dmon_unixpath(abs_filepath, sizeof(abs_filepath), abs_filepath); - // normalize path, so it would be the same on both MacOS file-system types (case/nocase) - _dmon_tolower(abs_filepath_lower, sizeof(abs_filepath), abs_filepath); + // normalize path for case-insensitive volumes + _dmon_tolower(abs_filepath_lower, sizeof(abs_filepath_lower), abs_filepath); DMON_ASSERT(strstr(abs_filepath_lower, watch->rootdir) == abs_filepath_lower); // strip the root dir from the beginning @@ -1608,7 +1605,7 @@ DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, const char* oldname, void* user), uint32_t flags, void* user_data) { - DMON_ASSERT(_dmon_init); + DMON_ASSERT(_dmon_init); DMON_ASSERT(watch_cb); DMON_ASSERT(rootdir && rootdir[0]); @@ -1707,7 +1704,6 @@ DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, cf_dirarr, kFSEventStreamEventIdSinceNow, 0.25, kFSEventStreamCreateFlagFileEvents); - CFRelease(cf_dirarr); CFRelease(cf_dir); @@ -1718,7 +1714,7 @@ DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, DMON_API_IMPL void dmon_unwatch(dmon_watch_id id) { - DMON_ASSERT(_dmon_init); + DMON_ASSERT(_dmon_init); DMON_ASSERT(id.id > 0); int index = id.id - 1; DMON_ASSERT(index < DMON_MAX_WATCHES); diff --git a/source/jsffi.c b/source/jsffi.c index f03d4a88..97d36f8c 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -225,8 +225,6 @@ JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#ATOM); \ TARGET = js2##TYPE(JS, __##PROP##__v); \ JS_FreeValue(JS,__##PROP##__v); }\ -#define JS_SetProperty(js, target, atom, val) JS_SetPropertyStr(js, target, #atom, val) - #define JS_SETATOM(JS, TARGET, ATOM, VALUE, TYPE) JS_SetProperty(JS, TARGET, #ATOM, TYPE##2js(JS, VALUE)); int JS_GETBOOL(JSContext *js, JSValue v, const char *prop) @@ -1057,7 +1055,7 @@ void SDL_GPURenderPass_free(JSRuntime *rt, SDL_GPURenderPass *c) { } #define GPURELEASECLASS(NAME) \ void SDL_GPU##NAME##_free(JSRuntime *rt, SDL_GPU##NAME *c) { \ SDL_ReleaseGPU##NAME(global_gpu, c); } \ -QJSCLASS(SDL_GPU##NAME) \ +QJSCLASS(SDL_GPU##NAME,) \ static JSClassID js_sprite_id; static void js_sprite_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { @@ -1065,7 +1063,7 @@ static void js_sprite_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_fu if (!sp) return; JS_MarkValue(rt, sp->image, mark_func); } -QJSCLASSMARK(sprite) +QJSCLASSMARK(sprite,) static JSClassID js_transform_id; static void js_transform_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { @@ -1080,12 +1078,12 @@ static void js_transform_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark JS_MarkValue(rt, t->jschildren[i], mark_func); } -QJSCLASSMARK(transform) -QJSCLASS(font) -QJSCLASS(datastream) -QJSCLASS(SDL_Window) -QJSCLASS(SDL_Renderer) -QJSCLASS(SDL_Camera) +QJSCLASSMARK(transform,) +QJSCLASS(font,) +QJSCLASS(datastream,) +QJSCLASS(SDL_Window,) +QJSCLASS(SDL_Renderer,) +QJSCLASS(SDL_Camera,) void SDL_Texture_free(JSRuntime *rt, SDL_Texture *t){ SDL_DestroyTexture(t); @@ -1101,8 +1099,8 @@ QJSCLASS(SDL_Surface, JS_SetProperty(js,j,height_atom,number2js(js,n->h)); ) -QJSCLASS(SDL_GPUDevice) -QJSCLASS(SDL_Thread) +QJSCLASS(SDL_GPUDevice,) +QJSCLASS(SDL_Thread,) GPURELEASECLASS(Buffer) GPURELEASECLASS(ComputePipeline) @@ -1113,25 +1111,25 @@ GPURELEASECLASS(Texture) GPURELEASECLASS(TransferBuffer) GPURELEASECLASS(Fence) -QJSCLASS(SDL_GPUCommandBuffer) -QJSCLASS(SDL_GPUComputePass) -QJSCLASS(SDL_GPUCopyPass) -QJSCLASS(SDL_GPURenderPass) -QJSCLASS(SDL_Cursor) +QJSCLASS(SDL_GPUCommandBuffer,) +QJSCLASS(SDL_GPUComputePass,) +QJSCLASS(SDL_GPUCopyPass,) +QJSCLASS(SDL_GPURenderPass,) +QJSCLASS(SDL_Cursor,) static void PHYSFS_File_free(JSRuntime *rt, PHYSFS_File *f) { PHYSFS_close(f); } -QJSCLASS(PHYSFS_File) +QJSCLASS(PHYSFS_File,) void rtree_free(JSRuntime *rt, rtree *tree) { rtree_destroy(tree); } -QJSCLASS(rtree) +QJSCLASS(rtree,) int js_arrlen(JSContext *js,JSValue v) { if (JS_IsUndefined(v)) return 0; @@ -2661,194 +2659,6 @@ static JSValue js_keymod(JSContext *js) return ret; } -static JSValue event2js(JSContext *js, SDL_Event event) -{ - JSValue e = JS_NewObject(js); - JS_SetPropertyStr(js, e, "type", JS_AtomToString(js, event_type_to_atom(js, event.type))); - JS_SetPropertyStr(js,e,"timestamp", number2js(js,event.common.timestamp)); - - switch(event.type) { - case SDL_EVENT_AUDIO_DEVICE_ADDED: - case SDL_EVENT_AUDIO_DEVICE_REMOVED: - JS_SetPropertyStr(js,e,"which", number2js(js,event.adevice.which)); - JS_SetPropertyStr(js,e,"recording", JS_NewBool(js,event.adevice.recording)); - break; - case SDL_EVENT_DISPLAY_ORIENTATION: - case SDL_EVENT_DISPLAY_ADDED: - case SDL_EVENT_DISPLAY_REMOVED: - case SDL_EVENT_DISPLAY_MOVED: - case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED: - case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED: - case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED: - JS_SetPropertyStr(js,e,"which", number2js(js,event.display.displayID)); - JS_SetPropertyStr(js,e,"data1", number2js(js,event.display.data1)); - JS_SetPropertyStr(js,e,"data2", number2js(js,event.display.data2)); - break; - case SDL_EVENT_MOUSE_MOTION: - JS_SetPropertyStr(js,e,"window", number2js(js,event.motion.windowID)); - JS_SetPropertyStr(js,e,"which", number2js(js,event.motion.which)); - JS_SetPropertyStr(js, e, "state", number2js(js,event.motion.state)); - JS_SetPropertyStr(js,e, "pos", vec22js(js,(HMM_Vec2){event.motion.x,event.motion.y})); - JS_SetPropertyStr(js,e,"d_pos", vec22js(js,(HMM_Vec2){event.motion.xrel, event.motion.yrel})); - break; - case SDL_EVENT_MOUSE_WHEEL: - JS_SetPropertyStr(js,e,"window", number2js(js,event.wheel.windowID)); - JS_SetPropertyStr(js,e,"which", number2js(js,event.wheel.which)); - JS_SetPropertyStr(js,e,"scroll", vec22js(js,(HMM_Vec2){event.wheel.x,event.wheel.y})); - JS_SetPropertyStr(js,e, "mouse", vec22js(js,(HMM_Vec2){event.wheel.mouse_x,event.wheel.mouse_y})); - break; - case SDL_EVENT_MOUSE_BUTTON_UP: - case SDL_EVENT_MOUSE_BUTTON_DOWN: - JS_SetPropertyStr(js,e,"window", number2js(js,event.button.windowID)); - JS_SetPropertyStr(js,e,"which", number2js(js,event.button.which)); - JS_SetPropertyStr(js,e,"down", JS_NewBool(js,event.button.down)); - JS_SetPropertyStr(js,e,"button", JS_AtomToString(js,mouse2atom(js,event.button.button))); - JS_SetPropertyStr(js,e,"clicks", number2js(js,event.button.clicks)); - JS_SetPropertyStr(js,e,"mouse", vec22js(js,(HMM_Vec2){event.button.x,event.button.y})); - break; - case SDL_EVENT_SENSOR_UPDATE: - JS_SetPropertyStr(js,e,"which", number2js(js,event.sensor.which)); - JS_SetPropertyStr(js,e, "sensor_timestamp", number2js(js,event.sensor.sensor_timestamp)); - break; - case SDL_EVENT_KEY_DOWN: - case SDL_EVENT_KEY_UP: - JS_SetPropertyStr(js,e,"window", number2js(js,event.key.windowID)); - JS_SetPropertyStr(js,e,"which", number2js(js,event.key.which)); - JS_SetPropertyStr(js,e,"down", JS_NewBool(js,event.key.down)); - JS_SetPropertyStr(js,e,"repeat", JS_NewBool(js,event.key.repeat)); - JS_SetPropertyStr(js,e,"key", number2js(js,event.key.key)); - JS_SetPropertyStr(js,e,"scancode", number2js(js,event.key.scancode)); - JS_SetPropertyStr(js,e,"mod", js_keymod(js)); - break; - case SDL_EVENT_FINGER_MOTION: - case SDL_EVENT_FINGER_DOWN: - case SDL_EVENT_FINGER_UP: - JS_SetPropertyStr(js,e,"touch", number2js(js,event.tfinger.touchID)); - JS_SetPropertyStr(js,e,"finger", number2js(js,event.tfinger.fingerID)); - JS_SetPropertyStr(js,e,"pos", vec22js(js, (HMM_Vec2){event.tfinger.x, event.tfinger.y})); - JS_SetPropertyStr(js,e,"d_pos", vec22js(js,(HMM_Vec2){event.tfinger.x, event.tfinger.dy})); - JS_SetPropertyStr(js,e,"pressure", number2js(js,event.tfinger.pressure)); - JS_SetPropertyStr(js,e,"window", number2js(js,event.key.windowID)); - break; - case SDL_EVENT_DROP_BEGIN: - case SDL_EVENT_DROP_FILE: - case SDL_EVENT_DROP_TEXT: - case SDL_EVENT_DROP_COMPLETE: - case SDL_EVENT_DROP_POSITION: - JS_SetPropertyStr(js,e,"window", number2js(js,event.drop.windowID)); - JS_SetPropertyStr(js,e,"pos", vec22js(js, (HMM_Vec2){event.drop.x,event.drop.y})); - JS_SetPropertyStr(js,e,"data", JS_NewString(js,event.drop.data)); - JS_SetPropertyStr(js,e,"source",JS_NewString(js,event.drop.source)); - break; - case SDL_EVENT_TEXT_INPUT: - JS_SetPropertyStr(js,e,"window", number2js(js,event.text.windowID)); - JS_SetPropertyStr(js,e,"text", JS_NewString(js,event.text.text)); - JS_SetPropertyStr(js,e,"mod", js_keymod(js)); - break; - case SDL_EVENT_CAMERA_DEVICE_APPROVED: - case SDL_EVENT_CAMERA_DEVICE_REMOVED: - case SDL_EVENT_CAMERA_DEVICE_ADDED: - case SDL_EVENT_CAMERA_DEVICE_DENIED: - JS_SetPropertyStr(js, e, "which", number2js(js,event.cdevice.which)); - break; - case SDL_EVENT_CLIPBOARD_UPDATE: - JS_SetPropertyStr(js, e, "owner", JS_NewBool(js,event.clipboard.owner)); - break; - case SDL_EVENT_WINDOW_SHOWN: - case SDL_EVENT_WINDOW_HIDDEN: - case SDL_EVENT_WINDOW_EXPOSED: - case SDL_EVENT_WINDOW_MOVED: - case SDL_EVENT_WINDOW_RESIZED: - case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: - case SDL_EVENT_WINDOW_METAL_VIEW_RESIZED: - case SDL_EVENT_WINDOW_MINIMIZED: - case SDL_EVENT_WINDOW_MAXIMIZED: - case SDL_EVENT_WINDOW_RESTORED: - case SDL_EVENT_WINDOW_MOUSE_ENTER: - case SDL_EVENT_WINDOW_MOUSE_LEAVE: - case SDL_EVENT_WINDOW_FOCUS_GAINED: - case SDL_EVENT_WINDOW_FOCUS_LOST: - case SDL_EVENT_WINDOW_CLOSE_REQUESTED: - case SDL_EVENT_WINDOW_HIT_TEST: - case SDL_EVENT_WINDOW_ICCPROF_CHANGED: - case SDL_EVENT_WINDOW_DISPLAY_CHANGED: - case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: - case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: - case SDL_EVENT_WINDOW_OCCLUDED: - case SDL_EVENT_WINDOW_ENTER_FULLSCREEN: - case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN: - case SDL_EVENT_WINDOW_DESTROYED: - case SDL_EVENT_WINDOW_HDR_STATE_CHANGED: - /* rest of SDL_EVENT_WINDOW_ here */ - JS_SetPropertyStr(js,e,"which", number2js(js, event.window.windowID)); - JS_SetPropertyStr(js,e,"data1", number2js(js, event.window.data1)); - JS_SetPropertyStr(js,e,"data2", number2js(js, event.window.data2)); - break; - - case SDL_EVENT_JOYSTICK_ADDED: - case SDL_EVENT_JOYSTICK_REMOVED: - case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE: - JS_SetPropertyStr(js,e,"which", number2js(js,event.jdevice.which)); - break; - case SDL_EVENT_JOYSTICK_AXIS_MOTION: - JS_SetPropertyStr(js,e,"which", number2js(js,event.jaxis.which)); - JS_SetPropertyStr(js,e,"axis", number2js(js,event.jaxis.axis)); - JS_SetPropertyStr(js,e,"value", number2js(js,event.jaxis.value)); - break; - case SDL_EVENT_JOYSTICK_BALL_MOTION: - JS_SetPropertyStr(js,e,"which", number2js(js,event.jball.which)); - JS_SetPropertyStr(js,e,"ball",number2js(js,event.jball.ball)); - JS_SetPropertyStr(js,e, "rel", vec22js(js,(HMM_Vec2){event.jball.xrel,event.jball.yrel})); - break; - case SDL_EVENT_JOYSTICK_BUTTON_DOWN: - case SDL_EVENT_JOYSTICK_BUTTON_UP: - JS_SetPropertyStr(js,e,"which", number2js(js,event.jbutton.which)); - JS_SetPropertyStr(js,e,"button", number2js(js,event.jbutton.button)); - JS_SetPropertyStr(js,e,"down", JS_NewBool(js,event.jbutton.down)); - break; - - case SDL_EVENT_GAMEPAD_ADDED: - case SDL_EVENT_GAMEPAD_REMOVED: - case SDL_EVENT_GAMEPAD_REMAPPED: - case SDL_EVENT_GAMEPAD_UPDATE_COMPLETE: - case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED: - JS_SetPropertyStr(js,e,"which", number2js(js,event.gdevice.which)); - break; - case SDL_EVENT_GAMEPAD_AXIS_MOTION: - JS_SetPropertyStr(js,e,"which", number2js(js,event.gaxis.which)); - JS_SetPropertyStr(js,e,"axis", number2js(js,event.gaxis.axis)); - JS_SetPropertyStr(js,e,"value", number2js(js,event.gaxis.value)); - break; - case SDL_EVENT_GAMEPAD_BUTTON_DOWN: - case SDL_EVENT_GAMEPAD_BUTTON_UP: - JS_SetPropertyStr(js,e,"which", number2js(js,event.gbutton.which)); - JS_SetPropertyStr(js,e,"button", number2js(js,event.gbutton.button)); - JS_SetPropertyStr(js,e,"down", JS_NewBool(js,event.gbutton.down)); - break; - case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: - case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: - case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: - JS_SetPropertyStr(js,e,"which", number2js(js,event.gtouchpad.which)); - JS_SetPropertyStr(js,e,"touchpad", number2js(js,event.gtouchpad.touchpad)); - JS_SetPropertyStr(js,e,"finger", number2js(js,event.gtouchpad.finger)); - JS_SetPropertyStr(js,e,"pos", vec22js(js,(HMM_Vec2){event.gtouchpad.x,event.gtouchpad.y})); - JS_SetPropertyStr(js,e,"pressure", number2js(js,event.gtouchpad.pressure)); - break; - case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: - JS_SetPropertyStr(js,e,"which", number2js(js,event.gsensor.which)); - JS_SetPropertyStr(js,e,"sensor", number2js(js,event.gsensor.sensor)); - JS_SetPropertyStr(js,e,"sensor_timestamp", number2js(js,event.gsensor.sensor_timestamp)); - break; - case SDL_EVENT_USER: - JS_SetPropertyStr(js,e,"cb", JS_DupValue(js,*(JSValue*)event.user.data1)); - JS_FreeValue(js,*(JSValue*)event.user.data1); - free(event.user.data1); - event.user.data1 = NULL; - break; - } - return e; -} - JSC_CCALL(camera_list, int num; SDL_CameraID *ids = SDL_GetCameras(&num); @@ -5963,8 +5773,6 @@ JSC_CCALL(io_searchpath, JS_SetPropertyUint32(js,ret,i,JS_NewString(js,paths[i])); ) -extern void prosperon_mount_core(); - JSC_CCALL(io_mount_core, int mount = JS_ToBool(js,argv[0]); if (!mount) @@ -7070,27 +6878,25 @@ JSC_CCALL(os_createactor, create_actor(margc, margv); ) +#include "qjs_wota.h" + JSC_CCALL(os_mailbox_push, if (argc < 2) return JS_ThrowInternalError(js, "Need an actor and a message"); - if (!JS_IsArrayBuffer(js, argv[1])) return JS_ThrowInternalError(js, "Object to push must be an array buffer"); + if (!JS_IsObject(argv[1])) return JS_ThrowInternalError(js, "Object to push must be an object."); const char *id = JS_ToCString(js, argv[0]); - prosperon_rt *target = get_actor(id); + int exist = actor_exists(id); JS_FreeCString(js,id); - if (!target) + if (!exist) return JS_ThrowInternalError(js, "No mailbox found for given ID"); - size_t size; - uint8_t *buf = JS_GetArrayBuffer(js, &size, argv[1]); - if (!buf) return JS_ThrowInternalError(js, "Could not get data from arraybuffer"); - - void *data = js_malloc_rt(JS_GetRuntime(js), size); - if (!data) return JS_ThrowInternalError(js, "Memory allocation failed"); - - memcpy(data, buf, size); - - struct message msg = {data, size}; - send_message(target, msg); + void *data = value2wota(js, argv[1]); + + char *err = send_message(id, data); + if (err) { + free(data); + return JS_ThrowInternalError(js, "Could not send message: %s", err); + } ) JSC_CCALL(os_register_actor, @@ -7101,15 +6907,14 @@ JSC_CCALL(os_register_actor, rt->message_handle = JS_DupValue(js, argv[1]); rt->context = js; JS_FreeCString(js, id); - printf("registered actor at %p with id %s, with function %p\n", rt, id, JS_VALUE_GET_PTR(rt->message_handle)); ) JSC_CCALL(os_mailbox_exist, const char *id = JS_ToCString(js, argv[0]); - prosperon_rt *actor = get_actor(id); + int exist = actor_exists(id); JS_FreeCString(js, id); - return JS_NewBool(js, actor ? 1 : 0); + return JS_NewBool(js, exist); ) JSC_CCALL(os_unneeded, @@ -7131,7 +6936,7 @@ JSValue js_os_set_trace(JSContext *js, JSValue self, JSValue value) JSC_CCALL(os_destroy, prosperon_rt *rt = JS_GetContextOpaque(js); - actor_free(rt); + rt->need_stop = 1; ) static const JSCFunctionListEntry js_os_funcs[] = { diff --git a/source/nota.h b/source/nota.h index 0440e41b..98f50586 100755 --- a/source/nota.h +++ b/source/nota.h @@ -29,7 +29,7 @@ #define NOTA_EXP_SIGN(CHAR) (CHAR & (1<<4)) #define NOTA_TYPE 0x70 #define NOTA_HEAD_DATA 0x0f -#define CONTINUE(CHAR) ((CHAR)>>7) +#define CONTINUE(CHAR) (CHAR>>7) #define UTF8_DATA 0x3f /* A helper to get the high-level Nota type nibble from a byte */ diff --git a/source/prosperon.c b/source/prosperon.c index 056ef75a..ad56e7c6 100644 --- a/source/prosperon.c +++ b/source/prosperon.c @@ -10,6 +10,10 @@ #include #include +#define WOTA_IMPLEMENTATION +#include "wota.h" +#include "qjs_wota.h" + #include "physfs.h" #include "stb_ds.h" #include "jsffi.h" @@ -36,18 +40,19 @@ static prosperon_rt **ready_queue = NULL; static prosperon_rt **slow_queue = NULL; -static prosperon_rt **reclaim_queue = NULL; static SDL_Mutex *queue_mutex = NULL; static SDL_Condition *queue_cond = NULL; +static SDL_Mutex *actors_mutex = NULL; static struct { char *key; prosperon_rt *value; } *actors = NULL; static unsigned char *zip_buffer_global = NULL; static char *prosperon = NULL; static void set_actor_state(prosperon_rt *actor); +static void actor_signal(prosperon_rt *actor); static Uint32 actor_remove_cb(prosperon_rt *actor, Uint32 id, Uint32 interval) { - printf("freeing actor after %ud\n", interval); + printf("freeing actor after %u\n", interval); actor_free(actor); return 0; } @@ -174,11 +179,13 @@ int prosperon_mount_core(void) if (zip_offset < 0 || zip_offset >= size) return fprintf(stderr, "Invalid zip offset: %ld\n", zip_offset), free(zip_buffer_global), 0; int ret = PHYSFS_mountMemory(zip_buffer_global + zip_offset, appended_zip_size, free_zip, "core.zip", NULL, 0); - if (!ret) printf("COULD NOT MOUNT! Reason: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + if (!ret) { + printf("COULD NOT MOUNT! Reason: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + } return ret; } -prosperon_rt *create_actor(int argc, char **argv) +void create_actor(int argc, char **argv) { prosperon_rt *actor = calloc(sizeof(*actor), 1); actor->cmd.argc = argc; @@ -188,46 +195,66 @@ prosperon_rt *create_actor(int argc, char **argv) actor->message_handle = JS_UNDEFINED; actor->unneeded = JS_UNDEFINED; actor->on_exception = JS_UNDEFINED; + arrsetcap(actor->messages, 5); - actor->mutex = SDL_CreateMutex(); + arrsetcap(actor->events, 5); + + actor->mutex = SDL_CreateMutex(); /* Protects JSContext + state */ + actor->msg_mutex = SDL_CreateMutex(); /* Mailbox queue lock */ + actor->evt_mutex = SDL_CreateMutex(); /* CHANGED FOR EVENTS: event queue lock */ + + /* Lock actor->mutex while initializing JS runtime. */ SDL_LockMutex(actor->mutex); - printf("created actor %p\n", actor); script_startup(actor); - const char *fnstr = JS_ToCString(actor->context, actor->message_handle); - printf("WWWWWW AFTER MAKE ACTOR WWWWWW\n"); - printf("NOW??? actor turn %p , fn at %p\n", actor, JS_VALUE_GET_PTR(actor->message_handle)); - printf("%s\n", fnstr); - JS_FreeCString(actor->context, fnstr); - set_actor_state(actor); SDL_UnlockMutex(actor->mutex); - return actor; + + if (actor->need_stop) + actor_free(actor); } prosperon_rt *get_actor(char *id) { int idx = shgeti(actors, id); - return idx == -1 ? NULL : actors[idx].value; + if (idx == -1) return NULL; + prosperon_rt *actor; + SDL_LockMutex(actors_mutex); + actor = actors[idx].value; + SDL_UnlockMutex(actors_mutex); + return actor; } char *register_actor(char *id, prosperon_rt *actor) { if (shgeti(actors, id) != -1) return "Actor with given ID already exists."; actor->id = strdup(id); + SDL_LockMutex(actors_mutex); shput(actors, id, actor); + SDL_UnlockMutex(actors_mutex); return NULL; } -void send_message(prosperon_rt *target, struct message msg) +char *send_message(char *id, void *msg) { - SDL_LockMutex(target->mutex); + prosperon_rt *target = get_actor(id); + if (!target) return "Could not get actor from id."; + + SDL_LockMutex(target->msg_mutex); arrput(target->messages, msg); - if (target->ar) SDL_RemoveTimer(target->ar), target->ar = 0; + SDL_UnlockMutex(target->msg_mutex); + + SDL_LockMutex(target->mutex); + if (target->ar) { + SDL_RemoveTimer(target->ar); + target->ar = 0; + } actor_signal(target); SDL_UnlockMutex(target->mutex); + + return NULL; } -void actor_signal(prosperon_rt *actor) +static void actor_signal(prosperon_rt *actor) { SDL_LockMutex(actor->mutex); if (actor->state != ACTOR_READY) { @@ -240,12 +267,22 @@ void actor_signal(prosperon_rt *actor) SDL_UnlockMutex(actor->mutex); } +/* set_actor_state should check if either messages or events are pending. */ static void set_actor_state(prosperon_rt *actor) { + SDL_LockMutex(actor->msg_mutex); + int has_messages = arrlen(actor->messages); + SDL_UnlockMutex(actor->msg_mutex); + + // CHANGED FOR EVENTS: also check actor->events + SDL_LockMutex(actor->evt_mutex); + int has_events = arrlen(actor->events); + SDL_UnlockMutex(actor->evt_mutex); + SDL_LockMutex(actor->mutex); - if (arrlen(actor->messages) == 0) { + if (has_messages == 0 && has_events == 0) { actor->state = ACTOR_IDLE; - actor->ar = SDL_AddTimerNS(100e9, actor_remove_cb, actor); + actor->ar = SDL_AddTimerNS(1e9, actor_remove_cb, actor); } else { actor->state = ACTOR_READY; SDL_LockMutex(queue_mutex); @@ -258,86 +295,148 @@ static void set_actor_state(prosperon_rt *actor) void actor_turn(prosperon_rt *actor) { - printf("AAAAAAAAAAAAAAAAA ACTOR TAKE TURN %p\n", actor); - printf("actor turn %p , fn at %p\n", actor, JS_VALUE_GET_PTR(actor->message_handle)); + /* 1) Copy and clear messages under msg_mutex. */ + SDL_LockMutex(actor->msg_mutex); + void **msgs_copy = NULL; + int msg_count = arrlen(actor->messages); + arrsetlen(msgs_copy, msg_count); + if (msg_count > 0) { + memcpy(msgs_copy, actor->messages, msg_count * sizeof(void*)); + arrsetlen(actor->messages,0); + } + SDL_UnlockMutex(actor->msg_mutex); + /* 2) Copy and clear events under evt_mutex. */ + SDL_LockMutex(actor->evt_mutex); + JSValue *ev_copy = NULL; + int ev_count = arrlen(actor->events); + arrsetlen(ev_copy, ev_count); + if (ev_count > 0) { + memcpy(ev_copy, actor->events, ev_count * sizeof(JSValue)); + arrsetlen(actor->events,0); + } + SDL_UnlockMutex(actor->evt_mutex); + + /* 3) Now lock the main actor->mutex to run JS with messages + events. */ SDL_LockMutex(actor->mutex); actor->state = ACTOR_RUNNING; - const char *fnstr = JS_ToCString(actor->context, actor->message_handle); - printf("NOW??? actor turn %p , fn at %p. S A FN???? %d\n", actor, JS_VALUE_GET_PTR(actor->message_handle), JS_IsFunction(actor->context, actor->message_handle)); - printf("%p\n", fnstr); - JS_FreeCString(actor->context, fnstr); - - - while (arrlen(actor->messages) > 0) { - struct message msg = actor->messages[0]; - arrdel(actor->messages, 0); - JSValue arg = JS_NewArrayBuffer(actor->context, msg.data, msg.size, js_dofree, NULL, 0); + /* Process messages */ + for (int i = 0; i < msg_count; i++) { + void *msg = msgs_copy[i]; + JSValue arg = wota2value(actor->context, msg); + free(msg); JSValue result = JS_Call(actor->context, actor->message_handle, JS_UNDEFINED, 1, &arg); uncaught_exception(actor->context, result); } + arrfree(msgs_copy); - while (arrlen(actor->events) > 0) { - JSValue event = actor->events[0]; - arrdel(actor->events, 0); - JSValue result = JS_Call(actor->context, event, JS_UNDEFINED, 0, NULL); + /* Process events */ + for (int i = 0; i < ev_count; i++) { + JSValue event_cb = ev_copy[i]; + JSValue result = JS_Call(actor->context, event_cb, JS_UNDEFINED, 0, NULL); uncaught_exception(actor->context, result); + JS_FreeValue(actor->context, event_cb); } + arrfree(ev_copy); set_actor_state(actor); SDL_UnlockMutex(actor->mutex); + + if (actor->need_stop) + actor_free(actor); } void actor_free(prosperon_rt *actor) { - printf("KILLING ACTOR %p\n", actor); + // Delete it out of actors first so it can no longer get messages + SDL_LockMutex(actors_mutex); shdel(actors, actor->id); - JSContext *js = actor->context; + int remaining = shlen(actors); + SDL_UnlockMutex(actors_mutex); + + // Do not go forward with actor destruction until the actor is completely free SDL_LockMutex(actor->mutex); + SDL_LockMutex(actor->evt_mutex); + SDL_LockMutex(actor->msg_mutex); + + JSContext *js = actor->context; + JS_FreeValue(js, actor->cycle_fn); JS_FreeValue(js, actor->idx_buffer); JS_FreeValue(js, actor->message_handle); JS_FreeValue(js, actor->on_exception); + SDL_RemoveTimer(actor->ar); - for (int i = 0; i < arrlen(actor->js_swapchains); i++) JS_FreeValue(js, actor->js_swapchains[i]); - for (int i = 0; i < arrlen(actor->events); i++) JS_FreeValue(js, actor->events[i]); - for (int i = 0; i < hmlen(actor->timers); i++) JS_FreeValue(js, actor->timers[i].value); + for (int i = 0; i < arrlen(actor->js_swapchains); i++) + JS_FreeValue(js, actor->js_swapchains[i]); + + for (int i = 0; i < hmlen(actor->timers); i++) { + SDL_RemoveTimer(actor->timers[i].key); + JS_FreeValue(js, actor->timers[i].value); + } + + hmfree(actor->timers); arrfree(actor->js_swapchains); - arrfree(actor->events); - arrfree(actor->messages); arrfree(actor->module_registry); + + for (int i = 0; i < arrlen(actor->messages); i++) + free(actor->messages[i]); + + arrfree(actor->messages); + + /* If still present, free each JSValue. */ + for (int i = 0; i < arrlen(actor->events); i++) + JS_FreeValue(js, actor->events[i]); + arrfree(actor->events); + + SDL_UnlockMutex(actor->mutex); + SDL_UnlockMutex(actor->msg_mutex); + SDL_UnlockMutex(actor->evt_mutex); + SDL_DestroyMutex(actor->mutex); + SDL_DestroyMutex(actor->msg_mutex); + SDL_DestroyMutex(actor->evt_mutex); + JSRuntime *rt = JS_GetRuntime(js); JS_FreeContext(js); JS_FreeRuntime(rt); free(actor->id); free(actor); + + if (remaining == 0) + exit(0); } +/* Timer callback adds an event to the queue under evt_mutex. */ Uint32 actor_timer_cb(prosperon_rt *actor, SDL_TimerID id, Uint32 interval) { int idx = hmgeti(actor->timers, id); if (idx == -1) return 0; - SDL_LockMutex(actor->mutex); + + SDL_LockMutex(actor->evt_mutex); JSValue cb = actor->timers[idx].value; hmdel(actor->timers, id); arrput(actor->events, cb); - SDL_UnlockMutex(actor->mutex); + SDL_UnlockMutex(actor->evt_mutex); + actor_signal(actor); return interval; } +/* JS function that schedules a timer. */ JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv) { prosperon_rt *actor = JS_GetContextOpaque(js); double seconds; - JS_ToFloat64(js, &seconds, argv[2]); - Uint64 ns = seconds * 1000000000.0f; + JS_ToFloat64(js, &seconds, argv[1]); + Uint64 ns = (Uint64)(seconds * 1000000000.0); Uint32 id = SDL_AddTimerNS(ns, actor_timer_cb, actor); - SDL_LockMutex(actor->mutex); - hmput(actor->timers, id, JS_DupValue(js, argv[1])); - SDL_UnlockMutex(actor->mutex); + + SDL_LockMutex(actor->evt_mutex); + hmput(actor->timers, id, JS_DupValue(js, argv[0])); + SDL_UnlockMutex(actor->evt_mutex); + return JS_UNDEFINED; } @@ -345,22 +444,32 @@ JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *arg { prosperon_rt *actor = JS_GetContextOpaque(js); Uint32 timer_id; - JS_ToUint32(js, &timer_id, argv[1]); + JS_ToUint32(js, &timer_id, argv[0]); SDL_RemoveTimer(timer_id); - SDL_LockMutex(actor->mutex); - hmdel(actor->timers, timer_id); - SDL_UnlockMutex(actor->mutex); + + JSValue cb = JS_UNDEFINED; + + SDL_LockMutex(actor->evt_mutex); + int id = hmgeti(actor->timers, timer_id); + if (id != -1) { + cb = actor->timers[id].value; + hmdel(actor->timers, timer_id); + } + SDL_UnlockMutex(actor->evt_mutex); + + JS_FreeValue(js,cb); + return JS_UNDEFINED; } void script_startup(prosperon_rt *prt) { JSRuntime *rt; - #ifdef TRACY_ENABLE +#ifdef TRACY_ENABLE rt = JS_NewRuntime2(&tracy_malloc_funcs, NULL); - #else +#else rt = JS_NewRuntime(); - #endif +#endif JSContext *js = JS_NewContextRaw(rt); JS_AddIntrinsicBaseObjects(js); JS_AddIntrinsicEval(js); @@ -373,6 +482,7 @@ void script_startup(prosperon_rt *prt) JS_AddIntrinsicBigDecimal(js); JS_AddIntrinsicOperators(js); JS_EnableBignumExt(js, 1); + JS_SetContextOpaque(js, prt); prt->context = js; ffi_load(js); @@ -387,55 +497,39 @@ void script_startup(prosperon_rt *prt) void *data = malloc(stat.filesize); PHYSFS_readBytes(eng, data, stat.filesize); PHYSFS_close(eng); - SDL_LockMutex(prt->mutex); - JSValue v = JS_Eval(js, data, strlen(data), ENGINE, JS_EVAL_FLAG_STRICT); + + /* Called with actor->mutex locked by create_actor(). */ + JSValue v = JS_Eval(js, data, (size_t)stat.filesize, ENGINE, JS_EVAL_FLAG_STRICT); uncaught_exception(js, v); - SDL_UnlockMutex(prt->mutex); } void uncaught_exception(JSContext *js, JSValue v) { + /* Simplified, just free the value. */ JS_FreeValue(js, v); return; - prosperon_rt *rt = JS_GetContextOpaque(js); - SDL_LockMutex(rt->mutex); - if (!JS_HasException(js)) goto END; - - JSValue exp = JS_GetException(js); - printf("error in actor %p\n", rt); - printf("value is tagged with %d. is error? %d\n", JS_VALUE_GET_TAG(exp), JS_IsError(js, exp)); - if (JS_IsFunction(js, rt->on_exception)) { - JSValue ret = JS_Call(js, rt->on_exception, JS_UNDEFINED, 1, &exp); - JS_FreeValue(js, ret); - } else { - const char *msg = JS_ToCString(js, exp); - JSValue stack = JS_GetPropertyStr(js, exp, "stack"); - const char *st = JS_ToCString(js, stack); - printf("Unhandled exception: %s\n%s\n", msg, st); - JS_FreeCString(js, st); - JS_FreeCString(js, msg); - JS_FreeValue(js, stack); - } - JS_FreeValue(js, exp); -END: - SDL_UnlockMutex(rt->mutex); + /* If you want to handle the exception properly, do it here. */ } void script_evalf(JSContext *js, const char *format, ...) { prosperon_rt *rt = JS_GetContextOpaque(js); SDL_LockMutex(rt->mutex); + va_list args; va_start(args, format); int len = vsnprintf(NULL, 0, format, args); va_end(args); + char *eval = malloc(len + 1); va_start(args, format); vsnprintf(eval, len + 1, format, args); va_end(args); + JSValue obj = JS_Eval(js, eval, len, "C eval", JS_EVAL_FLAG_STRICT); free(eval); uncaught_exception(js, obj); + SDL_UnlockMutex(rt->mutex); } @@ -444,13 +538,19 @@ static int crank_actor(void *data) while (true) { prosperon_rt *actor = NULL; SDL_LockMutex(queue_mutex); - if (arrlen(ready_queue) > 0) actor = ready_queue[0], arrdel(ready_queue, 0); - else if (arrlen(slow_queue) > 0) actor = slow_queue[0], arrdel(slow_queue, 0); - else { + if (arrlen(ready_queue) > 0) { + actor = ready_queue[0]; + arrdel(ready_queue, 0); + } else if (arrlen(slow_queue) > 0) { + actor = slow_queue[0]; + arrdel(slow_queue, 0); + } else { SDL_WaitCondition(queue_cond, queue_mutex); + SDL_UnlockMutex(queue_mutex); continue; } SDL_UnlockMutex(queue_mutex); + if (actor) actor_turn(actor); } return 0; @@ -485,13 +585,628 @@ static void exit_handler(void) } } +// Assume these helper functions exist or need to be implemented +const char* event_type_to_string(Uint32 event_type) { + switch (event_type) { + // Application events + case SDL_EVENT_QUIT: return "quit"; + case SDL_EVENT_TERMINATING: return "terminating"; + case SDL_EVENT_LOW_MEMORY: return "low_memory"; + case SDL_EVENT_WILL_ENTER_BACKGROUND: return "will_enter_background"; + case SDL_EVENT_DID_ENTER_BACKGROUND: return "did_enter_background"; + case SDL_EVENT_WILL_ENTER_FOREGROUND: return "will_enter_foreground"; + case SDL_EVENT_DID_ENTER_FOREGROUND: return "did_enter_foreground"; + case SDL_EVENT_LOCALE_CHANGED: return "locale_changed"; + case SDL_EVENT_SYSTEM_THEME_CHANGED: return "system_theme_changed"; + + // Display events + case SDL_EVENT_DISPLAY_ORIENTATION: return "display_orientation"; + case SDL_EVENT_DISPLAY_ADDED: return "display_added"; + case SDL_EVENT_DISPLAY_REMOVED: return "display_removed"; + case SDL_EVENT_DISPLAY_MOVED: return "display_moved"; + case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED: return "display_desktop_mode_changed"; + case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED: return "display_current_mode_changed"; + case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED: return "display_content_scale_changed"; + + // Window events + case SDL_EVENT_WINDOW_SHOWN: return "window_shown"; + case SDL_EVENT_WINDOW_HIDDEN: return "window_hidden"; + case SDL_EVENT_WINDOW_EXPOSED: return "window_exposed"; + case SDL_EVENT_WINDOW_MOVED: return "window_moved"; + case SDL_EVENT_WINDOW_RESIZED: return "window_resized"; + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: return "window_pixel_size_changed"; + case SDL_EVENT_WINDOW_METAL_VIEW_RESIZED: return "window_metal_view_resized"; + case SDL_EVENT_WINDOW_MINIMIZED: return "window_minimized"; + case SDL_EVENT_WINDOW_MAXIMIZED: return "window_maximized"; + case SDL_EVENT_WINDOW_RESTORED: return "window_restored"; + case SDL_EVENT_WINDOW_MOUSE_ENTER: return "window_mouse_enter"; + case SDL_EVENT_WINDOW_MOUSE_LEAVE: return "window_mouse_leave"; + case SDL_EVENT_WINDOW_FOCUS_GAINED: return "window_focus_gained"; + case SDL_EVENT_WINDOW_FOCUS_LOST: return "window_focus_lost"; + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: return "window_close_requested"; + case SDL_EVENT_WINDOW_HIT_TEST: return "window_hit_test"; + case SDL_EVENT_WINDOW_ICCPROF_CHANGED: return "window_iccprof_changed"; + case SDL_EVENT_WINDOW_DISPLAY_CHANGED: return "window_display_changed"; + case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: return "window_display_scale_changed"; + case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: return "window_safe_area_changed"; + case SDL_EVENT_WINDOW_OCCLUDED: return "window_occluded"; + case SDL_EVENT_WINDOW_ENTER_FULLSCREEN: return "window_enter_fullscreen"; + case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN: return "window_leave_fullscreen"; + case SDL_EVENT_WINDOW_DESTROYED: return "window_destroyed"; + case SDL_EVENT_WINDOW_HDR_STATE_CHANGED: return "window_hdr_state_changed"; + + // Keyboard events + case SDL_EVENT_KEY_DOWN: return "key_down"; + case SDL_EVENT_KEY_UP: return "key_up"; + case SDL_EVENT_TEXT_EDITING: return "text_editing"; + case SDL_EVENT_TEXT_INPUT: return "text_input"; + case SDL_EVENT_KEYMAP_CHANGED: return "keymap_changed"; + case SDL_EVENT_KEYBOARD_ADDED: return "keyboard_added"; + case SDL_EVENT_KEYBOARD_REMOVED: return "keyboard_removed"; + case SDL_EVENT_TEXT_EDITING_CANDIDATES: return "text_editing_candidates"; + + // Mouse events + case SDL_EVENT_MOUSE_MOTION: return "mouse_motion"; + case SDL_EVENT_MOUSE_BUTTON_DOWN: return "mouse_button_down"; + case SDL_EVENT_MOUSE_BUTTON_UP: return "mouse_button_up"; + case SDL_EVENT_MOUSE_WHEEL: return "mouse_wheel"; + case SDL_EVENT_MOUSE_ADDED: return "mouse_added"; + case SDL_EVENT_MOUSE_REMOVED: return "mouse_removed"; + + // Joystick events + case SDL_EVENT_JOYSTICK_AXIS_MOTION: return "joystick_axis_motion"; + case SDL_EVENT_JOYSTICK_BALL_MOTION: return "joystick_ball_motion"; + case SDL_EVENT_JOYSTICK_HAT_MOTION: return "joystick_hat_motion"; + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: return "joystick_button_down"; + case SDL_EVENT_JOYSTICK_BUTTON_UP: return "joystick_button_up"; + case SDL_EVENT_JOYSTICK_ADDED: return "joystick_added"; + case SDL_EVENT_JOYSTICK_REMOVED: return "joystick_removed"; + case SDL_EVENT_JOYSTICK_BATTERY_UPDATED: return "joystick_battery_updated"; + case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE: return "joystick_update_complete"; + + // Gamepad events + case SDL_EVENT_GAMEPAD_AXIS_MOTION: return "gamepad_axis_motion"; + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: return "gamepad_button_down"; + case SDL_EVENT_GAMEPAD_BUTTON_UP: return "gamepad_button_up"; + case SDL_EVENT_GAMEPAD_ADDED: return "gamepad_added"; + case SDL_EVENT_GAMEPAD_REMOVED: return "gamepad_removed"; + case SDL_EVENT_GAMEPAD_REMAPPED: return "gamepad_remapped"; + case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: return "gamepad_touchpad_down"; + case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: return "gamepad_touchpad_motion"; + case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: return "gamepad_touchpad_up"; + case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: return "gamepad_sensor_update"; + case SDL_EVENT_GAMEPAD_UPDATE_COMPLETE: return "gamepad_update_complete"; + case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED: return "gamepad_steam_handle_updated"; + + // Touch events + case SDL_EVENT_FINGER_DOWN: return "finger_down"; + case SDL_EVENT_FINGER_UP: return "finger_up"; + case SDL_EVENT_FINGER_MOTION: return "finger_motion"; + + // Clipboard events + case SDL_EVENT_CLIPBOARD_UPDATE: return "clipboard_update"; + + // Drag and drop events + case SDL_EVENT_DROP_FILE: return "drop_file"; + case SDL_EVENT_DROP_TEXT: return "drop_text"; + case SDL_EVENT_DROP_BEGIN: return "drop_begin"; + case SDL_EVENT_DROP_COMPLETE: return "drop_complete"; + case SDL_EVENT_DROP_POSITION: return "drop_position"; + + // Audio device events + case SDL_EVENT_AUDIO_DEVICE_ADDED: return "audio_device_added"; + case SDL_EVENT_AUDIO_DEVICE_REMOVED: return "audio_device_removed"; + case SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED: return "audio_device_format_changed"; + + // Sensor events + case SDL_EVENT_SENSOR_UPDATE: return "sensor_update"; + + // Pen events + case SDL_EVENT_PEN_PROXIMITY_IN: return "pen_proximity_in"; + case SDL_EVENT_PEN_PROXIMITY_OUT: return "pen_proximity_out"; + case SDL_EVENT_PEN_DOWN: return "pen_down"; + case SDL_EVENT_PEN_UP: return "pen_up"; + case SDL_EVENT_PEN_BUTTON_DOWN: return "pen_button_down"; + case SDL_EVENT_PEN_BUTTON_UP: return "pen_button_up"; + case SDL_EVENT_PEN_MOTION: return "pen_motion"; + case SDL_EVENT_PEN_AXIS: return "pen_axis"; + + // Camera events + case SDL_EVENT_CAMERA_DEVICE_ADDED: return "camera_device_added"; + case SDL_EVENT_CAMERA_DEVICE_REMOVED: return "camera_device_removed"; + case SDL_EVENT_CAMERA_DEVICE_APPROVED: return "camera_device_approved"; + case SDL_EVENT_CAMERA_DEVICE_DENIED: return "camera_device_denied"; + + // Render events + case SDL_EVENT_RENDER_TARGETS_RESET: return "render_targets_reset"; + case SDL_EVENT_RENDER_DEVICE_RESET: return "render_device_reset"; + case SDL_EVENT_RENDER_DEVICE_LOST: return "render_device_lost"; + + // User event (assuming it should be included) + case SDL_EVENT_USER: return "user"; + + default: return "unknown"; + } +} + +const char* mouse_button_to_string(int mouse) { + switch (mouse) { + case SDL_BUTTON_LEFT: return "left"; + case SDL_BUTTON_MIDDLE: return "middle"; + case SDL_BUTTON_RIGHT: return "right"; + case SDL_BUTTON_X1: return "x1"; + case SDL_BUTTON_X2: return "x2"; + default: return "left"; + } +} + +static void wota_write_vec2(WotaBuffer *wb, double x, double y) { + // We'll store as WOTA_ARR of length 2, then two numbers + wota_write_array(wb, 2); + wota_write_number(wb, x); + wota_write_number(wb, y); +} + +static int event2wota_count_props(const SDL_Event *event) +{ + // We always store at least "type" and "timestamp". + int count = 2; + + switch (event->type) { + + case SDL_EVENT_AUDIO_DEVICE_ADDED: + case SDL_EVENT_AUDIO_DEVICE_REMOVED: + count += 2; // which, recording + break; + + case SDL_EVENT_DISPLAY_ORIENTATION: + case SDL_EVENT_DISPLAY_ADDED: + case SDL_EVENT_DISPLAY_REMOVED: + case SDL_EVENT_DISPLAY_MOVED: + case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED: + case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED: + case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED: + count += 3; // which, data1, data2 + break; + + case SDL_EVENT_MOUSE_MOTION: + count += 4; // window, which, state, pos, d_pos => actually 5 keys + // "pos" is 1 key, "d_pos" is another => total is 6 keys + // Let's break that down carefully: + // "window" + // "which" + // "state" + // "pos" + // "d_pos" + // That is 5 additional keys, so overall = 2 (base) + 5 = 7 + count += 5; + break; + + case SDL_EVENT_MOUSE_WHEEL: + // window, which, scroll, mouse => 4 extra + count += 4; + break; + + case SDL_EVENT_MOUSE_BUTTON_UP: + case SDL_EVENT_MOUSE_BUTTON_DOWN: + // window, which, down, button, clicks, mouse => 6 extra + count += 6; + break; + + case SDL_EVENT_SENSOR_UPDATE: + // which, sensor_timestamp => 2 extra + count += 2; + break; + + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + // window, which, down, repeat, key, scancode, mod => 7 extra + count += 7; + break; + + case SDL_EVENT_FINGER_MOTION: + case SDL_EVENT_FINGER_DOWN: + case SDL_EVENT_FINGER_UP: + // touch, finger, pos, d_pos, pressure, window => 6 extra + count += 6; + break; + + case SDL_EVENT_DROP_BEGIN: + case SDL_EVENT_DROP_FILE: + case SDL_EVENT_DROP_TEXT: + case SDL_EVENT_DROP_COMPLETE: + case SDL_EVENT_DROP_POSITION: + // window, pos, data, source => 4 extra + count += 4; + break; + + case SDL_EVENT_TEXT_INPUT: + // window, text, mod => 3 extra + count += 3; + break; + + case SDL_EVENT_CAMERA_DEVICE_APPROVED: + case SDL_EVENT_CAMERA_DEVICE_REMOVED: + case SDL_EVENT_CAMERA_DEVICE_ADDED: + case SDL_EVENT_CAMERA_DEVICE_DENIED: + // which => 1 extra + count += 1; + break; + + case SDL_EVENT_CLIPBOARD_UPDATE: + // owner => 1 extra + count += 1; + break; + + /* Window events (just group them all together) */ + case SDL_EVENT_WINDOW_SHOWN: + case SDL_EVENT_WINDOW_HIDDEN: + case SDL_EVENT_WINDOW_EXPOSED: + case SDL_EVENT_WINDOW_MOVED: + case SDL_EVENT_WINDOW_RESIZED: + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + case SDL_EVENT_WINDOW_METAL_VIEW_RESIZED: + case SDL_EVENT_WINDOW_MINIMIZED: + case SDL_EVENT_WINDOW_MAXIMIZED: + case SDL_EVENT_WINDOW_RESTORED: + case SDL_EVENT_WINDOW_MOUSE_ENTER: + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + case SDL_EVENT_WINDOW_FOCUS_GAINED: + case SDL_EVENT_WINDOW_FOCUS_LOST: + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + case SDL_EVENT_WINDOW_HIT_TEST: + case SDL_EVENT_WINDOW_ICCPROF_CHANGED: + case SDL_EVENT_WINDOW_DISPLAY_CHANGED: + case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: + case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: + case SDL_EVENT_WINDOW_OCCLUDED: + case SDL_EVENT_WINDOW_ENTER_FULLSCREEN: + case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN: + case SDL_EVENT_WINDOW_DESTROYED: + case SDL_EVENT_WINDOW_HDR_STATE_CHANGED: + // which, data1, data2 => 3 extra + count += 3; + break; + + case SDL_EVENT_JOYSTICK_ADDED: + case SDL_EVENT_JOYSTICK_REMOVED: + case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE: + // which => 1 extra + count += 1; + break; + + case SDL_EVENT_JOYSTICK_AXIS_MOTION: + // which, axis, value => 3 extra + count += 3; + break; + + case SDL_EVENT_JOYSTICK_BALL_MOTION: + // which, ball, rel => 3 extra + count += 3; + break; + + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: + case SDL_EVENT_JOYSTICK_BUTTON_UP: + // which, button, down => 3 extra + count += 3; + break; + + case SDL_EVENT_GAMEPAD_ADDED: + case SDL_EVENT_GAMEPAD_REMOVED: + case SDL_EVENT_GAMEPAD_REMAPPED: + case SDL_EVENT_GAMEPAD_UPDATE_COMPLETE: + case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED: + // which => 1 extra + count += 1; + break; + + case SDL_EVENT_GAMEPAD_AXIS_MOTION: + // which, axis, value => 3 extra + count += 3; + break; + + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + case SDL_EVENT_GAMEPAD_BUTTON_UP: + // which, button, down => 3 extra + count += 3; + break; + + case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: + case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: + case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: + // which, touchpad, finger, pos, pressure => 5 extra + count += 5; + break; + + case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: + // which, sensor, sensor_timestamp => 3 extra + count += 3; + break; + + case SDL_EVENT_USER: + // cb => 1 extra + count += 1; + break; + } + + return count; +} + +static void event2wota_write(WotaBuffer *wb, const SDL_Event *e, int c) { + wota_write_record(wb, (unsigned long long)c); + wota_write_text(wb, "type"); + wota_write_text(wb, event_type_to_string(e->type)); + wota_write_text(wb, "timestamp"); + wota_write_number(wb, (double)e->common.timestamp); + switch(e->type) { + case SDL_EVENT_AUDIO_DEVICE_ADDED: + case SDL_EVENT_AUDIO_DEVICE_REMOVED: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->adevice.which); + wota_write_text(wb, "recording"); + wota_write_sym(wb, e->adevice.recording ? WOTA_TRUE : WOTA_FALSE); + break; + case SDL_EVENT_DISPLAY_ORIENTATION: + case SDL_EVENT_DISPLAY_ADDED: + case SDL_EVENT_DISPLAY_REMOVED: + case SDL_EVENT_DISPLAY_MOVED: + case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED: + case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED: + case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->display.displayID); + wota_write_text(wb, "data1"); + wota_write_number(wb, (double)e->display.data1); + wota_write_text(wb, "data2"); + wota_write_number(wb, (double)e->display.data2); + break; + case SDL_EVENT_MOUSE_MOTION: + wota_write_text(wb, "window"); + wota_write_number(wb, (double)e->motion.windowID); + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->motion.which); + wota_write_text(wb, "state"); + wota_write_number(wb, (double)e->motion.state); + wota_write_text(wb, "pos"); + wota_write_vec2(wb, (double)e->motion.x, (double)e->motion.y); + wota_write_text(wb, "d_pos"); + wota_write_vec2(wb, (double)e->motion.xrel, (double)e->motion.yrel); + break; + case SDL_EVENT_MOUSE_WHEEL: + wota_write_text(wb, "window"); + wota_write_number(wb, (double)e->wheel.windowID); + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->wheel.which); + wota_write_text(wb, "scroll"); + wota_write_vec2(wb, (double)e->wheel.x, (double)e->wheel.y); + wota_write_text(wb, "mouse"); + wota_write_vec2(wb, (double)e->wheel.mouse_x, (double)e->wheel.mouse_y); + break; + case SDL_EVENT_MOUSE_BUTTON_UP: + case SDL_EVENT_MOUSE_BUTTON_DOWN: + wota_write_text(wb, "window"); + wota_write_number(wb, (double)e->button.windowID); + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->button.which); + wota_write_text(wb, "down"); + wota_write_sym(wb, e->button.down ? WOTA_TRUE : WOTA_FALSE); + wota_write_text(wb, "button"); + wota_write_text(wb, mouse_button_to_string(e->button.button)); + wota_write_text(wb, "clicks"); + wota_write_number(wb, (double)e->button.clicks); + wota_write_text(wb, "mouse"); + wota_write_vec2(wb, (double)e->button.x, (double)e->button.y); + break; + case SDL_EVENT_SENSOR_UPDATE: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->sensor.which); + wota_write_text(wb, "sensor_timestamp"); + wota_write_number(wb, (double)e->sensor.sensor_timestamp); + break; + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + wota_write_text(wb, "window"); + wota_write_number(wb, (double)e->key.windowID); + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->key.which); + wota_write_text(wb, "down"); + wota_write_sym(wb, e->key.down ? WOTA_TRUE : WOTA_FALSE); + wota_write_text(wb, "repeat"); + wota_write_sym(wb, e->key.repeat ? WOTA_TRUE : WOTA_FALSE); + wota_write_text(wb, "key"); + wota_write_number(wb, (double)e->key.key); + wota_write_text(wb, "scancode"); + wota_write_number(wb, (double)e->key.scancode); + wota_write_text(wb, "mod"); + wota_write_number(wb, 0); + break; + case SDL_EVENT_FINGER_MOTION: + case SDL_EVENT_FINGER_DOWN: + case SDL_EVENT_FINGER_UP: + wota_write_text(wb, "touch"); + wota_write_number(wb, (double)e->tfinger.touchID); + wota_write_text(wb, "finger"); + wota_write_number(wb, (double)e->tfinger.fingerID); + wota_write_text(wb, "pos"); + wota_write_vec2(wb, (double)e->tfinger.x, (double)e->tfinger.y); + wota_write_text(wb, "d_pos"); + wota_write_vec2(wb, (double)e->tfinger.dx, (double)e->tfinger.dy); + wota_write_text(wb, "pressure"); + wota_write_number(wb, (double)e->tfinger.pressure); + wota_write_text(wb, "window"); + wota_write_number(wb, (double)e->key.windowID); + break; + case SDL_EVENT_DROP_BEGIN: + case SDL_EVENT_DROP_FILE: + case SDL_EVENT_DROP_TEXT: + case SDL_EVENT_DROP_COMPLETE: + case SDL_EVENT_DROP_POSITION: + wota_write_text(wb, "window"); + wota_write_number(wb, (double)e->drop.windowID); + wota_write_text(wb, "pos"); + wota_write_vec2(wb, (double)e->drop.x, (double)e->drop.y); + wota_write_text(wb, "data"); + wota_write_text(wb, e->drop.data ? e->drop.data : ""); + wota_write_text(wb, "source"); + wota_write_text(wb, e->drop.source ? e->drop.source : ""); + break; + case SDL_EVENT_TEXT_INPUT: + wota_write_text(wb, "window"); + wota_write_number(wb, (double)e->text.windowID); + wota_write_text(wb, "text"); + wota_write_text(wb, e->text.text); + wota_write_text(wb, "mod"); + wota_write_number(wb, 0); + break; + case SDL_EVENT_CAMERA_DEVICE_APPROVED: + case SDL_EVENT_CAMERA_DEVICE_REMOVED: + case SDL_EVENT_CAMERA_DEVICE_ADDED: + case SDL_EVENT_CAMERA_DEVICE_DENIED: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->cdevice.which); + break; + case SDL_EVENT_CLIPBOARD_UPDATE: + wota_write_text(wb, "owner"); + wota_write_sym(wb, e->clipboard.owner ? WOTA_TRUE : WOTA_FALSE); + break; + case SDL_EVENT_WINDOW_SHOWN: + case SDL_EVENT_WINDOW_HIDDEN: + case SDL_EVENT_WINDOW_EXPOSED: + case SDL_EVENT_WINDOW_MOVED: + case SDL_EVENT_WINDOW_RESIZED: + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + case SDL_EVENT_WINDOW_METAL_VIEW_RESIZED: + case SDL_EVENT_WINDOW_MINIMIZED: + case SDL_EVENT_WINDOW_MAXIMIZED: + case SDL_EVENT_WINDOW_RESTORED: + case SDL_EVENT_WINDOW_MOUSE_ENTER: + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + case SDL_EVENT_WINDOW_FOCUS_GAINED: + case SDL_EVENT_WINDOW_FOCUS_LOST: + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + case SDL_EVENT_WINDOW_HIT_TEST: + case SDL_EVENT_WINDOW_ICCPROF_CHANGED: + case SDL_EVENT_WINDOW_DISPLAY_CHANGED: + case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: + case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: + case SDL_EVENT_WINDOW_OCCLUDED: + case SDL_EVENT_WINDOW_ENTER_FULLSCREEN: + case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN: + case SDL_EVENT_WINDOW_DESTROYED: + case SDL_EVENT_WINDOW_HDR_STATE_CHANGED: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->window.windowID); + wota_write_text(wb, "data1"); + wota_write_number(wb, (double)e->window.data1); + wota_write_text(wb, "data2"); + wota_write_number(wb, (double)e->window.data2); + break; + case SDL_EVENT_JOYSTICK_ADDED: + case SDL_EVENT_JOYSTICK_REMOVED: + case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->jdevice.which); + break; + case SDL_EVENT_JOYSTICK_AXIS_MOTION: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->jaxis.which); + wota_write_text(wb, "axis"); + wota_write_number(wb, (double)e->jaxis.axis); + wota_write_text(wb, "value"); + wota_write_number(wb, (double)e->jaxis.value); + break; + case SDL_EVENT_JOYSTICK_BALL_MOTION: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->jball.which); + wota_write_text(wb, "ball"); + wota_write_number(wb, (double)e->jball.ball); + wota_write_text(wb, "rel"); + wota_write_vec2(wb, (double)e->jball.xrel, (double)e->jball.yrel); + break; + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: + case SDL_EVENT_JOYSTICK_BUTTON_UP: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->jbutton.which); + wota_write_text(wb, "button"); + wota_write_number(wb, (double)e->jbutton.button); + wota_write_text(wb, "down"); + wota_write_sym(wb, e->jbutton.down ? WOTA_TRUE : WOTA_FALSE); + break; + case SDL_EVENT_GAMEPAD_ADDED: + case SDL_EVENT_GAMEPAD_REMOVED: + case SDL_EVENT_GAMEPAD_REMAPPED: + case SDL_EVENT_GAMEPAD_UPDATE_COMPLETE: + case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->gdevice.which); + break; + case SDL_EVENT_GAMEPAD_AXIS_MOTION: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->gaxis.which); + wota_write_text(wb, "axis"); + wota_write_number(wb, (double)e->gaxis.axis); + wota_write_text(wb, "value"); + wota_write_number(wb, (double)e->gaxis.value); + break; + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + case SDL_EVENT_GAMEPAD_BUTTON_UP: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->gbutton.which); + wota_write_text(wb, "button"); + wota_write_number(wb, (double)e->gbutton.button); + wota_write_text(wb, "down"); + wota_write_sym(wb, e->gbutton.down ? WOTA_TRUE : WOTA_FALSE); + break; + case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: + case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: + case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->gtouchpad.which); + wota_write_text(wb, "touchpad"); + wota_write_number(wb, (double)e->gtouchpad.touchpad); + wota_write_text(wb, "finger"); + wota_write_number(wb, (double)e->gtouchpad.finger); + wota_write_text(wb, "pos"); + wota_write_vec2(wb, (double)e->gtouchpad.x, (double)e->gtouchpad.y); + wota_write_text(wb, "pressure"); + wota_write_number(wb, (double)e->gtouchpad.pressure); + break; + case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: + wota_write_text(wb, "which"); + wota_write_number(wb, (double)e->gsensor.which); + wota_write_text(wb, "sensor"); + wota_write_number(wb, (double)e->gsensor.sensor); + wota_write_text(wb, "sensor_timestamp"); + wota_write_number(wb, (double)e->gsensor.sensor_timestamp); + break; + case SDL_EVENT_USER: + wota_write_text(wb, "cb"); + wota_write_number(wb, (double)(uintptr_t)e->user.data1); + break; + } +} + +static WotaBuffer event2wota(const SDL_Event *event) { + WotaBuffer wb; + wota_buffer_init(&wb, 8); + int n = event2wota_count_props(event); + event2wota_write(&wb, event, n); + return wb; +} + int main(int argc, char **argv) { SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO); int cores = SDL_GetNumLogicalCPUCores(); - if (cores == 1) return printf("Platform has only one core!\n"), 1; + if (cores == 1) { + printf("Platform has only one core!\n"); + return 1; + } main_thread = SDL_GetCurrentThreadID(); - if (!main_thread) return printf("Platform does not support threads!\n"), 1; + if (!main_thread) { + printf("Platform does not support threads!\n"); + return 1; + } prosperon = argv[0]; PHYSFS_init(argv[0]); @@ -500,17 +1215,25 @@ int main(int argc, char **argv) PHYSFS_mount(base, "/", 0); int mounted = prosperon_mount_core(); if (!mounted) mounted = PHYSFS_mount("core.zip", NULL, 0); - if (!mounted) return printf("Could not mount core. Reason: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())), 1; + if (!mounted) { + printf("Could not mount core. Reason: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return 1; + } queue_mutex = SDL_CreateMutex(); queue_cond = SDL_CreateCondition(); + actors_mutex = SDL_CreateMutex(); + /* Create the initial actor from the main command line. */ char **margv = malloc(sizeof(char *) * argc); for (int i = 0; i < argc; i++) margv[i] = strdup(argv[i]); create_actor(argc, margv); - SDL_CreateThread(crank_actor, "actor_runner", NULL); + /* Start the thread that pumps ready actors. */ + SDL_Thread *thread = SDL_CreateThread(crank_actor, "actor_runner", NULL); + SDL_DetachThread(thread); + /* Set up signal and exit handlers. */ signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); signal(SIGSEGV, signal_handler); @@ -519,8 +1242,29 @@ int main(int argc, char **argv) SDL_Event event; while (SDL_WaitEvent(&event)) { - printf("Got event!\n"); - if (event.type == SDL_EVENT_QUIT) return 0; + WotaBuffer wb = event2wota(&event); + + // Broadcast this event to all actors + SDL_LockMutex(actors_mutex); + for (int i = 0; i < shlen(actors); i++) { + // Make a fresh copy so each actor owns and frees it independently + void *msg = malloc(wb.size); + memcpy(msg, wb.data, wb.size); + + send_message(actors[i].key, msg); + } + SDL_UnlockMutex(actors_mutex); + + wota_buffer_free(&wb); // free WotaBuffer } return 0; } + +int actor_exists(char *id) +{ + int idx = shgeti(actors,id); + if (idx == -1) + return 0; + else + return 1; +} diff --git a/source/prosperon.h b/source/prosperon.h index 15777ade..219697dc 100644 --- a/source/prosperon.h +++ b/source/prosperon.h @@ -31,11 +31,6 @@ typedef struct { char **argv; } cmdargs; -struct message { - void *data; - size_t size; -}; - typedef struct prosperon_rt { cmdargs cmd; JSContext *context; @@ -44,11 +39,15 @@ typedef struct prosperon_rt { JSValue on_exception; JSValue message_handle; JSValue unneeded; - struct { Uint32 key; JSValue value; } *timers; + + + /* The queue of JS callbacks triggered by timers or other signals */ JSValue *events; + ModuleEntry *module_registry; JSValue *js_swapchains; + /* Protects JSContext usage, actor->state, etc. */ SDL_Mutex *mutex; char *id; @@ -56,22 +55,26 @@ typedef struct prosperon_rt { double unneeded_secs; int idx_count; - struct message *messages; - SDL_Mutex *msg_mutex; + /* The “mailbox” for incoming messages + a dedicated lock for it: */ + void **messages; + SDL_Mutex *msg_mutex; /* For messages queue only */ + + /* CHANGED FOR EVENTS: a separate lock for the actor->events queue */ + struct { Uint32 key; JSValue value; } *timers; + SDL_Mutex *evt_mutex; /* For events queue only, and timer hash */ + int state; Uint32 ar; + int need_stop; } prosperon_rt; extern SDL_ThreadID main_thread; extern SDL_TLSID prosperon_id; -prosperon_rt *create_actor(int argc, char **argv); -prosperon_rt *get_actor(char *id); +void create_actor(int argc, char **argv); char *register_actor(char *id, prosperon_rt *actor); -void actor_signal(prosperon_rt *actor); -void send_message(prosperon_rt *actor, struct message msg); -void actor_turn(prosperon_rt *actor); void actor_free(prosperon_rt *actor); +char *send_message(char *id, void *msg); Uint32 actor_timer_cb(prosperon_rt *actor, SDL_TimerID id, Uint32 interval); JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv); JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *argv); @@ -79,5 +82,8 @@ void script_startup(prosperon_rt *rt); void script_evalf(JSContext *js, const char *format, ...); JSValue script_eval(JSContext *js, const char *file, const char *script); void uncaught_exception(JSContext *js, JSValue v); +int actor_exists(char *id); + +int prosperon_mount_core(void); #endif diff --git a/source/qjs_dmon.h b/source/qjs_dmon.h index 27b0e0b4..96b617d2 100644 --- a/source/qjs_dmon.h +++ b/source/qjs_dmon.h @@ -5,4 +5,4 @@ JSValue js_dmon_use(JSContext *js); -#endif \ No newline at end of file +#endif diff --git a/source/qjs_enet.c b/source/qjs_enet.c index 19042dd8..c0e920e4 100644 --- a/source/qjs_enet.c +++ b/source/qjs_enet.c @@ -370,9 +370,8 @@ static JSClassDef enet_peer_class = { JSValue js_enet_resolve_hostname(JSContext *js, JSValue self, int argc, JSValue *argv) { + // TODO: implement char *hostname = JS_ToCString(js, argv[0]); - ENetAddress addr; - // Note: This function seems incomplete in the original - should it return something? JS_FreeCString(js, hostname); return JS_UNDEFINED; } diff --git a/source/qjs_imgui.cpp b/source/qjs_imgui.cpp index a336a3d6..f84e9adb 100644 --- a/source/qjs_imgui.cpp +++ b/source/qjs_imgui.cpp @@ -278,7 +278,7 @@ JSC_SSCALL(imgui_textbox, ret = JS_DupValue(js,argv[1]); ) -JSC_SCALL(imgui_text, ImGui::Text(str) ) +JSC_SCALL(imgui_text, ImGui::Text("%s", str) ) JSC_SCALL(imgui_button, if (ImGui::Button(str)) diff --git a/source/qjs_tracy.c b/source/qjs_tracy.c index dc31c293..d96d3835 100644 --- a/source/qjs_tracy.c +++ b/source/qjs_tracy.c @@ -247,7 +247,7 @@ static JSValue js_tracy_image(JSContext *js, JSValue self, int argc, JSValue *ar return JS_UNDEFINED; } -#elifdef SOKOL_D3D11 +#elif defined(SOKOL_D3D11) #include static int query_count = 64*1024; diff --git a/source/qjs_wota.c b/source/qjs_wota.c index ea8fb132..28d7a44b 100644 --- a/source/qjs_wota.c +++ b/source/qjs_wota.c @@ -1,7 +1,6 @@ #include "quickjs.h" - -#define WOTA_IMPLEMENTATION #include "wota.h" +#include typedef struct WotaEncodeContext { JSContext *ctx; @@ -89,7 +88,12 @@ static void encode_object_properties(WotaEncodeContext *enc, JSValueConst val, J static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValueConst key) { JSContext *ctx = enc->ctx; - JSValue replaced = apply_replacer(enc, holder, key, val); + JSValue replaced; + if (!JS_IsUndefined(enc->replacer) && !JS_IsUndefined(key)) + replaced = apply_replacer(enc, holder, key, val); + else + replaced = JS_DupValue(enc->ctx, val); + int tag = JS_VALUE_GET_TAG(replaced); switch (tag) { case JS_TAG_INT: { @@ -177,12 +181,8 @@ static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueC JS_FreeValue(ctx, replaced); } -static char *decode_wota_value(JSContext *ctx, char *data_ptr, char *end_ptr, JSValue *out_val, JSValue holder, JSValue key, JSValue reviver) +static char *decode_wota_value(JSContext *ctx, char *data_ptr, JSValue *out_val, JSValue holder, JSValue key, JSValue reviver) { - if ((end_ptr - data_ptr) < 8) { - *out_val = JS_UNDEFINED; - return data_ptr; - } uint64_t first_word = *(uint64_t *)data_ptr; int type = (int)(first_word & 0xffU); switch (type) { @@ -228,7 +228,7 @@ static char *decode_wota_value(JSContext *ctx, char *data_ptr, char *end_ptr, JS JSValue arr = JS_NewArray(ctx); for (long long i = 0; i < c; i++) { JSValue elem_val = JS_UNDEFINED; - data_ptr = decode_wota_value(ctx, data_ptr, end_ptr, &elem_val, arr, JS_NewInt32(ctx, i), reviver); + data_ptr = decode_wota_value(ctx, data_ptr, &elem_val, arr, JS_NewInt32(ctx, i), reviver); JS_SetPropertyUint32(ctx, arr, i, elem_val); } *out_val = arr; @@ -243,7 +243,7 @@ static char *decode_wota_value(JSContext *ctx, char *data_ptr, char *end_ptr, JS data_ptr = wota_read_text(&tkey, data_ptr); JSValue prop_key = tkey ? JS_NewString(ctx, tkey) : JS_UNDEFINED; JSValue sub_val = JS_UNDEFINED; - data_ptr = decode_wota_value(ctx, data_ptr, end_ptr, &sub_val, obj, prop_key, reviver); + data_ptr = decode_wota_value(ctx, data_ptr, &sub_val, obj, prop_key, reviver); if (tkey) JS_SetPropertyStr(ctx, obj, tkey, sub_val); else JS_FreeValue(ctx, sub_val); JS_FreeValue(ctx, prop_key); @@ -271,6 +271,36 @@ static char *decode_wota_value(JSContext *ctx, char *data_ptr, char *end_ptr, JS return data_ptr; } +void *value2wota(JSContext *ctx, JSValue v, JSValue replacer) +{ + WotaEncodeContext enc_s, *enc = &enc_s; + + enc->ctx = ctx; + enc->visited_stack = JS_NewArray(ctx); + enc->cycle = 0; + enc->replacer = replacer; + wota_buffer_init(&enc->wb, 16); + wota_encode_value(enc, v, JS_UNDEFINED, JS_UNDEFINED); + if (enc->cycle) { + JS_FreeValue(ctx, enc->visited_stack); + wota_buffer_free(&enc->wb); + return NULL; + } + JS_FreeValue(ctx, enc->visited_stack); + size_t total_bytes = enc->wb.size * sizeof(uint64_t); + void *wota = realloc(enc->wb.data, total_bytes); + return wota; +} + +JSValue wota2value(JSContext *ctx, void *wota) +{ + JSValue result = JS_UNDEFINED; + JSValue holder = JS_NewObject(ctx); + decode_wota_value(ctx, wota, &result, holder, JS_UNDEFINED, JS_UNDEFINED); + JS_FreeValue(ctx, holder); + return result; +} + static JSValue js_wota_encode(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { if (argc < 1) return JS_ThrowTypeError(ctx, "wota.encode requires at least 1 argument"); @@ -280,7 +310,7 @@ static JSValue js_wota_encode(JSContext *ctx, JSValueConst this_val, int argc, J enc->cycle = 0; enc->replacer = (argc > 1 && JS_IsFunction(ctx, argv[1])) ? argv[1] : JS_UNDEFINED; wota_buffer_init(&enc->wb, 16); - wota_encode_value(enc, argv[0], JS_UNDEFINED, JS_NewString(ctx, "")); + wota_encode_value(enc, argv[0], JS_UNDEFINED, JS_UNDEFINED); if (enc->cycle) { JS_FreeValue(ctx, enc->visited_stack); wota_buffer_free(&enc->wb); @@ -301,10 +331,9 @@ static JSValue js_wota_decode(JSContext *ctx, JSValueConst this_val, int argc, J if (!buf) return JS_UNDEFINED; JSValue reviver = (argc > 1 && JS_IsFunction(ctx, argv[1])) ? argv[1] : JS_UNDEFINED; char *data_ptr = (char *)buf; - char *end_ptr = data_ptr + len; JSValue result = JS_UNDEFINED; JSValue holder = JS_NewObject(ctx); - decode_wota_value(ctx, data_ptr, end_ptr, &result, holder, JS_NewString(ctx, ""), reviver); + decode_wota_value(ctx, data_ptr, &result, holder, JS_NewString(ctx, ""), reviver); JS_FreeValue(ctx, holder); return result; } diff --git a/source/qjs_wota.h b/source/qjs_wota.h index 265675b5..de4f0458 100644 --- a/source/qjs_wota.h +++ b/source/qjs_wota.h @@ -2,6 +2,7 @@ #define QJS_WOTA_H #include +#include "wota.h" JSValue js_wota_use(JSContext*); diff --git a/source/render.h b/source/render.h index 7e73e0b2..e4db3fa7 100644 --- a/source/render.h +++ b/source/render.h @@ -19,8 +19,6 @@ typedef struct viewstate { extern viewstate globalview; -void render_init(); - void capture_screen(int x, int y, int w, int h, const char *path); void gif_rec_start(int w, int h, int cpf, int bitdepth); diff --git a/source/sprite.c b/source/sprite.c index ad3ea069..92dcbece 100644 --- a/source/sprite.c +++ b/source/sprite.c @@ -8,7 +8,7 @@ static sprite model = { .color = {1, 1, 1, 1} }; -sprite *make_sprite() +sprite *make_sprite(void) { sprite *sprite = malloc(sizeof(*sprite)); *sprite = model; diff --git a/source/sprite.h b/source/sprite.h index c5eb75f9..f2e9c908 100644 --- a/source/sprite.h +++ b/source/sprite.h @@ -16,7 +16,7 @@ struct sprite{ typedef struct sprite sprite; -sprite *make_sprite(); +sprite *make_sprite(void); void sprite_free(JSRuntime *rt, sprite *sprite); #endif diff --git a/source/thirdparty/imgui/GraphEditor.cpp b/source/thirdparty/imgui/GraphEditor.cpp index f75fdd9e..f34d2404 100644 --- a/source/thirdparty/imgui/GraphEditor.cpp +++ b/source/thirdparty/imgui/GraphEditor.cpp @@ -553,8 +553,6 @@ static bool DrawNode(ImDrawList* drawList, // test nested IO drawList->ChannelsSetCurrent(1); // Background - const size_t InputsCount = nodeTemplate.mInputCount; - const size_t OutputsCount = nodeTemplate.mOutputCount; /* for (int i = 0; i < 2; i++) @@ -647,7 +645,6 @@ static bool DrawNode(ImDrawList* drawList, drawList->AddLine(progressLineA, progressLineB, 0xFF400000, 3.f); drawList->AddLine(progressLineA, ImLerp(progressLineA, progressLineB, progress), 0xFFFF0000, 3.f); }*/ - ImVec2 imgPosMax = imgPos + ImVec2(imgSizeComp, imgSizeComp); //ImVec2 imageSize = delegate->GetEvaluationSize(nodeIndex); /*float imageRatio = 1.f; @@ -828,7 +825,6 @@ void Show(Delegate& delegate, const Options& options, ViewState& viewState, bool const ImVec2 windowPos = ImGui::GetCursorScreenPos(); const ImVec2 canvasSize = ImGui::GetContentRegionAvail(); - const ImVec2 scrollRegionLocalPos(0, 0); ImRect regionRect(windowPos, windowPos + canvasSize); diff --git a/source/thirdparty/par/par_shapes.h b/source/thirdparty/par/par_shapes.h index beae160a..1d33530b 100644 --- a/source/thirdparty/par/par_shapes.h +++ b/source/thirdparty/par/par_shapes.h @@ -101,16 +101,16 @@ par_shapes_mesh* par_shapes_create_parametric(par_shapes_fn, int slices, // Generate points for a 20-sided polyhedron that fits in the unit sphere. // Texture coordinates and normals are not generated. -par_shapes_mesh* par_shapes_create_icosahedron(); +par_shapes_mesh* par_shapes_create_icosahedron(void); // Generate points for a 12-sided polyhedron that fits in the unit sphere. // Again, texture coordinates and normals are not generated. -par_shapes_mesh* par_shapes_create_dodecahedron(); +par_shapes_mesh* par_shapes_create_dodecahedron(void); // More platonic solids. -par_shapes_mesh* par_shapes_create_octahedron(); -par_shapes_mesh* par_shapes_create_tetrahedron(); -par_shapes_mesh* par_shapes_create_cube(); +par_shapes_mesh* par_shapes_create_octahedron(void); +par_shapes_mesh* par_shapes_create_tetrahedron(void); +par_shapes_mesh* par_shapes_create_cube(void); // Generate an orientable disk shape in 3-space. Does not include normals or // texture coordinates. @@ -118,7 +118,7 @@ par_shapes_mesh* par_shapes_create_disk(float radius, int slices, float const* center, float const* normal); // Create an empty shape. Useful for building scenes with merge_and_free. -par_shapes_mesh* par_shapes_create_empty(); +par_shapes_mesh* par_shapes_create_empty(void); // Generate a rock shape that sits on the Y=0 plane, and sinks into it a bit. // This includes smooth normals but no texture coordinates. Each subdivision @@ -751,7 +751,7 @@ par_shapes_mesh* par_shapes_create_disk(float radius, int slices, return mesh; } -par_shapes_mesh* par_shapes_create_empty() +par_shapes_mesh* par_shapes_create_empty(void) { return PAR_CALLOC(par_shapes_mesh, 1); } @@ -875,7 +875,7 @@ void par_shapes_invert(par_shapes_mesh* m, int face, int nfaces) } } -par_shapes_mesh* par_shapes_create_icosahedron() +par_shapes_mesh* par_shapes_create_icosahedron(void) { static float verts[] = { 0.000, 0.000, 1.000, @@ -923,7 +923,7 @@ par_shapes_mesh* par_shapes_create_icosahedron() return mesh; } -par_shapes_mesh* par_shapes_create_dodecahedron() +par_shapes_mesh* par_shapes_create_dodecahedron(void) { static float verts[20 * 3] = { 0.607, 0.000, 0.795, @@ -985,7 +985,7 @@ par_shapes_mesh* par_shapes_create_dodecahedron() return mesh; } -par_shapes_mesh* par_shapes_create_octahedron() +par_shapes_mesh* par_shapes_create_octahedron(void) { static float verts[6 * 3] = { 0.000, 0.000, 1.000, @@ -1023,7 +1023,7 @@ par_shapes_mesh* par_shapes_create_octahedron() return mesh; } -par_shapes_mesh* par_shapes_create_tetrahedron() +par_shapes_mesh* par_shapes_create_tetrahedron(void) { static float verts[4 * 3] = { 0.000, 1.333, 0, @@ -1055,7 +1055,7 @@ par_shapes_mesh* par_shapes_create_tetrahedron() return mesh; } -par_shapes_mesh* par_shapes_create_cube() +par_shapes_mesh* par_shapes_create_cube(void) { static float verts[8 * 3] = { 0, 0, 0, // 0 @@ -1141,7 +1141,7 @@ static par_shapes__rule* par_shapes__pick_rule(const char* name, return rule; } -static par_shapes_mesh* par_shapes__create_turtle() +static par_shapes_mesh* par_shapes__create_turtle(void) { const float xaxis[] = {1, 0, 0}; const float yaxis[] = {0, 1, 0}; diff --git a/source/transform.h b/source/transform.h index 071fad02..d90c8bb9 100644 --- a/source/transform.h +++ b/source/transform.h @@ -23,7 +23,7 @@ typedef struct transform { void clean_all(JSContext *js); -transform *make_transform(); +transform *make_transform(void); void transform_free(JSRuntime *rt,transform *t); void transform_apply(transform *t); diff --git a/source/wota.h b/source/wota.h index b01844ad..429a93e2 100644 --- a/source/wota.h +++ b/source/wota.h @@ -309,8 +309,6 @@ char *wota_read_blob(long long *byte_len, char **blob, char *wota) memset(*blob, 0, (size_t)(*byte_len)); uint64_t *data_words = p + 1; - long long bits_remaining = (long long)nbits; - size_t byte_i = 0; int bit_in_byte = 0; /* If nbits is multiple of 8, we can do a bulk copy in 64-bit chunks, then do leftover if any. */ diff --git a/tests/delay.js b/tests/delay.js new file mode 100644 index 00000000..893570bb --- /dev/null +++ b/tests/delay.js @@ -0,0 +1,12 @@ +var count = 0 +function loop() +{ + count++; + console.log(`loop ${count}`); + if (count < 60) + $_.delay(loop, 0.01); + else + $_.stop() +} + +$_.delay(loop,0.01)