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
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:
@@ -1,3 +0,0 @@
|
||||
def:
|
||||
cd _prosperon && make && cp build_dbg/prosperon ..
|
||||
./prosperon
|
||||
@@ -147,7 +147,7 @@ sources = []
|
||||
src += [
|
||||
'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',
|
||||
'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'
|
||||
]
|
||||
# quirc src
|
||||
|
||||
5319
source/jsffi.c
5319
source/jsffi.c
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,79 @@
|
||||
#define FFI_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);
|
||||
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
|
||||
|
||||
@@ -49,11 +49,11 @@ static struct { char *key; prosperon_rt *value; } *actors = NULL;
|
||||
static unsigned char *zip_buffer_global = NULL;
|
||||
static char *prosperon = NULL;
|
||||
|
||||
static prosperon_rt *io_actor = NULL;
|
||||
prosperon_rt *io_actor = NULL;
|
||||
|
||||
static Uint32 queue_event;
|
||||
|
||||
static SDL_AtomicInt shutdown;
|
||||
static SDL_AtomicInt engine_shutdown;
|
||||
static SDL_Thread **runners = NULL;
|
||||
|
||||
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)
|
||||
{
|
||||
while (!SDL_GetAtomicInt(&shutdown)) {
|
||||
while (!SDL_GetAtomicInt(&engine_shutdown)) {
|
||||
SDL_LockMutex(queue_mutex);
|
||||
prosperon_rt *actor = NULL;
|
||||
if (arrlen(ready_queue) > 0) {
|
||||
@@ -720,7 +720,7 @@ static void exit_handler(void)
|
||||
script_evalf(js, "prosperon.dispatch('exit')");
|
||||
}
|
||||
|
||||
SDL_SetAtomicInt(&shutdown, 1);
|
||||
SDL_SetAtomicInt(&engine_shutdown, 1);
|
||||
int status;
|
||||
SDL_BroadcastCondition(queue_cond);
|
||||
for (int i = 0; i < arrlen(runners); i++)
|
||||
|
||||
447
source/qjs_geometry.c
Normal file
447
source/qjs_geometry.c
Normal 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
8
source/qjs_geometry.h
Normal 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 */
|
||||
@@ -22,15 +22,6 @@ SDL_Window *js2SDL_Window(JSContext *js, JSValue v);
|
||||
|
||||
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)
|
||||
{
|
||||
double n;
|
||||
@@ -176,12 +167,12 @@ void fill_plotdata(JSContext *js, JSValue v, JSValue last)
|
||||
/* arrsetlen(plotdata, 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)));
|
||||
}
|
||||
else {
|
||||
// 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;
|
||||
ImVec2 c;
|
||||
c.x = i;
|
||||
@@ -205,13 +196,13 @@ PLOT_FN(digitalplot, PlotDigital,,0)
|
||||
|
||||
JSC_SCALL(imgui_barplot,
|
||||
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,
|
||||
size_t 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);
|
||||
)
|
||||
|
||||
@@ -290,7 +281,7 @@ JSC_SCALL(imgui_slider,
|
||||
float high = JS_IsUndefined(argv[3]) ? 1.0 : js2number(js, argv[3]);
|
||||
|
||||
if (JS_IsArray(js, argv[1])) {
|
||||
int n = js_arrlen(js, argv[1]);
|
||||
int n = JS_ArrayLength(js, argv[1]);
|
||||
float a[n];
|
||||
js2floatarr(argv[1], n, a);
|
||||
|
||||
@@ -394,7 +385,7 @@ JSC_SCALL(imgui_tab,
|
||||
|
||||
JSC_SCALL(imgui_listbox,
|
||||
/* 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]);
|
||||
ImGui::ListBox(str, &idx, arr, n, 4);
|
||||
for (int i = 0; i < n; i++)
|
||||
@@ -495,7 +486,7 @@ JSC_SCALL(imgui_dndtarget,
|
||||
)
|
||||
|
||||
JSC_SCALL(imgui_color,
|
||||
/* int n = js_arrlen(js, argv[1]);
|
||||
/* int n = JS_ArrayLength(js, argv[1]);
|
||||
float color[n];
|
||||
js2floatarr(argv[1],n,color);
|
||||
|
||||
|
||||
382
source/qjs_io.c
Normal file
382
source/qjs_io.c
Normal 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
8
source/qjs_io.h
Normal 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 */
|
||||
@@ -21,6 +21,12 @@
|
||||
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, \
|
||||
const char *str = JS_ToCString(js,argv[0]); \
|
||||
__VA_ARGS__ ;\
|
||||
@@ -136,6 +142,29 @@ JSValue TYPE##2js(JSContext *js, TYPE *n) { \
|
||||
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) \
|
||||
JSValue NAME = JS_NewObject(js); \
|
||||
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_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]))
|
||||
|
||||
|
||||
509
source/qjs_math.c
Normal file
509
source/qjs_math.c
Normal 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
8
source/qjs_math.h
Normal 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
489
source/qjs_os.c
Normal 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
8
source/qjs_os.h
Normal 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 */
|
||||
@@ -56,7 +56,7 @@ JSC_CCALL(SDL_Renderer_rect,
|
||||
}
|
||||
|
||||
if (JS_IsArray(js,argv[0])) {
|
||||
int len = js_arrlen(js,argv[0]);
|
||||
int len = JS_ArrayLength(js,argv[0]);
|
||||
rect rects[len];
|
||||
for (int i = 0; i < len; 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);
|
||||
if (!tex) return JS_ThrowReferenceError(js, "Could not create texture from surface: %s", SDL_GetError());
|
||||
ret = SDL_Texture2js(js,tex);
|
||||
JS_SetProperty(js,ret,width, number2js(js,tex->w));
|
||||
JS_SetProperty(js,ret,height, number2js(js,tex->h));
|
||||
JS_SetPropertyStr(js,ret,"width", number2js(js,tex->w));
|
||||
JS_SetPropertyStr(js,ret,"height", number2js(js,tex->h));
|
||||
)
|
||||
|
||||
JSC_CCALL(SDL_Renderer_fillrect,
|
||||
@@ -93,7 +93,7 @@ JSC_CCALL(SDL_Renderer_fillrect,
|
||||
}
|
||||
|
||||
if (JS_IsArray(js,argv[0])) {
|
||||
int len = js_arrlen(js,argv[0]);
|
||||
int len = JS_ArrayLength(js,argv[0]);
|
||||
rect rects[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
|
||||
@@ -193,7 +193,7 @@ JSC_CCALL(renderer_line,
|
||||
}
|
||||
|
||||
if (JS_IsArray(js,argv[0])) {
|
||||
int len = js_arrlen(js,argv[0]);
|
||||
int len = JS_ArrayLength(js,argv[0]);
|
||||
HMM_Vec2 points[len];
|
||||
assert(sizeof(HMM_Vec2) == sizeof(SDL_FPoint));
|
||||
for (int i = 0; i < len; i++) {
|
||||
@@ -213,7 +213,7 @@ JSC_CCALL(renderer_point,
|
||||
}
|
||||
|
||||
if (JS_IsArray(js,argv[0])) {
|
||||
int len = js_arrlen(js,argv[0]);
|
||||
int len = JS_ArrayLength(js,argv[0]);
|
||||
HMM_Vec2 points[len];
|
||||
assert(sizeof(HMM_Vec2) ==sizeof(SDL_FPoint));
|
||||
for (int i = 0; i < len; i++) {
|
||||
@@ -393,7 +393,7 @@ JSC_CCALL(renderer_target,
|
||||
// color: the color this sprite should be hued by
|
||||
JSC_CCALL(renderer_make_sprite_mesh,
|
||||
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 count = quads*6;
|
||||
|
||||
@@ -403,10 +403,10 @@ JSC_CCALL(renderer_make_sprite_mesh,
|
||||
|
||||
for (int i = 0; i < quads; 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 jscolor = JS_GetProperty(js,sub,color);
|
||||
JSValue jssrc = JS_GetPropertyStr(js,sub,"src");
|
||||
JSValue jscolor = JS_GetPropertyStr(js,sub,"color");
|
||||
HMM_Vec4 color;
|
||||
|
||||
rect src;
|
||||
@@ -441,12 +441,12 @@ JSC_CCALL(renderer_make_sprite_mesh,
|
||||
}
|
||||
|
||||
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_SetProperty(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_SetProperty(js, ret, indices, make_quad_indices_buffer(js, quads));
|
||||
JS_SetProperty(js, ret, vertices, number2js(js, verts));
|
||||
JS_SetProperty(js, ret, count, number2js(js, count));
|
||||
JS_SetPropertyStr(js, ret, "pos", make_gpu_buffer(js, posdata, sizeof(*posdata) * 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_SetPropertyStr(js, ret, "color", make_gpu_buffer(js, colordata, sizeof(*colordata) * verts, JS_TYPED_ARRAY_FLOAT32, 4, 0,0));
|
||||
JS_SetPropertyStr(js, ret, "indices", make_quad_indices_buffer(js, quads));
|
||||
JS_SetPropertyStr(js, ret, "vertices", number2js(js, verts));
|
||||
JS_SetPropertyStr(js, ret, "count", number2js(js, count));
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_renderer_funcs[] = {
|
||||
|
||||
476
source/qjs_sdl.c
Normal file
476
source/qjs_sdl.c
Normal 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
10
source/qjs_sdl.h
Normal 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
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
38
source/qjs_sdl_gpu.h
Normal 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
64
source/qjs_sprite.c
Normal 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
12
source/qjs_sprite.h
Normal 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
212
source/qjs_transform.c
Normal 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
8
source/qjs_transform.h
Normal 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 */
|
||||
Reference in New Issue
Block a user