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