mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-03 03:18:09 -06:00
283 lines
9.1 KiB
C++
283 lines
9.1 KiB
C++
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <mutex>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <SDL3/SDL_gamepad.h>
|
|
#include "SDL3/SDL_joystick.h"
|
|
#include "common/assert.h"
|
|
#include "common/types.h"
|
|
#include "core/libraries/move/move.h"
|
|
#include "core/libraries/pad/pad.h"
|
|
#include "core/libraries/system/userservice.h"
|
|
|
|
struct SDL_Gamepad;
|
|
|
|
namespace Input {
|
|
|
|
enum class ControllerType {
|
|
Standard,
|
|
Move,
|
|
};
|
|
|
|
enum class Axis {
|
|
LeftX = 0,
|
|
LeftY = 1,
|
|
RightX = 2,
|
|
RightY = 3,
|
|
TriggerLeft = 4,
|
|
TriggerRight = 5,
|
|
|
|
AxisMax
|
|
};
|
|
|
|
struct TouchpadEntry {
|
|
u8 ID = 0;
|
|
bool state{};
|
|
u16 x{};
|
|
u16 y{};
|
|
};
|
|
|
|
struct Colour {
|
|
u8 r, g, b;
|
|
};
|
|
|
|
struct State {
|
|
private:
|
|
template <typename T>
|
|
using AxisArray = std::array<T, std::to_underlying(Axis::AxisMax)>;
|
|
static constexpr AxisArray<s32> axis_defaults{128, 128, 128, 128, 0, 0};
|
|
static constexpr u64 axis_smoothing_time{33000};
|
|
AxisArray<bool> axis_smoothing_flags{true};
|
|
AxisArray<u64> axis_smoothing_start_times{0};
|
|
AxisArray<int> axis_smoothing_start_values{axis_defaults};
|
|
AxisArray<int> axis_smoothing_end_values{axis_defaults};
|
|
|
|
public:
|
|
void OnButton(Libraries::Pad::OrbisPadButtonDataOffset, bool);
|
|
void OnAxis(Axis, int, bool smooth = true);
|
|
void OnTouchpad(int touchIndex, bool isDown, float x, float y);
|
|
void OnGyro(const float[3]);
|
|
void OnAccel(const float[3]);
|
|
void UpdateAxisSmoothing();
|
|
|
|
Libraries::Pad::OrbisPadButtonDataOffset buttonsState{};
|
|
u64 time = 0;
|
|
AxisArray<s32> axes{axis_defaults};
|
|
TouchpadEntry touchpad[2] = {{false, 0, 0}, {false, 0, 0}};
|
|
Libraries::Pad::OrbisFVector3 acceleration = {0.0f, -9.81f, 0.0f};
|
|
Libraries::Pad::OrbisFVector3 angularVelocity = {0.0f, 0.0f, 0.0f};
|
|
Libraries::Pad::OrbisFQuaternion orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
};
|
|
|
|
inline int GetAxis(int min, int max, int value) {
|
|
int v = (255 * (value - min)) / (max - min);
|
|
return (v < 0 ? 0 : (v > 255 ? 255 : v));
|
|
}
|
|
|
|
template <class T>
|
|
class RingBufferQueue {
|
|
public:
|
|
RingBufferQueue(size_t size) : m_storage(size) {}
|
|
|
|
void Push(T item) {
|
|
const size_t index = (m_begin + m_size) % m_storage.size();
|
|
m_storage[index] = std::move(item);
|
|
if (m_size < m_storage.size()) {
|
|
m_size += 1;
|
|
} else {
|
|
m_begin = (m_begin + 1) % m_storage.size();
|
|
}
|
|
}
|
|
|
|
std::optional<T> Pop() {
|
|
if (m_size == 0) {
|
|
return {};
|
|
}
|
|
const size_t index = m_begin;
|
|
m_begin = (m_begin + 1) % m_storage.size();
|
|
m_size -= 1;
|
|
return std::move(m_storage[index]);
|
|
}
|
|
|
|
private:
|
|
size_t m_begin = 0;
|
|
size_t m_size = 0;
|
|
std::vector<T> m_storage;
|
|
};
|
|
|
|
class GameController {
|
|
friend class GameControllers;
|
|
|
|
public:
|
|
GameController();
|
|
virtual ~GameController() = default;
|
|
|
|
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 UpdateAxisSmoothing();
|
|
void SetLightBarRGB(u8 r, u8 g, u8 b);
|
|
bool SetVibration(u8 smallMotor, u8 largeMotor);
|
|
void SetTouchpadState(int touchIndex, bool touchDown, float x, float y);
|
|
|
|
u8 GetTouchCount();
|
|
void SetTouchCount(u8 touchCount);
|
|
u8 GetSecondaryTouchCount();
|
|
void SetSecondaryTouchCount(u8 touchCount);
|
|
u8 GetPreviousTouchNum();
|
|
void SetPreviousTouchNum(u8 touchNum);
|
|
bool WasSecondaryTouchReset();
|
|
void UnsetSecondaryTouchResetBool();
|
|
|
|
void SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation);
|
|
Libraries::Pad::OrbisFQuaternion GetLastOrientation();
|
|
std::chrono::steady_clock::time_point GetLastUpdate();
|
|
void SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate);
|
|
|
|
float gyro_poll_rate;
|
|
float accel_poll_rate;
|
|
float gyro_buf[3] = {0.0f, 0.0f, 0.0f}, accel_buf[3] = {0.0f, 9.81f, 0.0f};
|
|
u32 user_id = Libraries::UserService::ORBIS_USER_SERVICE_USER_ID_INVALID;
|
|
SDL_Gamepad* m_sdl_gamepad = nullptr;
|
|
|
|
private:
|
|
void PushState();
|
|
|
|
bool m_connected = true;
|
|
int m_connected_count = 1;
|
|
u8 m_touch_count = 0;
|
|
u8 m_secondary_touch_count = 0;
|
|
u8 m_previous_touchnum = 0;
|
|
bool m_was_secondary_reset = false;
|
|
std::chrono::steady_clock::time_point m_last_update = {};
|
|
Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
|
|
u8 player_index = -1;
|
|
State m_state;
|
|
|
|
std::mutex m_states_queue_mutex;
|
|
RingBufferQueue<State> m_states_queue;
|
|
};
|
|
|
|
class MoveController {
|
|
friend class GameControllers;
|
|
|
|
public:
|
|
MoveController();
|
|
virtual ~MoveController() = default;
|
|
|
|
void ReadState(State* state, bool* isConnected, int* connectedCount);
|
|
int ReadStates(State* states, int states_num, bool* isConnected, int* connectedCount);
|
|
|
|
void Button(Libraries::Move::OrbisMoveButtonDataOffset 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 UpdateAxisSmoothing();
|
|
void SetLightBarRGB(u8 r, u8 g, u8 b);
|
|
bool SetVibration(u8 smallMotor, u8 largeMotor);
|
|
void SetTouchpadState(int touchIndex, bool touchDown, float x, float y);
|
|
|
|
u8 GetTouchCount();
|
|
void SetTouchCount(u8 touchCount);
|
|
u8 GetSecondaryTouchCount();
|
|
void SetSecondaryTouchCount(u8 touchCount);
|
|
u8 GetPreviousTouchNum();
|
|
void SetPreviousTouchNum(u8 touchNum);
|
|
bool WasSecondaryTouchReset();
|
|
void UnsetSecondaryTouchResetBool();
|
|
|
|
void SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation);
|
|
Libraries::Pad::OrbisFQuaternion GetLastOrientation();
|
|
std::chrono::steady_clock::time_point GetLastUpdate();
|
|
void SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate);
|
|
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
|
|
Libraries::Pad::OrbisFVector3& angularVelocity,
|
|
float deltaTime,
|
|
Libraries::Pad::OrbisFQuaternion& lastOrientation,
|
|
Libraries::Pad::OrbisFQuaternion& orientation);
|
|
|
|
float gyro_poll_rate;
|
|
float accel_poll_rate;
|
|
float gyro_buf[3] = {0.0f, 0.0f, 0.0f}, accel_buf[3] = {0.0f, 9.81f, 0.0f};
|
|
u32 user_id = Libraries::UserService::ORBIS_USER_SERVICE_USER_ID_INVALID;
|
|
SDL_Gamepad* m_sdl_gamepad = nullptr;
|
|
|
|
private:
|
|
void PushState();
|
|
|
|
bool m_connected = true;
|
|
int m_connected_count = 1;
|
|
u8 m_touch_count = 0;
|
|
u8 m_secondary_touch_count = 0;
|
|
u8 m_previous_touchnum = 0;
|
|
bool m_was_secondary_reset = false;
|
|
std::chrono::steady_clock::time_point m_last_update = {};
|
|
Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
|
|
u8 player_index = -1;
|
|
State m_state;
|
|
|
|
std::mutex m_states_queue_mutex;
|
|
RingBufferQueue<State> m_states_queue;
|
|
};
|
|
|
|
class GameControllers {
|
|
std::array<GameController*, 4> controllers;
|
|
std::array<MoveController*, 4> move_controllers;
|
|
|
|
static bool override_controller_color;
|
|
static Colour controller_override_color;
|
|
|
|
public:
|
|
GameControllers()
|
|
: controllers({new GameController(), new GameController(), new GameController(),
|
|
new GameController()}),
|
|
move_controllers({new MoveController(), new MoveController(), new MoveController(),
|
|
new MoveController()}) {};
|
|
virtual ~GameControllers() = default;
|
|
GameController* operator[](const size_t& i) const {
|
|
if (i > 3) {
|
|
UNREACHABLE_MSG("Index {} is out of bounds for GameControllers!", i);
|
|
}
|
|
return controllers[i];
|
|
}
|
|
static void TryOpenSDLControllers(GameControllers& controllers);
|
|
static u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id);
|
|
static std::optional<u8> GetControllerIndexFromUserID(s32 user_id);
|
|
static std::optional<u8> GetControllerIndexFromControllerID(s32 controller_id);
|
|
|
|
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
|
|
Libraries::Pad::OrbisFVector3& angularVelocity,
|
|
float deltaTime,
|
|
Libraries::Pad::OrbisFQuaternion& lastOrientation,
|
|
Libraries::Pad::OrbisFQuaternion& orientation);
|
|
static void SetOverrideControllerColor(bool set) {
|
|
override_controller_color = set;
|
|
}
|
|
static void SetControllerCustomColor(u8 r, u8 g, u8 b) {
|
|
controller_override_color = {r, g, b};
|
|
}
|
|
static bool GetOverrideControllerColor() {
|
|
return override_controller_color;
|
|
}
|
|
static Colour GetControllerCustomColor() {
|
|
return controller_override_color;
|
|
}
|
|
};
|
|
|
|
} // namespace Input
|