input now contains function to register any actor to OS events

This commit is contained in:
2025-05-26 17:56:43 -05:00
parent 2edcd89780
commit 2eb6b3e0b4
10 changed files with 193 additions and 179 deletions

View File

@@ -3,162 +3,17 @@ $_.unneeded(_ => {
var subscribers = []
var windows = []
var renderers = []
var os = use('os')
$_.receiver(e => {
if (e.type === "subscribe") {
console.log(json.encode(e))
return
if (e.op === "subscribe") {
if (!e.actor) throw Error('Got a subscribe message with no actor.');
subscribers.push(e.actor)
return;
}
if (e.type === "window") {
var window = windows[e.id]
switch (e.fn) {
case "create":
window = os.engine_start(e.config)
windows[e.id] = window
break;
case "fullscreen":
window.fullscreen()
break;
case "make_renderer":
var renderer = window.make_renderer(e.config || {})
renderers[e.renderer_id] = renderer
break;
case "keyboard_shown":
return window.keyboard_shown()
case "theme":
return window.theme()
case "safe_area":
return window.safe_area()
case "bordered":
window.bordered(e.value)
break;
case "set_icon":
window.set_icon(e.icon)
break;
case "set_title":
window.title = e.title
break;
case "get_title":
return window.title
case "set_size":
window.size = e.size
break;
case "get_size":
return window.size
case "mouse_grab":
window.mouse_grab(e.value)
break;
}
}
if (e.type === "render") {
var renderer = renderers[e.id]
switch (e.fn) {
case "draw_color":
renderer.draw_color(e.color)
break;
case "present":
renderer.present()
break;
case "clear":
renderer.clear()
break;
case "line":
renderer.line(e.config)
break;
case "point":
renderer.point(e.config)
break;
case "texture":
renderer.texture(e.texture, e.src_rect, e.dst_rect, e.angle, e.center)
break;
case "rects":
renderer.rects(e.rects)
break;
case "geometry":
renderer.geometry(e.vertices, e.indices)
break;
case "geometry2":
renderer.geometry2(e.vertices, e.indices)
break;
case "sprite":
renderer.sprite(e.config)
break;
case "load_texture":
renderer.load_texture(e.path)
break;
case "get_image":
renderer.get_image(e.config)
break;
case "scale":
renderer.scale(e.scale)
break;
case "logical_size":
renderer.logical_size(e.size)
break;
case "viewport":
renderer.viewport(e.viewport)
break;
case "clip":
renderer.clip(e.rect)
break;
case "vsync":
renderer.vsync(e.enable)
break;
case "coords":
renderer.coords(e.config)
break;
case "camera":
renderer.camera(e.cam, e.layer)
break;
case "screen2world":
return renderer.screen2world(e.point)
case "target":
renderer.target(e.target)
break;
}
}
for (var a of subscribers)
send(a, e);
});

View File

@@ -12,12 +12,6 @@ input.mouse_lock[prosperon.DOC] = `Capture or release the mouse, confining it wi
:return: None
`
input.cursor_set[prosperon.DOC] = `Set the given cursor (created by os.make_cursor) as the active mouse cursor.
:param cursor: The cursor to set.
:return: None
`
input.keyname[prosperon.DOC] = `Given a numeric keycode, return the corresponding key name (e.g., from SDL).
:param keycode: A numeric SDL keycode.

View File

@@ -375,8 +375,6 @@ char *js2strdup(JSContext *js, JSValue v) {
#include "qjs_macros.h"
void SDL_GPUCommandBuffer_free(JSRuntime *rt, SDL_GPUCommandBuffer *c)
{
@@ -1589,7 +1587,6 @@ void ffi_load(JSContext *js)
// extra
arrput(rt->module_registry, ((ModuleEntry){"io", js_io_use}));
arrput(rt->module_registry, ((ModuleEntry){"os", js_os_use}));
arrput(rt->module_registry, ((ModuleEntry){"input", js_input_use}));
arrput(rt->module_registry, MISTLINE(qr));
arrput(rt->module_registry, MISTLINE(http));
arrput(rt->module_registry, MISTLINE(crypto));
@@ -1604,6 +1601,7 @@ void ffi_load(JSContext *js)
// prosperon
arrput(rt->module_registry, ((ModuleEntry){"sdl_audio", js_sdl_audio_use}));
arrput(rt->module_registry, ((ModuleEntry){"sdl_video", js_sdl_video_use}));
arrput(rt->module_registry, ((ModuleEntry){"input", js_input_use}));
arrput(rt->module_registry, ((ModuleEntry){"surface", js_sdl_surface_use}));
arrput(rt->module_registry, MISTLINE(spline));
arrput(rt->module_registry, ((ModuleEntry){"geometry", js_geometry_use}));
@@ -1679,6 +1677,11 @@ void ffi_load(JSContext *js)
JS_SetPropertyStr(js, prosp, "hidden", hidden_fn);
JS_FreeValue(js,globalThis);
/*
prosperon_rt *actor = JS_GetContextOpaque(js);
actor->actor_sym = JS_NewSymbol(js, "actor symbol", 0);
JS_SetPropertyStr(js, hidden_fn, "ACTORDATA", actor->actor_sym);
*/
}

View File

@@ -49,7 +49,9 @@ static struct { char *key; prosperon_rt *value; } *actors = NULL;
static unsigned char *zip_buffer_global = NULL;
static char *prosperon = NULL;
prosperon_rt *io_actor = NULL;
/* Event watch subscribers */
char **event_watchers = NULL;
SDL_Mutex *event_watchers_mutex = NULL;
static Uint32 queue_event;
@@ -252,7 +254,10 @@ const char *register_actor(const char *id, prosperon_rt *actor, int mainthread)
const char *send_message(const char *id, void *msg)
{
prosperon_rt *target = get_actor(id);
if (!target) return "Could not get actor from id.";
if (!target) {
free(msg);
return "Could not get actor from id.";
}
SDL_LockMutex(target->msg_mutex);
arrput(target->messages, msg);
@@ -1328,11 +1333,67 @@ static void event2wota_write(WotaBuffer *wb, const SDL_Event *e, int c) {
}
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;
WotaBuffer wb;
wota_buffer_init(&wb, 8);
int n = event2wota_count_props(event);
event2wota_write(&wb, event, n);
return wb;
}
bool event_watch(void *data, SDL_Event *e)
{
if (e->type == queue_event) return true;
SDL_LockMutex(event_watchers_mutex);
int n_watchers = arrlen(event_watchers);
if (n_watchers == 0) {
SDL_UnlockMutex(event_watchers_mutex);
return true;
}
/* Create a copy of watcher IDs while holding the lock */
char **watchers_copy = NULL;
arrsetcap(watchers_copy, n_watchers);
for (int i = 0; i < n_watchers; i++) {
arrput(watchers_copy, strdup(event_watchers[i]));
}
SDL_UnlockMutex(event_watchers_mutex);
WotaBuffer wb = event2wota(e);
for (int i = 0; i < arrlen(watchers_copy); i++) {
if (actor_exists(watchers_copy[i])) {
const char *err = send_message(watchers_copy[i], wota_dup_data(&wb));
if (err) {
/* Remove dead actor from watchers */
SDL_LockMutex(event_watchers_mutex);
for (int j = 0; j < arrlen(event_watchers); j++) {
if (strcmp(event_watchers[j], watchers_copy[i]) == 0) {
free(event_watchers[j]);
arrdel(event_watchers, j);
break;
}
}
SDL_UnlockMutex(event_watchers_mutex);
}
} else {
/* Remove dead actor from watchers */
SDL_LockMutex(event_watchers_mutex);
for (int j = 0; j < arrlen(event_watchers); j++) {
if (strcmp(event_watchers[j], watchers_copy[i]) == 0) {
free(event_watchers[j]);
arrdel(event_watchers, j);
break;
}
}
SDL_UnlockMutex(event_watchers_mutex);
}
free(watchers_copy[i]);
}
arrfree(watchers_copy);
wota_buffer_free(&wb);
return true;
}
int main(int argc, char **argv)
@@ -1375,12 +1436,7 @@ int main(int argc, char **argv)
queue_mutex = SDL_CreateMutex();
queue_cond = SDL_CreateCondition();
actors_mutex = SDL_CreateMutex();
const char *io_script = "scripts/core/io.js";
char **io_argv = malloc(sizeof(char*) * 2);
io_argv[0] = strdup(argv[0]);
io_argv[1] = strdup(io_script);
io_actor = create_actor(2, io_argv, NULL);
event_watchers_mutex = SDL_CreateMutex();
/* Create the initial actor from the main command line. */
create_actor(argc, argv, NULL);
@@ -1399,14 +1455,12 @@ int main(int argc, char **argv)
signal(SIGSEGV, signal_handler);
signal(SIGABRT, signal_handler);
atexit(exit_handler);
SDL_AddEventWatch(event_watch, NULL);
SDL_Event event;
while (SDL_WaitEvent(&event)) {
if (event.type == queue_event) goto QUEUE;
WotaBuffer wb = event2wota(&event);
send_message(io_actor->id, wb.data);
continue;
if (event.type != queue_event) continue;
QUEUE:
SDL_LockMutex(queue_mutex);

View File

@@ -65,6 +65,8 @@ typedef struct prosperon_rt {
Uint32 ar;
int need_stop;
int main_thread_only;
JSValue actor_sym;
} prosperon_rt;
extern SDL_ThreadID main_thread;
@@ -82,6 +84,7 @@ 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);
int actor_exists(const char *id);
prosperon_rt *get_actor(char *id);
void set_actor_state(prosperon_rt *actor);
int prosperon_mount_core(void);

View File

@@ -8,13 +8,44 @@
#include <stdlib.h>
#include <string.h>
// External variables
extern prosperon_rt *io_actor;
// External function declarations
JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv);
JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *argv);
prosperon_rt *js2actor(JSContext *js, JSValue v)
{
if (!JS_IsObject(v))
return NULL;
/* Check if it has __ACTORDATA__ property */
JSValue actor_data = JS_GetPropertyStr(js, v, "__ACTORDATA__");
if (JS_IsUndefined(actor_data)) {
JS_FreeValue(js, actor_data);
return NULL;
}
/* Get the id from __ACTORDATA__ */
JSValue id_val = JS_GetPropertyStr(js, actor_data, "id");
JS_FreeValue(js, actor_data);
if (JS_IsUndefined(id_val)) {
JS_FreeValue(js, id_val);
return NULL;
}
const char *id = JS_ToCString(js, id_val);
JS_FreeValue(js, id_val);
if (!id)
return NULL;
/* Look up the actor by ID */
prosperon_rt *actor = get_actor((char*)id);
JS_FreeCString(js, id);
return actor;
}
JSC_CCALL(os_createactor,
int margc = JS_ArrayLength(js, argv[0]);

View File

@@ -2,7 +2,9 @@
#define QJS_ACTOR_H
#include <quickjs.h>
#include "prosperon.h"
JSValue js_actor_use(JSContext *js);
prosperon_rt *js2actor(JSContext *js, JSValue v);
#endif

View File

@@ -1,6 +1,9 @@
#include "qjs_sdl.h"
#include "jsffi.h"
#include "qjs_macros.h"
#include "prosperon.h"
#include "stb_ds.h"
#include "qjs_actor.h"
#include <SDL3/SDL.h>
@@ -82,12 +85,66 @@ JSC_CCALL(input_mousestate,
return m;
)
// watch events
extern char **event_watchers;
extern SDL_Mutex *event_watchers_mutex;
JSC_CCALL(input_watch,
/* Use js2actor to get the actor from the JS object */
prosperon_rt *actor = js2actor(js, argv[0]);
if (!actor)
return JS_ThrowTypeError(js, "First argument must be a valid actor object");
SDL_LockMutex(event_watchers_mutex);
/* Check if already watching */
int already_watching = 0;
for (int i = 0; i < arrlen(event_watchers); i++) {
if (strcmp(event_watchers[i], actor->id) == 0) {
already_watching = 1;
break;
}
}
if (!already_watching) {
arrput(event_watchers, strdup(actor->id));
}
SDL_UnlockMutex(event_watchers_mutex);
return JS_UNDEFINED;
)
JSC_CCALL(input_unwatch,
/* Use js2actor to get the actor from the JS object */
prosperon_rt *actor = js2actor(js, argv[0]);
if (!actor)
return JS_ThrowTypeError(js, "First argument must be a valid actor object");
SDL_LockMutex(event_watchers_mutex);
/* Find and remove from watchers */
for (int i = 0; i < arrlen(event_watchers); i++) {
if (strcmp(event_watchers[i], actor->id) == 0) {
free(event_watchers[i]);
arrdel(event_watchers, i);
break;
}
}
SDL_UnlockMutex(event_watchers_mutex);
return JS_UNDEFINED;
)
static const JSCFunctionListEntry js_input_funcs[] = {
MIST_FUNC_DEF(input, mouse_show, 1),
MIST_FUNC_DEF(input, mouse_lock, 1),
MIST_FUNC_DEF(input, keyname, 1),
MIST_FUNC_DEF(input, keymod, 0),
MIST_FUNC_DEF(input, mousestate, 0),
MIST_FUNC_DEF(input, watch, 1),
MIST_FUNC_DEF(input, unwatch, 1),
};
JSValue js_input_use(JSContext *js) {

View File

@@ -64,6 +64,8 @@ typedef struct WotaBuffer {
size_t capacity; /* allocated capacity in 64-bit words */
} WotaBuffer;
void *wota_dup_data(struct WotaBuffer *wb);
/* Buffer management */
void wota_buffer_init(WotaBuffer *wb, size_t initial_capacity_in_words);
void wota_buffer_free(WotaBuffer *wb);
@@ -118,6 +120,13 @@ static inline uint64_t wota_bswap64(uint64_t x)
#endif
}
void *wota_dup_data(WotaBuffer *wb)
{
void *copy = malloc(wb->size*8);
memcpy(copy, wb->data, wb->size*8);
return copy;
}
/* ================================================================
Helper: Grow the buffer to fit 'min_add' more 64-bit words
================================================================ */

View File

@@ -2,6 +2,8 @@
var draw2d
var graphics
var os = use('os');
var input = use('input')
input.watch($_)
// Create SDL video actor
var video = use('sdl_video');
@@ -10,6 +12,10 @@ var video_actor = {__ACTORDATA__:{id:video}};
var window_id = null;
var renderer_id = null;
$_.receiver(e => {
console.log(json.encode(e))
})
// Create window
send(video_actor, {
kind: "window",