initial refactor
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-macos (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled

This commit is contained in:
2025-05-22 11:48:27 -05:00
parent 7bab2f1b7a
commit a204fce4b5
24 changed files with 5802 additions and 4857 deletions

View File

@@ -1,3 +0,0 @@
def:
cd _prosperon && make && cp build_dbg/prosperon ..
./prosperon

View File

@@ -147,7 +147,7 @@ sources = []
src += [ src += [
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c', 'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
'render.c','simplex.c','spline.c', 'transform.c','prosperon.c', 'wildmatch.c', 'render.c','simplex.c','spline.c', 'transform.c','prosperon.c', 'wildmatch.c',
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_os.c',
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c' 'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c'
] ]
# quirc src # quirc src

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,79 @@
#define FFI_H #define FFI_H
#include <quickjs.h> #include <quickjs.h>
#include "HandmadeMath.h"
#include "render.h"
#include "stb_ds.h"
#define JS_SetProperty(js, tar, str, val) JS_SetPropertyStr(js, tar, #str, val)
#define JS_GetProperty(js, tar, atom) JS_GetPropertyStr(js, tar, #atom)
SDL_Window *global_window;
// Core FFI functions
void ffi_load(JSContext *js); void ffi_load(JSContext *js);
int js_print_exception(JSContext *js, JSValue v); int js_print_exception(JSContext *js, JSValue v);
// Common type definitions - rect and colorf are defined in render.h
// Common conversion functions used across modules
JSValue rect2js(JSContext *js, rect r);
JSValue vec22js(JSContext *js, HMM_Vec2 v);
JSValue vec32js(JSContext *js, HMM_Vec3 v);
JSValue vec42js(JSContext *js, HMM_Vec4 v);
JSValue quat2js(JSContext *js, HMM_Quat q);
JSValue color2js(JSContext *js, colorf c);
JSValue number2js(JSContext *js, double d);
JSValue angle2js(JSContext *js, double a);
rect js2rect(JSContext *js, JSValue v);
HMM_Vec2 js2vec2(JSContext *js, JSValue v);
HMM_Vec3 js2vec3(JSContext *js, JSValue v);
HMM_Vec4 js2vec4(JSContext *js, JSValue v);
HMM_Quat js2quat(JSContext *js, JSValue v);
colorf js2color(JSContext *js, JSValue v);
double js2number(JSContext *js, JSValue v);
double js2angle(JSContext *js, JSValue v);
// Forward declaration for MTRand from prosperon.h
typedef struct tagMTRand MTRand;
// Random number generation functions
double genRand(MTRand *mrand);
uint32_t genRandLong(MTRand *mrand);
void m_seedRand(MTRand *mrand, uint32_t seed);
double rand_range(JSContext *js, double min, double max);
// Common data structures
struct lrtb {
float l;
float r;
float t;
float b;
};
typedef struct lrtb lrtb;
typedef struct text_vert text_vert;
// Common macros for property access
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \
TARGET = js2##TYPE(JS, __##PROP##__v); \
JS_FreeValue(JS,__##PROP##__v); }\
#define JS_GETATOM(JS, TARGET, VALUE, ATOM, TYPE) {\
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#ATOM); \
TARGET = js2##TYPE(JS, __##PROP##__v); \
JS_FreeValue(JS,__##PROP##__v); }\
// Common conversion functions
lrtb js2lrtb(JSContext *js, JSValue v);
int js2bool(JSContext *js, JSValue v);
JSValue make_gpu_buffer(JSContext *js, void *data, size_t size, int type, int elements, int copy, int index);
JSValue make_quad_indices_buffer(JSContext *js, int quads);
JSValue quads_to_mesh(JSContext *js, text_vert *buffer);
// SDL type conversion functions
SDL_Window *js2SDL_Window(JSContext *js, JSValue v);
JSValue SDL_Window2js(JSContext *js, SDL_Window *w);
#endif #endif

View File

@@ -49,11 +49,11 @@ static struct { char *key; prosperon_rt *value; } *actors = NULL;
static unsigned char *zip_buffer_global = NULL; static unsigned char *zip_buffer_global = NULL;
static char *prosperon = NULL; static char *prosperon = NULL;
static prosperon_rt *io_actor = NULL; prosperon_rt *io_actor = NULL;
static Uint32 queue_event; static Uint32 queue_event;
static SDL_AtomicInt shutdown; static SDL_AtomicInt engine_shutdown;
static SDL_Thread **runners = NULL; static SDL_Thread **runners = NULL;
static Uint32 actor_remove_cb(prosperon_rt *actor, Uint32 id, Uint32 interval) static Uint32 actor_remove_cb(prosperon_rt *actor, Uint32 id, Uint32 interval)
@@ -673,7 +673,7 @@ void script_evalf(JSContext *js, const char *format, ...)
static int crank_actor(void *data) static int crank_actor(void *data)
{ {
while (!SDL_GetAtomicInt(&shutdown)) { while (!SDL_GetAtomicInt(&engine_shutdown)) {
SDL_LockMutex(queue_mutex); SDL_LockMutex(queue_mutex);
prosperon_rt *actor = NULL; prosperon_rt *actor = NULL;
if (arrlen(ready_queue) > 0) { if (arrlen(ready_queue) > 0) {
@@ -720,7 +720,7 @@ static void exit_handler(void)
script_evalf(js, "prosperon.dispatch('exit')"); script_evalf(js, "prosperon.dispatch('exit')");
} }
SDL_SetAtomicInt(&shutdown, 1); SDL_SetAtomicInt(&engine_shutdown, 1);
int status; int status;
SDL_BroadcastCondition(queue_cond); SDL_BroadcastCondition(queue_cond);
for (int i = 0; i < arrlen(runners); i++) for (int i = 0; i < arrlen(runners); i++)

447
source/qjs_geometry.c Normal file
View File

@@ -0,0 +1,447 @@
#include "qjs_geometry.h"
#include "jsffi.h"
#include "qjs_macros.h"
#include <SDL3/SDL.h>
#include <math.h>
#include "HandmadeMath.h"
#include "prosperon.h"
#include "font.h"
// GEOMETRY FUNCTIONS
JSC_CCALL(geometry_rect_intersection,
rect a = js2rect(js,argv[0]);
rect b = js2rect(js,argv[1]);
rect c;
SDL_GetRectIntersectionFloat(&a, &b, &c);
return rect2js(js,c);
)
JSC_CCALL(geometry_rect_intersects,
rect a = js2rect(js,argv[0]);
rect b = js2rect(js,argv[1]);
return JS_NewBool(js, SDL_HasRectIntersectionFloat(&a,&b));
)
JSC_CCALL(geometry_rect_inside,
rect inner = js2rect(js,argv[0]);
rect outer = js2rect(js,argv[1]);
return JS_NewBool(js,
inner.x >= outer.x &&
inner.x + inner.w <= outer.x + outer.w &&
inner.y >= outer.y &&
inner.y + inner.h <= outer.y + outer.h
);
)
JSC_CCALL(geometry_rect_random,
rect a = js2rect(js,argv[0]);
return vec22js(js,(HMM_Vec2){
a.x + rand_range(js,-0.5,0.5)*a.w,
a.y + rand_range(js,-0.5,0.5)*a.h
});
)
JSC_CCALL(geometry_rect_point_inside,
rect a = js2rect(js,argv[0]);
HMM_Vec2 p = js2vec2(js,argv[1]);
return JS_NewBool(js,p.x >= a.x && p.x <= a.x+a.w && p.y <= a.y+a.h && p.y >= a.y);
)
JSC_CCALL(geometry_cwh2rect,
HMM_Vec2 c = js2vec2(js,argv[0]);
HMM_Vec2 wh = js2vec2(js,argv[1]);
rect r;
r.x = c.x;
r.y = c.y;
r.w = wh.x;
r.h = wh.y;
return rect2js(js,r);
)
JSC_CCALL(geometry_rect_pos,
rect r = js2rect(js,argv[0]);
return vec22js(js,(HMM_Vec2){
.x = r.x,
.y = r.y
});
)
JSC_CCALL(geometry_rect_move,
rect r = js2rect(js,argv[0]);
HMM_Vec2 move = js2vec2(js,argv[1]);
r.x += move.x;
r.y += move.y;
return rect2js(js,r);
)
JSC_CCALL(geometry_rect_expand,
rect a = js2rect(js,argv[0]);
rect b = js2rect(js,argv[1]);
rect c = {0};
c.x = fmin(a.x,b.x);
c.y = fmin(a.y,b.y);
c.w = fmax(a.x+a.w,b.x+b.w);
c.h = fmax(a.y+a.h,b.y+b.h);
return rect2js(js,c);
)
// Helper functions for geometry operations
static inline void add_quad(text_vert **verts, rect *restrict src, rect *restrict dst)
{
text_vert v = (text_vert){
.pos = (HMM_Vec2){dst->x, dst->y},
.uv = (HMM_Vec2){src->x,src->y},
.color = (HMM_Vec4){1,1,1,1}
};
arrput(*verts, v);
v = (text_vert){
.pos = (HMM_Vec2){dst->x+dst->w, dst->y},
.uv = (HMM_Vec2){src->x+src->w,src->y},
.color = (HMM_Vec4){1,1,1,1}
};
arrput(*verts, v);
v = (text_vert){
.pos = (HMM_Vec2){dst->x, dst->y+dst->h},
.uv = (HMM_Vec2){src->x,src->y+src->h},
.color = (HMM_Vec4){1,1,1,1}
};
arrput(*verts, v);
v = (text_vert){
.pos = (HMM_Vec2){dst->x+dst->w, dst->y+dst->h},
.uv = (HMM_Vec2){src->x+src->w,src->y+src->h},
.color = (HMM_Vec4){1,1,1,1}
};
arrput(*verts, v);
}
static inline void tile_region(text_vert **verts, rect src_uv, rect dst, float tex_w, float tex_h, bool tile_x, bool tile_y)
{
// Convert the incoming UV rect into pixel coords
rect src_px = {
.x = src_uv.x * tex_w,
.y = src_uv.y * tex_h,
.w = src_uv.w * tex_w,
.h = src_uv.h * tex_h
};
// If src_px or dst is degenerate, early out
if (src_px.w <= 0.0f || src_px.h <= 0.0f || dst.w <= 0.0f || dst.h <= 0.0f)
return;
// How many full tiles horizontally/vertically?
// If not tiling in a dimension, we treat it as exactly 1 tile (no leftover).
float cols_f, rows_f;
float remain_wf = 0.0f;
float remain_hf = 0.0f;
if (tile_x) {
remain_wf = modff(dst.w / src_px.w, &cols_f);
} else {
// Only 1 "column" covering entire width, no leftover
cols_f = 1.0f;
remain_wf = 0.0f;
}
if (tile_y) {
remain_hf = modff(dst.h / src_px.h, &rows_f);
} else {
// Only 1 "row" covering entire height, no leftover
rows_f = 1.0f;
remain_hf = 0.0f;
}
int cols = (int)cols_f;
int rows = (int)rows_f;
// The leftover portion in screen coords (pixels)
float remain_dst_w = remain_wf * src_px.w;
float remain_dst_h = remain_hf * src_px.h;
// Build the UV rect for a "full" tile
rect src_full = src_uv;
// Partial leftover in UV
float remain_src_w_uv = remain_dst_w / tex_w;
float remain_src_h_uv = remain_dst_h / tex_h;
// For partial leftover in X dimension
rect src_partial_x = src_full;
src_partial_x.w = remain_src_w_uv;
// For partial leftover in Y dimension
rect src_partial_y = src_full;
src_partial_y.h = remain_src_h_uv;
// For partial leftover in both X & Y
rect src_partial_xy = src_full;
src_partial_xy.w = remain_src_w_uv;
src_partial_xy.h = remain_src_h_uv;
// Each tile is drawn 1:1 in screen coords
float tile_w = tile_x ? src_px.w : dst.w; // If not tiling horizontally, match the entire dst width
float tile_h = tile_y ? src_px.h : dst.h; // If not tiling vertically, match the entire dst height
rect curr_dst;
curr_dst.w = tile_w;
curr_dst.h = tile_h;
curr_dst.y = dst.y;
// Loop over rows
for (int y = 0; y < rows; y++) {
curr_dst.x = dst.x;
// Loop over columns
for (int x = 0; x < cols; x++) {
add_quad(verts, &src_full, &curr_dst);
curr_dst.x += tile_w;
}
// Right-side leftover tile (only if tile_x is true)
if (tile_x && remain_dst_w > 0.0f) {
rect partial_dst = {
.x = curr_dst.x, .y = curr_dst.y,
.w = remain_dst_w, .h = tile_h
};
add_quad(verts, &src_partial_x, &partial_dst);
}
curr_dst.y += tile_h;
}
// Bottom leftover row (only if tile_y is true)
if (tile_y && remain_dst_h > 0.0f) {
rect partial_row_dst;
partial_row_dst.w = tile_w;
partial_row_dst.h = remain_dst_h;
partial_row_dst.y = curr_dst.y;
partial_row_dst.x = dst.x;
// Full columns in leftover row
for (int x = 0; x < cols; x++) {
add_quad(verts, &src_partial_y, &partial_row_dst);
partial_row_dst.x += tile_w;
}
// Partial leftover corner (both X & Y leftover)
if (tile_x && remain_dst_w > 0.0f) {
rect partial_corner_dst = {
.x = partial_row_dst.x, .y = partial_row_dst.y,
.w = remain_dst_w, .h = remain_dst_h
};
add_quad(verts, &src_partial_xy, &partial_corner_dst);
}
}
}
JSC_CCALL(gpu_slice9,
JSValue jstex = argv[0];
rect dst = js2rect(js, argv[1]);
// Full texture in UV coords
rect src = {
.x = 0, .y = 0,
.w = 1, .h = 1
};
// The "slice" LRTB in PIXELS, but we convert to UV below
lrtb src_slice = js2lrtb(js, argv[2]);
lrtb dst_slice = src_slice;
HMM_Vec2 size;
JS_GETPROP(js, size.x, jstex, width, number)
JS_GETPROP(js, size.y, jstex, height, number)
JSValue info = argv[3];
int tile_top, tile_bottom, tile_left, tile_right, center_x, center_y;
JS_GETPROP(js,tile_top, info, tile_top, bool)
JS_GETPROP(js,tile_bottom,info,tile_bottom,bool)
JS_GETPROP(js,tile_left,info,tile_left,bool)
JS_GETPROP(js,tile_right,info,tile_right,bool)
JS_GETPROP(js, center_x, info, tile_center_x, bool)
JS_GETPROP(js, center_y, info, tile_center_y, bool)
// Convert the slice edges from pixel to UV
src_slice.l /= size.x;
src_slice.r /= size.x;
src_slice.t /= size.y;
src_slice.b /= size.y;
text_vert *verts = NULL;
rect curr_src;
rect curr_dst;
// bottom-left corner (single quad)
curr_src = src;
curr_src.w = src_slice.l;
curr_src.h = src_slice.b;
curr_dst = dst;
curr_dst.w = dst_slice.l;
curr_dst.h = dst_slice.b;
add_quad(&verts, &curr_src, &curr_dst);
// top-left corner (single quad)
curr_src = src;
curr_src.x = src.x;
curr_src.y = src.y + src.h - src_slice.t;
curr_src.w = src_slice.l;
curr_src.h = src_slice.t;
curr_dst = dst;
curr_dst.x = dst.x;
curr_dst.y = dst.y + dst.h - dst_slice.t;
curr_dst.w = dst_slice.l;
curr_dst.h = dst_slice.t;
add_quad(&verts, &curr_src, &curr_dst);
// bottom-right corner (single quad)
curr_src = src;
curr_src.x = src.x + src.w - src_slice.r;
curr_src.y = src.y;
curr_src.w = src_slice.r;
curr_src.h = src_slice.b;
curr_dst = dst;
curr_dst.x = dst.x + dst.w - dst_slice.r;
curr_dst.y = dst.y;
curr_dst.w = dst_slice.r;
curr_dst.h = dst_slice.b;
add_quad(&verts, &curr_src, &curr_dst);
// top-right corner (single quad)
curr_src = src;
curr_src.x = src.x + src.w - src_slice.r;
curr_src.y = src.y + src.h - src_slice.t;
curr_src.w = src_slice.r;
curr_src.h = src_slice.t;
curr_dst = dst;
curr_dst.x = dst.x + dst.w - dst_slice.r;
curr_dst.y = dst.y + dst.h - dst_slice.t;
curr_dst.w = dst_slice.r;
curr_dst.h = dst_slice.t;
add_quad(&verts, &curr_src, &curr_dst);
// left bar (tiled)
curr_src = src;
curr_src.x = src.x;
curr_src.y = src.y + src_slice.b;
curr_src.w = src_slice.l;
curr_src.h = src.h - src_slice.t - src_slice.b;
curr_dst = dst;
curr_dst.x = dst.x;
curr_dst.y = dst.y + dst_slice.b;
curr_dst.w = dst_slice.l;
curr_dst.h = dst.h - dst_slice.t - dst_slice.b;
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_left,tile_left);
// right bar (tiled)
curr_src = src;
curr_src.x = src.x + src.w - src_slice.r;
curr_src.y = src.y + src_slice.b;
curr_src.w = src_slice.r;
curr_src.h = src.h - src_slice.t - src_slice.b;
curr_dst = dst;
curr_dst.x = dst.x + dst.w - dst_slice.r;
curr_dst.y = dst.y + dst_slice.b;
curr_dst.w = dst_slice.r;
curr_dst.h = dst.h - dst_slice.t - dst_slice.b;
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_right,tile_right);
// bottom bar (tiled)
curr_src = src;
curr_src.x = src.x + src_slice.l;
curr_src.y = src.y;
curr_src.w = src.w - src_slice.l - src_slice.r;
curr_src.h = src_slice.b;
curr_dst = dst;
curr_dst.x = dst.x + dst_slice.l;
curr_dst.y = dst.y;
curr_dst.w = dst.w - dst_slice.l - dst_slice.r;
curr_dst.h = dst_slice.b;
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_bottom,tile_bottom);
// top bar (tiled)
curr_src = src;
curr_src.x = src.x + src_slice.l;
curr_src.y = src.y + src.h - src_slice.t;
curr_src.w = src.w - src_slice.l - src_slice.r;
curr_src.h = src_slice.t;
curr_dst = dst;
curr_dst.x = dst.x + dst_slice.l;
curr_dst.y = dst.y + dst.h - dst_slice.t;
curr_dst.w = dst.w - dst_slice.l - dst_slice.r;
curr_dst.h = dst_slice.t;
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_top,tile_top);
// center (tiled)
curr_src = src;
curr_src.x = src.x + src_slice.l;
curr_src.y = src.y + src_slice.b;
curr_src.w = src.w - src_slice.l - src_slice.r;
curr_src.h = src.h - src_slice.t - src_slice.b;
curr_dst = dst;
curr_dst.x = dst.x + dst_slice.l;
curr_dst.y = dst.y + dst_slice.b;
curr_dst.w = dst.w - dst_slice.l - dst_slice.r;
curr_dst.h = dst.h - dst_slice.t - dst_slice.b;
tile_region(&verts, curr_src, curr_dst, size.x, size.y, center_x,center_y);
JSValue mesh = quads_to_mesh(js, verts);
arrfree(verts);
ret = mesh;
)
JSC_CCALL(gpu_tile,
HMM_Vec2 size;
JSValue jstex = argv[0];
JS_GETATOM(js,size.x,jstex,width,number)
JS_GETATOM(js, size.y, jstex, height, number)
rect src_pixels = js2rect(js, argv[1]); // 'src' as pixel dimensions
rect dst = js2rect(js, argv[2]); // 'dst' as screen coords
int tilex, tiley;
JSValue jstile = argv[3];
JS_GETPROP(js,tilex,jstile,repeat_x,bool)
JS_GETPROP(js,tiley,jstile,repeat_y,bool)
text_vert *verts = NULL;
tile_region(&verts, src_pixels, dst, size.x, size.y,tilex,tiley);
ret = quads_to_mesh(js,verts);
arrfree(verts);
)
static const JSCFunctionListEntry js_geometry_funcs[] = {
MIST_FUNC_DEF(geometry, rect_intersection, 2),
MIST_FUNC_DEF(geometry, rect_intersects, 2),
MIST_FUNC_DEF(geometry, rect_expand, 2),
MIST_FUNC_DEF(geometry, rect_inside, 2),
MIST_FUNC_DEF(geometry, rect_random, 1),
MIST_FUNC_DEF(geometry, cwh2rect, 2),
MIST_FUNC_DEF(geometry, rect_point_inside, 2),
MIST_FUNC_DEF(geometry, rect_pos, 1),
MIST_FUNC_DEF(geometry, rect_move, 2),
MIST_FUNC_DEF(gpu, tile, 4),
MIST_FUNC_DEF(gpu, slice9, 3),
};
JSValue js_geometry_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_geometry_funcs,countof(js_geometry_funcs));
return mod;
}

8
source/qjs_geometry.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef QJS_GEOMETRY_H
#define QJS_GEOMETRY_H
#include "quickjs.h"
JSValue js_geometry_use(JSContext *ctx);
#endif /* QJS_GEOMETRY_H */

View File

@@ -22,15 +22,6 @@ SDL_Window *js2SDL_Window(JSContext *js, JSValue v);
static int START = 0; static int START = 0;
static inline int js_arrlen(JSContext *js, JSValue v)
{
JSValue len = JS_GetPropertyStr(js, v, "length");
int intlen;
JS_ToInt32(js, &intlen, len);
JS_FreeValue(js, len);
return intlen;
}
static inline double js2number(JSContext *js, JSValue v) static inline double js2number(JSContext *js, JSValue v)
{ {
double n; double n;
@@ -176,12 +167,12 @@ void fill_plotdata(JSContext *js, JSValue v, JSValue last)
/* arrsetlen(plotdata, 0); /* arrsetlen(plotdata, 0);
if (JS_IsArray(js,js_getpropidx(v, 0))) { if (JS_IsArray(js,js_getpropidx(v, 0))) {
for (int i = 0; i < js_arrlen(js, v); i++) for (int i = 0; i < JS_ArrayLength(js, v); i++)
arrput(plotdata, js2vec2(js, js_getpropidx(v, i))); arrput(plotdata, js2vec2(js, js_getpropidx(v, i)));
} }
else { else {
// Fill it with the x axis being the array index // Fill it with the x axis being the array index
for (int i = 0; i < js_arrlen(js, v); i++) { for (int i = 0; i < JS_ArrayLength(js, v); i++) {
if (JS_IsUndefined(js_getpropidx(v,i))) continue; if (JS_IsUndefined(js_getpropidx(v,i))) continue;
ImVec2 c; ImVec2 c;
c.x = i; c.x = i;
@@ -205,13 +196,13 @@ PLOT_FN(digitalplot, PlotDigital,,0)
JSC_SCALL(imgui_barplot, JSC_SCALL(imgui_barplot,
fill_plotdata(js, argv[1], JS_UNDEFINED); fill_plotdata(js, argv[1], JS_UNDEFINED);
ImPlot::PlotBars(str, &plotdata[0].x, &plotdata[0].y, js_arrlen(js, argv[1]), js2number(js, argv[2]), 0, 0, sizeof(ImVec2)); ImPlot::PlotBars(str, &plotdata[0].x, &plotdata[0].y, JS_ArrayLength(js, argv[1]), js2number(js, argv[2]), 0, 0, sizeof(ImVec2));
) )
JSC_SCALL(imgui_histogramplot, JSC_SCALL(imgui_histogramplot,
size_t offset, len, per_e; size_t offset, len, per_e;
JSValue typed = JS_GetTypedArrayBuffer(js, argv[1], &offset, &len, &per_e); JSValue typed = JS_GetTypedArrayBuffer(js, argv[1], &offset, &len, &per_e);
ImPlot::PlotHistogram(str, JS_GetArrayBuffer(js, NULL, typed), js_arrlen(js, argv[1])); ImPlot::PlotHistogram(str, JS_GetArrayBuffer(js, NULL, typed), JS_ArrayLength(js, argv[1]));
JS_FreeValue(js, typed); JS_FreeValue(js, typed);
) )
@@ -290,7 +281,7 @@ JSC_SCALL(imgui_slider,
float high = JS_IsUndefined(argv[3]) ? 1.0 : js2number(js, argv[3]); float high = JS_IsUndefined(argv[3]) ? 1.0 : js2number(js, argv[3]);
if (JS_IsArray(js, argv[1])) { if (JS_IsArray(js, argv[1])) {
int n = js_arrlen(js, argv[1]); int n = JS_ArrayLength(js, argv[1]);
float a[n]; float a[n];
js2floatarr(argv[1], n, a); js2floatarr(argv[1], n, a);
@@ -394,7 +385,7 @@ JSC_SCALL(imgui_tab,
JSC_SCALL(imgui_listbox, JSC_SCALL(imgui_listbox,
/* char **arr = js2strarr(argv[1]); /* char **arr = js2strarr(argv[1]);
int n = js_arrlen(js, argv[1]); int n = JS_ArrayLength(js, argv[1]);
int idx = js2number(js, argv[2]); int idx = js2number(js, argv[2]);
ImGui::ListBox(str, &idx, arr, n, 4); ImGui::ListBox(str, &idx, arr, n, 4);
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
@@ -495,7 +486,7 @@ JSC_SCALL(imgui_dndtarget,
) )
JSC_SCALL(imgui_color, JSC_SCALL(imgui_color,
/* int n = js_arrlen(js, argv[1]); /* int n = JS_ArrayLength(js, argv[1]);
float color[n]; float color[n];
js2floatarr(argv[1],n,color); js2floatarr(argv[1],n,color);

382
source/qjs_io.c Normal file
View File

@@ -0,0 +1,382 @@
#include "qjs_io.h"
#include "jsffi.h"
#include "qjs_macros.h"
#include <physfs.h>
#include <string.h>
#include <stdlib.h>
#include "wildmatch.h"
// Forward declaration for external function
extern void prosperon_mount_core(void);
// Helper function for array length using QuickJS
// JS_ArrayLength removed - use JS_ArrayLength directly
// PHYSFS_File free function
static void PHYSFS_File_free(JSRuntime *rt, PHYSFS_File *f)
{
PHYSFS_close(f);
}
// Class definition for PHYSFS_File
QJSCLASS(PHYSFS_File,)
// Helper function for writing to PHYSFS
static size_t js_physfs_write(JSContext *js, PHYSFS_File *f, JSValue val)
{
size_t len;
size_t wrote;
if (JS_IsString(val)) {
const char *data = JS_ToCStringLen(js,&len,val);
wrote = PHYSFS_writeBytes(f,data,len);
JS_FreeCString(js,data);
} else {
unsigned char *data = JS_GetArrayBuffer(js,&len,val);
wrote = PHYSFS_writeBytes(f,data,len);
}
if (wrote < len) wrote = -1;
return wrote;
}
// Glob data structure for filesystem operations
struct globdata {
JSContext *js;
JSValue arr;
char **globs;
char *glob;
int idx;
int recurse;
};
// Callback for globfs
static int globfs_cb(struct globdata *data, char *dir, char *file)
{
int needfree = 0;
char *path;
if (dir[0] == 0) path = file;
else {
path = malloc(strlen(dir) + strlen(file) + 2);
path[0] = 0;
strcat(path,dir);
strcat(path,"/");
strcat(path,file);
needfree = 1;
}
char **glob = data->globs;
while (*glob != NULL) {
if (wildmatch(*glob, path, WM_WILDSTAR) == WM_MATCH)
goto END;
glob++;
}
PHYSFS_Stat stat;
PHYSFS_stat(path, &stat);
if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY) {
PHYSFS_enumerate(path, globfs_cb, data);
goto END;
}
JS_SetPropertyUint32(data->js, data->arr, data->idx++, JS_NewString(data->js,path));
END:
if (needfree) free(path);
return 1;
}
// Callback for enumerate
static int enumerate_cb(void *udata, const char *dir, const char *fname)
{
struct globdata *data = (struct globdata*)udata;
char *path;
int needfree = 0;
if (dir[0] == 0) path = (char*)fname;
else {
size_t dlen = strlen(dir);
int ends_slash = (dlen && dir[dlen - 1] == '/');
path = malloc(dlen + strlen(fname) + (ends_slash ? 1 : 2));
if (ends_slash) sprintf(path, "%s%s", dir, fname); else sprintf(path, "%s/%s", dir, fname);
needfree = 1;
}
PHYSFS_Stat st;
if (!PHYSFS_stat(path, &st)) {
if (needfree) free(path);
return 1;
}
/* If it's a directory and we're recursing, enumerate it further. */
if (st.filetype == PHYSFS_FILETYPE_DIRECTORY && data->recurse)
PHYSFS_enumerate(path, enumerate_cb, data);
/* Add this item (file or directory) to the JS array. */
JS_SetPropertyUint32(data->js, data->arr, data->idx++, JS_NewString(data->js, path));
if (needfree) free(path);
return 1; /* continue enumerating */
}
// I/O FUNCTIONS
JSC_SCALL(io_rm,
if (!PHYSFS_delete(str)) ret = JS_ThrowReferenceError(js,"could not remove %s. %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_mkdir,
if (!PHYSFS_mkdir(str)) ret = JS_ThrowReferenceError(js,"could not make directory %s. %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_exists, ret = JS_NewBool(js,PHYSFS_exists(str)); )
JSC_SCALL(io_stat,
PHYSFS_Stat stat;
if (!PHYSFS_stat(str, &stat))
return JS_ThrowReferenceError(js, "%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
ret = JS_NewObject(js);
JS_SetPropertyStr(js,ret,"filesize", number2js(js,stat.filesize));
JS_SetPropertyStr(js,ret,"modtime", number2js(js,stat.modtime));
JS_SetPropertyStr(js,ret,"createtime", number2js(js,stat.createtime));
JS_SetPropertyStr(js,ret,"accesstime", number2js(js,stat.accesstime));
)
JSC_SCALL(io_slurpbytes,
PHYSFS_File *f = PHYSFS_openRead(str);
if (!f) {
ret = JS_ThrowReferenceError(js,"physfs error when slurping %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
goto END;
}
PHYSFS_Stat stat;
PHYSFS_stat(str,&stat);
void *data = malloc(stat.filesize);
PHYSFS_readBytes(f,data,stat.filesize);
PHYSFS_close(f);
ret = JS_NewArrayBufferCopy(js,data,stat.filesize);
END:
)
JSC_SCALL(io_slurp,
PHYSFS_File *f = PHYSFS_openRead(str);
if (!f) {
ret = JS_ThrowReferenceError(js,"physfs error when slurping %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
goto END;
}
PHYSFS_Stat stat;
PHYSFS_stat(str,&stat);
void *data = malloc(stat.filesize);
PHYSFS_readBytes(f,data,stat.filesize);
PHYSFS_close(f);
ret = JS_NewStringLen(js,data, stat.filesize);
free(data);
END:
)
JSC_SCALL(io_slurpwrite,
PHYSFS_File *f = PHYSFS_openWrite(str);
if (!f) {
ret = JS_ThrowReferenceError(js,"could not write to %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
goto END;
}
size_t wrote = js_physfs_write(js,f,argv[1]);
PHYSFS_close(f);
if (wrote == -1)
ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
END:
)
JSC_SSCALL(io_mount,
if (!PHYSFS_mount(str,str2,0)) ret = JS_ThrowReferenceError(js,"Unable to mount %s at %s: %s", str, str2, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_unmount,
if (!PHYSFS_unmount(str)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_writepath,
if (!PHYSFS_setWriteDir(str)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SSCALL(io_match,
if (wildmatch(str, str2, WM_PATHNAME | WM_PERIOD | WM_WILDSTAR) == WM_MATCH)
ret = JS_NewBool(js,1);
else
ret = JS_NewBool(js,0);
)
JSC_CCALL(io_globfs,
ret = JS_NewArray(js);
struct globdata data;
data.js = js;
data.arr = ret;
data.idx = 0;
int globs_len = JS_ArrayLength(js,argv[0]);
const char *globs[globs_len+1];
for (int i = 0; i < globs_len; i++) {
JSValue g = JS_GetPropertyUint32(js,argv[0],i);
globs[i] = JS_ToCString(js,g);
JS_FreeValue(js,g);
}
globs[globs_len] = NULL;
data.globs = globs;
const char *path = NULL;
if (!JS_IsUndefined(argv[1])) path = JS_ToCString(js,argv[1]);
PHYSFS_enumerate(path, globfs_cb, &data);
for (int i = 0; i < globs_len; i++)
JS_FreeCString(js,globs[i]);
ret = data.arr;
JS_FreeCString(js,path);
)
JSC_SCALL(io_enumerate,
/* First argument: str (directory name) */
/* Second argument: boolean => recurse or not */
ret = JS_NewArray(js);
struct globdata data;
data.js = js;
data.arr = ret;
data.idx = 0;
data.glob = NULL; /* not used here */
data.globs = NULL; /* not used here */
data.recurse = 0;
if (!JS_IsUndefined(argv[1])) /* parse second arg if provided */
data.recurse = JS_ToBool(js, argv[1]);
/* Enumerate the directory given by 'str'. */
PHYSFS_enumerate(str, enumerate_cb, &data);
/* Return the JS array we filled. */
ret = data.arr;
)
JSC_CCALL(io_basedir, return JS_NewString(js,PHYSFS_getBaseDir()))
JSC_SSCALL(io_prefdir, return JS_NewString(js,PHYSFS_getPrefDir(str, str2)))
JSC_SCALL(io_open,
PHYSFS_File *f = PHYSFS_openWrite(str);
if (!f)
ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
else
ret = PHYSFS_File2js(js,f);
)
JSC_SCALL(io_realdir,
const char *real = PHYSFS_getRealDir(str);
if (!real)
ret = JS_UNDEFINED;
else
ret = JS_NewString(js,real);
)
JSC_CCALL(io_searchpath,
ret = JS_NewArray(js);
char **paths = PHYSFS_getSearchPath();
for (int i = 0; paths[i] != NULL; i++)
JS_SetPropertyUint32(js,ret,i,JS_NewString(js,paths[i]));
)
JSC_CCALL(io_mount_core,
int mount = JS_ToBool(js,argv[0]);
if (!mount)
PHYSFS_unmount("core.zip");
else
prosperon_mount_core();
)
JSC_SCALL(io_is_directory,
PHYSFS_Stat stat;
int good = PHYSFS_stat(str, &stat);
if (!good)
ret = JS_NewBool(js, 0);
else
ret = JS_NewBool(js, stat.filetype == PHYSFS_FILETYPE_DIRECTORY);
)
// FILE FUNCTIONS
JSC_CCALL(file_close,
PHYSFS_File *f = js2PHYSFS_File(js,self);
PHYSFS_close(f);
)
JSC_CCALL(file_write,
PHYSFS_File *f = js2PHYSFS_File(js,self);
size_t wrote = js_physfs_write(js,f,argv[0]);
if (wrote == -1)
return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_CCALL(file_buffer,
PHYSFS_File *f = js2PHYSFS_File(js,self);
size_t size = js2number(js,argv[0]);
if (!PHYSFS_setBuffer(f,size))
return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_CCALL(file_tell,
PHYSFS_File *f = js2PHYSFS_File(js,self);
size_t tell = PHYSFS_tell(f);
if (tell == -1)
return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return number2js(js,tell);
)
JSC_CCALL(file_eof,
PHYSFS_File *f = js2PHYSFS_File(js,self);
return JS_NewBool(js, PHYSFS_eof(f));
)
static const JSCFunctionListEntry js_io_funcs[] = {
MIST_FUNC_DEF(io, rm, 1),
MIST_FUNC_DEF(io, mkdir, 1),
MIST_FUNC_DEF(io,stat,1),
MIST_FUNC_DEF(io, globfs, 2),
MIST_FUNC_DEF(io, match, 2),
MIST_FUNC_DEF(io, exists, 1),
MIST_FUNC_DEF(io, mount, 2),
MIST_FUNC_DEF(io,unmount,1),
MIST_FUNC_DEF(io,slurp,1),
MIST_FUNC_DEF(io,slurpbytes,1),
MIST_FUNC_DEF(io,slurpwrite,2),
MIST_FUNC_DEF(io,writepath, 1),
MIST_FUNC_DEF(io,basedir, 0),
MIST_FUNC_DEF(io, prefdir, 2),
MIST_FUNC_DEF(io, realdir, 1),
MIST_FUNC_DEF(io, open, 1),
MIST_FUNC_DEF(io, searchpath, 0),
MIST_FUNC_DEF(io, enumerate, 2),
MIST_FUNC_DEF(io, mount_core, 1),
MIST_FUNC_DEF(io, is_directory, 1),
};
static const JSCFunctionListEntry js_PHYSFS_File_funcs[] = {
MIST_FUNC_DEF(file, close, 0),
MIST_FUNC_DEF(file, write, 1),
MIST_FUNC_DEF(file, buffer, 1),
MIST_FUNC_DEF(file, tell, 0),
MIST_FUNC_DEF(file, eof, 0),
};
JSValue js_io_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_io_funcs,countof(js_io_funcs));
return mod;
}
// Note: PHYSFS_File class functions are registered via QJSCLASSPREP_FUNCS(PHYSFS_File) in main FFI loading

8
source/qjs_io.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef QJS_IO_H
#define QJS_IO_H
#include "quickjs.h"
JSValue js_io_use(JSContext *ctx);
#endif /* QJS_IO_H */

View File

@@ -21,6 +21,12 @@
return ret; \ return ret; \
} }
#define JSC_CCALL_EXTERN(NAME, ...) JSValue js_##NAME (JSContext *js, JSValue self, int argc, JSValue *argv) { \
JSValue ret = JS_UNDEFINED; \
__VA_ARGS__ ;\
return ret; \
}
#define JSC_SCALL(NAME, ...) JSC_CCALL(NAME, \ #define JSC_SCALL(NAME, ...) JSC_CCALL(NAME, \
const char *str = JS_ToCString(js,argv[0]); \ const char *str = JS_ToCString(js,argv[0]); \
__VA_ARGS__ ;\ __VA_ARGS__ ;\
@@ -136,6 +142,29 @@ JSValue TYPE##2js(JSContext *js, TYPE *n) { \
return j; }\ return j; }\
\ \
#define QJSCLASSMARK_EXTERN(TYPE, ...)\
static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\
TYPE *n = JS_GetOpaque(val, js_##TYPE##_id);\
TracyCFreeN(n, #TYPE); \
TYPE##_free(rt,n);}\
static JSClassDef js_##TYPE##_class = {\
.class_name = #TYPE,\
.finalizer = js_##TYPE##_finalizer,\
.gc_mark = js_##TYPE##_mark,\
};\
extern JSClassID js_##TYPE##_id;\
TYPE *js2##TYPE (JSContext *js, JSValue val) { \
if (JS_GetClassID(val) != js_##TYPE##_id) return NULL; \
return JS_GetOpaque(val,js_##TYPE##_id); \
}\
JSValue TYPE##2js(JSContext *js, TYPE *n) { \
JSValue j = JS_NewObjectClass(js,js_##TYPE##_id);\
JS_SetOpaque(j,n);\
__VA_ARGS__ \
TracyCAllocN(n, 1, #TYPE); \
return j; }\
\
#define QJSGLOBALCLASS(NAME) \ #define QJSGLOBALCLASS(NAME) \
JSValue NAME = JS_NewObject(js); \ JSValue NAME = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \ JS_SetPropertyFunctionList(js, NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \
@@ -153,11 +182,6 @@ JSValue TYPE##_proto = JS_NewObject(js); \
JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \ JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \
JS_SetPropertyStr(js, c_types, #TYPE, JS_DupValue(js,TYPE##_proto)); \ JS_SetPropertyStr(js, c_types, #TYPE, JS_DupValue(js,TYPE##_proto)); \
#define MISTUSE(NAME) \
JSValue js_##NAME##_use(JSContext *js) { \
JSValue mod = JS_NewObject(js); \
JS_SetPropertyFunctionList(js,mod,js_##NAME##_funcs,countof(js_##NAME##_funcs)); \
return mod; } \
#define countof(x) (sizeof(x)/sizeof((x)[0])) #define countof(x) (sizeof(x)/sizeof((x)[0]))

509
source/qjs_math.c Normal file
View File

@@ -0,0 +1,509 @@
#include "qjs_math.h"
#include "jsffi.h"
#include "qjs_macros.h"
#include <math.h>
#include <stdlib.h>
#include "HandmadeMath.h"
#include "prosperon.h"
// Utility function to get number from array index
static double js_getnum_uint32(JSContext *js, JSValue v, unsigned int i)
{
JSValue val = JS_GetPropertyUint32(js,v,i);
double ret = js2number(js, val);
JS_FreeValue(js,val);
return ret;
}
// Convert JS array to float array
static float *js2floats(JSContext *js, JSValue v, size_t *len)
{
*len = JS_ArrayLength(js,v);
float *arr = malloc(sizeof(float)* *len);
for (int i = 0; i < *len; i++)
arr[i] = js_getnum_uint32(js,v,i);
return arr;
}
// Convert float array to JS array
static JSValue floats2array(JSContext *js, float *vals, size_t len) {
JSValue arr = JS_NewArray(js);
for (size_t i = 0; i < len; i++) {
JS_SetPropertyUint32(js, arr, i, number2js(js, vals[i]));
}
return arr;
}
// Calculate vector length
static double arr_vec_length(JSContext *js,JSValue v)
{
int len = JS_ArrayLength(js,v);
switch(len) {
case 2: return HMM_LenV2(js2vec2(js,v));
case 3: return HMM_LenV3(js2vec3(js,v));
case 4: return HMM_LenV4(js2vec4(js,v));
}
double sum = 0;
for (int i = 0; i < len; i++)
sum += pow(js_getnum_uint32(js, v, i), 2);
return sqrt(sum);
}
// GCD helper function
static int gcd(int a, int b) {
if (b == 0)
return a;
return gcd(b, a % b);
}
// MATH FUNCTIONS
JSC_CCALL(math_rotate,
HMM_Vec2 vec = js2vec2(js,argv[0]);
double angle = js2angle(js, argv[1]);
HMM_Vec2 pivot = JS_IsUndefined(argv[2]) ? v2zero : js2vec2(js,argv[2]);
vec = HMM_SubV2(vec,pivot);
float r = HMM_LenV2(vec);
angle += atan2f(vec.y, vec.x);
vec.x = r * cosf(angle);
vec.y = r * sinf(angle);
vec = HMM_AddV2(vec,pivot);
return vec22js(js, vec);
)
JSC_CCALL(math_norm,
int len = JS_ArrayLength(js,argv[0]);
switch(len) {
case 2: return vec22js(js,HMM_NormV2(js2vec2(js,argv[0])));
case 3: return vec32js(js, HMM_NormV3(js2vec3(js,argv[0])));
case 4: return vec42js(js,HMM_NormV4(js2vec4(js,argv[0])));
}
double length = arr_vec_length(js,argv[0]);
JSValue newarr = JS_NewArray(js);
for (int i = 0; i < len; i++)
JS_SetPropertyUint32(js, newarr, i, number2js(js,js_getnum_uint32(js, argv[0],i)/length));
ret = newarr;
)
JSC_CCALL(math_angle_between,
int len = JS_ArrayLength(js,argv[0]);
switch(len) {
case 2: return angle2js(js,HMM_AngleV2(js2vec2(js,argv[0]), js2vec2(js,argv[1])));
case 3: return angle2js(js,HMM_AngleV3(js2vec3(js,argv[0]), js2vec3(js,argv[1])));
case 4: return angle2js(js,HMM_AngleV4(js2vec4(js,argv[0]), js2vec4(js,argv[1])));
}
return JS_ThrowReferenceError(js, "Input array must have a length between 2 and 4.");
)
JSC_CCALL(math_lerp,
double s = js2number(js,argv[0]);
double f = js2number(js,argv[1]);
double t = js2number(js,argv[2]);
ret = number2js(js,(f-s)*t+s);
)
JSC_CCALL(math_gcd, ret = number2js(js,gcd(js2number(js,argv[0]), js2number(js,argv[1]))); )
JSC_CCALL(math_lcm,
double a = js2number(js,argv[0]);
double b = js2number(js,argv[1]);
ret = number2js(js,(a*b)/gcd(a,b));
)
JSC_CCALL(math_clamp,
double x = js2number(js,argv[0]);
double l = js2number(js,argv[1]);
double h = js2number(js,argv[2]);
return number2js(js,x > h ? h : x < l ? l : x);
)
JSC_CCALL(math_angledist,
double a1 = js2number(js,argv[0]);
double a2 = js2number(js,argv[1]);
a1 = fmod(a1,1);
a2 = fmod(a2,1);
double dist = a2-a1;
if (dist == 0) return number2js(js,dist);
if (dist > 0) {
if (dist > 0.5) return number2js(js,dist-1);
return number2js(js,dist);
}
if (dist < -0.5) return number2js(js,dist+1);
return number2js(js,dist);
)
JSC_CCALL(math_jitter,
double n = js2number(js,argv[0]);
double pct = js2number(js,argv[1]);
return number2js(js,n + (rand_range(js,-pct,pct)*n));
)
JSC_CCALL(math_mean,
double len = JS_ArrayLength(js,argv[0]);
double sum = 0;
for (int i = 0; i < len; i++)
sum += js_getnum_uint32(js, argv[0], i);
return number2js(js,sum/len);
)
JSC_CCALL(math_sum,
double sum = 0.0;
int len = JS_ArrayLength(js,argv[0]);
for (int i = 0; i < len; i++)
sum += js_getnum_uint32(js, argv[0], i);
return number2js(js,sum);
)
JSC_CCALL(math_sigma,
int len = JS_ArrayLength(js,argv[0]);
double sum = 0;
for (int i = 0; i < len; i++)
sum += js_getnum_uint32(js, argv[0], i);
double mean = sum/(double)len;
sum = 0;
for (int i = 0; i < len; i++)
sum += pow(js_getnum_uint32(js, argv[0], i) - mean, 2);
return number2js(js,sqrt(sum/len));
)
JSC_CCALL(math_median,
int len = JS_ArrayLength(js,argv[0]);
double vals[len];
for (int i = 0; i < len; i++)
vals[i] = js_getnum_uint32(js, argv[0], i);
// Simple bubble sort for median calculation
for (int i = 0; i < len-1; i++) {
for (int j = 0; j < len-i-1; j++) {
if (vals[j] > vals[j+1]) {
double temp = vals[j];
vals[j] = vals[j+1];
vals[j+1] = temp;
}
}
}
if (len % 2 == 0)
return number2js(js,(vals[len/2-1] + vals[len/2]) / 2.0);
else
return number2js(js,vals[len/2]);
)
JSC_CCALL(math_length, return number2js(js,arr_vec_length(js,argv[0])); )
JSC_CCALL(math_from_to,
double start = js2number(js,argv[0]);
double end = js2number(js,argv[1]);
double step = js2number(js,argv[2]);
int inclusive = JS_ToBool(js,argv[3]);
int arr = JS_ToBool(js,argv[4]);
JSValue jsarr = JS_NewArray(js);
int i = 0;
for (double val = start; val <= end; val += step) {
if (val == end && !inclusive) break;
JS_SetPropertyUint32(js, jsarr, i++, number2js(js, val));
}
return jsarr;
)
JSC_CCALL(math_rand,
MTRand *mrand = &((prosperon_rt*)JS_GetContextOpaque(js))->mrand;
return number2js(js, genRand(mrand));
)
JSC_CCALL(math_randi,
MTRand *mrand = &((prosperon_rt*)JS_GetContextOpaque(js))->mrand;
return number2js(js, genRandLong(mrand));
)
JSC_CCALL(math_srand,
MTRand *mrand = &((prosperon_rt*)JS_GetContextOpaque(js))->mrand;
m_seedRand(mrand, js2number(js,argv[0]));
)
JSC_CCALL(math_dot,
size_t alen, blen;
float *a = js2floats(js,argv[0], &alen);
float *b = js2floats(js,argv[1], &blen);
float dot = 0;
size_t len = alen < blen? alen : blen;
for (size_t i = 0; i < len; i++)
dot += a[i] * b[i];
free(a);
free(b);
return number2js(js,dot);
)
JSC_CCALL(math_project,
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
}
// We'll work up to the smaller length
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
}
// Compute dot products: a·b and b·b
float ab = 0, bb = 0;
for (size_t i = 0; i < len; i++) {
ab += a[i] * b[i];
bb += b[i] * b[i];
}
// Build the result array
float *proj = (float*)malloc(sizeof(float) * len);
if (!proj) {
free(a);
free(b);
return JS_EXCEPTION; // or some error
}
float scale = (bb != 0.0f) ? (ab / bb) : 0.0f;
for (size_t i = 0; i < len; i++)
proj[i] = scale * b[i];
ret = floats2array(js, proj, len);
free(a);
free(b);
free(proj);
)
JSC_CCALL(math_midpoint,
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
}
float *m = (float*)malloc(sizeof(float) * len);
if (!m) {
free(a);
free(b);
return JS_EXCEPTION;
}
for (size_t i = 0; i < len; i++)
m[i] = (a[i] + b[i]) * 0.5f;
ret = floats2array(js, m, len);
free(a);
free(b);
free(m);
)
JSC_CCALL(math_reflect,
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
}
// Reflect vector a across normal b
// reflection = a - 2 * (a·b) * b
float ab = 0, bb = 0;
for (size_t i = 0; i < len; i++) {
ab += a[i] * b[i];
bb += b[i] * b[i];
}
float *result = (float*)malloc(sizeof(float) * len);
if (!result) {
free(a);
free(b);
return JS_EXCEPTION;
}
float scale = (bb != 0.0f) ? (2.0f * ab / bb) : 0.0f;
for (size_t i = 0; i < len; i++)
result[i] = a[i] - scale * b[i];
ret = floats2array(js, result, len);
free(a);
free(b);
free(result);
)
JSC_CCALL(math_direction,
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
}
// Direction vector from a to b (normalized)
float *dir = (float*)malloc(sizeof(float) * len);
if (!dir) {
free(a);
free(b);
return JS_EXCEPTION;
}
float mag = 0;
for (size_t i = 0; i < len; i++) {
dir[i] = b[i] - a[i];
mag += dir[i] * dir[i];
}
mag = sqrtf(mag);
if (mag > 0.0f) {
for (size_t i = 0; i < len; i++)
dir[i] /= mag;
}
ret = floats2array(js, dir, len);
free(a);
free(b);
free(dir);
)
JSC_CCALL(math_angle,
size_t len;
float *v = js2floats(js, argv[0], &len);
if (!v || len < 2) {
free(v);
return JS_UNDEFINED;
}
// Return angle in radians for 2D vector
ret = number2js(js, atan2f(v[1], v[0]));
free(v);
)
JSC_CCALL(math_distance,
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
}
float distSq = 0.0f;
for (size_t i = 0; i < len; i++) {
float diff = b[i] - a[i];
distSq += diff * diff;
}
float dist = sqrtf(distSq);
free(a);
free(b);
return number2js(js, dist);
)
static const JSCFunctionListEntry js_math_funcs[] = {
MIST_FUNC_DEF(math, dot,2),
MIST_FUNC_DEF(math, project,2),
MIST_FUNC_DEF(math, rotate, 3),
MIST_FUNC_DEF(math, midpoint, 2),
MIST_FUNC_DEF(math, reflect, 2),
MIST_FUNC_DEF(math, distance, 2),
MIST_FUNC_DEF(math, direction, 2),
MIST_FUNC_DEF(math, angle, 1),
MIST_FUNC_DEF(math, norm, 1),
MIST_FUNC_DEF(math, angle_between, 2),
MIST_FUNC_DEF(math, lerp, 3),
MIST_FUNC_DEF(math, gcd, 2),
MIST_FUNC_DEF(math, lcm, 2),
MIST_FUNC_DEF(math, clamp, 3),
MIST_FUNC_DEF(math, angledist, 2),
MIST_FUNC_DEF(math, jitter, 2),
MIST_FUNC_DEF(math, mean, 1),
MIST_FUNC_DEF(math, sum, 1),
MIST_FUNC_DEF(math, sigma, 1),
MIST_FUNC_DEF(math, median, 1),
MIST_FUNC_DEF(math, length, 1),
MIST_FUNC_DEF(math, from_to, 5),
MIST_FUNC_DEF(math, rand, 0),
MIST_FUNC_DEF(math, randi, 0),
MIST_FUNC_DEF(math, srand,0),
};
JSValue js_math_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_math_funcs,countof(js_math_funcs));
return mod;
}

8
source/qjs_math.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef QJS_MATH_H
#define QJS_MATH_H
#include "quickjs.h"
JSValue js_math_use(JSContext *ctx);
#endif /* QJS_MATH_H */

489
source/qjs_os.c Normal file
View File

@@ -0,0 +1,489 @@
#include "qjs_os.h"
#include "jsffi.h"
#include "qjs_macros.h"
#include "qjs_wota.h"
#include "prosperon.h"
#include "transform.h"
#include <SDL3/SDL.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include <signal.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/utsname.h>
#ifdef __linux__
#include <sys/sysinfo.h>
#endif
#endif
// External variables
extern int trace;
extern prosperon_rt *io_actor;
// External function declarations
extern JSClassID js_transform_id;
JSValue transform2js(JSContext *js, transform *t);
transform *js2transform(JSContext *js, JSValue v);
// OS FUNCTIONS
JSC_SCALL(os_openurl,
if (SDL_OpenURL(str) < 0)
ret = JS_ThrowInternalError(js, "%s", SDL_GetError());
)
JSC_SCALL(os_env,
char *env = getenv(str);
if (env) ret = JS_NewString(js,env);
)
JSC_CCALL(os_exit, exit(js2number(js,argv[0]));)
JSC_CCALL(os_now, return number2js(js, (double)SDL_GetTicksNS()/1000000000.0))
JSC_SCALL(os_system, ret = number2js(js,system(str)); )
JSC_SCALL(os_kill,
pid_t pid = js2number(js,argv[0]);
if (kill(pid, SIGTERM) < 0)
ret = JS_ThrowReferenceError(js,"could not kill process");
)
JSC_CCALL(os_sleep,
double secs = js2number(js,argv[0]);
int ms = secs*1000;
SDL_Delay(ms);
)
JSC_CCALL(os_battery_pct,
int pct;
SDL_PowerState state = SDL_GetPowerInfo(&pct, NULL);
return number2js(js,pct);
)
JSC_CCALL(os_battery_voltage,
return number2js(js,0);
)
JSC_CCALL(os_battery_seconds,
int secs;
SDL_PowerState state = SDL_GetPowerInfo(NULL, &secs);
return number2js(js, secs);
)
JSC_CCALL(os_power_state,
int pct, secs;
SDL_PowerState state = SDL_GetPowerInfo(&pct, &secs);
const char *statestr = "unknown";
switch(state) {
case SDL_POWERSTATE_ON_BATTERY: statestr = "battery"; break;
case SDL_POWERSTATE_NO_BATTERY: statestr = "no battery"; break;
case SDL_POWERSTATE_CHARGING: statestr = "charging"; break;
case SDL_POWERSTATE_CHARGED: statestr = "charged"; break;
}
ret = JS_NewObject(js);
JS_SetPropertyStr(js,ret,"state",JS_NewString(js,statestr));
JS_SetPropertyStr(js,ret,"percent",number2js(js,pct));
JS_SetPropertyStr(js,ret,"seconds",number2js(js,secs));
)
JSC_CCALL(os_totalmem, return number2js(js, SDL_GetSystemRAM()))
JSC_CCALL(os_platform, return JS_NewString(js,SDL_GetPlatform()))
JSC_CCALL(os_hostname,
#ifdef _WIN32
TCHAR buffer[256] = TEXT("");
DWORD size = sizeof(buffer) / sizeof(TCHAR);
GetComputerName(buffer, &size);
return JS_NewString(js, buffer);
#else
struct utsname buffer;
if (uname(&buffer) != 0) return JS_ThrowReferenceError(js,"Could not get hostname.");
return JS_NewString(js, buffer.nodename);
#endif
)
JSC_CCALL(os_make_transform,
transform *t = make_transform();
ret = transform2js(js,t);
// t->self = JS_DupValue(js,ret);
t->self = ret;
)
JSC_CCALL(os_clean_transforms,
clean_all(js);
)
JSC_CCALL(os_arch,
#if defined(__x86_64__) || defined(_M_X64)
return JS_NewString(js,"x64");
#elif defined(__aarch64__) || defined(_M_ARM64)
return JS_NewString(js,"arm64");
#elif defined(__arm__) || defined(_M_ARM)
return JS_NewString(js,"arm");
#elif defined(__i386__) || defined(_M_IX86)
return JS_NewString(js,"ia32");
#elif defined(__loongarch__) || defined(__loongarch32) || defined(__loongarch64)
return JS_NewString(js,"loong64");
#elif defined(__mips__) || defined(__mips) || defined(_M_MIPS)
// You might want to distinguish mips vs mipsel
return JS_NewString(js,"mips");
#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_M_PPC)
// You might want to distinguish ppc vs ppc64, big-endian vs little-endian
return JS_NewString(js,"ppc64");
#elif defined(__riscv) && __riscv_xlen == 64
return JS_NewString(js,"riscv64");
#elif defined(__s390x__)
return JS_NewString(js,"s390x");
#else
return JS_NewString(js,"unknown");
#endif
)
JSC_CCALL(os_freemem,
#ifdef _WIN32
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);
if (!GlobalMemoryStatusEx(&statex)) return JS_ThrowInternalError(js,"GlobalMemoryStatusEx failed");
return JS_NewInt64(js,(int64_t)statex.ullAvailPhys);
#elif defined(__linux__)
struct sysinfo info;
if (sysinfo(&info) == 0)
return JS_NewInt64(js,(int64_t)info.freeram * info.mem_unit);
return JS_NewInt64(js,0);
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
// A very rough fallback using the same sysconf approach
// (macOS or *BSD typically need specialized APIs to get free mem accurately)
// This is often only "unused" pages, ignoring caches, etc.
long pages = sysconf(_SC_AVPHYS_PAGES);
long page_size = sysconf(_SC_PAGE_SIZE);
if (pages < 0 || page_size < 0) return JS_NewInt64(js,0);
return JS_NewInt64(js,(int64_t)pages * (int64_t)page_size);
#else
// Fallback: unknown
return JS_NewInt64(js,0);
#endif
)
JSC_CCALL(os_version,
#ifdef _WIN32
typedef LONG (WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
HMODULE h = GetModuleHandleA("ntdll.dll");
if (h) {
RtlGetVersionPtr fx = (RtlGetVersionPtr)GetProcAddress(h, "RtlGetVersion");
if (fx) {
RTL_OSVERSIONINFOW ver;
memset(&ver, 0, sizeof(ver));
ver.dwOSVersionInfoSize = sizeof(ver);
if (!fx(&ver)) {
char buf[128];
sprintf(buf, "%u.%u.%u",
(unsigned)ver.dwMajorVersion,
(unsigned)ver.dwMinorVersion,
(unsigned)ver.dwBuildNumber
);
return JS_NewString(js, buf);
}
}
}
OSVERSIONINFOW wver;
memset(&wver, 0, sizeof(wver));
wver.dwOSVersionInfoSize = sizeof(wver);
if (GetVersionExW(&wver)) {
char buf[128];
sprintf(buf, "%u.%u.%u",
(unsigned)wver.dwMajorVersion,
(unsigned)wver.dwMinorVersion,
(unsigned)wver.dwBuildNumber
);
return JS_NewString(js, buf);
}
return JS_NewString(js, "Windows_Unknown");
#else
struct utsname info;
if (!uname(&info)) return JS_NewString(js, info.release);
return JS_NewString(js, "");
#endif
)
JSValue js_os_get_trace(JSContext *js, JSValue self)
{
return JS_NewBool(js, trace);
}
JSValue js_os_set_trace(JSContext *js, JSValue self, JSValue value)
{
trace = JS_ToBool(js, value);
return JS_UNDEFINED;
}
JSC_CCALL(os_on,
prosperon_rt *rt = JS_GetContextOpaque(js);
JS_FreeValue(js, rt->on_exception);
rt->on_exception = JS_DupValue(js,argv[1]);
)
#define JSOBJ_ADD_FIELD(OBJ, STRUCT, FIELD, TYPE) \
JS_SetPropertyStr(js, OBJ, #FIELD, TYPE##2js(js,STRUCT.FIELD));\
#define JSJMEMRET(FIELD) JSOBJ_ADD_FIELD(ret, jsmem, FIELD, number)
JSC_CCALL(os_mallinfo,
ret = JS_UNDEFINED;
/*struct mallinfo jsmem = mallinfo();
ret = JS_NewObject(js);
JSJMEMRET(arena);
JSJMEMRET(ordblks);
JSJMEMRET(smblks);
JSJMEMRET(hblks);
JSJMEMRET(hblkhd);
JSJMEMRET(usmblks);
JSJMEMRET(fsmblks);
JSJMEMRET(uordblks);
JSJMEMRET(fordblks);
JSJMEMRET(keepcost);*/
)
JSC_CCALL(os_rusage,
ret = JS_NewObject(js);
#ifndef _WIN32
struct rusage jsmem;
getrusage(RUSAGE_SELF, &jsmem);
JSJMEMRET(ru_maxrss);
JSJMEMRET(ru_ixrss);
JSJMEMRET(ru_idrss);
JSJMEMRET(ru_isrss);
JSJMEMRET(ru_minflt);
JSJMEMRET(ru_majflt);
JSJMEMRET(ru_nswap);
JSJMEMRET(ru_inblock);
JSJMEMRET(ru_oublock);
JSJMEMRET(ru_msgsnd);
JSJMEMRET(ru_msgrcv);
JSJMEMRET(ru_nsignals);
JSJMEMRET(ru_nvcsw);
JSJMEMRET(ru_nivcsw);
#endif
return ret;
)
JSC_CCALL(os_createprocess,
int ac = JS_ArrayLength(js,argv[0]);
const char *args[ac+1];
for (int i = 0; i < ac; i++) {
JSValue astr = JS_GetPropertyUint32(js,argv[0],i);
args[i] = JS_ToCString(js,astr);
JS_FreeValue(js,astr);
}
args[ac] = NULL;
SDL_Process *actor = SDL_CreateProcess(args, 0);
for (int i = 0; i < ac; i++)
JS_FreeCString(js,args[i]);
if (!actor)
return JS_ThrowReferenceError(js, "Unable to create process: %s\n", SDL_GetError());
)
JSC_CCALL(os_createactor,
int margc = JS_ArrayLength(js, argv[0]);
char **margv = malloc(margc*sizeof(char*));
for (int i = 0; i < margc; i++) {
JSValue val = JS_GetPropertyUint32(js, argv[0], i);
const char *cstr = JS_ToCString(js,val);
margv[i] = strdup(cstr);
JS_FreeCString(js,cstr);
JS_FreeValue(js,val);
}
create_actor(margc, margv);
)
JSC_CCALL(os_mailbox_push,
if (argc < 2) return JS_ThrowInternalError(js, "Need an actor and a message");
if (!JS_IsObject(argv[1])) return JS_ThrowInternalError(js, "Object to push must be an object.");
const char *id = JS_ToCString(js, argv[0]);
int exist = actor_exists(id);
JS_FreeCString(js,id);
if (!exist)
return JS_ThrowInternalError(js, "No mailbox found for given ID");
void *data = value2wota(js, argv[1], JS_UNDEFINED);
const 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,
prosperon_rt *rt = JS_GetContextOpaque(js);
const char *id = JS_ToCString(js, argv[0]);
const char *err = register_actor(id, rt, JS_ToBool(js, argv[2]));
if (err) return JS_ThrowInternalError(js, "Could not register actor: %s", err);
rt->message_handle = JS_DupValue(js, argv[1]);
rt->context = js;
JS_FreeCString(js, id);
)
JSC_CCALL(os_mailbox_exist,
const char *id = JS_ToCString(js, argv[0]);
int exist = actor_exists(id);
JS_FreeCString(js, id);
return JS_NewBool(js, exist);
)
JSC_CCALL(os_unneeded,
prosperon_rt *actor = JS_GetContextOpaque(js);
SDL_LockMutex(actor->msg_mutex);
JS_FreeValue(js, actor->unneeded);
if (!JS_IsFunction(js, argv[0])) {
actor->unneeded = JS_UNDEFINED;
goto END;
}
actor->unneeded = JS_DupValue(js, argv[0]);
JS_ToFloat64(js, &actor->unneeded_secs, argv[1]);
END:
if (actor->ar) {
SDL_RemoveTimer(actor->ar);
actor->ar = 0;
}
set_actor_state(actor);
SDL_UnlockMutex(actor->msg_mutex);
)
JSC_CCALL(os_destroy,
prosperon_rt *rt = JS_GetContextOpaque(js);
rt->need_stop = 1;
)
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.");
prosperon_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) {
prosperon_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;
}
JSValue js_os_ioactor(JSContext *js, JSValue self, int argc, JSValue *argv) {
return JS_NewString(js, io_actor->id);
}
static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_transform, 0),
MIST_FUNC_DEF(os, clean_transforms, 0),
MIST_FUNC_DEF(os, platform, 0),
MIST_FUNC_DEF(os, arch, 0),
MIST_FUNC_DEF(os, totalmem, 0),
MIST_FUNC_DEF(os, freemem, 0),
MIST_FUNC_DEF(os, hostname, 0),
MIST_FUNC_DEF(os, version, 0),
JS_CGETSET_DEF("trace", js_os_get_trace, js_os_set_trace),
MIST_FUNC_DEF(os, kill, 1),
MIST_FUNC_DEF(os, exit, 1),
MIST_FUNC_DEF(os, now, 0),
MIST_FUNC_DEF(os, openurl, 1),
MIST_FUNC_DEF(os, sleep, 1),
MIST_FUNC_DEF(os, battery_pct, 0),
MIST_FUNC_DEF(os, battery_voltage, 0),
MIST_FUNC_DEF(os, battery_seconds, 0),
MIST_FUNC_DEF(os, power_state, 0),
MIST_FUNC_DEF(os, on, 2),
MIST_FUNC_DEF(os, rusage, 0),
MIST_FUNC_DEF(os, mallinfo, 0),
// dangerous ones that need disabled for shipping
MIST_FUNC_DEF(os, env, 1),
MIST_FUNC_DEF(os, system, 1),
MIST_FUNC_DEF(os, createprocess, 0),
MIST_FUNC_DEF(os, createactor, 1),
MIST_FUNC_DEF(os, mailbox_push, 2),
MIST_FUNC_DEF(os, mailbox_exist, 1),
MIST_FUNC_DEF(actor, delay, 2),
MIST_FUNC_DEF(actor, removetimer, 1),
MIST_FUNC_DEF(os, register_actor, 2),
MIST_FUNC_DEF(os, unneeded, 2),
MIST_FUNC_DEF(os, destroy, 0),
MIST_FUNC_DEF(os, ioactor, 0),
};
JSValue js_os_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs));
return mod;
}

8
source/qjs_os.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef QJS_OS_H
#define QJS_OS_H
#include "quickjs.h"
JSValue js_os_use(JSContext *ctx);
#endif /* QJS_OS_H */

View File

@@ -56,7 +56,7 @@ JSC_CCALL(SDL_Renderer_rect,
} }
if (JS_IsArray(js,argv[0])) { if (JS_IsArray(js,argv[0])) {
int len = js_arrlen(js,argv[0]); int len = JS_ArrayLength(js,argv[0]);
rect rects[len]; rect rects[len];
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
JSValue val = JS_GetPropertyUint32(js,argv[0],i); JSValue val = JS_GetPropertyUint32(js,argv[0],i);
@@ -81,8 +81,8 @@ JSC_CCALL(renderer_load_texture,
SDL_Texture *tex = SDL_CreateTextureFromSurface(r,surf); SDL_Texture *tex = SDL_CreateTextureFromSurface(r,surf);
if (!tex) return JS_ThrowReferenceError(js, "Could not create texture from surface: %s", SDL_GetError()); if (!tex) return JS_ThrowReferenceError(js, "Could not create texture from surface: %s", SDL_GetError());
ret = SDL_Texture2js(js,tex); ret = SDL_Texture2js(js,tex);
JS_SetProperty(js,ret,width, number2js(js,tex->w)); JS_SetPropertyStr(js,ret,"width", number2js(js,tex->w));
JS_SetProperty(js,ret,height, number2js(js,tex->h)); JS_SetPropertyStr(js,ret,"height", number2js(js,tex->h));
) )
JSC_CCALL(SDL_Renderer_fillrect, JSC_CCALL(SDL_Renderer_fillrect,
@@ -93,7 +93,7 @@ JSC_CCALL(SDL_Renderer_fillrect,
} }
if (JS_IsArray(js,argv[0])) { if (JS_IsArray(js,argv[0])) {
int len = js_arrlen(js,argv[0]); int len = JS_ArrayLength(js,argv[0]);
rect rects[len]; rect rects[len];
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
JSValue val = JS_GetPropertyUint32(js,argv[0],i); JSValue val = JS_GetPropertyUint32(js,argv[0],i);
@@ -193,7 +193,7 @@ JSC_CCALL(renderer_line,
} }
if (JS_IsArray(js,argv[0])) { if (JS_IsArray(js,argv[0])) {
int len = js_arrlen(js,argv[0]); int len = JS_ArrayLength(js,argv[0]);
HMM_Vec2 points[len]; HMM_Vec2 points[len];
assert(sizeof(HMM_Vec2) == sizeof(SDL_FPoint)); assert(sizeof(HMM_Vec2) == sizeof(SDL_FPoint));
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
@@ -213,7 +213,7 @@ JSC_CCALL(renderer_point,
} }
if (JS_IsArray(js,argv[0])) { if (JS_IsArray(js,argv[0])) {
int len = js_arrlen(js,argv[0]); int len = JS_ArrayLength(js,argv[0]);
HMM_Vec2 points[len]; HMM_Vec2 points[len];
assert(sizeof(HMM_Vec2) ==sizeof(SDL_FPoint)); assert(sizeof(HMM_Vec2) ==sizeof(SDL_FPoint));
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
@@ -393,7 +393,7 @@ JSC_CCALL(renderer_target,
// color: the color this sprite should be hued by // color: the color this sprite should be hued by
JSC_CCALL(renderer_make_sprite_mesh, JSC_CCALL(renderer_make_sprite_mesh,
JSValue sprites = argv[0]; JSValue sprites = argv[0];
size_t quads = js_arrlen(js,argv[0]); size_t quads = JS_ArrayLength(js,argv[0]);
size_t verts = quads*4; size_t verts = quads*4;
size_t count = quads*6; size_t count = quads*6;
@@ -403,10 +403,10 @@ JSC_CCALL(renderer_make_sprite_mesh,
for (int i = 0; i < quads; i++) { for (int i = 0; i < quads; i++) {
JSValue sub = JS_GetPropertyUint32(js,sprites,i); JSValue sub = JS_GetPropertyUint32(js,sprites,i);
JSValue jstransform = JS_GetProperty(js,sub,transform); JSValue jstransform = JS_GetPropertyStr(js,sub,"transform");
JSValue jssrc = JS_GetProperty(js,sub,src); JSValue jssrc = JS_GetPropertyStr(js,sub,"src");
JSValue jscolor = JS_GetProperty(js,sub,color); JSValue jscolor = JS_GetPropertyStr(js,sub,"color");
HMM_Vec4 color; HMM_Vec4 color;
rect src; rect src;
@@ -441,12 +441,12 @@ JSC_CCALL(renderer_make_sprite_mesh,
} }
ret = JS_NewObject(js); ret = JS_NewObject(js);
JS_SetProperty(js, ret, pos, make_gpu_buffer(js, posdata, sizeof(*posdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0)); JS_SetPropertyStr(js, ret, "pos", make_gpu_buffer(js, posdata, sizeof(*posdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0));
JS_SetProperty(js, ret, uv, make_gpu_buffer(js, uvdata, sizeof(*uvdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0)); JS_SetPropertyStr(js, ret, "uv", make_gpu_buffer(js, uvdata, sizeof(*uvdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0));
JS_SetProperty(js, ret, color, make_gpu_buffer(js, colordata, sizeof(*colordata) * verts, JS_TYPED_ARRAY_FLOAT32, 4, 0,0)); JS_SetPropertyStr(js, ret, "color", make_gpu_buffer(js, colordata, sizeof(*colordata) * verts, JS_TYPED_ARRAY_FLOAT32, 4, 0,0));
JS_SetProperty(js, ret, indices, make_quad_indices_buffer(js, quads)); JS_SetPropertyStr(js, ret, "indices", make_quad_indices_buffer(js, quads));
JS_SetProperty(js, ret, vertices, number2js(js, verts)); JS_SetPropertyStr(js, ret, "vertices", number2js(js, verts));
JS_SetProperty(js, ret, count, number2js(js, count)); JS_SetPropertyStr(js, ret, "count", number2js(js, count));
) )
static const JSCFunctionListEntry js_renderer_funcs[] = { static const JSCFunctionListEntry js_renderer_funcs[] = {

476
source/qjs_sdl.c Normal file
View File

@@ -0,0 +1,476 @@
#include "qjs_sdl.h"
#include "jsffi.h"
#include "qjs_macros.h"
#include <SDL3/SDL.h>
// SDL Free functions
void SDL_Camera_free(JSRuntime *rt, SDL_Camera *cam)
{
SDL_CloseCamera(cam);
}
void SDL_Cursor_free(JSRuntime *rt, SDL_Cursor *c)
{
SDL_DestroyCursor(c);
}
void SDL_AudioStream_free(JSRuntime *rt, SDL_AudioStream *st) {
SDL_DestroyAudioStream(st);
}
// Class definitions for SDL types
QJSCLASS(SDL_Cursor,)
QJSCLASS(SDL_Camera,)
QJSCLASS(SDL_AudioStream,)
// Internal keymod function for input module
static JSValue js_keymod(JSContext *js)
{
SDL_Keymod modstate = SDL_GetModState();
JSValue ret = JS_NewObject(js);
if (SDL_KMOD_CTRL & modstate)
JS_SetPropertyStr(js,ret,"ctrl", JS_NewBool(js,1));
if (SDL_KMOD_SHIFT & modstate)
JS_SetPropertyStr(js,ret,"shift", JS_NewBool(js,1));
if (SDL_KMOD_ALT & modstate)
JS_SetPropertyStr(js,ret,"alt", JS_NewBool(js,1));
if (SDL_KMOD_GUI & modstate)
JS_SetPropertyStr(js,ret,"super", JS_NewBool(js,1));
if (SDL_KMOD_NUM & modstate)
JS_SetPropertyStr(js,ret,"numlock", JS_NewBool(js,1));
if (SDL_KMOD_CAPS & modstate)
JS_SetPropertyStr(js,ret,"caps", JS_NewBool(js,1));
if (SDL_KMOD_SCROLL & modstate)
JS_SetPropertyStr(js,ret,"scrolllock", JS_NewBool(js,1));
if (SDL_KMOD_MODE & modstate)
JS_SetPropertyStr(js,ret,"mode", JS_NewBool(js,1));
return ret;
}
// INPUT FUNCTIONS
JSC_CCALL(input_mouse_lock, SDL_CaptureMouse(JS_ToBool(js,argv[0])))
JSC_CCALL(input_mouse_show,
if (JS_ToBool(js,argv[0]))
SDL_ShowCursor();
else
SDL_HideCursor();
)
JSC_CCALL(input_cursor_set,
SDL_Cursor *c = js2SDL_Cursor(js,argv[0]);
if (!SDL_SetCursor(c))
return JS_ThrowReferenceError(js, "could not set cursor: %s", SDL_GetError());
)
JSC_CCALL(input_keyname,
return JS_NewString(js, SDL_GetKeyName(js2number(js,argv[0])));
)
JSC_CCALL(input_keymod,
return js_keymod(js);
)
JSC_CCALL(input_mousestate,
float x,y;
SDL_MouseButtonFlags flags = SDL_GetMouseState(&x,&y);
JSValue m = JS_NewObject(js);
JS_SetPropertyStr(js,m,"x", number2js(js,x));
JS_SetPropertyStr(js,m,"y", number2js(js,y));
if (flags & SDL_BUTTON_LMASK)
JS_SetPropertyStr(js, m, "left", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_MMASK)
JS_SetPropertyStr(js, m, "middle", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_RMASK)
JS_SetPropertyStr(js, m, "right", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X1MASK)
JS_SetPropertyStr(js, m, "x1", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X2MASK)
JS_SetPropertyStr(js, m, "x2", JS_NewBool(js, 1));
return m;
)
static const JSCFunctionListEntry js_input_funcs[] = {
MIST_FUNC_DEF(input, mouse_show, 1),
MIST_FUNC_DEF(input, mouse_lock, 1),
MIST_FUNC_DEF(input, cursor_set, 1),
MIST_FUNC_DEF(input, keyname, 1),
MIST_FUNC_DEF(input, keymod, 0),
MIST_FUNC_DEF(input, mousestate, 0),
};
JSValue js_input_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_input_funcs,countof(js_input_funcs));
// Initialize SDL cursor class (no functions)
JSValue c_types = JS_GetPropertyStr(js, JS_GetGlobalObject(js), "c_types");
QJSCLASSPREP_NO_FUNCS(SDL_Cursor)
JS_FreeValue(js, c_types);
return mod;
}
// CAMERA FUNCTIONS
JSC_CCALL(camera_list,
int num;
SDL_CameraID *ids = SDL_GetCameras(&num);
if (num == 0) return JS_UNDEFINED;
JSValue jsids = JS_NewArray(js);
for (int i = 0; i < num; i++)
JS_SetPropertyUint32(js,jsids, i, number2js(js,ids[i]));
return jsids;
)
JSC_CCALL(camera_open,
int id = js2number(js,argv[0]);
SDL_Camera *cam = SDL_OpenCamera(id, NULL);
if (!cam) ret = JS_ThrowReferenceError(js, "Could not open camera %d: %s\n", id, SDL_GetError());
else
ret = SDL_Camera2js(js,cam);
)
JSC_CCALL(camera_name,
const char *name = SDL_GetCameraName(js2number(js,argv[0]));
if (!name) return JS_ThrowReferenceError(js, "Could not get camera name from id %d.", (int)js2number(js,argv[0]));
return JS_NewString(js, name);
)
JSC_CCALL(camera_position,
SDL_CameraPosition pos = SDL_GetCameraPosition(js2number(js,argv[0]));
switch(pos) {
case SDL_CAMERA_POSITION_UNKNOWN: return JS_NewString(js,"unknown");
case SDL_CAMERA_POSITION_FRONT_FACING: return JS_NewString(js,"front");
case SDL_CAMERA_POSITION_BACK_FACING: return JS_NewString(js,"back");
}
)
static const JSCFunctionListEntry js_camera_funcs[] = {
MIST_FUNC_DEF(camera, list, 0),
MIST_FUNC_DEF(camera, open, 1),
MIST_FUNC_DEF(camera, name, 1),
MIST_FUNC_DEF(camera, position, 1),
};
static const JSCFunctionListEntry js_SDL_Camera_funcs[] =
{
// MIST_FUNC_DEF(camera, frame, 0),
// MIST_FUNC_DEF(camera, release_frame, 1),
};
JSValue js_camera_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_camera_funcs,countof(js_camera_funcs));
// Initialize SDL camera class with functions
JSValue c_types = JS_GetPropertyStr(js, JS_GetGlobalObject(js), "c_types");
QJSCLASSPREP_FUNCS(SDL_Camera)
JS_FreeValue(js, c_types);
return mod;
}
JSC_CCALL(camera_frame,
/*
SDL_ClearError();
SDL_Camera *cam = js2SDL_Camera(js,self);
if (!cam) return JS_ThrowReferenceError(js,"Self was not a camera: %s", SDL_GetError());
SDL_Surface *surf = SDL_AcquireCameraFrame(cam, NULL);
if (!surf) {
const char *msg = SDL_GetError();
if (msg[0] != 0)
return JS_ThrowReferenceError(js,"Could not get camera frame: %s", SDL_GetError());
else return JS_UNDEFINED;
}
return SDL_Surface2js(js,surf);
SDL_Surface *newsurf = SDL_CreateSurface(surf->w, surf->h, surf->format);
SDL_ReleaseCameraFrame(cam,surf);
int didit = SDL_BlitSurface(surf, NULL, newsurf, NULL);
if (!didit) {
SDL_DestroySurface(newsurf);
return JS_ThrowReferenceError(js, "Could not blit: %s", SDL_GetError());
}
return SDL_Surface2js(js,newsurf);
*/
)
JSC_CCALL(camera_release_frame,
/* SDL_Camera *cam = js2SDL_Camera(js,self);
SDL_Surface *surf = js2SDL_Surface(js,argv[0]);
SDL_ReleaseCameraFrame(cam,surf);
*/
)
// SDL AUDIO FUNCTIONS
// Audio format lookup table and conversion functions
static const struct { const char *s; SDL_AudioFormat f; } fmt_lut[] = {
{ "u8", SDL_AUDIO_U8 }, /* Unsigned 8-bit */
{ "s8", SDL_AUDIO_S8 }, /* Signed 8-bit */
{ "s16", SDL_AUDIO_S16 }, /* Signed 16-bit, host endian */
{ "s32", SDL_AUDIO_S32 }, /* Signed 32-bit, host endian */
{ "f32", SDL_AUDIO_F32 } /* Float 32-bit, host endian */
};
static int format_str_to_enum(const char *f, SDL_AudioFormat *out)
{
struct { const char *s; SDL_AudioFormat f; } map[] = {
{"u8", SDL_AUDIO_U8 }, {"s16", SDL_AUDIO_S16},
{"s32", SDL_AUDIO_S32}, {"f32", SDL_AUDIO_F32}
};
for (size_t i=0;i<countof(map);++i)
if (!strcmp(f,map[i].s)) { *out = map[i].f; return 1; }
return 0;
}
static const char *fmt2str(SDL_AudioFormat f)
{
for (size_t i = 0; i < countof(fmt_lut); ++i)
if (fmt_lut[i].f == f) return fmt_lut[i].s;
return "unknown";
}
static JSValue audiospec2js(JSContext *js, const SDL_AudioSpec *spec)
{
JSValue o = JS_NewObject(js);
/* stringify format (u8/s16/s32/f32) */
JS_SetPropertyStr(js, o, "format",
JS_NewString(js, fmt2str(spec->format)));
JS_SetPropertyStr(js, o, "channels",
JS_NewInt32(js, spec->channels));
JS_SetPropertyStr(js, o, "samplerate",
JS_NewInt32(js, spec->freq));
return o;
}
static SDL_AudioSpec js2audiospec(JSContext *js, JSValue obj)
{
SDL_AudioSpec spec;
JSValue v;
v = JS_GetPropertyStr(js, obj, "format");
if (!JS_IsUndefined(v)) {
const char *s = JS_ToCString(js, v);
format_str_to_enum(s, &spec.format);
JS_FreeCString(js, s);
}
JS_FreeValue(js, v);
v = JS_GetPropertyStr(js, obj, "channels");
if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.channels, v);
JS_FreeValue(js, v);
v = JS_GetPropertyStr(js, obj, "samplerate");
if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.freq, v);
JS_FreeValue(js, v);
return spec;
}
JSC_CCALL(sdl_audio_drivers,
int num = SDL_GetNumAudioDrivers();
JSValue arr = JS_NewArray(js);
for (int i = 0; i < num; i++)
JS_SetPropertyUint32(js, arr, i, JS_NewString(js, SDL_GetAudioDriver(i)));
return arr;
)
JSC_CCALL(sdl_audio_devices,
int n;
SDL_AudioDeviceID *ids = SDL_GetAudioPlaybackDevices(&n);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < n; i++)
JS_SetPropertyUint32(js,arr,i,JS_NewString(js, SDL_GetAudioDeviceName(ids[i])));
return arr;
)
JSC_CCALL(sdl_audio_open_stream,
const char *type = JS_IsString(argv[0]) ? JS_ToCString(js, argv[0]) : NULL;
SDL_AudioDeviceID devid = !strcmp(type, "capture") ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
if (type)
JS_FreeCString(js, type);
SDL_AudioStream *st;
if (JS_IsUndefined(argv[1]))
st = SDL_OpenAudioDeviceStream(devid, NULL, NULL, NULL);
else {
SDL_AudioSpec want = js2audiospec(js, argv[1]);
st = SDL_OpenAudioDeviceStream(devid, &want, NULL, NULL);
}
if (!st)
return JS_ThrowInternalError(js, "open failed: %s", SDL_GetError());
return SDL_AudioStream2js(js, st);
)
static const JSCFunctionListEntry js_sdl_audio_funcs[] = {
MIST_FUNC_DEF(sdl_audio, drivers, 0),
MIST_FUNC_DEF(sdl_audio, devices, 0),
MIST_FUNC_DEF(sdl_audio, open_stream, 2),
};
JSC_CCALL(sdl_audiostream_get_format,
SDL_AudioStream *as = js2SDL_AudioStream(js, self);
SDL_AudioSpec src;
SDL_AudioSpec dst;
SDL_GetAudioStreamFormat(as, &src, &dst);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "src", audiospec2js(js, &src));
JS_SetPropertyStr(js, obj, "dst", audiospec2js(js, &dst));
return obj;
)
JSC_CCALL(sdl_audiostream_set_format,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
const SDL_AudioSpec *src_ptr=NULL,*dst_ptr=NULL;
SDL_AudioSpec src={0},dst={0};
if(argc>0&&!JS_IsUndefined(argv[0])){
src=js2audiospec(js,argv[0]);
src_ptr=&src;
}
if(argc>1&&!JS_IsUndefined(argv[1])){
dst=js2audiospec(js,argv[1]);
dst_ptr=&dst;
}
if(!SDL_SetAudioStreamFormat(as,src_ptr,dst_ptr))
return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_UNDEFINED;
)
JSC_CCALL(sdl_audiostream_resume,
SDL_AudioStream *as = js2SDL_AudioStream(js,self);
if (!SDL_ResumeAudioStreamDevice(as))
return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_UNDEFINED;
)
JSC_CCALL(sdl_audiostream_clear,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
if (!SDL_ClearAudioStream(as))
return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_UNDEFINED;
)
JSC_CCALL(sdl_audiostream_flush,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
if(!SDL_FlushAudioStream(as))
return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_UNDEFINED;
)
JSC_CCALL(sdl_audiostream_available,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
Sint64 n = SDL_GetAudioStreamAvailable(as);
if(n<0) return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_NewInt64(js,n);
)
JSC_CCALL(sdl_audiostream_queued,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
Sint64 n = SDL_GetAudioStreamQueued(as);
if(n<0) return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_NewInt64(js,n);
)
/* ---------- data IO ---------------------------------------------------- */
JSC_CCALL(sdl_audiostream_put,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
size_t len;
void *buf = JS_GetArrayBuffer(js, &len, argv[0]);
if (!buf)
return JS_ThrowInternalError(js, "Requires array buffer.");
if (!SDL_PutAudioStreamData(as,buf,len))
return JS_ThrowInternalError(js, "%s", SDL_GetError());
return JS_UNDEFINED;
)
JSC_CCALL(sdl_audiostream_get,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
int want;
JS_ToInt32(js,&want,argv[0]);
void *data = malloc(want);
int got = SDL_GetAudioStreamData(as, data, want);
if (got<0) {
free(data);
return JS_ThrowInternalError(js,"%s",SDL_GetError());
}
JSValue ab = JS_NewArrayBufferCopy(js, data, got);
free(data);
return ab;
)
JSC_CCALL(sdl_audiostream_get_gain,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
return JS_NewFloat64(js,SDL_GetAudioStreamGain(as));
)
JSC_CCALL(sdl_audiostream_set_gain,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
double g; JS_ToFloat64(js,&g,argv[0]);
SDL_SetAudioStreamGain(as,(float)g);
return JS_UNDEFINED;
)
JSC_CCALL(sdl_audiostream_get_freq_ratio,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
return JS_NewFloat64(js,SDL_GetAudioStreamFrequencyRatio(as));
)
JSC_CCALL(sdl_audiostream_set_freq_ratio,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
double r; JS_ToFloat64(js,&r,argv[0]);
SDL_SetAudioStreamFrequencyRatio(as,(float)r);
return JS_UNDEFINED;
)
/* ---------- JS export list -------------------------------------------- */
static const JSCFunctionListEntry js_SDL_AudioStream_funcs[] = {
MIST_FUNC_DEF(sdl_audiostream, get_format, 0),
MIST_FUNC_DEF(sdl_audiostream, set_format, 2),
MIST_FUNC_DEF(sdl_audiostream, resume, 0),
MIST_FUNC_DEF(sdl_audiostream, clear, 0),
MIST_FUNC_DEF(sdl_audiostream, flush, 0),
MIST_FUNC_DEF(sdl_audiostream, available, 0),
MIST_FUNC_DEF(sdl_audiostream, queued, 0),
MIST_FUNC_DEF(sdl_audiostream, put, 1),
MIST_FUNC_DEF(sdl_audiostream, get, 1),
MIST_FUNC_DEF(sdl_audiostream, set_gain, 1),
MIST_FUNC_DEF(sdl_audiostream, get_gain, 0),
MIST_FUNC_DEF(sdl_audiostream, set_freq_ratio, 1),
MIST_FUNC_DEF(sdl_audiostream, get_freq_ratio, 0),
};
JSValue js_sdl_audio_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_sdl_audio_funcs,countof(js_sdl_audio_funcs));
return mod;
}

10
source/qjs_sdl.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef QJS_SDL_H
#define QJS_SDL_H
#include "quickjs.h"
JSValue js_input_use(JSContext *ctx);
JSValue js_camera_use(JSContext *ctx);
JSValue js_sdl_audio_use(JSContext *ctx);
#endif /* QJS_SDL_H */

2481
source/qjs_sdl_gpu.c Normal file

File diff suppressed because it is too large Load Diff

38
source/qjs_sdl_gpu.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef QJS_SDL_GPU_H
#define QJS_SDL_GPU_H
#include "jsffi.h"
#include "sprite.h"
// Forward declarations for sprite sorting functions
int sort_sprite(const sprite *a, const sprite *b);
int sort_sprite_backtofront(const sprite *a, const sprite *b);
int sort_sprite_fronttoback(const sprite *a, const sprite *b);
int sort_sprite_texture(const sprite *a, const sprite *b);
// Buffer check result structure
typedef struct {
JSValue val;
void *ptr;
size_t size;
int need_new;
} BufferCheckResult;
// Function for buffer management
BufferCheckResult get_or_extend_buffer(
JSContext *js,
JSValue old_mesh,
const char *prop,
size_t needed_size,
int type,
int elements_per_item,
int copy,
int index
);
// Base quad for sprite rendering
extern HMM_Vec3 base_quad[4];
JSValue js_sdl_gpu_use(JSContext *js);
#endif

64
source/qjs_sprite.c Normal file
View File

@@ -0,0 +1,64 @@
#include "qjs_sprite.h"
#include "jsffi.h"
#include "qjs_macros.h"
#include "sprite.h"
#include "HandmadeMath.h"
// Sprite class definitions
static JSClassID js_sprite_id;
static void js_sprite_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) {
sprite *sp = JS_GetOpaque(val, js_sprite_id);
if (!sp) return;
JS_MarkValue(rt, sp->image, mark_func);
}
// Class definition for sprite with mark function for GC
QJSCLASSMARK(sprite,)
// SPRITE GETTER/SETTER FUNCTIONS
JSC_GETSET(sprite, pos, vec2)
JSC_GETSET(sprite, center, vec2)
JSC_GETSET(sprite, layer, number)
JSC_GETSET(sprite, color, color)
JSC_GETSET(sprite, skew, vec2)
JSC_GETSET(sprite, scale, vec2)
JSC_GETSET(sprite, rotation, number)
// SPRITE ACTION FUNCTIONS
JSC_CCALL(sprite_move,
sprite *sp = js2sprite(js,self);
HMM_Vec2 mv = js2vec2(js,argv[0]);
sp->pos.x += mv.x;
sp->pos.y += mv.y;
)
JSC_CCALL(sprite_set_affine,
sprite *sp = js2sprite(js,self);
sprite_apply(sp);
)
JSC_CCALL(sprite_set_image,
sprite *sp = js2sprite(js,self);
if (!JS_IsUndefined(sp->image))
JS_FreeValue(js,sp->image);
sp->image = JS_DupValue(js, argv[0]);
)
static const JSCFunctionListEntry js_sprite_funcs[] = {
MIST_FUNC_DEF(sprite, set_affine, 0),
MIST_FUNC_DEF(sprite, set_image, 1),
MIST_FUNC_DEF(sprite, move, 1),
JS_CGETSET_DEF("pos", js_sprite_get_pos, js_sprite_set_pos),
JS_CGETSET_DEF("scale", js_sprite_get_scale, js_sprite_set_scale),
JS_CGETSET_DEF("skew", js_sprite_get_skew, js_sprite_set_skew),
JS_CGETSET_DEF("layer", js_sprite_get_layer, js_sprite_set_layer),
JS_CGETSET_DEF("color", js_sprite_get_color, js_sprite_set_color),
JS_CGETSET_DEF("center", js_sprite_get_center, js_sprite_set_center),
JS_CGETSET_DEF("rotation", js_sprite_get_rotation, js_sprite_set_rotation),
};
// Note: Like transform, sprite doesn't use MISTUSE because sprite is a C type created via os.make_sprite()
// The sprite functions are registered as methods on the sprite class prototype
// This would be handled in the main FFI loading where QJSCLASSPREP_FUNCS(sprite) is called

12
source/qjs_sprite.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef QJS_SPRITE_H
#define QJS_SPRITE_H
#include "quickjs.h"
#include "sprite.h"
JSValue js_sprite_use(JSContext *ctx);
sprite *js2sprite(JSContext *js, JSValue v);
#endif /* QJS_SPRITE_H */

212
source/qjs_transform.c Normal file
View File

@@ -0,0 +1,212 @@
#include "qjs_transform.h"
#include "jsffi.h"
#include "qjs_macros.h"
#include "transform.h"
#include "HandmadeMath.h"
#include "prosperon.h"
// Transform class definitions
JSClassID js_transform_id;
static void js_transform_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) {
transform *t = JS_GetOpaque(val, js_transform_id);
if (!t) return;
// Mark the JSValue references stored in your struct
JS_MarkValue(rt, t->change_hook, mark_func);
JS_MarkValue(rt, t->jsparent, mark_func);
// Mark the array elements
for (int i = 0; i < arrlen(t->jschildren); i++)
JS_MarkValue(rt, t->jschildren[i], mark_func);
}
QJSCLASSMARK_EXTERN(transform,)
// TRANSFORM GETTER/SETTER FUNCTIONS
JSC_GETSET_APPLY(transform, pos, vec3)
JSC_GETSET_APPLY(transform, scale, vec3)
JSC_GETSET_APPLY(transform, rotation, quat)
static JSValue js_transform_get_change_hook(JSContext *js, JSValueConst self)
{
transform *t = js2transform(js,self);
return JS_DupValue(js,t->change_hook);
}
static JSValue js_transform_set_change_hook(JSContext *js, JSValueConst self, JSValue v)
{
transform *t = js2transform(js,self);
if (!JS_IsUndefined(v) && !JS_IsFunction(js,v)) return JS_ThrowReferenceError(js, "Hook must be a function.");
JS_FreeValue(js,t->change_hook);
t->change_hook = JS_DupValue(js,v);
return JS_UNDEFINED;
}
static JSValue js_transform_get_parent(JSContext *js, JSValueConst self)
{
transform *t = js2transform(js,self);
if (t->parent) return JS_DupValue(js,t->jsparent);
return JS_UNDEFINED;
}
static JSValue js_transform_set_parent(JSContext *js, JSValueConst self, JSValue v)
{
transform *p = js2transform(js,v);
if (!JS_IsUndefined(v) && !p)
return JS_ThrowReferenceError(js,"Parent must be another transform.");
transform *t = js2transform(js,self);
if (t == p)
return JS_ThrowReferenceError(js, "A transform cannot be its own parent.");
if (t->parent) {
transform *cur_parent = t->parent;
JS_FreeValue(js,t->jsparent);
t->jsparent = JS_UNDEFINED;
for (int i = 0; i < arrlen(cur_parent->children); i++) {
if (cur_parent->children[i] == t) {
arrdelswap(cur_parent->children,i);
break;
}
}
for (int i = 0; i < arrlen(cur_parent->jschildren); i++) {
if (JS_SameValue(js,cur_parent->jschildren[i],self)) {
JS_FreeValue(js,cur_parent->jschildren[i]);
arrdelswap(cur_parent->jschildren,i);
break;
}
}
}
t->parent = p;
t->jsparent = JS_DupValue(js,v);
if (p) {
arrput(p->children, t);
JSValue child = JS_DupValue(js,self);
arrput(p->jschildren,child);
}
transform_apply(t);
return JS_UNDEFINED;
}
// TRANSFORM ACTION FUNCTIONS
JSC_CCALL(transform_move,
transform *t = js2transform(js,self);
transform_move(t, js2vec3(js,argv[0]));
)
JSC_CCALL(transform_lookat,
HMM_Vec3 point = js2vec3(js,argv[0]);
transform *go = js2transform(js,self);
HMM_Mat4 m = HMM_LookAt_RH(go->pos, point, vUP);
go->rotation = HMM_M4ToQ_RH(m);
transform_apply(go);
)
JSC_CCALL(transform_rotate,
HMM_Vec3 axis = js2vec3(js,argv[0]);
transform *t = js2transform(js,self);
HMM_Quat rot = HMM_QFromAxisAngle_RH(axis, js2angle(js,argv[1]));
t->rotation = HMM_MulQ(t->rotation,rot);
transform_apply(t);
)
JSC_CCALL(transform_angle,
HMM_Vec3 axis = js2vec3(js,argv[0]);
transform *t = js2transform(js,self);
if (axis.x) return angle2js(js,HMM_Q_Roll(t->rotation));
if (axis.y) return angle2js(js,HMM_Q_Pitch(t->rotation));
if (axis.z) return angle2js(js,HMM_Q_Yaw(t->rotation));
return angle2js(js,0);
)
JSC_CCALL(transform_direction,
transform *t = js2transform(js,self);
return vec32js(js, HMM_QVRot(js2vec3(js,argv[0]), t->rotation));
)
JSC_CCALL(transform_phys2d,
transform *t = js2transform(js,self);
HMM_Vec2 v = js2vec2(js,argv[0]);
float av = js2number(js,argv[1]);
float dt = js2number(js,argv[2]);
transform_move(t, (HMM_Vec3){v.x*dt,v.y*dt,0});
HMM_Quat rot = HMM_QFromAxisAngle_RH((HMM_Vec3){0,0,1}, av*dt);
t->rotation = HMM_MulQ(t->rotation, rot);
transform_apply(t);
)
JSC_CCALL(transform_unit,
transform *t = js2transform(js,self);
t->pos = v3zero;
t->rotation = QUAT1;
t->scale = v3one;
transform_apply(t);
)
JSC_CCALL(transform_trs,
transform *t = js2transform(js,self);
t->pos = JS_IsUndefined(argv[0]) ? v3zero : js2vec3(js,argv[0]);
t->rotation = JS_IsUndefined(argv[1]) ? QUAT1 : js2quat(js,argv[1]);
t->scale = JS_IsUndefined(argv[2]) ? v3one : js2vec3(js,argv[2]);
transform_apply(t);
)
JSC_CCALL(transform_rect,
transform *t = js2transform(js,self);
rect r = js2rect(js,argv[0]);
t->pos = (HMM_Vec3){r.x,r.y,0};
t->scale = (HMM_Vec3){r.w,r.h,1};
t->rotation = QUAT1;
transform_apply(t);
)
JSC_CCALL(transform_array,
transform *t = js2transform(js,self);
HMM_Mat4 m= transform2mat(t);
ret = JS_NewArray(js);
for (int i = 0; i < 16; i++)
JS_SetPropertyUint32(js,ret,i, number2js(js,m.em[i]));
)
JSC_CCALL(transform_torect,
transform *t = js2transform(js,self);
return rect2js(js,transform2rect(t));
)
JSC_CCALL(transform_children,
transform *t = js2transform(js,self);
ret = JS_NewArray(js);
for (int i = 0; i < arrlen(t->jschildren); i++)
JS_SetPropertyUint32(js,ret,i,JS_DupValue(js,t->jschildren[i]));
)
static const JSCFunctionListEntry js_transform_funcs[] = {
CGETSET_ADD(transform, pos),
CGETSET_ADD(transform, scale),
CGETSET_ADD(transform, rotation),
CGETSET_ADD(transform, parent),
CGETSET_ADD(transform, change_hook),
MIST_FUNC_DEF(transform, trs, 3),
MIST_FUNC_DEF(transform, phys2d, 3),
MIST_FUNC_DEF(transform, move, 1),
MIST_FUNC_DEF(transform, rotate, 2),
MIST_FUNC_DEF(transform, angle, 1),
MIST_FUNC_DEF(transform, lookat, 1),
MIST_FUNC_DEF(transform, direction, 1),
MIST_FUNC_DEF(transform, unit, 0),
MIST_FUNC_DEF(transform, rect, 1),
MIST_FUNC_DEF(transform, array, 0),
MIST_FUNC_DEF(transform, torect, 0),
MIST_FUNC_DEF(transform, children, 0),
};
// Note: Transform module doesn't use MISTUSE because transform is a C type that's created via os.make_transform()
// The transform functions are registered as methods on the transform class prototype
// This would be handled in the main FFI loading where QJSCLASSPREP_FUNCS(transform) is called

8
source/qjs_transform.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef QJS_TRANSFORM_H
#define QJS_TRANSFORM_H
#include "quickjs.h"
JSValue js_transform_use(JSContext *ctx);
#endif /* QJS_TRANSFORM_H */