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