diff --git a/src/common/config.cpp b/src/common/config.cpp index 9c316949a..dd4f72221 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -73,6 +73,7 @@ static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; static std::string trophyKey; static bool isPSNSignedIn = false; +static std::string defaultControllerID = ""; // Gui static bool load_game_size = true; @@ -528,6 +529,14 @@ void setPSNSignedIn(bool sign) { isPSNSignedIn = sign; } +std::string getDefaultControllerID() { + return defaultControllerID; +} + +void setDefaultControllerID(std::string id) { + defaultControllerID = id; +} + void load(const std::filesystem::path& path) { // If the configuration file does not exist, create it and return std::error_code error; @@ -566,6 +575,7 @@ void load(const std::filesystem::path& path) { checkCompatibilityOnStartup = toml::find_or(general, "checkCompatibilityOnStartup", false); chooseHomeTab = toml::find_or(general, "chooseHomeTab", "Release"); + defaultControllerID = toml::find_or(general, "defaultControllerID", ""); } if (data.contains("Input")) { @@ -725,6 +735,7 @@ void save(const std::filesystem::path& path) { data["General"]["sideTrophy"] = isSideTrophy; data["General"]["compatibilityEnabled"] = compatibilityData; data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup; + data["General"]["defaultControllerID"] = defaultControllerID; data["Input"]["cursorState"] = cursorState; data["Input"]["cursorHideTimeout"] = cursorHideTimeout; data["Input"]["useSpecialPad"] = useSpecialPad; diff --git a/src/common/config.h b/src/common/config.h index 38114983f..624a3a51f 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -93,6 +93,8 @@ bool isDevKitConsole(); // no set bool vkValidationGpuEnabled(); // no set bool getIsMotionControlsEnabled(); void setIsMotionControlsEnabled(bool use); +std::string getDefaultControllerID(); +void setDefaultControllerID(std::string id); // TODO bool GetLoadGameSizeEnabled(); diff --git a/src/imgui/renderer/imgui_impl_sdl3.cpp b/src/imgui/renderer/imgui_impl_sdl3.cpp index ccd31d03a..bd0c1a5db 100644 --- a/src/imgui/renderer/imgui_impl_sdl3.cpp +++ b/src/imgui/renderer/imgui_impl_sdl3.cpp @@ -730,6 +730,7 @@ static void UpdateGamepads() { ImGuiIO& io = ImGui::GetIO(); SdlData* bd = GetBackendData(); + /* // Update list of gamepads to use if (bd->want_update_gamepads_list && bd->gamepad_mode != ImGui_ImplSDL3_GamepadMode_Manual) { CloseGamepads(); @@ -741,8 +742,9 @@ static void UpdateGamepads() { if (bd->gamepad_mode == ImGui_ImplSDL3_GamepadMode_AutoFirst) break; } - bd->want_update_gamepads_list = false; + bd->want_update_gamepads_list = false; } + */ // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) diff --git a/src/qt_gui/control_settings.cpp b/src/qt_gui/control_settings.cpp index 319daecdd..15f4d4b95 100644 --- a/src/qt_gui/control_settings.cpp +++ b/src/qt_gui/control_settings.cpp @@ -9,8 +9,12 @@ #include "common/path_util.h" #include "control_settings.h" #include "input/input_handler.h" +#include "sdl_window.h" #include "ui_control_settings.h" +std::string ControllerSelect::ActiveGamepad = ""; +std::string ControllerSelect::DefaultGamepad = ""; + ControlSettings::ControlSettings(std::shared_ptr game_info_get, bool isGameRunning, std::string GameRunningSerial, QWidget* parent) : QDialog(parent), m_game_info(game_info_get), GameRunning(isGameRunning), @@ -21,7 +25,6 @@ ControlSettings::ControlSettings(std::shared_ptr game_info_get, b if (!GameRunning) { SDL_InitSubSystem(SDL_INIT_GAMEPAD); SDL_InitSubSystem(SDL_INIT_EVENTS); - CheckGamePad(); } else { SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); } @@ -29,6 +32,8 @@ ControlSettings::ControlSettings(std::shared_ptr game_info_get, b AddBoxItems(); SetUIValuestoMappings(); UpdateLightbarColor(); + CheckGamePad(); + ResetActiveControllerBox(); installEventFilter(this); ButtonsList = {ui->CrossButton, @@ -119,6 +124,18 @@ ControlSettings::ControlSettings(std::shared_ptr game_info_get, b connect(this, &ControlSettings::AxisChanged, this, [this]() { ConnectAxisInputs(MappingButton); }); + connect(ui->ActiveGamepadBox, &QComboBox::currentIndexChanged, this, + &ControlSettings::ActiveControllerChanged); + connect(ui->DefaultGamepadButton, &QPushButton::clicked, this, [this]() { + char pszGUID[33]; + SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepads[ui->ActiveGamepadBox->currentIndex()]), + pszGUID, 33); + Config::setDefaultControllerID(std::string(pszGUID)); + Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml"); + QMessageBox::information(this, tr("Default Controller Selected"), + tr("Active controller set as default")); + }); + RemapWrapper = SdlEventWrapper::Wrapper::GetInstance(); SdlEventWrapper::Wrapper::wrapperActive = true; QObject::connect(RemapWrapper, &SdlEventWrapper::Wrapper::SDLEvent, this, @@ -129,6 +146,99 @@ ControlSettings::ControlSettings(std::shared_ptr game_info_get, b } } +void ControlSettings::ActiveControllerChanged(int value) { + char pszGUID[33]; + SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepads[value]), pszGUID, 33); + QString GUID = QString::fromStdString(std::string(pszGUID)).right(16); + ui->ActiveGamepadLabel->setText("ID: " + GUID); + ControllerSelect::ActiveGamepad = std::string(pszGUID); + + if (!GameRunning) { + if (gamepad) { + SDL_CloseGamepad(gamepad); + gamepad = nullptr; + } + + gamepads = SDL_GetGamepads(&gamepad_count); + if (!gamepads) { + LOG_ERROR(Input, "Cannot get gamepad list: {}", SDL_GetError()); + return; + } + + for (int i = 0; i < gamepad_count; i++) { + char pszGUID[33]; + SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepads[i]), pszGUID, 33); + std::string currentGUID = std::string(pszGUID); + if (currentGUID == ControllerSelect::ActiveGamepad) { + gamepad = SDL_OpenGamepad(gamepads[i]); + LOG_WARNING(Input, "Opened gamepad: {}", i); + if (!gamepad) { + LOG_ERROR(Input, "Failed to open gamepad: {}", SDL_GetError()); + } + break; + } + } + + if (!gamepad) { + LOG_ERROR(Input, "Failed to open gamepad: {}", SDL_GetError()); + return; + } + } +} + +void ControlSettings::ResetActiveControllerBox() { + SDL_free(gamepads); + gamepads = SDL_GetGamepads(&gamepad_count); + if (!gamepads) { + LOG_ERROR(Input, "Cannot get gamepad list: {}", SDL_GetError()); + return; + } + + if (gamepad_count == 0) { + ui->ActiveGamepadBox->addItem("No gamepads detected"); + ui->ActiveGamepadBox->setCurrentIndex(0); + LOG_INFO(Input, "No gamepad found!"); + return; + } else { + for (int i = 0; i < gamepad_count; i++) { + QString name = SDL_GetGamepadNameForID(gamepads[i]); + ui->ActiveGamepadBox->addItem(QString("%1: %2").arg(QString::number(i + 1), name)); + } + } + + char pszGUID[33]; + if (ControllerSelect::ActiveGamepad != "") { + for (int i = 0; i < gamepad_count; i++) { + SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepads[i]), pszGUID, 33); + std::string currentGUID = std::string(pszGUID); + if (currentGUID == ControllerSelect::ActiveGamepad) { + SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepads[i]), pszGUID, 33); + QString GUID = QString::fromStdString(std::string(pszGUID)).right(16); + ui->ActiveGamepadLabel->setText("ID: " + GUID); + ui->ActiveGamepadBox->setCurrentIndex(i); + break; + } + } + } else if (Config::getDefaultControllerID() != "") { + for (int i = 0; i < gamepad_count; i++) { + SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepads[i]), pszGUID, 33); + std::string currentGUID = std::string(pszGUID); + if (currentGUID == Config::getDefaultControllerID()) { + SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepads[i]), pszGUID, 33); + QString GUID = QString::fromStdString(std::string(pszGUID)).right(16); + ui->ActiveGamepadLabel->setText("ID: " + GUID); + ui->ActiveGamepadBox->setCurrentIndex(i); + break; + } + } + } else { + SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepads[0]), pszGUID, 33); + QString GUID = QString::fromStdString(std::string(pszGUID)).right(16); + ui->ActiveGamepadLabel->setText("ID: " + GUID); + ui->ActiveGamepadBox->setCurrentIndex(0); + } +} + void ControlSettings::SaveControllerConfig(bool CloseOnSave) { QList list; list << ui->RStickUpButton << ui->RStickRightButton << ui->LStickUpButton @@ -640,38 +750,42 @@ void ControlSettings::UpdateLightbarColor() { } void ControlSettings::CheckGamePad() { - if (GameRunning) - return; - - if (gamepad) { + if ((gamepad)) { SDL_CloseGamepad(gamepad); gamepad = nullptr; } - int gamepad_count; - SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); - + 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; + if (!GameRunning) { + if (ControllerSelect::ActiveGamepad != "") { + for (int i = 0; i < gamepad_count; i++) { + char pszGUID[33]; + SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepads[i]), pszGUID, 33); + std::string currentGUID = std::string(pszGUID); + if (currentGUID == ControllerSelect::ActiveGamepad) { + gamepad = SDL_OpenGamepad(gamepads[i]); + LOG_WARNING(Input, "Opened gamepad: {}", i); + if (!gamepad) { + LOG_ERROR(Input, "Failed to open gamepad: {}", SDL_GetError()); + } + break; + } + } + } else { + LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count); + gamepad = SDL_OpenGamepad(gamepads[0]); + } + + if (!gamepad) { + LOG_ERROR(Input, "Failed to open gamepad 0: {}", SDL_GetError()); + return; + } } - - LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count); - gamepad = SDL_OpenGamepad(gamepads[0]); - - if (!gamepad) { - LOG_ERROR(Input, "Failed to open gamepad 0: {}", SDL_GetError()); - SDL_free(gamepads); - return; - } - - SDL_free(gamepads); } void ControlSettings::DisableMappingButtons() { @@ -914,6 +1028,8 @@ void ControlSettings::pollSDLEvents() { } if (event.type == SDL_EVENT_GAMEPAD_ADDED) { + ui->ActiveGamepadBox->clear(); + ResetActiveControllerBox(); CheckGamePad(); } @@ -923,8 +1039,12 @@ void ControlSettings::pollSDLEvents() { void ControlSettings::Cleanup() { SdlEventWrapper::Wrapper::wrapperActive = false; - if (gamepad) + if (gamepad) { SDL_CloseGamepad(gamepad); + gamepad = nullptr; + } + + SDL_free(gamepads); if (!GameRunning) { SDL_Event quitLoop{}; @@ -937,6 +1057,9 @@ void ControlSettings::Cleanup() { SDL_Quit(); } else { SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0"); + SDL_Event checkGamepad{}; + checkGamepad.type = SDL_EVENT_CHANGE_CONTROLLER; + SDL_PushEvent(&checkGamepad); } } diff --git a/src/qt_gui/control_settings.h b/src/qt_gui/control_settings.h index 76d16b84e..91c5442f0 100644 --- a/src/qt_gui/control_settings.h +++ b/src/qt_gui/control_settings.h @@ -1,6 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - +#pragma once #include #include #include @@ -29,6 +29,7 @@ private Q_SLOTS: void CheckMapping(QPushButton*& button); void StartTimer(QPushButton*& button, bool isButton); void ConnectAxisInputs(QPushButton*& button); + void ActiveControllerChanged(int value); private: std::unique_ptr ui; @@ -45,6 +46,7 @@ private: void DisableMappingButtons(); void EnableMappingButtons(); void Cleanup(); + void ResetActiveControllerBox(); QList ButtonsList; QList AxisList; @@ -59,9 +61,11 @@ private: bool MappingCompleted = false; QString mapping; int MappingTimer; + int gamepad_count; QTimer* timer; QPushButton* MappingButton; SDL_Gamepad* gamepad = nullptr; + SDL_JoystickID* gamepads; SdlEventWrapper::Wrapper* RemapWrapper; QFuture Polling; @@ -81,3 +85,8 @@ protected: Cleanup(); } }; + +namespace ControllerSelect { +extern std::string ActiveGamepad; +extern std::string DefaultGamepad; +} // namespace ControllerSelect diff --git a/src/qt_gui/control_settings.ui b/src/qt_gui/control_settings.ui index 2eb4c754c..c8cdc655c 100644 --- a/src/qt_gui/control_settings.ui +++ b/src/qt_gui/control_settings.ui @@ -1,6 +1,4 @@ - ControlSettings @@ -11,8 +9,8 @@ 0 0 - 1114 - 794 + 1124 + 847 @@ -33,8 +31,8 @@ 0 0 - 1094 - 744 + 1104 + 797 @@ -42,8 +40,8 @@ 0 0 - 1091 - 741 + 1101 + 791 @@ -246,14 +244,82 @@ + + + + L1 and L2 + + + + + + L2 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + + + + + + + 0 + 0 + + + + L1 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + + + + + Qt::Orientation::Vertical - - QSizePolicy::Policy::Maximum - 20 @@ -512,7 +578,7 @@ - + 0 @@ -598,199 +664,100 @@ - - - - - - - 0 - - - - - + + + + + 9 + true + + + + Active Gamepad + + - - - - - - 0 - 0 - - - - L1 - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - unmapped - - - - - - - - - - Qt::Orientation::Horizontal - - - QSizePolicy::Policy::Fixed - - - - 133 - 20 - - - - - - - - - 0 - 0 - - - - R1 - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - unmapped - - - - - - - + + + + 9 + false + + + - - - - - L2 - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - unmapped - - - - - - - - - - Options - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - unmapped - - - - - - - - - - R2 - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - unmapped - - - - - - - + + + + 9 + false + + + + Gamepad ID + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + - - - - + + + + + + + 9 + true + + + + Default Gamepad + + + + + + No default selected + + + + + + + + 9 + false + + + + Gamepad ID + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + + 9 + true + + + + Set Active Gamepad as Default + + + + + @@ -880,20 +847,32 @@ - - - Qt::Orientation::Horizontal + + + Options - - QSizePolicy::Policy::Fixed - - - - 133 - 20 - - - + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + @@ -1338,13 +1317,84 @@ + + + + R1 and R2 + + + + + + R2 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + + + + + + + 0 + 0 + + + + R1 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + + + + + Qt::Orientation::Vertical - QSizePolicy::Policy::Maximum + QSizePolicy::Policy::Expanding diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 69819a00f..4d0715060 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -21,6 +21,7 @@ #include "video_core/renderdoc.h" #ifdef ENABLE_QT_GUI +#include "qt_gui/control_settings.h" #include "qt_gui/sdl_event_wrapper.h" #endif @@ -100,6 +101,7 @@ void SDLInputEngine::Init() { if (m_gamepad) { SDL_CloseGamepad(m_gamepad); m_gamepad = nullptr; + LOG_WARNING(Input, "closed gamepad"); } int gamepad_count; @@ -114,12 +116,51 @@ void SDLInputEngine::Init() { return; } - LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count); - m_gamepad = SDL_OpenGamepad(gamepads[0]); + std::string activeGamepad = ""; + std::string defaultGamepad = Config::getDefaultControllerID(); +#ifdef ENABLE_QT_GUI + activeGamepad = ControllerSelect::ActiveGamepad; +#endif + + // If user selects an active gamepad, use that, otherwise, try the default if (!m_gamepad) { - LOG_ERROR(Input, "Failed to open gamepad 0: {}", SDL_GetError()); - SDL_free(gamepads); - return; + if (activeGamepad != "") { + for (int i = 0; i < gamepad_count; i++) { + char pszGUID[33]; + SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepads[i]), pszGUID, 33); + std::string currentGUID = std::string(pszGUID); + if (currentGUID == activeGamepad) { + m_gamepad = SDL_OpenGamepad(gamepads[i]); + if (!m_gamepad) { + LOG_ERROR(Input, "Failed to open gamepad: {}", SDL_GetError()); + } + break; + } + } + } else if (Config::getDefaultControllerID() != "") { + for (int i = 0; i < gamepad_count; i++) { + char pszGUID[33]; + SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepads[i]), pszGUID, 33); + std::string currentGUID = std::string(pszGUID); + if (currentGUID == Config::getDefaultControllerID()) { + m_gamepad = SDL_OpenGamepad(gamepads[i]); + if (!m_gamepad) { + LOG_ERROR(Input, "Failed to open gamepad: {}", SDL_GetError()); + } + break; + } + } + } + } + + if (!m_gamepad) { + 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: {}", SDL_GetError()); + SDL_free(gamepads); + return; + } } SDL_Joystick* joystick = SDL_GetGamepadJoystick(m_gamepad); @@ -426,6 +467,9 @@ void WindowSDL::WaitEvent() { DebugState.PauseGuestThreads(); } break; + case SDL_EVENT_CHANGE_CONTROLLER: + controller->GetEngine()->Init(); + break; default: break; } diff --git a/src/sdl_window.h b/src/sdl_window.h index 48a9be58c..ce79eedd0 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -9,6 +9,7 @@ #include "string" #define SDL_EVENT_TOGGLE_FULLSCREEN (SDL_EVENT_USER + 1) #define SDL_EVENT_TOGGLE_PAUSE (SDL_EVENT_USER + 2) +#define SDL_EVENT_CHANGE_CONTROLLER (SDL_EVENT_USER + 3) struct SDL_Window; struct SDL_Gamepad;