initial add

This commit is contained in:
2025-12-11 10:39:24 -06:00
commit c116f4665e
23 changed files with 8649 additions and 0 deletions

418
audio.c Normal file
View File

@@ -0,0 +1,418 @@
#include <SDL3/SDL.h>
#include <SDL3/SDL_audio.h>
#include "cell.h"
#define countof(x) (sizeof(x)/sizeof((x)[0]))
// Helper functions
double js2number(JSContext *js, JSValue v);
int js2bool(JSContext *js, JSValue v);
// Free functions for finalizers
void SDL_AudioStream_free(JSRuntime *rt, SDL_AudioStream *stream) {
SDL_DestroyAudioStream(stream);
}
// Class definitions
QJSCLASS(SDL_AudioStream,)
// Conversion functions
SDL_AudioFormat js2SDL_AudioFormat(JSContext *js, JSValue v) {
int fmt = js2number(js, v);
return (SDL_AudioFormat)fmt;
}
JSValue SDL_AudioFormat2js(JSContext *js, SDL_AudioFormat fmt) {
return JS_NewInt32(js, (int)fmt);
}
SDL_AudioDeviceID js2SDL_AudioDeviceID(JSContext *js, JSValue v) {
return (SDL_AudioDeviceID)js2number(js, v);
}
JSValue SDL_AudioDeviceID2js(JSContext *js, SDL_AudioDeviceID id) {
return JS_NewInt32(js, (int)id);
}
SDL_AudioSpec js2SDL_AudioSpec(JSContext *js, JSValue v) {
SDL_AudioSpec spec = {0};
JS_GETPROP(js, spec.format, v, format, SDL_AudioFormat)
JS_GETPROP(js, spec.channels, v, channels, number)
JS_GETPROP(js, spec.freq, v, freq, number)
return spec;
}
JSValue SDL_AudioSpec2js(JSContext *js, SDL_AudioSpec spec) {
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "format", SDL_AudioFormat2js(js, spec.format));
JS_SetPropertyStr(js, obj, "channels", JS_NewInt32(js, spec.channels));
JS_SetPropertyStr(js, obj, "freq", JS_NewInt32(js, spec.freq));
return obj;
}
// Enum mappings for audio formats (simplified)
JSValue js_get_audio_drivers(JSContext *js, JSValue self, int argc, JSValue *argv) {
int count = SDL_GetNumAudioDrivers();
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
const char *driver = SDL_GetAudioDriver(i);
JS_SetPropertyUint32(js, arr, i, JS_NewString(js, driver));
}
return arr;
}
JSValue js_get_current_audio_driver(JSContext *js, JSValue self, int argc, JSValue *argv) {
const char *driver = SDL_GetCurrentAudioDriver();
return driver ? JS_NewString(js, driver) : JS_NULL;
}
JSValue js_get_audio_playback_devices(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID *devices = SDL_GetAudioPlaybackDevices(NULL);
if (!devices) return JS_NULL;
JSValue arr = JS_NewArray(js);
for (int i = 0; devices[i]; i++) {
JS_SetPropertyUint32(js, arr, i, SDL_AudioDeviceID2js(js, devices[i]));
}
SDL_free(devices);
return arr;
}
JSValue js_get_audio_recording_devices(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID *devices = SDL_GetAudioRecordingDevices(NULL);
if (!devices) return JS_NULL;
JSValue arr = JS_NewArray(js);
for (int i = 0; devices[i]; i++) {
JS_SetPropertyUint32(js, arr, i, SDL_AudioDeviceID2js(js, devices[i]));
}
SDL_free(devices);
return arr;
}
JSValue js_get_audio_device_name(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
const char *name = SDL_GetAudioDeviceName(devid);
return name ? JS_NewString(js, name) : JS_NULL;
}
JSValue js_is_audio_device_playback(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
return JS_NewBool(js, SDL_IsAudioDevicePlayback(devid));
}
JSValue js_is_audio_device_physical(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
return JS_NewBool(js, SDL_IsAudioDevicePhysical(devid));
}
JSValue js_get_audio_device_format(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
SDL_AudioSpec spec;
if (!SDL_GetAudioDeviceFormat(devid, &spec, NULL)) {
return JS_NULL;
}
return SDL_AudioSpec2js(js, spec);
}
JSValue js_open_audio_device_stream(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
SDL_AudioSpec spec = {0};
if (argc > 1) {
spec = js2SDL_AudioSpec(js, argv[1]);
}
SDL_AudioStream *stream = SDL_OpenAudioDeviceStream(devid, &spec, NULL, NULL);
if (!stream) {
return JS_ThrowInternalError(js, "Failed to open audio device stream: %s", SDL_GetError());
}
return SDL_AudioStream2js(js, stream);
}
JSValue js_create_audio_stream(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioSpec src_spec = js2SDL_AudioSpec(js, argv[0]);
SDL_AudioSpec dst_spec = js2SDL_AudioSpec(js, argv[1]);
SDL_AudioStream *stream = SDL_CreateAudioStream(&src_spec, &dst_spec);
if (!stream) {
return JS_ThrowInternalError(js, "Failed to create audio stream: %s", SDL_GetError());
}
return SDL_AudioStream2js(js, stream);
}
JSC_CCALL(audio_stream_put_data,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
size_t len;
void *data = js_get_blob_data(js, &len, argv[0]);
if (data == -1)
return JS_EXCEPTION;
if (!data)
return JS_ThrowReferenceError(js, "invalid audio stream data");
if (!SDL_PutAudioStreamData(stream, data, len))
return JS_ThrowInternalError(js, "Failed to put audio stream data: %s", SDL_GetError());
)
JSC_CCALL(audio_stream_get_data,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
int len = js2number(js, argv[0]);
void *data = malloc(len);
int got = SDL_GetAudioStreamData(stream, data, len);
if (got < 0) {
free(data);
ret = JS_ThrowInternalError(js, "Failed to get audio stream data: %s", SDL_GetError());
} else {
ret = js_new_blob_stoned_copy(js, data, got);
free(data);
}
)
JSC_CCALL(audio_stream_available,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
ret = JS_NewInt32(js, SDL_GetAudioStreamAvailable(stream));
)
JSC_CCALL(audio_stream_queued,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
ret = JS_NewInt32(js, SDL_GetAudioStreamQueued(stream));
)
JSC_CCALL(audio_stream_flush,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_FlushAudioStream(stream);
)
JSC_CCALL(audio_stream_clear,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_ClearAudioStream(stream);
)
JSC_CCALL(audio_stream_bind,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
if (!SDL_BindAudioStream(devid, stream)) {
ret = JS_ThrowInternalError(js, "Failed to bind audio stream: %s", SDL_GetError());
}
)
JSC_CCALL(audio_stream_unbind,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_UnbindAudioStream(stream);
)
JSC_CCALL(audio_stream_get_format,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_AudioSpec src, dst;
if (!SDL_GetAudioStreamFormat(stream, &src, &dst)) {
ret = JS_NULL;
} else {
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "src", SDL_AudioSpec2js(js, src));
JS_SetPropertyStr(js, obj, "dst", SDL_AudioSpec2js(js, dst));
ret = obj;
}
)
JSC_CCALL(audio_stream_get_device,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_AudioDeviceID devid = SDL_GetAudioStreamDevice(stream);
ret = SDL_AudioDeviceID2js(js, devid);
)
JSValue js_audio_stream_get_gain(JSContext *js, JSValue self) {
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
return JS_NewFloat64(js, SDL_GetAudioStreamGain(stream));
}
JSValue js_audio_stream_set_gain(JSContext *js, JSValue self, JSValue val) {
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
float gain = js2number(js, val);
if (!SDL_SetAudioStreamGain(stream, gain)) {
return JS_ThrowInternalError(js, "Failed to set audio stream gain: %s", SDL_GetError());
}
return JS_NULL;
}
JSValue js_audio_stream_get_frequency_ratio(JSContext *js, JSValue self) {
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
return JS_NewFloat64(js, SDL_GetAudioStreamFrequencyRatio(stream));
}
JSValue js_audio_stream_set_frequency_ratio(JSContext *js, JSValue self, JSValue val) {
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
float ratio = js2number(js, val);
if (!SDL_SetAudioStreamFrequencyRatio(stream, ratio)) {
return JS_ThrowInternalError(js, "Failed to set audio stream frequency ratio: %s", SDL_GetError());
}
return JS_NULL;
}
JSC_CCALL(audio_device_pause,
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
SDL_PauseAudioDevice(devid);
)
JSC_CCALL(audio_device_resume,
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
SDL_ResumeAudioDevice(devid);
)
JSC_CCALL(audio_device_paused,
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
ret = JS_NewBool(js, SDL_AudioDevicePaused(devid));
)
JSC_CCALL(audio_stream_device_paused,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
ret = JS_NewBool(js, SDL_AudioStreamDevicePaused(stream));
)
JSC_CCALL(audio_stream_pause_device,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_PauseAudioStreamDevice(stream);
)
JSC_CCALL(audio_stream_resume_device,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_ResumeAudioStreamDevice(stream);
)
JSC_CCALL(audio_device_close,
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
SDL_CloseAudioDevice(devid);
)
// Helper to open a stream on the default playback or recording device
// open_stream("playback") or open_stream("recording")
JSValue js_open_stream(JSContext *js, JSValue self, int argc, JSValue *argv) {
const char *type = JS_ToCString(js, argv[0]);
if (!type) return JS_EXCEPTION;
SDL_AudioDeviceID devid;
if (strcmp(type, "playback") == 0) {
devid = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
} else if (strcmp(type, "recording") == 0) {
devid = SDL_AUDIO_DEVICE_DEFAULT_RECORDING;
} else {
JS_FreeCString(js, type);
return JS_ThrowTypeError(js, "open_stream: type must be 'playback' or 'recording'");
}
JS_FreeCString(js, type);
// Create stream with default spec (will be set by set_format)
SDL_AudioSpec spec = {0};
spec.format = SDL_AUDIO_F32;
spec.channels = 2;
spec.freq = 44100;
SDL_AudioStream *stream = SDL_OpenAudioDeviceStream(devid, &spec, NULL, NULL);
if (!stream) {
return JS_ThrowInternalError(js, "Failed to open audio stream: %s", SDL_GetError());
}
return SDL_AudioStream2js(js, stream);
}
JSValue js_load_wav(JSContext *js, JSValue self, int argc, JSValue *argv) {
const char *path = JS_ToCString(js, argv[0]);
SDL_AudioSpec spec;
Uint8 *data;
Uint32 len;
if (!SDL_LoadWAV(path, &spec, &data, &len)) {
JS_FreeCString(js, path);
return JS_ThrowInternalError(js, "Failed to load WAV: %s", SDL_GetError());
}
JS_FreeCString(js, path);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "spec", SDL_AudioSpec2js(js, spec));
JS_SetPropertyStr(js, obj, "data", js_new_blob_stoned_copy(js, data, len));
SDL_free(data);
return obj;
}
JSC_CCALL(convert_audio_samples,
SDL_AudioSpec src_spec = js2SDL_AudioSpec(js, argv[0]);
SDL_AudioSpec dst_spec = js2SDL_AudioSpec(js, argv[1]);
size_t src_len;
void *src_data = js_get_blob_data(js, &src_len, argv[2]);
if (src_data == -1 || !src_data) {
ret = JS_EXCEPTION;
} else {
Uint8 *dst_data = NULL;
int dst_len = 0;
if (!SDL_ConvertAudioSamples(&src_spec, src_data, (int)src_len, &dst_spec, &dst_data, &dst_len)) {
ret = JS_ThrowInternalError(js, "Failed to convert audio samples: %s", SDL_GetError());
} else {
ret = js_new_blob_stoned_copy(js, dst_data, dst_len);
SDL_free(dst_data);
}
}
)
JSC_CCALL(mix_audio,
SDL_AudioFormat format = js2SDL_AudioFormat(js, argv[0]);
size_t dst_len, src_len;
void *dst = js_get_blob_data(js, &dst_len, argv[1]);
if (dst == -1 || !dst)
return JS_EXCEPTION;
void *src = js_get_blob_data(js, &src_len, argv[2]);
if (src == -1 || !src)
return JS_EXCEPTION;
if (dst_len == 0)
return JS_ThrowInternalError(js, "No destination audio data provided");
if (src_len == 0)
return JS_ThrowInternalError(js, "No source audio data provided");
if (dst_len != src_len)
return JS_ThrowInternalError(js, "Source and destination audio data must be the same length");
float volume = js2number(js, argv[3]);
SDL_MixAudio(dst, src, format, dst_len, volume);
)
// Function list for SDL_AudioStream
static const JSCFunctionListEntry js_SDL_AudioStream_funcs[] = {
JS_CFUNC_DEF("put", 1, js_audio_stream_put_data),
JS_CFUNC_DEF("get", 1, js_audio_stream_get_data),
JS_CFUNC_DEF("available", 0, js_audio_stream_available),
JS_CFUNC_DEF("queued", 0, js_audio_stream_queued),
JS_CFUNC_DEF("flush", 0, js_audio_stream_flush),
JS_CFUNC_DEF("clear", 0, js_audio_stream_clear),
JS_CFUNC_DEF("bind", 1, js_audio_stream_bind),
JS_CFUNC_DEF("unbind", 0, js_audio_stream_unbind),
JS_CFUNC_DEF("get_format", 0, js_audio_stream_get_format),
JS_CFUNC_DEF("get_device", 0, js_audio_stream_get_device),
JS_CGETSET_DEF("gain", js_audio_stream_get_gain, js_audio_stream_set_gain),
JS_CGETSET_DEF("frequency_ratio", js_audio_stream_get_frequency_ratio, js_audio_stream_set_frequency_ratio),
JS_CFUNC_DEF("pause_device", 0, js_audio_stream_pause_device),
JS_CFUNC_DEF("resume_device", 0, js_audio_stream_resume_device),
JS_CFUNC_DEF("device_paused", 0, js_audio_stream_device_paused),
};
// Main function list
static const JSCFunctionListEntry js_sdl_audio_funcs[] = {
JS_CFUNC_DEF("get_drivers", 0, js_get_audio_drivers),
JS_CFUNC_DEF("get_current_driver", 0, js_get_current_audio_driver),
JS_CFUNC_DEF("get_playback_devices", 0, js_get_audio_playback_devices),
JS_CFUNC_DEF("get_recording_devices", 0, js_get_audio_recording_devices),
JS_CFUNC_DEF("get_device_name", 1, js_get_audio_device_name),
JS_CFUNC_DEF("is_playback_device", 1, js_is_audio_device_playback),
JS_CFUNC_DEF("is_physical_device", 1, js_is_audio_device_physical),
JS_CFUNC_DEF("get_device_format", 1, js_get_audio_device_format),
JS_CFUNC_DEF("open_device_stream", 1, js_open_audio_device_stream),
JS_CFUNC_DEF("open_stream", 1, js_open_stream),
JS_CFUNC_DEF("create_stream", 2, js_create_audio_stream),
JS_CFUNC_DEF("pause_device", 1, js_audio_device_pause),
JS_CFUNC_DEF("resume_device", 1, js_audio_device_resume),
JS_CFUNC_DEF("device_paused", 1, js_audio_device_paused),
JS_CFUNC_DEF("close_device", 1, js_audio_device_close),
JS_CFUNC_DEF("load_wav", 1, js_load_wav),
JS_CFUNC_DEF("convert_samples", 3, js_convert_audio_samples),
JS_CFUNC_DEF("mix_audio", 4, js_mix_audio),
};
CELL_USE_INIT(
SDL_Init(SDL_INIT_AUDIO);
JS_NewClassID(&js_SDL_AudioStream_id);
JS_NewClass(JS_GetRuntime(js), js_SDL_AudioStream_id, &js_SDL_AudioStream_class);
JSValue proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, proto, js_SDL_AudioStream_funcs, countof(js_SDL_AudioStream_funcs));
JS_SetClassProto(js, js_SDL_AudioStream_id, proto);
JSValue export = JS_NewObject(js);
JS_SetPropertyFunctionList(js, export, js_sdl_audio_funcs, countof(js_sdl_audio_funcs));
return export;
)

195
camera.c Normal file
View File

@@ -0,0 +1,195 @@
#include "cell.h"
#include <SDL3/SDL.h>
#include "sdl.h"
// Camera position enum to string
static const char *camera_position_to_string(SDL_CameraPosition pos) {
switch (pos) {
case SDL_CAMERA_POSITION_FRONT_FACING: return "front";
case SDL_CAMERA_POSITION_BACK_FACING: return "back";
default: return "unknown";
}
}
// SDL_Camera class
void SDL_Camera_free(JSRuntime *rt, SDL_Camera *camera) {
if (camera) SDL_CloseCamera(camera);
}
QJSCLASS(SDL_Camera,)
// SDL_GetNumCameraDrivers() -> number
JSC_CCALL(camera_get_num_drivers,
return JS_NewInt32(js, SDL_GetNumCameraDrivers());
)
// SDL_GetCameraDriver(index) -> string
JSC_CCALL(camera_get_driver,
int index;
JS_ToInt32(js, &index, argv[0]);
const char *name = SDL_GetCameraDriver(index);
return name ? JS_NewString(js, name) : JS_NULL;
)
// SDL_GetCurrentCameraDriver() -> string
JSC_CCALL(camera_get_current_driver,
const char *name = SDL_GetCurrentCameraDriver();
return name ? JS_NewString(js, name) : JS_NULL;
)
// SDL_GetCameras() -> array of camera IDs
JSC_CCALL(camera_get_cameras,
int count = 0;
SDL_CameraID *cameras = SDL_GetCameras(&count);
if (!cameras) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewUint32(js, cameras[i]));
}
SDL_free(cameras);
return arr;
)
// SDL_GetCameraSupportedFormats(id) -> array of format objects
JSC_CCALL(camera_get_supported_formats,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
int count = 0;
SDL_CameraSpec **specs = SDL_GetCameraSupportedFormats(id, &count);
if (!specs) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
SDL_CameraSpec *spec = specs[i];
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "format", SDL_PixelFormat2js(js, spec->format));
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));
JS_SetPropertyUint32(js, arr, i, obj);
}
SDL_free(specs);
return arr;
)
// SDL_GetCameraName(id) -> string
JSC_CCALL(camera_get_name,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
const char *name = SDL_GetCameraName(id);
return name ? JS_NewString(js, name) : JS_NULL;
)
// SDL_GetCameraPosition(id) -> string
JSC_CCALL(camera_get_position,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_CameraPosition pos = SDL_GetCameraPosition(id);
return JS_NewString(js, camera_position_to_string(pos));
)
// SDL_OpenCamera(id, spec) -> Camera object
JSC_CCALL(camera_open,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_CameraSpec spec_val, *spec = NULL;
if (argc > 1 && !JS_IsNull(argv[1])) {
JSValue format_val = JS_GetPropertyStr(js, argv[1], "format");
spec_val.format = js2SDL_PixelFormat(js, format_val);
JS_FreeValue(js, format_val);
JS_GETATOM(js, spec_val.width, argv[1], width, number);
JS_GETATOM(js, spec_val.height, argv[1], height, number);
JS_GETATOM(js, spec_val.framerate_numerator, argv[1], framerate_numerator, number);
JS_GETATOM(js, spec_val.framerate_denominator, argv[1], framerate_denominator, number);
spec = &spec_val;
}
SDL_Camera *camera = SDL_OpenCamera(id, spec);
if (!camera) return JS_NULL;
return SDL_Camera2js(js, camera);
)
// Camera instance methods
JSC_CCALL(camera_get_permission_state,
SDL_Camera *camera = js2SDL_Camera(js, self);
return JS_NewInt32(js, SDL_GetCameraPermissionState(camera));
)
JSC_CCALL(camera_get_id,
SDL_Camera *camera = js2SDL_Camera(js, self);
return JS_NewUint32(js, SDL_GetCameraID(camera));
)
JSC_CCALL(camera_get_format,
SDL_Camera *camera = js2SDL_Camera(js, self);
SDL_CameraSpec spec;
if (!SDL_GetCameraFormat(camera, &spec)) return JS_NULL;
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "format", SDL_PixelFormat2js(js, spec.format));
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;
)
JSC_CCALL(camera_acquire_frame,
SDL_Camera *camera = js2SDL_Camera(js, self);
Uint64 timestamp;
SDL_Surface *surface = SDL_AcquireCameraFrame(camera, &timestamp);
if (!surface) return JS_NULL;
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "surface", SDL_Surface2js(js, surface));
JS_SetPropertyStr(js, obj, "timestamp", JS_NewInt64(js, timestamp));
return obj;
)
JSC_CCALL(camera_release_frame,
SDL_Camera *camera = js2SDL_Camera(js, self);
SDL_Surface *surface = js2SDL_Surface(js, argv[0]);
SDL_ReleaseCameraFrame(camera, surface);
return JS_NULL;
)
JSC_CCALL(camera_close,
SDL_Camera *camera = js2SDL_Camera(js, self);
SDL_CloseCamera(camera);
return JS_NULL;
)
static const JSCFunctionListEntry js_SDL_Camera_funcs[] = {
MIST_FUNC_DEF(camera, get_permission_state, 0),
MIST_FUNC_DEF(camera, get_id, 0),
MIST_FUNC_DEF(camera, get_format, 0),
MIST_FUNC_DEF(camera, acquire_frame, 0),
MIST_FUNC_DEF(camera, release_frame, 1),
MIST_FUNC_DEF(camera, close, 0),
};
static const JSCFunctionListEntry js_camera_funcs[] = {
MIST_FUNC_DEF(camera, get_num_drivers, 0),
MIST_FUNC_DEF(camera, get_driver, 1),
MIST_FUNC_DEF(camera, get_current_driver, 0),
MIST_FUNC_DEF(camera, get_cameras, 0),
MIST_FUNC_DEF(camera, get_supported_formats, 1),
MIST_FUNC_DEF(camera, get_name, 1),
MIST_FUNC_DEF(camera, get_position, 1),
MIST_FUNC_DEF(camera, open, 2),
};
CELL_USE_INIT(
SDL_Init(SDL_INIT_CAMERA);
QJSCLASSPREP_FUNCS(SDL_Camera);
JSValue ret = JS_NewObject(js);
JS_SetPropertyFunctionList(js, ret, js_camera_funcs, countof(js_camera_funcs));
return ret;
)

2
cell.toml Normal file
View File

@@ -0,0 +1,2 @@
[compilation]
LDFLAGS = "-lSDL3"

90
clipboard.c Normal file
View File

@@ -0,0 +1,90 @@
#include "cell.h"
#include <SDL3/SDL.h>
// SDL_SetClipboardText(text) -> bool
JSC_CCALL(clipboard_set_text,
const char *text = JS_ToCString(js, argv[0]);
if (!text) return JS_EXCEPTION;
bool result = SDL_SetClipboardText(text);
JS_FreeCString(js, text);
return JS_NewBool(js, result);
)
// SDL_GetClipboardText() -> string
JSC_CCALL(clipboard_get_text,
char *text = SDL_GetClipboardText();
if (!text) return JS_NewString(js, "");
JSValue result = JS_NewString(js, text);
SDL_free(text);
return result;
)
// SDL_HasClipboardText() -> bool
JSC_CCALL(clipboard_has_text,
return JS_NewBool(js, SDL_HasClipboardText());
)
// SDL_SetPrimarySelectionText(text) -> bool
JSC_CCALL(clipboard_set_primary_text,
const char *text = JS_ToCString(js, argv[0]);
if (!text) return JS_EXCEPTION;
bool result = SDL_SetPrimarySelectionText(text);
JS_FreeCString(js, text);
return JS_NewBool(js, result);
)
// SDL_GetPrimarySelectionText() -> string
JSC_CCALL(clipboard_get_primary_text,
char *text = SDL_GetPrimarySelectionText();
if (!text) return JS_NewString(js, "");
JSValue result = JS_NewString(js, text);
SDL_free(text);
return result;
)
// SDL_HasPrimarySelectionText() -> bool
JSC_CCALL(clipboard_has_primary_text,
return JS_NewBool(js, SDL_HasPrimarySelectionText());
)
// SDL_ClearClipboardData() -> bool
JSC_CCALL(clipboard_clear,
return JS_NewBool(js, SDL_ClearClipboardData());
)
// SDL_HasClipboardData(mime_type) -> bool
JSC_CCALL(clipboard_has_data,
const char *mime_type = JS_ToCString(js, argv[0]);
if (!mime_type) return JS_EXCEPTION;
bool result = SDL_HasClipboardData(mime_type);
JS_FreeCString(js, mime_type);
return JS_NewBool(js, result);
)
// SDL_GetClipboardMimeTypes() -> array of strings
JSC_CCALL(clipboard_get_mime_types,
size_t count = 0;
char **types = SDL_GetClipboardMimeTypes(&count);
if (!types) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (size_t i = 0; i < count; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewString(js, types[i]));
}
SDL_free(types);
return arr;
)
static const JSCFunctionListEntry js_clipboard_funcs[] = {
MIST_FUNC_DEF(clipboard, set_text, 1),
MIST_FUNC_DEF(clipboard, get_text, 0),
MIST_FUNC_DEF(clipboard, has_text, 0),
MIST_FUNC_DEF(clipboard, set_primary_text, 1),
MIST_FUNC_DEF(clipboard, get_primary_text, 0),
MIST_FUNC_DEF(clipboard, has_primary_text, 0),
MIST_FUNC_DEF(clipboard, clear, 0),
MIST_FUNC_DEF(clipboard, has_data, 1),
MIST_FUNC_DEF(clipboard, get_mime_types, 0),
};
CELL_USE_FUNCS(js_clipboard_funcs)

547
events.c Normal file
View File

@@ -0,0 +1,547 @@
#include "cell.h"
#include <SDL3/SDL.h>
// Event type to string conversion
static const char *event_type_to_string(Uint32 type) {
switch (type) {
// Application events
case SDL_EVENT_QUIT: return "quit";
case SDL_EVENT_TERMINATING: return "terminating";
case SDL_EVENT_LOW_MEMORY: return "low_memory";
case SDL_EVENT_WILL_ENTER_BACKGROUND: return "will_enter_background";
case SDL_EVENT_DID_ENTER_BACKGROUND: return "did_enter_background";
case SDL_EVENT_WILL_ENTER_FOREGROUND: return "will_enter_foreground";
case SDL_EVENT_DID_ENTER_FOREGROUND: return "did_enter_foreground";
case SDL_EVENT_LOCALE_CHANGED: return "locale_changed";
case SDL_EVENT_SYSTEM_THEME_CHANGED: return "system_theme_changed";
// Display events
case SDL_EVENT_DISPLAY_ORIENTATION: return "display_orientation";
case SDL_EVENT_DISPLAY_ADDED: return "display_added";
case SDL_EVENT_DISPLAY_REMOVED: return "display_removed";
case SDL_EVENT_DISPLAY_MOVED: return "display_moved";
case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED: return "display_content_scale_changed";
// Window events
case SDL_EVENT_WINDOW_SHOWN: return "window_shown";
case SDL_EVENT_WINDOW_HIDDEN: return "window_hidden";
case SDL_EVENT_WINDOW_EXPOSED: return "window_exposed";
case SDL_EVENT_WINDOW_MOVED: return "window_moved";
case SDL_EVENT_WINDOW_RESIZED: return "window_resized";
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: return "window_pixel_size_changed";
case SDL_EVENT_WINDOW_MINIMIZED: return "window_minimized";
case SDL_EVENT_WINDOW_MAXIMIZED: return "window_maximized";
case SDL_EVENT_WINDOW_RESTORED: return "window_restored";
case SDL_EVENT_WINDOW_MOUSE_ENTER: return "window_mouse_enter";
case SDL_EVENT_WINDOW_MOUSE_LEAVE: return "window_mouse_leave";
case SDL_EVENT_WINDOW_FOCUS_GAINED: return "window_focus_gained";
case SDL_EVENT_WINDOW_FOCUS_LOST: return "window_focus_lost";
case SDL_EVENT_WINDOW_CLOSE_REQUESTED: return "window_close_requested";
case SDL_EVENT_WINDOW_HIT_TEST: return "window_hit_test";
case SDL_EVENT_WINDOW_ICCPROF_CHANGED: return "window_iccprof_changed";
case SDL_EVENT_WINDOW_DISPLAY_CHANGED: return "window_display_changed";
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: return "window_display_scale_changed";
case SDL_EVENT_WINDOW_OCCLUDED: return "window_occluded";
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN: return "window_enter_fullscreen";
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN: return "window_leave_fullscreen";
case SDL_EVENT_WINDOW_DESTROYED: return "window_destroyed";
// Keyboard events
case SDL_EVENT_KEY_DOWN: return "key_down";
case SDL_EVENT_KEY_UP: return "key_up";
case SDL_EVENT_TEXT_EDITING: return "text_editing";
case SDL_EVENT_TEXT_INPUT: return "text_input";
case SDL_EVENT_KEYMAP_CHANGED: return "keymap_changed";
case SDL_EVENT_KEYBOARD_ADDED: return "keyboard_added";
case SDL_EVENT_KEYBOARD_REMOVED: return "keyboard_removed";
// Mouse events
case SDL_EVENT_MOUSE_MOTION: return "mouse_motion";
case SDL_EVENT_MOUSE_BUTTON_DOWN: return "mouse_button_down";
case SDL_EVENT_MOUSE_BUTTON_UP: return "mouse_button_up";
case SDL_EVENT_MOUSE_WHEEL: return "mouse_wheel";
case SDL_EVENT_MOUSE_ADDED: return "mouse_added";
case SDL_EVENT_MOUSE_REMOVED: return "mouse_removed";
// Joystick events
case SDL_EVENT_JOYSTICK_AXIS_MOTION: return "joystick_axis_motion";
case SDL_EVENT_JOYSTICK_BALL_MOTION: return "joystick_ball_motion";
case SDL_EVENT_JOYSTICK_HAT_MOTION: return "joystick_hat_motion";
case SDL_EVENT_JOYSTICK_BUTTON_DOWN: return "joystick_button_down";
case SDL_EVENT_JOYSTICK_BUTTON_UP: return "joystick_button_up";
case SDL_EVENT_JOYSTICK_ADDED: return "joystick_added";
case SDL_EVENT_JOYSTICK_REMOVED: return "joystick_removed";
case SDL_EVENT_JOYSTICK_BATTERY_UPDATED: return "joystick_battery_updated";
case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE: return "joystick_update_complete";
// Gamepad events
case SDL_EVENT_GAMEPAD_AXIS_MOTION: return "gamepad_axis_motion";
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: return "gamepad_button_down";
case SDL_EVENT_GAMEPAD_BUTTON_UP: return "gamepad_button_up";
case SDL_EVENT_GAMEPAD_ADDED: return "gamepad_added";
case SDL_EVENT_GAMEPAD_REMOVED: return "gamepad_removed";
case SDL_EVENT_GAMEPAD_REMAPPED: return "gamepad_remapped";
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: return "gamepad_touchpad_down";
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: return "gamepad_touchpad_motion";
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: return "gamepad_touchpad_up";
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: return "gamepad_sensor_update";
case SDL_EVENT_GAMEPAD_UPDATE_COMPLETE: return "gamepad_update_complete";
case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED: return "gamepad_steam_handle_updated";
// Touch events
case SDL_EVENT_FINGER_DOWN: return "finger_down";
case SDL_EVENT_FINGER_UP: return "finger_up";
case SDL_EVENT_FINGER_MOTION: return "finger_motion";
// Clipboard events
case SDL_EVENT_CLIPBOARD_UPDATE: return "clipboard_update";
// Drag and drop events
case SDL_EVENT_DROP_FILE: return "drop_file";
case SDL_EVENT_DROP_TEXT: return "drop_text";
case SDL_EVENT_DROP_BEGIN: return "drop_begin";
case SDL_EVENT_DROP_COMPLETE: return "drop_complete";
case SDL_EVENT_DROP_POSITION: return "drop_position";
// Audio events
case SDL_EVENT_AUDIO_DEVICE_ADDED: return "audio_device_added";
case SDL_EVENT_AUDIO_DEVICE_REMOVED: return "audio_device_removed";
case SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED: return "audio_device_format_changed";
// Sensor events
case SDL_EVENT_SENSOR_UPDATE: return "sensor_update";
// Pen events
case SDL_EVENT_PEN_PROXIMITY_IN: return "pen_proximity_in";
case SDL_EVENT_PEN_PROXIMITY_OUT: return "pen_proximity_out";
case SDL_EVENT_PEN_DOWN: return "pen_down";
case SDL_EVENT_PEN_UP: return "pen_up";
case SDL_EVENT_PEN_BUTTON_DOWN: return "pen_button_down";
case SDL_EVENT_PEN_BUTTON_UP: return "pen_button_up";
case SDL_EVENT_PEN_MOTION: return "pen_motion";
case SDL_EVENT_PEN_AXIS: return "pen_axis";
// Camera events
case SDL_EVENT_CAMERA_DEVICE_ADDED: return "camera_device_added";
case SDL_EVENT_CAMERA_DEVICE_REMOVED: return "camera_device_removed";
case SDL_EVENT_CAMERA_DEVICE_APPROVED: return "camera_device_approved";
case SDL_EVENT_CAMERA_DEVICE_DENIED: return "camera_device_denied";
// Render events
case SDL_EVENT_RENDER_TARGETS_RESET: return "render_targets_reset";
case SDL_EVENT_RENDER_DEVICE_RESET: return "render_device_reset";
case SDL_EVENT_RENDER_DEVICE_LOST: return "render_device_lost";
default: return "unknown";
}
}
// Mouse button to string
static const char *mouse_button_to_string(Uint8 button) {
switch (button) {
case SDL_BUTTON_LEFT: return "left";
case SDL_BUTTON_MIDDLE: return "middle";
case SDL_BUTTON_RIGHT: return "right";
case SDL_BUTTON_X1: return "x1";
case SDL_BUTTON_X2: return "x2";
default: return "unknown";
}
}
// Convert SDL_Event to JS object
static JSValue event_to_js(JSContext *js, SDL_Event *event) {
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "type", JS_NewString(js, event_type_to_string(event->type)));
JS_SetPropertyStr(js, obj, "timestamp", JS_NewInt64(js, event->common.timestamp));
switch (event->type) {
// Window events
case SDL_EVENT_WINDOW_SHOWN:
case SDL_EVENT_WINDOW_HIDDEN:
case SDL_EVENT_WINDOW_EXPOSED:
case SDL_EVENT_WINDOW_MOVED:
case SDL_EVENT_WINDOW_RESIZED:
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
case SDL_EVENT_WINDOW_MINIMIZED:
case SDL_EVENT_WINDOW_MAXIMIZED:
case SDL_EVENT_WINDOW_RESTORED:
case SDL_EVENT_WINDOW_MOUSE_ENTER:
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
case SDL_EVENT_WINDOW_FOCUS_GAINED:
case SDL_EVENT_WINDOW_FOCUS_LOST:
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
case SDL_EVENT_WINDOW_HIT_TEST:
case SDL_EVENT_WINDOW_ICCPROF_CHANGED:
case SDL_EVENT_WINDOW_DISPLAY_CHANGED:
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
case SDL_EVENT_WINDOW_OCCLUDED:
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
case SDL_EVENT_WINDOW_DESTROYED:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->window.windowID));
JS_SetPropertyStr(js, obj, "data1", JS_NewInt32(js, event->window.data1));
JS_SetPropertyStr(js, obj, "data2", JS_NewInt32(js, event->window.data2));
break;
// Keyboard events
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->key.windowID));
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->key.which));
JS_SetPropertyStr(js, obj, "scancode", JS_NewInt32(js, event->key.scancode));
JS_SetPropertyStr(js, obj, "key", JS_NewUint32(js, event->key.key));
JS_SetPropertyStr(js, obj, "mod", JS_NewUint32(js, event->key.mod));
JS_SetPropertyStr(js, obj, "repeat", JS_NewBool(js, event->key.repeat));
JS_SetPropertyStr(js, obj, "down", JS_NewBool(js, event->key.down));
break;
// Text input
case SDL_EVENT_TEXT_INPUT:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->text.windowID));
JS_SetPropertyStr(js, obj, "text", JS_NewString(js, event->text.text));
break;
// Text editing
case SDL_EVENT_TEXT_EDITING:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->edit.windowID));
JS_SetPropertyStr(js, obj, "text", JS_NewString(js, event->edit.text));
JS_SetPropertyStr(js, obj, "start", JS_NewInt32(js, event->edit.start));
JS_SetPropertyStr(js, obj, "length", JS_NewInt32(js, event->edit.length));
break;
// Mouse motion
case SDL_EVENT_MOUSE_MOTION:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->motion.windowID));
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->motion.which));
JS_SetPropertyStr(js, obj, "state", JS_NewUint32(js, event->motion.state));
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, event->motion.x));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, event->motion.y));
JS_SetPropertyStr(js, obj, "xrel", JS_NewFloat64(js, event->motion.xrel));
JS_SetPropertyStr(js, obj, "yrel", JS_NewFloat64(js, event->motion.yrel));
break;
// Mouse button
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->button.windowID));
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->button.which));
JS_SetPropertyStr(js, obj, "button", JS_NewString(js, mouse_button_to_string(event->button.button)));
JS_SetPropertyStr(js, obj, "button_num", JS_NewUint32(js, event->button.button));
JS_SetPropertyStr(js, obj, "down", JS_NewBool(js, event->button.down));
JS_SetPropertyStr(js, obj, "clicks", JS_NewUint32(js, event->button.clicks));
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, event->button.x));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, event->button.y));
break;
// Mouse wheel
case SDL_EVENT_MOUSE_WHEEL:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->wheel.windowID));
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->wheel.which));
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, event->wheel.x));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, event->wheel.y));
JS_SetPropertyStr(js, obj, "direction", JS_NewString(js,
event->wheel.direction == SDL_MOUSEWHEEL_FLIPPED ? "flipped" : "normal"));
JS_SetPropertyStr(js, obj, "mouse_x", JS_NewFloat64(js, event->wheel.mouse_x));
JS_SetPropertyStr(js, obj, "mouse_y", JS_NewFloat64(js, event->wheel.mouse_y));
break;
// Joystick axis
case SDL_EVENT_JOYSTICK_AXIS_MOTION:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->jaxis.which));
JS_SetPropertyStr(js, obj, "axis", JS_NewUint32(js, event->jaxis.axis));
JS_SetPropertyStr(js, obj, "value", JS_NewInt32(js, event->jaxis.value));
break;
// Joystick ball
case SDL_EVENT_JOYSTICK_BALL_MOTION:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->jball.which));
JS_SetPropertyStr(js, obj, "ball", JS_NewUint32(js, event->jball.ball));
JS_SetPropertyStr(js, obj, "xrel", JS_NewInt32(js, event->jball.xrel));
JS_SetPropertyStr(js, obj, "yrel", JS_NewInt32(js, event->jball.yrel));
break;
// Joystick hat
case SDL_EVENT_JOYSTICK_HAT_MOTION:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->jhat.which));
JS_SetPropertyStr(js, obj, "hat", JS_NewUint32(js, event->jhat.hat));
JS_SetPropertyStr(js, obj, "value", JS_NewUint32(js, event->jhat.value));
break;
// Joystick button
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
case SDL_EVENT_JOYSTICK_BUTTON_UP:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->jbutton.which));
JS_SetPropertyStr(js, obj, "button", JS_NewUint32(js, event->jbutton.button));
JS_SetPropertyStr(js, obj, "down", JS_NewBool(js, event->jbutton.down));
break;
// Joystick device
case SDL_EVENT_JOYSTICK_ADDED:
case SDL_EVENT_JOYSTICK_REMOVED:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->jdevice.which));
break;
// Gamepad axis
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->gaxis.which));
JS_SetPropertyStr(js, obj, "axis", JS_NewUint32(js, event->gaxis.axis));
JS_SetPropertyStr(js, obj, "value", JS_NewInt32(js, event->gaxis.value));
break;
// Gamepad button
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->gbutton.which));
JS_SetPropertyStr(js, obj, "button", JS_NewUint32(js, event->gbutton.button));
JS_SetPropertyStr(js, obj, "down", JS_NewBool(js, event->gbutton.down));
break;
// Gamepad device
case SDL_EVENT_GAMEPAD_ADDED:
case SDL_EVENT_GAMEPAD_REMOVED:
case SDL_EVENT_GAMEPAD_REMAPPED:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->gdevice.which));
break;
// Gamepad touchpad
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->gtouchpad.which));
JS_SetPropertyStr(js, obj, "touchpad", JS_NewInt32(js, event->gtouchpad.touchpad));
JS_SetPropertyStr(js, obj, "finger", JS_NewInt32(js, event->gtouchpad.finger));
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, event->gtouchpad.x));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, event->gtouchpad.y));
JS_SetPropertyStr(js, obj, "pressure", JS_NewFloat64(js, event->gtouchpad.pressure));
break;
// Gamepad sensor
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->gsensor.which));
JS_SetPropertyStr(js, obj, "sensor", JS_NewInt32(js, event->gsensor.sensor));
{
JSValue data = JS_NewArray(js);
for (int i = 0; i < 3; i++) {
JS_SetPropertyUint32(js, data, i, JS_NewFloat64(js, event->gsensor.data[i]));
}
JS_SetPropertyStr(js, obj, "data", data);
}
JS_SetPropertyStr(js, obj, "sensor_timestamp", JS_NewInt64(js, event->gsensor.sensor_timestamp));
break;
// Touch finger
case SDL_EVENT_FINGER_DOWN:
case SDL_EVENT_FINGER_UP:
case SDL_EVENT_FINGER_MOTION:
JS_SetPropertyStr(js, obj, "touch_id", JS_NewInt64(js, event->tfinger.touchID));
JS_SetPropertyStr(js, obj, "finger_id", JS_NewInt64(js, event->tfinger.fingerID));
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, event->tfinger.x));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, event->tfinger.y));
JS_SetPropertyStr(js, obj, "dx", JS_NewFloat64(js, event->tfinger.dx));
JS_SetPropertyStr(js, obj, "dy", JS_NewFloat64(js, event->tfinger.dy));
JS_SetPropertyStr(js, obj, "pressure", JS_NewFloat64(js, event->tfinger.pressure));
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->tfinger.windowID));
break;
// Drop events
case SDL_EVENT_DROP_FILE:
case SDL_EVENT_DROP_TEXT:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->drop.windowID));
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, event->drop.x));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, event->drop.y));
if (event->drop.data)
JS_SetPropertyStr(js, obj, "data", JS_NewString(js, event->drop.data));
break;
case SDL_EVENT_DROP_BEGIN:
case SDL_EVENT_DROP_COMPLETE:
case SDL_EVENT_DROP_POSITION:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->drop.windowID));
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, event->drop.x));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, event->drop.y));
break;
// Audio device
case SDL_EVENT_AUDIO_DEVICE_ADDED:
case SDL_EVENT_AUDIO_DEVICE_REMOVED:
case SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->adevice.which));
JS_SetPropertyStr(js, obj, "recording", JS_NewBool(js, event->adevice.recording));
break;
// Sensor update
case SDL_EVENT_SENSOR_UPDATE:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->sensor.which));
{
JSValue data = JS_NewArray(js);
for (int i = 0; i < 6; i++) {
JS_SetPropertyUint32(js, data, i, JS_NewFloat64(js, event->sensor.data[i]));
}
JS_SetPropertyStr(js, obj, "data", data);
}
JS_SetPropertyStr(js, obj, "sensor_timestamp", JS_NewInt64(js, event->sensor.sensor_timestamp));
break;
// Pen events
case SDL_EVENT_PEN_PROXIMITY_IN:
case SDL_EVENT_PEN_PROXIMITY_OUT:
case SDL_EVENT_PEN_DOWN:
case SDL_EVENT_PEN_UP:
case SDL_EVENT_PEN_MOTION:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->pmotion.windowID));
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->pmotion.which));
JS_SetPropertyStr(js, obj, "pen_state", JS_NewUint32(js, event->pmotion.pen_state));
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, event->pmotion.x));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, event->pmotion.y));
break;
case SDL_EVENT_PEN_BUTTON_DOWN:
case SDL_EVENT_PEN_BUTTON_UP:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->pbutton.windowID));
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->pbutton.which));
JS_SetPropertyStr(js, obj, "pen_state", JS_NewUint32(js, event->pbutton.pen_state));
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, event->pbutton.x));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, event->pbutton.y));
JS_SetPropertyStr(js, obj, "button", JS_NewUint32(js, event->pbutton.button));
JS_SetPropertyStr(js, obj, "down", JS_NewBool(js, event->pbutton.down));
break;
case SDL_EVENT_PEN_AXIS:
JS_SetPropertyStr(js, obj, "window_id", JS_NewUint32(js, event->paxis.windowID));
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->paxis.which));
JS_SetPropertyStr(js, obj, "pen_state", JS_NewUint32(js, event->paxis.pen_state));
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, event->paxis.x));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, event->paxis.y));
JS_SetPropertyStr(js, obj, "axis", JS_NewUint32(js, event->paxis.axis));
JS_SetPropertyStr(js, obj, "value", JS_NewFloat64(js, event->paxis.value));
break;
// Camera device
case SDL_EVENT_CAMERA_DEVICE_ADDED:
case SDL_EVENT_CAMERA_DEVICE_REMOVED:
case SDL_EVENT_CAMERA_DEVICE_APPROVED:
case SDL_EVENT_CAMERA_DEVICE_DENIED:
JS_SetPropertyStr(js, obj, "which", JS_NewUint32(js, event->cdevice.which));
break;
// Display events
case SDL_EVENT_DISPLAY_ORIENTATION:
case SDL_EVENT_DISPLAY_ADDED:
case SDL_EVENT_DISPLAY_REMOVED:
case SDL_EVENT_DISPLAY_MOVED:
case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED:
JS_SetPropertyStr(js, obj, "display_id", JS_NewUint32(js, event->display.displayID));
JS_SetPropertyStr(js, obj, "data1", JS_NewInt32(js, event->display.data1));
break;
default:
break;
}
return obj;
}
// SDL_PumpEvents()
JSC_CCALL(events_pump,
SDL_PumpEvents();
return JS_NULL;
)
// SDL_PollEvent() -> event object or null
JSC_CCALL(events_poll,
SDL_Event event;
if (SDL_PollEvent(&event)) {
return event_to_js(js, &event);
}
return JS_NULL;
)
// SDL_WaitEvent(timeout_ms) -> event object or null
JSC_CCALL(events_wait,
SDL_Event event;
int timeout = -1;
if (argc > 0) JS_ToInt32(js, &timeout, argv[0]);
bool result;
if (timeout < 0) {
result = SDL_WaitEvent(&event);
} else {
result = SDL_WaitEventTimeout(&event, timeout);
}
if (result) {
return event_to_js(js, &event);
}
return JS_NULL;
)
// SDL_PeepEvents(count, action, min_type, max_type) -> array of events
JSC_CCALL(events_peep,
int count = 10;
if (argc > 0) JS_ToInt32(js, &count, argv[0]);
SDL_Event *events = malloc(count * sizeof(SDL_Event));
if (!events) return JS_ThrowOutOfMemory(js);
int num = SDL_PeepEvents(events, count, SDL_PEEKEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST);
if (num < 0) {
free(events);
return JS_NewArray(js);
}
JSValue arr = JS_NewArray(js);
for (int i = 0; i < num; i++) {
JS_SetPropertyUint32(js, arr, i, event_to_js(js, &events[i]));
}
free(events);
return arr;
)
// SDL_HasEvent(type) -> bool
JSC_CCALL(events_has,
uint32_t type;
JS_ToUint32(js, &type, argv[0]);
return JS_NewBool(js, SDL_HasEvent(type));
)
// SDL_FlushEvent(type)
JSC_CCALL(events_flush,
uint32_t type;
JS_ToUint32(js, &type, argv[0]);
SDL_FlushEvent(type);
return JS_NULL;
)
// SDL_FlushEvents(min_type, max_type)
JSC_CCALL(events_flush_range,
uint32_t min_type, max_type;
JS_ToUint32(js, &min_type, argv[0]);
JS_ToUint32(js, &max_type, argv[1]);
SDL_FlushEvents(min_type, max_type);
return JS_NULL;
)
// Get all pending events as array
JSC_CCALL(events_get_all,
JSValue arr = JS_NewArray(js);
SDL_Event event;
int i = 0;
while (SDL_PollEvent(&event)) {
JS_SetPropertyUint32(js, arr, i++, event_to_js(js, &event));
}
return arr;
)
static const JSCFunctionListEntry js_events_funcs[] = {
MIST_FUNC_DEF(events, pump, 0),
MIST_FUNC_DEF(events, poll, 0),
MIST_FUNC_DEF(events, wait, 1),
MIST_FUNC_DEF(events, peep, 1),
MIST_FUNC_DEF(events, has, 1),
MIST_FUNC_DEF(events, flush, 1),
MIST_FUNC_DEF(events, flush_range, 2),
MIST_FUNC_DEF(events, get_all, 0),
};
CELL_USE_FUNCS(js_events_funcs)

127
examples/lenna.ce Normal file
View File

@@ -0,0 +1,127 @@
/*
* Lenna Image Viewer - SDL3 Example
*
* Demonstrates loading an image from cell-image and rendering it with SDL3.
* Use WASD to move the image around the window.
*
* Usage: cell run examples/lenna.ce
*/
var io = use('fd')
var video = use('video')
var render = use('render')
var surface = use('surface')
var events = use('events')
var keyboard = use('keyboard')
var png = use('gitea.pockle.world/john/cell-image/png')
var time = use('time')
// Create window
var win = new video.window({
title: "Lenna - WASD to move",
width: 640,
height: 480
})
// Create renderer
var renderer = new render(win)
renderer.win = win
// Load lenna.png using cell-image
log.console("Loading lenna.png...")
var img_blob = io.slurp("lenna.png")
if (!img_blob) {
log.console("Error: Could not load lenna.png")
$_.stop()
}
var img = png.decode(img_blob)
if (!img) {
log.console("Error: Could not decode lenna.png")
$_.stop()
}
log.console("Image loaded: " + text(img.width) + "x" + text(img.height))
// Create SDL surface from image data
var surf = new surface({
width: img.width,
height: img.height,
format: "rgba32",
pixels: img.pixels,
pitch: img.pitch
})
// Create texture from surface
var tex = renderer.load_texture(surf)
log.console("Texture created: " + text(tex.width) + "x" + text(tex.height))
// Image position (centered initially)
var img_x = (640 - img.width) / 2
var img_y = (480 - img.height) / 2
var speed = 200 // pixels per second
// Track key states
var keys = {
w: false,
a: false,
s: false,
d: false
}
var running = true
var last_time = time.number()
function frame() {
if (!running) {
log.console("Exiting...")
$_.stop()
return
}
// Calculate delta time
var now = time.number()
var dt = now - last_time
last_time = now
// Process events
var ev
while ((ev = events.poll()) != null) {
if (ev.type == "quit" || ev.type == "window_close_requested") {
running = false
return
}
if (ev.type == "key_down" || ev.type == "key_up") {
var key_name = keyboard.get_key_name(ev.key).toLowerCase()
var pressed = ev.type == "key_down"
if (key_name == "w") keys.w = pressed
if (key_name == "a") keys.a = pressed
if (key_name == "s") keys.s = pressed
if (key_name == "d") keys.d = pressed
if (key_name == "escape" && pressed) running = false
}
}
// Update position based on keys
if (keys.w) img_y -= speed * dt
if (keys.s) img_y += speed * dt
if (keys.a) img_x -= speed * dt
if (keys.d) img_x += speed * dt
// Clear screen (dark gray)
renderer.draw_color([0.2, 0.2, 0.2, 1])
renderer.clear()
// Draw texture at current position
renderer.texture(tex, { x: img_x, y: img_y, width: tex.width, height: tex.height }, null, null)
// Present
renderer.present()
// Schedule next frame
$_.delay(frame, 1/60)
}
log.console("Starting render loop... Press ESC to exit, WASD to move")
frame()

502
gamepad.c Normal file
View File

@@ -0,0 +1,502 @@
#include "cell.h"
#include <SDL3/SDL.h>
// Gamepad type enum to string
static const char *gamepad_type_to_string(SDL_GamepadType type) {
switch (type) {
case SDL_GAMEPAD_TYPE_STANDARD: return "standard";
case SDL_GAMEPAD_TYPE_XBOX360: return "xbox360";
case SDL_GAMEPAD_TYPE_XBOXONE: return "xboxone";
case SDL_GAMEPAD_TYPE_PS3: return "ps3";
case SDL_GAMEPAD_TYPE_PS4: return "ps4";
case SDL_GAMEPAD_TYPE_PS5: return "ps5";
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO: return "switch_pro";
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT: return "joycon_left";
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT: return "joycon_right";
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR: return "joycon_pair";
default: return "unknown";
}
}
// Gamepad button enum to string
static const char *gamepad_button_to_string(SDL_GamepadButton button) {
switch (button) {
case SDL_GAMEPAD_BUTTON_SOUTH: return "south";
case SDL_GAMEPAD_BUTTON_EAST: return "east";
case SDL_GAMEPAD_BUTTON_WEST: return "west";
case SDL_GAMEPAD_BUTTON_NORTH: return "north";
case SDL_GAMEPAD_BUTTON_BACK: return "back";
case SDL_GAMEPAD_BUTTON_GUIDE: return "guide";
case SDL_GAMEPAD_BUTTON_START: return "start";
case SDL_GAMEPAD_BUTTON_LEFT_STICK: return "left_stick";
case SDL_GAMEPAD_BUTTON_RIGHT_STICK: return "right_stick";
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: return "left_shoulder";
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: return "right_shoulder";
case SDL_GAMEPAD_BUTTON_DPAD_UP: return "dpad_up";
case SDL_GAMEPAD_BUTTON_DPAD_DOWN: return "dpad_down";
case SDL_GAMEPAD_BUTTON_DPAD_LEFT: return "dpad_left";
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: return "dpad_right";
case SDL_GAMEPAD_BUTTON_MISC1: return "misc1";
case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1: return "right_paddle1";
case SDL_GAMEPAD_BUTTON_LEFT_PADDLE1: return "left_paddle1";
case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2: return "right_paddle2";
case SDL_GAMEPAD_BUTTON_LEFT_PADDLE2: return "left_paddle2";
case SDL_GAMEPAD_BUTTON_TOUCHPAD: return "touchpad";
case SDL_GAMEPAD_BUTTON_MISC2: return "misc2";
case SDL_GAMEPAD_BUTTON_MISC3: return "misc3";
case SDL_GAMEPAD_BUTTON_MISC4: return "misc4";
case SDL_GAMEPAD_BUTTON_MISC5: return "misc5";
case SDL_GAMEPAD_BUTTON_MISC6: return "misc6";
default: return "invalid";
}
}
static SDL_GamepadButton string_to_gamepad_button(const char *str) {
if (!str) return SDL_GAMEPAD_BUTTON_INVALID;
if (!strcmp(str, "south")) return SDL_GAMEPAD_BUTTON_SOUTH;
if (!strcmp(str, "east")) return SDL_GAMEPAD_BUTTON_EAST;
if (!strcmp(str, "west")) return SDL_GAMEPAD_BUTTON_WEST;
if (!strcmp(str, "north")) return SDL_GAMEPAD_BUTTON_NORTH;
if (!strcmp(str, "back")) return SDL_GAMEPAD_BUTTON_BACK;
if (!strcmp(str, "guide")) return SDL_GAMEPAD_BUTTON_GUIDE;
if (!strcmp(str, "start")) return SDL_GAMEPAD_BUTTON_START;
if (!strcmp(str, "left_stick")) return SDL_GAMEPAD_BUTTON_LEFT_STICK;
if (!strcmp(str, "right_stick")) return SDL_GAMEPAD_BUTTON_RIGHT_STICK;
if (!strcmp(str, "left_shoulder")) return SDL_GAMEPAD_BUTTON_LEFT_SHOULDER;
if (!strcmp(str, "right_shoulder")) return SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER;
if (!strcmp(str, "dpad_up")) return SDL_GAMEPAD_BUTTON_DPAD_UP;
if (!strcmp(str, "dpad_down")) return SDL_GAMEPAD_BUTTON_DPAD_DOWN;
if (!strcmp(str, "dpad_left")) return SDL_GAMEPAD_BUTTON_DPAD_LEFT;
if (!strcmp(str, "dpad_right")) return SDL_GAMEPAD_BUTTON_DPAD_RIGHT;
if (!strcmp(str, "misc1")) return SDL_GAMEPAD_BUTTON_MISC1;
if (!strcmp(str, "touchpad")) return SDL_GAMEPAD_BUTTON_TOUCHPAD;
return SDL_GAMEPAD_BUTTON_INVALID;
}
// Gamepad axis enum to string
static const char *gamepad_axis_to_string(SDL_GamepadAxis axis) {
switch (axis) {
case SDL_GAMEPAD_AXIS_LEFTX: return "leftx";
case SDL_GAMEPAD_AXIS_LEFTY: return "lefty";
case SDL_GAMEPAD_AXIS_RIGHTX: return "rightx";
case SDL_GAMEPAD_AXIS_RIGHTY: return "righty";
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: return "left_trigger";
case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: return "right_trigger";
default: return "invalid";
}
}
static SDL_GamepadAxis string_to_gamepad_axis(const char *str) {
if (!str) return SDL_GAMEPAD_AXIS_INVALID;
if (!strcmp(str, "leftx")) return SDL_GAMEPAD_AXIS_LEFTX;
if (!strcmp(str, "lefty")) return SDL_GAMEPAD_AXIS_LEFTY;
if (!strcmp(str, "rightx")) return SDL_GAMEPAD_AXIS_RIGHTX;
if (!strcmp(str, "righty")) return SDL_GAMEPAD_AXIS_RIGHTY;
if (!strcmp(str, "left_trigger")) return SDL_GAMEPAD_AXIS_LEFT_TRIGGER;
if (!strcmp(str, "right_trigger")) return SDL_GAMEPAD_AXIS_RIGHT_TRIGGER;
return SDL_GAMEPAD_AXIS_INVALID;
}
// Button label enum to string
static const char *button_label_to_string(SDL_GamepadButtonLabel label) {
switch (label) {
case SDL_GAMEPAD_BUTTON_LABEL_A: return "a";
case SDL_GAMEPAD_BUTTON_LABEL_B: return "b";
case SDL_GAMEPAD_BUTTON_LABEL_X: return "x";
case SDL_GAMEPAD_BUTTON_LABEL_Y: return "y";
case SDL_GAMEPAD_BUTTON_LABEL_CROSS: return "cross";
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE: return "circle";
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE: return "square";
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE: return "triangle";
default: return "unknown";
}
}
// SDL_Gamepad class
void SDL_Gamepad_free(JSRuntime *rt, SDL_Gamepad *gamepad) {
if (gamepad) SDL_CloseGamepad(gamepad);
}
QJSCLASS(SDL_Gamepad,)
// SDL_AddGamepadMapping(mapping) -> int
JSC_SCALL(gamepad_add_mapping,
const char *mapping = JS_ToCString(js, argv[0]);
if (!mapping) return JS_EXCEPTION;
int result = SDL_AddGamepadMapping(mapping);
JS_FreeCString(js, mapping);
return JS_NewInt32(js, result);
)
// SDL_AddGamepadMappingsFromFile(file) -> int
JSC_SCALL(gamepad_add_mappings_from_file,
const char *file = JS_ToCString(js, argv[0]);
if (!file) return JS_EXCEPTION;
int result = SDL_AddGamepadMappingsFromFile(file);
JS_FreeCString(js, file);
return JS_NewInt32(js, result);
)
// SDL_GetGamepads() -> array of gamepad IDs
JSC_CCALL(gamepad_get_gamepads,
int count = 0;
SDL_JoystickID *gamepads = SDL_GetGamepads(&count);
if (!gamepads) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewUint32(js, gamepads[i]));
}
SDL_free(gamepads);
return arr;
)
// SDL_IsGamepad(id) -> bool
JSC_CCALL(gamepad_is_gamepad,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
return JS_NewBool(js, SDL_IsGamepad(id));
)
// SDL_GetGamepadNameForID(id) -> string
JSC_CCALL(gamepad_get_name_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
const char *name = SDL_GetGamepadNameForID(id);
return name ? JS_NewString(js, name) : JS_NULL;
)
// SDL_GetGamepadPathForID(id) -> string
JSC_CCALL(gamepad_get_path_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
const char *path = SDL_GetGamepadPathForID(id);
return path ? JS_NewString(js, path) : JS_NULL;
)
// SDL_GetGamepadPlayerIndexForID(id) -> number
JSC_CCALL(gamepad_get_player_index_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
return JS_NewInt32(js, SDL_GetGamepadPlayerIndexForID(id));
)
// SDL_GetGamepadTypeForID(id) -> string
JSC_CCALL(gamepad_get_type_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_GamepadType type = SDL_GetGamepadTypeForID(id);
return JS_NewString(js, gamepad_type_to_string(type));
)
// SDL_OpenGamepad(id) -> Gamepad object
JSC_CCALL(gamepad_open,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_Gamepad *gamepad = SDL_OpenGamepad(id);
if (!gamepad) return JS_NULL;
return SDL_Gamepad2js(js, gamepad);
)
// SDL_GetGamepadFromID(id) -> Gamepad object
JSC_CCALL(gamepad_get_from_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_Gamepad *gamepad = SDL_GetGamepadFromID(id);
if (!gamepad) return JS_NULL;
return SDL_Gamepad2js(js, gamepad);
)
// SDL_GetGamepadFromPlayerIndex(player_index) -> Gamepad object
JSC_CCALL(gamepad_get_from_player_index,
int player_index;
JS_ToInt32(js, &player_index, argv[0]);
SDL_Gamepad *gamepad = SDL_GetGamepadFromPlayerIndex(player_index);
if (!gamepad) return JS_NULL;
return SDL_Gamepad2js(js, gamepad);
)
// SDL_GetGamepadButtonFromString(str) -> button enum
JSC_SCALL(gamepad_button_from_string,
SDL_GamepadButton button = SDL_GetGamepadButtonFromString(str);
return JS_NewString(js, gamepad_button_to_string(button));
)
// SDL_GetGamepadAxisFromString(str) -> axis enum
JSC_SCALL(gamepad_axis_from_string,
SDL_GamepadAxis axis = SDL_GetGamepadAxisFromString(str);
return JS_NewString(js, gamepad_axis_to_string(axis));
)
// Gamepad instance methods
JSC_CCALL(gamepad_get_name,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
const char *name = SDL_GetGamepadName(gamepad);
return name ? JS_NewString(js, name) : JS_NULL;
)
JSC_CCALL(gamepad_get_path,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
const char *path = SDL_GetGamepadPath(gamepad);
return path ? JS_NewString(js, path) : JS_NULL;
)
JSC_CCALL(gamepad_get_type,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
SDL_GamepadType type = SDL_GetGamepadType(gamepad);
return JS_NewString(js, gamepad_type_to_string(type));
)
JSC_CCALL(gamepad_get_player_index,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
return JS_NewInt32(js, SDL_GetGamepadPlayerIndex(gamepad));
)
JSC_CCALL(gamepad_set_player_index,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
int player_index;
JS_ToInt32(js, &player_index, argv[0]);
return JS_NewBool(js, SDL_SetGamepadPlayerIndex(gamepad, player_index));
)
JSC_CCALL(gamepad_get_vendor,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
return JS_NewUint32(js, SDL_GetGamepadVendor(gamepad));
)
JSC_CCALL(gamepad_get_product,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
return JS_NewUint32(js, SDL_GetGamepadProduct(gamepad));
)
JSC_CCALL(gamepad_get_product_version,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
return JS_NewUint32(js, SDL_GetGamepadProductVersion(gamepad));
)
JSC_CCALL(gamepad_get_serial,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
const char *serial = SDL_GetGamepadSerial(gamepad);
return serial ? JS_NewString(js, serial) : JS_NULL;
)
JSC_CCALL(gamepad_get_id,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
return JS_NewUint32(js, SDL_GetGamepadID(gamepad));
)
JSC_CCALL(gamepad_connected,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
return JS_NewBool(js, SDL_GamepadConnected(gamepad));
)
JSC_CCALL(gamepad_get_axis,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
const char *axis_str = JS_ToCString(js, argv[0]);
if (!axis_str) return JS_EXCEPTION;
SDL_GamepadAxis axis = string_to_gamepad_axis(axis_str);
JS_FreeCString(js, axis_str);
return JS_NewInt32(js, SDL_GetGamepadAxis(gamepad, axis));
)
JSC_CCALL(gamepad_get_button,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
const char *button_str = JS_ToCString(js, argv[0]);
if (!button_str) return JS_EXCEPTION;
SDL_GamepadButton button = string_to_gamepad_button(button_str);
JS_FreeCString(js, button_str);
return JS_NewBool(js, SDL_GetGamepadButton(gamepad, button));
)
JSC_CCALL(gamepad_get_button_label,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
const char *button_str = JS_ToCString(js, argv[0]);
if (!button_str) return JS_EXCEPTION;
SDL_GamepadButton button = string_to_gamepad_button(button_str);
JS_FreeCString(js, button_str);
SDL_GamepadButtonLabel label = SDL_GetGamepadButtonLabel(gamepad, button);
return JS_NewString(js, button_label_to_string(label));
)
JSC_CCALL(gamepad_get_num_touchpads,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
return JS_NewInt32(js, SDL_GetNumGamepadTouchpads(gamepad));
)
JSC_CCALL(gamepad_get_num_touchpad_fingers,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
int touchpad;
JS_ToInt32(js, &touchpad, argv[0]);
return JS_NewInt32(js, SDL_GetNumGamepadTouchpadFingers(gamepad, touchpad));
)
JSC_CCALL(gamepad_get_touchpad_finger,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
int touchpad, finger;
JS_ToInt32(js, &touchpad, argv[0]);
JS_ToInt32(js, &finger, argv[1]);
bool down;
float x, y, pressure;
if (!SDL_GetGamepadTouchpadFinger(gamepad, touchpad, finger, &down, &x, &y, &pressure))
return JS_NULL;
JSValue result = JS_NewObject(js);
JS_SetPropertyStr(js, result, "down", JS_NewBool(js, down));
JS_SetPropertyStr(js, result, "x", JS_NewFloat64(js, x));
JS_SetPropertyStr(js, result, "y", JS_NewFloat64(js, y));
JS_SetPropertyStr(js, result, "pressure", JS_NewFloat64(js, pressure));
return result;
)
JSC_CCALL(gamepad_has_sensor,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
int type;
JS_ToInt32(js, &type, argv[0]);
return JS_NewBool(js, SDL_GamepadHasSensor(gamepad, (SDL_SensorType)type));
)
JSC_CCALL(gamepad_set_sensor_enabled,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
int type;
JS_ToInt32(js, &type, argv[0]);
bool enabled = JS_ToBool(js, argv[1]);
return JS_NewBool(js, SDL_SetGamepadSensorEnabled(gamepad, (SDL_SensorType)type, enabled));
)
JSC_CCALL(gamepad_sensor_enabled,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
int type;
JS_ToInt32(js, &type, argv[0]);
return JS_NewBool(js, SDL_GamepadSensorEnabled(gamepad, (SDL_SensorType)type));
)
JSC_CCALL(gamepad_get_sensor_data,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
int type;
JS_ToInt32(js, &type, argv[0]);
int num_values = 3;
if (argc > 1) JS_ToInt32(js, &num_values, argv[1]);
float *data = malloc(num_values * sizeof(float));
if (!data) return JS_ThrowOutOfMemory(js);
if (!SDL_GetGamepadSensorData(gamepad, (SDL_SensorType)type, data, num_values)) {
free(data);
return JS_NULL;
}
JSValue arr = JS_NewArray(js);
for (int i = 0; i < num_values; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewFloat64(js, data[i]));
}
free(data);
return arr;
)
JSC_CCALL(gamepad_rumble,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
uint32_t low_freq, high_freq, duration_ms;
JS_ToUint32(js, &low_freq, argv[0]);
JS_ToUint32(js, &high_freq, argv[1]);
JS_ToUint32(js, &duration_ms, argv[2]);
return JS_NewBool(js, SDL_RumbleGamepad(gamepad, low_freq, high_freq, duration_ms));
)
JSC_CCALL(gamepad_rumble_triggers,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
uint32_t left, right, duration_ms;
JS_ToUint32(js, &left, argv[0]);
JS_ToUint32(js, &right, argv[1]);
JS_ToUint32(js, &duration_ms, argv[2]);
return JS_NewBool(js, SDL_RumbleGamepadTriggers(gamepad, left, right, duration_ms));
)
JSC_CCALL(gamepad_set_led,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
int r, g, b;
JS_ToInt32(js, &r, argv[0]);
JS_ToInt32(js, &g, argv[1]);
JS_ToInt32(js, &b, argv[2]);
return JS_NewBool(js, SDL_SetGamepadLED(gamepad, r, g, b));
)
JSC_CCALL(gamepad_get_power_info,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
int percent;
SDL_PowerState state = SDL_GetGamepadPowerInfo(gamepad, &percent);
JSValue result = JS_NewObject(js);
const char *state_str;
switch (state) {
case SDL_POWERSTATE_ON_BATTERY: state_str = "on_battery"; break;
case SDL_POWERSTATE_NO_BATTERY: state_str = "no_battery"; break;
case SDL_POWERSTATE_CHARGING: state_str = "charging"; break;
case SDL_POWERSTATE_CHARGED: state_str = "charged"; break;
default: state_str = "unknown"; break;
}
JS_SetPropertyStr(js, result, "state", JS_NewString(js, state_str));
JS_SetPropertyStr(js, result, "percent", JS_NewInt32(js, percent));
return result;
)
JSC_CCALL(gamepad_close,
SDL_Gamepad *gamepad = js2SDL_Gamepad(js, self);
SDL_CloseGamepad(gamepad);
return JS_NULL;
)
static const JSCFunctionListEntry js_SDL_Gamepad_funcs[] = {
MIST_FUNC_DEF(gamepad, get_name, 0),
MIST_FUNC_DEF(gamepad, get_path, 0),
MIST_FUNC_DEF(gamepad, get_type, 0),
MIST_FUNC_DEF(gamepad, get_player_index, 0),
MIST_FUNC_DEF(gamepad, set_player_index, 1),
MIST_FUNC_DEF(gamepad, get_vendor, 0),
MIST_FUNC_DEF(gamepad, get_product, 0),
MIST_FUNC_DEF(gamepad, get_product_version, 0),
MIST_FUNC_DEF(gamepad, get_serial, 0),
MIST_FUNC_DEF(gamepad, get_id, 0),
MIST_FUNC_DEF(gamepad, connected, 0),
MIST_FUNC_DEF(gamepad, get_axis, 1),
MIST_FUNC_DEF(gamepad, get_button, 1),
MIST_FUNC_DEF(gamepad, get_button_label, 1),
MIST_FUNC_DEF(gamepad, get_num_touchpads, 0),
MIST_FUNC_DEF(gamepad, get_num_touchpad_fingers, 1),
MIST_FUNC_DEF(gamepad, get_touchpad_finger, 2),
MIST_FUNC_DEF(gamepad, has_sensor, 1),
MIST_FUNC_DEF(gamepad, set_sensor_enabled, 2),
MIST_FUNC_DEF(gamepad, sensor_enabled, 1),
MIST_FUNC_DEF(gamepad, get_sensor_data, 2),
MIST_FUNC_DEF(gamepad, rumble, 3),
MIST_FUNC_DEF(gamepad, rumble_triggers, 3),
MIST_FUNC_DEF(gamepad, set_led, 3),
MIST_FUNC_DEF(gamepad, get_power_info, 0),
MIST_FUNC_DEF(gamepad, close, 0),
};
static const JSCFunctionListEntry js_gamepad_funcs[] = {
MIST_FUNC_DEF(gamepad, add_mapping, 1),
MIST_FUNC_DEF(gamepad, add_mappings_from_file, 1),
MIST_FUNC_DEF(gamepad, get_gamepads, 0),
MIST_FUNC_DEF(gamepad, is_gamepad, 1),
MIST_FUNC_DEF(gamepad, get_name_for_id, 1),
MIST_FUNC_DEF(gamepad, get_path_for_id, 1),
MIST_FUNC_DEF(gamepad, get_player_index_for_id, 1),
MIST_FUNC_DEF(gamepad, get_type_for_id, 1),
MIST_FUNC_DEF(gamepad, open, 1),
MIST_FUNC_DEF(gamepad, get_from_id, 1),
MIST_FUNC_DEF(gamepad, get_from_player_index, 1),
MIST_FUNC_DEF(gamepad, button_from_string, 1),
MIST_FUNC_DEF(gamepad, axis_from_string, 1),
};
CELL_USE_INIT(
SDL_Init(SDL_INIT_GAMEPAD);
QJSCLASSPREP_FUNCS(SDL_Gamepad);
JSValue ret = JS_NewObject(js);
JS_SetPropertyFunctionList(js, ret, js_gamepad_funcs, countof(js_gamepad_funcs));
return ret;
)

1724
gpu.c Normal file

File diff suppressed because it is too large Load Diff

212
haptic.c Normal file
View File

@@ -0,0 +1,212 @@
#include "cell.h"
#include <SDL3/SDL.h>
// Forward declaration for joystick function
extern SDL_Joystick *js2SDL_Joystick(JSContext *js, JSValue val);
// SDL_Haptic class
void SDL_Haptic_free(JSRuntime *rt, SDL_Haptic *haptic) {
if (haptic) SDL_CloseHaptic(haptic);
}
QJSCLASS(SDL_Haptic,)
// SDL_GetHaptics() -> array of haptic IDs
JSC_CCALL(haptic_get_haptics,
int count = 0;
SDL_HapticID *haptics = SDL_GetHaptics(&count);
if (!haptics) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewUint32(js, haptics[i]));
}
SDL_free(haptics);
return arr;
)
// SDL_GetHapticNameForID(id) -> string
JSC_CCALL(haptic_get_name_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
const char *name = SDL_GetHapticNameForID(id);
return name ? JS_NewString(js, name) : JS_NULL;
)
// SDL_OpenHaptic(id) -> Haptic object
JSC_CCALL(haptic_open,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_Haptic *haptic = SDL_OpenHaptic(id);
if (!haptic) return JS_NULL;
return SDL_Haptic2js(js, haptic);
)
// SDL_OpenHapticFromJoystick(joystick) -> Haptic object
JSC_CCALL(haptic_open_from_joystick,
SDL_Joystick *joystick = js2SDL_Joystick(js, argv[0]);
if (!joystick) return JS_NULL;
SDL_Haptic *haptic = SDL_OpenHapticFromJoystick(joystick);
if (!haptic) return JS_NULL;
return SDL_Haptic2js(js, haptic);
)
// SDL_OpenHapticFromMouse() -> Haptic object
JSC_CCALL(haptic_open_from_mouse,
SDL_Haptic *haptic = SDL_OpenHapticFromMouse();
if (!haptic) return JS_NULL;
return SDL_Haptic2js(js, haptic);
)
// SDL_IsMouseHaptic() -> bool
JSC_CCALL(haptic_is_mouse_haptic,
return JS_NewBool(js, SDL_IsMouseHaptic());
)
// Haptic instance methods
JSC_CCALL(haptic_get_id,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
return JS_NewUint32(js, SDL_GetHapticID(haptic));
)
JSC_CCALL(haptic_get_name,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
const char *name = SDL_GetHapticName(haptic);
return name ? JS_NewString(js, name) : JS_NULL;
)
JSC_CCALL(haptic_get_features,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
return JS_NewUint32(js, SDL_GetHapticFeatures(haptic));
)
JSC_CCALL(haptic_get_num_axes,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
return JS_NewInt32(js, SDL_GetNumHapticAxes(haptic));
)
JSC_CCALL(haptic_get_max_effects,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
return JS_NewInt32(js, SDL_GetMaxHapticEffects(haptic));
)
JSC_CCALL(haptic_get_max_effects_playing,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
return JS_NewInt32(js, SDL_GetMaxHapticEffectsPlaying(haptic));
)
JSC_CCALL(haptic_rumble_supported,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
return JS_NewBool(js, SDL_HapticRumbleSupported(haptic));
)
JSC_CCALL(haptic_init_rumble,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
return JS_NewBool(js, SDL_InitHapticRumble(haptic));
)
JSC_CCALL(haptic_play_rumble,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
float strength = js2number(js, argv[0]);
uint32_t length;
JS_ToUint32(js, &length, argv[1]);
return JS_NewBool(js, SDL_PlayHapticRumble(haptic, strength, length));
)
JSC_CCALL(haptic_stop_rumble,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
return JS_NewBool(js, SDL_StopHapticRumble(haptic));
)
JSC_CCALL(haptic_set_gain,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
int gain;
JS_ToInt32(js, &gain, argv[0]);
return JS_NewBool(js, SDL_SetHapticGain(haptic, gain));
)
JSC_CCALL(haptic_set_autocenter,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
int autocenter;
JS_ToInt32(js, &autocenter, argv[0]);
return JS_NewBool(js, SDL_SetHapticAutocenter(haptic, autocenter));
)
JSC_CCALL(haptic_pause,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
return JS_NewBool(js, SDL_PauseHaptic(haptic));
)
JSC_CCALL(haptic_resume,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
return JS_NewBool(js, SDL_ResumeHaptic(haptic));
)
JSC_CCALL(haptic_stop_all,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
return JS_NewBool(js, SDL_StopHapticEffects(haptic));
)
JSC_CCALL(haptic_close,
SDL_Haptic *haptic = js2SDL_Haptic(js, self);
SDL_CloseHaptic(haptic);
return JS_NULL;
)
static const JSCFunctionListEntry js_SDL_Haptic_funcs[] = {
MIST_FUNC_DEF(haptic, get_id, 0),
MIST_FUNC_DEF(haptic, get_name, 0),
MIST_FUNC_DEF(haptic, get_features, 0),
MIST_FUNC_DEF(haptic, get_num_axes, 0),
MIST_FUNC_DEF(haptic, get_max_effects, 0),
MIST_FUNC_DEF(haptic, get_max_effects_playing, 0),
MIST_FUNC_DEF(haptic, rumble_supported, 0),
MIST_FUNC_DEF(haptic, init_rumble, 0),
MIST_FUNC_DEF(haptic, play_rumble, 2),
MIST_FUNC_DEF(haptic, stop_rumble, 0),
MIST_FUNC_DEF(haptic, set_gain, 1),
MIST_FUNC_DEF(haptic, set_autocenter, 1),
MIST_FUNC_DEF(haptic, pause, 0),
MIST_FUNC_DEF(haptic, resume, 0),
MIST_FUNC_DEF(haptic, stop_all, 0),
MIST_FUNC_DEF(haptic, close, 0),
};
static const JSCFunctionListEntry js_haptic_funcs[] = {
MIST_FUNC_DEF(haptic, get_haptics, 0),
MIST_FUNC_DEF(haptic, get_name_for_id, 1),
MIST_FUNC_DEF(haptic, open, 1),
MIST_FUNC_DEF(haptic, open_from_joystick, 1),
MIST_FUNC_DEF(haptic, open_from_mouse, 0),
MIST_FUNC_DEF(haptic, is_mouse_haptic, 0),
};
CELL_USE_INIT(
SDL_Init(SDL_INIT_HAPTIC);
QJSCLASSPREP_FUNCS(SDL_Haptic);
JSValue ret = JS_NewObject(js);
JS_SetPropertyFunctionList(js, ret, js_haptic_funcs, countof(js_haptic_funcs));
// Export feature constants
JS_SetPropertyStr(js, ret, "CONSTANT", JS_NewUint32(js, SDL_HAPTIC_CONSTANT));
JS_SetPropertyStr(js, ret, "SINE", JS_NewUint32(js, SDL_HAPTIC_SINE));
JS_SetPropertyStr(js, ret, "SQUARE", JS_NewUint32(js, SDL_HAPTIC_SQUARE));
JS_SetPropertyStr(js, ret, "TRIANGLE", JS_NewUint32(js, SDL_HAPTIC_TRIANGLE));
JS_SetPropertyStr(js, ret, "SAWTOOTHUP", JS_NewUint32(js, SDL_HAPTIC_SAWTOOTHUP));
JS_SetPropertyStr(js, ret, "SAWTOOTHDOWN", JS_NewUint32(js, SDL_HAPTIC_SAWTOOTHDOWN));
JS_SetPropertyStr(js, ret, "RAMP", JS_NewUint32(js, SDL_HAPTIC_RAMP));
JS_SetPropertyStr(js, ret, "SPRING", JS_NewUint32(js, SDL_HAPTIC_SPRING));
JS_SetPropertyStr(js, ret, "DAMPER", JS_NewUint32(js, SDL_HAPTIC_DAMPER));
JS_SetPropertyStr(js, ret, "INERTIA", JS_NewUint32(js, SDL_HAPTIC_INERTIA));
JS_SetPropertyStr(js, ret, "FRICTION", JS_NewUint32(js, SDL_HAPTIC_FRICTION));
JS_SetPropertyStr(js, ret, "LEFTRIGHT", JS_NewUint32(js, SDL_HAPTIC_LEFTRIGHT));
JS_SetPropertyStr(js, ret, "CUSTOM", JS_NewUint32(js, SDL_HAPTIC_CUSTOM));
JS_SetPropertyStr(js, ret, "GAIN", JS_NewUint32(js, SDL_HAPTIC_GAIN));
JS_SetPropertyStr(js, ret, "AUTOCENTER", JS_NewUint32(js, SDL_HAPTIC_AUTOCENTER));
JS_SetPropertyStr(js, ret, "STATUS", JS_NewUint32(js, SDL_HAPTIC_STATUS));
JS_SetPropertyStr(js, ret, "PAUSE", JS_NewUint32(js, SDL_HAPTIC_PAUSE));
JS_SetPropertyStr(js, ret, "INFINITY", JS_NewUint32(js, SDL_HAPTIC_INFINITY));
return ret;
)

280
hidapi.c Normal file
View File

@@ -0,0 +1,280 @@
#include "cell.h"
#include <SDL3/SDL.h>
// SDL_hid_device class
void SDL_hid_device_free(JSRuntime *rt, SDL_hid_device *dev) {
if (dev) SDL_hid_close(dev);
}
QJSCLASS(SDL_hid_device,)
// Bus type to string
static const char *bus_type_to_string(SDL_hid_bus_type type) {
switch (type) {
case SDL_HID_API_BUS_USB: return "usb";
case SDL_HID_API_BUS_BLUETOOTH: return "bluetooth";
case SDL_HID_API_BUS_I2C: return "i2c";
case SDL_HID_API_BUS_SPI: return "spi";
default: return "unknown";
}
}
// Helper to convert device info to JS object
static JSValue device_info_to_js(JSContext *js, SDL_hid_device_info *info) {
JSValue obj = JS_NewObject(js);
if (info->path)
JS_SetPropertyStr(js, obj, "path", JS_NewString(js, info->path));
JS_SetPropertyStr(js, obj, "vendor_id", JS_NewUint32(js, info->vendor_id));
JS_SetPropertyStr(js, obj, "product_id", JS_NewUint32(js, info->product_id));
JS_SetPropertyStr(js, obj, "release_number", JS_NewUint32(js, info->release_number));
JS_SetPropertyStr(js, obj, "usage_page", JS_NewUint32(js, info->usage_page));
JS_SetPropertyStr(js, obj, "usage", JS_NewUint32(js, info->usage));
JS_SetPropertyStr(js, obj, "interface_number", JS_NewInt32(js, info->interface_number));
JS_SetPropertyStr(js, obj, "interface_class", JS_NewInt32(js, info->interface_class));
JS_SetPropertyStr(js, obj, "interface_subclass", JS_NewInt32(js, info->interface_subclass));
JS_SetPropertyStr(js, obj, "interface_protocol", JS_NewInt32(js, info->interface_protocol));
JS_SetPropertyStr(js, obj, "bus_type", JS_NewString(js, bus_type_to_string(info->bus_type)));
// Convert wide strings to UTF-8
if (info->serial_number) {
char buf[256];
wcstombs(buf, info->serial_number, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = 0;
JS_SetPropertyStr(js, obj, "serial_number", JS_NewString(js, buf));
}
if (info->manufacturer_string) {
char buf[256];
wcstombs(buf, info->manufacturer_string, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = 0;
JS_SetPropertyStr(js, obj, "manufacturer", JS_NewString(js, buf));
}
if (info->product_string) {
char buf[256];
wcstombs(buf, info->product_string, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = 0;
JS_SetPropertyStr(js, obj, "product", JS_NewString(js, buf));
}
return obj;
}
// SDL_hid_init() -> int
JSC_CCALL(hidapi_init,
return JS_NewInt32(js, SDL_hid_init());
)
// SDL_hid_exit() -> int
JSC_CCALL(hidapi_exit,
return JS_NewInt32(js, SDL_hid_exit());
)
// SDL_hid_device_change_count() -> number
JSC_CCALL(hidapi_device_change_count,
return JS_NewUint32(js, SDL_hid_device_change_count());
)
// SDL_hid_enumerate(vendor_id, product_id) -> array of device info
JSC_CCALL(hidapi_enumerate,
uint32_t vendor_id = 0, product_id = 0;
if (argc > 0) JS_ToUint32(js, &vendor_id, argv[0]);
if (argc > 1) JS_ToUint32(js, &product_id, argv[1]);
SDL_hid_device_info *devs = SDL_hid_enumerate(vendor_id, product_id);
if (!devs) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
int i = 0;
for (SDL_hid_device_info *cur = devs; cur; cur = cur->next) {
JS_SetPropertyUint32(js, arr, i++, device_info_to_js(js, cur));
}
SDL_hid_free_enumeration(devs);
return arr;
)
// SDL_hid_open(vendor_id, product_id) -> device
JSC_CCALL(hidapi_open,
uint32_t vendor_id, product_id;
JS_ToUint32(js, &vendor_id, argv[0]);
JS_ToUint32(js, &product_id, argv[1]);
SDL_hid_device *dev = SDL_hid_open(vendor_id, product_id, NULL);
if (!dev) return JS_NULL;
return SDL_hid_device2js(js, dev);
)
// SDL_hid_open_path(path) -> device
JSC_SCALL(hidapi_open_path,
const char *path = JS_ToCString(js, argv[0]);
if (!path) return JS_EXCEPTION;
SDL_hid_device *dev = SDL_hid_open_path(path);
JS_FreeCString(js, path);
if (!dev) return JS_NULL;
return SDL_hid_device2js(js, dev);
)
// Device instance methods
JSC_CCALL(hidapi_write,
SDL_hid_device *dev = js2SDL_hid_device(js, self);
size_t len;
void *data = js_get_blob_data(js, &len, argv[0]);
if (!data || data == (void*)-1) return JS_ThrowTypeError(js, "Expected ArrayBuffer");
return JS_NewInt32(js, SDL_hid_write(dev, data, len));
)
JSC_CCALL(hidapi_read,
SDL_hid_device *dev = js2SDL_hid_device(js, self);
int length = 64;
if (argc > 0) JS_ToInt32(js, &length, argv[0]);
unsigned char *buf = malloc(length);
if (!buf) return JS_ThrowOutOfMemory(js);
int result = SDL_hid_read(dev, buf, length);
if (result < 0) {
free(buf);
return JS_NULL;
}
ret = js_new_blob_stoned_copy(js, buf, result);
free(buf);
return ret;
)
JSC_CCALL(hidapi_read_timeout,
SDL_hid_device *dev = js2SDL_hid_device(js, self);
int length = 64, timeout = -1;
if (argc > 0) JS_ToInt32(js, &length, argv[0]);
if (argc > 1) JS_ToInt32(js, &timeout, argv[1]);
unsigned char *buf = malloc(length);
if (!buf) return JS_ThrowOutOfMemory(js);
int result = SDL_hid_read_timeout(dev, buf, length, timeout);
if (result < 0) {
free(buf);
return JS_NULL;
}
ret = js_new_blob_stoned_copy(js, buf, result);
free(buf);
return ret;
)
JSC_CCALL(hidapi_set_nonblocking,
SDL_hid_device *dev = js2SDL_hid_device(js, self);
int nonblock = JS_ToBool(js, argv[0]);
return JS_NewInt32(js, SDL_hid_set_nonblocking(dev, nonblock));
)
JSC_CCALL(hidapi_send_feature_report,
SDL_hid_device *dev = js2SDL_hid_device(js, self);
size_t len;
void *data = js_get_blob_data(js, &len, argv[0]);
if (!data || data == (void*)-1) return JS_ThrowTypeError(js, "Expected ArrayBuffer");
return JS_NewInt32(js, SDL_hid_send_feature_report(dev, data, len));
)
JSC_CCALL(hidapi_get_feature_report,
SDL_hid_device *dev = js2SDL_hid_device(js, self);
int length = 64;
if (argc > 0) JS_ToInt32(js, &length, argv[0]);
unsigned char *buf = malloc(length);
if (!buf) return JS_ThrowOutOfMemory(js);
// First byte is report number
buf[0] = 0;
if (argc > 1) {
int report_num;
JS_ToInt32(js, &report_num, argv[1]);
buf[0] = report_num;
}
int result = SDL_hid_get_feature_report(dev, buf, length);
if (result < 0) {
free(buf);
return JS_NULL;
}
ret = js_new_blob_stoned_copy(js, buf, result);
free(buf);
return ret;
)
JSC_CCALL(hidapi_get_input_report,
SDL_hid_device *dev = js2SDL_hid_device(js, self);
int length = 64;
if (argc > 0) JS_ToInt32(js, &length, argv[0]);
unsigned char *buf = malloc(length);
if (!buf) return JS_ThrowOutOfMemory(js);
buf[0] = 0;
if (argc > 1) {
int report_num;
JS_ToInt32(js, &report_num, argv[1]);
buf[0] = report_num;
}
int result = SDL_hid_get_input_report(dev, buf, length);
if (result < 0) {
free(buf);
return JS_NULL;
}
ret = js_new_blob_stoned_copy(js, buf, result);
free(buf);
return ret;
)
JSC_CCALL(hidapi_get_device_info,
SDL_hid_device *dev = js2SDL_hid_device(js, self);
SDL_hid_device_info *info = SDL_hid_get_device_info(dev);
if (!info) return JS_NULL;
return device_info_to_js(js, info);
)
JSC_CCALL(hidapi_close,
SDL_hid_device *dev = js2SDL_hid_device(js, self);
SDL_hid_close(dev);
return JS_NULL;
)
static const JSCFunctionListEntry js_SDL_hid_device_funcs[] = {
MIST_FUNC_DEF(hidapi, write, 1),
MIST_FUNC_DEF(hidapi, read, 1),
MIST_FUNC_DEF(hidapi, read_timeout, 2),
MIST_FUNC_DEF(hidapi, set_nonblocking, 1),
MIST_FUNC_DEF(hidapi, send_feature_report, 1),
MIST_FUNC_DEF(hidapi, get_feature_report, 2),
MIST_FUNC_DEF(hidapi, get_input_report, 2),
MIST_FUNC_DEF(hidapi, get_device_info, 0),
MIST_FUNC_DEF(hidapi, close, 0),
};
static const JSCFunctionListEntry js_hidapi_funcs[] = {
MIST_FUNC_DEF(hidapi, init, 0),
MIST_FUNC_DEF(hidapi, exit, 0),
MIST_FUNC_DEF(hidapi, device_change_count, 0),
MIST_FUNC_DEF(hidapi, enumerate, 2),
MIST_FUNC_DEF(hidapi, open, 2),
MIST_FUNC_DEF(hidapi, open_path, 1),
};
CELL_USE_INIT(
QJSCLASSPREP_FUNCS(SDL_hid_device);
JSValue ret = JS_NewObject(js);
JS_SetPropertyFunctionList(js, ret, js_hidapi_funcs, countof(js_hidapi_funcs));
// Export bus type constants
JS_SetPropertyStr(js, ret, "BUS_UNKNOWN", JS_NewInt32(js, SDL_HID_API_BUS_UNKNOWN));
JS_SetPropertyStr(js, ret, "BUS_USB", JS_NewInt32(js, SDL_HID_API_BUS_USB));
JS_SetPropertyStr(js, ret, "BUS_BLUETOOTH", JS_NewInt32(js, SDL_HID_API_BUS_BLUETOOTH));
JS_SetPropertyStr(js, ret, "BUS_I2C", JS_NewInt32(js, SDL_HID_API_BUS_I2C));
JS_SetPropertyStr(js, ret, "BUS_SPI", JS_NewInt32(js, SDL_HID_API_BUS_SPI));
return ret;
)

780
input.c Normal file
View File

@@ -0,0 +1,780 @@
#include "cell.h"
#include "wota.h"
#include <SDL3/SDL.h>
// 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;
)
// Event processing functions (moved from cell.c)
const char* event_type_to_string(Uint32 event_type) {
switch (event_type) {
// Application events
case SDL_EVENT_QUIT: return "quit";
case SDL_EVENT_TERMINATING: return "terminating";
case SDL_EVENT_LOW_MEMORY: return "low_memory";
case SDL_EVENT_WILL_ENTER_BACKGROUND: return "will_enter_background";
case SDL_EVENT_DID_ENTER_BACKGROUND: return "did_enter_background";
case SDL_EVENT_WILL_ENTER_FOREGROUND: return "will_enter_foreground";
case SDL_EVENT_DID_ENTER_FOREGROUND: return "did_enter_foreground";
case SDL_EVENT_LOCALE_CHANGED: return "locale_changed";
case SDL_EVENT_SYSTEM_THEME_CHANGED: return "system_theme_changed";
// Display events
case SDL_EVENT_DISPLAY_ORIENTATION: return "display_orientation";
case SDL_EVENT_DISPLAY_ADDED: return "display_added";
case SDL_EVENT_DISPLAY_REMOVED: return "display_removed";
case SDL_EVENT_DISPLAY_MOVED: return "display_moved";
case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED: return "display_desktop_mode_changed";
case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED: return "display_current_mode_changed";
case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED: return "display_content_scale_changed";
// Window events
case SDL_EVENT_WINDOW_SHOWN: return "window_shown";
case SDL_EVENT_WINDOW_HIDDEN: return "window_hidden";
case SDL_EVENT_WINDOW_EXPOSED: return "window_exposed";
case SDL_EVENT_WINDOW_MOVED: return "window_moved";
case SDL_EVENT_WINDOW_RESIZED: return "window_resized";
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: return "window_pixel_size_changed";
case SDL_EVENT_WINDOW_METAL_VIEW_RESIZED: return "window_metal_view_resized";
case SDL_EVENT_WINDOW_MINIMIZED: return "window_minimized";
case SDL_EVENT_WINDOW_MAXIMIZED: return "window_maximized";
case SDL_EVENT_WINDOW_RESTORED: return "window_restored";
case SDL_EVENT_WINDOW_MOUSE_ENTER: return "window_mouse_enter";
case SDL_EVENT_WINDOW_MOUSE_LEAVE: return "window_mouse_leave";
case SDL_EVENT_WINDOW_FOCUS_GAINED: return "window_focus_gained";
case SDL_EVENT_WINDOW_FOCUS_LOST: return "window_focus_lost";
case SDL_EVENT_WINDOW_CLOSE_REQUESTED: return "window_close_requested";
case SDL_EVENT_WINDOW_HIT_TEST: return "window_hit_test";
case SDL_EVENT_WINDOW_ICCPROF_CHANGED: return "window_iccprof_changed";
case SDL_EVENT_WINDOW_DISPLAY_CHANGED: return "window_display_changed";
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: return "window_display_scale_changed";
case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: return "window_safe_area_changed";
case SDL_EVENT_WINDOW_OCCLUDED: return "window_occluded";
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN: return "window_enter_fullscreen";
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN: return "window_leave_fullscreen";
case SDL_EVENT_WINDOW_DESTROYED: return "window_destroyed";
case SDL_EVENT_WINDOW_HDR_STATE_CHANGED: return "window_hdr_state_changed";
// Keyboard events
case SDL_EVENT_KEY_DOWN: return "key_down";
case SDL_EVENT_KEY_UP: return "key_up";
case SDL_EVENT_TEXT_EDITING: return "text_editing";
case SDL_EVENT_TEXT_INPUT: return "text_input";
case SDL_EVENT_KEYMAP_CHANGED: return "keymap_changed";
case SDL_EVENT_KEYBOARD_ADDED: return "keyboard_added";
case SDL_EVENT_KEYBOARD_REMOVED: return "keyboard_removed";
case SDL_EVENT_TEXT_EDITING_CANDIDATES: return "text_editing_candidates";
// Mouse events
case SDL_EVENT_MOUSE_MOTION: return "mouse_motion";
case SDL_EVENT_MOUSE_BUTTON_DOWN: return "mouse_button_down";
case SDL_EVENT_MOUSE_BUTTON_UP: return "mouse_button_up";
case SDL_EVENT_MOUSE_WHEEL: return "mouse_wheel";
case SDL_EVENT_MOUSE_ADDED: return "mouse_added";
case SDL_EVENT_MOUSE_REMOVED: return "mouse_removed";
// Joystick events
case SDL_EVENT_JOYSTICK_AXIS_MOTION: return "joystick_axis_motion";
case SDL_EVENT_JOYSTICK_BALL_MOTION: return "joystick_ball_motion";
case SDL_EVENT_JOYSTICK_HAT_MOTION: return "joystick_hat_motion";
case SDL_EVENT_JOYSTICK_BUTTON_DOWN: return "joystick_button_down";
case SDL_EVENT_JOYSTICK_BUTTON_UP: return "joystick_button_up";
case SDL_EVENT_JOYSTICK_ADDED: return "joystick_added";
case SDL_EVENT_JOYSTICK_REMOVED: return "joystick_removed";
case SDL_EVENT_JOYSTICK_BATTERY_UPDATED: return "joystick_battery_updated";
case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE: return "joystick_update_complete";
// Gamepad events
case SDL_EVENT_GAMEPAD_AXIS_MOTION: return "gamepad_axis_motion";
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: return "gamepad_button_down";
case SDL_EVENT_GAMEPAD_BUTTON_UP: return "gamepad_button_up";
case SDL_EVENT_GAMEPAD_ADDED: return "gamepad_added";
case SDL_EVENT_GAMEPAD_REMOVED: return "gamepad_removed";
case SDL_EVENT_GAMEPAD_REMAPPED: return "gamepad_remapped";
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: return "gamepad_touchpad_down";
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: return "gamepad_touchpad_motion";
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: return "gamepad_touchpad_up";
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: return "gamepad_sensor_update";
case SDL_EVENT_GAMEPAD_UPDATE_COMPLETE: return "gamepad_update_complete";
case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED: return "gamepad_steam_handle_updated";
// Touch events
case SDL_EVENT_FINGER_DOWN: return "finger_down";
case SDL_EVENT_FINGER_UP: return "finger_up";
case SDL_EVENT_FINGER_MOTION: return "finger_motion";
// Clipboard events
case SDL_EVENT_CLIPBOARD_UPDATE: return "clipboard_update";
// Drag and drop events
case SDL_EVENT_DROP_FILE: return "drop_file";
case SDL_EVENT_DROP_TEXT: return "drop_text";
case SDL_EVENT_DROP_BEGIN: return "drop_begin";
case SDL_EVENT_DROP_COMPLETE: return "drop_complete";
case SDL_EVENT_DROP_POSITION: return "drop_position";
// Audio device events
case SDL_EVENT_AUDIO_DEVICE_ADDED: return "audio_device_added";
case SDL_EVENT_AUDIO_DEVICE_REMOVED: return "audio_device_removed";
case SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED: return "audio_device_format_changed";
// Sensor events
case SDL_EVENT_SENSOR_UPDATE: return "sensor_update";
// Pen events
case SDL_EVENT_PEN_PROXIMITY_IN: return "pen_proximity_in";
case SDL_EVENT_PEN_PROXIMITY_OUT: return "pen_proximity_out";
case SDL_EVENT_PEN_DOWN: return "pen_down";
case SDL_EVENT_PEN_UP: return "pen_up";
case SDL_EVENT_PEN_BUTTON_DOWN: return "pen_button_down";
case SDL_EVENT_PEN_BUTTON_UP: return "pen_button_up";
case SDL_EVENT_PEN_MOTION: return "pen_motion";
case SDL_EVENT_PEN_AXIS: return "pen_axis";
// Camera events
case SDL_EVENT_CAMERA_DEVICE_ADDED: return "camera_device_added";
case SDL_EVENT_CAMERA_DEVICE_REMOVED: return "camera_device_removed";
case SDL_EVENT_CAMERA_DEVICE_APPROVED: return "camera_device_approved";
case SDL_EVENT_CAMERA_DEVICE_DENIED: return "camera_device_denied";
// Render events
case SDL_EVENT_RENDER_TARGETS_RESET: return "render_targets_reset";
case SDL_EVENT_RENDER_DEVICE_RESET: return "render_device_reset";
case SDL_EVENT_RENDER_DEVICE_LOST: return "render_device_lost";
// User event (assuming it should be included)
case SDL_EVENT_USER: return "user";
default: return "unknown";
}
}
const char* mouse_button_to_string(int mouse) {
switch (mouse) {
case SDL_BUTTON_LEFT: return "left";
case SDL_BUTTON_MIDDLE: return "middle";
case SDL_BUTTON_RIGHT: return "right";
case SDL_BUTTON_X1: return "x1";
case SDL_BUTTON_X2: return "x2";
default: return "left";
}
}
static void wota_write_vec2(WotaBuffer *wb, double x, double y) {
// We'll store as WOTA_ARR of length 2, then two numbers
wota_write_array(wb, 2);
wota_write_number(wb, x);
wota_write_number(wb, y);
}
static int event2wota_count_props(const SDL_Event *event)
{
// We always store at least "type" and "timestamp".
int count = 2;
switch (event->type) {
case SDL_EVENT_AUDIO_DEVICE_ADDED:
case SDL_EVENT_AUDIO_DEVICE_REMOVED:
count += 2; // which, recording
break;
case SDL_EVENT_DISPLAY_ORIENTATION:
case SDL_EVENT_DISPLAY_ADDED:
case SDL_EVENT_DISPLAY_REMOVED:
case SDL_EVENT_DISPLAY_MOVED:
case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED:
case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED:
case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED:
count += 3; // which, orientation/data1, data2
break;
case SDL_EVENT_MOUSE_MOTION:
count += 5;
break;
case SDL_EVENT_MOUSE_WHEEL:
// window, which, scroll, mouse => 4 extra
count += 4;
break;
case SDL_EVENT_MOUSE_BUTTON_UP:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
// window, which, down, button, clicks, mouse => 6 extra
count += 6;
break;
case SDL_EVENT_SENSOR_UPDATE:
// which, sensor_timestamp => 2 extra
count += 2;
break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
// window, which, down, repeat, key, scancode, mod => 7 extra
count += 7;
break;
case SDL_EVENT_FINGER_MOTION:
case SDL_EVENT_FINGER_DOWN:
case SDL_EVENT_FINGER_UP:
// touch, finger, pos, d_pos, pressure, window => 6 extra
count += 6;
break;
case SDL_EVENT_DROP_BEGIN:
case SDL_EVENT_DROP_FILE:
case SDL_EVENT_DROP_TEXT:
case SDL_EVENT_DROP_COMPLETE:
case SDL_EVENT_DROP_POSITION:
// window, pos, data, source => 4 extra
count += 4;
break;
case SDL_EVENT_TEXT_INPUT:
// window, text, mod => 3 extra
count += 3;
break;
case SDL_EVENT_CAMERA_DEVICE_APPROVED:
case SDL_EVENT_CAMERA_DEVICE_REMOVED:
case SDL_EVENT_CAMERA_DEVICE_ADDED:
case SDL_EVENT_CAMERA_DEVICE_DENIED:
// which => 1 extra
count += 1;
break;
case SDL_EVENT_CLIPBOARD_UPDATE:
// owner => 1 extra
count += 1;
break;
/* Window events that only need 'which' */
case SDL_EVENT_WINDOW_EXPOSED:
case SDL_EVENT_WINDOW_FOCUS_GAINED:
case SDL_EVENT_WINDOW_FOCUS_LOST:
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
// which => 1 extra
count += 1;
break;
/* Window events that need data1 and data2 */
case SDL_EVENT_WINDOW_SHOWN:
case SDL_EVENT_WINDOW_HIDDEN:
case SDL_EVENT_WINDOW_MOVED:
case SDL_EVENT_WINDOW_RESIZED:
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
case SDL_EVENT_WINDOW_METAL_VIEW_RESIZED:
case SDL_EVENT_WINDOW_MINIMIZED:
case SDL_EVENT_WINDOW_MAXIMIZED:
case SDL_EVENT_WINDOW_RESTORED:
case SDL_EVENT_WINDOW_MOUSE_ENTER:
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
case SDL_EVENT_WINDOW_HIT_TEST:
case SDL_EVENT_WINDOW_ICCPROF_CHANGED:
case SDL_EVENT_WINDOW_DISPLAY_CHANGED:
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
case SDL_EVENT_WINDOW_OCCLUDED:
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
case SDL_EVENT_WINDOW_DESTROYED:
case SDL_EVENT_WINDOW_HDR_STATE_CHANGED:
// which, x/width/display_index, y/height => 3 extra
count += 3;
break;
case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED:
// which, x, y, width, height => 5 extra
count += 5;
break;
case SDL_EVENT_JOYSTICK_ADDED:
case SDL_EVENT_JOYSTICK_REMOVED:
case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE:
// which => 1 extra
count += 1;
break;
case SDL_EVENT_JOYSTICK_AXIS_MOTION:
// which, axis, value => 3 extra
count += 3;
break;
case SDL_EVENT_JOYSTICK_BALL_MOTION:
// which, ball, rel => 3 extra
count += 3;
break;
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
case SDL_EVENT_JOYSTICK_BUTTON_UP:
// which, button, down => 3 extra
count += 3;
break;
case SDL_EVENT_GAMEPAD_ADDED:
case SDL_EVENT_GAMEPAD_REMOVED:
case SDL_EVENT_GAMEPAD_REMAPPED:
case SDL_EVENT_GAMEPAD_UPDATE_COMPLETE:
case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED:
// which => 1 extra
count += 1;
break;
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
// which, axis, value => 3 extra
count += 3;
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
// which, button, down => 3 extra
count += 3;
break;
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
// which, touchpad, finger, pos, pressure => 5 extra
count += 5;
break;
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
// which, sensor, sensor_timestamp => 3 extra
count += 3;
break;
case SDL_EVENT_USER:
// cb => 1 extra
count += 1;
break;
}
return count;
}
static void event2wota_write(WotaBuffer *wb, const SDL_Event *e, int c) {
wota_write_record(wb, (unsigned long long)c);
wota_write_text(wb, "type");
wota_write_text(wb, event_type_to_string(e->type));
wota_write_text(wb, "timestamp");
wota_write_number(wb, (double)e->common.timestamp);
switch(e->type) {
case SDL_EVENT_AUDIO_DEVICE_ADDED:
case SDL_EVENT_AUDIO_DEVICE_REMOVED:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->adevice.which);
wota_write_text(wb, "recording");
wota_write_sym(wb, e->adevice.recording ? WOTA_TRUE : WOTA_FALSE);
break;
case SDL_EVENT_DISPLAY_ORIENTATION:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->display.displayID);
wota_write_text(wb, "orientation");
wota_write_number(wb, (double)e->display.data1);
wota_write_text(wb, "data2");
wota_write_number(wb, (double)e->display.data2);
break;
case SDL_EVENT_DISPLAY_ADDED:
case SDL_EVENT_DISPLAY_REMOVED:
case SDL_EVENT_DISPLAY_MOVED:
case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED:
case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED:
case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->display.displayID);
wota_write_text(wb, "data1");
wota_write_number(wb, (double)e->display.data1);
wota_write_text(wb, "data2");
wota_write_number(wb, (double)e->display.data2);
break;
case SDL_EVENT_MOUSE_MOTION:
wota_write_text(wb, "window");
wota_write_number(wb, (double)e->motion.windowID);
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->motion.which);
wota_write_text(wb, "state");
wota_write_number(wb, (double)e->motion.state);
wota_write_text(wb, "pos");
wota_write_vec2(wb, (double)e->motion.x, (double)e->motion.y);
wota_write_text(wb, "d_pos");
wota_write_vec2(wb, (double)e->motion.xrel, (double)e->motion.yrel);
break;
case SDL_EVENT_MOUSE_WHEEL:
wota_write_text(wb, "window");
wota_write_number(wb, (double)e->wheel.windowID);
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->wheel.which);
wota_write_text(wb, "scroll");
wota_write_vec2(wb, (double)e->wheel.x, (double)e->wheel.y);
wota_write_text(wb, "pos");
wota_write_vec2(wb, (double)e->wheel.mouse_x, (double)e->wheel.mouse_y);
break;
case SDL_EVENT_MOUSE_BUTTON_UP:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
wota_write_text(wb, "window");
wota_write_number(wb, (double)e->button.windowID);
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->button.which);
wota_write_text(wb, "down");
wota_write_sym(wb, e->button.down ? WOTA_TRUE : WOTA_FALSE);
wota_write_text(wb, "button");
wota_write_text(wb, mouse_button_to_string(e->button.button));
wota_write_text(wb, "clicks");
wota_write_number(wb, (double)e->button.clicks);
wota_write_text(wb, "pos");
wota_write_vec2(wb, (double)e->button.x, (double)e->button.y);
break;
case SDL_EVENT_SENSOR_UPDATE:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->sensor.which);
wota_write_text(wb, "sensor_timestamp");
wota_write_number(wb, (double)e->sensor.sensor_timestamp);
break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
wota_write_text(wb, "window");
wota_write_number(wb, (double)e->key.windowID);
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->key.which);
wota_write_text(wb, "down");
wota_write_sym(wb, e->key.down ? WOTA_TRUE : WOTA_FALSE);
wota_write_text(wb, "repeat");
wota_write_sym(wb, e->key.repeat ? WOTA_TRUE : WOTA_FALSE);
wota_write_text(wb, "key");
wota_write_number(wb, (double)e->key.key);
wota_write_text(wb, "scancode");
wota_write_number(wb, (double)e->key.scancode);
wota_write_text(wb, "mod");
wota_write_number(wb, (double)e->key.mod);
break;
case SDL_EVENT_FINGER_MOTION:
case SDL_EVENT_FINGER_DOWN:
case SDL_EVENT_FINGER_UP:
wota_write_text(wb, "touch");
wota_write_number(wb, (double)e->tfinger.touchID);
wota_write_text(wb, "finger");
wota_write_number(wb, (double)e->tfinger.fingerID);
wota_write_text(wb, "pos");
wota_write_vec2(wb, (double)e->tfinger.x, (double)e->tfinger.y);
wota_write_text(wb, "d_pos");
wota_write_vec2(wb, (double)e->tfinger.dx, (double)e->tfinger.dy);
wota_write_text(wb, "pressure");
wota_write_number(wb, (double)e->tfinger.pressure);
wota_write_text(wb, "window");
wota_write_number(wb, (double)e->key.windowID);
break;
case SDL_EVENT_DROP_BEGIN:
case SDL_EVENT_DROP_FILE:
case SDL_EVENT_DROP_TEXT:
case SDL_EVENT_DROP_COMPLETE:
case SDL_EVENT_DROP_POSITION:
wota_write_text(wb, "window");
wota_write_number(wb, (double)e->drop.windowID);
wota_write_text(wb, "pos");
wota_write_vec2(wb, (double)e->drop.x, (double)e->drop.y);
wota_write_text(wb, "data");
wota_write_text(wb, e->drop.data ? e->drop.data : "");
wota_write_text(wb, "source");
wota_write_text(wb, e->drop.source ? e->drop.source : "");
break;
case SDL_EVENT_TEXT_INPUT:
wota_write_text(wb, "window");
wota_write_number(wb, (double)e->text.windowID);
wota_write_text(wb, "text");
wota_write_text(wb, e->text.text);
wota_write_text(wb, "mod");
wota_write_number(wb, 0);
break;
case SDL_EVENT_CAMERA_DEVICE_APPROVED:
case SDL_EVENT_CAMERA_DEVICE_REMOVED:
case SDL_EVENT_CAMERA_DEVICE_ADDED:
case SDL_EVENT_CAMERA_DEVICE_DENIED:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->cdevice.which);
break;
case SDL_EVENT_CLIPBOARD_UPDATE:
wota_write_text(wb, "owner");
wota_write_sym(wb, e->clipboard.owner ? WOTA_TRUE : WOTA_FALSE);
break;
case SDL_EVENT_WINDOW_EXPOSED:
case SDL_EVENT_WINDOW_FOCUS_GAINED:
case SDL_EVENT_WINDOW_FOCUS_LOST:
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->window.windowID);
break;
case SDL_EVENT_WINDOW_SHOWN:
case SDL_EVENT_WINDOW_HIDDEN:
case SDL_EVENT_WINDOW_MINIMIZED:
case SDL_EVENT_WINDOW_MAXIMIZED:
case SDL_EVENT_WINDOW_RESTORED:
case SDL_EVENT_WINDOW_MOUSE_ENTER:
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
case SDL_EVENT_WINDOW_HIT_TEST:
case SDL_EVENT_WINDOW_ICCPROF_CHANGED:
case SDL_EVENT_WINDOW_OCCLUDED:
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
case SDL_EVENT_WINDOW_DESTROYED:
case SDL_EVENT_WINDOW_HDR_STATE_CHANGED:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->window.windowID);
wota_write_text(wb, "data1");
wota_write_number(wb, (double)e->window.data1);
wota_write_text(wb, "data2");
wota_write_number(wb, (double)e->window.data2);
break;
case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED:
{
SDL_Window *window = SDL_GetWindowFromID(e->window.windowID);
SDL_Rect safe_area = {0, 0, 0, 0};
if (window && SDL_GetWindowSafeArea(window, &safe_area)) {
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->window.windowID);
wota_write_text(wb, "x");
wota_write_number(wb, (double)safe_area.x);
wota_write_text(wb, "y");
wota_write_number(wb, (double)safe_area.y);
wota_write_text(wb, "width");
wota_write_number(wb, (double)safe_area.w);
wota_write_text(wb, "height");
wota_write_number(wb, (double)safe_area.h);
} else {
// Fallback to original behavior if SDL_GetWindowSafeArea fails
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->window.windowID);
wota_write_text(wb, "data1");
wota_write_number(wb, (double)e->window.data1);
wota_write_text(wb, "data2");
wota_write_number(wb, (double)e->window.data2);
}
}
break;
case SDL_EVENT_WINDOW_MOVED:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->window.windowID);
wota_write_text(wb, "x");
wota_write_number(wb, (double)e->window.data1);
wota_write_text(wb, "y");
wota_write_number(wb, (double)e->window.data2);
break;
case SDL_EVENT_WINDOW_RESIZED:
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
case SDL_EVENT_WINDOW_METAL_VIEW_RESIZED:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->window.windowID);
wota_write_text(wb, "width");
wota_write_number(wb, (double)e->window.data1);
wota_write_text(wb, "height");
wota_write_number(wb, (double)e->window.data2);
break;
case SDL_EVENT_WINDOW_DISPLAY_CHANGED:
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->window.windowID);
wota_write_text(wb, "display_index");
wota_write_number(wb, (double)e->window.data1);
wota_write_text(wb, "data2");
wota_write_number(wb, (double)e->window.data2);
break;
case SDL_EVENT_JOYSTICK_ADDED:
case SDL_EVENT_JOYSTICK_REMOVED:
case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->jdevice.which);
break;
case SDL_EVENT_JOYSTICK_AXIS_MOTION:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->jaxis.which);
wota_write_text(wb, "axis");
wota_write_number(wb, (double)e->jaxis.axis);
wota_write_text(wb, "value");
wota_write_number(wb, (double)e->jaxis.value);
break;
case SDL_EVENT_JOYSTICK_BALL_MOTION:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->jball.which);
wota_write_text(wb, "ball");
wota_write_number(wb, (double)e->jball.ball);
wota_write_text(wb, "rel");
wota_write_vec2(wb, (double)e->jball.xrel, (double)e->jball.yrel);
break;
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
case SDL_EVENT_JOYSTICK_BUTTON_UP:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->jbutton.which);
wota_write_text(wb, "button");
wota_write_number(wb, (double)e->jbutton.button);
wota_write_text(wb, "down");
wota_write_sym(wb, e->jbutton.down ? WOTA_TRUE : WOTA_FALSE);
break;
case SDL_EVENT_GAMEPAD_ADDED:
case SDL_EVENT_GAMEPAD_REMOVED:
case SDL_EVENT_GAMEPAD_REMAPPED:
case SDL_EVENT_GAMEPAD_UPDATE_COMPLETE:
case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->gdevice.which);
break;
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->gaxis.which);
wota_write_text(wb, "axis");
wota_write_text(wb, SDL_GetGamepadStringForAxis(e->gaxis.axis));
wota_write_text(wb, "value");
// Normalize axis values
double normalized_value;
if (e->gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER ||
e->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
// Triggers: 0 to 32767 -> 0 to 1
normalized_value = (double)e->gaxis.value / 32767.0;
} else {
// Thumbsticks: -32768 to 32767 -> -1 to 1
normalized_value = (double)e->gaxis.value / 32767.0;
}
wota_write_number(wb, normalized_value);
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->gbutton.which);
wota_write_text(wb, "button");
wota_write_text(wb, SDL_GetGamepadStringForButton(e->gbutton.button));
wota_write_text(wb, "down");
wota_write_sym(wb, e->gbutton.down ? WOTA_TRUE : WOTA_FALSE);
break;
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->gtouchpad.which);
wota_write_text(wb, "touchpad");
wota_write_number(wb, (double)e->gtouchpad.touchpad);
wota_write_text(wb, "finger");
wota_write_number(wb, (double)e->gtouchpad.finger);
wota_write_text(wb, "pos");
wota_write_vec2(wb, (double)e->gtouchpad.x, (double)e->gtouchpad.y);
wota_write_text(wb, "pressure");
wota_write_number(wb, (double)e->gtouchpad.pressure);
break;
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
wota_write_text(wb, "which");
wota_write_number(wb, (double)e->gsensor.which);
wota_write_text(wb, "sensor");
wota_write_number(wb, (double)e->gsensor.sensor);
wota_write_text(wb, "sensor_timestamp");
wota_write_number(wb, (double)e->gsensor.sensor_timestamp);
break;
case SDL_EVENT_USER:
wota_write_text(wb, "cb");
wota_write_number(wb, (double)(uintptr_t)e->user.data1);
break;
}
}
static WotaBuffer event2wota(const SDL_Event *event) {
WotaBuffer wb;
wota_buffer_init(&wb, 8);
int n = event2wota_count_props(event);
event2wota_write(&wb, event, n);
return wb;
}
// Get all events directly from SDL event queue
JSC_CCALL(input_get_events,
JSValue events_array = JS_NewArray(js);
SDL_Event event;
int event_count = 0;
while (SDL_PollEvent(&event)) {
// gui_input(&event);
WotaBuffer wb = event2wota(&event);
JSValue event_obj = wota2value(js, wb.data);
JS_SetPropertyUint32(js, events_array, event_count, event_obj);
wota_buffer_free(&wb);
event_count++;
}
return events_array;
)
JSC_CCALL(input_gamepad_id_to_type,
int id = js2number(js, argv[0]);
return JS_NewString(js, SDL_GetGamepadStringForType(SDL_GetGamepadTypeForID(id)));
)
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, get_events, 0),
MIST_FUNC_DEF(input, gamepad_id_to_type, 1),
};
CELL_USE_FUNCS(js_input_funcs)

393
joystick.c Normal file
View File

@@ -0,0 +1,393 @@
#include "cell.h"
#include <SDL3/SDL.h>
// Joystick type enum to string
static const char *joystick_type_to_string(SDL_JoystickType type) {
switch (type) {
case SDL_JOYSTICK_TYPE_GAMEPAD: return "gamepad";
case SDL_JOYSTICK_TYPE_WHEEL: return "wheel";
case SDL_JOYSTICK_TYPE_ARCADE_STICK: return "arcade_stick";
case SDL_JOYSTICK_TYPE_FLIGHT_STICK: return "flight_stick";
case SDL_JOYSTICK_TYPE_DANCE_PAD: return "dance_pad";
case SDL_JOYSTICK_TYPE_GUITAR: return "guitar";
case SDL_JOYSTICK_TYPE_DRUM_KIT: return "drum_kit";
case SDL_JOYSTICK_TYPE_ARCADE_PAD: return "arcade_pad";
case SDL_JOYSTICK_TYPE_THROTTLE: return "throttle";
default: return "unknown";
}
}
// Connection state to string
static const char *connection_state_to_string(SDL_JoystickConnectionState state) {
switch (state) {
case SDL_JOYSTICK_CONNECTION_WIRED: return "wired";
case SDL_JOYSTICK_CONNECTION_WIRELESS: return "wireless";
case SDL_JOYSTICK_CONNECTION_UNKNOWN: return "unknown";
default: return "invalid";
}
}
// SDL_Joystick class
void SDL_Joystick_free(JSRuntime *rt, SDL_Joystick *joystick) {
if (joystick) SDL_CloseJoystick(joystick);
}
QJSCLASS(SDL_Joystick,)
// SDL_LockJoysticks()
JSC_CCALL(joystick_lock,
SDL_LockJoysticks();
return JS_NULL;
)
// SDL_UnlockJoysticks()
JSC_CCALL(joystick_unlock,
SDL_UnlockJoysticks();
return JS_NULL;
)
// SDL_HasJoystick() -> bool
JSC_CCALL(joystick_has,
return JS_NewBool(js, SDL_HasJoystick());
)
// SDL_GetJoysticks() -> array of joystick IDs
JSC_CCALL(joystick_get_joysticks,
int count = 0;
SDL_JoystickID *joysticks = SDL_GetJoysticks(&count);
if (!joysticks) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewUint32(js, joysticks[i]));
}
SDL_free(joysticks);
return arr;
)
// SDL_GetJoystickNameForID(id) -> string
JSC_CCALL(joystick_get_name_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
const char *name = SDL_GetJoystickNameForID(id);
return name ? JS_NewString(js, name) : JS_NULL;
)
// SDL_GetJoystickPathForID(id) -> string
JSC_CCALL(joystick_get_path_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
const char *path = SDL_GetJoystickPathForID(id);
return path ? JS_NewString(js, path) : JS_NULL;
)
// SDL_GetJoystickPlayerIndexForID(id) -> number
JSC_CCALL(joystick_get_player_index_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
return JS_NewInt32(js, SDL_GetJoystickPlayerIndexForID(id));
)
// SDL_GetJoystickGUIDForID(id) -> string
JSC_CCALL(joystick_get_guid_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_GUID guid = SDL_GetJoystickGUIDForID(id);
char buf[64];
SDL_GUIDToString(guid, buf, sizeof(buf));
return JS_NewString(js, buf);
)
// SDL_GetJoystickVendorForID(id) -> number
JSC_CCALL(joystick_get_vendor_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
return JS_NewUint32(js, SDL_GetJoystickVendorForID(id));
)
// SDL_GetJoystickProductForID(id) -> number
JSC_CCALL(joystick_get_product_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
return JS_NewUint32(js, SDL_GetJoystickProductForID(id));
)
// SDL_GetJoystickProductVersionForID(id) -> number
JSC_CCALL(joystick_get_product_version_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
return JS_NewUint32(js, SDL_GetJoystickProductVersionForID(id));
)
// SDL_GetJoystickTypeForID(id) -> string
JSC_CCALL(joystick_get_type_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_JoystickType type = SDL_GetJoystickTypeForID(id);
return JS_NewString(js, joystick_type_to_string(type));
)
// SDL_OpenJoystick(id) -> Joystick object
JSC_CCALL(joystick_open,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_Joystick *joystick = SDL_OpenJoystick(id);
if (!joystick) return JS_NULL;
return SDL_Joystick2js(js, joystick);
)
// SDL_GetJoystickFromID(id) -> Joystick object (does not take ownership)
JSC_CCALL(joystick_get_from_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_Joystick *joystick = SDL_GetJoystickFromID(id);
if (!joystick) return JS_NULL;
// Note: This returns an existing joystick, not a new one
// We should not free it when the JS object is garbage collected
return SDL_Joystick2js(js, joystick);
)
// SDL_GetJoystickFromPlayerIndex(player_index) -> Joystick object
JSC_CCALL(joystick_get_from_player_index,
int player_index;
JS_ToInt32(js, &player_index, argv[0]);
SDL_Joystick *joystick = SDL_GetJoystickFromPlayerIndex(player_index);
if (!joystick) return JS_NULL;
return SDL_Joystick2js(js, joystick);
)
// Joystick instance methods
JSC_CCALL(joystick_get_name,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
const char *name = SDL_GetJoystickName(joystick);
return name ? JS_NewString(js, name) : JS_NULL;
)
JSC_CCALL(joystick_get_path,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
const char *path = SDL_GetJoystickPath(joystick);
return path ? JS_NewString(js, path) : JS_NULL;
)
JSC_CCALL(joystick_get_player_index,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
return JS_NewInt32(js, SDL_GetJoystickPlayerIndex(joystick));
)
JSC_CCALL(joystick_set_player_index,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
int player_index;
JS_ToInt32(js, &player_index, argv[0]);
return JS_NewBool(js, SDL_SetJoystickPlayerIndex(joystick, player_index));
)
JSC_CCALL(joystick_get_guid,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
SDL_GUID guid = SDL_GetJoystickGUID(joystick);
char buf[64];
SDL_GUIDToString(guid, buf, sizeof(buf));
return JS_NewString(js, buf);
)
JSC_CCALL(joystick_get_vendor,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
return JS_NewUint32(js, SDL_GetJoystickVendor(joystick));
)
JSC_CCALL(joystick_get_product,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
return JS_NewUint32(js, SDL_GetJoystickProduct(joystick));
)
JSC_CCALL(joystick_get_product_version,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
return JS_NewUint32(js, SDL_GetJoystickProductVersion(joystick));
)
JSC_CCALL(joystick_get_serial,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
const char *serial = SDL_GetJoystickSerial(joystick);
return serial ? JS_NewString(js, serial) : JS_NULL;
)
JSC_CCALL(joystick_get_type,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
SDL_JoystickType type = SDL_GetJoystickType(joystick);
return JS_NewString(js, joystick_type_to_string(type));
)
JSC_CCALL(joystick_get_id,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
return JS_NewUint32(js, SDL_GetJoystickID(joystick));
)
JSC_CCALL(joystick_get_num_axes,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
return JS_NewInt32(js, SDL_GetNumJoystickAxes(joystick));
)
JSC_CCALL(joystick_get_num_balls,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
return JS_NewInt32(js, SDL_GetNumJoystickBalls(joystick));
)
JSC_CCALL(joystick_get_num_hats,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
return JS_NewInt32(js, SDL_GetNumJoystickHats(joystick));
)
JSC_CCALL(joystick_get_num_buttons,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
return JS_NewInt32(js, SDL_GetNumJoystickButtons(joystick));
)
JSC_CCALL(joystick_get_axis,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
int axis;
JS_ToInt32(js, &axis, argv[0]);
return JS_NewInt32(js, SDL_GetJoystickAxis(joystick, axis));
)
JSC_CCALL(joystick_get_ball,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
int ball;
JS_ToInt32(js, &ball, argv[0]);
int dx, dy;
if (!SDL_GetJoystickBall(joystick, ball, &dx, &dy)) return JS_NULL;
JSValue result = JS_NewObject(js);
JS_SetPropertyStr(js, result, "dx", JS_NewInt32(js, dx));
JS_SetPropertyStr(js, result, "dy", JS_NewInt32(js, dy));
return result;
)
JSC_CCALL(joystick_get_hat,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
int hat;
JS_ToInt32(js, &hat, argv[0]);
return JS_NewUint32(js, SDL_GetJoystickHat(joystick, hat));
)
JSC_CCALL(joystick_get_button,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
int button;
JS_ToInt32(js, &button, argv[0]);
return JS_NewBool(js, SDL_GetJoystickButton(joystick, button));
)
JSC_CCALL(joystick_rumble,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
uint32_t low_freq, high_freq, duration_ms;
JS_ToUint32(js, &low_freq, argv[0]);
JS_ToUint32(js, &high_freq, argv[1]);
JS_ToUint32(js, &duration_ms, argv[2]);
return JS_NewBool(js, SDL_RumbleJoystick(joystick, low_freq, high_freq, duration_ms));
)
JSC_CCALL(joystick_rumble_triggers,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
uint32_t left, right, duration_ms;
JS_ToUint32(js, &left, argv[0]);
JS_ToUint32(js, &right, argv[1]);
JS_ToUint32(js, &duration_ms, argv[2]);
return JS_NewBool(js, SDL_RumbleJoystickTriggers(joystick, left, right, duration_ms));
)
JSC_CCALL(joystick_set_led,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
int r, g, b;
JS_ToInt32(js, &r, argv[0]);
JS_ToInt32(js, &g, argv[1]);
JS_ToInt32(js, &b, argv[2]);
return JS_NewBool(js, SDL_SetJoystickLED(joystick, r, g, b));
)
JSC_CCALL(joystick_get_connection_state,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
SDL_JoystickConnectionState state = SDL_GetJoystickConnectionState(joystick);
return JS_NewString(js, connection_state_to_string(state));
)
JSC_CCALL(joystick_get_power_info,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
int percent;
SDL_PowerState state = SDL_GetJoystickPowerInfo(joystick, &percent);
JSValue result = JS_NewObject(js);
const char *state_str;
switch (state) {
case SDL_POWERSTATE_ON_BATTERY: state_str = "on_battery"; break;
case SDL_POWERSTATE_NO_BATTERY: state_str = "no_battery"; break;
case SDL_POWERSTATE_CHARGING: state_str = "charging"; break;
case SDL_POWERSTATE_CHARGED: state_str = "charged"; break;
default: state_str = "unknown"; break;
}
JS_SetPropertyStr(js, result, "state", JS_NewString(js, state_str));
JS_SetPropertyStr(js, result, "percent", JS_NewInt32(js, percent));
return result;
)
JSC_CCALL(joystick_close,
SDL_Joystick *joystick = js2SDL_Joystick(js, self);
SDL_CloseJoystick(joystick);
return JS_NULL;
)
static const JSCFunctionListEntry js_SDL_Joystick_funcs[] = {
MIST_FUNC_DEF(joystick, get_name, 0),
MIST_FUNC_DEF(joystick, get_path, 0),
MIST_FUNC_DEF(joystick, get_player_index, 0),
MIST_FUNC_DEF(joystick, set_player_index, 1),
MIST_FUNC_DEF(joystick, get_guid, 0),
MIST_FUNC_DEF(joystick, get_vendor, 0),
MIST_FUNC_DEF(joystick, get_product, 0),
MIST_FUNC_DEF(joystick, get_product_version, 0),
MIST_FUNC_DEF(joystick, get_serial, 0),
MIST_FUNC_DEF(joystick, get_type, 0),
MIST_FUNC_DEF(joystick, get_id, 0),
MIST_FUNC_DEF(joystick, get_num_axes, 0),
MIST_FUNC_DEF(joystick, get_num_balls, 0),
MIST_FUNC_DEF(joystick, get_num_hats, 0),
MIST_FUNC_DEF(joystick, get_num_buttons, 0),
MIST_FUNC_DEF(joystick, get_axis, 1),
MIST_FUNC_DEF(joystick, get_ball, 1),
MIST_FUNC_DEF(joystick, get_hat, 1),
MIST_FUNC_DEF(joystick, get_button, 1),
MIST_FUNC_DEF(joystick, rumble, 3),
MIST_FUNC_DEF(joystick, rumble_triggers, 3),
MIST_FUNC_DEF(joystick, set_led, 3),
MIST_FUNC_DEF(joystick, get_connection_state, 0),
MIST_FUNC_DEF(joystick, get_power_info, 0),
MIST_FUNC_DEF(joystick, close, 0),
};
static const JSCFunctionListEntry js_joystick_funcs[] = {
MIST_FUNC_DEF(joystick, lock, 0),
MIST_FUNC_DEF(joystick, unlock, 0),
MIST_FUNC_DEF(joystick, has, 0),
MIST_FUNC_DEF(joystick, get_joysticks, 0),
MIST_FUNC_DEF(joystick, get_name_for_id, 1),
MIST_FUNC_DEF(joystick, get_path_for_id, 1),
MIST_FUNC_DEF(joystick, get_player_index_for_id, 1),
MIST_FUNC_DEF(joystick, get_guid_for_id, 1),
MIST_FUNC_DEF(joystick, get_vendor_for_id, 1),
MIST_FUNC_DEF(joystick, get_product_for_id, 1),
MIST_FUNC_DEF(joystick, get_product_version_for_id, 1),
MIST_FUNC_DEF(joystick, get_type_for_id, 1),
MIST_FUNC_DEF(joystick, open, 1),
MIST_FUNC_DEF(joystick, get_from_id, 1),
MIST_FUNC_DEF(joystick, get_from_player_index, 1),
};
CELL_USE_INIT(
SDL_Init(SDL_INIT_JOYSTICK);
QJSCLASSPREP_FUNCS(SDL_Joystick);
JSValue ret = JS_NewObject(js);
JS_SetPropertyFunctionList(js, ret, js_joystick_funcs, countof(js_joystick_funcs));
// Export axis constants
JS_SetPropertyStr(js, ret, "AXIS_MAX", JS_NewInt32(js, SDL_JOYSTICK_AXIS_MAX));
JS_SetPropertyStr(js, ret, "AXIS_MIN", JS_NewInt32(js, SDL_JOYSTICK_AXIS_MIN));
return ret;
)

143
keyboard.c Normal file
View File

@@ -0,0 +1,143 @@
#include "cell.h"
#include <SDL3/SDL.h>
// SDL_HasKeyboard() -> bool
JSC_CCALL(keyboard_has,
return JS_NewBool(js, SDL_HasKeyboard());
)
// SDL_GetKeyboards() -> array of keyboard IDs
JSC_CCALL(keyboard_get_keyboards,
int count = 0;
SDL_KeyboardID *keyboards = SDL_GetKeyboards(&count);
if (!keyboards) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewUint32(js, keyboards[i]));
}
SDL_free(keyboards);
return arr;
)
// SDL_GetKeyboardNameForID(id) -> string
JSC_SCALL(keyboard_get_name,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
const char *name = SDL_GetKeyboardNameForID(id);
return name ? JS_NewString(js, name) : JS_NewString(js, "");
)
// SDL_GetKeyboardState() -> object with key states
JSC_CCALL(keyboard_get_state,
int numkeys = 0;
const bool *state = SDL_GetKeyboardState(&numkeys);
if (!state) return JS_NULL;
JSValue arr = JS_NewArray(js);
for (int i = 0; i < numkeys; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewBool(js, state[i]));
}
return arr;
)
// SDL_ResetKeyboard()
JSC_CCALL(keyboard_reset,
SDL_ResetKeyboard();
return JS_NULL;
)
// SDL_GetModState() -> number (bitmask)
JSC_CCALL(keyboard_get_mod_state,
return JS_NewUint32(js, SDL_GetModState());
)
// SDL_SetModState(modstate)
JSC_CCALL(keyboard_set_mod_state,
uint32_t modstate;
JS_ToUint32(js, &modstate, argv[0]);
SDL_SetModState((SDL_Keymod)modstate);
return JS_NULL;
)
// SDL_GetKeyFromScancode(scancode, modstate, key_event) -> keycode
JSC_CCALL(keyboard_get_key_from_scancode,
int scancode;
uint32_t modstate = 0;
int key_event = 0;
JS_ToInt32(js, &scancode, argv[0]);
if (argc > 1) JS_ToUint32(js, &modstate, argv[1]);
if (argc > 2) key_event = JS_ToBool(js, argv[2]);
return JS_NewUint32(js, SDL_GetKeyFromScancode((SDL_Scancode)scancode, (SDL_Keymod)modstate, key_event));
)
// SDL_GetScancodeFromKey(key) -> scancode
JSC_CCALL(keyboard_get_scancode_from_key,
uint32_t key;
JS_ToUint32(js, &key, argv[0]);
SDL_Keymod modstate;
SDL_Scancode scancode = SDL_GetScancodeFromKey((SDL_Keycode)key, &modstate);
JSValue result = JS_NewObject(js);
JS_SetPropertyStr(js, result, "scancode", JS_NewInt32(js, scancode));
JS_SetPropertyStr(js, result, "modstate", JS_NewUint32(js, modstate));
return result;
)
// SDL_GetScancodeName(scancode) -> string
JSC_CCALL(keyboard_get_scancode_name,
int scancode;
JS_ToInt32(js, &scancode, argv[0]);
const char *name = SDL_GetScancodeName((SDL_Scancode)scancode);
return JS_NewString(js, name ? name : "");
)
// SDL_GetScancodeFromName(name) -> scancode
JSC_SCALL(keyboard_get_scancode_from_name,
const char *name = JS_ToCString(js, argv[0]);
if (!name) return JS_EXCEPTION;
SDL_Scancode scancode = SDL_GetScancodeFromName(name);
JS_FreeCString(js, name);
return JS_NewInt32(js, scancode);
)
// SDL_GetKeyName(key) -> string
JSC_CCALL(keyboard_get_key_name,
uint32_t key;
JS_ToUint32(js, &key, argv[0]);
const char *name = SDL_GetKeyName((SDL_Keycode)key);
return JS_NewString(js, name ? name : "");
)
// SDL_GetKeyFromName(name) -> keycode
JSC_SCALL(keyboard_get_key_from_name,
const char *name = JS_ToCString(js, argv[0]);
if (!name) return JS_EXCEPTION;
SDL_Keycode key = SDL_GetKeyFromName(name);
JS_FreeCString(js, name);
return JS_NewUint32(js, key);
)
// SDL_HasScreenKeyboardSupport() -> bool
JSC_CCALL(keyboard_has_screen_support,
return JS_NewBool(js, SDL_HasScreenKeyboardSupport());
)
static const JSCFunctionListEntry js_keyboard_funcs[] = {
MIST_FUNC_DEF(keyboard, has, 0),
MIST_FUNC_DEF(keyboard, get_keyboards, 0),
MIST_FUNC_DEF(keyboard, get_name, 1),
MIST_FUNC_DEF(keyboard, get_state, 0),
MIST_FUNC_DEF(keyboard, reset, 0),
MIST_FUNC_DEF(keyboard, get_mod_state, 0),
MIST_FUNC_DEF(keyboard, set_mod_state, 1),
MIST_FUNC_DEF(keyboard, get_key_from_scancode, 3),
MIST_FUNC_DEF(keyboard, get_scancode_from_key, 1),
MIST_FUNC_DEF(keyboard, get_scancode_name, 1),
MIST_FUNC_DEF(keyboard, get_scancode_from_name, 1),
MIST_FUNC_DEF(keyboard, get_key_name, 1),
MIST_FUNC_DEF(keyboard, get_key_from_name, 1),
MIST_FUNC_DEF(keyboard, has_screen_support, 0),
};
CELL_USE_FUNCS(js_keyboard_funcs)

109
mouse.c Normal file
View File

@@ -0,0 +1,109 @@
#include "cell.h"
#include <SDL3/SDL.h>
// SDL_HasMouse() -> bool
JSC_CCALL(mouse_has,
return JS_NewBool(js, SDL_HasMouse());
)
// SDL_GetMice() -> array of mouse IDs
JSC_CCALL(mouse_get_mice,
int count = 0;
SDL_MouseID *mice = SDL_GetMice(&count);
if (!mice) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewUint32(js, mice[i]));
}
SDL_free(mice);
return arr;
)
// SDL_GetMouseNameForID(id) -> string
JSC_CCALL(mouse_get_name,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
const char *name = SDL_GetMouseNameForID(id);
return name ? JS_NewString(js, name) : JS_NewString(js, "");
)
// SDL_GetMouseState() -> {x, y, buttons}
JSC_CCALL(mouse_get_state,
float x, y;
SDL_MouseButtonFlags buttons = SDL_GetMouseState(&x, &y);
JSValue result = JS_NewObject(js);
JS_SetPropertyStr(js, result, "x", JS_NewFloat64(js, x));
JS_SetPropertyStr(js, result, "y", JS_NewFloat64(js, y));
JS_SetPropertyStr(js, result, "buttons", JS_NewUint32(js, buttons));
return result;
)
// SDL_GetGlobalMouseState() -> {x, y, buttons}
JSC_CCALL(mouse_get_global_state,
float x, y;
SDL_MouseButtonFlags buttons = SDL_GetGlobalMouseState(&x, &y);
JSValue result = JS_NewObject(js);
JS_SetPropertyStr(js, result, "x", JS_NewFloat64(js, x));
JS_SetPropertyStr(js, result, "y", JS_NewFloat64(js, y));
JS_SetPropertyStr(js, result, "buttons", JS_NewUint32(js, buttons));
return result;
)
// SDL_GetRelativeMouseState() -> {x, y, buttons}
JSC_CCALL(mouse_get_relative_state,
float x, y;
SDL_MouseButtonFlags buttons = SDL_GetRelativeMouseState(&x, &y);
JSValue result = JS_NewObject(js);
JS_SetPropertyStr(js, result, "x", JS_NewFloat64(js, x));
JS_SetPropertyStr(js, result, "y", JS_NewFloat64(js, y));
JS_SetPropertyStr(js, result, "buttons", JS_NewUint32(js, buttons));
return result;
)
// SDL_WarpMouseGlobal(x, y) -> bool
JSC_CCALL(mouse_warp_global,
float x = js2number(js, argv[0]);
float y = js2number(js, argv[1]);
return JS_NewBool(js, SDL_WarpMouseGlobal(x, y));
)
// SDL_CaptureMouse(enabled) -> bool
JSC_CCALL(mouse_capture,
bool enabled = JS_ToBool(js, argv[0]);
return JS_NewBool(js, SDL_CaptureMouse(enabled));
)
// SDL_ShowCursor() -> bool
JSC_CCALL(mouse_show_cursor,
return JS_NewBool(js, SDL_ShowCursor());
)
// SDL_HideCursor() -> bool
JSC_CCALL(mouse_hide_cursor,
return JS_NewBool(js, SDL_HideCursor());
)
// SDL_CursorVisible() -> bool
JSC_CCALL(mouse_cursor_visible,
return JS_NewBool(js, SDL_CursorVisible());
)
static const JSCFunctionListEntry js_mouse_funcs[] = {
MIST_FUNC_DEF(mouse, has, 0),
MIST_FUNC_DEF(mouse, get_mice, 0),
MIST_FUNC_DEF(mouse, get_name, 1),
MIST_FUNC_DEF(mouse, get_state, 0),
MIST_FUNC_DEF(mouse, get_global_state, 0),
MIST_FUNC_DEF(mouse, get_relative_state, 0),
MIST_FUNC_DEF(mouse, warp_global, 2),
MIST_FUNC_DEF(mouse, capture, 1),
MIST_FUNC_DEF(mouse, show_cursor, 0),
MIST_FUNC_DEF(mouse, hide_cursor, 0),
MIST_FUNC_DEF(mouse, cursor_visible, 0),
};
CELL_USE_FUNCS(js_mouse_funcs)

83
pen.c Normal file
View File

@@ -0,0 +1,83 @@
#include "cell.h"
#include <SDL3/SDL.h>
// Pen axis enum to string
static const char *pen_axis_to_string(SDL_PenAxis axis) {
switch (axis) {
case SDL_PEN_AXIS_PRESSURE: return "pressure";
case SDL_PEN_AXIS_XTILT: return "xtilt";
case SDL_PEN_AXIS_YTILT: return "ytilt";
case SDL_PEN_AXIS_DISTANCE: return "distance";
case SDL_PEN_AXIS_ROTATION: return "rotation";
case SDL_PEN_AXIS_SLIDER: return "slider";
case SDL_PEN_AXIS_TANGENTIAL_PRESSURE: return "tangential_pressure";
default: return "unknown";
}
}
static SDL_PenAxis string_to_pen_axis(const char *str) {
if (!str) return SDL_PEN_AXIS_PRESSURE;
if (!strcmp(str, "pressure")) return SDL_PEN_AXIS_PRESSURE;
if (!strcmp(str, "xtilt")) return SDL_PEN_AXIS_XTILT;
if (!strcmp(str, "ytilt")) return SDL_PEN_AXIS_YTILT;
if (!strcmp(str, "distance")) return SDL_PEN_AXIS_DISTANCE;
if (!strcmp(str, "rotation")) return SDL_PEN_AXIS_ROTATION;
if (!strcmp(str, "slider")) return SDL_PEN_AXIS_SLIDER;
if (!strcmp(str, "tangential_pressure")) return SDL_PEN_AXIS_TANGENTIAL_PRESSURE;
return SDL_PEN_AXIS_PRESSURE;
}
// Get pen axis count
JSC_CCALL(pen_get_axis_count,
return JS_NewInt32(js, SDL_PEN_AXIS_COUNT);
)
// Get pen axis name
JSC_CCALL(pen_get_axis_name,
int axis;
JS_ToInt32(js, &axis, argv[0]);
return JS_NewString(js, pen_axis_to_string((SDL_PenAxis)axis));
)
// Get pen axis from name
JSC_SCALL(pen_get_axis_from_name,
const char *name = JS_ToCString(js, argv[0]);
if (!name) return JS_EXCEPTION;
SDL_PenAxis axis = string_to_pen_axis(name);
JS_FreeCString(js, name);
return JS_NewInt32(js, axis);
)
// Pen input flags helpers
JSC_CCALL(pen_input_down,
return JS_NewUint32(js, SDL_PEN_INPUT_DOWN);
)
JSC_CCALL(pen_input_button_1,
return JS_NewUint32(js, SDL_PEN_INPUT_BUTTON_1);
)
JSC_CCALL(pen_input_button_2,
return JS_NewUint32(js, SDL_PEN_INPUT_BUTTON_2);
)
JSC_CCALL(pen_input_button_3,
return JS_NewUint32(js, SDL_PEN_INPUT_BUTTON_3);
)
JSC_CCALL(pen_input_eraser_tip,
return JS_NewUint32(js, SDL_PEN_INPUT_ERASER_TIP);
)
static const JSCFunctionListEntry js_pen_funcs[] = {
MIST_FUNC_DEF(pen, get_axis_count, 0),
MIST_FUNC_DEF(pen, get_axis_name, 1),
MIST_FUNC_DEF(pen, get_axis_from_name, 1),
MIST_FUNC_DEF(pen, input_down, 0),
MIST_FUNC_DEF(pen, input_button_1, 0),
MIST_FUNC_DEF(pen, input_button_2, 0),
MIST_FUNC_DEF(pen, input_button_3, 0),
MIST_FUNC_DEF(pen, input_eraser_tip, 0),
};
CELL_USE_FUNCS(js_pen_funcs)

250
render.c Normal file
View File

@@ -0,0 +1,250 @@
#include "cell.h"
#include <SDL3/SDL.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "sdl.h"
#include <assert.h>
void SDL_Renderer_free(JSRuntime *rt, SDL_Renderer *renderer) {
if (renderer) SDL_DestroyRenderer(renderer);
}
void SDL_Texture_free(JSRuntime *rt, SDL_Texture *texture) {
if (texture) SDL_DestroyTexture(texture);
}
QJSCLASS(SDL_Renderer,)
QJSCLASS(SDL_Texture,)
JSC_CCALL(SDL_Renderer_clear,
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
SDL_RenderClear(renderer);
)
JSC_CCALL(SDL_Renderer_present,
SDL_Renderer *ren = js2SDL_Renderer(js,self);
SDL_RenderPresent(ren);
)
JSC_CCALL(SDL_Renderer_draw_color,
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
colorf color = js2color(js,argv[0]);
SDL_SetRenderDrawColorFloat(renderer, color.x, color.y, color.z, color.w);
)
JSC_CCALL(SDL_Renderer_rect,
SDL_Renderer *r = js2SDL_Renderer(js,self);
if (!JS_IsNull(argv[1])) {
colorf color = js2color(js,argv[1]);
SDL_SetRenderDrawColorFloat(r, color.x, color.y, color.z, color.w);
}
if (JS_IsArray(js,argv[0])) {
int len = JS_ArrayLength(js,argv[0]);
rect rects[len];
for (int i = 0; i < len; i++) {
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
rects[i] = js2rect(js,val);
JS_FreeValue(js,val);
}
SDL_RenderFillRects(r,rects,len);
} else {
rect rect_var = js2rect(js,argv[0]);
SDL_RenderFillRect(r,&rect_var);
}
)
JSC_CCALL(SDL_Renderer_fillrect,
SDL_Renderer *r = js2SDL_Renderer(js,self);
if (!JS_IsNull(argv[1])) {
colorf color = js2color(js,argv[1]);
SDL_SetRenderDrawColorFloat(r, color.x, color.y, color.z, color.w);
}
if (JS_IsArray(js,argv[0])) {
int len = JS_ArrayLength(js,argv[0]);
rect rects[len];
for (int i = 0; i < len; i++) {
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
rects[i] = js2rect(js,val);
JS_FreeValue(js,val);
}
SDL_RenderFillRects(r,rects,len);
} else {
rect rect_var = js2rect(js,argv[0]);
SDL_RenderFillRect(r,&rect_var);
}
return JS_NULL;
)
JSC_CCALL(renderer_texture,
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
SDL_Texture *tex = js2SDL_Texture(js,argv[0]);
rect dst = js2rect(js,argv[1]);
if (!JS_IsNull(argv[3])) {
colorf color = js2color(js,argv[3]);
SDL_SetTextureColorModFloat(tex, color.x, color.y, color.z);
SDL_SetTextureAlphaModFloat(tex,color.w);
}
if (JS_IsNull(argv[2]))
SDL_RenderTexture(renderer,tex,NULL,&dst);
else {
rect src = js2rect(js,argv[2]);
SDL_RenderTextureRotated(renderer, tex, &src, &dst, 0, NULL, SDL_FLIP_NONE);
}
)
JSC_CCALL(renderer_load_texture,
SDL_Renderer *renderer = js2SDL_Renderer(js, self);
SDL_Surface *surf = js2SDL_Surface(js, argv[0]);
SDL_Texture *tex = SDL_CreateTextureFromSurface(renderer, surf);
if (!tex) return JS_ThrowReferenceError(js, "Could not create texture from surface: %s", SDL_GetError());
ret = SDL_Texture2js(js, tex);
JS_SetPropertyStr(js, ret, "width", number2js(js,tex->w));
JS_SetPropertyStr(js,ret,"height", number2js(js,tex->h));
)
JSC_CCALL(renderer_get_image,
SDL_Renderer *r = js2SDL_Renderer(js,self);
SDL_Surface *surf;
rect rect;
if (JS_IsNull(argv[0]))
surf = SDL_RenderReadPixels(r,NULL);
else {
rect = js2rect(js,argv[0]);
surf = SDL_RenderReadPixels(r,&rect);
}
if (!surf) return JS_ThrowReferenceError(js, "could not make surface from renderer");
return SDL_Surface2js(js,surf);
)
JSC_CCALL(renderer_line,
SDL_Renderer *r = js2SDL_Renderer(js,self);
if (!JS_IsNull(argv[1])) {
colorf color = js2color(js,argv[1]);
SDL_SetRenderDrawColorFloat(r, color.x, color.y, color.z, color.w);
}
if (JS_IsArray(js,argv[0])) {
int len = JS_ArrayLength(js,argv[0]);
vec2 points[len];
assert(sizeof(vec2) == sizeof(SDL_FPoint));
for (int i = 0; i < len; i++) {
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
points[i] = js2vec2(js,val);
JS_FreeValue(js,val);
}
SDL_RenderLines(r,points,len);
}
)
JSC_CCALL(renderer_point,
SDL_Renderer *r = js2SDL_Renderer(js,self);
if (!JS_IsNull(argv[1])) {
colorf color = js2color(js,argv[1]);
SDL_SetRenderDrawColorFloat(r, color.x, color.y, color.z, color.w);
}
if (JS_IsArray(js,argv[0])) {
int len = JS_ArrayLength(js,argv[0]);
vec2 points[len];
assert(sizeof(vec2) ==sizeof(SDL_FPoint));
for (int i = 0; i < len; i++) {
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
points[i] = js2vec2(js,val);
JS_FreeValue(js,val);
}
SDL_RenderPoints(r, points, len);
return JS_NULL;
}
vec2 point = js2vec2(js,argv[0]);
SDL_RenderPoint(r,point.x,point.y);
)
JSC_CCALL(renderer_logical_size,
SDL_Renderer *r = js2SDL_Renderer(js,self);
vec2 v = js2vec2(js,argv[0]);
SDL_SetRenderLogicalPresentation(r,v.x,v.y,SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
)
JSC_CCALL(renderer_viewport,
SDL_Renderer *r = js2SDL_Renderer(js,self);
if (JS_IsNull(argv[0]))
SDL_SetRenderViewport(r,NULL);
else {
rect view = js2rect(js,argv[0]);
SDL_SetRenderViewport(r,&view);
}
return JS_NULL;
)
JSC_CCALL(renderer_clip,
SDL_Renderer *r = js2SDL_Renderer(js,self);
if (JS_IsNull(argv[0]))
SDL_SetRenderClipRect(r,NULL);
else {
rect view = js2rect(js,argv[0]);
SDL_SetRenderClipRect(r,&view);
}
)
JSC_CCALL(renderer_scale,
SDL_Renderer *r = js2SDL_Renderer(js,self);
vec2 v = js2vec2(js,argv[0]);
SDL_SetRenderScale(r, v.x, v.y);
)
JSC_CCALL(renderer_vsync,
SDL_Renderer *r = js2SDL_Renderer(js,self);
SDL_SetRenderVSync(r,js2number(js,argv[0]));
)
JSC_CCALL(renderer_target,
SDL_Renderer *r = js2SDL_Renderer(js,self);
if (JS_IsNull(argv[0]))
SDL_SetRenderTarget(r, NULL);
else {
SDL_Texture *tex = js2SDL_Texture(js,argv[0]);
SDL_SetRenderTarget(r,tex);
}
)
static const JSCFunctionListEntry js_SDL_Renderer_funcs[] = {
JS_CFUNC_DEF("clear", 0, js_SDL_Renderer_clear),
JS_CFUNC_DEF("present", 0, js_SDL_Renderer_present),
JS_CFUNC_DEF("draw_color", 1, js_SDL_Renderer_draw_color),
JS_CFUNC_DEF("rect", 2, js_SDL_Renderer_rect),
JS_CFUNC_DEF("fillrect", 2, js_SDL_Renderer_fillrect),
JS_CFUNC_DEF("line", 2, js_renderer_line),
JS_CFUNC_DEF("point", 2, js_renderer_point),
JS_CFUNC_DEF("texture", 4, js_renderer_texture),
JS_CFUNC_DEF("get_image", 1, js_renderer_get_image),
JS_CFUNC_DEF("scale", 1, js_renderer_scale),
JS_CFUNC_DEF("logical_size", 1, js_renderer_logical_size),
JS_CFUNC_DEF("viewport", 1, js_renderer_viewport),
JS_CFUNC_DEF("clip", 1, js_renderer_clip),
JS_CFUNC_DEF("vsync", 1, js_renderer_vsync),
JS_CFUNC_DEF("target", 1, js_renderer_target),
JS_CFUNC_DEF("load_texture", 1, js_renderer_load_texture),
};
JSC_CCALL(SDL_Renderer_constructor,
if (argc < 1) return JS_ThrowTypeError(js, "Renderer constructor requires a window argument");
SDL_Window *win = js2SDL_Window(js, argv[0]);
SDL_Renderer *r = SDL_CreateRenderer(win, NULL);
if (!r) return JS_ThrowReferenceError(js, "Error creating renderer: %s",SDL_GetError());
SDL_SetRenderDrawBlendMode(r, SDL_BLENDMODE_BLEND);
return SDL_Renderer2js(js, r);
)
CELL_USE_INIT(
SDL_Init(SDL_INIT_VIDEO);
JSValue renderer_ctor = QJSCLASSPREP_FUNCS_CTOR(SDL_Renderer,1);
QJSCLASSPREP_NO_FUNCS(SDL_Texture)
return renderer_ctor;
)

333
sdl.c Normal file
View File

@@ -0,0 +1,333 @@
#include "cell.h"
#include "sdl.h"
#include <math.h>
colorf js2color(JSContext *js,JSValue v) {
if (JS_IsNull(v)) return (colorf){1,1,1,1};
colorf color = {1,1,1,1}; // Default to white
if (JS_IsArray(js, v)) {
// Handle array format: [r, g, b, a]
JSValue c[4];
for (int i = 0; i < 4; i++) c[i] = JS_GetPropertyUint32(js,v,i);
color.x = js2number(js,c[0]);
color.y = js2number(js,c[1]);
color.z = js2number(js,c[2]);
color.w = JS_IsNull(c[3]) ? 1.0 : js2number(js,c[3]);
for (int i = 0; i < 4; i++) JS_FreeValue(js,c[i]);
} else if (JS_IsObject(v)) {
JS_GETPROP(js, color.x, v, r, number)
JS_GETPROP(js, color.y, v, g, number)
JS_GETPROP(js, color.z, v, b, number)
JS_GETPROP(js, color.w, v, a, number)
}
return color;
}
JSValue color2js(JSContext *js, colorf color)
{
JSValue arr = JS_NewArray(js);
JS_SetPropertyUint32(js, arr,0,number2js(js,(double)color.x));
JS_SetPropertyUint32(js, arr,1,number2js(js,(double)color.y));
JS_SetPropertyUint32(js, arr,2,number2js(js,(double)color.z));
JS_SetPropertyUint32(js, arr,3,number2js(js,(double)color.w));
return arr;
}
vec2 js2vec2(JSContext *js,JSValue v)
{
vec2 v2;
// Check if it's an array
if (JS_IsArray(js, v)) {
{ JSValue val = JS_GetPropertyUint32(js,v,0); v2.x = js2number(js, val); JS_FreeValue(js,val); }
{ JSValue val = JS_GetPropertyUint32(js,v,1); v2.y = js2number(js, val); JS_FreeValue(js,val); }
} else {
// Try to get x,y properties from object
JSValue x_val = JS_GetPropertyStr(js, v, "x");
JSValue y_val = JS_GetPropertyStr(js, v, "y");
v2.x = js2number(js, x_val);
v2.y = js2number(js, y_val);
JS_FreeValue(js, x_val);
JS_FreeValue(js, y_val);
}
return v2;
}
vec3 js2vec3(JSContext *js,JSValue v)
{
vec3 v3;
{ JSValue val = JS_GetPropertyUint32(js, v,0); v3.x = js2number(js, val); JS_FreeValue(js,val); }
{ JSValue val = JS_GetPropertyUint32(js, v,1); v3.y = js2number(js, val); JS_FreeValue(js,val); }
{ JSValue val = JS_GetPropertyUint32(js, v,2); v3.z = js2number(js, val); JS_FreeValue(js,val); }
return v3;
}
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++)
{ JSValue val = JS_GetPropertyUint32(js,v,i); arr[i] = js2number(js, val); JS_FreeValue(js,val); }
return arr;
}
double *js2doubles(JSContext *js, JSValue v, size_t *len)
{
*len = JS_ArrayLength(js,v);
double *arr = malloc(sizeof(double)* *len);
for (int i = 0; i < *len; i++)
{ JSValue val = JS_GetPropertyUint32(js,v,i); arr[i] = js2number(js, val); JS_FreeValue(js,val); }
return arr;
}
vec3 js2vec3f(JSContext *js, JSValue v)
{
vec3 vec;
if (JS_IsArray(js, v))
return js2vec3(js,v);
else
vec.x = vec.y = vec.z = js2number(js,v);
return vec;
}
JSValue vec32js(JSContext *js, vec3 v)
{
JSValue array = JS_NewArray(js);
JS_SetPropertyUint32(js, array,0,number2js(js,v.x));
JS_SetPropertyUint32(js, array,1,number2js(js,v.y));
JS_SetPropertyUint32(js, array,2,number2js(js,v.z));
return array;
}
JSValue vec3f2js(JSContext *js, vec3 v)
{
return vec32js(js,v);
}
JSValue quat2js(JSContext *js, quat q)
{
JSValue arr = JS_NewArray(js);
JS_SetPropertyUint32(js, arr, 0, number2js(js,q.x));
JS_SetPropertyUint32(js, arr,1,number2js(js,q.y));
JS_SetPropertyUint32(js, arr,2,number2js(js,q.z));
JS_SetPropertyUint32(js, arr,3,number2js(js,q.w));
return arr;
}
vec4 js2vec4(JSContext *js, JSValue v)
{
vec4_union v4;
for (int i = 0; i < 4; i++)
{ JSValue val = JS_GetPropertyUint32(js, v,i); v4.e[i] = js2number(js, val); JS_FreeValue(js,val); }
return (vec4){v4.x, v4.y, v4.z, v4.w};
}
double arr_vec_length(JSContext *js,JSValue v)
{
int len = JS_ArrayLength(js,v);
switch(len) {
case 2: {
vec2 v2 = js2vec2(js,v);
return sqrt(v2.x*v2.x + v2.y*v2.y);
}
case 3: {
vec3 v3 = js2vec3(js,v);
return sqrt(v3.x*v3.x + v3.y*v3.y + v3.z*v3.z);
}
case 4: {
vec4 v4 = js2vec4(js,v);
return sqrt(v4.x*v4.x + v4.y*v4.y + v4.z*v4.z + v4.w*v4.w);
}
}
double sum = 0;
for (int i = 0; i < len; i++)
{ JSValue val = JS_GetPropertyUint32(js, v, i); double num = js2number(js, val); JS_FreeValue(js,val); sum += pow(num, 2); }
return sqrt(sum);
}
quat js2quat(JSContext *js,JSValue v)
{
vec4_union v4;
for (int i = 0; i < 4; i++)
{ JSValue val = JS_GetPropertyUint32(js, v,i); v4.e[i] = js2number(js, val); JS_FreeValue(js,val); }
return (quat){v4.x, v4.y, v4.z, v4.w};
}
JSValue vec42js(JSContext *js, vec4 v)
{
JSValue array = JS_NewArray(js);
JS_SetPropertyUint32(js, array,0,number2js(js,v.x));
JS_SetPropertyUint32(js, array,1,number2js(js,v.y));
JS_SetPropertyUint32(js, array,2,number2js(js,v.z));
JS_SetPropertyUint32(js, array,3,number2js(js,v.w));
return array;
}
vec2 *js2cpvec2arr(JSContext *js,JSValue v) {
int n = JS_ArrayLength(js,v);
vec2 *arr = malloc(sizeof(vec2) * n);
for (int i = 0; i < n; i++) {
JSValue ii = JS_GetPropertyUint32(js,v,i);
arr[i] = js2vec2(js,ii);
JS_FreeValue(js,ii);
}
return arr;
}
rect js2rect(JSContext *js,JSValue v) {
if (JS_IsNull(v)) return (rect){0,0,1,1};
rect rect;
JS_GETATOM(js,rect.x,v,x,number)
JS_GETATOM(js,rect.y,v,y,number)
JS_GETATOM(js,rect.w,v,width,number)
JS_GETATOM(js,rect.h,v,height,number)
float anchor_x, anchor_y;
JS_GETATOM(js, anchor_x, v, anchor_x, number)
JS_GETATOM(js, anchor_y, v, anchor_y, number)
rect.y -= anchor_y*rect.h;
rect.x -= anchor_x*rect.w;
return rect;
}
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;
}
lrtb js2lrtb(JSContext *js, JSValue v)
{
lrtb ret = {0};
JS_GETATOM(js,ret.l,v,l,number)
JS_GETATOM(js,ret.r,v,r,number)
JS_GETATOM(js,ret.b,v,b,number)
JS_GETATOM(js,ret.t,v,t,number)
return ret;
}
JSValue vec22js(JSContext *js,vec2 v)
{
JSValue array = JS_NewArray(js);
JS_SetPropertyUint32(js, array,0,number2js(js,v.x));
JS_SetPropertyUint32(js, array,1,number2js(js,v.y));
return array;
}
JSValue vecarr2js(JSContext *js,vec2 *points, int n) {
JSValue array = JS_NewArray(js);
for (int i = 0; i < n; i++)
JS_SetPropertyUint32(js, array,i,vec22js(js,points[i]));
return array;
}
JSValue rect2js(JSContext *js,rect rect) {
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "x", number2js(js, rect.x));
JS_SetPropertyStr(js, obj, "y", number2js(js, rect.y));
JS_SetPropertyStr(js, obj, "width", number2js(js, rect.w));
JS_SetPropertyStr(js, obj, "height", number2js(js, rect.h));
return obj;
}
float *rgba2floats(float *r, struct rgba c)
{
r[0] = (float)c.r / RGBA_MAX;
r[1] = (float)c.g / RGBA_MAX;
r[2] = (float)c.b / RGBA_MAX;
r[3] = (float)c.a / RGBA_MAX;
return r;
}
JSValue angle2js(JSContext *js, double angle) {
return number2js(js, angle);
}
double js2angle(JSContext *js, JSValue v) {
return js2number(js, v);
}
// Pixel format enum conversion using the new system
ENUM_MAPPING_TABLE(SDL_PixelFormat) = {
{SDL_PIXELFORMAT_UNKNOWN, "unknown"},
{SDL_PIXELFORMAT_INDEX1LSB, "index1lsb"},
{SDL_PIXELFORMAT_INDEX1MSB, "index1msb"},
{SDL_PIXELFORMAT_INDEX2LSB, "index2lsb"},
{SDL_PIXELFORMAT_INDEX2MSB, "index2msb"},
{SDL_PIXELFORMAT_INDEX4LSB, "index4lsb"},
{SDL_PIXELFORMAT_INDEX4MSB, "index4msb"},
{SDL_PIXELFORMAT_INDEX8, "index8"},
{SDL_PIXELFORMAT_RGB332, "rgb332"},
{SDL_PIXELFORMAT_XRGB4444, "xrgb4444"},
{SDL_PIXELFORMAT_XBGR4444, "xbgr4444"},
{SDL_PIXELFORMAT_XRGB1555, "xrgb1555"},
{SDL_PIXELFORMAT_XBGR1555, "xbgr1555"},
{SDL_PIXELFORMAT_ARGB4444, "argb4444"},
{SDL_PIXELFORMAT_RGBA4444, "rgba4444"},
{SDL_PIXELFORMAT_ABGR4444, "abgr4444"},
{SDL_PIXELFORMAT_BGRA4444, "bgra4444"},
{SDL_PIXELFORMAT_ARGB1555, "argb1555"},
{SDL_PIXELFORMAT_RGBA5551, "rgba5551"},
{SDL_PIXELFORMAT_ABGR1555, "abgr1555"},
{SDL_PIXELFORMAT_BGRA5551, "bgra5551"},
{SDL_PIXELFORMAT_RGB565, "rgb565"},
{SDL_PIXELFORMAT_BGR565, "bgr565"},
{SDL_PIXELFORMAT_RGB24, "rgb24"},
{SDL_PIXELFORMAT_BGR24, "bgr24"},
{SDL_PIXELFORMAT_XRGB8888, "xrgb8888"},
{SDL_PIXELFORMAT_RGBX8888, "rgbx8888"},
{SDL_PIXELFORMAT_XBGR8888, "xbgr8888"},
{SDL_PIXELFORMAT_BGRX8888, "bgrx8888"},
{SDL_PIXELFORMAT_ARGB8888, "argb8888"},
{SDL_PIXELFORMAT_RGBA8888, "rgba8888"},
{SDL_PIXELFORMAT_ABGR8888, "abgr8888"},
{SDL_PIXELFORMAT_BGRA8888, "bgra8888"},
{SDL_PIXELFORMAT_XRGB2101010, "xrgb2101010"},
{SDL_PIXELFORMAT_XBGR2101010, "xbgr2101010"},
{SDL_PIXELFORMAT_ARGB2101010, "argb2101010"},
{SDL_PIXELFORMAT_ABGR2101010, "abgr2101010"},
{SDL_PIXELFORMAT_RGB48, "rgb48"},
{SDL_PIXELFORMAT_BGR48, "bgr48"},
{SDL_PIXELFORMAT_RGBA64, "rgba64"},
{SDL_PIXELFORMAT_ARGB64, "argb64"},
{SDL_PIXELFORMAT_BGRA64, "bgra64"},
{SDL_PIXELFORMAT_ABGR64, "abgr64"},
{SDL_PIXELFORMAT_RGB48_FLOAT, "rgb48_float"},
{SDL_PIXELFORMAT_BGR48_FLOAT, "bgr48_float"},
{SDL_PIXELFORMAT_RGBA64_FLOAT, "rgba64_float"},
{SDL_PIXELFORMAT_ARGB64_FLOAT, "argb64_float"},
{SDL_PIXELFORMAT_BGRA64_FLOAT, "bgra64_float"},
{SDL_PIXELFORMAT_ABGR64_FLOAT, "abgr64_float"},
{SDL_PIXELFORMAT_RGB96_FLOAT, "rgb96_float"},
{SDL_PIXELFORMAT_BGR96_FLOAT, "bgr96_float"},
{SDL_PIXELFORMAT_RGBA128_FLOAT, "rgba128_float"},
{SDL_PIXELFORMAT_ARGB128_FLOAT, "argb128_float"},
{SDL_PIXELFORMAT_BGRA128_FLOAT, "bgra128_float"},
{SDL_PIXELFORMAT_ABGR128_FLOAT, "abgr128_float"},
{SDL_PIXELFORMAT_YV12, "yv12"},
{SDL_PIXELFORMAT_IYUV, "iyuv"},
{SDL_PIXELFORMAT_YUY2, "yuy2"},
{SDL_PIXELFORMAT_UYVY, "uyvy"},
{SDL_PIXELFORMAT_YVYU, "yvyu"},
{SDL_PIXELFORMAT_NV12, "nv12"},
{SDL_PIXELFORMAT_NV21, "nv21"},
{SDL_PIXELFORMAT_P010, "p010"},
{SDL_PIXELFORMAT_RGBA32, "rgba32"}
};
JS2ENUM(SDL_PixelFormat)

109
sdl.h Normal file
View File

@@ -0,0 +1,109 @@
#ifndef QJS_SDL_H
#define QJS_SDL_H
#include <SDL3/SDL.h>
#include "cell.h"
// Simple vector types to replace HandmadeMath dependency
typedef struct {
float x, y;
} vec2;
typedef struct {
float x, y, z;
} vec3;
typedef struct {
float x, y, z, w;
} vec4;
typedef struct {
float x, y, z, w;
} quat;
typedef union {
struct { float x, y, z, w; };
float e[4];
} vec4_union;
SDL_Window *js2SDL_Window(JSContext *js, JSValue v);
JSValue SDL_Window2js(JSContext *js, SDL_Window *w);
SDL_PixelFormat str2pixelformat(const char *str);
SDL_PixelFormat js2pixelformat(JSContext *js, JSValue v);
JSValue pixelformat2js(JSContext *js, SDL_PixelFormat format);
const char *pixelformat2str(SDL_PixelFormat format);
// New enum system functions
int js2SDL_PixelFormat(JSContext *js, JSValue v);
JSValue SDL_PixelFormat2js(JSContext *js, int enumval);
SDL_Colorspace str2colorspace(const char *str);
SDL_Colorspace js2colorspace(JSContext *js, JSValue v);
JSValue colorspace2js(JSContext *js, SDL_Colorspace colorspace);
const char *colorspace2str(SDL_Colorspace colorspace);
// SDL Scale Mode functions
SDL_ScaleMode js2SDL_ScaleMode(JSContext *js, JSValue v);
JSValue SDL_ScaleMode2js(JSContext *js, SDL_ScaleMode mode);
// Surface type
typedef struct SDL_Surface SDL_Surface;
SDL_Surface *js2SDL_Surface(JSContext *js, JSValue v);
JSValue SDL_Surface2js(JSContext *js, SDL_Surface *s);
struct lrtb {
float l;
float r;
float t;
float b;
};
typedef struct lrtb lrtb;
lrtb js2lrtb(JSContext *js, JSValue v);
JSValue lrtb2js(JSContext *js, lrtb r);
struct text_vert {
vec2 pos;
vec2 uv;
vec4 color;
};
typedef struct text_vert text_vert;
JSValue quads_to_mesh(JSContext *js, text_vert *argv);
typedef vec4 colorf;
typedef SDL_FRect rect;
typedef SDL_Rect irect;
// Common conversion functions used across modules
JSValue rect2js(JSContext *js, rect r);
JSValue vec22js(JSContext *js, vec2 v);
JSValue vec32js(JSContext *js, vec3 v);
JSValue vec42js(JSContext *js, vec4 v);
JSValue quat2js(JSContext *js, quat q);
JSValue color2js(JSContext *js, colorf c);
JSValue angle2js(JSContext *js, double a);
rect js2rect(JSContext *js, JSValue v);
vec2 js2vec2(JSContext *js, JSValue v);
vec3 js2vec3(JSContext *js, JSValue v);
vec4 js2vec4(JSContext *js, JSValue v);
quat js2quat(JSContext *js, JSValue v);
colorf js2color(JSContext *js, JSValue v);
double js2angle(JSContext *js, JSValue v);
#define RGBA_MAX 255
struct rgba {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
};
typedef struct rgba rgba;
float *rgba2floats(float *r, struct rgba c);
#endif

156
sensor.c Normal file
View File

@@ -0,0 +1,156 @@
#include "cell.h"
#include <SDL3/SDL.h>
// Sensor type enum to string
static const char *sensor_type_to_string(SDL_SensorType type) {
switch (type) {
case SDL_SENSOR_ACCEL: return "accel";
case SDL_SENSOR_GYRO: return "gyro";
case SDL_SENSOR_ACCEL_L: return "accel_l";
case SDL_SENSOR_GYRO_L: return "gyro_l";
case SDL_SENSOR_ACCEL_R: return "accel_r";
case SDL_SENSOR_GYRO_R: return "gyro_r";
case SDL_SENSOR_UNKNOWN: return "unknown";
default: return "invalid";
}
}
// SDL_Sensor class
void SDL_Sensor_free(JSRuntime *rt, SDL_Sensor *sensor) {
if (sensor) SDL_CloseSensor(sensor);
}
QJSCLASS(SDL_Sensor,)
// SDL_GetSensors() -> array of sensor IDs
JSC_CCALL(sensor_get_sensors,
int count = 0;
SDL_SensorID *sensors = SDL_GetSensors(&count);
if (!sensors) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewUint32(js, sensors[i]));
}
SDL_free(sensors);
return arr;
)
// SDL_GetSensorNameForID(id) -> string
JSC_CCALL(sensor_get_name_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
const char *name = SDL_GetSensorNameForID(id);
return name ? JS_NewString(js, name) : JS_NULL;
)
// SDL_GetSensorTypeForID(id) -> string
JSC_CCALL(sensor_get_type_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_SensorType type = SDL_GetSensorTypeForID(id);
return JS_NewString(js, sensor_type_to_string(type));
)
// SDL_GetSensorNonPortableTypeForID(id) -> number
JSC_CCALL(sensor_get_non_portable_type_for_id,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
return JS_NewInt32(js, SDL_GetSensorNonPortableTypeForID(id));
)
// SDL_OpenSensor(id) -> Sensor object
JSC_CCALL(sensor_open,
uint32_t id;
JS_ToUint32(js, &id, argv[0]);
SDL_Sensor *sensor = SDL_OpenSensor(id);
if (!sensor) return JS_NULL;
return SDL_Sensor2js(js, sensor);
)
// Sensor methods
JSC_CCALL(sensor_get_name,
SDL_Sensor *sensor = js2SDL_Sensor(js, self);
const char *name = SDL_GetSensorName(sensor);
return name ? JS_NewString(js, name) : JS_NULL;
)
JSC_CCALL(sensor_get_type,
SDL_Sensor *sensor = js2SDL_Sensor(js, self);
SDL_SensorType type = SDL_GetSensorType(sensor);
return JS_NewString(js, sensor_type_to_string(type));
)
JSC_CCALL(sensor_get_non_portable_type,
SDL_Sensor *sensor = js2SDL_Sensor(js, self);
return JS_NewInt32(js, SDL_GetSensorNonPortableType(sensor));
)
JSC_CCALL(sensor_get_id,
SDL_Sensor *sensor = js2SDL_Sensor(js, self);
return JS_NewUint32(js, SDL_GetSensorID(sensor));
)
JSC_CCALL(sensor_get_data,
SDL_Sensor *sensor = js2SDL_Sensor(js, self);
int num_values = 3;
if (argc > 0) JS_ToInt32(js, &num_values, argv[0]);
float *data = malloc(num_values * sizeof(float));
if (!data) return JS_ThrowOutOfMemory(js);
if (!SDL_GetSensorData(sensor, data, num_values)) {
free(data);
return JS_NULL;
}
JSValue arr = JS_NewArray(js);
for (int i = 0; i < num_values; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewFloat64(js, data[i]));
}
free(data);
return arr;
)
JSC_CCALL(sensor_close,
SDL_Sensor *sensor = js2SDL_Sensor(js, self);
SDL_CloseSensor(sensor);
return JS_NULL;
)
// SDL_UpdateSensors()
JSC_CCALL(sensor_update,
SDL_UpdateSensors();
return JS_NULL;
)
static const JSCFunctionListEntry js_SDL_Sensor_funcs[] = {
MIST_FUNC_DEF(sensor, get_name, 0),
MIST_FUNC_DEF(sensor, get_type, 0),
MIST_FUNC_DEF(sensor, get_non_portable_type, 0),
MIST_FUNC_DEF(sensor, get_id, 0),
MIST_FUNC_DEF(sensor, get_data, 1),
MIST_FUNC_DEF(sensor, close, 0),
};
static const JSCFunctionListEntry js_sensor_funcs[] = {
MIST_FUNC_DEF(sensor, get_sensors, 0),
MIST_FUNC_DEF(sensor, get_name_for_id, 1),
MIST_FUNC_DEF(sensor, get_type_for_id, 1),
MIST_FUNC_DEF(sensor, get_non_portable_type_for_id, 1),
MIST_FUNC_DEF(sensor, open, 1),
MIST_FUNC_DEF(sensor, update, 0),
};
CELL_USE_INIT(
SDL_Init(SDL_INIT_SENSOR);
QJSCLASSPREP_FUNCS(SDL_Sensor);
JSValue ret = JS_NewObject(js);
JS_SetPropertyFunctionList(js, ret, js_sensor_funcs, countof(js_sensor_funcs));
// Export standard gravity constant
JS_SetPropertyStr(js, ret, "STANDARD_GRAVITY", JS_NewFloat64(js, SDL_STANDARD_GRAVITY));
return ret;
)

1109
surface.c Normal file

File diff suppressed because it is too large Load Diff

73
touch.c Normal file
View File

@@ -0,0 +1,73 @@
#include "cell.h"
#include <SDL3/SDL.h>
// SDL_GetTouchDevices() -> array of touch device IDs
JSC_CCALL(touch_get_devices,
int count = 0;
SDL_TouchID *devices = SDL_GetTouchDevices(&count);
if (!devices) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewInt64(js, devices[i]));
}
SDL_free(devices);
return arr;
)
// SDL_GetTouchDeviceName(touchID) -> string
JSC_CCALL(touch_get_device_name,
int64_t touchID;
JS_ToInt64(js, &touchID, argv[0]);
const char *name = SDL_GetTouchDeviceName((SDL_TouchID)touchID);
return name ? JS_NewString(js, name) : JS_NULL;
)
// SDL_GetTouchDeviceType(touchID) -> string
static const char *touch_device_type_to_string(SDL_TouchDeviceType type) {
switch (type) {
case SDL_TOUCH_DEVICE_DIRECT: return "direct";
case SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE: return "indirect_absolute";
case SDL_TOUCH_DEVICE_INDIRECT_RELATIVE: return "indirect_relative";
default: return "invalid";
}
}
JSC_CCALL(touch_get_device_type,
int64_t touchID;
JS_ToInt64(js, &touchID, argv[0]);
SDL_TouchDeviceType type = SDL_GetTouchDeviceType((SDL_TouchID)touchID);
return JS_NewString(js, touch_device_type_to_string(type));
)
// SDL_GetTouchFingers(touchID) -> array of finger objects
JSC_CCALL(touch_get_fingers,
int64_t touchID;
JS_ToInt64(js, &touchID, argv[0]);
int count = 0;
SDL_Finger **fingers = SDL_GetTouchFingers((SDL_TouchID)touchID, &count);
if (!fingers) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
SDL_Finger *f = fingers[i];
JSValue finger = JS_NewObject(js);
JS_SetPropertyStr(js, finger, "id", JS_NewInt64(js, f->id));
JS_SetPropertyStr(js, finger, "x", JS_NewFloat64(js, f->x));
JS_SetPropertyStr(js, finger, "y", JS_NewFloat64(js, f->y));
JS_SetPropertyStr(js, finger, "pressure", JS_NewFloat64(js, f->pressure));
JS_SetPropertyUint32(js, arr, i, finger);
}
SDL_free(fingers);
return arr;
)
static const JSCFunctionListEntry js_touch_funcs[] = {
MIST_FUNC_DEF(touch, get_devices, 0),
MIST_FUNC_DEF(touch, get_device_name, 1),
MIST_FUNC_DEF(touch, get_device_type, 1),
MIST_FUNC_DEF(touch, get_fingers, 1),
};
CELL_USE_FUNCS(js_touch_funcs)

255
tray.c Normal file
View File

@@ -0,0 +1,255 @@
#include "cell.h"
#include <SDL3/SDL.h>
// Forward declaration for surface function
extern SDL_Surface *js2SDL_Surface(JSContext *js, JSValue val);
// SDL_Tray class
void SDL_Tray_free(JSRuntime *rt, SDL_Tray *tray) {
if (tray) SDL_DestroyTray(tray);
}
QJSCLASS(SDL_Tray,)
// SDL_TrayMenu class (not freed separately - destroyed with tray)
void SDL_TrayMenu_free(JSRuntime *rt, SDL_TrayMenu *menu) {
// Menus are destroyed with their parent tray
}
QJSCLASS(SDL_TrayMenu,)
// SDL_TrayEntry class (not freed separately - destroyed with tray)
void SDL_TrayEntry_free(JSRuntime *rt, SDL_TrayEntry *entry) {
// Entries are destroyed with their parent tray
}
QJSCLASS(SDL_TrayEntry,)
// SDL_CreateTray(icon, tooltip) -> Tray object
JSC_CCALL(tray_create,
SDL_Surface *icon = NULL;
const char *tooltip = NULL;
if (argc > 0 && !JS_IsNull(argv[0])) {
icon = js2SDL_Surface(js, argv[0]);
}
if (argc > 1 && !JS_IsNull(argv[1])) {
tooltip = JS_ToCString(js, argv[1]);
}
SDL_Tray *tray = SDL_CreateTray(icon, tooltip);
if (tooltip) JS_FreeCString(js, tooltip);
if (!tray) return JS_NULL;
return SDL_Tray2js(js, tray);
)
// Tray instance methods
JSC_CCALL(tray_set_icon,
SDL_Tray *tray = js2SDL_Tray(js, self);
SDL_Surface *icon = NULL;
if (argc > 0 && !JS_IsNull(argv[0])) {
icon = js2SDL_Surface(js, argv[0]);
}
SDL_SetTrayIcon(tray, icon);
return JS_NULL;
)
JSC_CCALL(tray_set_tooltip,
SDL_Tray *tray = js2SDL_Tray(js, self);
const char *tooltip = NULL;
if (argc > 0 && !JS_IsNull(argv[0])) {
tooltip = JS_ToCString(js, argv[0]);
}
SDL_SetTrayTooltip(tray, tooltip);
if (tooltip) JS_FreeCString(js, tooltip);
return JS_NULL;
)
JSC_CCALL(tray_create_menu,
SDL_Tray *tray = js2SDL_Tray(js, self);
SDL_TrayMenu *menu = SDL_CreateTrayMenu(tray);
if (!menu) return JS_NULL;
return SDL_TrayMenu2js(js, menu);
)
JSC_CCALL(tray_get_menu,
SDL_Tray *tray = js2SDL_Tray(js, self);
SDL_TrayMenu *menu = SDL_GetTrayMenu(tray);
if (!menu) return JS_NULL;
return SDL_TrayMenu2js(js, menu);
)
JSC_CCALL(tray_destroy,
SDL_Tray *tray = js2SDL_Tray(js, self);
SDL_DestroyTray(tray);
return JS_NULL;
)
// TrayMenu instance methods
JSC_CCALL(traymenu_get_entries,
SDL_TrayMenu *menu = js2SDL_TrayMenu(js, self);
int count = 0;
const SDL_TrayEntry **entries = SDL_GetTrayEntries(menu, &count);
if (!entries) return JS_NewArray(js);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
JS_SetPropertyUint32(js, arr, i, SDL_TrayEntry2js(js, (SDL_TrayEntry*)entries[i]));
}
return arr;
)
JSC_CCALL(traymenu_insert_entry,
SDL_TrayMenu *menu = js2SDL_TrayMenu(js, self);
int pos = -1;
const char *label = NULL;
uint32_t flags = SDL_TRAYENTRY_BUTTON;
if (argc > 0) JS_ToInt32(js, &pos, argv[0]);
if (argc > 1 && !JS_IsNull(argv[1])) {
label = JS_ToCString(js, argv[1]);
}
if (argc > 2) JS_ToUint32(js, &flags, argv[2]);
SDL_TrayEntry *entry = SDL_InsertTrayEntryAt(menu, pos, label, flags);
if (label) JS_FreeCString(js, label);
if (!entry) return JS_NULL;
return SDL_TrayEntry2js(js, entry);
)
JSC_CCALL(traymenu_get_parent_tray,
SDL_TrayMenu *menu = js2SDL_TrayMenu(js, self);
SDL_Tray *tray = SDL_GetTrayMenuParentTray(menu);
if (!tray) return JS_NULL;
return SDL_Tray2js(js, tray);
)
JSC_CCALL(traymenu_get_parent_entry,
SDL_TrayMenu *menu = js2SDL_TrayMenu(js, self);
SDL_TrayEntry *entry = SDL_GetTrayMenuParentEntry(menu);
if (!entry) return JS_NULL;
return SDL_TrayEntry2js(js, entry);
)
// TrayEntry instance methods
JSC_CCALL(trayentry_create_submenu,
SDL_TrayEntry *entry = js2SDL_TrayEntry(js, self);
SDL_TrayMenu *menu = SDL_CreateTraySubmenu(entry);
if (!menu) return JS_NULL;
return SDL_TrayMenu2js(js, menu);
)
JSC_CCALL(trayentry_get_submenu,
SDL_TrayEntry *entry = js2SDL_TrayEntry(js, self);
SDL_TrayMenu *menu = SDL_GetTraySubmenu(entry);
if (!menu) return JS_NULL;
return SDL_TrayMenu2js(js, menu);
)
JSC_CCALL(trayentry_set_label,
SDL_TrayEntry *entry = js2SDL_TrayEntry(js, self);
const char *label = JS_ToCString(js, argv[0]);
if (!label) return JS_EXCEPTION;
SDL_SetTrayEntryLabel(entry, label);
JS_FreeCString(js, label);
return JS_NULL;
)
JSC_CCALL(trayentry_get_label,
SDL_TrayEntry *entry = js2SDL_TrayEntry(js, self);
const char *label = SDL_GetTrayEntryLabel(entry);
return label ? JS_NewString(js, label) : JS_NULL;
)
JSC_CCALL(trayentry_set_checked,
SDL_TrayEntry *entry = js2SDL_TrayEntry(js, self);
bool checked = JS_ToBool(js, argv[0]);
SDL_SetTrayEntryChecked(entry, checked);
return JS_NULL;
)
JSC_CCALL(trayentry_get_checked,
SDL_TrayEntry *entry = js2SDL_TrayEntry(js, self);
return JS_NewBool(js, SDL_GetTrayEntryChecked(entry));
)
JSC_CCALL(trayentry_set_enabled,
SDL_TrayEntry *entry = js2SDL_TrayEntry(js, self);
bool enabled = JS_ToBool(js, argv[0]);
SDL_SetTrayEntryEnabled(entry, enabled);
return JS_NULL;
)
JSC_CCALL(trayentry_get_enabled,
SDL_TrayEntry *entry = js2SDL_TrayEntry(js, self);
return JS_NewBool(js, SDL_GetTrayEntryEnabled(entry));
)
JSC_CCALL(trayentry_get_parent,
SDL_TrayEntry *entry = js2SDL_TrayEntry(js, self);
SDL_TrayMenu *menu = SDL_GetTrayEntryParent(entry);
if (!menu) return JS_NULL;
return SDL_TrayMenu2js(js, menu);
)
JSC_CCALL(trayentry_remove,
SDL_TrayEntry *entry = js2SDL_TrayEntry(js, self);
SDL_RemoveTrayEntry(entry);
return JS_NULL;
)
static const JSCFunctionListEntry js_SDL_Tray_funcs[] = {
MIST_FUNC_DEF(tray, set_icon, 1),
MIST_FUNC_DEF(tray, set_tooltip, 1),
MIST_FUNC_DEF(tray, create_menu, 0),
MIST_FUNC_DEF(tray, get_menu, 0),
MIST_FUNC_DEF(tray, destroy, 0),
};
static const JSCFunctionListEntry js_SDL_TrayMenu_funcs[] = {
MIST_FUNC_DEF(traymenu, get_entries, 0),
MIST_FUNC_DEF(traymenu, insert_entry, 3),
MIST_FUNC_DEF(traymenu, get_parent_tray, 0),
MIST_FUNC_DEF(traymenu, get_parent_entry, 0),
};
static const JSCFunctionListEntry js_SDL_TrayEntry_funcs[] = {
MIST_FUNC_DEF(trayentry, create_submenu, 0),
MIST_FUNC_DEF(trayentry, get_submenu, 0),
MIST_FUNC_DEF(trayentry, set_label, 1),
MIST_FUNC_DEF(trayentry, get_label, 0),
MIST_FUNC_DEF(trayentry, set_checked, 1),
MIST_FUNC_DEF(trayentry, get_checked, 0),
MIST_FUNC_DEF(trayentry, set_enabled, 1),
MIST_FUNC_DEF(trayentry, get_enabled, 0),
MIST_FUNC_DEF(trayentry, get_parent, 0),
MIST_FUNC_DEF(trayentry, remove, 0),
};
static const JSCFunctionListEntry js_tray_funcs[] = {
MIST_FUNC_DEF(tray, create, 2),
};
SDL_Surface *js2SDL_Surface(JSContext *js, JSValue val);
CELL_USE_INIT(
QJSCLASSPREP_FUNCS(SDL_Tray);
QJSCLASSPREP_FUNCS(SDL_TrayMenu);
QJSCLASSPREP_FUNCS(SDL_TrayEntry);
JSValue ret = JS_NewObject(js);
JS_SetPropertyFunctionList(js, ret, js_tray_funcs, countof(js_tray_funcs));
// Export entry flags
JS_SetPropertyStr(js, ret, "BUTTON", JS_NewUint32(js, SDL_TRAYENTRY_BUTTON));
JS_SetPropertyStr(js, ret, "CHECKBOX", JS_NewUint32(js, SDL_TRAYENTRY_CHECKBOX));
JS_SetPropertyStr(js, ret, "SUBMENU", JS_NewUint32(js, SDL_TRAYENTRY_SUBMENU));
JS_SetPropertyStr(js, ret, "DISABLED", JS_NewUint32(js, SDL_TRAYENTRY_DISABLED));
JS_SetPropertyStr(js, ret, "CHECKED", JS_NewUint32(js, SDL_TRAYENTRY_CHECKED));
return ret;
)

759
video.c Normal file
View File

@@ -0,0 +1,759 @@
#include "cell.h"
#include <SDL3/SDL.h>
#include <SDL3/SDL_gpu.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_properties.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "sdl.h"
// SDL Window free function
void SDL_Window_free(JSRuntime *rt, SDL_Window *w)
{
SDL_DestroyWindow(w);
}
QJSCLASS(SDL_Window,)
void SDL_Cursor_free(JSRuntime *rt, SDL_Cursor *c)
{
SDL_DestroyCursor(c);
}
QJSCLASS(SDL_Cursor,)
// Forward declarations for blend mode helpers
static JSValue blendmode2js(JSContext *js, SDL_BlendMode mode);
static SDL_BlendMode js2blendmode(JSContext *js, JSValue v);
// Window constructor function
static JSValue js_SDL_Window_constructor(JSContext *js, JSValueConst new_target, int argc, JSValueConst *argv)
{
if (argc < 1 || !JS_IsObject(argv[0]))
return JS_ThrowTypeError(js, "Window constructor requires an object argument");
JSValue opts = argv[0];
// Get basic properties (defaults are handled in JavaScript)
const char *title = NULL;
JSValue title_val = JS_GetPropertyStr(js, opts, "title");
if (!JS_IsNull(title_val)) {
title = JS_ToCString(js, title_val);
}
JS_FreeValue(js, title_val);
if (!title) {
return JS_ThrowTypeError(js, "Window title is required");
}
int width = 640;
JSValue width_val = JS_GetPropertyStr(js, opts, "width");
if (!JS_IsNull(width_val)) {
width = js2number(js, width_val);
}
JS_FreeValue(js, width_val);
int height = 480;
JSValue height_val = JS_GetPropertyStr(js, opts, "height");
if (!JS_IsNull(height_val)) {
height = js2number(js, height_val);
}
JS_FreeValue(js, height_val);
// Create SDL properties object
SDL_PropertiesID props = SDL_CreateProperties();
// Always set basic properties
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, height);
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title);
// Handle window position
JSValue x_val = JS_GetPropertyStr(js, opts, "x");
if (!JS_IsNull(x_val)) {
if (JS_IsString(x_val)) {
const char *pos = JS_ToCString(js, x_val);
if (strcmp(pos, "centered") == 0)
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED);
else
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_UNDEFINED);
JS_FreeCString(js, pos);
} else {
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, js2number(js, x_val));
}
}
JS_FreeValue(js, x_val);
JSValue y_val = JS_GetPropertyStr(js, opts, "y");
if (!JS_IsNull(y_val)) {
if (JS_IsString(y_val)) {
const char *pos = JS_ToCString(js, y_val);
if (strcmp(pos, "centered") == 0)
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED);
else
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_UNDEFINED);
JS_FreeCString(js, pos);
} else {
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, js2number(js, y_val));
}
}
JS_FreeValue(js, y_val);
// Helper function to check and set boolean properties
#define SET_BOOL_PROP(js_name, sdl_prop) do { \
JSValue val = JS_GetPropertyStr(js, opts, js_name); \
if (!JS_IsNull(val)) { \
SDL_SetBooleanProperty(props, sdl_prop, JS_ToBool(js, val)); \
} \
JS_FreeValue(js, val); \
} while(0)
// Set all boolean properties directly on the SDL properties object
SET_BOOL_PROP("resizable", SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN);
SET_BOOL_PROP("fullscreen", SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN);
SET_BOOL_PROP("hidden", SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN);
SET_BOOL_PROP("borderless", SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN);
SET_BOOL_PROP("alwaysOnTop", SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN);
SET_BOOL_PROP("minimized", SDL_PROP_WINDOW_CREATE_MINIMIZED_BOOLEAN);
SET_BOOL_PROP("maximized", SDL_PROP_WINDOW_CREATE_MAXIMIZED_BOOLEAN);
SET_BOOL_PROP("mouseGrabbed", SDL_PROP_WINDOW_CREATE_MOUSE_GRABBED_BOOLEAN);
SET_BOOL_PROP("highPixelDensity", SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN);
SET_BOOL_PROP("transparent", SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN);
SET_BOOL_PROP("utility", SDL_PROP_WINDOW_CREATE_UTILITY_BOOLEAN);
SET_BOOL_PROP("tooltip", SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN);
SET_BOOL_PROP("popupMenu", SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN);
SET_BOOL_PROP("opengl", SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN);
SET_BOOL_PROP("vulkan", SDL_PROP_WINDOW_CREATE_VULKAN_BOOLEAN);
SET_BOOL_PROP("metal", SDL_PROP_WINDOW_CREATE_METAL_BOOLEAN);
SET_BOOL_PROP("modal", SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN);
SET_BOOL_PROP("externalGraphicsContext", SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN);
// Handle focusable (inverse logic)
JSValue focusable_val = JS_GetPropertyStr(js, opts, "focusable");
if (!JS_IsNull(focusable_val)) {
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN, JS_ToBool(js, focusable_val));
}
JS_FreeValue(js, focusable_val);
// Handle notFocusable (for backwards compatibility)
JSValue not_focusable_val = JS_GetPropertyStr(js, opts, "notFocusable");
if (!JS_IsNull(not_focusable_val)) {
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN, !JS_ToBool(js, not_focusable_val));
}
JS_FreeValue(js, not_focusable_val);
#undef SET_BOOL_PROP
// Handle parent window
JSValue parent_val = JS_GetPropertyStr(js, opts, "parent");
if (!JS_IsNull(parent_val)) {
SDL_Window *parent = js2SDL_Window(js, parent_val);
if (parent) {
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, parent);
}
}
JS_FreeValue(js, parent_val);
// Create window with properties
SDL_Window *window = SDL_CreateWindowWithProperties(props);
SDL_DestroyProperties(props);
// Always free the title string since we allocated it
if (title) {
JS_FreeCString(js, title);
}
if (!window) {
return JS_ThrowReferenceError(js, "Failed to create window: %s", SDL_GetError());
}
// Create the window JS object
JSValue window_obj = SDL_Window2js(js, window);
// Set additional properties that can't be set during creation
// These will be applied through the property setters
JSValue opacity_val = JS_GetPropertyStr(js, opts, "opacity");
if (!JS_IsNull(opacity_val)) {
JS_SetPropertyStr(js, window_obj, "opacity", opacity_val);
}
JSValue min_size_val = JS_GetPropertyStr(js, opts, "minimumSize");
if (!JS_IsNull(min_size_val)) {
JS_SetPropertyStr(js, window_obj, "minimumSize", min_size_val);
}
JSValue max_size_val = JS_GetPropertyStr(js, opts, "maximumSize");
if (!JS_IsNull(max_size_val)) {
JS_SetPropertyStr(js, window_obj, "maximumSize", max_size_val);
}
JSValue pos_val = JS_GetPropertyStr(js, opts, "position");
if (!JS_IsNull(pos_val)) {
JS_SetPropertyStr(js, window_obj, "position", pos_val);
}
// Handle text input
JSValue text_input = JS_GetPropertyStr(js, opts, "textInput");
if (JS_ToBool(js, text_input)) {
// SDL_StartTextInput(window);
}
JS_FreeValue(js, text_input);
printf("created window %p\n", window);
return window_obj;
}
JSC_CCALL(SDL_Window_fullscreen,
SDL_SetWindowFullscreen(js2SDL_Window(js,self), SDL_WINDOW_FULLSCREEN)
)
JSValue js_SDL_Window_keyboard_shown(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_Window *window = js2SDL_Window(js,self);
return JS_NewBool(js,SDL_ScreenKeyboardShown(window));
}
JSValue js_window_theme(JSContext *js, JSValue self, int argc, JSValue *argv)
{
return JS_NULL;
}
JSValue js_window_safe_area(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_Rect r;
SDL_GetWindowSafeArea(w, &r);
rect newr;
SDL_RectToFRect(&r, &newr);
return rect2js(js,newr);
}
JSValue js_window_bordered(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowBordered(w, JS_ToBool(js,argv[0]));
return JS_NULL;
}
JSValue js_window_get_title(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
const char *title = SDL_GetWindowTitle(w);
return JS_NewString(js,title);
}
JSValue js_window_set_title(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
const char *title = JS_ToCString(js,val);
SDL_SetWindowTitle(w,title);
JS_FreeCString(js,title);
return JS_NULL;
}
JSValue js_window_get_size(JSContext *js, JSValue self)
{
SDL_Window *win = js2SDL_Window(js,self);
int w, h;
SDL_GetWindowSize(win, &w, &h);
return vec22js(js, (vec2){w,h});
}
JSValue js_window_set_size(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
vec2 size = js2vec2(js,val);
SDL_SetWindowSize(w,size.x,size.y);
return JS_NULL;
}
JSValue js_window_set_icon(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_Surface *s = js2SDL_Surface(js,argv[0]);
if (!SDL_SetWindowIcon(w,s))
return JS_ThrowReferenceError(js, "could not set window icon: %s", SDL_GetError());
return JS_NULL;
}
// Position getter/setter
JSValue js_window_get_position(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
int x, y;
SDL_GetWindowPosition(w, &x, &y);
return vec22js(js, (vec2){x,y});
}
JSValue js_window_set_position(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
vec2 pos = js2vec2(js,val);
SDL_SetWindowPosition(w,pos.x,pos.y);
return JS_NULL;
}
// Mouse grab getter/setter
JSValue js_window_get_mouseGrab(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
return JS_NewBool(js, SDL_GetWindowMouseGrab(w));
}
JSValue js_window_set_mouseGrab(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowMouseGrab(w, JS_ToBool(js,val));
return JS_NULL;
}
// Keyboard grab getter/setter
JSValue js_window_get_keyboardGrab(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
return JS_NewBool(js, SDL_GetWindowKeyboardGrab(w));
}
JSValue js_window_set_keyboardGrab(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowKeyboardGrab(w, JS_ToBool(js,val));
return JS_NULL;
}
// Opacity getter/setter
JSValue js_window_get_opacity(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
return number2js(js, SDL_GetWindowOpacity(w));
}
JSValue js_window_set_opacity(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
float opacity = js2number(js,val);
SDL_SetWindowOpacity(w, opacity);
return JS_NULL;
}
// Minimum size getter/setter
JSValue js_window_get_minimumSize(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
int width, height;
SDL_GetWindowMinimumSize(w, &width, &height);
return vec22js(js, (vec2){width,height});
}
JSValue js_window_set_minimumSize(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
vec2 size = js2vec2(js,val);
SDL_SetWindowMinimumSize(w,size.x,size.y);
return JS_NULL;
}
// Maximum size getter/setter
JSValue js_window_get_maximumSize(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
int width, height;
SDL_GetWindowMaximumSize(w, &width, &height);
return vec22js(js, (vec2){width,height});
}
JSValue js_window_set_maximumSize(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
vec2 size = js2vec2(js,val);
SDL_SetWindowMaximumSize(w,size.x,size.y);
return JS_NULL;
}
// Resizable setter (read from flags)
JSValue js_window_get_resizable(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
return JS_NewBool(js, flags & SDL_WINDOW_RESIZABLE);
}
JSValue js_window_set_resizable(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowResizable(w, JS_ToBool(js,val));
return JS_NULL;
}
// Bordered getter/setter
JSValue js_window_get_bordered(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
return JS_NewBool(js, !(flags & SDL_WINDOW_BORDERLESS));
}
JSValue js_window_set_bordered(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowBordered(w, JS_ToBool(js,val));
return JS_NULL;
}
// Always on top getter/setter
JSValue js_window_get_alwaysOnTop(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
return JS_NewBool(js, flags & SDL_WINDOW_ALWAYS_ON_TOP);
}
JSValue js_window_set_alwaysOnTop(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowAlwaysOnTop(w, JS_ToBool(js,val));
return JS_NULL;
}
// Fullscreen getter/setter
JSValue js_window_get_fullscreen(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
return JS_NewBool(js, flags & SDL_WINDOW_FULLSCREEN);
}
JSValue js_window_set_fullscreen(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowFullscreen(w, JS_ToBool(js,val));
return JS_NULL;
}
// Focusable setter
JSValue js_window_get_focusable(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
return JS_NewBool(js, !(flags & SDL_WINDOW_NOT_FOCUSABLE));
}
JSValue js_window_set_focusable(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowFocusable(w, JS_ToBool(js,val));
return JS_NULL;
}
// Modal setter
JSValue js_window_get_modal(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
return JS_NewBool(js, flags & SDL_WINDOW_MODAL);
}
JSValue js_window_set_modal(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowModal(w, JS_ToBool(js,val));
return JS_NULL;
}
// Hidden/visible state
JSValue js_window_get_visible(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
return JS_NewBool(js, !(flags & SDL_WINDOW_HIDDEN));
}
JSValue js_window_set_visible(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
if (JS_ToBool(js,val))
SDL_ShowWindow(w);
else
SDL_HideWindow(w);
return JS_NULL;
}
// Minimized state
JSValue js_window_get_minimized(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
return JS_NewBool(js, flags & SDL_WINDOW_MINIMIZED);
}
JSValue js_window_set_minimized(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
if (JS_ToBool(js,val))
SDL_MinimizeWindow(w);
else
SDL_RestoreWindow(w);
return JS_NULL;
}
// Maximized state
JSValue js_window_get_maximized(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
return JS_NewBool(js, flags & SDL_WINDOW_MAXIMIZED);
}
JSValue js_window_set_maximized(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
if (JS_ToBool(js,val))
SDL_MaximizeWindow(w);
else
SDL_RestoreWindow(w);
return JS_NULL;
}
// Other window methods
JSValue js_window_raise(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_RaiseWindow(w);
return JS_NULL;
}
JSValue js_window_restore(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_RestoreWindow(w);
return JS_NULL;
}
JSValue js_window_flash(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_FlashOperation op = SDL_FLASH_BRIEFLY;
if (argc > 0 && JS_IsString(argv[0])) {
const char *operation = JS_ToCString(js,argv[0]);
if (strcmp(operation, "cancel") == 0) op = SDL_FLASH_CANCEL;
else if (strcmp(operation, "briefly") == 0) op = SDL_FLASH_BRIEFLY;
else if (strcmp(operation, "until_focused") == 0) op = SDL_FLASH_UNTIL_FOCUSED;
JS_FreeCString(js,operation);
}
SDL_FlashWindow(w, op);
return JS_NULL;
}
JSValue js_window_destroy(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_DestroyWindow(w);
return JS_NULL;
}
JSValue js_window_get_id(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
return number2js(js, SDL_GetWindowID(w));
}
JSValue js_window_get_parent(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_Window *parent = SDL_GetWindowParent(w);
if (!parent) return JS_NULL;
return SDL_Window2js(js, parent);
}
JSValue js_window_set_parent(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_Window *parent = NULL;
if (!JS_IsNull(val))
parent = js2SDL_Window(js,val);
SDL_SetWindowParent(w, parent);
return JS_NULL;
}
JSValue js_window_get_pixelDensity(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
return number2js(js, SDL_GetWindowPixelDensity(w));
}
JSValue js_window_get_displayScale(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
return number2js(js, SDL_GetWindowDisplayScale(w));
}
JSValue js_window_get_sizeInPixels(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
int width, height;
SDL_GetWindowSizeInPixels(w, &width, &height);
return vec22js(js, (vec2){width,height});
}
// Surface related
JSValue js_window_get_surface(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_Surface *surf = SDL_GetWindowSurface(w);
if (!surf) return JS_NULL;
return SDL_Surface2js(js, surf);
}
JSValue js_window_updateSurface(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
if (!SDL_UpdateWindowSurface(w))
return JS_ThrowReferenceError(js, "Failed to update window surface: %s", SDL_GetError());
return JS_NULL;
}
JSValue js_window_updateSurfaceRects(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
if (!JS_IsArray(js, argv[0]))
return JS_ThrowTypeError(js, "Expected array of rectangles");
int len = JS_ArrayLength(js, argv[0]);
SDL_Rect rects[len];
for (int i = 0; i < len; i++) {
JSValue val = JS_GetPropertyUint32(js, argv[0], i);
rect r = js2rect(js, val);
rects[i] = (SDL_Rect){r.x, r.y, r.w, r.h};
JS_FreeValue(js, val);
}
if (!SDL_UpdateWindowSurfaceRects(w, rects, len))
return JS_ThrowReferenceError(js, "Failed to update window surface rects: %s", SDL_GetError());
return JS_NULL;
}
JSValue js_window_get_flags(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
JSValue ret = JS_NewObject(js);
JS_SetPropertyStr(js, ret, "fullscreen", JS_NewBool(js, flags & SDL_WINDOW_FULLSCREEN));
JS_SetPropertyStr(js, ret, "opengl", JS_NewBool(js, flags & SDL_WINDOW_OPENGL));
JS_SetPropertyStr(js, ret, "occluded", JS_NewBool(js, flags & SDL_WINDOW_OCCLUDED));
JS_SetPropertyStr(js, ret, "hidden", JS_NewBool(js, flags & SDL_WINDOW_HIDDEN));
JS_SetPropertyStr(js, ret, "borderless", JS_NewBool(js, flags & SDL_WINDOW_BORDERLESS));
JS_SetPropertyStr(js, ret, "resizable", JS_NewBool(js, flags & SDL_WINDOW_RESIZABLE));
JS_SetPropertyStr(js, ret, "minimized", JS_NewBool(js, flags & SDL_WINDOW_MINIMIZED));
JS_SetPropertyStr(js, ret, "maximized", JS_NewBool(js, flags & SDL_WINDOW_MAXIMIZED));
JS_SetPropertyStr(js, ret, "mouseGrabbed", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_GRABBED));
JS_SetPropertyStr(js, ret, "inputFocus", JS_NewBool(js, flags & SDL_WINDOW_INPUT_FOCUS));
JS_SetPropertyStr(js, ret, "mouseFocus", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_FOCUS));
JS_SetPropertyStr(js, ret, "external", JS_NewBool(js, flags & SDL_WINDOW_EXTERNAL));
JS_SetPropertyStr(js, ret, "modal", JS_NewBool(js, flags & SDL_WINDOW_MODAL));
JS_SetPropertyStr(js, ret, "highPixelDensity", JS_NewBool(js, flags & SDL_WINDOW_HIGH_PIXEL_DENSITY));
JS_SetPropertyStr(js, ret, "mouseCapture", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_CAPTURE));
JS_SetPropertyStr(js, ret, "mouseRelativeMode", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_RELATIVE_MODE));
JS_SetPropertyStr(js, ret, "alwaysOnTop", JS_NewBool(js, flags & SDL_WINDOW_ALWAYS_ON_TOP));
JS_SetPropertyStr(js, ret, "utility", JS_NewBool(js, flags & SDL_WINDOW_UTILITY));
JS_SetPropertyStr(js, ret, "tooltip", JS_NewBool(js, flags & SDL_WINDOW_TOOLTIP));
JS_SetPropertyStr(js, ret, "popupMenu", JS_NewBool(js, flags & SDL_WINDOW_POPUP_MENU));
JS_SetPropertyStr(js, ret, "keyboardGrabbed", JS_NewBool(js, flags & SDL_WINDOW_KEYBOARD_GRABBED));
JS_SetPropertyStr(js, ret, "vulkan", JS_NewBool(js, flags & SDL_WINDOW_VULKAN));
JS_SetPropertyStr(js, ret, "metal", JS_NewBool(js, flags & SDL_WINDOW_METAL));
JS_SetPropertyStr(js, ret, "transparent", JS_NewBool(js, flags & SDL_WINDOW_TRANSPARENT));
JS_SetPropertyStr(js, ret, "notFocusable", JS_NewBool(js, flags & SDL_WINDOW_NOT_FOCUSABLE));
return ret;
}
JSValue js_window_sync(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SyncWindow(w);
return JS_NULL;
}
static const JSCFunctionListEntry js_SDL_Window_funcs[] = {
MIST_FUNC_DEF(SDL_Window, fullscreen, 0),
MIST_FUNC_DEF(SDL_Window, keyboard_shown, 0),
MIST_FUNC_DEF(window, theme, 0),
MIST_FUNC_DEF(window, safe_area, 0),
MIST_FUNC_DEF(window, set_icon, 1),
MIST_FUNC_DEF(window, raise, 0),
MIST_FUNC_DEF(window, restore, 0),
MIST_FUNC_DEF(window, flash, 1),
MIST_FUNC_DEF(window, destroy, 0),
MIST_FUNC_DEF(window, sync, 0),
CGETSET_ADD(window, title),
CGETSET_ADD(window, size),
CGETSET_ADD(window, position),
CGETSET_ADD(window, mouseGrab),
CGETSET_ADD(window, keyboardGrab),
CGETSET_ADD(window, opacity),
CGETSET_ADD(window, minimumSize),
CGETSET_ADD(window, maximumSize),
CGETSET_ADD(window, resizable),
CGETSET_ADD(window, bordered),
CGETSET_ADD(window, alwaysOnTop),
CGETSET_ADD(window, fullscreen),
CGETSET_ADD(window, focusable),
CGETSET_ADD(window, modal),
CGETSET_ADD(window, visible),
CGETSET_ADD(window, minimized),
CGETSET_ADD(window, maximized),
CGETSET_ADD(window, parent),
JS_CGETSET_DEF("id", js_window_get_id, NULL),
JS_CGETSET_DEF("pixelDensity", js_window_get_pixelDensity, NULL),
JS_CGETSET_DEF("displayScale", js_window_get_displayScale, NULL),
JS_CGETSET_DEF("sizeInPixels", js_window_get_sizeInPixels, NULL),
JS_CGETSET_DEF("flags", js_window_get_flags, NULL),
JS_CGETSET_DEF("surface", js_window_get_surface, NULL),
MIST_FUNC_DEF(window, updateSurface, 0),
MIST_FUNC_DEF(window, updateSurfaceRects, 1),
};
// Cursor creation function
JSC_CCALL(sdl_create_cursor,
SDL_Surface *surf = js2SDL_Surface(js, argv[0]);
if (!surf) return JS_ThrowReferenceError(js, "Invalid surface");
vec2 hot = {0, 0};
if (argc > 1) hot = js2vec2(js, argv[1]);
SDL_Cursor *cursor = SDL_CreateColorCursor(surf, hot.x, hot.y);
if (!cursor) return JS_ThrowReferenceError(js, "Failed to create cursor: %s", SDL_GetError());
return SDL_Cursor2js(js, cursor);
)
// Set cursor function
JSC_CCALL(sdl_set_cursor,
SDL_Cursor *cursor = js2SDL_Cursor(js, argv[0]);
if (!cursor) return JS_ThrowReferenceError(js, "Invalid cursor");
SDL_SetCursor(cursor);
)
CELL_USE_INIT(
if (!SDL_Init(SDL_INIT_VIDEO))
return JS_ThrowInternalError(js, "Unable to initialize video subsystem: %s", SDL_GetError());
JSValue ret = JS_NewObject(js);
JS_SetPropertyStr(js, ret, "window", QJSCLASSPREP_FUNCS_CTOR(SDL_Window, 1));
QJSCLASSPREP_NO_FUNCS(SDL_Cursor);
// Add cursor functions
JS_SetPropertyStr(js, ret, "createCursor", JS_NewCFunction(js, js_sdl_create_cursor, "createCursor", 2));
JS_SetPropertyStr(js, ret, "setCursor", JS_NewCFunction(js, js_sdl_set_cursor, "setCursor", 1));
return ret;
)