mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-04-19 19:14:48 +00:00
Merge 72f233486d
into 69777e2ffa
This commit is contained in:
commit
0cdec9cabb
13 changed files with 552 additions and 506 deletions
|
@ -42,7 +42,9 @@ static u32 screenHeight = 720;
|
|||
static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select
|
||||
static std::string logFilter;
|
||||
static std::string logType = "sync";
|
||||
static std::string userName = "shadPS4";
|
||||
static std::array<std::string, 4> userNames = {"shadPS4"
|
||||
"shadps4-2",
|
||||
"shadPS4-3", "shadPS4-4"};
|
||||
static std::string updateChannel;
|
||||
static std::string chooseHomeTab;
|
||||
static std::string backButtonBehavior = "left";
|
||||
|
@ -236,7 +238,7 @@ std::string getLogType() {
|
|||
}
|
||||
|
||||
std::string getUserName() {
|
||||
return userName;
|
||||
return userNames[0];
|
||||
}
|
||||
|
||||
std::string getUpdateChannel() {
|
||||
|
@ -486,7 +488,7 @@ void setSeparateLogFilesEnabled(bool enabled) {
|
|||
}
|
||||
|
||||
void setUserName(const std::string& type) {
|
||||
userName = type;
|
||||
userNames[0] = type;
|
||||
}
|
||||
|
||||
void setUpdateChannel(const std::string& type) {
|
||||
|
@ -762,7 +764,7 @@ void load(const std::filesystem::path& path) {
|
|||
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
|
||||
logFilter = toml::find_or<std::string>(general, "logFilter", "");
|
||||
logType = toml::find_or<std::string>(general, "logType", "sync");
|
||||
userName = toml::find_or<std::string>(general, "userName", "shadPS4");
|
||||
userNames[0] = toml::find_or<std::string>(general, "userName", "shadPS4");
|
||||
if (Common::g_is_release) {
|
||||
updateChannel = toml::find_or<std::string>(general, "updateChannel", "Release");
|
||||
} else {
|
||||
|
@ -960,7 +962,7 @@ void save(const std::filesystem::path& path) {
|
|||
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
|
||||
data["General"]["logFilter"] = logFilter;
|
||||
data["General"]["logType"] = logType;
|
||||
data["General"]["userName"] = userName;
|
||||
data["General"]["userName"] = userNames[0];
|
||||
data["General"]["updateChannel"] = updateChannel;
|
||||
data["General"]["chooseHomeTab"] = chooseHomeTab;
|
||||
data["General"]["showSplash"] = isShowSplash;
|
||||
|
@ -1107,7 +1109,7 @@ void setDefaultValues() {
|
|||
screenHeight = 720;
|
||||
logFilter = "";
|
||||
logType = "sync";
|
||||
userName = "shadPS4";
|
||||
userNames = {"shadPS4", "shadps4-2", "shadPS4-3", "shadPS4-4"};
|
||||
if (Common::g_is_release) {
|
||||
updateChannel = "Release";
|
||||
} else {
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
namespace Libraries::Pad {
|
||||
|
||||
using Input::GameController;
|
||||
|
||||
int PS4_SYSV_ABI scePadClose(s32 handle) {
|
||||
LOG_ERROR(Lib_Pad, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
|
@ -101,7 +99,7 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn
|
|||
pInfo->connectedCount = 1;
|
||||
pInfo->connected = false;
|
||||
pInfo->deviceClass = OrbisPadDeviceClass::Standard;
|
||||
return ORBIS_OK;
|
||||
return 0;
|
||||
}
|
||||
pInfo->touchPadInfo.pixelDensity = 1;
|
||||
pInfo->touchPadInfo.resolution.x = 1920;
|
||||
|
@ -116,7 +114,7 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn
|
|||
pInfo->connectionType = ORBIS_PAD_PORT_TYPE_SPECIAL;
|
||||
pInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass();
|
||||
}
|
||||
return ORBIS_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadGetDataInternal() {
|
||||
|
@ -262,7 +260,9 @@ int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenP
|
|||
}
|
||||
LOG_INFO(Lib_Pad, "(DUMMY) called user_id = {} type = {} index = {}", userId, type, index);
|
||||
scePadResetLightBar(1);
|
||||
return 1; // dummy
|
||||
return userId;
|
||||
// todo: using userId as handle works and simplifies some logic,
|
||||
// but it's not supposed to be used this way
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadOpenExt(s32 userId, s32 type, s32 index,
|
||||
|
@ -275,7 +275,7 @@ int PS4_SYSV_ABI scePadOpenExt(s32 userId, s32 type, s32 index,
|
|||
if (type != ORBIS_PAD_PORT_TYPE_STANDARD && type != ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL)
|
||||
return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
return 1; // dummy
|
||||
return userId; // dummy
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadOpenExt2() {
|
||||
|
@ -290,12 +290,12 @@ int PS4_SYSV_ABI scePadOutputReport() {
|
|||
|
||||
int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
|
||||
LOG_TRACE(Lib_Pad, "called");
|
||||
LOG_DEBUG(Lib_Pad, "handle: {}", handle);
|
||||
int connected_count = 0;
|
||||
bool connected = false;
|
||||
Input::State states[64];
|
||||
auto* controller = Common::Singleton<GameController>::Instance();
|
||||
const auto* engine = controller->GetEngine();
|
||||
int ret_num = controller->ReadStates(states, num, &connected, &connected_count);
|
||||
auto controllers = *Common::Singleton<Input::GameControllers>::Instance();
|
||||
int ret_num = controllers[handle - 1]->ReadStates(states, num, &connected, &connected_count);
|
||||
|
||||
if (!connected) {
|
||||
ret_num = 1;
|
||||
|
@ -315,15 +315,9 @@ 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;
|
||||
pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
if (engine) {
|
||||
const auto gyro_poll_rate = engine->GetAccelPollRate();
|
||||
if (gyro_poll_rate != 0.0f) {
|
||||
GameController::CalculateOrientation(pData[i].acceleration,
|
||||
pData[i].angularVelocity,
|
||||
1.0f / gyro_poll_rate, pData[i].orientation);
|
||||
}
|
||||
}
|
||||
Input::GameController::CalculateOrientation(pData[i].acceleration, pData[i].angularVelocity,
|
||||
1.0f / controllers[handle - 1]->gyro_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;
|
||||
|
@ -363,15 +357,15 @@ int PS4_SYSV_ABI scePadReadHistory() {
|
|||
|
||||
int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
|
||||
LOG_TRACE(Lib_Pad, "called");
|
||||
LOG_DEBUG(Lib_Pad, "handle: {}", handle);
|
||||
if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) {
|
||||
return ORBIS_PAD_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
auto* controller = Common::Singleton<GameController>::Instance();
|
||||
const auto* engine = controller->GetEngine();
|
||||
auto controllers = *Common::Singleton<Input::GameControllers>::Instance();
|
||||
int connectedCount = 0;
|
||||
bool isConnected = false;
|
||||
Input::State state;
|
||||
controller->ReadState(&state, &isConnected, &connectedCount);
|
||||
controllers[handle - 1]->ReadState(&state, &isConnected, &connectedCount);
|
||||
pData->buttons = state.buttonsState;
|
||||
pData->leftStick.x = state.axes[static_cast<int>(Input::Axis::LeftX)];
|
||||
pData->leftStick.y = state.axes[static_cast<int>(Input::Axis::LeftY)];
|
||||
|
@ -385,14 +379,9 @@ 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;
|
||||
pData->orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
if (engine) {
|
||||
const auto gyro_poll_rate = engine->GetAccelPollRate();
|
||||
if (gyro_poll_rate != 0.0f) {
|
||||
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
|
||||
1.0f / gyro_poll_rate, pData->orientation);
|
||||
}
|
||||
}
|
||||
Input::GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
|
||||
1.0f / controllers[handle - 1]->gyro_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;
|
||||
|
@ -419,9 +408,9 @@ int PS4_SYSV_ABI scePadResetLightBar(s32 handle) {
|
|||
if (handle != 1) {
|
||||
return ORBIS_PAD_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
auto* controller = Common::Singleton<GameController>::Instance();
|
||||
auto controllers = *Common::Singleton<Input::GameControllers>::Instance();
|
||||
int* rgb = Config::GetControllerCustomColor();
|
||||
controller->SetLightBarRGB(rgb[0], rgb[1], rgb[2]);
|
||||
controllers[0]->SetLightBarRGB(rgb[0], rgb[1], rgb[2]);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -493,7 +482,7 @@ int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pPar
|
|||
return ORBIS_PAD_ERROR_INVALID_LIGHTBAR_SETTING;
|
||||
}
|
||||
|
||||
auto* controller = Common::Singleton<GameController>::Instance();
|
||||
auto* controller = Common::Singleton<Input::GameController>::Instance();
|
||||
controller->SetLightBarRGB(pParam->r, pParam->g, pParam->b);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
@ -561,7 +550,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<GameController>::Instance();
|
||||
auto* controller = Common::Singleton<Input::GameController>::Instance();
|
||||
controller->SetVibration(pParam->smallMotor, pParam->largeMotor);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#include "common/singleton.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "core/libraries/system/userservice_error.h"
|
||||
#include "core/tls.h"
|
||||
#include "input/controller.h"
|
||||
|
||||
namespace Libraries::UserService {
|
||||
|
||||
|
@ -105,15 +110,23 @@ int PS4_SYSV_ABI sceUserServiceGetDiscPlayerFlag() {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
std::queue<OrbisUserServiceEvent> user_service_event_queue = {};
|
||||
|
||||
void AddUserServiceEvent(const OrbisUserServiceEvent e) {
|
||||
LOG_DEBUG(Lib_UserService, "Event added to queue: {} {}", (u8)e.event, e.userId);
|
||||
user_service_event_queue.push(e);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceUserServiceGetEvent(OrbisUserServiceEvent* event) {
|
||||
LOG_TRACE(Lib_UserService, "(DUMMY) called");
|
||||
// fake a loggin event
|
||||
static bool logged_in = false;
|
||||
|
||||
if (!logged_in) {
|
||||
logged_in = true;
|
||||
event->event = OrbisUserServiceEventType::Login;
|
||||
event->userId = 1;
|
||||
if (!user_service_event_queue.empty()) {
|
||||
OrbisUserServiceEvent& temp = user_service_event_queue.front();
|
||||
event->event = temp.event;
|
||||
event->userId = temp.userId;
|
||||
user_service_event_queue.pop();
|
||||
LOG_INFO(Lib_UserService, "Event processed by the game: {} {}", (u8)temp.event,
|
||||
temp.userId);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -573,11 +586,10 @@ s32 PS4_SYSV_ABI sceUserServiceGetLoginUserIdList(OrbisUserServiceLoginUserIdLis
|
|||
return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
// TODO only first user, do the others as well
|
||||
userIdList->user_id[0] = 1;
|
||||
userIdList->user_id[1] = ORBIS_USER_SERVICE_USER_ID_INVALID;
|
||||
userIdList->user_id[2] = ORBIS_USER_SERVICE_USER_ID_INVALID;
|
||||
userIdList->user_id[3] = ORBIS_USER_SERVICE_USER_ID_INVALID;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto controllers = *Common::Singleton<Input::GameControllers>::Instance();
|
||||
userIdList->user_id[i] = controllers[i]->user_id;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -1048,7 +1060,7 @@ s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, OrbisUserServiceUserCol
|
|||
LOG_ERROR(Lib_UserService, "color is null");
|
||||
return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
*color = OrbisUserServiceUserColor::Blue;
|
||||
*color = (OrbisUserServiceUserColor)(user_id - 1);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -1078,7 +1090,12 @@ s32 PS4_SYSV_ABI sceUserServiceGetUserName(int user_id, char* user_name, std::si
|
|||
LOG_ERROR(Lib_UserService, "buffer is too short");
|
||||
return ORBIS_USER_SERVICE_ERROR_BUFFER_TOO_SHORT;
|
||||
}
|
||||
snprintf(user_name, size, "%s", name.c_str());
|
||||
if (user_id == 1) {
|
||||
snprintf(user_name, size, "%s", name.c_str());
|
||||
} else {
|
||||
snprintf(user_name, size, "%s - %d", name.c_str(), user_id);
|
||||
// todo add list of names to config
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,8 @@ struct OrbisUserServiceEvent {
|
|||
OrbisUserServiceUserId userId;
|
||||
};
|
||||
|
||||
void AddUserServiceEvent(const OrbisUserServiceEvent e);
|
||||
|
||||
int PS4_SYSV_ABI sceUserServiceInitializeForShellCore();
|
||||
int PS4_SYSV_ABI sceUserServiceTerminateForShellCore();
|
||||
int PS4_SYSV_ABI sceUserServiceDestroyUser();
|
||||
|
|
|
@ -53,7 +53,7 @@ Emulator::Emulator() {
|
|||
|
||||
// Defer until after logging is initialized.
|
||||
memory = Core::Memory::Instance();
|
||||
controller = Common::Singleton<Input::GameController>::Instance();
|
||||
controllers = Common::Singleton<Input::GameControllers>::Instance();
|
||||
linker = Common::Singleton<Core::Linker>::Instance();
|
||||
|
||||
// Load renderdoc module.
|
||||
|
@ -215,7 +215,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
|
|||
}
|
||||
}
|
||||
window = std::make_unique<Frontend::WindowSDL>(
|
||||
Config::getScreenWidth(), Config::getScreenHeight(), controller, window_title);
|
||||
Config::getScreenWidth(), Config::getScreenHeight(), controllers, window_title);
|
||||
|
||||
g_window = window.get();
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ private:
|
|||
void LoadSystemModules(const std::string& game_serial);
|
||||
|
||||
Core::MemoryManager* memory;
|
||||
Input::GameController* controller;
|
||||
Input::GameControllers* controllers;
|
||||
Core::Linker* linker;
|
||||
std::unique_ptr<Frontend::WindowSDL> window;
|
||||
std::chrono::steady_clock::time_point start_time;
|
||||
|
|
|
@ -1,64 +1,17 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <unordered_set>
|
||||
#include <SDL3/SDL.h>
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "input/controller.h"
|
||||
|
||||
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<int>(axis)] = value;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
GameController::GameController() {
|
||||
m_states_num = 0;
|
||||
m_last_state = State();
|
||||
|
@ -124,22 +77,45 @@ void GameController::AddState(const State& state) {
|
|||
m_states_num++;
|
||||
}
|
||||
|
||||
void GameController::CheckButton(int id, OrbisPadButtonDataOffset button, bool is_pressed) {
|
||||
void GameController::CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button,
|
||||
bool is_pressed) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto state = GetLastState();
|
||||
|
||||
state.time = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
state.OnButton(button, is_pressed);
|
||||
if (is_pressed) {
|
||||
state.buttonsState |= button;
|
||||
} else {
|
||||
state.buttonsState &= ~button;
|
||||
}
|
||||
|
||||
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();
|
||||
state.OnAxis(axis, value);
|
||||
int axis_id = static_cast<int>(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;
|
||||
}
|
||||
}
|
||||
|
||||
AddState(state);
|
||||
}
|
||||
|
@ -150,7 +126,9 @@ void GameController::Gyro(int id, const float gyro[3]) {
|
|||
state.time = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
|
||||
// Update the angular velocity (gyro data)
|
||||
state.OnGyro(gyro);
|
||||
state.angularVelocity.x = gyro[0]; // X-axis
|
||||
state.angularVelocity.y = gyro[1]; // Y-axis
|
||||
state.angularVelocity.z = gyro[2]; // Z-axis
|
||||
|
||||
AddState(state);
|
||||
}
|
||||
|
@ -160,7 +138,9 @@ void GameController::Acceleration(int id, const float acceleration[3]) {
|
|||
state.time = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
|
||||
// Update the acceleration values
|
||||
state.OnAccel(acceleration);
|
||||
state.acceleration.x = acceleration[0]; // X-axis
|
||||
state.acceleration.y = acceleration[1]; // Y-axis
|
||||
state.acceleration.z = acceleration[2]; // Z-axis
|
||||
|
||||
AddState(state);
|
||||
}
|
||||
|
@ -233,48 +213,134 @@ void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceler
|
|||
}
|
||||
|
||||
void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) {
|
||||
if (!m_engine) {
|
||||
return;
|
||||
if (m_sdl_gamepad != nullptr) {
|
||||
SDL_SetGamepadLED(m_sdl_gamepad, r, g, b);
|
||||
}
|
||||
std::scoped_lock _{m_mutex};
|
||||
m_engine->SetLightBarRGB(r, g, b);
|
||||
}
|
||||
|
||||
void GameController::SetVibration(u8 smallMotor, u8 largeMotor) {
|
||||
if (!m_engine) {
|
||||
return;
|
||||
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);
|
||||
}
|
||||
std::scoped_lock _{m_mutex};
|
||||
m_engine->SetVibration(smallMotor, largeMotor);
|
||||
return true;
|
||||
}
|
||||
|
||||
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.OnTouchpad(touchIndex, touchDown, x, y);
|
||||
|
||||
state.touchpad[touchIndex].state = touchDown;
|
||||
state.touchpad[touchIndex].x = static_cast<u16>(x * 1920);
|
||||
state.touchpad[touchIndex].y = static_cast<u16>(y * 941);
|
||||
|
||||
AddState(state);
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::SetEngine(std::unique_ptr<Engine> engine) {
|
||||
std::scoped_lock _{m_mutex};
|
||||
m_engine = std::move(engine);
|
||||
if (m_engine) {
|
||||
m_engine->Init();
|
||||
}
|
||||
}
|
||||
void GameControllers::TryOpenSDLControllers(GameControllers& controllers) {
|
||||
using namespace Libraries::UserService;
|
||||
int controller_count;
|
||||
SDL_JoystickID* new_joysticks = SDL_GetGamepads(&controller_count);
|
||||
|
||||
Engine* GameController::GetEngine() {
|
||||
return m_engine.get();
|
||||
std::unordered_set<SDL_JoystickID> assigned_ids;
|
||||
std::array<bool, 4> slot_taken{false, false, false, false};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
SDL_Gamepad* pad = controllers[i]->m_sdl_gamepad;
|
||||
if (pad) {
|
||||
SDL_JoystickID id = SDL_GetGamepadID(pad);
|
||||
bool still_connected = false;
|
||||
for (int j = 0; j < controller_count; j++) {
|
||||
if (new_joysticks[j] == id) {
|
||||
still_connected = true;
|
||||
assigned_ids.insert(id);
|
||||
slot_taken[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!still_connected) {
|
||||
AddUserServiceEvent(
|
||||
{OrbisUserServiceEventType::Logout, SDL_GetGamepadPlayerIndex(pad) + 1});
|
||||
SDL_CloseGamepad(pad);
|
||||
controllers[i]->m_sdl_gamepad = nullptr;
|
||||
controllers[i]->user_id = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < controller_count; j++) {
|
||||
SDL_JoystickID id = new_joysticks[j];
|
||||
if (assigned_ids.contains(id))
|
||||
continue;
|
||||
|
||||
SDL_Gamepad* pad = SDL_OpenGamepad(id);
|
||||
if (!pad)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!slot_taken[i]) {
|
||||
controllers[i]->m_sdl_gamepad = pad;
|
||||
controllers[i]->user_id = i + 1;
|
||||
slot_taken[i] = true;
|
||||
AddUserServiceEvent(
|
||||
{OrbisUserServiceEventType::Login, SDL_GetGamepadPlayerIndex(pad) + 1});
|
||||
|
||||
if (SDL_SetGamepadSensorEnabled(controllers[i]->m_sdl_gamepad, SDL_SENSOR_GYRO,
|
||||
true)) {
|
||||
controllers[i]->gyro_poll_rate = SDL_GetGamepadSensorDataRate(
|
||||
controllers[i]->m_sdl_gamepad, SDL_SENSOR_GYRO);
|
||||
LOG_INFO(Input, "Gyro initialized, poll rate: {}",
|
||||
controllers[i]->gyro_poll_rate);
|
||||
} else {
|
||||
LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad {}",
|
||||
controllers[i]->user_id);
|
||||
}
|
||||
if (SDL_SetGamepadSensorEnabled(controllers[i]->m_sdl_gamepad, SDL_SENSOR_ACCEL,
|
||||
true)) {
|
||||
controllers[i]->accel_poll_rate = SDL_GetGamepadSensorDataRate(
|
||||
controllers[i]->m_sdl_gamepad, SDL_SENSOR_ACCEL);
|
||||
LOG_INFO(Input, "Accel initialized, poll rate: {}",
|
||||
controllers[i]->accel_poll_rate);
|
||||
} else {
|
||||
LOG_ERROR(Input, "Failed to initialize accel controls for gamepad {}",
|
||||
controllers[i]->user_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// }
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include "common/assert.h"
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
|
||||
struct SDL_Gamepad;
|
||||
|
||||
namespace Input {
|
||||
|
||||
enum class Axis {
|
||||
|
@ -28,14 +29,7 @@ struct TouchpadEntry {
|
|||
u16 y{};
|
||||
};
|
||||
|
||||
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]);
|
||||
|
||||
struct State {
|
||||
Libraries::Pad::OrbisPadButtonDataOffset buttonsState{};
|
||||
u64 time = 0;
|
||||
int axes[static_cast<int>(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0};
|
||||
|
@ -45,24 +39,16 @@ public:
|
|||
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) {
|
||||
return std::clamp((255 * (value - min)) / (max - min), 0, 255);
|
||||
int v = (255 * (value - min)) / (max - min);
|
||||
return (v < 0 ? 0 : (v > 255 ? 255 : v));
|
||||
}
|
||||
|
||||
constexpr u32 MAX_STATES = 32;
|
||||
|
||||
class GameController {
|
||||
friend class GameControllers;
|
||||
|
||||
public:
|
||||
GameController();
|
||||
virtual ~GameController() = default;
|
||||
|
@ -76,12 +62,13 @@ public:
|
|||
void Gyro(int id, const float gyro[3]);
|
||||
void Acceleration(int id, const float acceleration[3]);
|
||||
void SetLightBarRGB(u8 r, u8 g, u8 b);
|
||||
void SetVibration(u8 smallMotor, u8 largeMotor);
|
||||
bool SetVibration(u8 smallMotor, u8 largeMotor);
|
||||
void SetTouchpadState(int touchIndex, bool touchDown, float x, float y);
|
||||
void SetEngine(std::unique_ptr<Engine>);
|
||||
Engine* GetEngine();
|
||||
u32 Poll();
|
||||
|
||||
float gyro_poll_rate;
|
||||
float accel_poll_rate;
|
||||
u32 user_id = -1; // ORBIS_USER_SERVICE_USER_ID_INVALID
|
||||
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
|
||||
Libraries::Pad::OrbisFVector3& angularVelocity,
|
||||
float deltaTime,
|
||||
|
@ -101,7 +88,24 @@ private:
|
|||
std::array<State, MAX_STATES> m_states;
|
||||
std::array<StateInternal, MAX_STATES> m_private;
|
||||
|
||||
std::unique_ptr<Engine> m_engine = nullptr;
|
||||
SDL_Gamepad* m_sdl_gamepad = nullptr;
|
||||
};
|
||||
|
||||
class GameControllers {
|
||||
std::array<GameController*, 4> controllers;
|
||||
|
||||
public:
|
||||
GameControllers()
|
||||
: controllers({new GameController(), new GameController(), new GameController(),
|
||||
new GameController()}) {};
|
||||
virtual ~GameControllers() = default;
|
||||
GameController* operator[](const size_t& i) const {
|
||||
if (i > 3) {
|
||||
UNREACHABLE_MSG("Index out of bounds for GameControllers!");
|
||||
}
|
||||
return controllers[i];
|
||||
}
|
||||
static void TryOpenSDLControllers(GameControllers& controllers);
|
||||
};
|
||||
|
||||
} // namespace Input
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "common/elf_info.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/singleton.h"
|
||||
#include "input/controller.h"
|
||||
#include "input/input_mouse.h"
|
||||
|
||||
|
@ -61,42 +62,11 @@ std::list<std::pair<InputEvent, bool>> pressed_keys;
|
|||
std::list<InputID> toggled_keys;
|
||||
static std::vector<BindingConnection> connections;
|
||||
|
||||
auto output_array = std::array{
|
||||
// Important: these have to be the first, or else they will update in the wrong order
|
||||
ControllerOutput(LEFTJOYSTICK_HALFMODE),
|
||||
ControllerOutput(RIGHTJOYSTICK_HALFMODE),
|
||||
ControllerOutput(KEY_TOGGLE),
|
||||
|
||||
// Button mappings
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_NORTH), // Triangle
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_EAST), // Circle
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_SOUTH), // Cross
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_WEST), // Square
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_LEFT_SHOULDER), // L1
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_LEFT_STICK), // L3
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER), // R1
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_RIGHT_STICK), // R3
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_START), // Options
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_TOUCHPAD), // TouchPad
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_UP), // Up
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_DOWN), // Down
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_LEFT), // Left
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_RIGHT), // Right
|
||||
|
||||
// Axis mappings
|
||||
// ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTX, false),
|
||||
// ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTY, false),
|
||||
// ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTX, false),
|
||||
// ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTY, false),
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTX),
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTY),
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTX),
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTY),
|
||||
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFT_TRIGGER),
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER),
|
||||
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_INVALID),
|
||||
std::array<ControllerAllOutputs, 4> output_arrays = {
|
||||
ControllerAllOutputs(0),
|
||||
ControllerAllOutputs(1),
|
||||
ControllerAllOutputs(2),
|
||||
ControllerAllOutputs(3),
|
||||
};
|
||||
|
||||
void ControllerOutput::LinkJoystickAxes() {
|
||||
|
@ -197,6 +167,14 @@ InputBinding GetBindingFromString(std::string& line) {
|
|||
return InputBinding(keys[0], keys[1], keys[2]);
|
||||
}
|
||||
|
||||
std::optional<int> parseInt(const std::string& s) {
|
||||
try {
|
||||
return std::stoi(s);
|
||||
} catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
void ParseInputConfig(const std::string game_id = "") {
|
||||
std::string config_type = Config::GetUseUnifiedInputConfig() ? "default" : game_id;
|
||||
const auto config_file = Config::GetFoolproofKbmConfigFile(config_type);
|
||||
|
@ -254,16 +232,39 @@ void ParseInputConfig(const std::string game_id = "") {
|
|||
|
||||
std::string output_string = line.substr(0, equal_pos);
|
||||
std::string input_string = line.substr(equal_pos + 1);
|
||||
s8 input_gamepad_id = -1, output_gamepad_id = -1; // -1 means it's not specified
|
||||
|
||||
// todo: here the inputs and outputs are formatted and split, we need to extract the
|
||||
// controller ID now
|
||||
|
||||
// input gamepad id is only for controllers, it's discarded otherwise
|
||||
std::size_t input_colon_pos = input_string.find(':');
|
||||
if (input_colon_pos != std::string::npos) {
|
||||
auto temp = parseInt(input_string.substr(input_colon_pos + 1));
|
||||
if (!temp) {
|
||||
LOG_WARNING(Input, "Invalid gamepad ID value at line {}: \"{}\"", lineCount, line);
|
||||
} else {
|
||||
input_gamepad_id = *temp;
|
||||
}
|
||||
input_string = input_string.substr(0, input_colon_pos);
|
||||
}
|
||||
|
||||
// if not provided, assume it's for all gamepads, if the input is a controller and that also
|
||||
// doesn't have an ID, and for the first otherwise
|
||||
std::size_t output_colon_pos = output_string.find(':');
|
||||
if (output_colon_pos != std::string::npos) {
|
||||
auto temp = parseInt(output_string.substr(output_colon_pos + 1));
|
||||
if (!temp) {
|
||||
LOG_WARNING(Input, "Invalid gamepad ID value at line {}: \"{}\"", lineCount, line);
|
||||
} else {
|
||||
output_gamepad_id = *temp;
|
||||
}
|
||||
output_string = output_string.substr(0, output_colon_pos);
|
||||
}
|
||||
|
||||
std::size_t comma_pos = input_string.find(',');
|
||||
|
||||
auto parseInt = [](const std::string& s) -> std::optional<int> {
|
||||
try {
|
||||
return std::stoi(s);
|
||||
} catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
// todo: make remapping work for special bindings for gamepads that are not the first
|
||||
if (output_string == "mouse_to_joystick") {
|
||||
if (input_string == "left") {
|
||||
SetMouseToJoystick(1);
|
||||
|
@ -286,7 +287,7 @@ void ParseInputConfig(const std::string game_id = "") {
|
|||
continue;
|
||||
}
|
||||
ControllerOutput* toggle_out =
|
||||
&*std::ranges::find(output_array, ControllerOutput(KEY_TOGGLE));
|
||||
&*std::ranges::find(output_arrays[0].data, ControllerOutput(KEY_TOGGLE));
|
||||
BindingConnection toggle_connection = BindingConnection(
|
||||
InputBinding(toggle_keys.keys[0]), toggle_out, 0, toggle_keys.keys[1]);
|
||||
connections.insert(connections.end(), toggle_connection);
|
||||
|
@ -380,34 +381,60 @@ void ParseInputConfig(const std::string game_id = "") {
|
|||
continue;
|
||||
}
|
||||
if (button_it != string_to_cbutton_map.end()) {
|
||||
// todo add new shit here
|
||||
connection = BindingConnection(
|
||||
binding, &*std::ranges::find(output_array, ControllerOutput(button_it->second)));
|
||||
connections.insert(connections.end(), connection);
|
||||
binding,
|
||||
&*std::ranges::find(output_arrays[std::clamp(output_gamepad_id - 1, 0, 3)].data,
|
||||
ControllerOutput(button_it->second)));
|
||||
|
||||
} else if (axis_it != string_to_axis_map.end()) {
|
||||
// todo add new shit here
|
||||
int value_to_set = binding.keys[2].type == InputType::Axis ? 0 : axis_it->second.value;
|
||||
connection = BindingConnection(
|
||||
binding,
|
||||
&*std::ranges::find(output_array, ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID,
|
||||
axis_it->second.axis,
|
||||
axis_it->second.value >= 0)),
|
||||
&*std::ranges::find(output_arrays[std::clamp(output_gamepad_id - 1, 0, 3)].data,
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID,
|
||||
axis_it->second.axis,
|
||||
axis_it->second.value >= 0)),
|
||||
value_to_set);
|
||||
connections.insert(connections.end(), connection);
|
||||
} else {
|
||||
LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.",
|
||||
lineCount, line);
|
||||
continue;
|
||||
}
|
||||
// todo make the following: if the input binding contains a controller input, and gamepad ID
|
||||
// isn't specified for either inputs or output (both are -1), then multiply the binding and
|
||||
// add it to all 4 controllers
|
||||
if (connection.HasGamepadInput() && input_gamepad_id == -1 && output_gamepad_id == -1) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
BindingConnection copy = connection.CopyWithChangedGamepadId(i + 1);
|
||||
copy.output = &*std::ranges::find(output_arrays[i].data, *connection.output);
|
||||
connections.push_back(copy);
|
||||
}
|
||||
} else {
|
||||
connections.push_back(connection);
|
||||
}
|
||||
LOG_DEBUG(Input, "Succesfully parsed line {}", lineCount);
|
||||
}
|
||||
file.close();
|
||||
std::sort(connections.begin(), connections.end());
|
||||
for (auto& c : connections) {
|
||||
// todo add new shit here
|
||||
LOG_DEBUG(Input, "Binding: {} : {}", c.output->ToString(), c.binding.ToString());
|
||||
}
|
||||
LOG_DEBUG(Input, "Done parsing the input config!");
|
||||
}
|
||||
|
||||
BindingConnection BindingConnection::CopyWithChangedGamepadId(u8 gamepad) {
|
||||
BindingConnection copy = *this;
|
||||
for (auto& key : copy.binding.keys) {
|
||||
if (key.type == InputType::Controller || key.type == InputType::Axis) {
|
||||
key.gamepad_id = gamepad;
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
u32 GetMouseWheelEvent(const SDL_Event& event) {
|
||||
if (event.type != SDL_EVENT_MOUSE_WHEEL && event.type != SDL_EVENT_MOUSE_WHEEL_OFF) {
|
||||
LOG_WARNING(Input, "Something went wrong with wheel input parsing!");
|
||||
|
@ -426,6 +453,8 @@ u32 GetMouseWheelEvent(const SDL_Event& event) {
|
|||
}
|
||||
|
||||
InputEvent InputBinding::GetInputEventFromSDLEvent(const SDL_Event& e) {
|
||||
// todo add new shit here
|
||||
u8 gamepad = 1;
|
||||
switch (e.type) {
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
|
@ -439,18 +468,19 @@ InputEvent InputBinding::GetInputEventFromSDLEvent(const SDL_Event& e) {
|
|||
e.type == SDL_EVENT_MOUSE_WHEEL, 0);
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
return InputEvent(InputType::Controller, (u32)e.gbutton.button, e.gbutton.down, 0);
|
||||
gamepad = GetGamepadIndexFromJoystickId(e.gbutton.which);
|
||||
return InputEvent({InputType::Controller, (u32)e.gbutton.button, gamepad}, e.gbutton.down,
|
||||
0);
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||
return InputEvent(InputType::Axis, (u32)e.gaxis.axis, true, e.gaxis.value / 256);
|
||||
gamepad = GetGamepadIndexFromJoystickId(e.gaxis.which);
|
||||
return InputEvent({InputType::Axis, (u32)e.gaxis.axis, gamepad}, true, e.gaxis.value / 256);
|
||||
default:
|
||||
return InputEvent();
|
||||
}
|
||||
}
|
||||
|
||||
GameController* ControllerOutput::controller = nullptr;
|
||||
void ControllerOutput::SetControllerOutputController(GameController* c) {
|
||||
ControllerOutput::controller = c;
|
||||
}
|
||||
GameControllers ControllerOutput::controllers =
|
||||
*Common::Singleton<Input::GameControllers>::Instance();
|
||||
|
||||
void ToggleKeyInList(InputID input) {
|
||||
if (input.type == InputType::Axis) {
|
||||
|
@ -492,7 +522,7 @@ void ControllerOutput::AddUpdate(InputEvent event) {
|
|||
*new_param = (event.active ? event.axis_value : 0) + *new_param;
|
||||
}
|
||||
}
|
||||
void ControllerOutput::FinalizeUpdate() {
|
||||
void ControllerOutput::FinalizeUpdate(u8 gamepad_index) {
|
||||
state_changed = old_button_state != new_button_state || old_param != *new_param;
|
||||
if (!state_changed) {
|
||||
return;
|
||||
|
@ -506,8 +536,8 @@ void ControllerOutput::FinalizeUpdate() {
|
|||
touchpad_x = Config::getBackButtonBehavior() == "left" ? 0.25f
|
||||
: Config::getBackButtonBehavior() == "right" ? 0.75f
|
||||
: 0.5f;
|
||||
controller->SetTouchpadState(0, new_button_state, touchpad_x, 0.5f);
|
||||
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||
controllers[0]->SetTouchpadState(0, new_button_state, touchpad_x, 0.5f);
|
||||
controllers[0]->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||
break;
|
||||
case LEFTJOYSTICK_HALFMODE:
|
||||
leftjoystick_halfmode = new_button_state;
|
||||
|
@ -520,7 +550,8 @@ void ControllerOutput::FinalizeUpdate() {
|
|||
// fine, and a toggle doesn't have to checked against every input that's bound to it, it's
|
||||
// enough that one is pressed
|
||||
default: // is a normal key (hopefully)
|
||||
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||
controllers[gamepad_index]->CheckButton(0, SDLGamepadToOrbisButton(button),
|
||||
new_button_state);
|
||||
break;
|
||||
}
|
||||
} else if (axis != SDL_GAMEPAD_AXIS_INVALID && positive_axis) {
|
||||
|
@ -550,18 +581,20 @@ void ControllerOutput::FinalizeUpdate() {
|
|||
break;
|
||||
case Axis::TriggerLeft:
|
||||
ApplyDeadzone(new_param, lefttrigger_deadzone);
|
||||
controller->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param));
|
||||
controller->CheckButton(0, OrbisPadButtonDataOffset::L2, *new_param > 0x20);
|
||||
controllers[gamepad_index]->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param));
|
||||
controllers[gamepad_index]->CheckButton(0, OrbisPadButtonDataOffset::L2,
|
||||
*new_param > 0x20);
|
||||
return;
|
||||
case Axis::TriggerRight:
|
||||
ApplyDeadzone(new_param, righttrigger_deadzone);
|
||||
controller->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param));
|
||||
controller->CheckButton(0, OrbisPadButtonDataOffset::R2, *new_param > 0x20);
|
||||
controllers[gamepad_index]->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param));
|
||||
controllers[gamepad_index]->CheckButton(0, OrbisPadButtonDataOffset::R2,
|
||||
*new_param > 0x20);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
controller->Axis(0, c_axis, GetAxis(-0x80, 0x7f, *new_param * multiplier));
|
||||
controllers[gamepad_index]->Axis(0, c_axis, GetAxis(-0x80, 0x7f, *new_param * multiplier));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -693,23 +726,35 @@ InputEvent BindingConnection::ProcessBinding() {
|
|||
}
|
||||
|
||||
void ActivateOutputsFromInputs() {
|
||||
// Reset values and flags
|
||||
for (auto& it : pressed_keys) {
|
||||
it.second = false;
|
||||
}
|
||||
for (auto& it : output_array) {
|
||||
it.ResetUpdate();
|
||||
}
|
||||
|
||||
// Iterate over all inputs, and update their respecive outputs accordingly
|
||||
for (auto& it : connections) {
|
||||
it.output->AddUpdate(it.ProcessBinding());
|
||||
}
|
||||
// todo find a better solution
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
||||
// Update all outputs
|
||||
for (auto& it : output_array) {
|
||||
it.FinalizeUpdate();
|
||||
// Reset values and flags
|
||||
for (auto& it : pressed_keys) {
|
||||
it.second = false;
|
||||
}
|
||||
for (auto& it : output_arrays[i].data) {
|
||||
it.ResetUpdate();
|
||||
}
|
||||
|
||||
// Iterate over all inputs, and update their respecive outputs accordingly
|
||||
for (auto& it : connections) {
|
||||
// only update this when it's the correct pass
|
||||
if (it.output->gamepad_id == i) {
|
||||
it.output->AddUpdate(it.ProcessBinding());
|
||||
}
|
||||
}
|
||||
|
||||
// Update all outputs
|
||||
for (auto& it : output_arrays[i].data) {
|
||||
it.FinalizeUpdate(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id) {
|
||||
return SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(id)) + 1;
|
||||
}
|
||||
|
||||
} // namespace Input
|
||||
|
|
|
@ -49,21 +49,24 @@ class InputID {
|
|||
public:
|
||||
InputType type;
|
||||
u32 sdl_id;
|
||||
InputID(InputType d = InputType::Count, u32 i = (u32)-1) : type(d), sdl_id(i) {}
|
||||
u8 gamepad_id;
|
||||
InputID(InputType d = InputType::Count, u32 i = (u32)-1, u8 g = 1)
|
||||
: type(d), sdl_id(i), gamepad_id(g) {}
|
||||
bool operator==(const InputID& o) const {
|
||||
return type == o.type && sdl_id == o.sdl_id;
|
||||
return type == o.type && sdl_id == o.sdl_id && gamepad_id == o.gamepad_id;
|
||||
}
|
||||
bool operator!=(const InputID& o) const {
|
||||
return type != o.type || sdl_id != o.sdl_id;
|
||||
return type != o.type || sdl_id != o.sdl_id || gamepad_id != o.gamepad_id;
|
||||
}
|
||||
bool operator<=(const InputID& o) const {
|
||||
return type <= o.type && sdl_id <= o.sdl_id;
|
||||
return std::tie(gamepad_id, type, sdl_id) <= std::tie(o.gamepad_id, o.type, o.sdl_id);
|
||||
}
|
||||
bool IsValid() const {
|
||||
return *this != InputID();
|
||||
}
|
||||
std::string ToString() {
|
||||
return fmt::format("({}: {:x})", input_type_names[(u8)type], sdl_id);
|
||||
return fmt::format("({}. {}: {:x})", gamepad_id, input_type_names[(u8)type], sdl_id);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -321,14 +324,15 @@ public:
|
|||
static InputEvent GetInputEventFromSDLEvent(const SDL_Event& e);
|
||||
};
|
||||
class ControllerOutput {
|
||||
static GameController* controller;
|
||||
static GameControllers controllers;
|
||||
|
||||
public:
|
||||
static void SetControllerOutputController(GameController* c);
|
||||
static void GetGetGamepadIndexFromSDLJoystickID(const SDL_JoystickID id) {}
|
||||
static void LinkJoystickAxes();
|
||||
|
||||
u32 button;
|
||||
u32 axis;
|
||||
u8 gamepad_id;
|
||||
// these are only used as s8,
|
||||
// but I added some padding to avoid overflow if it's activated by multiple inputs
|
||||
// axis_plus and axis_minus pairs share a common new_param, the other outputs have their own
|
||||
|
@ -342,6 +346,7 @@ public:
|
|||
new_param = new s16(0);
|
||||
old_param = 0;
|
||||
positive_axis = p;
|
||||
gamepad_id = 0;
|
||||
}
|
||||
ControllerOutput(const ControllerOutput& o) : button(o.button), axis(o.axis) {
|
||||
new_param = new s16(*o.new_param);
|
||||
|
@ -367,7 +372,7 @@ public:
|
|||
|
||||
void ResetUpdate();
|
||||
void AddUpdate(InputEvent event);
|
||||
void FinalizeUpdate();
|
||||
void FinalizeUpdate(u8 gamepad_index);
|
||||
};
|
||||
class BindingConnection {
|
||||
public:
|
||||
|
@ -382,6 +387,13 @@ public:
|
|||
output = out;
|
||||
toggle = t;
|
||||
}
|
||||
BindingConnection& operator=(const BindingConnection& o) {
|
||||
binding = o.binding;
|
||||
output = o.output;
|
||||
axis_param = o.axis_param;
|
||||
toggle = o.toggle;
|
||||
return *this;
|
||||
}
|
||||
bool operator<(const BindingConnection& other) const {
|
||||
// a button is a higher priority than an axis, as buttons can influence axes
|
||||
// (e.g. joystick_halfmode)
|
||||
|
@ -395,13 +407,70 @@ public:
|
|||
}
|
||||
return false;
|
||||
}
|
||||
bool HasGamepadInput() {
|
||||
for (auto& key : binding.keys) {
|
||||
if (key.type == InputType::Controller || key.type == InputType::Axis) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
BindingConnection CopyWithChangedGamepadId(u8 gamepad);
|
||||
InputEvent ProcessBinding();
|
||||
};
|
||||
|
||||
class ControllerAllOutputs {
|
||||
public:
|
||||
std::array<ControllerOutput, 24> data = {
|
||||
// Important: these have to be the first, or else they will update in the wrong order
|
||||
ControllerOutput(LEFTJOYSTICK_HALFMODE),
|
||||
ControllerOutput(RIGHTJOYSTICK_HALFMODE),
|
||||
ControllerOutput(KEY_TOGGLE),
|
||||
|
||||
// Button mappings
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_NORTH), // Triangle
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_EAST), // Circle
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_SOUTH), // Cross
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_WEST), // Square
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_LEFT_SHOULDER), // L1
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_LEFT_STICK), // L3
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER), // R1
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_RIGHT_STICK), // R3
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_START), // Options
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_TOUCHPAD), // TouchPad
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_UP), // Up
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_DOWN), // Down
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_LEFT), // Left
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_RIGHT), // Right
|
||||
|
||||
// Axis mappings
|
||||
// ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTX, false),
|
||||
// ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTY, false),
|
||||
// ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTX, false),
|
||||
// ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTY, false),
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTX),
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTY),
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTX),
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTY),
|
||||
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFT_TRIGGER),
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER),
|
||||
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_INVALID),
|
||||
};
|
||||
ControllerAllOutputs(u8 g) {
|
||||
for (int i = 0; i < 24; i++) {
|
||||
data[i].gamepad_id = g;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Updates the list of pressed keys with the given input.
|
||||
// Returns whether the list was updated or not.
|
||||
bool UpdatePressedKeys(InputEvent event);
|
||||
|
||||
void ActivateOutputsFromInputs();
|
||||
|
||||
u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id);
|
||||
|
||||
} // namespace Input
|
||||
|
|
|
@ -61,6 +61,8 @@ A: -F12: Triggers Renderdoc capture
|
|||
-F9: Pauses emultor, if the debug menu is open
|
||||
-F8: Reparses the config file while in-game
|
||||
-F7: Toggles mouse capture and mouse input
|
||||
-F5: Registers a new simulated controller for the game. Required if you want to use your keyboard for multiple player inputs without actually connecting a new controller.
|
||||
-F4: Unregisters the last simulated controller. For the average user, this isn't really useful, and it is also possible to disconnect even the first controller with this, so only use it if you know what you're doing. If you accidentally hit this and remove all inputs, you can add back the first controller with F5.
|
||||
|
||||
Q: How do I change between mouse and controller joystick input, and why is it even required?
|
||||
A: You can switch between them with F7, and it is required, because mouse input is done with polling, which means mouse movement is checked every frame, and if it didn't move, the code manually sets the emulator's virtual controller to 0 (back to the center), even if other input devices would update it.
|
||||
|
@ -100,6 +102,12 @@ axis_left_y_minus = w;
|
|||
You can make a comment line by putting # as the first character.
|
||||
Whitespace doesn't matter, <output>=<input>; is just as valid as <output> = <input>;
|
||||
';' at the ends of lines is also optional.
|
||||
|
||||
You can bind inputs to an output that's not the first controller with:
|
||||
<controller_output> : <index_of_output> = <input>
|
||||
For example:
|
||||
cross : 2 = o
|
||||
binds 'o' to the second controller's cross button. See the FAQ on how to enable a second controller without actually connecting one.
|
||||
)";
|
||||
}
|
||||
QString bindings() {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "core/debug_state.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "imgui/renderer/imgui_core.h"
|
||||
#include "input/controller.h"
|
||||
#include "input/input_handler.h"
|
||||
|
@ -24,239 +25,55 @@
|
|||
#include "SDL3/SDL_metal.h"
|
||||
#endif
|
||||
|
||||
namespace Input {
|
||||
|
||||
using Libraries::Pad::OrbisPadButtonDataOffset;
|
||||
|
||||
static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) {
|
||||
using OPBDO = OrbisPadButtonDataOffset;
|
||||
|
||||
switch (button) {
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||
return OPBDO::Down;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||
return OPBDO::Up;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||
return OPBDO::Left;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||
return OPBDO::Right;
|
||||
case SDL_GAMEPAD_BUTTON_SOUTH:
|
||||
return OPBDO::Cross;
|
||||
case SDL_GAMEPAD_BUTTON_NORTH:
|
||||
return OPBDO::Triangle;
|
||||
case SDL_GAMEPAD_BUTTON_WEST:
|
||||
return OPBDO::Square;
|
||||
case SDL_GAMEPAD_BUTTON_EAST:
|
||||
return OPBDO::Circle;
|
||||
case SDL_GAMEPAD_BUTTON_START:
|
||||
return OPBDO::Options;
|
||||
case SDL_GAMEPAD_BUTTON_TOUCHPAD:
|
||||
return OPBDO::TouchPad;
|
||||
case SDL_GAMEPAD_BUTTON_BACK:
|
||||
return OPBDO::TouchPad;
|
||||
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||
return OPBDO::L1;
|
||||
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||
return OPBDO::R1;
|
||||
case SDL_GAMEPAD_BUTTON_LEFT_STICK:
|
||||
return OPBDO::L3;
|
||||
case SDL_GAMEPAD_BUTTON_RIGHT_STICK:
|
||||
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:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
SDLInputEngine::~SDLInputEngine() {
|
||||
if (m_gamepad) {
|
||||
SDL_CloseGamepad(m_gamepad);
|
||||
}
|
||||
}
|
||||
|
||||
void SDLInputEngine::Init() {
|
||||
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: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
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);
|
||||
m_gamepad = SDL_OpenGamepad(gamepads[0]);
|
||||
if (!m_gamepad) {
|
||||
LOG_ERROR(Input, "Failed to open gamepad 0: {}", SDL_GetError());
|
||||
SDL_free(gamepads);
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Joystick* joystick = SDL_GetGamepadJoystick(m_gamepad);
|
||||
Uint16 vendor = SDL_GetJoystickVendor(joystick);
|
||||
Uint16 product = SDL_GetJoystickProduct(joystick);
|
||||
|
||||
bool isDualSense = (vendor == 0x054C && product == 0x0CE6);
|
||||
|
||||
LOG_INFO(Input, "Gamepad Vendor: {:04X}, Product: {:04X}", vendor, product);
|
||||
if (isDualSense) {
|
||||
LOG_INFO(Input, "Detected DualSense Controller");
|
||||
}
|
||||
|
||||
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, error: {}",
|
||||
SDL_GetError());
|
||||
SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_GYRO, false);
|
||||
}
|
||||
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, error: {}",
|
||||
SDL_GetError());
|
||||
SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_ACCEL, false);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_free(gamepads);
|
||||
|
||||
int* rgb = Config::GetControllerCustomColor();
|
||||
|
||||
if (isDualSense) {
|
||||
if (SDL_SetJoystickLED(joystick, rgb[0], rgb[1], rgb[2]) == 0) {
|
||||
LOG_INFO(Input, "Set DualSense LED to R:{} G:{} B:{}", rgb[0], rgb[1], rgb[2]);
|
||||
} else {
|
||||
LOG_ERROR(Input, "Failed to set DualSense LED: {}", SDL_GetError());
|
||||
}
|
||||
} else {
|
||||
SetLightBarRGB(rgb[0], rgb[1], rgb[2]);
|
||||
}
|
||||
}
|
||||
|
||||
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<int>(Axis::AxisMax); ++i) {
|
||||
const auto axis = static_cast<Axis>(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 OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) {
|
||||
switch (button) {
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||
return OrbisPadButtonDataOffset::Down;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||
return OrbisPadButtonDataOffset::Up;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||
return OrbisPadButtonDataOffset::Left;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||
return OrbisPadButtonDataOffset::Right;
|
||||
case SDL_GAMEPAD_BUTTON_SOUTH:
|
||||
return OrbisPadButtonDataOffset::Cross;
|
||||
case SDL_GAMEPAD_BUTTON_NORTH:
|
||||
return OrbisPadButtonDataOffset::Triangle;
|
||||
case SDL_GAMEPAD_BUTTON_WEST:
|
||||
return OrbisPadButtonDataOffset::Square;
|
||||
case SDL_GAMEPAD_BUTTON_EAST:
|
||||
return OrbisPadButtonDataOffset::Circle;
|
||||
case SDL_GAMEPAD_BUTTON_START:
|
||||
return OrbisPadButtonDataOffset::Options;
|
||||
case SDL_GAMEPAD_BUTTON_TOUCHPAD:
|
||||
return OrbisPadButtonDataOffset::TouchPad;
|
||||
case SDL_GAMEPAD_BUTTON_BACK:
|
||||
return OrbisPadButtonDataOffset::TouchPad;
|
||||
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||
return OrbisPadButtonDataOffset::L1;
|
||||
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||
return OrbisPadButtonDataOffset::R1;
|
||||
case SDL_GAMEPAD_BUTTON_LEFT_STICK:
|
||||
return OrbisPadButtonDataOffset::L3;
|
||||
case SDL_GAMEPAD_BUTTON_RIGHT_STICK:
|
||||
return OrbisPadButtonDataOffset::R3;
|
||||
default:
|
||||
return OrbisPadButtonDataOffset::None;
|
||||
}
|
||||
}
|
||||
|
||||
static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint32 interval) {
|
||||
auto* controller = reinterpret_cast<Input::GameController*>(userdata);
|
||||
return controller->Poll();
|
||||
}
|
||||
|
||||
WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_,
|
||||
WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameControllers* controllers_,
|
||||
std::string_view window_title)
|
||||
: width{width_}, height{height_}, controller{controller_} {
|
||||
: width{width_}, height{height_}, controllers{*controllers_} {
|
||||
if (!SDL_SetHint(SDL_HINT_APP_NAME, "shadPS4")) {
|
||||
UNREACHABLE_MSG("Failed to set SDL window hint: {}", SDL_GetError());
|
||||
}
|
||||
|
@ -300,7 +117,6 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_
|
|||
SDL_SetWindowFullscreen(window, Config::getIsFullscreen());
|
||||
|
||||
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
||||
controller->SetEngine(std::make_unique<Input::SDLInputEngine>());
|
||||
|
||||
#if defined(SDL_PLATFORM_WIN32)
|
||||
window_info.type = WindowSystemType::Windows;
|
||||
|
@ -325,9 +141,10 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_
|
|||
window_info.render_surface = SDL_Metal_GetLayer(SDL_Metal_CreateView(window));
|
||||
#endif
|
||||
// input handler init-s
|
||||
Input::ControllerOutput::SetControllerOutputController(controller);
|
||||
Input::ControllerOutput::LinkJoystickAxes();
|
||||
Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial()));
|
||||
// default login
|
||||
using namespace Libraries::UserService;
|
||||
}
|
||||
|
||||
WindowSDL::~WindowSDL() = default;
|
||||
|
@ -365,34 +182,28 @@ void WindowSDL::WaitEvent() {
|
|||
break;
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
controller->SetEngine(std::make_unique<Input::SDLInputEngine>());
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
|
||||
controller->SetTouchpadState(event.gtouchpad.finger,
|
||||
event.type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP, event.gtouchpad.x,
|
||||
event.gtouchpad.y);
|
||||
// todo handle userserviceevents here
|
||||
Input::GameControllers::TryOpenSDLControllers(controllers);
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||
OnGamepadEvent(&event);
|
||||
break;
|
||||
// i really would have appreciated ANY KIND OF DOCUMENTATION ON THIS
|
||||
// AND IT DOESN'T EVEN USE PROPER ENUMS
|
||||
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
|
||||
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: {
|
||||
int controller_id = Input::GetGamepadIndexFromJoystickId(event.gsensor.which) - 1;
|
||||
switch ((SDL_SensorType)event.gsensor.sensor) {
|
||||
case SDL_SENSOR_GYRO:
|
||||
controller->Gyro(0, event.gsensor.data);
|
||||
controllers[controller_id]->Gyro(0, event.gsensor.data);
|
||||
break;
|
||||
case SDL_SENSOR_ACCEL:
|
||||
controller->Acceleration(0, event.gsensor.data);
|
||||
controllers[controller_id]->Acceleration(0, event.gsensor.data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_QUIT:
|
||||
is_open = false;
|
||||
break;
|
||||
|
@ -421,8 +232,8 @@ void WindowSDL::WaitEvent() {
|
|||
}
|
||||
|
||||
void WindowSDL::InitTimers() {
|
||||
SDL_AddTimer(100, &PollController, controller);
|
||||
SDL_AddTimer(33, Input::MousePolling, (void*)controller);
|
||||
SDL_AddTimer(100, &PollController, controllers[0]);
|
||||
SDL_AddTimer(33, Input::MousePolling, (void*)controllers[0]);
|
||||
}
|
||||
|
||||
void WindowSDL::RequestKeyboard() {
|
||||
|
@ -468,6 +279,7 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) {
|
|||
|
||||
// Handle window controls outside of the input maps
|
||||
if (event->type == SDL_EVENT_KEY_DOWN) {
|
||||
using namespace Libraries::UserService;
|
||||
u32 input_id = input_event.input.sdl_id;
|
||||
// Reparse kbm inputs
|
||||
if (input_id == SDLK_F8) {
|
||||
|
@ -493,6 +305,28 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) {
|
|||
VideoCore::TriggerCapture();
|
||||
return;
|
||||
}
|
||||
// test controller connect/disconnect
|
||||
else if (input_id == SDLK_F4) {
|
||||
auto controllers = *Common::Singleton<Input::GameControllers>::Instance();
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
if (controllers[i]->user_id != -1) {
|
||||
AddUserServiceEvent(
|
||||
{OrbisUserServiceEventType::Logout, (s32)controllers[i]->user_id});
|
||||
controllers[i]->user_id = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (input_id == SDLK_F5) {
|
||||
auto controllers = *Common::Singleton<Input::GameControllers>::Instance();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (controllers[i]->user_id == -1) {
|
||||
controllers[i]->user_id = i + 1;
|
||||
AddUserServiceEvent(
|
||||
{OrbisUserServiceEventType::Login, (s32)controllers[i]->user_id});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it's a wheel event, make a timer that turns it off after a set time
|
||||
|
@ -520,10 +354,37 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) {
|
|||
// as it would break the entire touchpad handling
|
||||
// You can still bind other things to it though
|
||||
if (event->gbutton.button == SDL_GAMEPAD_BUTTON_TOUCHPAD) {
|
||||
controller->CheckButton(0, OrbisPadButtonDataOffset::TouchPad, input_down);
|
||||
controllers[Input::GetGamepadIndexFromJoystickId(event->gbutton.which)]->CheckButton(
|
||||
0, OrbisPadButtonDataOffset::TouchPad, input_down);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->type) {
|
||||
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
|
||||
switch ((SDL_SensorType)event->gsensor.sensor) {
|
||||
case SDL_SENSOR_GYRO:
|
||||
controllers[Input::GetGamepadIndexFromJoystickId(event->gsensor.which)]->Gyro(
|
||||
0, event->gsensor.data);
|
||||
break;
|
||||
case SDL_SENSOR_ACCEL:
|
||||
controllers[Input::GetGamepadIndexFromJoystickId(event->gsensor.which)]->Acceleration(
|
||||
0, event->gsensor.data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
|
||||
controllers[Input::GetGamepadIndexFromJoystickId(event->gtouchpad.which)]->SetTouchpadState(
|
||||
event->gtouchpad.finger, event->type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP,
|
||||
event->gtouchpad.x, event->gtouchpad.y);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// add/remove it from the list
|
||||
bool inputs_changed = Input::UpdatePressedKeys(input_event);
|
||||
|
||||
|
|
|
@ -15,25 +15,8 @@ struct SDL_Gamepad;
|
|||
union SDL_Event;
|
||||
|
||||
namespace Input {
|
||||
|
||||
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 = 0.0f;
|
||||
float m_accel_poll_rate = 0.0f;
|
||||
};
|
||||
|
||||
} // namespace Input
|
||||
class GameController;
|
||||
}
|
||||
|
||||
namespace Frontend {
|
||||
|
||||
|
@ -65,7 +48,7 @@ class WindowSDL {
|
|||
int keyboard_grab = 0;
|
||||
|
||||
public:
|
||||
explicit WindowSDL(s32 width, s32 height, Input::GameController* controller,
|
||||
explicit WindowSDL(s32 width, s32 height, Input::GameControllers* controllers,
|
||||
std::string_view window_title);
|
||||
~WindowSDL();
|
||||
|
||||
|
@ -103,7 +86,7 @@ private:
|
|||
private:
|
||||
s32 width;
|
||||
s32 height;
|
||||
Input::GameController* controller;
|
||||
Input::GameControllers controllers{};
|
||||
WindowSystemInfo window_info{};
|
||||
SDL_Window* window{};
|
||||
bool is_shown{};
|
||||
|
|
Loading…
Add table
Reference in a new issue