From f85ab38f9e8cd837106fa148e7cc8ef98f626d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 18 Jan 2025 01:57:01 +0700 Subject: [PATCH 1/5] input: Don't use old input state in GameController::ReadState() Fix input lag when there is multiple user inputs happen at the same time. SDL input events can only fire one at a time and game may get some input events later than other. --- src/core/libraries/pad/pad.cpp | 33 ++++-- src/input/controller.cpp | 147 +++++++++++++------------- src/input/controller.h | 35 +++++-- src/sdl_window.cpp | 181 +++++++++++++++++++++++++++++---- src/sdl_window.h | 22 +++- 5 files changed, 306 insertions(+), 112 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 7eb628a90e9..9a44f91f063 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -11,6 +11,8 @@ namespace Libraries::Pad { +using Input::GameController; + int PS4_SYSV_ABI scePadClose(s32 handle) { LOG_ERROR(Lib_Pad, "(STUBBED) called"); return ORBIS_OK; @@ -290,7 +292,8 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { int connected_count = 0; bool connected = false; Input::State states[64]; - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); + const auto* engine = controller->GetEngine(); int ret_num = controller->ReadStates(states, num, &connected, &connected_count); if (!connected) { @@ -311,9 +314,14 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { pData[i].angularVelocity.x = states[i].angularVelocity.x; pData[i].angularVelocity.y = states[i].angularVelocity.y; pData[i].angularVelocity.z = states[i].angularVelocity.z; - Input::GameController::CalculateOrientation(pData[i].acceleration, pData[i].angularVelocity, - 1.0f / controller->accel_poll_rate, - pData[i].orientation); + if (engine) { + const auto accel_poll_rate = engine->GetAccelPollRate(); + if (accel_poll_rate != 0.0f) { + GameController::CalculateOrientation(pData[i].acceleration, + pData[i].angularVelocity, + 1.0f / accel_poll_rate, pData[i].orientation); + } + } pData[i].touchData.touchNum = (states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0); pData[i].touchData.touch[0].x = states[i].touchpad[0].x; @@ -356,7 +364,8 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) { return ORBIS_PAD_ERROR_INVALID_HANDLE; } - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); + const auto* engine = controller->GetEngine(); int connectedCount = 0; bool isConnected = false; Input::State state; @@ -374,9 +383,13 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { pData->angularVelocity.x = state.angularVelocity.x; pData->angularVelocity.y = state.angularVelocity.y; pData->angularVelocity.z = state.angularVelocity.z; - Input::GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, - 1.0f / controller->accel_poll_rate, - pData->orientation); + if (engine) { + const auto accel_poll_rate = engine->GetAccelPollRate(); + if (accel_poll_rate != 0.0f) { + GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, + 1.0f / accel_poll_rate, pData->orientation); + } + } pData->touchData.touchNum = (state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0); pData->touchData.touch[0].x = state.touchpad[0].x; @@ -468,7 +481,7 @@ int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pPar return ORBIS_PAD_ERROR_INVALID_LIGHTBAR_SETTING; } - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); controller->SetLightBarRGB(pParam->r, pParam->g, pParam->b); return ORBIS_OK; } @@ -536,7 +549,7 @@ int PS4_SYSV_ABI scePadSetVibration(s32 handle, const OrbisPadVibrationParam* pP if (pParam != nullptr) { LOG_DEBUG(Lib_Pad, "scePadSetVibration called handle = {} data = {} , {}", handle, pParam->smallMotor, pParam->largeMotor); - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); controller->SetVibration(pParam->smallMotor, pParam->largeMotor); return ORBIS_OK; } diff --git a/src/input/controller.cpp b/src/input/controller.cpp index eb43e6adfdc..dc5fdf37072 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -10,6 +10,55 @@ namespace Input { +using Libraries::Pad::OrbisPadButtonDataOffset; + +void State::OnButton(OrbisPadButtonDataOffset button, bool isPressed) { + if (isPressed) { + buttonsState |= button; + } else { + buttonsState &= ~button; + } +} + +void State::OnAxis(Axis axis, int value) { + const auto toggle = [&](const auto button) { + if (value > 0) { + buttonsState |= button; + } else { + buttonsState &= ~button; + } + }; + switch (axis) { + case Axis::TriggerLeft: + toggle(OrbisPadButtonDataOffset::L2); + break; + case Axis::TriggerRight: + toggle(OrbisPadButtonDataOffset::R2); + break; + default: + break; + } + axes[static_cast(axis)] = value; +} + +void State::OnTouchpad(int touchIndex, bool isDown, float x, float y) { + touchpad[touchIndex].state = isDown; + touchpad[touchIndex].x = static_cast(x * 1920); + touchpad[touchIndex].y = static_cast(y * 941); +} + +void State::OnGyro(const float gyro[3]) { + angularVelocity.x = gyro[0]; + angularVelocity.y = gyro[1]; + angularVelocity.z = gyro[2]; +} + +void State::OnAccel(const float accel[3]) { + acceleration.x = accel[0]; + acceleration.y = accel[1]; + acceleration.z = accel[2]; +} + GameController::GameController() { m_states_num = 0; m_last_state = State(); @@ -20,7 +69,7 @@ void GameController::ReadState(State* state, bool* isConnected, int* connectedCo *isConnected = m_connected; *connectedCount = m_connected_count; - *state = GetLastState(); + *state = m_engine && m_connected ? m_engine->ReadState() : GetLastState(); } int GameController::ReadStates(State* states, int states_num, bool* isConnected, @@ -75,45 +124,22 @@ void GameController::AddState(const State& state) { m_states_num++; } -void GameController::CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, - bool is_pressed) { +void GameController::CheckButton(int id, OrbisPadButtonDataOffset button, bool is_pressed) { std::scoped_lock lock{m_mutex}; auto state = GetLastState(); + state.time = Libraries::Kernel::sceKernelGetProcessTime(); - if (is_pressed) { - state.buttonsState |= button; - } else { - state.buttonsState &= ~button; - } + state.OnButton(button, is_pressed); AddState(state); } void GameController::Axis(int id, Input::Axis axis, int value) { - using Libraries::Pad::OrbisPadButtonDataOffset; - std::scoped_lock lock{m_mutex}; auto state = GetLastState(); state.time = Libraries::Kernel::sceKernelGetProcessTime(); - int axis_id = static_cast(axis); - state.axes[axis_id] = value; - - if (axis == Input::Axis::TriggerLeft) { - if (value > 0) { - state.buttonsState |= OrbisPadButtonDataOffset::L2; - } else { - state.buttonsState &= ~OrbisPadButtonDataOffset::L2; - } - } - - if (axis == Input::Axis::TriggerRight) { - if (value > 0) { - state.buttonsState |= OrbisPadButtonDataOffset::R2; - } else { - state.buttonsState &= ~OrbisPadButtonDataOffset::R2; - } - } + state.OnAxis(axis, value); AddState(state); } @@ -124,9 +150,7 @@ void GameController::Gyro(int id, const float gyro[3]) { state.time = Libraries::Kernel::sceKernelGetProcessTime(); // Update the angular velocity (gyro data) - state.angularVelocity.x = gyro[0]; // X-axis - state.angularVelocity.y = gyro[1]; // Y-axis - state.angularVelocity.z = gyro[2]; // Z-axis + state.OnGyro(gyro); AddState(state); } @@ -136,9 +160,7 @@ void GameController::Acceleration(int id, const float acceleration[3]) { state.time = Libraries::Kernel::sceKernelGetProcessTime(); // Update the acceleration values - state.acceleration.x = acceleration[0]; // X-axis - state.acceleration.y = acceleration[1]; // Y-axis - state.acceleration.z = acceleration[2]; // Z-axis + state.OnAccel(acceleration); AddState(state); } @@ -211,62 +233,49 @@ void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceler } void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) { - if (m_sdl_gamepad != nullptr) { - SDL_SetGamepadLED(m_sdl_gamepad, r, g, b); + if (!m_engine) { + return; } + std::scoped_lock _{m_mutex}; + m_engine->SetLightBarRGB(r, g, b); } -bool GameController::SetVibration(u8 smallMotor, u8 largeMotor) { - if (m_sdl_gamepad != nullptr) { - return SDL_RumbleGamepad(m_sdl_gamepad, (smallMotor / 255.0f) * 0xFFFF, - (largeMotor / 255.0f) * 0xFFFF, -1); +void GameController::SetVibration(u8 smallMotor, u8 largeMotor) { + if (!m_engine) { + return; } - return true; + std::scoped_lock _{m_mutex}; + m_engine->SetVibration(smallMotor, largeMotor); } void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, float y) { if (touchIndex < 2) { std::scoped_lock lock{m_mutex}; auto state = GetLastState(); - state.time = Libraries::Kernel::sceKernelGetProcessTime(); - state.touchpad[touchIndex].state = touchDown; - state.touchpad[touchIndex].x = static_cast(x * 1920); - state.touchpad[touchIndex].y = static_cast(y * 941); + state.time = Libraries::Kernel::sceKernelGetProcessTime(); + state.OnTouchpad(touchIndex, touchDown, x, y); AddState(state); } } -void GameController::TryOpenSDLController() { - if (m_sdl_gamepad == nullptr || !SDL_GamepadConnected(m_sdl_gamepad)) { - int gamepad_count; - SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); - m_sdl_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr; - if (Config::getIsMotionControlsEnabled()) { - if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_GYRO, true)) { - gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_GYRO); - LOG_INFO(Input, "Gyro initialized, poll rate: {}", gyro_poll_rate); - } else { - LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad"); - } - if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_ACCEL, true)) { - accel_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_ACCEL); - LOG_INFO(Input, "Accel initialized, poll rate: {}", accel_poll_rate); - } else { - LOG_ERROR(Input, "Failed to initialize accel controls for gamepad"); - } - } - - SDL_free(gamepads); - - SetLightBarRGB(0, 0, 255); +void GameController::SetEngine(Engine* engine) { + std::scoped_lock _{m_mutex}; + delete m_engine; + m_engine = engine; + if (m_engine) { + m_engine->Init(); } } +Engine* GameController::GetEngine() { + return m_engine; +} + u32 GameController::Poll() { - std::scoped_lock lock{m_mutex}; if (m_connected) { + std::scoped_lock lock{m_mutex}; auto time = Libraries::Kernel::sceKernelGetProcessTime(); if (m_states_num == 0) { auto diff = (time - m_last_state.time) / 1000; diff --git a/src/input/controller.h b/src/input/controller.h index c6fc02c2407..bb736345753 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -3,12 +3,11 @@ #pragma once +#include #include #include "common/types.h" #include "core/libraries/pad/pad.h" -struct SDL_Gamepad; - namespace Input { enum class Axis { @@ -28,7 +27,14 @@ struct TouchpadEntry { u16 y{}; }; -struct State { +class State { +public: + void OnButton(Libraries::Pad::OrbisPadButtonDataOffset, bool); + void OnAxis(Axis, int); + void OnTouchpad(int touchIndex, bool isDown, float x, float y); + void OnGyro(const float[3]); + void OnAccel(const float[3]); + Libraries::Pad::OrbisPadButtonDataOffset buttonsState{}; u64 time = 0; int axes[static_cast(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0}; @@ -38,9 +44,19 @@ struct State { Libraries::Pad::OrbisFQuaternion orientation = {0.0f, 0.0f, 0.0f, 1.0f}; }; +class Engine { +public: + virtual ~Engine() = default; + virtual void Init() = 0; + virtual void SetLightBarRGB(u8 r, u8 g, u8 b) = 0; + virtual void SetVibration(u8 smallMotor, u8 largeMotor) = 0; + virtual State ReadState() = 0; + virtual float GetAccelPollRate() const = 0; + virtual float GetGyroPollRate() const = 0; +}; + inline int GetAxis(int min, int max, int value) { - int v = (255 * (value - min)) / (max - min); - return (v < 0 ? 0 : (v > 255 ? 255 : v)); + return std::clamp((255 * (value - min)) / (max - min), 0, 255); } constexpr u32 MAX_STATES = 64; @@ -59,13 +75,12 @@ class GameController { void Gyro(int id, const float gyro[3]); void Acceleration(int id, const float acceleration[3]); void SetLightBarRGB(u8 r, u8 g, u8 b); - bool SetVibration(u8 smallMotor, u8 largeMotor); + void SetVibration(u8 smallMotor, u8 largeMotor); void SetTouchpadState(int touchIndex, bool touchDown, float x, float y); - void TryOpenSDLController(); + void SetEngine(Engine*); + Engine* GetEngine(); u32 Poll(); - float gyro_poll_rate; - float accel_poll_rate; static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, Libraries::Pad::OrbisFVector3& angularVelocity, float deltaTime, @@ -85,7 +100,7 @@ class GameController { std::array m_states; std::array m_private; - SDL_Gamepad* m_sdl_gamepad = nullptr; + Engine* m_engine = nullptr; }; } // namespace Input diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index b0126def20b..153f417c553 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/config.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" #include "imgui/renderer/imgui_core.h" #include "input/controller.h" @@ -20,47 +21,185 @@ #include #endif -namespace Frontend { +namespace Input { -using namespace Libraries::Pad; +using Libraries::Pad::OrbisPadButtonDataOffset; static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) { + using OPBDO = OrbisPadButtonDataOffset; + switch (button) { case SDL_GAMEPAD_BUTTON_DPAD_DOWN: - return OrbisPadButtonDataOffset::Down; + return OPBDO::Down; case SDL_GAMEPAD_BUTTON_DPAD_UP: - return OrbisPadButtonDataOffset::Up; + return OPBDO::Up; case SDL_GAMEPAD_BUTTON_DPAD_LEFT: - return OrbisPadButtonDataOffset::Left; + return OPBDO::Left; case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: - return OrbisPadButtonDataOffset::Right; + return OPBDO::Right; case SDL_GAMEPAD_BUTTON_SOUTH: - return OrbisPadButtonDataOffset::Cross; + return OPBDO::Cross; case SDL_GAMEPAD_BUTTON_NORTH: - return OrbisPadButtonDataOffset::Triangle; + return OPBDO::Triangle; case SDL_GAMEPAD_BUTTON_WEST: - return OrbisPadButtonDataOffset::Square; + return OPBDO::Square; case SDL_GAMEPAD_BUTTON_EAST: - return OrbisPadButtonDataOffset::Circle; + return OPBDO::Circle; case SDL_GAMEPAD_BUTTON_START: - return OrbisPadButtonDataOffset::Options; + return OPBDO::Options; case SDL_GAMEPAD_BUTTON_TOUCHPAD: - return OrbisPadButtonDataOffset::TouchPad; + return OPBDO::TouchPad; case SDL_GAMEPAD_BUTTON_BACK: - return OrbisPadButtonDataOffset::TouchPad; + return OPBDO::TouchPad; case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: - return OrbisPadButtonDataOffset::L1; + return OPBDO::L1; case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: - return OrbisPadButtonDataOffset::R1; + return OPBDO::R1; case SDL_GAMEPAD_BUTTON_LEFT_STICK: - return OrbisPadButtonDataOffset::L3; + return OPBDO::L3; case SDL_GAMEPAD_BUTTON_RIGHT_STICK: - return OrbisPadButtonDataOffset::R3; + return OPBDO::R3; + default: + return OPBDO::None; + } +} + +static SDL_GamepadAxis InputAxisToSDL(Axis axis) { + switch (axis) { + case Axis::LeftX: + return SDL_GAMEPAD_AXIS_LEFTX; + case Axis::LeftY: + return SDL_GAMEPAD_AXIS_LEFTY; + case Axis::RightX: + return SDL_GAMEPAD_AXIS_RIGHTX; + case Axis::RightY: + return SDL_GAMEPAD_AXIS_RIGHTY; + case Axis::TriggerLeft: + return SDL_GAMEPAD_AXIS_LEFT_TRIGGER; + case Axis::TriggerRight: + return SDL_GAMEPAD_AXIS_RIGHT_TRIGGER; default: - return OrbisPadButtonDataOffset::None; + UNREACHABLE(); + } +} + +SDLInputEngine::~SDLInputEngine() { + if (m_gamepad) { + SDL_CloseGamepad(m_gamepad); + } +} + +void SDLInputEngine::Init() { + int gamepad_count; + SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); + if (m_gamepad) { + SDL_CloseGamepad(m_gamepad); + } + m_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr; + if (Config::getIsMotionControlsEnabled()) { + if (SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_GYRO, true)) { + m_gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_gamepad, SDL_SENSOR_GYRO); + LOG_INFO(Input, "Gyro initialized, poll rate: {}", m_gyro_poll_rate); + } else { + LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad"); + } + if (SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_ACCEL, true)) { + m_accel_poll_rate = SDL_GetGamepadSensorDataRate(m_gamepad, SDL_SENSOR_ACCEL); + LOG_INFO(Input, "Accel initialized, poll rate: {}", m_accel_poll_rate); + } else { + LOG_ERROR(Input, "Failed to initialize accel controls for gamepad"); + }; + } + SDL_free(gamepads); + SetLightBarRGB(0, 0, 255); +} + +void SDLInputEngine::SetLightBarRGB(u8 r, u8 g, u8 b) { + if (m_gamepad) { + SDL_SetGamepadLED(m_gamepad, r, g, b); + } +} + +void SDLInputEngine::SetVibration(u8 smallMotor, u8 largeMotor) { + if (m_gamepad) { + const auto low_freq = (smallMotor / 255.0f) * 0xFFFF; + const auto high_freq = (largeMotor / 255.0f) * 0xFFFF; + SDL_RumbleGamepad(m_gamepad, low_freq, high_freq, -1); + } +} + +State SDLInputEngine::ReadState() { + State state{}; + state.time = Libraries::Kernel::sceKernelGetProcessTime(); + + // Buttons + for (u8 i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i) { + auto orbisButton = SDLGamepadToOrbisButton(i); + if (orbisButton == OrbisPadButtonDataOffset::None) { + continue; + } + state.OnButton(orbisButton, SDL_GetGamepadButton(m_gamepad, (SDL_GamepadButton)i)); + } + + // Axes + for (int i = 0; i < static_cast(Axis::AxisMax); ++i) { + const auto axis = static_cast(i); + const auto value = SDL_GetGamepadAxis(m_gamepad, InputAxisToSDL(axis)); + switch (axis) { + case Axis::TriggerLeft: + case Axis::TriggerRight: + state.OnAxis(axis, GetAxis(0, 0x8000, value)); + break; + default: + state.OnAxis(axis, GetAxis(-0x8000, 0x8000, value)); + break; + } + } + + // Touchpad + if (SDL_GetNumGamepadTouchpads(m_gamepad) > 0) { + for (int finger = 0; finger < 2; ++finger) { + bool down; + float x, y; + if (SDL_GetGamepadTouchpadFinger(m_gamepad, 0, finger, &down, &x, &y, NULL)) { + state.OnTouchpad(finger, down, x, y); + } + } + } + + // Gyro + if (SDL_GamepadHasSensor(m_gamepad, SDL_SENSOR_GYRO)) { + float gyro[3]; + if (SDL_GetGamepadSensorData(m_gamepad, SDL_SENSOR_GYRO, gyro, 3)) { + state.OnGyro(gyro); + } } + + // Accel + if (SDL_GamepadHasSensor(m_gamepad, SDL_SENSOR_ACCEL)) { + float accel[3]; + if (SDL_GetGamepadSensorData(m_gamepad, SDL_SENSOR_ACCEL, accel, 3)) { + state.OnAccel(accel); + } + } + + return state; } +float SDLInputEngine::GetGyroPollRate() const { + return m_gyro_poll_rate; +} + +float SDLInputEngine::GetAccelPollRate() const { + return m_accel_poll_rate; +} + +} // namespace Input + +namespace Frontend { + +using namespace Libraries::Pad; + static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint32 interval) { auto* controller = reinterpret_cast(userdata); return controller->Poll(); @@ -112,7 +251,7 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_ SDL_SetWindowFullscreen(window, Config::getIsFullscreen()); SDL_InitSubSystem(SDL_INIT_GAMEPAD); - controller->TryOpenSDLController(); + controller->SetEngine(new Input::SDLInputEngine()); #if defined(SDL_PLATFORM_WIN32) window_info.type = WindowSystemType::Windows; @@ -422,7 +561,7 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { switch (event->type) { case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_REMOVED: - controller->TryOpenSDLController(); + controller->SetEngine(new Input::SDLInputEngine()); break; case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: @@ -433,7 +572,7 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: { - button = SDLGamepadToOrbisButton(event->gbutton.button); + button = Input::SDLGamepadToOrbisButton(event->gbutton.button); if (button == OrbisPadButtonDataOffset::None) { break; } diff --git a/src/sdl_window.h b/src/sdl_window.h index 78d4bbc39f9..3ab3c361370 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -5,14 +5,32 @@ #include #include "common/types.h" +#include "input/controller.h" struct SDL_Window; struct SDL_Gamepad; union SDL_Event; namespace Input { -class GameController; -} + +class SDLInputEngine : public Engine { +public: + ~SDLInputEngine() override; + void Init() override; + void SetLightBarRGB(u8 r, u8 g, u8 b) override; + void SetVibration(u8 smallMotor, u8 largeMotor) override; + float GetGyroPollRate() const override; + float GetAccelPollRate() const override; + State ReadState() override; + +private: + SDL_Gamepad* m_gamepad = nullptr; + + float m_gyro_poll_rate{}; + float m_accel_poll_rate{}; +}; + +} // namespace Input namespace Frontend { From ecbee1ff55c0ddcf2f7c735f0a7aa96e74a2bde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 18 Jan 2025 13:04:51 +0700 Subject: [PATCH 2/5] input: More error checking --- src/sdl_window.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 153f417c553..89187df69bb 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -90,12 +90,24 @@ SDLInputEngine::~SDLInputEngine() { } void SDLInputEngine::Init() { - int gamepad_count; - SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); if (m_gamepad) { SDL_CloseGamepad(m_gamepad); + m_gamepad = nullptr; + } + int gamepad_count; + SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); + if (!gamepads) { + LOG_ERROR(Input, "Cannot get gamepad list: {}", std::string_view(SDL_GetError())); + return; + } + if (gamepad_count == 0) { + LOG_INFO(Input, "No gamepad found!"); + return; + } + LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count); + if (!(m_gamepad = SDL_OpenGamepad(gamepads[0]))) { + LOG_ERROR(Input, "Failed to open gamepad 0: {}", std::string_view(SDL_GetError())); } - m_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr; if (Config::getIsMotionControlsEnabled()) { if (SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_GYRO, true)) { m_gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_gamepad, SDL_SENSOR_GYRO); From 10ea1d266ccafc102b0569c648233f3390443ce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 18 Jan 2025 13:16:40 +0700 Subject: [PATCH 3/5] Use unique_ptr --- src/input/controller.cpp | 7 +++---- src/input/controller.h | 5 +++-- src/sdl_window.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index dc5fdf37072..71f0b0c099f 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -260,17 +260,16 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f } } -void GameController::SetEngine(Engine* engine) { +void GameController::SetEngine(std::unique_ptr engine) { std::scoped_lock _{m_mutex}; - delete m_engine; - m_engine = engine; + m_engine = std::move(engine); if (m_engine) { m_engine->Init(); } } Engine* GameController::GetEngine() { - return m_engine; + return m_engine.get(); } u32 GameController::Poll() { diff --git a/src/input/controller.h b/src/input/controller.h index bb736345753..a45e71d7781 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include "common/types.h" #include "core/libraries/pad/pad.h" @@ -77,7 +78,7 @@ class GameController { void SetLightBarRGB(u8 r, u8 g, u8 b); void SetVibration(u8 smallMotor, u8 largeMotor); void SetTouchpadState(int touchIndex, bool touchDown, float x, float y); - void SetEngine(Engine*); + void SetEngine(std::unique_ptr); Engine* GetEngine(); u32 Poll(); @@ -100,7 +101,7 @@ class GameController { std::array m_states; std::array m_private; - Engine* m_engine = nullptr; + std::unique_ptr m_engine = nullptr; }; } // namespace Input diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 89187df69bb..0de0ea39652 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -263,7 +263,7 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_ SDL_SetWindowFullscreen(window, Config::getIsFullscreen()); SDL_InitSubSystem(SDL_INIT_GAMEPAD); - controller->SetEngine(new Input::SDLInputEngine()); + controller->SetEngine(std::make_unique()); #if defined(SDL_PLATFORM_WIN32) window_info.type = WindowSystemType::Windows; @@ -573,7 +573,7 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { switch (event->type) { case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_REMOVED: - controller->SetEngine(new Input::SDLInputEngine()); + controller->SetEngine(std::make_unique()); break; case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: From 8ac50a614ee61a0c9e70e883f477cffce5afc73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 18 Jan 2025 13:20:18 +0700 Subject: [PATCH 4/5] Cleanup --- src/sdl_window.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 0de0ea39652..655e9f9af1c 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -97,7 +97,7 @@ void SDLInputEngine::Init() { int gamepad_count; SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); if (!gamepads) { - LOG_ERROR(Input, "Cannot get gamepad list: {}", std::string_view(SDL_GetError())); + LOG_ERROR(Input, "Cannot get gamepad list: {}", SDL_GetError()); return; } if (gamepad_count == 0) { @@ -106,7 +106,7 @@ void SDLInputEngine::Init() { } LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count); if (!(m_gamepad = SDL_OpenGamepad(gamepads[0]))) { - LOG_ERROR(Input, "Failed to open gamepad 0: {}", std::string_view(SDL_GetError())); + LOG_ERROR(Input, "Failed to open gamepad 0: {}", SDL_GetError()); } if (Config::getIsMotionControlsEnabled()) { if (SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_GYRO, true)) { From 5dd67b9dd83523bf63a47a485147f71d95a0af0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 18 Jan 2025 13:26:57 +0700 Subject: [PATCH 5/5] Add missing return --- src/sdl_window.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 655e9f9af1c..d1fe6bbab32 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -102,11 +102,14 @@ void SDLInputEngine::Init() { } if (gamepad_count == 0) { LOG_INFO(Input, "No gamepad found!"); + SDL_free(gamepads); return; } LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count); if (!(m_gamepad = SDL_OpenGamepad(gamepads[0]))) { LOG_ERROR(Input, "Failed to open gamepad 0: {}", SDL_GetError()); + SDL_free(gamepads); + return; } if (Config::getIsMotionControlsEnabled()) { if (SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_GYRO, true)) {