2022-07-27 11:25:25 -04:00
|
|
|
#include "input.hpp"
|
|
|
|
|
#include "internal.hpp"
|
|
|
|
|
|
|
|
|
|
#include "magic_enum.hpp"
|
|
|
|
|
|
2025-04-02 19:57:16 -06:00
|
|
|
#include <SDL3/SDL_haptic.h>
|
|
|
|
|
#include <SDL3/SDL_version.h>
|
|
|
|
|
#include <SDL3/SDL.h>
|
2022-07-27 11:25:25 -04:00
|
|
|
|
|
|
|
|
#include <absl/container/btree_map.h>
|
|
|
|
|
#include <absl/container/flat_hash_map.h>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
using namespace std::string_view_literals;
|
|
|
|
|
|
|
|
|
|
namespace aurora::input {
|
2025-04-14 17:31:48 -07:00
|
|
|
Module Log("aurora::input");
|
2022-07-27 11:25:25 -04:00
|
|
|
absl::flat_hash_map<Uint32, GameController> 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 {};
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-02 19:57:16 -06:00
|
|
|
SDL_JoystickID add_controller(SDL_JoystickID which) noexcept {
|
|
|
|
|
auto* ctrl = SDL_OpenGamepad(which);
|
2022-07-27 11:25:25 -04:00
|
|
|
if (ctrl != nullptr) {
|
|
|
|
|
GameController controller;
|
|
|
|
|
controller.m_controller = ctrl;
|
|
|
|
|
controller.m_index = which;
|
2025-04-02 19:57:16 -06:00
|
|
|
controller.m_vid = SDL_GetGamepadVendor(ctrl);
|
|
|
|
|
controller.m_pid = SDL_GetGamepadProduct(ctrl);
|
2022-07-27 11:25:25 -04:00
|
|
|
if (controller.m_vid == 0x05ac /* USB_VENDOR_APPLE */ && controller.m_pid == 3) {
|
|
|
|
|
// Ignore Apple TV remote
|
2025-04-02 19:57:16 -06:00
|
|
|
SDL_CloseGamepad(ctrl);
|
2022-07-27 11:25:25 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
2026-03-27 18:44:47 +01:00
|
|
|
controller.m_isGameCube = SDL_GetGamepadType(ctrl) == SDL_GAMEPAD_TYPE_GAMECUBE;
|
2025-04-02 19:57:16 -06:00
|
|
|
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));
|
2022-07-27 11:25:25 -04:00
|
|
|
g_GameControllers[instance] = controller;
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void remove_controller(Uint32 instance) noexcept {
|
|
|
|
|
if (g_GameControllers.find(instance) != g_GameControllers.end()) {
|
2025-04-02 19:57:16 -06:00
|
|
|
SDL_CloseGamepad(g_GameControllers[instance].m_controller);
|
2022-07-27 11:25:25 -04:00
|
|
|
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()) {
|
2025-04-02 19:57:16 -06:00
|
|
|
return SDL_GetGamepadPlayerIndex(g_GameControllers[instance].m_controller);
|
2022-07-27 11:25:25 -04:00
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void set_player_index(Uint32 instance, Sint32 index) noexcept {
|
|
|
|
|
if (g_GameControllers.find(instance) != g_GameControllers.end()) {
|
2025-04-02 19:57:16 -06:00
|
|
|
SDL_SetGamepadPlayerIndex(g_GameControllers[instance].m_controller, index);
|
2022-07-27 11:25:25 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string controller_name(Uint32 instance) noexcept {
|
|
|
|
|
if (g_GameControllers.find(instance) != g_GameControllers.end()) {
|
2025-04-02 19:57:16 -06:00
|
|
|
const auto* name = SDL_GetGamepadName(g_GameControllers[instance].m_controller);
|
2022-07-27 11:25:25 -04:00
|
|
|
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()) {
|
2025-04-02 19:57:16 -06:00
|
|
|
SDL_RumbleGamepad(g_GameControllers[instance].m_controller, low_freq_intensity, high_freq_intensity, duration_ms);
|
2022-07-27 11:25:25 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t controller_count() noexcept { return g_GameControllers.size(); }
|
|
|
|
|
|
2023-01-21 19:48:26 -08:00
|
|
|
void initialize() noexcept {
|
|
|
|
|
/* Make sure we initialize everything input related now, this will automatically add all of the connected controllers
|
|
|
|
|
* as expected */
|
2025-04-02 19:57:16 -06:00
|
|
|
ASSERT(SDL_Init(SDL_INIT_HAPTIC | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD), "Failed to initialize SDL subsystems: {}",
|
|
|
|
|
SDL_GetError());
|
2023-01-21 19:48:26 -08:00
|
|
|
}
|
2022-07-27 11:25:25 -04:00
|
|
|
} // namespace aurora::input
|