Files
cell-sdl3/gamepad.c
2025-12-11 10:39:24 -06:00

503 lines
17 KiB
C

#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;
)