281 lines
8.3 KiB
C
281 lines
8.3 KiB
C
#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;
|
|
)
|