#include "input.hpp" #include "internal.hpp" #include "magic_enum.hpp" #include #include #include #include #include #include using namespace std::string_view_literals; namespace aurora::input { Module Log("aurora::input"); absl::flat_hash_map g_GameControllers; GameController* get_controller_for_player(uint32_t player) noexcept { for (auto& [which, controller] : g_GameControllers) { if (player_index(which) == player) { return &controller; } } #if 0 /* If we don't have a controller assigned to this port use the first unassigned controller */ if (!g_GameControllers.empty()) { int32_t availIndex = -1; GameController* ct = nullptr; for (auto& controller : g_GameControllers) { if (player_index(controller.first) == -1) { availIndex = controller.first; ct = &controller.second; break; } } if (availIndex != -1) { set_player_index(availIndex, player); return ct; } } #endif return nullptr; } Sint32 get_instance_for_player(uint32_t player) noexcept { for (const auto& [which, controller] : g_GameControllers) { if (player_index(which) == player) { return which; } } return {}; } SDL_JoystickID add_controller(SDL_JoystickID which) noexcept { auto* ctrl = SDL_OpenGamepad(which); if (ctrl != nullptr) { GameController controller; controller.m_controller = ctrl; controller.m_index = which; controller.m_vid = SDL_GetGamepadVendor(ctrl); controller.m_pid = SDL_GetGamepadProduct(ctrl); if (controller.m_vid == 0x05ac /* USB_VENDOR_APPLE */ && controller.m_pid == 3) { // Ignore Apple TV remote SDL_CloseGamepad(ctrl); return -1; } controller.m_isGameCube = SDL_GetGamepadType(ctrl) == SDL_GAMEPAD_TYPE_GAMECUBE; const auto props = SDL_GetGamepadProperties(ctrl); controller.m_hasRumble = SDL_GetBooleanProperty(props, SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN, true); SDL_JoystickID instance = SDL_GetJoystickID(SDL_GetGamepadJoystick(ctrl)); g_GameControllers[instance] = controller; return instance; } return -1; } void remove_controller(Uint32 instance) noexcept { if (g_GameControllers.find(instance) != g_GameControllers.end()) { SDL_CloseGamepad(g_GameControllers[instance].m_controller); g_GameControllers.erase(instance); } } bool is_gamecube(Uint32 instance) noexcept { if (g_GameControllers.find(instance) != g_GameControllers.end()) { return g_GameControllers[instance].m_isGameCube; } return false; } int32_t player_index(Uint32 instance) noexcept { if (g_GameControllers.find(instance) != g_GameControllers.end()) { return SDL_GetGamepadPlayerIndex(g_GameControllers[instance].m_controller); } return -1; } void set_player_index(Uint32 instance, Sint32 index) noexcept { if (g_GameControllers.find(instance) != g_GameControllers.end()) { SDL_SetGamepadPlayerIndex(g_GameControllers[instance].m_controller, index); } } std::string controller_name(Uint32 instance) noexcept { if (g_GameControllers.find(instance) != g_GameControllers.end()) { const auto* name = SDL_GetGamepadName(g_GameControllers[instance].m_controller); if (name != nullptr) { return {name}; } } return {}; } bool controller_has_rumble(Uint32 instance) noexcept { if (g_GameControllers.find(instance) != g_GameControllers.end()) { return g_GameControllers[instance].m_hasRumble; } return false; } void controller_rumble(uint32_t instance, uint16_t low_freq_intensity, uint16_t high_freq_intensity, uint16_t duration_ms) noexcept { if (g_GameControllers.find(instance) != g_GameControllers.end()) { SDL_RumbleGamepad(g_GameControllers[instance].m_controller, low_freq_intensity, high_freq_intensity, duration_ms); } } uint32_t controller_count() noexcept { return g_GameControllers.size(); } void initialize() noexcept { /* Make sure we initialize everything input related now, this will automatically add all of the connected controllers * as expected */ ASSERT(SDL_Init(SDL_INIT_HAPTIC | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD), "Failed to initialize SDL subsystems: {}", SDL_GetError()); } } // namespace aurora::input