diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 4654c63d9..de1a3e7c7 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include #include @@ -20,6 +19,8 @@ namespace Input { using Libraries::Pad::OrbisPadButtonDataOffset; +static constexpr u64 MaxQueueAgeUs = 16'000; + void State::OnButton(OrbisPadButtonDataOffset button, bool isPressed) { if (isPressed) { buttonsState |= button; @@ -93,6 +94,7 @@ void State::UpdateAxisSmoothing() { GameController::GameController() : m_states_queue(64) {} void GameController::ReadState(State* state, bool* isConnected, int* connectedCount) { + std::lock_guard lg(m_state_mutex); *isConnected = m_connected; *connectedCount = m_connected_count; *state = m_state; @@ -100,6 +102,7 @@ void GameController::ReadState(State* state, bool* isConnected, int* connectedCo int GameController::ReadStates(State* states, int states_num, bool* isConnected, int* connectedCount) { + std::lock_guard lg(m_state_mutex); *isConnected = m_connected; *connectedCount = m_connected_count; @@ -107,16 +110,6 @@ int GameController::ReadStates(State* states, int states_num, bool* isConnected, return 0; } - std::lock_guard lg(m_states_queue_mutex); - if (states_num == 1) { - auto o_state = m_states_queue.PopLatest(); - if (!o_state) { - return 0; - } - states[0] = *o_state; - return 1; - } - int ret_num = 0; for (int i = 0; i < states_num; i++) { auto o_state = m_states_queue.Pop(); @@ -129,47 +122,33 @@ int GameController::ReadStates(State* states, int states_num, bool* isConnected, } void GameController::Button(OrbisPadButtonDataOffset button, bool is_pressed) { + std::lock_guard lg(m_state_mutex); m_state.OnButton(button, is_pressed); - PushState(); + PushStateLocked(); } void GameController::Axis(Input::Axis axis, int value, bool smooth) { + std::lock_guard lg(m_state_mutex); m_state.OnAxis(axis, value, smooth); - m_state.UpdateAxisSmoothing(); - PushState(); -} - -void GameController::Gyro(int id) { - m_state.OnGyro(gyro_buf); -} - -void GameController::Acceleration(int id) { - m_state.OnAccel(accel_buf); + PushStateLocked(); } void GameController::UpdateGyro(const float gyro[3]) { - std::scoped_lock l(m_states_queue_mutex); + std::scoped_lock l(m_state_mutex); std::memcpy(gyro_buf, gyro, sizeof(gyro_buf)); } void GameController::UpdateAcceleration(const float acceleration[3]) { - std::scoped_lock l(m_states_queue_mutex); + std::scoped_lock l(m_state_mutex); std::memcpy(accel_buf, acceleration, sizeof(accel_buf)); } void GameController::PollState() { - m_state.UpdateAxisSmoothing(); - m_state.OnGyro(gyro_buf); - m_state.OnAccel(accel_buf); - PushState(); + std::lock_guard lg(m_state_mutex); + PushStateLocked(); } -void GameController::UpdateAxisSmoothing() { - m_state.UpdateAxisSmoothing(); - PushState(); -} - -void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) { +void GameController::SetLightBarRGB(u8 const r, u8 const g, u8 const b) { if (override_colour.has_value()) { return; } @@ -179,6 +158,10 @@ void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) { } } +void GameController::SetLightBarRGB(Colour const c) { + SetLightBarRGB(c.r, c.g, c.b); +} + Colour GameController::GetLightBarRGB() { return colour; } @@ -189,6 +172,22 @@ void GameController::PollLightColour() { } } +void GameControllers::ResetLightbarColors() { + for (auto& c : controllers) { + auto const* u = UserManagement.GetUserByID(c->user_id); + if (!u || !c->m_sdl_gamepad) { + continue; + } + auto const i = u->user_color - 1; + if (i < 0 || i > 3) { + continue; + } + auto const& col = g_user_colours[i]; + c->override_colour = std::nullopt; + c->SetLightBarRGB(col); + } +} + bool GameController::SetVibration(u8 smallMotor, u8 largeMotor) { if (m_sdl_gamepad != nullptr) { return SDL_RumbleGamepad(m_sdl_gamepad, (smallMotor / 255.0f) * 0xFFFF, @@ -199,9 +198,10 @@ bool GameController::SetVibration(u8 smallMotor, u8 largeMotor) { void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, float y) { if (touchIndex < 2) { + std::lock_guard lg(m_state_mutex); bool was_pressed = m_state.touchpad[0].state || m_state.touchpad[1].state; m_state.OnTouchpad(touchIndex, touchDown, x, y); - PushState(); + PushStateLocked(); if (!m_state.touchpad[0].state && !m_state.touchpad[1].state && was_pressed) { last_touch_down_timestamp = 0; } else if ((m_state.touchpad[0].state || m_state.touchpad[1].state) && !was_pressed) { @@ -251,6 +251,21 @@ void GameControllers::CalculateOrientation(Libraries::Pad::OrbisFVector3& accele orientation.y, orientation.z, orientation.w); } +void GameController::ConnectController(SDL_Gamepad* pad) { + std::scoped_lock l(m_state_mutex); + m_states_queue.Clear(); + m_sdl_gamepad = pad; + m_connected_count = 1; + m_connected = true; +} +void GameController::DisconnectController() { + std::scoped_lock l(m_state_mutex); + m_states_queue.Clear(); + m_sdl_gamepad = nullptr; + m_connected_count = 0; + m_connected = false; +} + bool is_first_check = true; void GameControllers::TryOpenSDLControllers() { @@ -279,16 +294,9 @@ void GameControllers::TryOpenSDLControllers() { } if (!still_connected) { auto u = UserManagement.GetUserByID(controllers[i]->user_id); - UserManagement.LogoutUser(u); SDL_CloseGamepad(pad); - controllers[i]->m_sdl_gamepad = nullptr; + controllers[i]->DisconnectController(); controllers[i]->user_id = -1; - controllers[i]->m_connected = false; - controllers[i]->m_connected_count = 0; - { - std::scoped_lock l(controllers[i]->m_states_queue_mutex); - controllers[i]->m_states_queue.Clear(); - } slot_taken[i] = false; } } @@ -313,18 +321,12 @@ void GameControllers::TryOpenSDLControllers() { // Player N won't be registered at all } auto* c = controllers[i]; - c->m_sdl_gamepad = pad; LOG_INFO(Input, "Gamepad registered for slot {}! Handle: {}", i, SDL_GetGamepadID(pad)); - c->user_id = u->user_id; - c->m_connected = true; - c->m_connected_count = 1; - { - std::scoped_lock l(c->m_states_queue_mutex); - c->m_states_queue.Clear(); - } slot_taken[i] = true; + c->user_id = u->user_id; UserManagement.LoginUser(u, i + 1); + c->ConnectController(pad); if (EmulatorSettings.IsMotionControlsEnabled()) { if (SDL_SetGamepadSensorEnabled(c->m_sdl_gamepad, SDL_SENSOR_GYRO, true)) { c->gyro_poll_rate = @@ -352,6 +354,7 @@ void GameControllers::TryOpenSDLControllers() { if (controller_count - move_count == 0) { auto u = UserManagement.GetUserByPlayerIndex(1); controllers[0]->user_id = u->user_id; + controllers[0]->ConnectController(nullptr); UserManagement.LoginUser(u, 1); } } @@ -408,9 +411,15 @@ void GameController::SetLastUpdate(std::chrono::steady_clock::time_point lastUpd m_last_update = lastUpdate; } -void GameController::PushState() { - std::lock_guard lg(m_states_queue_mutex); +void GameController::PushStateLocked() { + m_state.UpdateAxisSmoothing(); + m_state.OnGyro(gyro_buf); + m_state.OnAccel(accel_buf); m_state.time = Libraries::Kernel::sceKernelGetProcessTime(); + const u64 oldest_allowed = + m_state.time > MaxQueueAgeUs ? m_state.time - MaxQueueAgeUs : 0; + m_states_queue.DiscardWhile( + [oldest_allowed](const State& state) { return state.time < oldest_allowed; }); m_states_queue.Push(m_state); } diff --git a/src/input/controller.h b/src/input/controller.h index 4c5d48ef3..96f3f45a5 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -3,9 +3,7 @@ #pragma once -#include #include -#include #include #include @@ -45,6 +43,12 @@ struct TouchpadEntry { struct Colour { u8 r, g, b; }; +static constexpr Input::Colour g_user_colours[4]{ + {0, 0, 255}, // blue + {255, 0, 0}, // red + {0, 255, 0}, // green + {255, 0, 255}, // pink +}; struct State { private: @@ -104,14 +108,12 @@ public: return std::move(m_storage[index]); } - std::optional PopLatest() { - if (m_size == 0) { - return {}; + template + void DiscardWhile(Predicate predicate) { + while (m_size > 0 && predicate(m_storage[m_begin])) { + m_begin = (m_begin + 1) % m_storage.size(); + m_size -= 1; } - const size_t index = (m_begin + m_size - 1) % m_storage.size(); - m_begin = 0; - m_size = 0; - return std::move(m_storage[index]); } void Clear() { @@ -131,19 +133,19 @@ class GameController { public: GameController(); virtual ~GameController() = default; + void ConnectController(SDL_Gamepad* pad); + void DisconnectController(); void ReadState(State* state, bool* isConnected, int* connectedCount); int ReadStates(State* states, int states_num, bool* isConnected, int* connectedCount); void Button(Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed); void Axis(Input::Axis axis, int value, bool smooth = true); - void Gyro(int id); - void Acceleration(int id); void UpdateGyro(const float gyro[3]); void UpdateAcceleration(const float acceleration[3]); void PollState(); - void UpdateAxisSmoothing(); - void SetLightBarRGB(u8 r, u8 g, u8 b); + void SetLightBarRGB(u8 const r, u8 const g, u8 const b); + void SetLightBarRGB(Colour const c); Colour GetLightBarRGB(); void PollLightColour(); bool SetVibration(u8 smallMotor, u8 largeMotor); @@ -171,10 +173,11 @@ public: u64 last_touch_down_timestamp = 0; private: - void PushState(); + // m_state_mutex must be held by the caller. + void PushStateLocked(); - bool m_connected = true; - int m_connected_count = 1; + bool m_connected = false; + int m_connected_count = 0; u8 m_touch_count = 0; u8 m_secondary_touch_count = 0; u8 m_previous_touchnum = 0; @@ -186,7 +189,7 @@ private: State m_state; - std::mutex m_states_queue_mutex; + std::mutex m_state_mutex; RingBufferQueue m_states_queue; }; @@ -196,10 +199,7 @@ class GameControllers { public: GameControllers() : controllers({new GameController(), new GameController(), new GameController(), - new GameController(), new GameController()}) { - controllers[4]->m_connected = false; - controllers[4]->m_connected_count = 0; - }; + new GameController(), new GameController()}) {}; virtual ~GameControllers() = default; GameController* operator[](const size_t& i) const { if (i > 4) { @@ -224,6 +224,7 @@ public: controllers[i]->SetLightBarRGB(r, g, b); controllers[i]->override_colour = {r, g, b}; } + void ResetLightbarColors(); }; } // namespace Input diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 7f5f7c20c..78606f50b 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -267,6 +267,7 @@ void WindowSDL::WaitEvent() { break; } controllers[i]->user_id = u->user_id; + controllers[i]->ConnectController(controllers[i]->m_sdl_gamepad); UserManagement.LoginUser(u, i + 1); break; } @@ -277,6 +278,7 @@ void WindowSDL::WaitEvent() { for (int i = 3; i >= 0; i--) { if (controllers[i]->user_id != -1) { UserManagement.LogoutUser(UserManagement.GetUserByID(controllers[i]->user_id)); + controllers[i]->DisconnectController(); controllers[i]->user_id = -1; break; }