763 lines
26 KiB
C
763 lines
26 KiB
C
#include "qjs_sdl.h"
|
|
#include "jsffi.h"
|
|
#include "qjs_macros.h"
|
|
#include "cell.h"
|
|
#include "qjs_blob.h"
|
|
#include "stb_ds.h"
|
|
#include "qjs_actor.h"
|
|
#include "qjs_sdl_surface.h"
|
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
// SDL Free functions
|
|
void SDL_Camera_free(JSRuntime *rt, SDL_Camera *cam)
|
|
{
|
|
SDL_CloseCamera(cam);
|
|
}
|
|
|
|
void SDL_AudioStream_free(JSRuntime *rt, SDL_AudioStream *st) {
|
|
SDL_DestroyAudioStream(st);
|
|
}
|
|
|
|
// Class definitions for SDL types
|
|
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_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;
|
|
)
|
|
|
|
// watch events
|
|
extern char **event_watchers;
|
|
extern SDL_Mutex *event_watchers_mutex;
|
|
|
|
JSC_CCALL(input_watch,
|
|
/* Use js2actor to get the actor from the JS object */
|
|
cell_rt *actor = js2actor(js, argv[0]);
|
|
if (!actor)
|
|
return JS_ThrowTypeError(js, "First argument must be a valid actor object");
|
|
|
|
SDL_LockMutex(event_watchers_mutex);
|
|
|
|
/* Check if already watching */
|
|
int already_watching = 0;
|
|
for (int i = 0; i < arrlen(event_watchers); i++) {
|
|
if (strcmp(event_watchers[i], actor->id) == 0) {
|
|
already_watching = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!already_watching) {
|
|
arrput(event_watchers, strdup(actor->id));
|
|
}
|
|
|
|
SDL_UnlockMutex(event_watchers_mutex);
|
|
|
|
return JS_UNDEFINED;
|
|
)
|
|
|
|
JSC_CCALL(input_unwatch,
|
|
/* Use js2actor to get the actor from the JS object */
|
|
cell_rt *actor = js2actor(js, argv[0]);
|
|
if (!actor)
|
|
return JS_ThrowTypeError(js, "First argument must be a valid actor object");
|
|
|
|
SDL_LockMutex(event_watchers_mutex);
|
|
|
|
/* Find and remove from watchers */
|
|
for (int i = 0; i < arrlen(event_watchers); i++) {
|
|
if (strcmp(event_watchers[i], actor->id) == 0) {
|
|
free(event_watchers[i]);
|
|
arrdel(event_watchers, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDL_UnlockMutex(event_watchers_mutex);
|
|
|
|
return JS_UNDEFINED;
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_input_funcs[] = {
|
|
MIST_FUNC_DEF(input, mouse_show, 1),
|
|
MIST_FUNC_DEF(input, mouse_lock, 1),
|
|
MIST_FUNC_DEF(input, keyname, 1),
|
|
MIST_FUNC_DEF(input, keymod, 0),
|
|
MIST_FUNC_DEF(input, mousestate, 0),
|
|
MIST_FUNC_DEF(input, watch, 1),
|
|
MIST_FUNC_DEF(input, unwatch, 1),
|
|
};
|
|
|
|
JSValue js_input_use(JSContext *js) {
|
|
JSValue mod = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js,mod,js_input_funcs,countof(js_input_funcs));
|
|
return mod;
|
|
}
|
|
|
|
// CAMERA FUNCTIONS
|
|
|
|
// Helper functions for camera format conversion
|
|
const char *pixelformat2str(SDL_PixelFormat format) {
|
|
switch(format) {
|
|
case SDL_PIXELFORMAT_UNKNOWN: return "unknown";
|
|
case SDL_PIXELFORMAT_INDEX1LSB: return "index1lsb";
|
|
case SDL_PIXELFORMAT_INDEX1MSB: return "index1msb";
|
|
case SDL_PIXELFORMAT_INDEX2LSB: return "index2lsb";
|
|
case SDL_PIXELFORMAT_INDEX2MSB: return "index2msb";
|
|
case SDL_PIXELFORMAT_INDEX4LSB: return "index4lsb";
|
|
case SDL_PIXELFORMAT_INDEX4MSB: return "index4msb";
|
|
case SDL_PIXELFORMAT_INDEX8: return "index8";
|
|
case SDL_PIXELFORMAT_RGB332: return "rgb332";
|
|
case SDL_PIXELFORMAT_XRGB4444: return "xrgb4444";
|
|
case SDL_PIXELFORMAT_XBGR4444: return "xbgr4444";
|
|
case SDL_PIXELFORMAT_XRGB1555: return "xrgb1555";
|
|
case SDL_PIXELFORMAT_XBGR1555: return "xbgr1555";
|
|
case SDL_PIXELFORMAT_ARGB4444: return "argb4444";
|
|
case SDL_PIXELFORMAT_RGBA4444: return "rgba4444";
|
|
case SDL_PIXELFORMAT_ABGR4444: return "abgr4444";
|
|
case SDL_PIXELFORMAT_BGRA4444: return "bgra4444";
|
|
case SDL_PIXELFORMAT_ARGB1555: return "argb1555";
|
|
case SDL_PIXELFORMAT_RGBA5551: return "rgba5551";
|
|
case SDL_PIXELFORMAT_ABGR1555: return "abgr1555";
|
|
case SDL_PIXELFORMAT_BGRA5551: return "bgra5551";
|
|
case SDL_PIXELFORMAT_RGB565: return "rgb565";
|
|
case SDL_PIXELFORMAT_BGR565: return "bgr565";
|
|
case SDL_PIXELFORMAT_RGB24: return "rgb24";
|
|
case SDL_PIXELFORMAT_BGR24: return "bgr24";
|
|
case SDL_PIXELFORMAT_XRGB8888: return "xrgb8888";
|
|
case SDL_PIXELFORMAT_RGBX8888: return "rgbx8888";
|
|
case SDL_PIXELFORMAT_XBGR8888: return "xbgr8888";
|
|
case SDL_PIXELFORMAT_BGRX8888: return "bgrx8888";
|
|
case SDL_PIXELFORMAT_ARGB8888: return "argb8888";
|
|
case SDL_PIXELFORMAT_RGBA8888: return "rgba8888";
|
|
case SDL_PIXELFORMAT_ABGR8888: return "abgr8888";
|
|
case SDL_PIXELFORMAT_BGRA8888: return "bgra8888";
|
|
case SDL_PIXELFORMAT_XRGB2101010: return "xrgb2101010";
|
|
case SDL_PIXELFORMAT_XBGR2101010: return "xbgr2101010";
|
|
case SDL_PIXELFORMAT_ARGB2101010: return "argb2101010";
|
|
case SDL_PIXELFORMAT_ABGR2101010: return "abgr2101010";
|
|
case SDL_PIXELFORMAT_RGB48: return "rgb48";
|
|
case SDL_PIXELFORMAT_BGR48: return "bgr48";
|
|
case SDL_PIXELFORMAT_RGBA64: return "rgba64";
|
|
case SDL_PIXELFORMAT_ARGB64: return "argb64";
|
|
case SDL_PIXELFORMAT_BGRA64: return "bgra64";
|
|
case SDL_PIXELFORMAT_ABGR64: return "abgr64";
|
|
case SDL_PIXELFORMAT_RGB48_FLOAT: return "rgb48_float";
|
|
case SDL_PIXELFORMAT_BGR48_FLOAT: return "bgr48_float";
|
|
case SDL_PIXELFORMAT_RGBA64_FLOAT: return "rgba64_float";
|
|
case SDL_PIXELFORMAT_ARGB64_FLOAT: return "argb64_float";
|
|
case SDL_PIXELFORMAT_BGRA64_FLOAT: return "bgra64_float";
|
|
case SDL_PIXELFORMAT_ABGR64_FLOAT: return "abgr64_float";
|
|
case SDL_PIXELFORMAT_RGB96_FLOAT: return "rgb96_float";
|
|
case SDL_PIXELFORMAT_BGR96_FLOAT: return "bgr96_float";
|
|
case SDL_PIXELFORMAT_RGBA128_FLOAT: return "rgba128_float";
|
|
case SDL_PIXELFORMAT_ARGB128_FLOAT: return "argb128_float";
|
|
case SDL_PIXELFORMAT_BGRA128_FLOAT: return "bgra128_float";
|
|
case SDL_PIXELFORMAT_ABGR128_FLOAT: return "abgr128_float";
|
|
case SDL_PIXELFORMAT_YV12: return "yv12";
|
|
case SDL_PIXELFORMAT_IYUV: return "iyuv";
|
|
case SDL_PIXELFORMAT_YUY2: return "yuy2";
|
|
case SDL_PIXELFORMAT_UYVY: return "uyvy";
|
|
case SDL_PIXELFORMAT_YVYU: return "yvyu";
|
|
case SDL_PIXELFORMAT_NV12: return "nv12";
|
|
case SDL_PIXELFORMAT_NV21: return "nv21";
|
|
case SDL_PIXELFORMAT_P010: return "p010";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
SDL_PixelFormat str2pixelformat(const char *str) {
|
|
if (!strcmp(str, "unknown")) return SDL_PIXELFORMAT_UNKNOWN;
|
|
if (!strcmp(str, "index1lsb")) return SDL_PIXELFORMAT_INDEX1LSB;
|
|
if (!strcmp(str, "index1msb")) return SDL_PIXELFORMAT_INDEX1MSB;
|
|
if (!strcmp(str, "index2lsb")) return SDL_PIXELFORMAT_INDEX2LSB;
|
|
if (!strcmp(str, "index2msb")) return SDL_PIXELFORMAT_INDEX2MSB;
|
|
if (!strcmp(str, "index4lsb")) return SDL_PIXELFORMAT_INDEX4LSB;
|
|
if (!strcmp(str, "index4msb")) return SDL_PIXELFORMAT_INDEX4MSB;
|
|
if (!strcmp(str, "index8")) return SDL_PIXELFORMAT_INDEX8;
|
|
if (!strcmp(str, "rgb332")) return SDL_PIXELFORMAT_RGB332;
|
|
if (!strcmp(str, "xrgb4444")) return SDL_PIXELFORMAT_XRGB4444;
|
|
if (!strcmp(str, "xbgr4444")) return SDL_PIXELFORMAT_XBGR4444;
|
|
if (!strcmp(str, "xrgb1555")) return SDL_PIXELFORMAT_XRGB1555;
|
|
if (!strcmp(str, "xbgr1555")) return SDL_PIXELFORMAT_XBGR1555;
|
|
if (!strcmp(str, "argb4444")) return SDL_PIXELFORMAT_ARGB4444;
|
|
if (!strcmp(str, "rgba4444")) return SDL_PIXELFORMAT_RGBA4444;
|
|
if (!strcmp(str, "abgr4444")) return SDL_PIXELFORMAT_ABGR4444;
|
|
if (!strcmp(str, "bgra4444")) return SDL_PIXELFORMAT_BGRA4444;
|
|
if (!strcmp(str, "argb1555")) return SDL_PIXELFORMAT_ARGB1555;
|
|
if (!strcmp(str, "rgba5551")) return SDL_PIXELFORMAT_RGBA5551;
|
|
if (!strcmp(str, "abgr1555")) return SDL_PIXELFORMAT_ABGR1555;
|
|
if (!strcmp(str, "bgra5551")) return SDL_PIXELFORMAT_BGRA5551;
|
|
if (!strcmp(str, "rgb565")) return SDL_PIXELFORMAT_RGB565;
|
|
if (!strcmp(str, "bgr565")) return SDL_PIXELFORMAT_BGR565;
|
|
if (!strcmp(str, "rgb24")) return SDL_PIXELFORMAT_RGB24;
|
|
if (!strcmp(str, "bgr24")) return SDL_PIXELFORMAT_BGR24;
|
|
if (!strcmp(str, "xrgb8888")) return SDL_PIXELFORMAT_XRGB8888;
|
|
if (!strcmp(str, "rgbx8888")) return SDL_PIXELFORMAT_RGBX8888;
|
|
if (!strcmp(str, "xbgr8888")) return SDL_PIXELFORMAT_XBGR8888;
|
|
if (!strcmp(str, "bgrx8888")) return SDL_PIXELFORMAT_BGRX8888;
|
|
if (!strcmp(str, "argb8888")) return SDL_PIXELFORMAT_ARGB8888;
|
|
if (!strcmp(str, "rgba8888")) return SDL_PIXELFORMAT_RGBA8888;
|
|
if (!strcmp(str, "abgr8888")) return SDL_PIXELFORMAT_ABGR8888;
|
|
if (!strcmp(str, "bgra8888")) return SDL_PIXELFORMAT_BGRA8888;
|
|
if (!strcmp(str, "xrgb2101010")) return SDL_PIXELFORMAT_XRGB2101010;
|
|
if (!strcmp(str, "xbgr2101010")) return SDL_PIXELFORMAT_XBGR2101010;
|
|
if (!strcmp(str, "argb2101010")) return SDL_PIXELFORMAT_ARGB2101010;
|
|
if (!strcmp(str, "abgr2101010")) return SDL_PIXELFORMAT_ABGR2101010;
|
|
if (!strcmp(str, "rgb48")) return SDL_PIXELFORMAT_RGB48;
|
|
if (!strcmp(str, "bgr48")) return SDL_PIXELFORMAT_BGR48;
|
|
if (!strcmp(str, "rgba64")) return SDL_PIXELFORMAT_RGBA64;
|
|
if (!strcmp(str, "argb64")) return SDL_PIXELFORMAT_ARGB64;
|
|
if (!strcmp(str, "bgra64")) return SDL_PIXELFORMAT_BGRA64;
|
|
if (!strcmp(str, "abgr64")) return SDL_PIXELFORMAT_ABGR64;
|
|
if (!strcmp(str, "rgb48_float")) return SDL_PIXELFORMAT_RGB48_FLOAT;
|
|
if (!strcmp(str, "bgr48_float")) return SDL_PIXELFORMAT_BGR48_FLOAT;
|
|
if (!strcmp(str, "rgba64_float")) return SDL_PIXELFORMAT_RGBA64_FLOAT;
|
|
if (!strcmp(str, "argb64_float")) return SDL_PIXELFORMAT_ARGB64_FLOAT;
|
|
if (!strcmp(str, "bgra64_float")) return SDL_PIXELFORMAT_BGRA64_FLOAT;
|
|
if (!strcmp(str, "abgr64_float")) return SDL_PIXELFORMAT_ABGR64_FLOAT;
|
|
if (!strcmp(str, "rgb96_float")) return SDL_PIXELFORMAT_RGB96_FLOAT;
|
|
if (!strcmp(str, "bgr96_float")) return SDL_PIXELFORMAT_BGR96_FLOAT;
|
|
if (!strcmp(str, "rgba128_float")) return SDL_PIXELFORMAT_RGBA128_FLOAT;
|
|
if (!strcmp(str, "argb128_float")) return SDL_PIXELFORMAT_ARGB128_FLOAT;
|
|
if (!strcmp(str, "bgra128_float")) return SDL_PIXELFORMAT_BGRA128_FLOAT;
|
|
if (!strcmp(str, "abgr128_float")) return SDL_PIXELFORMAT_ABGR128_FLOAT;
|
|
if (!strcmp(str, "yv12")) return SDL_PIXELFORMAT_YV12;
|
|
if (!strcmp(str, "iyuv")) return SDL_PIXELFORMAT_IYUV;
|
|
if (!strcmp(str, "yuy2")) return SDL_PIXELFORMAT_YUY2;
|
|
if (!strcmp(str, "uyvy")) return SDL_PIXELFORMAT_UYVY;
|
|
if (!strcmp(str, "yvyu")) return SDL_PIXELFORMAT_YVYU;
|
|
if (!strcmp(str, "nv12")) return SDL_PIXELFORMAT_NV12;
|
|
if (!strcmp(str, "nv21")) return SDL_PIXELFORMAT_NV21;
|
|
if (!strcmp(str, "p010")) return SDL_PIXELFORMAT_P010;
|
|
if (!strcmp(str, "rgba32")) return SDL_PIXELFORMAT_RGBA32;
|
|
return SDL_PIXELFORMAT_UNKNOWN;
|
|
}
|
|
|
|
static JSValue cameraspec2js(JSContext *js, const SDL_CameraSpec *spec) {
|
|
JSValue obj = JS_NewObject(js);
|
|
|
|
JS_SetPropertyStr(js, obj, "format", JS_NewString(js, pixelformat2str(spec->format)));
|
|
JS_SetPropertyStr(js, obj, "colorspace", JS_NewInt32(js, spec->colorspace));
|
|
JS_SetPropertyStr(js, obj, "width", JS_NewInt32(js, spec->width));
|
|
JS_SetPropertyStr(js, obj, "height", JS_NewInt32(js, spec->height));
|
|
JS_SetPropertyStr(js, obj, "framerate_numerator", JS_NewInt32(js, spec->framerate_numerator));
|
|
JS_SetPropertyStr(js, obj, "framerate_denominator", JS_NewInt32(js, spec->framerate_denominator));
|
|
|
|
return obj;
|
|
}
|
|
|
|
static SDL_CameraSpec js2cameraspec(JSContext *js, JSValue obj) {
|
|
SDL_CameraSpec spec = {0};
|
|
|
|
JSValue v;
|
|
|
|
v = JS_GetPropertyStr(js, obj, "format");
|
|
if (!JS_IsUndefined(v)) {
|
|
const char *s = JS_ToCString(js, v);
|
|
spec.format = str2pixelformat(s);
|
|
JS_FreeCString(js, s);
|
|
}
|
|
JS_FreeValue(js, v);
|
|
|
|
v = JS_GetPropertyStr(js, obj, "colorspace");
|
|
if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.colorspace, v);
|
|
JS_FreeValue(js, v);
|
|
|
|
v = JS_GetPropertyStr(js, obj, "width");
|
|
if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.width, v);
|
|
JS_FreeValue(js, v);
|
|
|
|
v = JS_GetPropertyStr(js, obj, "height");
|
|
if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.height, v);
|
|
JS_FreeValue(js, v);
|
|
|
|
v = JS_GetPropertyStr(js, obj, "framerate_numerator");
|
|
if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.framerate_numerator, v);
|
|
JS_FreeValue(js, v);
|
|
|
|
v = JS_GetPropertyStr(js, obj, "framerate_denominator");
|
|
if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.framerate_denominator, v);
|
|
JS_FreeValue(js, v);
|
|
|
|
return spec;
|
|
}
|
|
|
|
JSC_CCALL(camera_list,
|
|
int num;
|
|
JSValue jsids = JS_NewArray(js);
|
|
SDL_CameraID *ids = SDL_GetCameras(&num);
|
|
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_CameraSpec *spec_ptr = NULL;
|
|
SDL_CameraSpec spec;
|
|
|
|
// Check if a format spec was provided
|
|
if (argc > 1 && !JS_IsUndefined(argv[1])) {
|
|
spec = js2cameraspec(js, argv[1]);
|
|
spec_ptr = &spec;
|
|
}
|
|
|
|
SDL_Camera *cam = SDL_OpenCamera(id, spec_ptr);
|
|
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");
|
|
}
|
|
)
|
|
|
|
JSC_CCALL(camera_drivers,
|
|
int num = SDL_GetNumCameraDrivers();
|
|
JSValue arr = JS_NewArray(js);
|
|
for (int i = 0; i < num; i++)
|
|
JS_SetPropertyUint32(js, arr, i, JS_NewString(js, SDL_GetCameraDriver(i)));
|
|
return arr;
|
|
)
|
|
|
|
JSC_CCALL(camera_supported_formats,
|
|
SDL_CameraID id = js2number(js,argv[0]);
|
|
int num;
|
|
SDL_CameraSpec **specs = SDL_GetCameraSupportedFormats(id, &num);
|
|
|
|
if (!specs)
|
|
return JS_ThrowReferenceError(js, "Could not get supported formats for camera %d: %s", id, SDL_GetError());
|
|
|
|
JSValue arr = JS_NewArray(js);
|
|
for (int i = 0; i < num; i++) {
|
|
JS_SetPropertyUint32(js, arr, i, cameraspec2js(js, specs[i]));
|
|
}
|
|
|
|
SDL_free(specs);
|
|
return arr;
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_camera_funcs[] = {
|
|
MIST_FUNC_DEF(camera, list, 0),
|
|
MIST_FUNC_DEF(camera, open, 2),
|
|
MIST_FUNC_DEF(camera, name, 1),
|
|
MIST_FUNC_DEF(camera, position, 1),
|
|
MIST_FUNC_DEF(camera, drivers, 0),
|
|
MIST_FUNC_DEF(camera, supported_formats, 1),
|
|
};
|
|
|
|
JSC_CCALL(camera_capture,
|
|
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;
|
|
}
|
|
|
|
// Create a copy of the surface
|
|
SDL_Surface *newsurf = SDL_CreateSurface(surf->w, surf->h, surf->format);
|
|
|
|
if (!newsurf) {
|
|
SDL_ReleaseCameraFrame(cam, surf);
|
|
return JS_ThrowReferenceError(js, "Could not create surface: %s", SDL_GetError());
|
|
}
|
|
|
|
// Copy the surface data
|
|
int result = SDL_BlitSurface(surf, NULL, newsurf, NULL);
|
|
|
|
// Release the camera frame
|
|
SDL_ReleaseCameraFrame(cam, surf);
|
|
|
|
if (result != 0) {
|
|
SDL_DestroySurface(newsurf);
|
|
return JS_ThrowReferenceError(js, "Could not blit surface: %s", SDL_GetError());
|
|
}
|
|
|
|
return SDL_Surface2js(js,newsurf);
|
|
)
|
|
|
|
JSC_CCALL(camera_get_driver,
|
|
SDL_Camera *cam = js2SDL_Camera(js,self);
|
|
if (!cam) return JS_ThrowReferenceError(js,"Self was not a camera: %s", SDL_GetError());
|
|
|
|
const char *driver = SDL_GetCurrentCameraDriver();
|
|
if (!driver) return JS_UNDEFINED;
|
|
|
|
return JS_NewString(js, driver);
|
|
)
|
|
|
|
JSC_CCALL(camera_get_format,
|
|
SDL_Camera *cam = js2SDL_Camera(js,self);
|
|
if (!cam) return JS_ThrowReferenceError(js,"Self was not a camera: %s", SDL_GetError());
|
|
|
|
SDL_CameraSpec spec;
|
|
if (!SDL_GetCameraFormat(cam, &spec))
|
|
return JS_ThrowReferenceError(js, "Could not get camera format: %s", SDL_GetError());
|
|
|
|
return cameraspec2js(js, &spec);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_SDL_Camera_funcs[] =
|
|
{
|
|
MIST_FUNC_DEF(camera, capture, 0),
|
|
MIST_FUNC_DEF(camera, get_driver, 0),
|
|
MIST_FUNC_DEF(camera, get_format, 0),
|
|
};
|
|
|
|
JSValue js_camera_use(JSContext *js) {
|
|
SDL_Init(SDL_INIT_CAMERA);
|
|
JSValue mod = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js,mod,js_camera_funcs,countof(js_camera_funcs));
|
|
QJSCLASSPREP_FUNCS(SDL_Camera)
|
|
return mod;
|
|
}
|
|
|
|
// 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_get_blob_data(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_new_blob_stoned_copy(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) {
|
|
if (!SDL_Init(SDL_INIT_AUDIO))
|
|
return JS_ThrowInternalError(js, "Unable to initialize audio subsystem: %s", SDL_GetError());
|
|
|
|
QJSCLASSPREP_FUNCS(SDL_AudioStream)
|
|
|
|
JSValue mod = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js,mod,js_sdl_audio_funcs,countof(js_sdl_audio_funcs));
|
|
return mod;
|
|
}
|