#include "cell.h" #include // 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; )