From 180a34808a2ad1cf63d6f24b4fcaeadedd892e85 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Thu, 27 Feb 2025 14:02:03 +0100 Subject: [PATCH 01/30] Multiple controllers v0 Contains: - replacing a whole lot of things that were just a global with a 4 long array global - implementing sceUserServiceGetEvent to handle users connecting/disconnecting (only half done, currently you need to specify the number of players from config - Even more shit added to remapping, this time a new field denoting the controller ID for the input or output (for inputs it only concerns gamepads, as those are the only ones that you can connect more than one of) - reverts ngoquang's engine, as it's currently unused, and I didn't want to deal with yet another system that had to be updated --- src/common/config.cpp | 12 + src/common/config.h | 2 + src/core/libraries/pad/pad.cpp | 59 ++--- src/core/libraries/system/userservice.cpp | 50 +++- src/core/libraries/system/userservice.h | 2 + src/emulator.cpp | 4 +- src/emulator.h | 2 +- src/input/controller.cpp | 152 +++++------ src/input/controller.h | 51 ++-- src/input/input_handler.cpp | 209 ++++++++++------ src/input/input_handler.h | 81 +++++- src/sdl_window.cpp | 291 +++++----------------- src/sdl_window.h | 25 +- 13 files changed, 449 insertions(+), 491 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 111c0cfa9..0a0d2ba34 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -75,6 +75,8 @@ static double trophyNotificationDuration = 6.0; static bool useUnifiedInputConfig = true; static bool overrideControllerColor = false; static int controllerCustomColorRGB[3] = {0, 0, 255}; +static int numberOfPlayers = 1; +static bool separateupdatefolder = false; static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; static std::string trophyKey; @@ -110,6 +112,14 @@ static bool showLabelsUnderIcons = true; // Language u32 m_language = 1; // english +void SetNumberOfPlayers(int num) { + numberOfPlayers = num; +} + +int GetNumberOfPlayers() { + return numberOfPlayers; +} + bool allowHDR() { return isHDRAllowed; } @@ -788,6 +798,7 @@ void load(const std::filesystem::path& path) { specialPadClass = toml::find_or(input, "specialPadClass", 1); isMotionControlsEnabled = toml::find_or(input, "isMotionControlsEnabled", true); useUnifiedInputConfig = toml::find_or(input, "useUnifiedInputConfig", true); + numberOfPlayers = toml::find_or(input, "numberOfPlayers", 1); } if (data.contains("GPU")) { @@ -976,6 +987,7 @@ void save(const std::filesystem::path& path) { data["Input"]["specialPadClass"] = specialPadClass; data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled; data["Input"]["useUnifiedInputConfig"] = useUnifiedInputConfig; + data["Input"]["numberOfPlayers"] = numberOfPlayers; data["GPU"]["screenWidth"] = screenWidth; data["GPU"]["screenHeight"] = screenHeight; data["GPU"]["nullGpu"] = isNullGpu; diff --git a/src/common/config.h b/src/common/config.h index aba23621c..06d45b686 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -20,6 +20,8 @@ void load(const std::filesystem::path& path); void save(const std::filesystem::path& path); void saveMainWindow(const std::filesystem::path& path); +void SetNumberOfPlayers(int num); +int GetNumberOfPlayers(); std::string getTrophyKey(); void setTrophyKey(std::string key); bool GetLoadGameSizeEnabled(); diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 5dfc68e90..a62afc1a9 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -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; @@ -98,10 +96,10 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->stickInfo.deadZoneLeft = 1; pInfo->stickInfo.deadZoneRight = 1; pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; - pInfo->connectedCount = 1; + pInfo->connectedCount = 2; pInfo->connected = false; pInfo->deviceClass = OrbisPadDeviceClass::Standard; - return ORBIS_OK; + return 0; } pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; @@ -109,14 +107,14 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->stickInfo.deadZoneLeft = 1; pInfo->stickInfo.deadZoneRight = 1; pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; - pInfo->connectedCount = 1; + pInfo->connectedCount = 2; pInfo->connected = true; pInfo->deviceClass = OrbisPadDeviceClass::Standard; if (Config::getUseSpecialPad()) { 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::Instance(); - const auto* engine = controller->GetEngine(); - int ret_num = controller->ReadStates(states, num, &connected, &connected_count); + auto controllers = *Common::Singleton::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::Instance(); - const auto* engine = controller->GetEngine(); + auto controllers = *Common::Singleton::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(Input::Axis::LeftX)]; pData->leftStick.y = state.axes[static_cast(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::Instance(); + auto controllers = *Common::Singleton::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::Instance(); + auto* controller = Common::Singleton::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::Instance(); + auto* controller = Common::Singleton::Instance(); controller->SetVibration(pParam->smallMotor, pParam->largeMotor); return ORBIS_OK; } diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index b4bf189ea..4da46676d 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -1,12 +1,15 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/config.h" #include "common/logging/log.h" #include "core/libraries/libs.h" #include "core/libraries/system/userservice.h" #include "core/libraries/system/userservice_error.h" +#include "core/tls.h" namespace Libraries::UserService { @@ -105,19 +108,41 @@ int PS4_SYSV_ABI sceUserServiceGetDiscPlayerFlag() { return ORBIS_OK; } +std::queue user_service_event_queue = {}; + +void AddUserServiceEvent(const OrbisUserServiceEvent e) { + 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(); return ORBIS_OK; } return ORBIS_USER_SERVICE_ERROR_NO_EVENT; + + // // fake a login event + // static bool logged_in_1 = false; + // static bool logged_in_2 = false; + + // if (!logged_in_1) { + // logged_in_1 = true; + // event->event = OrbisUserServiceEventType::Login; + // event->userId = 1; + // return ORBIS_OK; + // } + // if (!logged_in_2) { + // logged_in_2 = true; + // event->event = OrbisUserServiceEventType::Login; + // event->userId = 2; + // return ORBIS_OK; + // } } int PS4_SYSV_ABI sceUserServiceGetEventCalendarType() { @@ -574,9 +599,9 @@ s32 PS4_SYSV_ABI sceUserServiceGetLoginUserIdList(OrbisUserServiceLoginUserIdLis } // 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; + userIdList->user_id[1] = 2; + userIdList->user_id[2] = 3; + userIdList->user_id[3] = 4; return ORBIS_OK; } @@ -1078,7 +1103,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; } diff --git a/src/core/libraries/system/userservice.h b/src/core/libraries/system/userservice.h index 66ac2b69d..2a4de2db8 100644 --- a/src/core/libraries/system/userservice.h +++ b/src/core/libraries/system/userservice.h @@ -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(); diff --git a/src/emulator.cpp b/src/emulator.cpp index 1a71b99cb..902c4bae0 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -53,7 +53,7 @@ Emulator::Emulator() { // Defer until after logging is initialized. memory = Core::Memory::Instance(); - controller = Common::Singleton::Instance(); + controllers = Common::Singleton::Instance(); linker = Common::Singleton::Instance(); // Load renderdoc module. @@ -215,7 +215,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector( - Config::getScreenWidth(), Config::getScreenHeight(), controller, window_title); + Config::getScreenWidth(), Config::getScreenHeight(), controllers, window_title); g_window = window.get(); diff --git a/src/emulator.h b/src/emulator.h index 08c2807a1..41eafda9e 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -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 window; std::chrono::steady_clock::time_point start_time; diff --git a/src/input/controller.cpp b/src/input/controller.cpp index bb8db9a7c..8bc458be6 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -10,55 +10,6 @@ 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(axis)] = value; -} - -void State::OnTouchpad(int touchIndex, bool isDown, float x, float y) { - touchpad[touchIndex].state = isDown; - touchpad[touchIndex].x = static_cast(x * 1920); - touchpad[touchIndex].y = static_cast(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 +75,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(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 +124,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 +136,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 +211,72 @@ 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(x * 1920); + state.touchpad[touchIndex].y = static_cast(y * 941); AddState(state); } } -void GameController::SetEngine(std::unique_ptr engine) { - std::scoped_lock _{m_mutex}; - m_engine = std::move(engine); - if (m_engine) { - m_engine->Init(); +void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { + int controller_count; + SDL_JoystickID* joysticks = SDL_GetGamepads(&controller_count); + for (int i = 0; i < 4; i++) { + if (i < controller_count) { + controllers[i]->m_sdl_gamepad = SDL_OpenGamepad(joysticks[i]); + } else { + controllers[i]->m_sdl_gamepad = nullptr; + } } -} -Engine* GameController::GetEngine() { - return m_engine.get(); + // 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; diff --git a/src/input/controller.h b/src/input/controller.h index bbaed75ea..e4a0adc3e 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -3,12 +3,13 @@ #pragma once -#include -#include #include +#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(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0}; @@ -45,24 +39,15 @@ 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 +61,12 @@ 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* GetEngine(); u32 Poll(); + float gyro_poll_rate; + float accel_poll_rate; static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, Libraries::Pad::OrbisFVector3& angularVelocity, float deltaTime, @@ -101,7 +86,21 @@ private: std::array m_states; std::array m_private; - std::unique_ptr m_engine = nullptr; + SDL_Gamepad* m_sdl_gamepad = nullptr; +}; + +class GameControllers { + std::array 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 diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index 3e2d66a6b..4f6b4c6ba 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -22,6 +22,11 @@ #include "common/elf_info.h" #include "common/io_file.h" #include "common/path_util.h" +<<<<<<< HEAD +======= +#include "common/singleton.h" +#include "common/version.h" +>>>>>>> ac515a2bb (Multiple controllers v0) #include "input/controller.h" #include "input/input_mouse.h" @@ -61,42 +66,11 @@ std::list> pressed_keys; std::list toggled_keys; static std::vector 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 output_arrays = { + ControllerAllOutputs(0), + ControllerAllOutputs(1), + ControllerAllOutputs(2), + ControllerAllOutputs(3), }; void ControllerOutput::LinkJoystickAxes() { @@ -197,6 +171,14 @@ InputBinding GetBindingFromString(std::string& line) { return InputBinding(keys[0], keys[1], keys[2]); } +std::optional 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 +236,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 { - 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 +291,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 +385,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 +457,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 +472,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 = SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(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 = SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(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::Instance(); void ToggleKeyInList(InputID input) { if (input.type == InputType::Axis) { @@ -492,7 +526,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 +540,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; @@ -519,8 +553,13 @@ void ControllerOutput::FinalizeUpdate() { // to do it, and it would be inconvenient to force it here, when AddUpdate does the job just // fine, and a toggle doesn't have to checked against every input that's bound to it, it's // enough that one is pressed + case SDL_GAMEPAD_BUTTON_START: + controllers[gamepad_index]->CheckButton(0, SDLGamepadToOrbisButton(button), + new_button_state); + break; 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 +589,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, 0x80, *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, 0x80, *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, 0x80, *new_param * multiplier)); } } @@ -693,22 +734,30 @@ 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); + } } } diff --git a/src/input/input_handler.h b/src/input/input_handler.h index 0178e7937..173fb341c 100644 --- a/src/input/input_handler.h +++ b/src/input/input_handler.h @@ -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,9 +407,64 @@ 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 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); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index e369240c6..4ce02b8ef 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -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(Axis::AxisMax); ++i) { - const auto axis = static_cast(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(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,7 @@ 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::GameControllers::TryOpenSDLControllers(controllers); #if defined(SDL_PLATFORM_WIN32) window_info.type = WindowSystemType::Windows; @@ -325,9 +142,14 @@ 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; + int player_count = Config::GetNumberOfPlayers(); + for (int i = 0; i < player_count; i++) { + AddUserServiceEvent({OrbisUserServiceEventType::Login, i + 1}); + } } WindowSDL::~WindowSDL() = default; @@ -365,14 +187,15 @@ void WindowSDL::WaitEvent() { break; case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_REMOVED: - controller->SetEngine(std::make_unique()); + // todo handle userserviceevents here + Input::GameControllers::TryOpenSDLControllers(controllers); 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); + controllers[0]->SetTouchpadState(event.gtouchpad.finger, + event.type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP, + event.gtouchpad.x, event.gtouchpad.y); break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: @@ -384,10 +207,10 @@ void WindowSDL::WaitEvent() { case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: switch ((SDL_SensorType)event.gsensor.sensor) { case SDL_SENSOR_GYRO: - controller->Gyro(0, event.gsensor.data); + controllers[0]->Gyro(0, event.gsensor.data); break; case SDL_SENSOR_ACCEL: - controller->Acceleration(0, event.gsensor.data); + controllers[0]->Acceleration(0, event.gsensor.data); break; default: break; @@ -421,8 +244,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() { @@ -520,7 +343,7 @@ 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[0]->CheckButton(0, OrbisPadButtonDataOffset::TouchPad, input_down); return; } diff --git a/src/sdl_window.h b/src/sdl_window.h index 48a9be58c..754fa4834 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -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{}; From 8cc101b3b0ea7b0ddc17016722f2d9f97d9b95e0 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Thu, 27 Feb 2025 14:15:14 +0100 Subject: [PATCH 02/30] clang --- src/core/libraries/pad/pad.cpp | 2 +- src/input/controller.h | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index a62afc1a9..b9ddd8ba9 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -365,7 +365,7 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { int connectedCount = 0; bool isConnected = false; Input::State state; - controllers[ handle - 1]->ReadState(&state, &isConnected, &connectedCount); + controllers[handle - 1]->ReadState(&state, &isConnected, &connectedCount); pData->buttons = state.buttonsState; pData->leftStick.x = state.axes[static_cast(Input::Axis::LeftX)]; pData->leftStick.y = state.axes[static_cast(Input::Axis::LeftY)]; diff --git a/src/input/controller.h b/src/input/controller.h index e4a0adc3e..a60a83dea 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -47,7 +47,8 @@ inline int GetAxis(int min, int max, int value) { constexpr u32 MAX_STATES = 32; class GameController { -friend class GameControllers; + friend class GameControllers; + public: GameController(); virtual ~GameController() = default; @@ -91,8 +92,11 @@ private: class GameControllers { std::array controllers; + public: - GameControllers() : controllers({new GameController(), new GameController(), new GameController(), new GameController()}) {}; + GameControllers() + : controllers({new GameController(), new GameController(), new GameController(), + new GameController()}) {}; virtual ~GameControllers() = default; GameController* operator[](const size_t& i) const { if (i > 3) { From c1bb630dfdaaeefb8aa10d7d87605621a24fe604 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Thu, 27 Feb 2025 14:35:41 +0100 Subject: [PATCH 03/30] Make non-remappable inputs work --- src/input/input_handler.cpp | 8 ++++-- src/input/input_handler.h | 2 ++ src/sdl_window.cpp | 50 +++++++++++++++++++++---------------- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index 4f6b4c6ba..93db8448b 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -472,11 +472,11 @@ 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: - gamepad = SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(e.gbutton.which)); + gamepad = GetGamepadIndexFromJoystickId(e.gbutton.which); return InputEvent({InputType::Controller, (u32)e.gbutton.button, gamepad}, e.gbutton.down, 0); case SDL_EVENT_GAMEPAD_AXIS_MOTION: - gamepad = SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(e.gaxis.which)); + gamepad = GetGamepadIndexFromJoystickId(e.gaxis.which); return InputEvent({InputType::Axis, (u32)e.gaxis.axis, gamepad}, true, e.gaxis.value / 256); default: return InputEvent(); @@ -761,4 +761,8 @@ void ActivateOutputsFromInputs() { } } +u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id) { + return SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(id)); +} + } // namespace Input diff --git a/src/input/input_handler.h b/src/input/input_handler.h index 173fb341c..3484ab446 100644 --- a/src/input/input_handler.h +++ b/src/input/input_handler.h @@ -471,4 +471,6 @@ bool UpdatePressedKeys(InputEvent event); void ActivateOutputsFromInputs(); +u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id); + } // namespace Input diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 4ce02b8ef..cd576271b 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -190,32 +190,11 @@ void WindowSDL::WaitEvent() { // todo handle userserviceevents here Input::GameControllers::TryOpenSDLControllers(controllers); break; - case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: - case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: - case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: - controllers[0]->SetTouchpadState(event.gtouchpad.finger, - event.type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP, - event.gtouchpad.x, event.gtouchpad.y); - 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: - switch ((SDL_SensorType)event.gsensor.sensor) { - case SDL_SENSOR_GYRO: - controllers[0]->Gyro(0, event.gsensor.data); - break; - case SDL_SENSOR_ACCEL: - controllers[0]->Acceleration(0, event.gsensor.data); - break; - default: - break; - } - break; case SDL_EVENT_QUIT: is_open = false; break; @@ -343,10 +322,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) { - controllers[0]->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); From 303b4c4ce2a676ae0af2506edd619db7ee79702e Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:03:40 +0100 Subject: [PATCH 04/30] Correctly handle sceUserServiceGetLoginUserIdList Used by CUSA02366 --- src/core/libraries/system/userservice.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 4da46676d..87d6adf42 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -598,11 +598,14 @@ 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] = 2; - userIdList->user_id[2] = 3; - userIdList->user_id[3] = 4; - + int player_count = Config::GetNumberOfPlayers(); + for (int i = 0; i < 4; i++) { + if(i < player_count) { + userIdList->user_id[i] = i + 1; + } else { + userIdList->user_id[i] = ORBIS_USER_SERVICE_USER_ID_INVALID; + } + } return ORBIS_OK; } @@ -1073,7 +1076,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; } From f04cdd7cc89d0e8adaa47cbb6c5acdacf6792113 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:11:04 +0100 Subject: [PATCH 05/30] clang --- src/core/libraries/system/userservice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 87d6adf42..436d36bd5 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -600,7 +600,7 @@ s32 PS4_SYSV_ABI sceUserServiceGetLoginUserIdList(OrbisUserServiceLoginUserIdLis // TODO only first user, do the others as well int player_count = Config::GetNumberOfPlayers(); for (int i = 0; i < 4; i++) { - if(i < player_count) { + if (i < player_count) { userIdList->user_id[i] = i + 1; } else { userIdList->user_id[i] = ORBIS_USER_SERVICE_USER_ID_INVALID; From a2264e9bd0835cd23ef2f9df847185104ee59d24 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:17:49 +0100 Subject: [PATCH 06/30] Cleanup --- src/core/libraries/system/userservice.cpp | 17 ----------------- src/input/input_handler.cpp | 4 ---- 2 files changed, 21 deletions(-) diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 436d36bd5..ea6d4d88d 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -126,23 +126,6 @@ s32 PS4_SYSV_ABI sceUserServiceGetEvent(OrbisUserServiceEvent* event) { } return ORBIS_USER_SERVICE_ERROR_NO_EVENT; - - // // fake a login event - // static bool logged_in_1 = false; - // static bool logged_in_2 = false; - - // if (!logged_in_1) { - // logged_in_1 = true; - // event->event = OrbisUserServiceEventType::Login; - // event->userId = 1; - // return ORBIS_OK; - // } - // if (!logged_in_2) { - // logged_in_2 = true; - // event->event = OrbisUserServiceEventType::Login; - // event->userId = 2; - // return ORBIS_OK; - // } } int PS4_SYSV_ABI sceUserServiceGetEventCalendarType() { diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index 93db8448b..1749ec356 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -553,10 +553,6 @@ void ControllerOutput::FinalizeUpdate(u8 gamepad_index) { // to do it, and it would be inconvenient to force it here, when AddUpdate does the job just // fine, and a toggle doesn't have to checked against every input that's bound to it, it's // enough that one is pressed - case SDL_GAMEPAD_BUTTON_START: - controllers[gamepad_index]->CheckButton(0, SDLGamepadToOrbisButton(button), - new_button_state); - break; default: // is a normal key (hopefully) controllers[gamepad_index]->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state); From c9ece44207bfd19e249e7554121f7d97760f8091 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Thu, 27 Feb 2025 18:30:56 +0100 Subject: [PATCH 07/30] really --- src/input/input_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index 1749ec356..f28111add 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -758,7 +758,7 @@ void ActivateOutputsFromInputs() { } u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id) { - return SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(id)); + return SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(id)) + 1; } } // namespace Input From c05a5223ca63acbb855f96be683e8251089973b6 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 28 Feb 2025 10:19:16 +0100 Subject: [PATCH 08/30] Stash --- src/input/controller.cpp | 12 ++++++++++++ src/sdl_window.cpp | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 8bc458be6..130b9d416 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -6,6 +6,7 @@ #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 { @@ -239,13 +240,24 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f } void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { + using namespace Libraries::UserService; int controller_count; SDL_JoystickID* joysticks = SDL_GetGamepads(&controller_count); for (int i = 0; i < 4; i++) { if (i < controller_count) { + SDL_Gamepad** temp = &(controllers[i]->m_sdl_gamepad); controllers[i]->m_sdl_gamepad = SDL_OpenGamepad(joysticks[i]); + if (*temp == 0) { + AddUserServiceEvent({OrbisUserServiceEventType::Login, + SDL_GetGamepadPlayerIndex(controllers[i]->m_sdl_gamepad) + 2}); + } } else { + SDL_Gamepad** temp = &(controllers[i]->m_sdl_gamepad); controllers[i]->m_sdl_gamepad = nullptr; + if (*temp != 0) { + AddUserServiceEvent( + {OrbisUserServiceEventType::Logout, SDL_GetGamepadPlayerIndex(*temp) + 2}); + } } } diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index cd576271b..546156963 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -270,6 +270,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) { @@ -295,6 +296,17 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) { VideoCore::TriggerCapture(); return; } + // test controller connect/disconnect + else if (input_id == SDLK_F4) { + int player_count = Config::GetNumberOfPlayers(); + AddUserServiceEvent({OrbisUserServiceEventType::Logout, player_count}); + Config::SetNumberOfPlayers(player_count - 1); + } + else if (input_id == SDLK_F5) { + int player_count = Config::GetNumberOfPlayers(); + AddUserServiceEvent({OrbisUserServiceEventType::Login, player_count + 1}); + Config::SetNumberOfPlayers(player_count + 1); + } } // if it's a wheel event, make a timer that turns it off after a set time From 7d80d133eba761eaee5dc4c6be4316396f87bae5 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 28 Feb 2025 11:26:26 +0100 Subject: [PATCH 09/30] More logging --- src/core/libraries/system/userservice.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index ea6d4d88d..9d2aecd98 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -111,6 +111,7 @@ int PS4_SYSV_ABI sceUserServiceGetDiscPlayerFlag() { std::queue 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); } @@ -122,6 +123,8 @@ s32 PS4_SYSV_ABI sceUserServiceGetEvent(OrbisUserServiceEvent* event) { 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; } From e92481770cc2940b61f9bda5ccc39e7600bf13cf Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sat, 29 Mar 2025 10:15:37 +0100 Subject: [PATCH 10/30] clang --- src/core/libraries/system/userservice.cpp | 2 +- src/input/input_handler.cpp | 10 +++------- src/sdl_window.cpp | 3 +-- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 9d2aecd98..986af1169 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -124,7 +124,7 @@ s32 PS4_SYSV_ABI sceUserServiceGetEvent(OrbisUserServiceEvent* event) { event->userId = temp.userId; user_service_event_queue.pop(); LOG_INFO(Lib_UserService, "Event processed by the game: {} {}", (u8)temp.event, - temp.userId); + temp.userId); return ORBIS_OK; } diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index f28111add..f757f191a 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -22,11 +22,7 @@ #include "common/elf_info.h" #include "common/io_file.h" #include "common/path_util.h" -<<<<<<< HEAD -======= #include "common/singleton.h" -#include "common/version.h" ->>>>>>> ac515a2bb (Multiple controllers v0) #include "input/controller.h" #include "input/input_mouse.h" @@ -585,20 +581,20 @@ void ControllerOutput::FinalizeUpdate(u8 gamepad_index) { break; case Axis::TriggerLeft: ApplyDeadzone(new_param, lefttrigger_deadzone); - controllers[gamepad_index]->Axis(0, c_axis, GetAxis(0x0, 0x80, *new_param)); + 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); - controllers[gamepad_index]->Axis(0, c_axis, GetAxis(0x0, 0x80, *new_param)); + 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; } - controllers[gamepad_index]->Axis(0, c_axis, GetAxis(-0x80, 0x80, *new_param * multiplier)); + controllers[gamepad_index]->Axis(0, c_axis, GetAxis(-0x80, 0x7f, *new_param * multiplier)); } } diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 546156963..29d078f62 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -301,8 +301,7 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) { int player_count = Config::GetNumberOfPlayers(); AddUserServiceEvent({OrbisUserServiceEventType::Logout, player_count}); Config::SetNumberOfPlayers(player_count - 1); - } - else if (input_id == SDLK_F5) { + } else if (input_id == SDLK_F5) { int player_count = Config::GetNumberOfPlayers(); AddUserServiceEvent({OrbisUserServiceEventType::Login, player_count + 1}); Config::SetNumberOfPlayers(player_count + 1); From fa2774458884544b6fe4fce146c377b639fe53b7 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 15 Apr 2025 14:32:27 +0200 Subject: [PATCH 11/30] Cleanup + add ability to connect/disconnect controllers at runtime --- src/core/libraries/system/userservice.cpp | 10 ++-- src/input/controller.cpp | 56 ++++++++++++++++++----- src/input/controller.h | 4 ++ src/sdl_window.cpp | 9 ++-- 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 986af1169..5381b4d64 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -6,10 +6,12 @@ #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 { @@ -584,13 +586,9 @@ s32 PS4_SYSV_ABI sceUserServiceGetLoginUserIdList(OrbisUserServiceLoginUserIdLis return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT; } // TODO only first user, do the others as well - int player_count = Config::GetNumberOfPlayers(); for (int i = 0; i < 4; i++) { - if (i < player_count) { - userIdList->user_id[i] = i + 1; - } else { - userIdList->user_id[i] = ORBIS_USER_SERVICE_USER_ID_INVALID; - } + auto controllers = *Common::Singleton::Instance(); + userIdList->user_id[i] = controllers[i]->GetUserId(); } return ORBIS_OK; } diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 130b9d416..06d97fe95 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/config.h" #include "common/logging/log.h" @@ -242,21 +243,52 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { using namespace Libraries::UserService; int controller_count; - SDL_JoystickID* joysticks = SDL_GetGamepads(&controller_count); + SDL_JoystickID* new_joysticks = SDL_GetGamepads(&controller_count); + + std::unordered_set assigned_ids; + std::array slot_taken{false, false, false, false}; + for (int i = 0; i < 4; i++) { - if (i < controller_count) { - SDL_Gamepad** temp = &(controllers[i]->m_sdl_gamepad); - controllers[i]->m_sdl_gamepad = SDL_OpenGamepad(joysticks[i]); - if (*temp == 0) { - AddUserServiceEvent({OrbisUserServiceEventType::Login, - SDL_GetGamepadPlayerIndex(controllers[i]->m_sdl_gamepad) + 2}); + 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; + } } - } else { - SDL_Gamepad** temp = &(controllers[i]->m_sdl_gamepad); - controllers[i]->m_sdl_gamepad = nullptr; - if (*temp != 0) { + if (!still_connected) { AddUserServiceEvent( - {OrbisUserServiceEventType::Logout, SDL_GetGamepadPlayerIndex(*temp) + 2}); + {OrbisUserServiceEventType::Logout, SDL_GetGamepadPlayerIndex(pad) + 1}); + SDL_CloseGamepad(pad); + controllers[i]->m_sdl_gamepad = nullptr; + controllers[i]->user_id = -1; + } + } + } + + // Now, add any new controllers not already assigned + for (int j = 0; j < controller_count; j++) { + SDL_JoystickID id = new_joysticks[j]; + if (assigned_ids.contains(id)) + continue; // already handled + + 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}); + break; } } } diff --git a/src/input/controller.h b/src/input/controller.h index a60a83dea..c5706e7fc 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -65,6 +65,9 @@ public: bool SetVibration(u8 smallMotor, u8 largeMotor); void SetTouchpadState(int touchIndex, bool touchDown, float x, float y); u32 Poll(); + u32 GetUserId() { + return user_id; + } float gyro_poll_rate; float accel_poll_rate; @@ -86,6 +89,7 @@ private: u32 m_first_state = 0; std::array m_states; std::array m_private; + u32 user_id = -1; // ORBIS_USER_SERVICE_USER_ID_INVALID SDL_Gamepad* m_sdl_gamepad = nullptr; }; diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 29d078f62..d9ee4774c 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -117,7 +117,6 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameControllers* controller SDL_SetWindowFullscreen(window, Config::getIsFullscreen()); SDL_InitSubSystem(SDL_INIT_GAMEPAD); - Input::GameControllers::TryOpenSDLControllers(controllers); #if defined(SDL_PLATFORM_WIN32) window_info.type = WindowSystemType::Windows; @@ -146,10 +145,10 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameControllers* controller Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial())); // default login using namespace Libraries::UserService; - int player_count = Config::GetNumberOfPlayers(); - for (int i = 0; i < player_count; i++) { - AddUserServiceEvent({OrbisUserServiceEventType::Login, i + 1}); - } + // int player_count = Config::GetNumberOfPlayers(); + // for (int i = 0; i < player_count; i++) { + // AddUserServiceEvent({OrbisUserServiceEventType::Login, i + 1}); + // } } WindowSDL::~WindowSDL() = default; From ce359b5b4dd54a054784eec4bb9cdef2de6fd2cb Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 15 Apr 2025 15:07:39 +0200 Subject: [PATCH 12/30] Make motion controls work with multiple controllers --- src/input/controller.cpp | 24 ++++++++++++++++++++++-- src/sdl_window.cpp | 15 +++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 06d97fe95..d69338236 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -271,11 +271,10 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { } } - // Now, add any new controllers not already assigned for (int j = 0; j < controller_count; j++) { SDL_JoystickID id = new_joysticks[j]; if (assigned_ids.contains(id)) - continue; // already handled + continue; SDL_Gamepad* pad = SDL_OpenGamepad(id); if (!pad) @@ -288,6 +287,27 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { 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; } } diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index d9ee4774c..526ea8f0c 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -194,6 +194,21 @@ void WindowSDL::WaitEvent() { case SDL_EVENT_GAMEPAD_AXIS_MOTION: OnGamepadEvent(&event); break; + 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: + controllers[controller_id]->Gyro( + 0, event.gsensor.data); + break; + case SDL_SENSOR_ACCEL: + controllers[controller_id]->Acceleration( + 0, event.gsensor.data); + break; + default: + break; + } + break;} case SDL_EVENT_QUIT: is_open = false; break; From edf14ac019b0c2ac2cd01a3cd5d1fd55004509b1 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 15 Apr 2025 15:27:22 +0200 Subject: [PATCH 13/30] Make F4/F5 adding/removing controllers a full feature instead of an accidentally pushed debug tool --- src/core/libraries/system/userservice.cpp | 2 +- src/input/controller.h | 5 +--- src/sdl_window.cpp | 35 +++++++++++++++-------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 5381b4d64..843ea583f 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -588,7 +588,7 @@ s32 PS4_SYSV_ABI sceUserServiceGetLoginUserIdList(OrbisUserServiceLoginUserIdLis // TODO only first user, do the others as well for (int i = 0; i < 4; i++) { auto controllers = *Common::Singleton::Instance(); - userIdList->user_id[i] = controllers[i]->GetUserId(); + userIdList->user_id[i] = controllers[i]->user_id; } return ORBIS_OK; } diff --git a/src/input/controller.h b/src/input/controller.h index c5706e7fc..a33c3ce60 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -65,12 +65,10 @@ public: bool SetVibration(u8 smallMotor, u8 largeMotor); void SetTouchpadState(int touchIndex, bool touchDown, float x, float y); u32 Poll(); - u32 GetUserId() { - return user_id; - } 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, @@ -89,7 +87,6 @@ private: u32 m_first_state = 0; std::array m_states; std::array m_private; - u32 user_id = -1; // ORBIS_USER_SERVICE_USER_ID_INVALID SDL_Gamepad* m_sdl_gamepad = nullptr; }; diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 526ea8f0c..718e5bb7e 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -194,21 +194,20 @@ void WindowSDL::WaitEvent() { case SDL_EVENT_GAMEPAD_AXIS_MOTION: OnGamepadEvent(&event); break; - 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: - controllers[controller_id]->Gyro( - 0, event.gsensor.data); + controllers[controller_id]->Gyro(0, event.gsensor.data); break; case SDL_SENSOR_ACCEL: - controllers[controller_id]->Acceleration( - 0, event.gsensor.data); + controllers[controller_id]->Acceleration(0, event.gsensor.data); break; default: break; } - break;} + break; + } case SDL_EVENT_QUIT: is_open = false; break; @@ -312,13 +311,25 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) { } // test controller connect/disconnect else if (input_id == SDLK_F4) { - int player_count = Config::GetNumberOfPlayers(); - AddUserServiceEvent({OrbisUserServiceEventType::Logout, player_count}); - Config::SetNumberOfPlayers(player_count - 1); + auto controllers = *Common::Singleton::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) { - int player_count = Config::GetNumberOfPlayers(); - AddUserServiceEvent({OrbisUserServiceEventType::Login, player_count + 1}); - Config::SetNumberOfPlayers(player_count + 1); + auto controllers = *Common::Singleton::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; + } + } } } From aa288d91417de2bb294455bf4e5ff51a961ebed2 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 15 Apr 2025 15:34:09 +0200 Subject: [PATCH 14/30] Cleanup + initial code to set custom names for different players --- src/common/config.cpp | 23 ++++++----------------- src/common/config.h | 2 -- src/sdl_window.cpp | 4 ---- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 0a0d2ba34..1c2d54327 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -42,7 +42,7 @@ 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 userNames = {"shadPS4" "shadps4-2", "shadPS4-3", "shadPS4-4"}; static std::string updateChannel; static std::string chooseHomeTab; static std::string backButtonBehavior = "left"; @@ -75,7 +75,6 @@ static double trophyNotificationDuration = 6.0; static bool useUnifiedInputConfig = true; static bool overrideControllerColor = false; static int controllerCustomColorRGB[3] = {0, 0, 255}; -static int numberOfPlayers = 1; static bool separateupdatefolder = false; static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; @@ -112,14 +111,6 @@ static bool showLabelsUnderIcons = true; // Language u32 m_language = 1; // english -void SetNumberOfPlayers(int num) { - numberOfPlayers = num; -} - -int GetNumberOfPlayers() { - return numberOfPlayers; -} - bool allowHDR() { return isHDRAllowed; } @@ -246,7 +237,7 @@ std::string getLogType() { } std::string getUserName() { - return userName; + return userNames[0]; } std::string getUpdateChannel() { @@ -496,7 +487,7 @@ void setSeparateLogFilesEnabled(bool enabled) { } void setUserName(const std::string& type) { - userName = type; + userNames[0] = type; } void setUpdateChannel(const std::string& type) { @@ -772,7 +763,7 @@ void load(const std::filesystem::path& path) { enableDiscordRPC = toml::find_or(general, "enableDiscordRPC", true); logFilter = toml::find_or(general, "logFilter", ""); logType = toml::find_or(general, "logType", "sync"); - userName = toml::find_or(general, "userName", "shadPS4"); + userNames[0] = toml::find_or(general, "userName", "shadPS4"); if (Common::g_is_release) { updateChannel = toml::find_or(general, "updateChannel", "Release"); } else { @@ -798,7 +789,6 @@ void load(const std::filesystem::path& path) { specialPadClass = toml::find_or(input, "specialPadClass", 1); isMotionControlsEnabled = toml::find_or(input, "isMotionControlsEnabled", true); useUnifiedInputConfig = toml::find_or(input, "useUnifiedInputConfig", true); - numberOfPlayers = toml::find_or(input, "numberOfPlayers", 1); } if (data.contains("GPU")) { @@ -971,7 +961,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; @@ -987,7 +977,6 @@ void save(const std::filesystem::path& path) { data["Input"]["specialPadClass"] = specialPadClass; data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled; data["Input"]["useUnifiedInputConfig"] = useUnifiedInputConfig; - data["Input"]["numberOfPlayers"] = numberOfPlayers; data["GPU"]["screenWidth"] = screenWidth; data["GPU"]["screenHeight"] = screenHeight; data["GPU"]["nullGpu"] = isNullGpu; @@ -1119,7 +1108,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 { diff --git a/src/common/config.h b/src/common/config.h index 06d45b686..aba23621c 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -20,8 +20,6 @@ void load(const std::filesystem::path& path); void save(const std::filesystem::path& path); void saveMainWindow(const std::filesystem::path& path); -void SetNumberOfPlayers(int num); -int GetNumberOfPlayers(); std::string getTrophyKey(); void setTrophyKey(std::string key); bool GetLoadGameSizeEnabled(); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 718e5bb7e..16d8478ea 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -145,10 +145,6 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameControllers* controller Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial())); // default login using namespace Libraries::UserService; - // int player_count = Config::GetNumberOfPlayers(); - // for (int i = 0; i < player_count; i++) { - // AddUserServiceEvent({OrbisUserServiceEventType::Login, i + 1}); - // } } WindowSDL::~WindowSDL() = default; From 781a2d0b8d10d4972e0719a3d1442396c1707aad Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 15 Apr 2025 15:39:04 +0200 Subject: [PATCH 15/30] clang --- src/common/config.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 1c2d54327..ff2ae7f34 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -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::array userNames = {"shadPS4" "shadps4-2", "shadPS4-3", "shadPS4-4"}; +static std::array userNames = {"shadPS4" + "shadps4-2", + "shadPS4-3", "shadPS4-4"}; static std::string updateChannel; static std::string chooseHomeTab; static std::string backButtonBehavior = "left"; From 858dda2bd098d038f2553546d744d53a3192e2e7 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 15 Apr 2025 17:15:58 +0200 Subject: [PATCH 16/30] Fix for XInput controllers having a different ID range --- src/input/controller.cpp | 12 ++++++++++++ src/input/controller.h | 4 ++++ src/input/input_handler.cpp | 8 ++------ src/input/input_handler.h | 2 -- src/sdl_window.cpp | 22 ++++++++++++---------- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index d69338236..4b19d581b 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "common/config.h" #include "common/logging/log.h" #include "core/libraries/kernel/time.h" @@ -358,4 +359,15 @@ u32 GameController::Poll() { return 100; } +u8 GameControllers::GetGamepadIndexFromJoystickId(SDL_JoystickID id) { + auto controllers = *Common::Singleton::Instance(); + for (int i = 0; i < 4; i++) { + if (controllers[i]->m_sdl_gamepad && + SDL_GetGamepadID(controllers[i]->m_sdl_gamepad) == id) { + return i + 1; + } + } + return -1; // Not found +} + } // namespace Input diff --git a/src/input/controller.h b/src/input/controller.h index a33c3ce60..b1c0acbc3 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -8,6 +8,8 @@ #include "common/types.h" #include "core/libraries/pad/pad.h" +#include "SDL3/SDL_joystick.h" + struct SDL_Gamepad; namespace Input { @@ -106,6 +108,8 @@ public: return controllers[i]; } static void TryOpenSDLControllers(GameControllers& controllers); + + static u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id); }; } // namespace Input diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index f757f191a..a37b26851 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -468,11 +468,11 @@ 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: - gamepad = GetGamepadIndexFromJoystickId(e.gbutton.which); + gamepad = Input::GameControllers::GetGamepadIndexFromJoystickId(e.gbutton.which); return InputEvent({InputType::Controller, (u32)e.gbutton.button, gamepad}, e.gbutton.down, 0); case SDL_EVENT_GAMEPAD_AXIS_MOTION: - gamepad = GetGamepadIndexFromJoystickId(e.gaxis.which); + gamepad = Input::GameControllers::GetGamepadIndexFromJoystickId(e.gaxis.which); return InputEvent({InputType::Axis, (u32)e.gaxis.axis, gamepad}, true, e.gaxis.value / 256); default: return InputEvent(); @@ -753,8 +753,4 @@ void ActivateOutputsFromInputs() { } } -u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id) { - return SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(id)) + 1; -} - } // namespace Input diff --git a/src/input/input_handler.h b/src/input/input_handler.h index 3484ab446..173fb341c 100644 --- a/src/input/input_handler.h +++ b/src/input/input_handler.h @@ -471,6 +471,4 @@ bool UpdatePressedKeys(InputEvent event); void ActivateOutputsFromInputs(); -u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id); - } // namespace Input diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 16d8478ea..3fb66e56f 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -191,7 +191,8 @@ void WindowSDL::WaitEvent() { OnGamepadEvent(&event); break; case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: { - int controller_id = Input::GetGamepadIndexFromJoystickId(event.gsensor.which) - 1; + int controller_id = + Input::GameControllers::GetGamepadIndexFromJoystickId(event.gsensor.which) - 1; switch ((SDL_SensorType)event.gsensor.sensor) { case SDL_SENSOR_GYRO: controllers[controller_id]->Gyro(0, event.gsensor.data); @@ -354,8 +355,8 @@ 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) { - controllers[Input::GetGamepadIndexFromJoystickId(event->gbutton.which)]->CheckButton( - 0, OrbisPadButtonDataOffset::TouchPad, input_down); + controllers[Input::GameControllers::GetGamepadIndexFromJoystickId(event->gbutton.which)] + ->CheckButton(0, OrbisPadButtonDataOffset::TouchPad, input_down); return; } @@ -363,12 +364,12 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { 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); + controllers[Input::GameControllers::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); + controllers[Input::GameControllers::GetGamepadIndexFromJoystickId(event->gsensor.which)] + ->Acceleration(0, event->gsensor.data); break; default: break; @@ -377,9 +378,10 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { 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); + controllers[Input::GameControllers::GetGamepadIndexFromJoystickId(event->gtouchpad.which)] + ->SetTouchpadState(event->gtouchpad.finger, + event->type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP, event->gtouchpad.x, + event->gtouchpad.y); return; default: break; From 8a1053544b3ddcecb9f74f40bb6431ca0c3e04cc Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 15 Apr 2025 17:35:23 +0200 Subject: [PATCH 17/30] Documentation --- src/qt_gui/kbm_help_dialog.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/qt_gui/kbm_help_dialog.h b/src/qt_gui/kbm_help_dialog.h index 3e39d4397..9812f96bc 100644 --- a/src/qt_gui/kbm_help_dialog.h +++ b/src/qt_gui/kbm_help_dialog.h @@ -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, =; is just as valid as = ; ';' at the ends of lines is also optional. + +You can bind inputs to an output that's not the first controller with: + : = +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() { From b56fbe144057cc3e734d812bd4eac0e0bd4d7bb2 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 15 Apr 2025 18:17:50 +0200 Subject: [PATCH 18/30] Fix merge --- src/common/config.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index ff2ae7f34..6a0810346 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -77,7 +77,6 @@ static double trophyNotificationDuration = 6.0; static bool useUnifiedInputConfig = true; static bool overrideControllerColor = false; static int controllerCustomColorRGB[3] = {0, 0, 255}; -static bool separateupdatefolder = false; static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; static std::string trophyKey; From 38aaa2530fee92c8fdbdc49d9c92ab909b6e81be Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 15 Apr 2025 18:18:04 +0200 Subject: [PATCH 19/30] Revert "Fix for XInput controllers having a different ID range" This reverts commit 858dda2bd098d038f2553546d744d53a3192e2e7. This is even worse than the previous version --- src/input/controller.cpp | 12 ------------ src/input/controller.h | 4 ---- src/input/input_handler.cpp | 8 ++++++-- src/input/input_handler.h | 2 ++ src/sdl_window.cpp | 22 ++++++++++------------ 5 files changed, 18 insertions(+), 30 deletions(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 4b19d581b..d69338236 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -3,7 +3,6 @@ #include #include -#include #include "common/config.h" #include "common/logging/log.h" #include "core/libraries/kernel/time.h" @@ -359,15 +358,4 @@ u32 GameController::Poll() { return 100; } -u8 GameControllers::GetGamepadIndexFromJoystickId(SDL_JoystickID id) { - auto controllers = *Common::Singleton::Instance(); - for (int i = 0; i < 4; i++) { - if (controllers[i]->m_sdl_gamepad && - SDL_GetGamepadID(controllers[i]->m_sdl_gamepad) == id) { - return i + 1; - } - } - return -1; // Not found -} - } // namespace Input diff --git a/src/input/controller.h b/src/input/controller.h index b1c0acbc3..a33c3ce60 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -8,8 +8,6 @@ #include "common/types.h" #include "core/libraries/pad/pad.h" -#include "SDL3/SDL_joystick.h" - struct SDL_Gamepad; namespace Input { @@ -108,8 +106,6 @@ public: return controllers[i]; } static void TryOpenSDLControllers(GameControllers& controllers); - - static u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id); }; } // namespace Input diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index a37b26851..f757f191a 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -468,11 +468,11 @@ 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: - gamepad = Input::GameControllers::GetGamepadIndexFromJoystickId(e.gbutton.which); + gamepad = GetGamepadIndexFromJoystickId(e.gbutton.which); return InputEvent({InputType::Controller, (u32)e.gbutton.button, gamepad}, e.gbutton.down, 0); case SDL_EVENT_GAMEPAD_AXIS_MOTION: - gamepad = Input::GameControllers::GetGamepadIndexFromJoystickId(e.gaxis.which); + gamepad = GetGamepadIndexFromJoystickId(e.gaxis.which); return InputEvent({InputType::Axis, (u32)e.gaxis.axis, gamepad}, true, e.gaxis.value / 256); default: return InputEvent(); @@ -753,4 +753,8 @@ void ActivateOutputsFromInputs() { } } +u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id) { + return SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(id)) + 1; +} + } // namespace Input diff --git a/src/input/input_handler.h b/src/input/input_handler.h index 173fb341c..3484ab446 100644 --- a/src/input/input_handler.h +++ b/src/input/input_handler.h @@ -471,4 +471,6 @@ bool UpdatePressedKeys(InputEvent event); void ActivateOutputsFromInputs(); +u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id); + } // namespace Input diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 3fb66e56f..16d8478ea 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -191,8 +191,7 @@ void WindowSDL::WaitEvent() { OnGamepadEvent(&event); break; case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: { - int controller_id = - Input::GameControllers::GetGamepadIndexFromJoystickId(event.gsensor.which) - 1; + int controller_id = Input::GetGamepadIndexFromJoystickId(event.gsensor.which) - 1; switch ((SDL_SensorType)event.gsensor.sensor) { case SDL_SENSOR_GYRO: controllers[controller_id]->Gyro(0, event.gsensor.data); @@ -355,8 +354,8 @@ 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) { - controllers[Input::GameControllers::GetGamepadIndexFromJoystickId(event->gbutton.which)] - ->CheckButton(0, OrbisPadButtonDataOffset::TouchPad, input_down); + controllers[Input::GetGamepadIndexFromJoystickId(event->gbutton.which)]->CheckButton( + 0, OrbisPadButtonDataOffset::TouchPad, input_down); return; } @@ -364,12 +363,12 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: switch ((SDL_SensorType)event->gsensor.sensor) { case SDL_SENSOR_GYRO: - controllers[Input::GameControllers::GetGamepadIndexFromJoystickId(event->gsensor.which)] - ->Gyro(0, event->gsensor.data); + controllers[Input::GetGamepadIndexFromJoystickId(event->gsensor.which)]->Gyro( + 0, event->gsensor.data); break; case SDL_SENSOR_ACCEL: - controllers[Input::GameControllers::GetGamepadIndexFromJoystickId(event->gsensor.which)] - ->Acceleration(0, event->gsensor.data); + controllers[Input::GetGamepadIndexFromJoystickId(event->gsensor.which)]->Acceleration( + 0, event->gsensor.data); break; default: break; @@ -378,10 +377,9 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: - controllers[Input::GameControllers::GetGamepadIndexFromJoystickId(event->gtouchpad.which)] - ->SetTouchpadState(event->gtouchpad.finger, - event->type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP, event->gtouchpad.x, - event->gtouchpad.y); + 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; From 72f233486dff6f38b5a4932caa28b428a365f97b Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 15 Apr 2025 18:21:59 +0200 Subject: [PATCH 20/30] Remove accidentally left in test values --- src/core/libraries/pad/pad.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index b9ddd8ba9..bd8a0af57 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -96,7 +96,7 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->stickInfo.deadZoneLeft = 1; pInfo->stickInfo.deadZoneRight = 1; pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; - pInfo->connectedCount = 2; + pInfo->connectedCount = 1; pInfo->connected = false; pInfo->deviceClass = OrbisPadDeviceClass::Standard; return 0; @@ -107,7 +107,7 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->stickInfo.deadZoneLeft = 1; pInfo->stickInfo.deadZoneRight = 1; pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; - pInfo->connectedCount = 2; + pInfo->connectedCount = 1; pInfo->connected = true; pInfo->deviceClass = OrbisPadDeviceClass::Standard; if (Config::getUseSpecialPad()) { From 01d8f85bc87b9940ca859593418ed467e5de04df Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 25 Apr 2025 10:56:00 +0200 Subject: [PATCH 21/30] Fix gamepad indexing + cleanup --- src/core/libraries/pad/pad.cpp | 6 ++---- src/input/controller.cpp | 25 +------------------------ src/input/input_handler.cpp | 6 +++++- src/sdl_window.cpp | 2 +- 4 files changed, 9 insertions(+), 30 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index bd8a0af57..293d31272 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -289,8 +289,7 @@ 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); + LOG_TRACE(Lib_Pad, "handle: {}", handle); int connected_count = 0; bool connected = false; Input::State states[64]; @@ -356,8 +355,7 @@ 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); + LOG_TRACE(Lib_Pad, "handle: {}", handle); if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) { return ORBIS_PAD_ERROR_INVALID_HANDLE; } diff --git a/src/input/controller.cpp b/src/input/controller.cpp index d69338236..740001bda 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -285,6 +285,7 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { controllers[i]->m_sdl_gamepad = pad; controllers[i]->user_id = i + 1; slot_taken[i] = true; + SDL_SetGamepadPlayerIndex(pad, i); AddUserServiceEvent( {OrbisUserServiceEventType::Login, SDL_GetGamepadPlayerIndex(pad) + 1}); @@ -312,30 +313,6 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { } } } - - // 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() { diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index f757f191a..ba7686a95 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -754,7 +754,11 @@ void ActivateOutputsFromInputs() { } u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id) { - return SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(id)) + 1; + u8 index = SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(id)); + if (index > 3) [[unlikely]] { + UNREACHABLE_MSG("Index out of bounds: {}", index); + } + return index; } } // namespace Input diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 16d8478ea..c5a0b2799 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -191,7 +191,7 @@ void WindowSDL::WaitEvent() { OnGamepadEvent(&event); break; case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: { - int controller_id = Input::GetGamepadIndexFromJoystickId(event.gsensor.which) - 1; + int controller_id = Input::GetGamepadIndexFromJoystickId(event.gsensor.which); switch ((SDL_SensorType)event.gsensor.sensor) { case SDL_SENSOR_GYRO: controllers[controller_id]->Gyro(0, event.gsensor.data); From c962e70fd8bb904f47acc87bc8e446b81a9470a3 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:14:34 +0200 Subject: [PATCH 22/30] Fine you win --- src/core/libraries/pad/pad.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 293d31272..218939e6f 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -290,6 +290,9 @@ int PS4_SYSV_ABI scePadOutputReport() { int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { LOG_TRACE(Lib_Pad, "handle: {}", handle); + if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE || handle != std::clamp(handle, 1, 4)) { + return ORBIS_PAD_ERROR_INVALID_HANDLE; + } int connected_count = 0; bool connected = false; Input::State states[64]; @@ -356,7 +359,7 @@ int PS4_SYSV_ABI scePadReadHistory() { int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { LOG_TRACE(Lib_Pad, "handle: {}", handle); - if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) { + if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE || handle != std::clamp(handle, 1, 4)) { return ORBIS_PAD_ERROR_INVALID_HANDLE; } auto controllers = *Common::Singleton::Instance(); From f29d79ec40382479ef6faab77a93043cd5dd6333 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:32:15 +0200 Subject: [PATCH 23/30] Initialize the first controller for the keyboard even if no controllers are connected --- src/core/libraries/system/userservice.cpp | 2 +- src/input/controller.cpp | 9 +++++++++ src/sdl_window.cpp | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 843ea583f..3f737e0a4 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -586,8 +586,8 @@ s32 PS4_SYSV_ABI sceUserServiceGetLoginUserIdList(OrbisUserServiceLoginUserIdLis return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT; } // TODO only first user, do the others as well + auto controllers = *Common::Singleton::Instance(); for (int i = 0; i < 4; i++) { - auto controllers = *Common::Singleton::Instance(); userIdList->user_id[i] = controllers[i]->user_id; } return ORBIS_OK; diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 740001bda..0d9a307ba 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -240,6 +240,8 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f } } +bool is_first_check = true; + void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { using namespace Libraries::UserService; int controller_count; @@ -313,6 +315,13 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { } } } + if (is_first_check) [[unlikely]] { + is_first_check = false; + if (controller_count == 0) { + controllers[0]->user_id = 1; + AddUserServiceEvent({OrbisUserServiceEventType::Login, 1}); + } + } } u32 GameController::Poll() { diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index c5a0b2799..cf2d101ae 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -143,7 +143,7 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameControllers* controller // input handler init-s Input::ControllerOutput::LinkJoystickAxes(); Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial())); - // default login + Input::GameControllers::TryOpenSDLControllers(controllers); using namespace Libraries::UserService; } From e26ed735337bc82b72b472f5769334a995458a8a Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:38:05 +0200 Subject: [PATCH 24/30] Print error on SDL_SetGamepadPlayerIndex failure --- src/input/controller.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 0d9a307ba..41e35eaaf 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -287,7 +287,10 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { controllers[i]->m_sdl_gamepad = pad; controllers[i]->user_id = i + 1; slot_taken[i] = true; - SDL_SetGamepadPlayerIndex(pad, i); + bool err = SDL_SetGamepadPlayerIndex(pad, i); + if (!err) { + LOG_ERROR(Input, "Failed to set controller index: {}", SDL_GetError()); + } AddUserServiceEvent( {OrbisUserServiceEventType::Login, SDL_GetGamepadPlayerIndex(pad) + 1}); From ba29985151b1c95703a60aaa7d1e1a5956ffb468 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 25 Apr 2025 20:20:25 +0200 Subject: [PATCH 25/30] This time for sure --- src/input/controller.cpp | 17 +++++++++++++---- src/input/controller.h | 3 +++ src/input/input_handler.cpp | 12 ++---------- src/input/input_handler.h | 2 -- src/sdl_window.cpp | 22 ++++++++++++---------- 5 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 41e35eaaf..dd98ec2e6 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "common/config.h" #include "common/logging/log.h" #include "core/libraries/kernel/time.h" @@ -287,10 +288,7 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { controllers[i]->m_sdl_gamepad = pad; controllers[i]->user_id = i + 1; slot_taken[i] = true; - bool err = SDL_SetGamepadPlayerIndex(pad, i); - if (!err) { - LOG_ERROR(Input, "Failed to set controller index: {}", SDL_GetError()); - } + controllers[i]->player_index = i; AddUserServiceEvent( {OrbisUserServiceEventType::Login, SDL_GetGamepadPlayerIndex(pad) + 1}); @@ -347,4 +345,15 @@ u32 GameController::Poll() { return 100; } +u8 GameControllers::GetGamepadIndexFromJoystickId(SDL_JoystickID id) { + auto& controllers = *Common::Singleton::Instance(); + auto gamepad = SDL_GetGamepadFromID(id); + for (int i = 0; i < 4; i++) { + if (controllers[i]->m_sdl_gamepad == gamepad) { + return controllers[i]->player_index; + } + } + UNREACHABLE_MSG("Gamepad not registered!"); +} + } // namespace Input diff --git a/src/input/controller.h b/src/input/controller.h index a33c3ce60..b62c9e05f 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -4,6 +4,7 @@ #pragma once #include +#include "SDL3/SDL_joystick.h" #include "common/assert.h" #include "common/types.h" #include "core/libraries/pad/pad.h" @@ -89,6 +90,7 @@ private: std::array m_private; SDL_Gamepad* m_sdl_gamepad = nullptr; + u8 player_index = -1; }; class GameControllers { @@ -106,6 +108,7 @@ public: return controllers[i]; } static void TryOpenSDLControllers(GameControllers& controllers); + static u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id); }; } // namespace Input diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index ba7686a95..c6f8120d2 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -468,11 +468,11 @@ 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: - gamepad = GetGamepadIndexFromJoystickId(e.gbutton.which); + gamepad = GameControllers::GetGamepadIndexFromJoystickId(e.gbutton.which) + 1; return InputEvent({InputType::Controller, (u32)e.gbutton.button, gamepad}, e.gbutton.down, 0); case SDL_EVENT_GAMEPAD_AXIS_MOTION: - gamepad = GetGamepadIndexFromJoystickId(e.gaxis.which); + gamepad = GameControllers::GetGamepadIndexFromJoystickId(e.gaxis.which) + 1; return InputEvent({InputType::Axis, (u32)e.gaxis.axis, gamepad}, true, e.gaxis.value / 256); default: return InputEvent(); @@ -753,12 +753,4 @@ void ActivateOutputsFromInputs() { } } -u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id) { - u8 index = SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(id)); - if (index > 3) [[unlikely]] { - UNREACHABLE_MSG("Index out of bounds: {}", index); - } - return index; -} - } // namespace Input diff --git a/src/input/input_handler.h b/src/input/input_handler.h index 3484ab446..173fb341c 100644 --- a/src/input/input_handler.h +++ b/src/input/input_handler.h @@ -471,6 +471,4 @@ bool UpdatePressedKeys(InputEvent event); void ActivateOutputsFromInputs(); -u8 GetGamepadIndexFromJoystickId(SDL_JoystickID id); - } // namespace Input diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index cf2d101ae..3f4723da9 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -191,7 +191,8 @@ void WindowSDL::WaitEvent() { OnGamepadEvent(&event); break; case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: { - int controller_id = Input::GetGamepadIndexFromJoystickId(event.gsensor.which); + int controller_id = + Input::GameControllers::GetGamepadIndexFromJoystickId(event.gsensor.which); switch ((SDL_SensorType)event.gsensor.sensor) { case SDL_SENSOR_GYRO: controllers[controller_id]->Gyro(0, event.gsensor.data); @@ -354,8 +355,8 @@ 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) { - controllers[Input::GetGamepadIndexFromJoystickId(event->gbutton.which)]->CheckButton( - 0, OrbisPadButtonDataOffset::TouchPad, input_down); + controllers[Input::GameControllers::GetGamepadIndexFromJoystickId(event->gbutton.which)] + ->CheckButton(0, OrbisPadButtonDataOffset::TouchPad, input_down); return; } @@ -363,12 +364,12 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { 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); + controllers[Input::GameControllers::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); + controllers[Input::GameControllers::GetGamepadIndexFromJoystickId(event->gsensor.which)] + ->Acceleration(0, event->gsensor.data); break; default: break; @@ -377,9 +378,10 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { 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); + controllers[Input::GameControllers::GetGamepadIndexFromJoystickId(event->gtouchpad.which)] + ->SetTouchpadState(event->gtouchpad.finger, + event->type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP, event->gtouchpad.x, + event->gtouchpad.y); return; default: break; From eaecab404112a0e716ed4961fc15668aa4dc3d08 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 25 Apr 2025 20:37:41 +0200 Subject: [PATCH 26/30] bruh --- src/input/controller.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index dd98ec2e6..915a44dfd 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -265,8 +265,7 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { } } if (!still_connected) { - AddUserServiceEvent( - {OrbisUserServiceEventType::Logout, SDL_GetGamepadPlayerIndex(pad) + 1}); + AddUserServiceEvent({OrbisUserServiceEventType::Logout, i + 1}); SDL_CloseGamepad(pad); controllers[i]->m_sdl_gamepad = nullptr; controllers[i]->user_id = -1; @@ -289,8 +288,7 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { controllers[i]->user_id = i + 1; slot_taken[i] = true; controllers[i]->player_index = i; - AddUserServiceEvent( - {OrbisUserServiceEventType::Login, SDL_GetGamepadPlayerIndex(pad) + 1}); + AddUserServiceEvent({OrbisUserServiceEventType::Login, i + 1}); if (SDL_SetGamepadSensorEnabled(controllers[i]->m_sdl_gamepad, SDL_SENSOR_GYRO, true)) { From 057f2f9beb55442a29b1fa381b8f26e59b155877 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 25 Apr 2025 21:14:08 +0200 Subject: [PATCH 27/30] Even more logging --- src/input/controller.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 915a44dfd..c6aae0b6e 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -285,6 +285,7 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { for (int i = 0; i < 4; i++) { if (!slot_taken[i]) { controllers[i]->m_sdl_gamepad = pad; + LOG_INFO(Input, "Gamepad registered for slot {}! Handle: {}", i, (void*)pad); controllers[i]->user_id = i + 1; slot_taken[i] = true; controllers[i]->player_index = i; @@ -346,12 +347,15 @@ u32 GameController::Poll() { u8 GameControllers::GetGamepadIndexFromJoystickId(SDL_JoystickID id) { auto& controllers = *Common::Singleton::Instance(); auto gamepad = SDL_GetGamepadFromID(id); + if (!gamepad) { + UNREACHABLE_MSG("Gamepad is null!"); + } for (int i = 0; i < 4; i++) { if (controllers[i]->m_sdl_gamepad == gamepad) { return controllers[i]->player_index; } } - UNREACHABLE_MSG("Gamepad not registered!"); + UNREACHABLE_MSG("Gamepad not registered! Handle: {}", (void*)gamepad); } } // namespace Input From 05ee6f0f425dbda9530a6fb3401d8994bf3182b4 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 25 Apr 2025 21:54:11 +0200 Subject: [PATCH 28/30] Please work --- src/input/controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index c6aae0b6e..63631653c 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -351,7 +351,7 @@ u8 GameControllers::GetGamepadIndexFromJoystickId(SDL_JoystickID id) { UNREACHABLE_MSG("Gamepad is null!"); } for (int i = 0; i < 4; i++) { - if (controllers[i]->m_sdl_gamepad == gamepad) { + if (SDL_GetGamepadID(controllers[i]->m_sdl_gamepad) == id) { return controllers[i]->player_index; } } From e0320352f15f9902783fb8373ad2376c99ef1503 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sat, 26 Apr 2025 09:38:23 +0200 Subject: [PATCH 29/30] Good enough --- src/core/libraries/system/userservice.cpp | 12 +++++++++--- src/input/controller.cpp | 12 ++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 3f737e0a4..df6eeee14 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -580,15 +580,21 @@ int PS4_SYSV_ABI sceUserServiceGetLoginFlag() { } s32 PS4_SYSV_ABI sceUserServiceGetLoginUserIdList(OrbisUserServiceLoginUserIdList* userIdList) { - LOG_DEBUG(Lib_UserService, "called"); + // LOG_DEBUG(Lib_UserService, "called"); if (userIdList == nullptr) { LOG_ERROR(Lib_UserService, "user_id is null"); return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT; } // TODO only first user, do the others as well auto controllers = *Common::Singleton::Instance(); - for (int i = 0; i < 4; i++) { - userIdList->user_id[i] = controllers[i]->user_id; + int li = 0; + for (int ci = 0; ci < 4; ci++) { + if (controllers[ci]->user_id != -1) { + userIdList->user_id[li++] = controllers[ci]->user_id; + } + } + for (; li < 4; li++) { + userIdList->user_id[li] = -1; } return ORBIS_OK; } diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 63631653c..911805b13 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -269,6 +269,9 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { SDL_CloseGamepad(pad); controllers[i]->m_sdl_gamepad = nullptr; controllers[i]->user_id = -1; + slot_taken[i] = false; + } else { + controllers[i]->player_index = i; } } } @@ -285,7 +288,8 @@ void GameControllers::TryOpenSDLControllers(GameControllers& controllers) { for (int i = 0; i < 4; i++) { if (!slot_taken[i]) { controllers[i]->m_sdl_gamepad = pad; - LOG_INFO(Input, "Gamepad registered for slot {}! Handle: {}", i, (void*)pad); + LOG_INFO(Input, "Gamepad registered for slot {}! Handle: {}", i, + SDL_GetGamepadID(pad)); controllers[i]->user_id = i + 1; slot_taken[i] = true; controllers[i]->player_index = i; @@ -346,16 +350,12 @@ u32 GameController::Poll() { u8 GameControllers::GetGamepadIndexFromJoystickId(SDL_JoystickID id) { auto& controllers = *Common::Singleton::Instance(); - auto gamepad = SDL_GetGamepadFromID(id); - if (!gamepad) { - UNREACHABLE_MSG("Gamepad is null!"); - } for (int i = 0; i < 4; i++) { if (SDL_GetGamepadID(controllers[i]->m_sdl_gamepad) == id) { return controllers[i]->player_index; } } - UNREACHABLE_MSG("Gamepad not registered! Handle: {}", (void*)gamepad); + UNREACHABLE_MSG("Handle {} is not registered!", id); } } // namespace Input From ed0f1a91322b29644d477409c9805edad23edefb Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sun, 25 May 2025 16:08:16 +0200 Subject: [PATCH 30/30] Test for XInput --- src/input/controller.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 911805b13..eae786e44 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -349,13 +349,9 @@ u32 GameController::Poll() { } u8 GameControllers::GetGamepadIndexFromJoystickId(SDL_JoystickID id) { - auto& controllers = *Common::Singleton::Instance(); - for (int i = 0; i < 4; i++) { - if (SDL_GetGamepadID(controllers[i]->m_sdl_gamepad) == id) { - return controllers[i]->player_index; - } - } - UNREACHABLE_MSG("Handle {} is not registered!", id); + s32 index = SDL_GetGamepadPlayerIndex(SDL_GetGamepadFromID(id)); + LOG_INFO(Input, "Gamepad index: {}", index); + return index; } } // namespace Input