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 += [
|
src += [
|
||||||
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
||||||
'render.c','simplex.c','spline.c', 'transform.c','prosperon.c', 'wildmatch.c',
|
'render.c','simplex.c','spline.c', 'transform.c','prosperon.c', 'wildmatch.c',
|
||||||
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c',
|
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_os.c',
|
||||||
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c'
|
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c'
|
||||||
]
|
]
|
||||||
# quirc src
|
# quirc src
|
||||||
|
|||||||
5321
source/jsffi.c
5321
source/jsffi.c
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,79 @@
|
|||||||
#define FFI_H
|
#define FFI_H
|
||||||
|
|
||||||
#include <quickjs.h>
|
#include <quickjs.h>
|
||||||
|
#include "HandmadeMath.h"
|
||||||
|
#include "render.h"
|
||||||
|
#include "stb_ds.h"
|
||||||
|
|
||||||
|
#define JS_SetProperty(js, tar, str, val) JS_SetPropertyStr(js, tar, #str, val)
|
||||||
|
#define JS_GetProperty(js, tar, atom) JS_GetPropertyStr(js, tar, #atom)
|
||||||
|
|
||||||
|
SDL_Window *global_window;
|
||||||
|
|
||||||
|
// Core FFI functions
|
||||||
void ffi_load(JSContext *js);
|
void ffi_load(JSContext *js);
|
||||||
int js_print_exception(JSContext *js, JSValue v);
|
int js_print_exception(JSContext *js, JSValue v);
|
||||||
|
|
||||||
|
// Common type definitions - rect and colorf are defined in render.h
|
||||||
|
|
||||||
|
// Common conversion functions used across modules
|
||||||
|
JSValue rect2js(JSContext *js, rect r);
|
||||||
|
JSValue vec22js(JSContext *js, HMM_Vec2 v);
|
||||||
|
JSValue vec32js(JSContext *js, HMM_Vec3 v);
|
||||||
|
JSValue vec42js(JSContext *js, HMM_Vec4 v);
|
||||||
|
JSValue quat2js(JSContext *js, HMM_Quat q);
|
||||||
|
JSValue color2js(JSContext *js, colorf c);
|
||||||
|
JSValue number2js(JSContext *js, double d);
|
||||||
|
JSValue angle2js(JSContext *js, double a);
|
||||||
|
|
||||||
|
rect js2rect(JSContext *js, JSValue v);
|
||||||
|
HMM_Vec2 js2vec2(JSContext *js, JSValue v);
|
||||||
|
HMM_Vec3 js2vec3(JSContext *js, JSValue v);
|
||||||
|
HMM_Vec4 js2vec4(JSContext *js, JSValue v);
|
||||||
|
HMM_Quat js2quat(JSContext *js, JSValue v);
|
||||||
|
colorf js2color(JSContext *js, JSValue v);
|
||||||
|
double js2number(JSContext *js, JSValue v);
|
||||||
|
double js2angle(JSContext *js, JSValue v);
|
||||||
|
|
||||||
|
// Forward declaration for MTRand from prosperon.h
|
||||||
|
typedef struct tagMTRand MTRand;
|
||||||
|
|
||||||
|
// Random number generation functions
|
||||||
|
double genRand(MTRand *mrand);
|
||||||
|
uint32_t genRandLong(MTRand *mrand);
|
||||||
|
void m_seedRand(MTRand *mrand, uint32_t seed);
|
||||||
|
double rand_range(JSContext *js, double min, double max);
|
||||||
|
|
||||||
|
// Common data structures
|
||||||
|
struct lrtb {
|
||||||
|
float l;
|
||||||
|
float r;
|
||||||
|
float t;
|
||||||
|
float b;
|
||||||
|
};
|
||||||
|
typedef struct lrtb lrtb;
|
||||||
|
typedef struct text_vert text_vert;
|
||||||
|
|
||||||
|
// Common macros for property access
|
||||||
|
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
|
||||||
|
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \
|
||||||
|
TARGET = js2##TYPE(JS, __##PROP##__v); \
|
||||||
|
JS_FreeValue(JS,__##PROP##__v); }\
|
||||||
|
|
||||||
|
#define JS_GETATOM(JS, TARGET, VALUE, ATOM, TYPE) {\
|
||||||
|
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#ATOM); \
|
||||||
|
TARGET = js2##TYPE(JS, __##PROP##__v); \
|
||||||
|
JS_FreeValue(JS,__##PROP##__v); }\
|
||||||
|
|
||||||
|
// Common conversion functions
|
||||||
|
lrtb js2lrtb(JSContext *js, JSValue v);
|
||||||
|
int js2bool(JSContext *js, JSValue v);
|
||||||
|
JSValue make_gpu_buffer(JSContext *js, void *data, size_t size, int type, int elements, int copy, int index);
|
||||||
|
JSValue make_quad_indices_buffer(JSContext *js, int quads);
|
||||||
|
JSValue quads_to_mesh(JSContext *js, text_vert *buffer);
|
||||||
|
|
||||||
|
// SDL type conversion functions
|
||||||
|
SDL_Window *js2SDL_Window(JSContext *js, JSValue v);
|
||||||
|
JSValue SDL_Window2js(JSContext *js, SDL_Window *w);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -49,11 +49,11 @@ static struct { char *key; prosperon_rt *value; } *actors = NULL;
|
|||||||
static unsigned char *zip_buffer_global = NULL;
|
static unsigned char *zip_buffer_global = NULL;
|
||||||
static char *prosperon = NULL;
|
static char *prosperon = NULL;
|
||||||
|
|
||||||
static prosperon_rt *io_actor = NULL;
|
prosperon_rt *io_actor = NULL;
|
||||||
|
|
||||||
static Uint32 queue_event;
|
static Uint32 queue_event;
|
||||||
|
|
||||||
static SDL_AtomicInt shutdown;
|
static SDL_AtomicInt engine_shutdown;
|
||||||
static SDL_Thread **runners = NULL;
|
static SDL_Thread **runners = NULL;
|
||||||
|
|
||||||
static Uint32 actor_remove_cb(prosperon_rt *actor, Uint32 id, Uint32 interval)
|
static Uint32 actor_remove_cb(prosperon_rt *actor, Uint32 id, Uint32 interval)
|
||||||
@@ -673,7 +673,7 @@ void script_evalf(JSContext *js, const char *format, ...)
|
|||||||
|
|
||||||
static int crank_actor(void *data)
|
static int crank_actor(void *data)
|
||||||
{
|
{
|
||||||
while (!SDL_GetAtomicInt(&shutdown)) {
|
while (!SDL_GetAtomicInt(&engine_shutdown)) {
|
||||||
SDL_LockMutex(queue_mutex);
|
SDL_LockMutex(queue_mutex);
|
||||||
prosperon_rt *actor = NULL;
|
prosperon_rt *actor = NULL;
|
||||||
if (arrlen(ready_queue) > 0) {
|
if (arrlen(ready_queue) > 0) {
|
||||||
@@ -720,7 +720,7 @@ static void exit_handler(void)
|
|||||||
script_evalf(js, "prosperon.dispatch('exit')");
|
script_evalf(js, "prosperon.dispatch('exit')");
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SetAtomicInt(&shutdown, 1);
|
SDL_SetAtomicInt(&engine_shutdown, 1);
|
||||||
int status;
|
int status;
|
||||||
SDL_BroadcastCondition(queue_cond);
|
SDL_BroadcastCondition(queue_cond);
|
||||||
for (int i = 0; i < arrlen(runners); i++)
|
for (int i = 0; i < arrlen(runners); i++)
|
||||||
|
|||||||
447
source/qjs_geometry.c
Normal file
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 int START = 0;
|
||||||
|
|
||||||
static inline int js_arrlen(JSContext *js, JSValue v)
|
|
||||||
{
|
|
||||||
JSValue len = JS_GetPropertyStr(js, v, "length");
|
|
||||||
int intlen;
|
|
||||||
JS_ToInt32(js, &intlen, len);
|
|
||||||
JS_FreeValue(js, len);
|
|
||||||
return intlen;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline double js2number(JSContext *js, JSValue v)
|
static inline double js2number(JSContext *js, JSValue v)
|
||||||
{
|
{
|
||||||
double n;
|
double n;
|
||||||
@@ -176,12 +167,12 @@ void fill_plotdata(JSContext *js, JSValue v, JSValue last)
|
|||||||
/* arrsetlen(plotdata, 0);
|
/* arrsetlen(plotdata, 0);
|
||||||
|
|
||||||
if (JS_IsArray(js,js_getpropidx(v, 0))) {
|
if (JS_IsArray(js,js_getpropidx(v, 0))) {
|
||||||
for (int i = 0; i < js_arrlen(js, v); i++)
|
for (int i = 0; i < JS_ArrayLength(js, v); i++)
|
||||||
arrput(plotdata, js2vec2(js, js_getpropidx(v, i)));
|
arrput(plotdata, js2vec2(js, js_getpropidx(v, i)));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Fill it with the x axis being the array index
|
// Fill it with the x axis being the array index
|
||||||
for (int i = 0; i < js_arrlen(js, v); i++) {
|
for (int i = 0; i < JS_ArrayLength(js, v); i++) {
|
||||||
if (JS_IsUndefined(js_getpropidx(v,i))) continue;
|
if (JS_IsUndefined(js_getpropidx(v,i))) continue;
|
||||||
ImVec2 c;
|
ImVec2 c;
|
||||||
c.x = i;
|
c.x = i;
|
||||||
@@ -205,13 +196,13 @@ PLOT_FN(digitalplot, PlotDigital,,0)
|
|||||||
|
|
||||||
JSC_SCALL(imgui_barplot,
|
JSC_SCALL(imgui_barplot,
|
||||||
fill_plotdata(js, argv[1], JS_UNDEFINED);
|
fill_plotdata(js, argv[1], JS_UNDEFINED);
|
||||||
ImPlot::PlotBars(str, &plotdata[0].x, &plotdata[0].y, js_arrlen(js, argv[1]), js2number(js, argv[2]), 0, 0, sizeof(ImVec2));
|
ImPlot::PlotBars(str, &plotdata[0].x, &plotdata[0].y, JS_ArrayLength(js, argv[1]), js2number(js, argv[2]), 0, 0, sizeof(ImVec2));
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_SCALL(imgui_histogramplot,
|
JSC_SCALL(imgui_histogramplot,
|
||||||
size_t offset, len, per_e;
|
size_t offset, len, per_e;
|
||||||
JSValue typed = JS_GetTypedArrayBuffer(js, argv[1], &offset, &len, &per_e);
|
JSValue typed = JS_GetTypedArrayBuffer(js, argv[1], &offset, &len, &per_e);
|
||||||
ImPlot::PlotHistogram(str, JS_GetArrayBuffer(js, NULL, typed), js_arrlen(js, argv[1]));
|
ImPlot::PlotHistogram(str, JS_GetArrayBuffer(js, NULL, typed), JS_ArrayLength(js, argv[1]));
|
||||||
JS_FreeValue(js, typed);
|
JS_FreeValue(js, typed);
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -290,7 +281,7 @@ JSC_SCALL(imgui_slider,
|
|||||||
float high = JS_IsUndefined(argv[3]) ? 1.0 : js2number(js, argv[3]);
|
float high = JS_IsUndefined(argv[3]) ? 1.0 : js2number(js, argv[3]);
|
||||||
|
|
||||||
if (JS_IsArray(js, argv[1])) {
|
if (JS_IsArray(js, argv[1])) {
|
||||||
int n = js_arrlen(js, argv[1]);
|
int n = JS_ArrayLength(js, argv[1]);
|
||||||
float a[n];
|
float a[n];
|
||||||
js2floatarr(argv[1], n, a);
|
js2floatarr(argv[1], n, a);
|
||||||
|
|
||||||
@@ -394,7 +385,7 @@ JSC_SCALL(imgui_tab,
|
|||||||
|
|
||||||
JSC_SCALL(imgui_listbox,
|
JSC_SCALL(imgui_listbox,
|
||||||
/* char **arr = js2strarr(argv[1]);
|
/* char **arr = js2strarr(argv[1]);
|
||||||
int n = js_arrlen(js, argv[1]);
|
int n = JS_ArrayLength(js, argv[1]);
|
||||||
int idx = js2number(js, argv[2]);
|
int idx = js2number(js, argv[2]);
|
||||||
ImGui::ListBox(str, &idx, arr, n, 4);
|
ImGui::ListBox(str, &idx, arr, n, 4);
|
||||||
for (int i = 0; i < n; i++)
|
for (int i = 0; i < n; i++)
|
||||||
@@ -495,7 +486,7 @@ JSC_SCALL(imgui_dndtarget,
|
|||||||
)
|
)
|
||||||
|
|
||||||
JSC_SCALL(imgui_color,
|
JSC_SCALL(imgui_color,
|
||||||
/* int n = js_arrlen(js, argv[1]);
|
/* int n = JS_ArrayLength(js, argv[1]);
|
||||||
float color[n];
|
float color[n];
|
||||||
js2floatarr(argv[1],n,color);
|
js2floatarr(argv[1],n,color);
|
||||||
|
|
||||||
|
|||||||
382
source/qjs_io.c
Normal file
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; \
|
return ret; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define JSC_CCALL_EXTERN(NAME, ...) JSValue js_##NAME (JSContext *js, JSValue self, int argc, JSValue *argv) { \
|
||||||
|
JSValue ret = JS_UNDEFINED; \
|
||||||
|
__VA_ARGS__ ;\
|
||||||
|
return ret; \
|
||||||
|
}
|
||||||
|
|
||||||
#define JSC_SCALL(NAME, ...) JSC_CCALL(NAME, \
|
#define JSC_SCALL(NAME, ...) JSC_CCALL(NAME, \
|
||||||
const char *str = JS_ToCString(js,argv[0]); \
|
const char *str = JS_ToCString(js,argv[0]); \
|
||||||
__VA_ARGS__ ;\
|
__VA_ARGS__ ;\
|
||||||
@@ -136,6 +142,29 @@ JSValue TYPE##2js(JSContext *js, TYPE *n) { \
|
|||||||
return j; }\
|
return j; }\
|
||||||
\
|
\
|
||||||
|
|
||||||
|
#define QJSCLASSMARK_EXTERN(TYPE, ...)\
|
||||||
|
static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\
|
||||||
|
TYPE *n = JS_GetOpaque(val, js_##TYPE##_id);\
|
||||||
|
TracyCFreeN(n, #TYPE); \
|
||||||
|
TYPE##_free(rt,n);}\
|
||||||
|
static JSClassDef js_##TYPE##_class = {\
|
||||||
|
.class_name = #TYPE,\
|
||||||
|
.finalizer = js_##TYPE##_finalizer,\
|
||||||
|
.gc_mark = js_##TYPE##_mark,\
|
||||||
|
};\
|
||||||
|
extern JSClassID js_##TYPE##_id;\
|
||||||
|
TYPE *js2##TYPE (JSContext *js, JSValue val) { \
|
||||||
|
if (JS_GetClassID(val) != js_##TYPE##_id) return NULL; \
|
||||||
|
return JS_GetOpaque(val,js_##TYPE##_id); \
|
||||||
|
}\
|
||||||
|
JSValue TYPE##2js(JSContext *js, TYPE *n) { \
|
||||||
|
JSValue j = JS_NewObjectClass(js,js_##TYPE##_id);\
|
||||||
|
JS_SetOpaque(j,n);\
|
||||||
|
__VA_ARGS__ \
|
||||||
|
TracyCAllocN(n, 1, #TYPE); \
|
||||||
|
return j; }\
|
||||||
|
\
|
||||||
|
|
||||||
#define QJSGLOBALCLASS(NAME) \
|
#define QJSGLOBALCLASS(NAME) \
|
||||||
JSValue NAME = JS_NewObject(js); \
|
JSValue NAME = JS_NewObject(js); \
|
||||||
JS_SetPropertyFunctionList(js, NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \
|
JS_SetPropertyFunctionList(js, NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \
|
||||||
@@ -153,11 +182,6 @@ JSValue TYPE##_proto = JS_NewObject(js); \
|
|||||||
JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \
|
JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \
|
||||||
JS_SetPropertyStr(js, c_types, #TYPE, JS_DupValue(js,TYPE##_proto)); \
|
JS_SetPropertyStr(js, c_types, #TYPE, JS_DupValue(js,TYPE##_proto)); \
|
||||||
|
|
||||||
#define MISTUSE(NAME) \
|
|
||||||
JSValue js_##NAME##_use(JSContext *js) { \
|
|
||||||
JSValue mod = JS_NewObject(js); \
|
|
||||||
JS_SetPropertyFunctionList(js,mod,js_##NAME##_funcs,countof(js_##NAME##_funcs)); \
|
|
||||||
return mod; } \
|
|
||||||
|
|
||||||
#define countof(x) (sizeof(x)/sizeof((x)[0]))
|
#define countof(x) (sizeof(x)/sizeof((x)[0]))
|
||||||
|
|
||||||
|
|||||||
509
source/qjs_math.c
Normal file
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])) {
|
if (JS_IsArray(js,argv[0])) {
|
||||||
int len = js_arrlen(js,argv[0]);
|
int len = JS_ArrayLength(js,argv[0]);
|
||||||
rect rects[len];
|
rect rects[len];
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
|
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
|
||||||
@@ -81,8 +81,8 @@ JSC_CCALL(renderer_load_texture,
|
|||||||
SDL_Texture *tex = SDL_CreateTextureFromSurface(r,surf);
|
SDL_Texture *tex = SDL_CreateTextureFromSurface(r,surf);
|
||||||
if (!tex) return JS_ThrowReferenceError(js, "Could not create texture from surface: %s", SDL_GetError());
|
if (!tex) return JS_ThrowReferenceError(js, "Could not create texture from surface: %s", SDL_GetError());
|
||||||
ret = SDL_Texture2js(js,tex);
|
ret = SDL_Texture2js(js,tex);
|
||||||
JS_SetProperty(js,ret,width, number2js(js,tex->w));
|
JS_SetPropertyStr(js,ret,"width", number2js(js,tex->w));
|
||||||
JS_SetProperty(js,ret,height, number2js(js,tex->h));
|
JS_SetPropertyStr(js,ret,"height", number2js(js,tex->h));
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(SDL_Renderer_fillrect,
|
JSC_CCALL(SDL_Renderer_fillrect,
|
||||||
@@ -93,7 +93,7 @@ JSC_CCALL(SDL_Renderer_fillrect,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (JS_IsArray(js,argv[0])) {
|
if (JS_IsArray(js,argv[0])) {
|
||||||
int len = js_arrlen(js,argv[0]);
|
int len = JS_ArrayLength(js,argv[0]);
|
||||||
rect rects[len];
|
rect rects[len];
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
|
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
|
||||||
@@ -193,7 +193,7 @@ JSC_CCALL(renderer_line,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (JS_IsArray(js,argv[0])) {
|
if (JS_IsArray(js,argv[0])) {
|
||||||
int len = js_arrlen(js,argv[0]);
|
int len = JS_ArrayLength(js,argv[0]);
|
||||||
HMM_Vec2 points[len];
|
HMM_Vec2 points[len];
|
||||||
assert(sizeof(HMM_Vec2) == sizeof(SDL_FPoint));
|
assert(sizeof(HMM_Vec2) == sizeof(SDL_FPoint));
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
@@ -213,7 +213,7 @@ JSC_CCALL(renderer_point,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (JS_IsArray(js,argv[0])) {
|
if (JS_IsArray(js,argv[0])) {
|
||||||
int len = js_arrlen(js,argv[0]);
|
int len = JS_ArrayLength(js,argv[0]);
|
||||||
HMM_Vec2 points[len];
|
HMM_Vec2 points[len];
|
||||||
assert(sizeof(HMM_Vec2) ==sizeof(SDL_FPoint));
|
assert(sizeof(HMM_Vec2) ==sizeof(SDL_FPoint));
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
@@ -393,7 +393,7 @@ JSC_CCALL(renderer_target,
|
|||||||
// color: the color this sprite should be hued by
|
// color: the color this sprite should be hued by
|
||||||
JSC_CCALL(renderer_make_sprite_mesh,
|
JSC_CCALL(renderer_make_sprite_mesh,
|
||||||
JSValue sprites = argv[0];
|
JSValue sprites = argv[0];
|
||||||
size_t quads = js_arrlen(js,argv[0]);
|
size_t quads = JS_ArrayLength(js,argv[0]);
|
||||||
size_t verts = quads*4;
|
size_t verts = quads*4;
|
||||||
size_t count = quads*6;
|
size_t count = quads*6;
|
||||||
|
|
||||||
@@ -403,10 +403,10 @@ JSC_CCALL(renderer_make_sprite_mesh,
|
|||||||
|
|
||||||
for (int i = 0; i < quads; i++) {
|
for (int i = 0; i < quads; i++) {
|
||||||
JSValue sub = JS_GetPropertyUint32(js,sprites,i);
|
JSValue sub = JS_GetPropertyUint32(js,sprites,i);
|
||||||
JSValue jstransform = JS_GetProperty(js,sub,transform);
|
JSValue jstransform = JS_GetPropertyStr(js,sub,"transform");
|
||||||
|
|
||||||
JSValue jssrc = JS_GetProperty(js,sub,src);
|
JSValue jssrc = JS_GetPropertyStr(js,sub,"src");
|
||||||
JSValue jscolor = JS_GetProperty(js,sub,color);
|
JSValue jscolor = JS_GetPropertyStr(js,sub,"color");
|
||||||
HMM_Vec4 color;
|
HMM_Vec4 color;
|
||||||
|
|
||||||
rect src;
|
rect src;
|
||||||
@@ -441,12 +441,12 @@ JSC_CCALL(renderer_make_sprite_mesh,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = JS_NewObject(js);
|
ret = JS_NewObject(js);
|
||||||
JS_SetProperty(js, ret, pos, make_gpu_buffer(js, posdata, sizeof(*posdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0));
|
JS_SetPropertyStr(js, ret, "pos", make_gpu_buffer(js, posdata, sizeof(*posdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0));
|
||||||
JS_SetProperty(js, ret, uv, make_gpu_buffer(js, uvdata, sizeof(*uvdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0));
|
JS_SetPropertyStr(js, ret, "uv", make_gpu_buffer(js, uvdata, sizeof(*uvdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0));
|
||||||
JS_SetProperty(js, ret, color, make_gpu_buffer(js, colordata, sizeof(*colordata) * verts, JS_TYPED_ARRAY_FLOAT32, 4, 0,0));
|
JS_SetPropertyStr(js, ret, "color", make_gpu_buffer(js, colordata, sizeof(*colordata) * verts, JS_TYPED_ARRAY_FLOAT32, 4, 0,0));
|
||||||
JS_SetProperty(js, ret, indices, make_quad_indices_buffer(js, quads));
|
JS_SetPropertyStr(js, ret, "indices", make_quad_indices_buffer(js, quads));
|
||||||
JS_SetProperty(js, ret, vertices, number2js(js, verts));
|
JS_SetPropertyStr(js, ret, "vertices", number2js(js, verts));
|
||||||
JS_SetProperty(js, ret, count, number2js(js, count));
|
JS_SetPropertyStr(js, ret, "count", number2js(js, count));
|
||||||
)
|
)
|
||||||
|
|
||||||
static const JSCFunctionListEntry js_renderer_funcs[] = {
|
static const JSCFunctionListEntry js_renderer_funcs[] = {
|
||||||
|
|||||||
476
source/qjs_sdl.c
Normal file
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