initial add
This commit is contained in:
418
audio.c
Normal file
418
audio.c
Normal 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
195
camera.c
Normal 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, ×tamp);
|
||||
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;
|
||||
)
|
||||
90
clipboard.c
Normal file
90
clipboard.c
Normal 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
547
events.c
Normal 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
127
examples/lenna.ce
Normal 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
502
gamepad.c
Normal 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;
|
||||
)
|
||||
212
haptic.c
Normal file
212
haptic.c
Normal 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
280
hidapi.c
Normal 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
780
input.c
Normal 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
393
joystick.c
Normal 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
143
keyboard.c
Normal 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
109
mouse.c
Normal 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
83
pen.c
Normal 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
250
render.c
Normal 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
333
sdl.c
Normal 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
109
sdl.h
Normal 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
156
sensor.c
Normal 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;
|
||||
)
|
||||
73
touch.c
Normal file
73
touch.c
Normal 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
255
tray.c
Normal 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
759
video.c
Normal 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;
|
||||
)
|
||||
Reference in New Issue
Block a user