From c887bcf7b91d844373e865aa5e657b10294d7254 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 3 Jun 2025 16:13:54 -0500 Subject: [PATCH] no longer need to send ids to window renderer; per actor config --- .cell/cell.toml | 24 ++--- prosperon/examples/chess/main.ce | 3 +- prosperon/graphics.cm | 3 - prosperon/main.ce | 72 +++++-------- prosperon/resources.cm | 2 + prosperon/{_sdl_video.ce => sdl_video.ce} | 117 +++++----------------- scripts/engine.cm | 25 +++++ scripts/help.ce | 1 + source/cell.c | 12 +-- source/cell.h | 4 +- source/qjs_actor.c | 2 +- source/qjs_sdl_video.c | 60 +++-------- 12 files changed, 112 insertions(+), 213 deletions(-) rename prosperon/{_sdl_video.ce => sdl_video.ce} (87%) diff --git a/.cell/cell.toml b/.cell/cell.toml index e1581d24..6190029a 100644 --- a/.cell/cell.toml +++ b/.cell/cell.toml @@ -1,21 +1,13 @@ +sdl_video = "main" [dependencies] extramath = "https://gitea.pockle.world/john/extramath@master" - [system] -# seconds before idle actor reclamation ar_timer = 60 - -# MB of memory an actor can use; 0 for unbounded -actor_memory = 0 - -# seconds per net service pull -net_service = 0.1 - -# seconds to hold callback for reply messages; 0 for unbounded -reply_timeout = 60 - -# max number of simultaneous actors -actor_max = 10_000 - -# MB of memory each actor's stack can grow to +actor_memory = 0 +net_service = 0.1 +reply_timeout = 60 +actor_max = "10_000" stack_max = 0 +[actors] +[actors.prosperon/sdl_video] +main = true \ No newline at end of file diff --git a/prosperon/examples/chess/main.ce b/prosperon/examples/chess/main.ce index cdb644c9..68862442 100644 --- a/prosperon/examples/chess/main.ce +++ b/prosperon/examples/chess/main.ce @@ -5,6 +5,8 @@ var draw2d = use('prosperon/draw2d') var blob = use('blob') +log.console("HERE") + /*──── import our pieces + systems ───────────────────────────────────*/ var Grid = use('grid'); // your new ctor var MovementSystem = use('movement').MovementSystem; @@ -277,7 +279,6 @@ function draw() draw2d.clear() drawBoard() drawPieces() - draw2d.text("HELL", {x: 100, y: 100}, 'fonts/c64.ttf', 16, [1,1,1,1]) return draw2d.get_commands() } diff --git a/prosperon/graphics.cm b/prosperon/graphics.cm index fa18e88b..a49ed2e7 100644 --- a/prosperon/graphics.cm +++ b/prosperon/graphics.cm @@ -7,7 +7,6 @@ rectangle packing, etc. ` var renderer_actor = arg[0] -var renderer_id = arg[1] var io = use('io') var time = use('time') @@ -43,7 +42,6 @@ Object.defineProperties(graphics.Image.prototype, { // Send message to load texture send(renderer_actor, { kind: "renderer", - id: renderer_id, op: "loadTexture", data: this[CPU] }, function(response) { @@ -349,7 +347,6 @@ graphics.get_font = function get_font(path, size) { // Load font texture via renderer actor (async) send(renderer_actor, { kind: "renderer", - id: renderer_id, op: "loadTexture", data: font.surface }, function(response) { diff --git a/prosperon/main.ce b/prosperon/main.ce index 2ab6aebf..8d99e402 100644 --- a/prosperon/main.ce +++ b/prosperon/main.ce @@ -2,7 +2,28 @@ var os = use('os'); var io = use('io'); var transform = use('transform'); var rasterize = use('rasterize'); -var video_actor = use('sdl_video') + +var game = args[0] + +var video + +$_.start(e => { + if (e.type !== 'greet') return + video = e.actor + graphics = use('graphics', video) + send(video, {kind:"window", op:"makeRenderer"}, e => { + $_.start(e => { + if (gameactor) return + gameactor = e.actor + loop() + }, args[0]) + }) +}, 'prosperon/sdl_video', { + title: "Prosperon", + width:500, + height:500 +}) + var input = use('input') input.watch($_) @@ -77,49 +98,9 @@ var cammy = util.camera_globals(camera) var graphics -var window -var render - var gameactor -var game = args[0] -$_.start(e => { - if (gameactor) return - gameactor = e.actor - loop() -}, args[0]) - -send(video_actor, { - kind: "window", - op:"create", - data: { - title: "Moth Test", - width: 500, - height: 500 - } -}, e => { - if (e.error) { - log.error(e.error) - os.exit(1) - } - - window = e.id - - send(video_actor,{ - kind:"window", - op:"makeRenderer", - id:window - }, e => { - if (e.error) { - log.error(e.error) - os.exit(1) - } - - render = e.id - graphics = use('graphics', video_actor, e.id) - }) -}) var last = os.now() @@ -262,8 +243,9 @@ function translate_draw_commands(commands) { return renderer_commands } -function loop() +function loop(time) { + $_.delay(loop, 1/60) os.frame() var now = os.now() var dt = now - last @@ -295,9 +277,8 @@ function loop() op: "present" }) - send(video_actor, { + send(video, { kind: "renderer", - id: render, op: "batch", data: batch_commands }, _ => { @@ -320,14 +301,13 @@ function loop() // Calculate average FPS var avg_fps = fps_sum / fps_samples.length } - - loop() }) }) }) } $_.receiver(e => { + log.console(json.encode(e)) if (e.type === 'quit') $_.stop() diff --git a/prosperon/resources.cm b/prosperon/resources.cm index 70944ec2..ef8795c9 100644 --- a/prosperon/resources.cm +++ b/prosperon/resources.cm @@ -1,3 +1,5 @@ +var io = use('io') + Object.defineProperty(Function.prototype, "hashify", { value: function () { var hash = new Map() diff --git a/prosperon/_sdl_video.ce b/prosperon/sdl_video.ce similarity index 87% rename from prosperon/_sdl_video.ce rename to prosperon/sdl_video.ce index 6738667b..5b7fa297 100644 --- a/prosperon/_sdl_video.ce +++ b/prosperon/sdl_video.ce @@ -1,8 +1,12 @@ +var video = use('sdl_video'); + // SDL Video Actor // This actor runs on the main thread and handles all SDL video operations -var surface = use('surface') +var surface = use('surface'); + +var ren +var win -// Default window configuration - documents all available window options var default_window = { // Basic properties title: "Prosperon Window", @@ -53,10 +57,11 @@ var default_window = { textInput: true, // Enable text input on creation }; +var config = Object.assign({}, default_window, arg[0] || {}); +win = new video.window(config); + // Resource tracking var resources = { - window: {}, - renderer: {}, texture: {}, surface: {}, cursor: {} @@ -115,26 +120,10 @@ $_.receiver(function(msg) { // Window operations function handle_window(msg) { - // Special case: create doesn't need an existing window - if (msg.op === 'create') { - var config = Object.assign({}, default_window, msg.data || {}); - var id = allocate_id(); - var window = new prosperon.endowments.window(config); - resources.window[id] = window; - return {id: id, data: {size: window.size}}; - } - - // All other operations require a valid window ID - if (!msg.id || !resources.window[msg.id]) { - return {error: "Invalid window id: " + msg.id}; - } - - var win = resources.window[msg.id]; - switch (msg.op) { case 'destroy': win.destroy(); - delete resources.window[msg.id]; + win = undefined return {success: true}; case 'show': @@ -211,10 +200,10 @@ function handle_window(msg) { return {success: true}; case 'makeRenderer': - var renderer = win.make_renderer(); - var renderer_id = allocate_id(); - resources.renderer[renderer_id] = renderer; - return {id: renderer_id}; + if (ren) + return {reason: "Already made a renderer"} + ren = win.make_renderer() + return {success:true}; default: return {error: "Unknown window operation: " + msg.op}; @@ -223,33 +212,11 @@ function handle_window(msg) { // Renderer operations function handle_renderer(msg) { - // Special case: createWindowAndRenderer creates both - if (msg.op === 'createWindowAndRenderer') { - var data = msg.data || {}; - var result = prosperon.endowments.createWindowAndRenderer( - data.title || "Prosperon Window", - data.width || 640, - data.height || 480, - data.flags || 0 - ); - var win_id = allocate_id(); - var ren_id = allocate_id(); - resources.window[win_id] = result.window; - resources.renderer[ren_id] = result.renderer; - return {window_id: win_id, renderer_id: ren_id}; - } - - // All other operations require a valid renderer ID - if (!msg.id || !resources.renderer[msg.id]) { - return {error: "Invalid renderer id: " + msg.id}; - } - - var ren = resources.renderer[msg.id]; - + if (!ren) return{reason:'no renderer!'} + switch (msg.op) { case 'destroy': - delete resources.renderer[msg.id]; - // Renderer is automatically destroyed when all references are gone + ren = undefined return {success: true}; case 'clear': @@ -268,22 +235,6 @@ function handle_renderer(msg) { var prop = msg.data ? msg.data.property : null; if (!prop) return {error: "Missing property name"}; - // Handle special cases - if (prop === 'window') { - var win = ren.window; - if (!win) return {data: null}; - // Find window ID - for (var id in resources.window) { - if (resources.window[id] === win) { - return {data: id}; - } - } - // Window not tracked, add it - var win_id = allocate_id(); - resources.window[win_id] = win; - return {data: win_id}; - } - // Handle special getters that might return objects if (prop === 'drawColor') { var color = ren[prop]; @@ -300,6 +251,8 @@ function handle_renderer(msg) { var value = msg.value if (!prop) return {error: "Missing property name"}; + if (!value) return {error: "No value to set"} + // Validate property is settable var readonly = ['window', 'name', 'outputSize', 'currentOutputSize', 'logicalPresentationRect', 'safeArea']; if (readonly.indexOf(prop) !== -1) { @@ -462,29 +415,11 @@ function handle_renderer(msg) { return {data: ren.coordsToWindow(msg.data.pos)}; case 'batch': - // Execute a batch of operations if (!msg.data || !Array.isArray(msg.data)) return {error: "Missing or invalid data array"}; var results = []; for (var i = 0; i < msg.data.length; i++) { - var cmd = msg.data[i]; - if (!cmd.op) { - results.push({error: "Command at index " + i + " missing op"}); - continue; - } - - // Create a temporary message object for the command - var temp_msg = { - kind: 'renderer', - id: msg.id, - op: cmd.op, - prop: cmd.prop, - value: cmd.value, - data: cmd.data - }; - - // Recursively call handle_renderer for each command - var result = handle_renderer(temp_msg); + var result = handle_renderer(msg.data[i]); results.push(result); } @@ -510,11 +445,11 @@ function handle_texture(msg) { if (msg.data.surface_id) { var surf = resources.surface[msg.data.surface_id]; if (!surf) return {error: "Invalid surface id"}; - tex = new prosperon.endowments.texture(renderer, surf); + tex = new video.texture(renderer, surf); } // Create from properties else if (msg.data.width && msg.data.height) { - tex = new prosperon.endowments.texture(renderer, { + tex = new video.texture(renderer, { width: msg.data.width, height: msg.data.height, format: msg.data.format || 'rgba8888', @@ -611,7 +546,7 @@ function handle_cursor(msg) { var surf = new surface(msg.data) var hotspot = msg.data.hotspot || [0, 0]; - var cursor = prosperon.endowments.createCursor(surf, hotspot); + var cursor = video.createCursor(surf, hotspot); var cursor_id = allocate_id(); resources.cursor[cursor_id] = cursor; @@ -622,7 +557,7 @@ function handle_cursor(msg) { if (msg.id && resources.cursor[msg.id]) { cursor = resources.cursor[msg.id]; } - prosperon.endowments.setCursor(cursor); + video.setCursor(cursor); return {success: true}; case 'destroy': @@ -642,7 +577,7 @@ prosperon.endowments = prosperon.endowments || {}; // Mouse operations function handle_mouse(msg) { - var mouse = prosperon.endowments.mouse; + var mouse = video.mouse; switch (msg.op) { case 'show': @@ -736,7 +671,7 @@ function handle_mouse(msg) { // Keyboard operations function handle_keyboard(msg) { - var keyboard = prosperon.endowments.keyboard; + var keyboard = video.keyboard; switch (msg.op) { case 'get_state': diff --git a/scripts/engine.cm b/scripts/engine.cm index f3781a8e..256e5acf 100644 --- a/scripts/engine.cm +++ b/scripts/engine.cm @@ -256,6 +256,24 @@ globalThis.json = use('json') globalThis.text = use('text') var time = use('time') +// Load actor-specific configuration +function load_actor_config(program) { + // Extract actor name from program path + // e.g., "prosperon/_sdl_video" or "extramath/spline" + var actor_name = program + if (program.includes('/')) { + actor_name = program + } + + if (config.actors && config.actors[actor_name]) { + // Merge actor config into cell.args + for (var key in config.actors[actor_name]) { + log.console(`setting ${key}`) + cell.args[key] = config.actors[actor_name][key] + } + } +} + var blob = use('blob') function deepFreeze(object) { @@ -627,6 +645,10 @@ function turn(msg) send_messages() } +load_actor_config(cell.args.program) + +log.console(`actor ${cell.args.program} is ${cell.args.main}`) + actor_mod.register_actor(cell.id, turn, cell.args.main, config.system.ar_timer) if (config.system.actor_memory) @@ -751,6 +773,9 @@ function enet_check() // Finally, run the program actor_mod.setname(cell.args.program) +// Load actor-specific configuration before running + + var prog = null var progPath = cell.args.program diff --git a/scripts/help.ce b/scripts/help.ce index 67845cc3..59038052 100644 --- a/scripts/help.ce +++ b/scripts/help.ce @@ -36,6 +36,7 @@ if (io.exists(cell_man)) { log.console(" vendor Copy all dependencies locally") log.console(" build Compile all modules to bytecode") log.console(" patch Create a patch for a module") + log.console(" config Manage system and actor configurations") log.console(" help Show this help message") log.console("") log.console("Run 'cell help ' for more information on a command.") diff --git a/source/cell.c b/source/cell.c index c19e51a8..8502ef67 100644 --- a/source/cell.c +++ b/source/cell.c @@ -323,7 +323,7 @@ int prosperon_mount_core(void) return ret; } -cell_rt *create_actor(void *wota, void (*hook)(JSContext*)) +cell_rt *create_actor(void *wota) { cell_rt *actor = calloc(sizeof(*actor), 1); actor->init_wota = wota; @@ -341,7 +341,7 @@ cell_rt *create_actor(void *wota, void (*hook)(JSContext*)) /* Lock actor->mutex while initializing JS runtime. */ SDL_LockMutex(actor->mutex); - script_startup(actor, hook); + script_startup(actor); SDL_UnlockMutex(actor->mutex); set_actor_state(actor); @@ -702,7 +702,7 @@ static int actor_interrupt_cb(JSRuntime *rt, cell_rt *crt) return 0; } -void script_startup(cell_rt *prt, void (*hook)(JSContext*)) +void script_startup(cell_rt *prt) { JSRuntime *rt; #ifdef TRACY_ENABLE @@ -747,10 +747,6 @@ void script_startup(cell_rt *prt, void (*hook)(JSContext*)) PHYSFS_close(eng); data[stat.filesize] = 0; - /* Call hook function if provided before evaluating engine */ - if (hook) - hook(js); - /* 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); @@ -1582,7 +1578,7 @@ int main(int argc, char **argv) wota_write_array(&startwota, actor_argc - 1); for (int i = 1; i < actor_argc; i++) wota_write_text(&startwota, actor_argv[i]); - root_cell = create_actor(startwota.data, NULL); + root_cell = create_actor(startwota.data); /* Launch runner threads */ for (int i = 0; i < cores; i++) { diff --git a/source/cell.h b/source/cell.h index 038c9e14..e2968fee 100644 --- a/source/cell.h +++ b/source/cell.h @@ -79,7 +79,7 @@ extern SDL_TLSID prosperon_id; extern cell_rt *root_cell; // first actor in the system -cell_rt *create_actor(void *wota, void (*hook)(JSContext*)); +cell_rt *create_actor(void *wota); const char *register_actor(const char *id, cell_rt *actor, int mainthread, double ar); void actor_disrupt(cell_rt *actor); @@ -87,7 +87,7 @@ const char *send_message(const char *id, void *msg); Uint32 actor_timer_cb(cell_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); -void script_startup(cell_rt *rt, void (*hook)(JSContext*)); +void script_startup(cell_rt *rt); void script_evalf(JSContext *js, const char *format, ...); JSValue script_eval(JSContext *js, const char *file, const char *script); int uncaught_exception(JSContext *js, JSValue v); diff --git a/source/qjs_actor.c b/source/qjs_actor.c index 826ae657..4d564c67 100644 --- a/source/qjs_actor.c +++ b/source/qjs_actor.c @@ -75,7 +75,7 @@ JSValue actor2js(JSContext *js, cell_rt *actor) JSC_CCALL(os_createactor, void *startup = value2wota(js, argv[0], JS_UNDEFINED); - create_actor(startup, NULL); + create_actor(startup); ) JSC_CCALL(os_mailbox_push, diff --git a/source/qjs_sdl_video.c b/source/qjs_sdl_video.c index 8c325b1c..1f7bf330 100644 --- a/source/qjs_sdl_video.c +++ b/source/qjs_sdl_video.c @@ -1744,12 +1744,13 @@ JSC_CCALL(sdl_createWindowAndRenderer, return ret; ) -// Hook function to set up endowments for the video actor -static void video_actor_hook(JSContext *js) { - // Get prosperon object - JSValue global = JS_GetGlobalObject(js); - JSValue prosperon = JS_GetPropertyStr(js, global, "prosperon"); - JS_FreeValue(js,global); +#include "qjs_wota.h" + +JSValue js_sdl_video_use(JSContext *js) { + if (!SDL_Init(SDL_INIT_VIDEO)) + return JS_ThrowInternalError(js, "Unable to initialize video subsystem: %s", SDL_GetError()); + + JSValue ret = JS_NewObject(js); // Initialize classes QJSCLASSPREP_FUNCS(SDL_Window) @@ -1769,50 +1770,19 @@ static void video_actor_hook(JSContext *js) { // Set prototype on constructor JS_SetConstructor(js, texture_ctor, SDL_Texture_proto); - // Get or create endowments object - JSValue endowments = JS_GetPropertyStr(js, prosperon, "endowments"); - if (JS_IsUndefined(endowments)) { - endowments = JS_NewObject(js); - JS_SetPropertyStr(js, prosperon, "endowments", JS_DupValue(js, endowments)); - } - // Set constructors in endowments - JS_SetPropertyStr(js, endowments, "window", window_ctor); - JS_SetPropertyStr(js, endowments, "texture", texture_ctor); + JS_SetPropertyStr(js, ret, "window", window_ctor); + JS_SetPropertyStr(js, ret, "texture", texture_ctor); // Add utility function - JS_SetPropertyStr(js, endowments, "createWindowAndRenderer", + JS_SetPropertyStr(js, ret, "createWindowAndRenderer", JS_NewCFunction(js, js_sdl_createWindowAndRenderer, "createWindowAndRenderer", 4)); // Add cursor functions - JS_SetPropertyStr(js, endowments, "createCursor", - JS_NewCFunction(js, js_sdl_create_cursor, "createCursor", 2)); - JS_SetPropertyStr(js, endowments, "setCursor", - JS_NewCFunction(js, js_sdl_set_cursor, "setCursor", 1)); + JS_SetPropertyStr(js, ret, "createCursor", + JS_NewCFunction(js, js_sdl_create_cursor, "createCursor", 2)); + JS_SetPropertyStr(js, ret, "setCursor", + JS_NewCFunction(js, js_sdl_set_cursor, "setCursor", 1)); - JS_FreeValue(js, endowments); - JS_FreeValue(js, prosperon); -} - -#include "qjs_wota.h" - -JSValue js_sdl_video_use(JSContext *js) { - if (!SDL_Init(SDL_INIT_VIDEO)) - return JS_ThrowInternalError(js, "Unable to initialize video subsystem: %s", SDL_GetError()); - - // Generate a unique ID for the video actor - char id[64]; - snprintf(id, sizeof(id), "video_%llu", (unsigned long long)SDL_GetTicks()); - - JSValue startup = JS_NewObject(js); - JS_SetPropertyStr(js,startup, "id", JS_NewStringLen(js,id,64)); - JS_SetPropertyStr(js,startup, "program", JS_NewString(js,"prosperon/_sdl_video")); - JS_SetPropertyStr(js,startup,"main",JS_NewBool(js,1)); - - void *wota = value2wota(js,startup, JS_UNDEFINED); - - JS_FreeValue(js,startup); - - cell_rt *actor = create_actor(wota, video_actor_hook); - return actor2js(js,actor); + return ret; }