mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-10-12 02:22:27 +00:00
input: reduce pad state latency for single-sample reads (#4508)
* input: reduce pad state latency for single-sample reads * Restore original disconnected pad handling * input: restore axis smoothing default in FinalizeUpdate Explicitly passing false to GameController::Axis() unconditionally disabled smoothing for all input sources. Controller hardware is unaffected (the <16-unit guard in UpdateAxisSmoothing already skips interpolation for fine-grained inputs), but KBM users rely on it for smooth analog motion (e.g. Bloodborne). Revert to the default smooth=true. * input: correct controller state copyright year
This commit is contained in:
@@ -1188,6 +1188,7 @@ set(IMGUI src/imgui/imgui_config.h
|
||||
|
||||
set(INPUT src/input/controller.cpp
|
||||
src/input/controller.h
|
||||
src/input/controller_state.cpp
|
||||
src/input/input_handler.cpp
|
||||
src/input/input_handler.h
|
||||
src/input/input_mouse.cpp
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
template <class T>
|
||||
@@ -38,8 +41,13 @@ public:
|
||||
return m_storage[m_begin];
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
m_begin = 0;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
u64 m_begin = 0;
|
||||
u64 m_size = 0;
|
||||
std::vector<T> m_storage;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -86,11 +86,8 @@ bool ReadControllerState(Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
if (has_state) {
|
||||
*has_state = false;
|
||||
}
|
||||
Input::State pad_state{};
|
||||
bool connected = false;
|
||||
int connected_count = 0;
|
||||
(*controllers)[index]->ReadState(&pad_state, &connected, &connected_count);
|
||||
if (!connected) {
|
||||
Input::State pad_state = (*controllers)[index]->ReadState();
|
||||
if (!pad_state.connected) {
|
||||
return false;
|
||||
}
|
||||
if (has_state) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "pad.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
namespace Libraries::Pad {
|
||||
@@ -137,10 +138,7 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn
|
||||
if (it == handle_to_controller_map.end()) {
|
||||
return ORBIS_PAD_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
bool connected = false;
|
||||
int connected_count = 0;
|
||||
Input::State state{};
|
||||
it->second->ReadState(&state, &connected, &connected_count);
|
||||
const Input::State state = it->second->ReadState();
|
||||
|
||||
std::memset(pInfo, 0, sizeof(OrbisPadControllerInformation));
|
||||
pInfo->touchPadInfo.pixelDensity = 1;
|
||||
@@ -149,10 +147,10 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn
|
||||
pInfo->stickInfo.deadZoneLeft = 1;
|
||||
pInfo->stickInfo.deadZoneRight = 1;
|
||||
pInfo->connectionType = ORBIS_PAD_CONNECTION_TYPE_LOCAL;
|
||||
pInfo->connectedCount = static_cast<u8>(std::clamp(connected_count, 0, 0xff));
|
||||
pInfo->connectedCount = state.connected_count;
|
||||
pInfo->deviceClass = OrbisPadDeviceClass::Standard;
|
||||
pInfo->connected = connected;
|
||||
if (connected) {
|
||||
pInfo->connected = state.connected;
|
||||
if (state.connected) {
|
||||
pInfo->deviceClass = EmulatorSettings.IsUsingSpecialPad()
|
||||
? (OrbisPadDeviceClass)EmulatorSettings.GetSpecialPadClass()
|
||||
: OrbisPadDeviceClass::Standard;
|
||||
@@ -367,9 +365,8 @@ int PS4_SYSV_ABI scePadOutputReport() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int ProcessStates(s32 handle, OrbisPadData* pData, Input::GameController& controller,
|
||||
Input::State* states, s32 num, bool connected, u32 connected_count) {
|
||||
if (!connected) {
|
||||
int ProcessStates(OrbisPadData* pData, const Input::State* states, s32 num) {
|
||||
if (num > 0 && !states[0].connected) {
|
||||
pData[0] = {};
|
||||
pData[0].orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
pData[0].connected = false;
|
||||
@@ -378,15 +375,15 @@ int ProcessStates(s32 handle, OrbisPadData* pData, Input::GameController& contro
|
||||
|
||||
const bool gamepad_input_intercepted = ImGui::Core::IsGamepadInputCaptured();
|
||||
for (int i = 0; i < num; i++) {
|
||||
pData[i] = {};
|
||||
if (gamepad_input_intercepted) {
|
||||
pData[i] = {};
|
||||
pData[i].buttons = OrbisPadButtonDataOffset::Intercepted;
|
||||
pData[i].leftStick = {128, 128};
|
||||
pData[i].rightStick = {128, 128};
|
||||
pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
pData[i].connected = connected;
|
||||
pData[i].connected = states[i].connected;
|
||||
pData[i].timestamp = states[i].time;
|
||||
pData[i].connectedCount = connected_count;
|
||||
pData[i].connectedCount = states[i].connected_count;
|
||||
pData[i].deviceUniqueDataLen = 0;
|
||||
continue;
|
||||
}
|
||||
@@ -404,65 +401,10 @@ int ProcessStates(s32 handle, OrbisPadData* pData, Input::GameController& contro
|
||||
pData[i].angularVelocity.x = states[i].angularVelocity.x;
|
||||
pData[i].angularVelocity.y = states[i].angularVelocity.y;
|
||||
pData[i].angularVelocity.z = states[i].angularVelocity.z;
|
||||
pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
|
||||
const auto gyro_poll_rate = controller.accel_poll_rate;
|
||||
if (gyro_poll_rate != 0.0f) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
float deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
now - controller.GetLastUpdate())
|
||||
.count() /
|
||||
1000000.0f;
|
||||
controller.SetLastUpdate(now);
|
||||
Libraries::Pad::OrbisFQuaternion lastOrientation = controller.GetLastOrientation();
|
||||
Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
GameControllers::CalculateOrientation(pData->acceleration, pData->angularVelocity,
|
||||
deltaTime, lastOrientation, outputOrientation);
|
||||
pData[i].orientation = outputOrientation;
|
||||
controller.SetLastOrientation(outputOrientation);
|
||||
}
|
||||
pData[i].orientation = states[i].orientation;
|
||||
pData[i].touchData.touchNum =
|
||||
(states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0);
|
||||
|
||||
if (handle == 1) {
|
||||
if (controller.GetTouchCount() >= 127) {
|
||||
controller.SetTouchCount(0);
|
||||
}
|
||||
|
||||
if (controller.GetSecondaryTouchCount() >= 127) {
|
||||
controller.SetSecondaryTouchCount(0);
|
||||
}
|
||||
|
||||
if (pData->touchData.touchNum == 1 && controller.GetPreviousTouchNum() == 0) {
|
||||
controller.SetTouchCount(controller.GetTouchCount() + 1);
|
||||
controller.SetSecondaryTouchCount(controller.GetTouchCount());
|
||||
} else if (pData->touchData.touchNum == 2 && controller.GetPreviousTouchNum() == 1) {
|
||||
controller.SetSecondaryTouchCount(controller.GetSecondaryTouchCount() + 1);
|
||||
} else if (pData->touchData.touchNum == 0 && controller.GetPreviousTouchNum() > 0) {
|
||||
if (controller.GetTouchCount() < controller.GetSecondaryTouchCount()) {
|
||||
controller.SetTouchCount(controller.GetSecondaryTouchCount());
|
||||
} else {
|
||||
if (controller.WasSecondaryTouchReset()) {
|
||||
controller.SetTouchCount(controller.GetSecondaryTouchCount());
|
||||
controller.UnsetSecondaryTouchResetBool();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
controller.SetPreviousTouchNum(pData->touchData.touchNum);
|
||||
|
||||
if (pData->touchData.touchNum == 1) {
|
||||
states[i].touchpad[0].ID = controller.GetTouchCount();
|
||||
states[i].touchpad[1].ID = 0;
|
||||
} else if (pData->touchData.touchNum == 2) {
|
||||
states[i].touchpad[0].ID = controller.GetTouchCount();
|
||||
states[i].touchpad[1].ID = controller.GetSecondaryTouchCount();
|
||||
}
|
||||
} else {
|
||||
states[i].touchpad[0].ID = 1;
|
||||
states[i].touchpad[1].ID = 2;
|
||||
}
|
||||
|
||||
if (!states[i].touchpad[0].state && states[i].touchpad[1].state) {
|
||||
pData[i].touchData.touch[0].x = states[i].touchpad[1].x;
|
||||
pData[i].touchData.touch[0].y = states[i].touchpad[1].y;
|
||||
@@ -476,14 +418,11 @@ int ProcessStates(s32 handle, OrbisPadData* pData, Input::GameController& contro
|
||||
pData[i].touchData.touch[1].id = states[i].touchpad[1].ID;
|
||||
}
|
||||
if (Common::ElfInfo::Instance().FirmwareVer() > Common::ElfInfo::FW_350) {
|
||||
pData[i].touchData.time_since_touch_held_down =
|
||||
controller.last_touch_down_timestamp == 0
|
||||
? 0
|
||||
: states[i].time - controller.last_touch_down_timestamp;
|
||||
pData[i].touchData.time_since_touch_held_down = states[i].touch_time_since_held_down;
|
||||
}
|
||||
pData[i].connected = connected;
|
||||
pData[i].connected = states[i].connected;
|
||||
pData[i].timestamp = states[i].time;
|
||||
pData[i].connectedCount = connected_count;
|
||||
pData[i].connectedCount = states[i].connected_count;
|
||||
pData[i].deviceUniqueDataLen = 0;
|
||||
}
|
||||
|
||||
@@ -492,17 +431,17 @@ int ProcessStates(s32 handle, OrbisPadData* pData, Input::GameController& contro
|
||||
|
||||
int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
|
||||
LOG_TRACE(Lib_Pad, "called");
|
||||
int connected_count = 0;
|
||||
bool connected = false;
|
||||
std::vector<Input::State> states(64);
|
||||
if (pData == nullptr || num < 1 || num > ORBIS_PAD_MAX_DATA_NUM) {
|
||||
return ORBIS_PAD_ERROR_INVALID_ARG;
|
||||
}
|
||||
auto it = handle_to_controller_map.find(handle);
|
||||
if (it == handle_to_controller_map.end()) {
|
||||
return ORBIS_PAD_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
auto& controller = *it->second;
|
||||
int ret_num = controller.ReadStates(states.data(), num, &connected, &connected_count);
|
||||
return ProcessStates(handle, pData, controller, states.data(), ret_num, connected,
|
||||
connected_count);
|
||||
std::array<Input::State, ORBIS_PAD_MAX_DATA_NUM> states;
|
||||
const int ret_num = controller.ReadStates(states.data(), num);
|
||||
return ProcessStates(pData, states.data(), ret_num);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadReadBlasterForTracker() {
|
||||
@@ -527,17 +466,8 @@ int PS4_SYSV_ABI scePadReadHistory() {
|
||||
|
||||
int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
|
||||
LOG_TRACE(Lib_Pad, "handle: {}", handle);
|
||||
auto it = handle_to_controller_map.find(handle);
|
||||
if (it == handle_to_controller_map.end()) {
|
||||
return ORBIS_PAD_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
auto& controller = *it->second;
|
||||
int connected_count = 0;
|
||||
bool connected = false;
|
||||
Input::State state;
|
||||
controller.ReadState(&state, &connected, &connected_count);
|
||||
ProcessStates(handle, pData, controller, &state, 1, connected, connected_count);
|
||||
return ORBIS_OK;
|
||||
const int result = scePadRead(handle, pData, 1);
|
||||
return result < 0 ? result : ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadReadStateExt() {
|
||||
@@ -583,9 +513,7 @@ int PS4_SYSV_ABI scePadResetOrientation(s32 handle) {
|
||||
return ORBIS_PAD_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
auto& controller = *it->second;
|
||||
Libraries::Pad::OrbisFQuaternion defaultOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
controller.SetLastOrientation(defaultOrientation);
|
||||
controller.SetLastUpdate(std::chrono::steady_clock::now());
|
||||
controller.ResetOrientation();
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@@ -816,6 +744,8 @@ int PS4_SYSV_ABI Func_EF103E845B6F0420() {
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
Common::Singleton<GameControllers>::Instance()->TryOpenSDLControllers();
|
||||
|
||||
LIB_FUNCTION("6ncge5+l5Qs", "libScePad", 1, "libScePad", scePadClose);
|
||||
LIB_FUNCTION("kazv1NzSB8c", "libScePad", 1, "libScePad", scePadConnectPort);
|
||||
LIB_FUNCTION("AcslpN1jHR8", "libScePad", 1, "libScePad",
|
||||
|
||||
@@ -14,6 +14,7 @@ class SymbolsResolver;
|
||||
namespace Libraries::Pad {
|
||||
|
||||
constexpr int ORBIS_PAD_MAX_TOUCH_NUM = 2;
|
||||
constexpr int ORBIS_PAD_MAX_DATA_NUM = 64;
|
||||
constexpr int ORBIS_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE = 12;
|
||||
|
||||
constexpr int ORBIS_PAD_PORT_TYPE_STANDARD = 0;
|
||||
|
||||
+176
-239
@@ -1,16 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <common/elf_info.h>
|
||||
#include <common/singleton.h>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "controller.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "core/user_settings.h"
|
||||
#include "input/controller.h"
|
||||
@@ -19,135 +21,195 @@ namespace Input {
|
||||
|
||||
using Libraries::Pad::OrbisPadButtonDataOffset;
|
||||
|
||||
void State::OnButton(OrbisPadButtonDataOffset button, bool isPressed) {
|
||||
if (isPressed) {
|
||||
buttonsState |= button;
|
||||
} else {
|
||||
buttonsState &= ~button;
|
||||
namespace {
|
||||
|
||||
void CalculateOrientation(const Libraries::Pad::OrbisFVector3& angular_velocity, float delta_time,
|
||||
const Libraries::Pad::OrbisFQuaternion& last_orientation,
|
||||
Libraries::Pad::OrbisFQuaternion& orientation) {
|
||||
if (delta_time > 1.0f) {
|
||||
orientation = last_orientation;
|
||||
return;
|
||||
}
|
||||
Libraries::Pad::OrbisFQuaternion q = last_orientation;
|
||||
const Libraries::Pad::OrbisFQuaternion omega = {angular_velocity.x, angular_velocity.y,
|
||||
angular_velocity.z, 0.0f};
|
||||
|
||||
const Libraries::Pad::OrbisFQuaternion q_omega = {
|
||||
q.w * omega.x + q.x * omega.w + q.y * omega.z - q.z * omega.y,
|
||||
q.w * omega.y + q.y * omega.w + q.z * omega.x - q.x * omega.z,
|
||||
q.w * omega.z + q.z * omega.w + q.x * omega.y - q.y * omega.x,
|
||||
q.w * omega.w - q.x * omega.x - q.y * omega.y - q.z * omega.z};
|
||||
|
||||
const Libraries::Pad::OrbisFQuaternion q_dot = {0.5f * q_omega.x, 0.5f * q_omega.y,
|
||||
0.5f * q_omega.z, 0.5f * q_omega.w};
|
||||
|
||||
q.x += q_dot.x * delta_time;
|
||||
q.y += q_dot.y * delta_time;
|
||||
q.z += q_dot.z * delta_time;
|
||||
q.w += q_dot.w * delta_time;
|
||||
|
||||
const float norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
|
||||
q.x /= norm;
|
||||
q.y /= norm;
|
||||
q.z /= norm;
|
||||
q.w /= norm;
|
||||
orientation = q;
|
||||
}
|
||||
|
||||
void State::OnAxis(Axis axis, int value, bool smooth) {
|
||||
auto const i = std::to_underlying(axis);
|
||||
// forcibly finish the previous smoothing task by jumping to the end
|
||||
axes[i] = axis_smoothing_end_values[i];
|
||||
|
||||
axis_smoothing_start_times[i] = time;
|
||||
axis_smoothing_start_values[i] = axes[i];
|
||||
axis_smoothing_end_values[i] = value;
|
||||
axis_smoothing_flags[i] = smooth;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void State::OnTouchpad(int touchIndex, bool isDown, float x, float y) {
|
||||
touchpad[touchIndex].state = isDown;
|
||||
touchpad[touchIndex].x = static_cast<u16>(x * 1920);
|
||||
touchpad[touchIndex].y = static_cast<u16>(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];
|
||||
}
|
||||
|
||||
void State::UpdateAxisSmoothing() {
|
||||
for (int i = 0; i < std::to_underlying(Axis::AxisMax); i++) {
|
||||
// if it's not to be smoothed or close enough, just jump to the end
|
||||
if (!axis_smoothing_flags[i] || std::abs(axes[i] - axis_smoothing_end_values[i]) < 16) {
|
||||
if (axes[i] != axis_smoothing_end_values[i]) {
|
||||
axes[i] = axis_smoothing_end_values[i];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
auto now = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
f32 t =
|
||||
std::clamp((now - axis_smoothing_start_times[i]) / f32{axis_smoothing_time}, 0.f, 1.f);
|
||||
axes[i] = s32(axis_smoothing_start_values[i] * (1 - t) + axis_smoothing_end_values[i] * t);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
GameController::GameController() : m_states_queue(64) {}
|
||||
|
||||
void GameController::ReadState(State* state, bool* isConnected, int* connectedCount) {
|
||||
*isConnected = m_connected;
|
||||
*connectedCount = m_connected_count;
|
||||
*state = m_state;
|
||||
State GameController::ReadState() {
|
||||
std::lock_guard lock{m_state_mutex};
|
||||
return m_state;
|
||||
}
|
||||
|
||||
int GameController::ReadStates(State* states, int states_num, bool* isConnected,
|
||||
int* connectedCount) {
|
||||
*isConnected = m_connected;
|
||||
*connectedCount = m_connected_count;
|
||||
|
||||
int ret_num = 0;
|
||||
if (m_connected) {
|
||||
std::lock_guard lg(m_states_queue_mutex);
|
||||
for (int i = 0; i < states_num; i++) {
|
||||
auto o_state = m_states_queue.Pop();
|
||||
if (!o_state) {
|
||||
break;
|
||||
}
|
||||
states[ret_num++] = *o_state;
|
||||
}
|
||||
int GameController::ReadStates(State* states, int states_num) {
|
||||
std::lock_guard lock{m_state_mutex};
|
||||
if (states_num <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return ret_num;
|
||||
|
||||
if (!m_state.connected) {
|
||||
states[0] = m_state;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (states_num == 1) {
|
||||
// Retained history can make a later multi-sample read return up to 64 stale reports, so
|
||||
// mixed single- and multi-sample reads require dedicated tests.
|
||||
states[0] = m_state;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_count = 0;
|
||||
while (read_count < states_num) {
|
||||
auto state = m_states_queue.Pop();
|
||||
if (!state) {
|
||||
break;
|
||||
}
|
||||
states[read_count++] = std::move(*state);
|
||||
}
|
||||
return read_count;
|
||||
}
|
||||
|
||||
void GameController::Button(OrbisPadButtonDataOffset button, bool is_pressed) {
|
||||
std::lock_guard lock{m_state_mutex};
|
||||
m_state.OnButton(button, is_pressed);
|
||||
PushState();
|
||||
PushStateLocked();
|
||||
}
|
||||
|
||||
void GameController::Axis(Input::Axis axis, int value, bool smooth) {
|
||||
m_state.OnAxis(axis, value, smooth);
|
||||
PushState();
|
||||
}
|
||||
|
||||
void GameController::Gyro(int id) {
|
||||
m_state.OnGyro(gyro_buf);
|
||||
PushState();
|
||||
}
|
||||
|
||||
void GameController::Acceleration(int id) {
|
||||
m_state.OnAccel(accel_buf);
|
||||
PushState();
|
||||
std::lock_guard lock{m_state_mutex};
|
||||
const u64 timestamp = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
m_state.OnAxis(axis, value, timestamp, smooth);
|
||||
PushStateLocked(timestamp);
|
||||
}
|
||||
|
||||
void GameController::UpdateGyro(const float gyro[3]) {
|
||||
std::scoped_lock l(m_states_queue_mutex);
|
||||
std::lock_guard lock{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::lock_guard lock{m_state_mutex};
|
||||
std::memcpy(accel_buf, acceleration, sizeof(accel_buf));
|
||||
}
|
||||
|
||||
void GameController::UpdateAxisSmoothing() {
|
||||
m_state.UpdateAxisSmoothing();
|
||||
void GameController::PollState() {
|
||||
std::lock_guard lock{m_state_mutex};
|
||||
PushStateLocked();
|
||||
}
|
||||
|
||||
void GameController::ResetOrientation() {
|
||||
std::lock_guard lock{m_state_mutex};
|
||||
m_state.orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
m_last_orientation_update = 0;
|
||||
PushStateLocked();
|
||||
}
|
||||
|
||||
void GameController::SetTouchpadState(int touch_index, bool touch_down, float x, float y) {
|
||||
if (touch_index < 0 || touch_index >= 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock{m_state_mutex};
|
||||
const u64 timestamp = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
const bool was_pressed = m_state.touchpad[0].state || m_state.touchpad[1].state;
|
||||
auto& touch = m_state.touchpad[touch_index];
|
||||
if (touch_down && !touch.state) {
|
||||
touch.ID = m_next_touch_id;
|
||||
m_next_touch_id = m_next_touch_id == 127 ? 1 : m_next_touch_id + 1;
|
||||
}
|
||||
m_state.OnTouchpad(touch_index, touch_down, x, y);
|
||||
const bool is_pressed = m_state.touchpad[0].state || m_state.touchpad[1].state;
|
||||
if (!was_pressed && is_pressed) {
|
||||
m_touch_down_timestamp = timestamp;
|
||||
} else if (was_pressed && !is_pressed) {
|
||||
m_touch_down_timestamp = 0;
|
||||
}
|
||||
PushStateLocked(timestamp);
|
||||
}
|
||||
|
||||
void GameController::ConnectController(SDL_Gamepad* pad) {
|
||||
std::lock_guard lock{m_state_mutex};
|
||||
m_sdl_gamepad = pad;
|
||||
m_states_queue.Clear();
|
||||
if (!m_state.connected) {
|
||||
++m_state.connected_count;
|
||||
if (m_state.connected_count == 0) {
|
||||
m_state.connected_count = 1;
|
||||
}
|
||||
}
|
||||
m_state.connected = true;
|
||||
m_last_orientation_update = 0;
|
||||
PushStateLocked();
|
||||
}
|
||||
|
||||
void GameController::DisconnectController() {
|
||||
std::lock_guard lock{m_state_mutex};
|
||||
m_states_queue.Clear();
|
||||
m_sdl_gamepad = nullptr;
|
||||
|
||||
const u8 connected_count = m_state.connected_count;
|
||||
m_state = {};
|
||||
m_state.connected_count = connected_count;
|
||||
std::fill(gyro_buf, gyro_buf + 3, 0.0f);
|
||||
std::fill(accel_buf, accel_buf + 3, 0.0f);
|
||||
accel_buf[1] = 9.81f;
|
||||
m_next_touch_id = 1;
|
||||
m_touch_down_timestamp = 0;
|
||||
m_state.connected = false;
|
||||
m_last_orientation_update = 0;
|
||||
PushStateLocked();
|
||||
}
|
||||
|
||||
void GameController::UpdateOrientationLocked(u64 timestamp) {
|
||||
if (m_last_orientation_update == 0 || timestamp <= m_last_orientation_update) {
|
||||
m_last_orientation_update = timestamp;
|
||||
return;
|
||||
}
|
||||
const float delta_time =
|
||||
static_cast<float>(timestamp - m_last_orientation_update) / 1'000'000.f;
|
||||
Libraries::Pad::OrbisFQuaternion orientation{};
|
||||
CalculateOrientation(m_state.angularVelocity, delta_time, m_state.orientation, orientation);
|
||||
m_state.orientation = orientation;
|
||||
m_last_orientation_update = timestamp;
|
||||
}
|
||||
|
||||
void GameController::PushStateLocked(u64 timestamp) {
|
||||
if (timestamp == 0) {
|
||||
timestamp = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
}
|
||||
m_state.UpdateAxisSmoothing(timestamp);
|
||||
m_state.OnGyro(gyro_buf);
|
||||
m_state.OnAccel(accel_buf);
|
||||
UpdateOrientationLocked(timestamp);
|
||||
m_state.time = timestamp;
|
||||
m_state.touch_time_since_held_down =
|
||||
m_touch_down_timestamp == 0 ? 0 : timestamp - m_touch_down_timestamp;
|
||||
m_states_queue.Push(m_state);
|
||||
}
|
||||
|
||||
void GameController::SetLightBarRGB(u8 const r, u8 const g, u8 const b) {
|
||||
@@ -198,77 +260,11 @@ bool GameController::SetVibration(u8 smallMotor, u8 largeMotor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, float y) {
|
||||
if (touchIndex < 2) {
|
||||
bool was_pressed = m_state.touchpad[0].state || m_state.touchpad[1].state;
|
||||
m_state.OnTouchpad(touchIndex, touchDown, x, y);
|
||||
PushState();
|
||||
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) {
|
||||
last_touch_down_timestamp = m_state.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameControllers::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
|
||||
Libraries::Pad::OrbisFVector3& angularVelocity,
|
||||
float deltaTime,
|
||||
Libraries::Pad::OrbisFQuaternion& lastOrientation,
|
||||
Libraries::Pad::OrbisFQuaternion& orientation) {
|
||||
// avoid wildly off values coming from elapsed time between two samples
|
||||
// being too high, such as on the first time the controller is polled
|
||||
if (deltaTime > 1.0f) {
|
||||
orientation = lastOrientation;
|
||||
return;
|
||||
}
|
||||
Libraries::Pad::OrbisFQuaternion q = lastOrientation;
|
||||
Libraries::Pad::OrbisFQuaternion ω = {angularVelocity.x, angularVelocity.y, angularVelocity.z,
|
||||
0.0f};
|
||||
|
||||
Libraries::Pad::OrbisFQuaternion qω = {q.w * ω.x + q.x * ω.w + q.y * ω.z - q.z * ω.y,
|
||||
q.w * ω.y + q.y * ω.w + q.z * ω.x - q.x * ω.z,
|
||||
q.w * ω.z + q.z * ω.w + q.x * ω.y - q.y * ω.x,
|
||||
q.w * ω.w - q.x * ω.x - q.y * ω.y - q.z * ω.z};
|
||||
|
||||
Libraries::Pad::OrbisFQuaternion qDot = {0.5f * qω.x, 0.5f * qω.y, 0.5f * qω.z, 0.5f * qω.w};
|
||||
|
||||
q.x += qDot.x * deltaTime;
|
||||
q.y += qDot.y * deltaTime;
|
||||
q.z += qDot.z * deltaTime;
|
||||
q.w += qDot.w * deltaTime;
|
||||
|
||||
float norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
|
||||
q.x /= norm;
|
||||
q.y /= norm;
|
||||
q.z /= norm;
|
||||
q.w /= norm;
|
||||
|
||||
orientation.x = q.x;
|
||||
orientation.y = q.y;
|
||||
orientation.z = q.z;
|
||||
orientation.w = q.w;
|
||||
LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x,
|
||||
orientation.y, orientation.z, orientation.w);
|
||||
}
|
||||
|
||||
void GameController::ConnectController(SDL_Gamepad* pad) {
|
||||
m_sdl_gamepad = pad;
|
||||
m_connected_count = 1;
|
||||
m_connected = true;
|
||||
}
|
||||
void GameController::DisconnectController() {
|
||||
m_sdl_gamepad = nullptr;
|
||||
m_connected_count = 0;
|
||||
m_connected = false;
|
||||
}
|
||||
|
||||
bool is_first_check = true;
|
||||
static bool is_first_check = true;
|
||||
|
||||
void GameControllers::TryOpenSDLControllers() {
|
||||
using namespace Libraries::UserService;
|
||||
int controller_count;
|
||||
s32 move_count = 0;
|
||||
SDL_JoystickID* new_joysticks = SDL_GetGamepads(&controller_count);
|
||||
LOG_INFO(Input, "{} controllers are currently connected", controller_count);
|
||||
|
||||
@@ -280,7 +276,6 @@ void GameControllers::TryOpenSDLControllers() {
|
||||
if (pad) {
|
||||
SDL_JoystickID id = SDL_GetGamepadID(pad);
|
||||
bool still_connected = false;
|
||||
ControllerType type = ControllerType::Standard;
|
||||
for (int j = 0; j < controller_count; j++) {
|
||||
if (new_joysticks[j] == id) {
|
||||
still_connected = true;
|
||||
@@ -290,7 +285,6 @@ void GameControllers::TryOpenSDLControllers() {
|
||||
}
|
||||
}
|
||||
if (!still_connected) {
|
||||
auto u = UserManagement.GetUserByID(controllers[i]->user_id);
|
||||
SDL_CloseGamepad(pad);
|
||||
controllers[i]->DisconnectController();
|
||||
controllers[i]->user_id = -1;
|
||||
@@ -326,17 +320,17 @@ void GameControllers::TryOpenSDLControllers() {
|
||||
c->ConnectController(pad);
|
||||
if (EmulatorSettings.IsMotionControlsEnabled()) {
|
||||
if (SDL_SetGamepadSensorEnabled(c->m_sdl_gamepad, SDL_SENSOR_GYRO, true)) {
|
||||
c->gyro_poll_rate =
|
||||
const float poll_rate =
|
||||
SDL_GetGamepadSensorDataRate(c->m_sdl_gamepad, SDL_SENSOR_GYRO);
|
||||
LOG_INFO(Input, "Gyro initialized, poll rate: {}", c->gyro_poll_rate);
|
||||
LOG_INFO(Input, "Gyro initialized, poll rate: {}", poll_rate);
|
||||
} else {
|
||||
LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad {}",
|
||||
c->user_id);
|
||||
}
|
||||
if (SDL_SetGamepadSensorEnabled(c->m_sdl_gamepad, SDL_SENSOR_ACCEL, true)) {
|
||||
c->accel_poll_rate =
|
||||
const float poll_rate =
|
||||
SDL_GetGamepadSensorDataRate(c->m_sdl_gamepad, SDL_SENSOR_ACCEL);
|
||||
LOG_INFO(Input, "Accel initialized, poll rate: {}", c->accel_poll_rate);
|
||||
LOG_INFO(Input, "Accel initialized, poll rate: {}", poll_rate);
|
||||
} else {
|
||||
LOG_ERROR(Input, "Failed to initialize accel controls for gamepad {}",
|
||||
c->user_id);
|
||||
@@ -348,7 +342,7 @@ void GameControllers::TryOpenSDLControllers() {
|
||||
}
|
||||
if (is_first_check) [[unlikely]] {
|
||||
is_first_check = false;
|
||||
if (controller_count - move_count == 0) {
|
||||
if (controller_count == 0) {
|
||||
auto u = UserManagement.GetUserByPlayerIndex(1);
|
||||
controllers[0]->user_id = u->user_id;
|
||||
controllers[0]->ConnectController(nullptr);
|
||||
@@ -357,63 +351,6 @@ void GameControllers::TryOpenSDLControllers() {
|
||||
}
|
||||
SDL_free(new_joysticks);
|
||||
}
|
||||
u8 GameController::GetTouchCount() {
|
||||
return m_touch_count;
|
||||
}
|
||||
|
||||
void GameController::SetTouchCount(u8 touchCount) {
|
||||
m_touch_count = touchCount;
|
||||
}
|
||||
|
||||
u8 GameController::GetSecondaryTouchCount() {
|
||||
return m_secondary_touch_count;
|
||||
}
|
||||
|
||||
void GameController::SetSecondaryTouchCount(u8 touchCount) {
|
||||
m_secondary_touch_count = touchCount;
|
||||
if (touchCount == 0) {
|
||||
m_was_secondary_reset = true;
|
||||
}
|
||||
}
|
||||
|
||||
u8 GameController::GetPreviousTouchNum() {
|
||||
return m_previous_touchnum;
|
||||
}
|
||||
|
||||
void GameController::SetPreviousTouchNum(u8 touchNum) {
|
||||
m_previous_touchnum = touchNum;
|
||||
}
|
||||
|
||||
bool GameController::WasSecondaryTouchReset() {
|
||||
return m_was_secondary_reset;
|
||||
}
|
||||
|
||||
void GameController::UnsetSecondaryTouchResetBool() {
|
||||
m_was_secondary_reset = false;
|
||||
}
|
||||
|
||||
void GameController::SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation) {
|
||||
m_orientation = orientation;
|
||||
}
|
||||
|
||||
Libraries::Pad::OrbisFQuaternion GameController::GetLastOrientation() {
|
||||
return m_orientation;
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::time_point GameController::GetLastUpdate() {
|
||||
return m_last_update;
|
||||
}
|
||||
|
||||
void GameController::SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate) {
|
||||
m_last_update = lastUpdate;
|
||||
}
|
||||
|
||||
void GameController::PushState() {
|
||||
std::lock_guard lg(m_states_queue_mutex);
|
||||
m_state.time = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
m_states_queue.Push(m_state);
|
||||
}
|
||||
|
||||
u8 GameControllers::GetGamepadIndexFromJoystickId(SDL_JoystickID id) {
|
||||
auto g = SDL_GetGamepadFromID(id);
|
||||
ASSERT(g != nullptr);
|
||||
|
||||
+19
-47
@@ -3,9 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
#include "SDL3/SDL_joystick.h"
|
||||
@@ -14,14 +15,8 @@
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
|
||||
struct SDL_Gamepad;
|
||||
|
||||
namespace Input {
|
||||
|
||||
enum class ControllerType {
|
||||
Standard,
|
||||
};
|
||||
|
||||
enum class Axis {
|
||||
LeftX = 0,
|
||||
LeftY = 1,
|
||||
@@ -63,19 +58,22 @@ private:
|
||||
|
||||
public:
|
||||
void OnButton(Libraries::Pad::OrbisPadButtonDataOffset, bool);
|
||||
void OnAxis(Axis, int, bool smooth = true);
|
||||
void OnAxis(Axis, int, u64 timestamp, 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();
|
||||
void UpdateAxisSmoothing(u64 timestamp);
|
||||
|
||||
Libraries::Pad::OrbisPadButtonDataOffset buttonsState{};
|
||||
u64 time = 0;
|
||||
AxisArray<s32> axes{axis_defaults};
|
||||
TouchpadEntry touchpad[2] = {{false, 0, 0}, {false, 0, 0}};
|
||||
TouchpadEntry touchpad[2]{};
|
||||
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};
|
||||
u64 touch_time_since_held_down{};
|
||||
bool connected{};
|
||||
u8 connected_count{};
|
||||
};
|
||||
|
||||
inline int GetAxis(int min, int max, int value) {
|
||||
@@ -92,16 +90,15 @@ public:
|
||||
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);
|
||||
State ReadState();
|
||||
int ReadStates(State* states, int states_num);
|
||||
|
||||
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 PollState();
|
||||
void ResetOrientation();
|
||||
void SetLightBarRGB(u8 const r, u8 const g, u8 const b);
|
||||
void SetLightBarRGB(Colour const c);
|
||||
Colour GetLightBarRGB();
|
||||
@@ -109,44 +106,24 @@ public:
|
||||
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};
|
||||
s32 user_id = Libraries::UserService::ORBIS_USER_SERVICE_USER_ID_INVALID;
|
||||
SDL_Gamepad* m_sdl_gamepad = nullptr;
|
||||
u64 last_touch_down_timestamp = 0;
|
||||
|
||||
private:
|
||||
void PushState();
|
||||
// m_state_mutex must be held by the caller.
|
||||
void PushStateLocked(u64 timestamp = 0);
|
||||
void UpdateOrientationLocked(u64 timestamp);
|
||||
|
||||
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;
|
||||
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 m_next_touch_id{1};
|
||||
u64 m_touch_down_timestamp{};
|
||||
u64 m_last_orientation_update{};
|
||||
Colour colour;
|
||||
std::optional<Colour> override_colour{};
|
||||
|
||||
State m_state;
|
||||
|
||||
std::mutex m_states_queue_mutex;
|
||||
std::mutex m_state_mutex;
|
||||
RingBufferQueue<State> m_states_queue;
|
||||
};
|
||||
|
||||
@@ -169,11 +146,6 @@ public:
|
||||
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);
|
||||
void SetControllerCustomColor(s32 i, u8 r, u8 g, u8 b) {
|
||||
// reset to ensure the next function always runs, even if there already was a preexisting
|
||||
// override colour before
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "input/controller.h"
|
||||
|
||||
namespace Input {
|
||||
|
||||
using Libraries::Pad::OrbisPadButtonDataOffset;
|
||||
|
||||
void State::OnButton(OrbisPadButtonDataOffset button, bool is_pressed) {
|
||||
if (is_pressed) {
|
||||
buttonsState |= button;
|
||||
} else {
|
||||
buttonsState &= ~button;
|
||||
}
|
||||
}
|
||||
|
||||
void State::OnAxis(Axis axis, int value, u64 timestamp, bool smooth) {
|
||||
const auto index = std::to_underlying(axis);
|
||||
axes[index] = axis_smoothing_end_values[index];
|
||||
|
||||
axis_smoothing_start_times[index] = timestamp;
|
||||
axis_smoothing_start_values[index] = axes[index];
|
||||
axis_smoothing_end_values[index] = value;
|
||||
axis_smoothing_flags[index] = smooth;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void State::OnTouchpad(int touch_index, bool is_down, float x, float y) {
|
||||
touchpad[touch_index].state = is_down;
|
||||
touchpad[touch_index].x = static_cast<u16>(x * 1920);
|
||||
touchpad[touch_index].y = static_cast<u16>(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];
|
||||
}
|
||||
|
||||
void State::UpdateAxisSmoothing(u64 timestamp) {
|
||||
for (int i = 0; i < std::to_underlying(Axis::AxisMax); ++i) {
|
||||
if (!axis_smoothing_flags[i] || std::abs(axes[i] - axis_smoothing_end_values[i]) < 16) {
|
||||
axes[i] = axis_smoothing_end_values[i];
|
||||
continue;
|
||||
}
|
||||
const f32 t = std::clamp(
|
||||
(timestamp - axis_smoothing_start_times[i]) / f32{axis_smoothing_time}, 0.f, 1.f);
|
||||
axes[i] = s32(axis_smoothing_start_values[i] * (1 - t) + axis_smoothing_end_values[i] * t);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Input
|
||||
+1
-4
@@ -83,9 +83,7 @@ static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) {
|
||||
|
||||
static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint32 interval) {
|
||||
auto* controller = reinterpret_cast<Input::GameController*>(userdata);
|
||||
controller->UpdateAxisSmoothing();
|
||||
controller->Gyro(0);
|
||||
controller->Acceleration(0);
|
||||
controller->PollState();
|
||||
return interval;
|
||||
}
|
||||
|
||||
@@ -181,7 +179,6 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameControllers* controller
|
||||
// input handler init-s
|
||||
Input::ControllerOutput::LinkJoystickAxes();
|
||||
Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial()));
|
||||
controllers.TryOpenSDLControllers();
|
||||
|
||||
if (EmulatorSettings.IsBackgroundControllerInput()) {
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||
|
||||
Reference in New Issue
Block a user