1615 lines
49 KiB
C
1615 lines
49 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <assert.h>
|
|
#include <inttypes.h>
|
|
#include <limits.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <SDL3/SDL_atomic.h>
|
|
|
|
#define WOTA_IMPLEMENTATION
|
|
#include "wota.h"
|
|
#include "qjs_wota.h"
|
|
|
|
#include "physfs.h"
|
|
#include "stb_ds.h"
|
|
#include "jsffi.h"
|
|
#include "cell.h"
|
|
|
|
#ifdef TRACY_ENABLE
|
|
#include <tracy/TracyC.h>
|
|
#if defined(__APPLE__)
|
|
#include <malloc/malloc.h>
|
|
#define MALLOC_OVERHEAD 0
|
|
#elif defined(_WIN32)
|
|
#include <malloc.h>
|
|
#define MALLOC_OVERHEAD 8
|
|
#elif defined(__linux__) || defined(__GLIBC__)
|
|
#define _GNU_SOURCE
|
|
#include <malloc.h>
|
|
#define MALLOC_OVERHEAD 8
|
|
#endif
|
|
#define likely(x) __builtin_expect(!!(x), 1)
|
|
#define unlikely(x) __builtin_expect(!!(x), 0)
|
|
#endif
|
|
|
|
int tracy_profiling_enabled = 0;
|
|
|
|
#define ENGINE "engine.cm"
|
|
|
|
static cell_rt **ready_queue = NULL;
|
|
static cell_rt **main_ready_queue = NULL;
|
|
static SDL_Mutex *queue_mutex = NULL;
|
|
static SDL_Condition *queue_cond = NULL;
|
|
static SDL_Mutex *actors_mutex = NULL;
|
|
static struct { char *key; cell_rt *value; } *actors = NULL;
|
|
static unsigned char *zip_buffer_global = NULL;
|
|
static char *prosperon = NULL;
|
|
|
|
/* Event watch subscribers */
|
|
char **event_watchers = NULL;
|
|
SDL_Mutex *event_watchers_mutex = NULL;
|
|
|
|
static Uint32 queue_event;
|
|
|
|
static SDL_AtomicInt engine_shutdown;
|
|
static SDL_Thread **runners = NULL;
|
|
|
|
static Uint32 actor_remove_cb(cell_rt *actor, Uint32 id, Uint32 interval)
|
|
{
|
|
if (JS_IsUndefined(actor->unneeded))
|
|
actor_free(actor);
|
|
else {
|
|
SDL_LockMutex(actor->mutex);
|
|
JSValue ret = JS_Call(actor->context, actor->unneeded, JS_UNDEFINED, 0, NULL);
|
|
uncaught_exception(actor->context, ret);
|
|
SDL_UnlockMutex(actor->mutex);
|
|
|
|
set_actor_state(actor);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void js_dofree(JSRuntime *rt, void *opaque, void *ptr)
|
|
{
|
|
js_free_rt(rt, ptr);
|
|
}
|
|
|
|
SDL_ThreadID main_thread = 0;
|
|
SDL_TLSID prosperon_id;
|
|
|
|
#ifdef TRACY_ENABLE
|
|
static size_t js_tracy_malloc_usable_size(const void *ptr)
|
|
{
|
|
#if defined(__APPLE__)
|
|
return malloc_size(ptr);
|
|
#elif defined(_WIN32)
|
|
return _msize((void *)ptr);
|
|
#elif defined(EMSCRIPTEN)
|
|
return 0;
|
|
#elif defined(__linux__) || defined(__GLIBC__)
|
|
return malloc_usable_size((void *)ptr);
|
|
#else
|
|
return malloc_usable_size((void *)ptr);
|
|
#endif
|
|
}
|
|
|
|
static void *js_tracy_malloc(JSMallocState *s, size_t size)
|
|
{
|
|
void *ptr;
|
|
assert(size != 0);
|
|
if (unlikely(s->malloc_size + size > s->malloc_limit)) return NULL;
|
|
ptr = malloc(size);
|
|
if (!ptr) return NULL;
|
|
s->malloc_count++;
|
|
s->malloc_size += js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
|
TracyCAllocN(ptr, js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD, "quickjs");
|
|
return ptr;
|
|
}
|
|
|
|
static void js_tracy_free(JSMallocState *s, void *ptr)
|
|
{
|
|
if (!ptr) return;
|
|
s->malloc_count--;
|
|
s->malloc_size -= js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
|
TracyCFreeN(ptr, "quickjs");
|
|
free(ptr);
|
|
}
|
|
|
|
static void *js_tracy_realloc(JSMallocState *s, void *ptr, size_t size)
|
|
{
|
|
size_t old_size;
|
|
if (!ptr) return size ? js_tracy_malloc(s, size) : NULL;
|
|
old_size = js_tracy_malloc_usable_size(ptr);
|
|
if (!size) {
|
|
s->malloc_count--;
|
|
s->malloc_size -= old_size + MALLOC_OVERHEAD;
|
|
TracyCFreeN(ptr, "quickjs");
|
|
free(ptr);
|
|
return NULL;
|
|
}
|
|
if (s->malloc_size + size - old_size > s->malloc_limit) return NULL;
|
|
TracyCFreeN(ptr, "quickjs");
|
|
ptr = realloc(ptr, size);
|
|
if (!ptr) return NULL;
|
|
s->malloc_size += js_tracy_malloc_usable_size(ptr) - old_size;
|
|
TracyCAllocN(ptr, js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD, "quickjs");
|
|
return ptr;
|
|
}
|
|
|
|
static const JSMallocFunctions tracy_malloc_funcs = {
|
|
js_tracy_malloc,
|
|
js_tracy_free,
|
|
js_tracy_realloc,
|
|
js_tracy_malloc_usable_size
|
|
};
|
|
#endif
|
|
|
|
static void free_zip(void)
|
|
{
|
|
free(zip_buffer_global);
|
|
zip_buffer_global = NULL;
|
|
}
|
|
|
|
int prosperon_mount_core(void)
|
|
{
|
|
size_t size;
|
|
char exe_path[PATH_MAX];
|
|
|
|
// Get the full path of the executable
|
|
const char *base_dir = PHYSFS_getBaseDir();
|
|
if (base_dir) {
|
|
snprintf(exe_path, sizeof(exe_path), "%s%s", base_dir, PHYSFS_getDirSeparator());
|
|
|
|
// Extract just the executable name from argv[0]
|
|
const char *exe_name = strrchr(prosperon, '/');
|
|
if (!exe_name) exe_name = strrchr(prosperon, '\\');
|
|
if (exe_name) exe_name++; else exe_name = prosperon;
|
|
|
|
strncat(exe_path, exe_name, sizeof(exe_path) - strlen(exe_path) - 1);
|
|
} else {
|
|
strncpy(exe_path, prosperon, sizeof(exe_path) - 1);
|
|
exe_path[sizeof(exe_path) - 1] = '\0';
|
|
}
|
|
|
|
FILE *f = fopen(exe_path, "rb");
|
|
if (!f) return perror("fopen"), 0;
|
|
if (fseek(f, 0, SEEK_END) != 0) return perror("fseek"), fclose(f), 0;
|
|
size = ftell(f);
|
|
if (size < 0) return perror("ftell"), fclose(f), 0;
|
|
zip_buffer_global = malloc(size);
|
|
if (!zip_buffer_global) return perror("malloc"), fclose(f), 0;
|
|
rewind(f);
|
|
if (fread(zip_buffer_global, 1, size, f) != size) {
|
|
perror("fread");
|
|
free(zip_buffer_global);
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
fclose(f);
|
|
|
|
long max_comment_len = 0xFFFF;
|
|
long eocd_search_start = (size > max_comment_len + 22) ? size - (max_comment_len + 22) : 0;
|
|
long eocd_pos = -1;
|
|
for (long i = size - 22; i >= eocd_search_start; i--)
|
|
if (zip_buffer_global[i] == 'P' && zip_buffer_global[i + 1] == 'K' &&
|
|
zip_buffer_global[i + 2] == 0x05 && zip_buffer_global[i + 3] == 0x06) {
|
|
eocd_pos = i;
|
|
break;
|
|
}
|
|
if (eocd_pos < 0) {
|
|
free(zip_buffer_global);
|
|
return 0;
|
|
}
|
|
|
|
uint16_t comment_length = zip_buffer_global[eocd_pos + 20] | (zip_buffer_global[eocd_pos + 21] << 8);
|
|
int eocd_size = 22 + comment_length;
|
|
uint32_t cd_size = zip_buffer_global[eocd_pos + 12] | (zip_buffer_global[eocd_pos + 13] << 8) |
|
|
(zip_buffer_global[eocd_pos + 14] << 16) | (zip_buffer_global[eocd_pos + 15] << 24);
|
|
uint32_t cd_offset_rel = zip_buffer_global[eocd_pos + 16] | (zip_buffer_global[eocd_pos + 17] << 8) |
|
|
(zip_buffer_global[eocd_pos + 18] << 16) | (zip_buffer_global[eocd_pos + 19] << 24);
|
|
uint32_t appended_zip_size = cd_offset_rel + cd_size + eocd_size;
|
|
long zip_offset = size - appended_zip_size;
|
|
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()));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
cell_rt *create_actor(void *wota, void (*hook)(JSContext*))
|
|
{
|
|
cell_rt *actor = calloc(sizeof(*actor), 1);
|
|
actor->init_wota = wota;
|
|
actor->cycle_fn = JS_UNDEFINED;
|
|
actor->idx_buffer = JS_UNDEFINED;
|
|
actor->message_handle = JS_UNDEFINED;
|
|
actor->unneeded = JS_UNDEFINED;
|
|
actor->on_exception = JS_UNDEFINED;
|
|
|
|
arrsetcap(actor->messages, 5);
|
|
arrsetcap(actor->events, 5);
|
|
|
|
actor->mutex = SDL_CreateMutex(); /* Protects JSContext + state */
|
|
actor->msg_mutex = SDL_CreateMutex(); /* Mailbox queue lock */
|
|
actor->turn = SDL_CreateMutex();
|
|
|
|
/* Lock actor->mutex while initializing JS runtime. */
|
|
SDL_LockMutex(actor->mutex);
|
|
script_startup(actor, hook);
|
|
SDL_UnlockMutex(actor->mutex);
|
|
|
|
set_actor_state(actor);
|
|
|
|
return actor;
|
|
}
|
|
|
|
cell_rt *get_actor(char *id)
|
|
{
|
|
SDL_LockMutex(actors_mutex);
|
|
int idx = shgeti(actors, id);
|
|
if (idx == -1) {
|
|
SDL_UnlockMutex(actors_mutex);
|
|
return NULL;
|
|
}
|
|
cell_rt *actor = actors[idx].value;
|
|
SDL_UnlockMutex(actors_mutex);
|
|
return actor;
|
|
}
|
|
|
|
const char *register_actor(const char *id, cell_rt *actor, int mainthread)
|
|
{
|
|
SDL_LockMutex(actors_mutex);
|
|
if (shgeti(actors, id) != -1) {
|
|
SDL_UnlockMutex(actors_mutex);
|
|
return "Actor with given ID already exists.";
|
|
}
|
|
actor->main_thread_only = mainthread;
|
|
actor->id = strdup(id);
|
|
shput(actors, id, actor);
|
|
SDL_UnlockMutex(actors_mutex);
|
|
return NULL;
|
|
}
|
|
|
|
const char *send_message(const char *id, void *msg)
|
|
{
|
|
cell_rt *target = get_actor(id);
|
|
if (!target) {
|
|
free(msg);
|
|
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);
|
|
|
|
set_actor_state(target);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* set_actor_state should check if either messages or events are pending. */
|
|
void set_actor_state(cell_rt *actor)
|
|
{
|
|
SDL_LockMutex(actor->msg_mutex);
|
|
if (actor->need_stop) {
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
actor_free(actor);
|
|
return;
|
|
}
|
|
|
|
int has_messages = arrlen(actor->messages);
|
|
int has_events = arrlen(actor->events);
|
|
int has_upcoming = hmlen(actor->timers);
|
|
|
|
if (actor->state == ACTOR_RUNNING)
|
|
actor->state = ACTOR_IDLE;
|
|
|
|
switch(actor->state) {
|
|
case ACTOR_IDLE:
|
|
if (has_messages || has_events) {
|
|
actor->state = ACTOR_READY;
|
|
SDL_LockMutex(queue_mutex);
|
|
if (actor->main_thread_only) {
|
|
arrput(main_ready_queue, actor);
|
|
SDL_Event event;
|
|
event.type = queue_event;
|
|
SDL_PushEvent(&event);
|
|
} else {
|
|
SDL_SignalCondition(queue_cond);
|
|
arrput(ready_queue, actor);
|
|
}
|
|
SDL_UnlockMutex(queue_mutex);
|
|
goto END;
|
|
}
|
|
break;
|
|
|
|
case ACTOR_READY:
|
|
if (!has_messages && !has_events) {
|
|
actor->state = ACTOR_IDLE;
|
|
goto END;
|
|
}
|
|
break;
|
|
}
|
|
|
|
END:
|
|
if (actor->state == ACTOR_IDLE && !actor->ar && !has_upcoming) {
|
|
if (JS_IsUndefined(actor->unneeded))
|
|
actor->ar = SDL_AddTimerNS(SDL_SECONDS_TO_NS(1), actor_remove_cb, actor);
|
|
else {
|
|
if (!isinf(actor->unneeded_secs))
|
|
actor->ar = SDL_AddTimerNS(SDL_SECONDS_TO_NS(actor->unneeded_secs), actor_remove_cb, actor);
|
|
}
|
|
}
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
}
|
|
|
|
void actor_turn(cell_rt *actor, int greedy)
|
|
{
|
|
SDL_LockMutex(actor->turn);
|
|
#ifdef TRACY_ENABLE
|
|
if (tracy_profiling_enabled)
|
|
TracyCFiberEnter(actor->name);
|
|
#endif
|
|
|
|
SDL_LockMutex(actor->msg_mutex);
|
|
actor->state = ACTOR_RUNNING;
|
|
|
|
int msgs = 0;
|
|
int events = 0;
|
|
int need_stop = 0;
|
|
JSValue result;
|
|
|
|
msgs = arrlen(actor->messages);
|
|
events = arrlen(actor->events);
|
|
need_stop = actor->need_stop;
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
|
|
if (need_stop) goto KILL;
|
|
if (!msgs && !events) goto END;
|
|
if (!msgs) goto EVENT;
|
|
|
|
MESSAGE:
|
|
SDL_LockMutex(actor->msg_mutex);
|
|
void *msg = actor->messages[0];
|
|
arrdel(actor->messages,0);
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
|
|
SDL_LockMutex(actor->mutex);
|
|
JSValue arg = wota2value(actor->context, msg);
|
|
free(msg);
|
|
result = JS_Call(actor->context, actor->message_handle, JS_UNDEFINED, 1, &arg);
|
|
uncaught_exception(actor->context, result);
|
|
JS_FreeValue(actor->context,arg);
|
|
SDL_UnlockMutex(actor->mutex);
|
|
|
|
if (!greedy) goto END;
|
|
|
|
SDL_LockMutex(actor->msg_mutex);
|
|
need_stop = actor->need_stop;
|
|
msgs = arrlen(actor->messages);
|
|
events = arrlen(actor->events);
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
|
|
if (need_stop) goto KILL;
|
|
|
|
if (!msgs && !events) goto END;
|
|
|
|
SDL_LockMutex(queue_mutex);
|
|
int queuen = arrlen(ready_queue);
|
|
SDL_UnlockMutex(queue_mutex);
|
|
|
|
if (queuen != 0)
|
|
goto END;
|
|
|
|
if (msgs)
|
|
goto MESSAGE;
|
|
|
|
EVENT:
|
|
SDL_LockMutex(actor->msg_mutex);
|
|
JSValue event = actor->events[0];
|
|
arrdel(actor->events, 0);
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
|
|
SDL_LockMutex(actor->mutex);
|
|
result = JS_Call(actor->context, event, JS_UNDEFINED, 0, NULL);
|
|
uncaught_exception(actor->context, result);
|
|
JS_FreeValue(actor->context, event);
|
|
SDL_UnlockMutex(actor->mutex);
|
|
|
|
if (!greedy) goto END;
|
|
|
|
SDL_LockMutex(actor->msg_mutex);
|
|
events = arrlen(actor->events);
|
|
need_stop = actor->need_stop;
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
|
|
if (need_stop) goto KILL;
|
|
if (!events) goto END;
|
|
|
|
SDL_LockMutex(queue_mutex);
|
|
int n = arrlen(ready_queue);
|
|
SDL_UnlockMutex(queue_mutex);
|
|
|
|
if (n != 0)
|
|
goto END;
|
|
|
|
goto EVENT;
|
|
|
|
END:
|
|
#ifdef TRACY_ENABLE
|
|
if (tracy_profiling_enabled)
|
|
TracyCFiberLeave(actor->name);
|
|
#endif
|
|
SDL_UnlockMutex(actor->turn);
|
|
set_actor_state(actor);
|
|
return;
|
|
|
|
KILL:
|
|
#ifdef TRACY_ENABLE
|
|
if (tracy_profiling_enabled)
|
|
TracyCFiberLeave(actor->name);
|
|
#endif
|
|
SDL_UnlockMutex(actor->turn);
|
|
actor_free(actor);
|
|
}
|
|
|
|
void actor_free(cell_rt *actor)
|
|
{
|
|
// Delete it out of actors first so it can no longer get messages
|
|
SDL_LockMutex(actors_mutex);
|
|
shdel(actors, actor->id);
|
|
int remaining = shlen(actors);
|
|
SDL_UnlockMutex(actors_mutex);
|
|
|
|
// If in a queue, remove it
|
|
SDL_LockMutex(queue_mutex);
|
|
|
|
for (int i = 0; i < arrlen(ready_queue); i++) {
|
|
if (ready_queue[i] == actor) {
|
|
arrdel(ready_queue, i);
|
|
break;
|
|
}
|
|
}
|
|
SDL_UnlockMutex(queue_mutex);
|
|
|
|
// Do not go forward with actor destruction until the actor is completely free
|
|
SDL_LockMutex(actor->mutex);
|
|
SDL_LockMutex(actor->msg_mutex);
|
|
SDL_LockMutex(actor->turn);
|
|
|
|
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);
|
|
JS_FreeValue(js, actor->unneeded);
|
|
|
|
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 < 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->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);
|
|
|
|
JSRuntime *rt = JS_GetRuntime(js);
|
|
JS_FreeContext(js);
|
|
JS_FreeRuntime(rt);
|
|
free(actor->id);
|
|
|
|
SDL_UnlockMutex(actor->mutex);
|
|
SDL_DestroyMutex(actor->mutex);
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
SDL_DestroyMutex(actor->msg_mutex);
|
|
SDL_UnlockMutex(actor->turn);
|
|
SDL_DestroyMutex(actor->turn);
|
|
|
|
free(actor);
|
|
|
|
if (remaining == 1)
|
|
exit(0);
|
|
}
|
|
|
|
/* Timer callback adds an event to the queue under evt_mutex. */
|
|
Uint32 actor_timer_cb(cell_rt *actor, SDL_TimerID id, Uint32 interval)
|
|
{
|
|
SDL_LockMutex(actor->msg_mutex);
|
|
int idx = hmgeti(actor->timers, id);
|
|
if (idx == -1) goto END;
|
|
|
|
JSValue cb = actor->timers[idx].value;
|
|
hmdel(actor->timers, id);
|
|
arrput(actor->events, cb);
|
|
set_actor_state(actor);
|
|
|
|
END:
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
return 0;
|
|
}
|
|
|
|
/* JS function that schedules a timer. */
|
|
JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
if (!JS_IsFunction(js, argv[0]))
|
|
return JS_ThrowReferenceError(js, "Argument must be a function.");
|
|
|
|
cell_rt *actor = JS_GetContextOpaque(js);
|
|
double seconds;
|
|
JS_ToFloat64(js, &seconds, argv[1]);
|
|
if (seconds <= 0) {
|
|
SDL_LockMutex(actor->msg_mutex);
|
|
JSValue cb = JS_DupValue(js, argv[0]);
|
|
arrput(actor->events, cb);
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
return JS_NewInt32(js, -1);
|
|
}
|
|
Uint64 ns = seconds * SDL_NS_PER_SECOND;
|
|
|
|
Uint32 id = SDL_AddTimerNS(ns, actor_timer_cb, actor);
|
|
|
|
SDL_LockMutex(actor->msg_mutex);
|
|
JSValue cb = JS_DupValue(js, argv[0]);
|
|
hmput(actor->timers, id, cb);
|
|
if (actor->ar) {
|
|
SDL_RemoveTimer(actor->ar);
|
|
actor->ar = 0;
|
|
}
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
|
|
return JS_NewUint32(js, id);
|
|
}
|
|
|
|
JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
cell_rt *actor = JS_GetContextOpaque(js);
|
|
Uint32 timer_id;
|
|
JS_ToUint32(js, &timer_id, argv[0]);
|
|
if (timer_id == -1) return JS_UNDEFINED;
|
|
|
|
SDL_RemoveTimer(timer_id);
|
|
|
|
JSValue cb = JS_UNDEFINED;
|
|
|
|
SDL_LockMutex(actor->msg_mutex);
|
|
int id = hmgeti(actor->timers, timer_id);
|
|
if (id != -1) {
|
|
cb = actor->timers[id].value;
|
|
hmdel(actor->timers, timer_id);
|
|
}
|
|
SDL_UnlockMutex(actor->msg_mutex);
|
|
|
|
JS_FreeValue(js,cb);
|
|
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
// Wrapper struct to keep the array pointer stable
|
|
typedef struct {
|
|
TracyCZoneCtx *arr; // stb_ds dynamic array
|
|
} tracy_stack_t;
|
|
|
|
// Global TLS ID for the Tracy stack
|
|
static SDL_TLSID tracy_stack_id = {0};
|
|
|
|
// Cleanup function for the wrapper struct
|
|
static void tracy_cleanup_stack(void *value)
|
|
{
|
|
tracy_stack_t *stack = value;
|
|
if (stack) {
|
|
arrfree(stack->arr);
|
|
free(stack);
|
|
}
|
|
}
|
|
|
|
// Get or initialize the thread-local Tracy stack
|
|
static tracy_stack_t *get_tracy_stack(void)
|
|
{
|
|
tracy_stack_t *stack = SDL_GetTLS(&tracy_stack_id);
|
|
if (!stack) {
|
|
stack = malloc(sizeof(tracy_stack_t));
|
|
stack->arr = NULL; // stb_ds starts with NULL
|
|
arrsetcap(stack->arr, 5); // Initial capacity
|
|
SDL_SetTLS(&tracy_stack_id, stack, tracy_cleanup_stack);
|
|
}
|
|
return stack;
|
|
}
|
|
|
|
void tracy_call_hook(JSContext *js, JSValue fn)
|
|
{
|
|
if (!tracy_profiling_enabled)
|
|
return;
|
|
|
|
tracy_stack_t *stack = get_tracy_stack();
|
|
js_debug debug;
|
|
js_debug_info(js, fn, &debug);
|
|
|
|
uint64_t srcloc = ___tracy_alloc_srcloc(debug.line, debug.filename, strlen(debug.filename), debug.name, strlen(debug.name), debug.unique);
|
|
arrput(stack->arr, ___tracy_emit_zone_begin_alloc(srcloc, 1));
|
|
|
|
free_js_debug_info(js, &debug);
|
|
}
|
|
|
|
void tracy_end_hook(JSContext *js, JSValue fn)
|
|
{
|
|
if (!tracy_profiling_enabled)
|
|
return;
|
|
|
|
tracy_stack_t *stack = get_tracy_stack();
|
|
if (arrlen(stack->arr) > 0)
|
|
___tracy_emit_zone_end(arrpop(stack->arr));
|
|
}
|
|
|
|
void script_startup(cell_rt *prt, void (*hook)(JSContext*))
|
|
{
|
|
JSRuntime *rt;
|
|
#ifdef TRACY_ENABLE
|
|
if (tracy_profiling_enabled)
|
|
rt = JS_NewRuntime2(&tracy_malloc_funcs, NULL);
|
|
else
|
|
rt = JS_NewRuntime();
|
|
#else
|
|
rt = JS_NewRuntime();
|
|
#endif
|
|
|
|
JSContext *js = JS_NewContextRaw(rt);
|
|
|
|
#ifdef TRACY_ENABLE
|
|
if (tracy_profiling_enabled) {
|
|
js_debug_sethook(js, tracy_call_hook, JS_HOOK_CALL);
|
|
js_debug_sethook(js, tracy_end_hook, JS_HOOK_RET);
|
|
}
|
|
#endif
|
|
|
|
JS_AddIntrinsicBaseObjects(js);
|
|
JS_AddIntrinsicEval(js);
|
|
JS_AddIntrinsicRegExp(js);
|
|
JS_AddIntrinsicJSON(js);
|
|
JS_AddIntrinsicMapSet(js);
|
|
JS_AddIntrinsicProxy(js);
|
|
|
|
JS_SetContextOpaque(js, prt);
|
|
prt->context = js;
|
|
ffi_load(js);
|
|
|
|
PHYSFS_File *eng = PHYSFS_openRead(ENGINE);
|
|
if (!eng) {
|
|
printf("Could not open file! %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
|
return;
|
|
}
|
|
PHYSFS_Stat stat;
|
|
PHYSFS_stat(ENGINE, &stat);
|
|
char *data = malloc(stat.filesize+1);
|
|
PHYSFS_readBytes(eng, data, stat.filesize);
|
|
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);
|
|
}
|
|
|
|
int uncaught_exception(JSContext *js, JSValue v)
|
|
{
|
|
cell_rt *rt = JS_GetContextOpaque(js);
|
|
SDL_LockMutex(rt->mutex);
|
|
JS_FreeValue(js,v);
|
|
|
|
if (!JS_HasException(js)) {
|
|
JS_FreeValue(js,v);
|
|
SDL_UnlockMutex(rt->mutex);
|
|
return 1;
|
|
}
|
|
|
|
JSValue exp = JS_GetException(js);
|
|
|
|
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");
|
|
if (!JS_IsUndefined(stack)) {
|
|
const char *st = JS_ToCString(js, stack);
|
|
printf("Unhandled error: %s\n%s\n", msg, st);
|
|
JS_FreeCString(js,st);
|
|
JS_FreeValue(js,stack);
|
|
} else
|
|
printf("Unhandled exception: %s\n", msg);
|
|
|
|
JS_FreeCString(js, msg);
|
|
}
|
|
JS_FreeValue(js, exp);
|
|
SDL_UnlockMutex(rt->mutex);
|
|
return 0;
|
|
}
|
|
|
|
void script_evalf(JSContext *js, const char *format, ...)
|
|
{
|
|
cell_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);
|
|
}
|
|
|
|
static int crank_actor(void *data)
|
|
{
|
|
while (!SDL_GetAtomicInt(&engine_shutdown)) {
|
|
SDL_LockMutex(queue_mutex);
|
|
cell_rt *actor = NULL;
|
|
if (arrlen(ready_queue) > 0) {
|
|
actor = ready_queue[0];
|
|
arrdel(ready_queue, 0);
|
|
} else {
|
|
SDL_WaitCondition(queue_cond, queue_mutex);
|
|
SDL_UnlockMutex(queue_mutex);
|
|
continue;
|
|
}
|
|
SDL_UnlockMutex(queue_mutex);
|
|
|
|
if (actor) actor_turn(actor, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void exit_handler(void)
|
|
{
|
|
SDL_SetAtomicInt(&engine_shutdown, 1);
|
|
|
|
/* Push a terminating event to the SDL event queue */
|
|
SDL_Event terminating_event;
|
|
terminating_event.type = SDL_EVENT_TERMINATING;
|
|
SDL_PushEvent(&terminating_event);
|
|
|
|
int status;
|
|
SDL_BroadcastCondition(queue_cond);
|
|
for (int i = 0; i < arrlen(runners); i++)
|
|
SDL_WaitThread(runners[i], &status);
|
|
|
|
SDL_Quit();
|
|
exit(0);
|
|
}
|
|
|
|
static void signal_handler(int sig)
|
|
{
|
|
const char *str = NULL;
|
|
switch (sig) {
|
|
case SIGABRT: str = "SIGABRT"; break;
|
|
case SIGFPE: str = "SIGFPE"; break;
|
|
case SIGILL: str = "SIGILL"; break;
|
|
case SIGINT: str = "SIGINT"; break;
|
|
case SIGSEGV: str = "SIGSEGV"; break;
|
|
case SIGTERM: str = "SIGTERM"; break;
|
|
}
|
|
if (!str) return;
|
|
|
|
exit_handler();
|
|
}
|
|
|
|
// 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 += 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, "pos");
|
|
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, "pos");
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
const char *new_cwd = NULL;
|
|
int profile_enabled = 0;
|
|
int script_start = 1;
|
|
|
|
// Check for --profile flag first
|
|
if (argc > 1 && strcmp(argv[1], "--profile") == 0) {
|
|
profile_enabled = 1;
|
|
script_start = 2;
|
|
}
|
|
|
|
// Check for --cwd flag
|
|
for (int i = script_start; i < argc; i++) {
|
|
if (strcmp(argv[i], "--cwd") == 0) {
|
|
i++;
|
|
if (i < argc) new_cwd = argv[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!SDL_Init(SDL_INIT_EVENTS)) {
|
|
printf("CRITICAL ERROR: %s\n", SDL_GetError());
|
|
exit(1);
|
|
}
|
|
queue_event = SDL_RegisterEvents(1);
|
|
|
|
#ifdef TRACY_ENABLE
|
|
tracy_profiling_enabled = profile_enabled;
|
|
#endif
|
|
|
|
main_thread = SDL_GetCurrentThreadID();
|
|
|
|
int cores = SDL_GetNumLogicalCPUCores();
|
|
|
|
prosperon = argv[0];
|
|
PHYSFS_init(argv[0]);
|
|
|
|
// Mount the current working directory where the command was called from
|
|
char cwd[PATH_MAX];
|
|
|
|
if (!new_cwd) {
|
|
new_cwd = SDL_GetCurrentDirectory();
|
|
if (!new_cwd) {
|
|
printf("error: %s\n", SDL_GetError());
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
int mounted = prosperon_mount_core();
|
|
if (!mounted) mounted = PHYSFS_mount("core.zip", NULL, 0);
|
|
if (!mounted) mounted = PHYSFS_mount("scripts", NULL, 0);
|
|
if (!mounted) {
|
|
printf("Could not mount core. Reason: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
|
return 1;
|
|
}
|
|
|
|
PHYSFS_mount(new_cwd, NULL, 0);
|
|
PHYSFS_setWriteDir(new_cwd);
|
|
|
|
SDL_free(new_cwd);
|
|
|
|
queue_mutex = SDL_CreateMutex();
|
|
queue_cond = SDL_CreateCondition();
|
|
actors_mutex = SDL_CreateMutex();
|
|
event_watchers_mutex = SDL_CreateMutex();
|
|
|
|
/* Create the initial actor from the main command line. */
|
|
/* Adjust argc and argv to skip --profile if present */
|
|
char **actor_argv = argv + (script_start - 1);
|
|
|
|
WotaBuffer startwota;
|
|
wota_buffer_init(&startwota, 5);
|
|
wota_write_record(&startwota, 1);
|
|
wota_write_text(&startwota, "program");
|
|
wota_write_text(&startwota, actor_argv[1]); // program name
|
|
create_actor(startwota.data,NULL); // this can fall off because the actor takes care of freeing the wota data
|
|
|
|
/* Start the thread that pumps ready actors, one per logical core. */
|
|
for (int i = 0; i < cores; i++) {
|
|
char threadname[128];
|
|
snprintf(threadname, 128, "actor runner %d", i);
|
|
SDL_Thread *thread = SDL_CreateThread(crank_actor, threadname, NULL);
|
|
arrput(runners, thread);
|
|
}
|
|
|
|
/* Set up signal and exit handlers. */
|
|
signal(SIGINT, signal_handler);
|
|
signal(SIGTERM, signal_handler);
|
|
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) continue;
|
|
|
|
QUEUE:
|
|
SDL_LockMutex(queue_mutex);
|
|
if (arrlen(main_ready_queue) == 0) {
|
|
SDL_UnlockMutex(queue_mutex);
|
|
continue;
|
|
}
|
|
cell_rt *actor = main_ready_queue[0];
|
|
arrdel(main_ready_queue, 0);
|
|
SDL_UnlockMutex(queue_mutex);
|
|
|
|
actor_turn(actor, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int actor_exists(const char *id)
|
|
{
|
|
SDL_LockMutex(actors_mutex);
|
|
int idx = shgeti(actors,id);
|
|
SDL_UnlockMutex(actors_mutex);
|
|
if (idx == -1)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|