mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-06-10 03:35:00 -06:00
input: synchronize controller state updates
This commit is contained in:
parent
db7685adeb
commit
62b0fee5fb
@ -1,7 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
#include <SDL3/SDL.h>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -3,9 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@ -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<T> PopLatest() {
|
||||
if (m_size == 0) {
|
||||
return {};
|
||||
template <typename Predicate>
|
||||
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<State> 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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user