diff --git a/CMakeLists.txt b/CMakeLists.txt index ab846fa9d..466933608 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1068,6 +1068,8 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/gui_settings.h src/qt_gui/settings.cpp src/qt_gui/settings.h + src/qt_gui/sdl_event_wrapper.cpp + src/qt_gui/sdl_event_wrapper.h ${EMULATOR} ${RESOURCE_FILES} ${TRANSLATIONS} diff --git a/src/qt_gui/control_settings.cpp b/src/qt_gui/control_settings.cpp index 5f2dbc2f5..0ce6bfb37 100644 --- a/src/qt_gui/control_settings.cpp +++ b/src/qt_gui/control_settings.cpp @@ -10,17 +10,21 @@ #include "control_settings.h" #include "ui_control_settings.h" -ControlSettings::ControlSettings(std::shared_ptr game_info_get, QWidget* parent) - : QDialog(parent), m_game_info(game_info_get), ui(new Ui::ControlSettings) { +ControlSettings::ControlSettings(std::shared_ptr game_info_get, bool isGameRunning, + QWidget* parent) + : QDialog(parent), m_game_info(game_info_get), GameRunning(isGameRunning), + ui(new Ui::ControlSettings) { ui->setupUi(this); - SDL_InitSubSystem(SDL_INIT_GAMEPAD); - SDL_InitSubSystem(SDL_INIT_EVENTS); + if (!GameRunning) { + SDL_InitSubSystem(SDL_INIT_GAMEPAD); + SDL_InitSubSystem(SDL_INIT_EVENTS); + } else { + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + } CheckGamePad(); - QFuture future = QtConcurrent::run(&ControlSettings::pollSDLEvents, this); - AddBoxItems(); SetUIValuestoMappings(); UpdateLightbarColor(); @@ -113,6 +117,14 @@ ControlSettings::ControlSettings(std::shared_ptr game_info_get, Q [this]() { CheckMapping(MappingButton); }); connect(this, &ControlSettings::AxisChanged, this, [this]() { ConnectAxisInputs(MappingButton); }); + + RemapWrapper = SdlEventWrapper::Wrapper::GetInstance(); + SdlEventWrapper::Wrapper::wrapperActive = true; + QObject::connect(RemapWrapper, &SdlEventWrapper::Wrapper::SDLEvent, this, + &ControlSettings::processSDLEvents); + + if (!GameRunning) + QFuture future = QtConcurrent::run(&ControlSettings::pollSDLEvents, this); } void ControlSettings::SaveControllerConfig(bool CloseOnSave) { @@ -621,9 +633,12 @@ void ControlSettings::UpdateLightbarColor() { } void ControlSettings::CheckGamePad() { - if (m_gamepad) { - SDL_CloseGamepad(m_gamepad); - m_gamepad = nullptr; + if (GameRunning) + return; + + if (gamepad) { + SDL_CloseGamepad(gamepad); + gamepad = nullptr; } int gamepad_count; @@ -641,9 +656,9 @@ void ControlSettings::CheckGamePad() { } LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count); - m_gamepad = SDL_OpenGamepad(gamepads[0]); + gamepad = SDL_OpenGamepad(gamepads[0]); - if (!m_gamepad) { + if (!gamepad) { LOG_ERROR(Input, "Failed to open gamepad 0: {}", SDL_GetError()); SDL_free(gamepads); return; @@ -697,7 +712,6 @@ void ControlSettings::StartTimer(QPushButton*& button, bool isButton) { MappingTimer = 3; isButton ? EnableButtonMapping = true : EnableAxisMapping = true; MappingCompleted = false; - triggerWasPressed = false; mapping = button->text(); DisableMappingButtons(); @@ -743,8 +757,8 @@ void ControlSettings::SetMapping(QString input) { mapping = input; MappingCompleted = true; if (EnableAxisMapping) { - emit AxisChanged(); emit PushGamepadEvent(); + emit AxisChanged(); } } @@ -760,141 +774,138 @@ bool ControlSettings::eventFilter(QObject* obj, QEvent* event) { return QDialog::eventFilter(obj, event); } -void ControlSettings::pollSDLEvents() { - SDL_Event event; - - while (isRunning) { - if (!SDL_WaitEvent(&event)) { - return; - } - - if (event.type == SDL_EVENT_QUIT) { - isRunning = false; +void ControlSettings::processSDLEvents(int Type, int Input, int Value) { + if (Type == SDL_EVENT_QUIT) { + SdlEventWrapper::Wrapper::wrapperActive = false; + if (gamepad) + SDL_CloseGamepad(gamepad); + if (!GameRunning) { SDL_QuitSubSystem(SDL_INIT_GAMEPAD); SDL_QuitSubSystem(SDL_INIT_EVENTS); SDL_Quit(); + } else { + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0"); + } + } + + if (Type == SDL_EVENT_GAMEPAD_ADDED) { + if (!GameRunning) + CheckGamePad(); + } + + if (EnableButtonMapping) { + if (Type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) { + switch (Input) { + case SDL_GAMEPAD_BUTTON_SOUTH: + pressedButtons.insert("cross"); + break; + case SDL_GAMEPAD_BUTTON_EAST: + pressedButtons.insert("circle"); + break; + case SDL_GAMEPAD_BUTTON_NORTH: + pressedButtons.insert("triangle"); + break; + case SDL_GAMEPAD_BUTTON_WEST: + pressedButtons.insert("square"); + break; + case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: + pressedButtons.insert("l1"); + break; + case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: + pressedButtons.insert("r1"); + break; + case SDL_GAMEPAD_BUTTON_LEFT_STICK: + pressedButtons.insert("l3"); + break; + case SDL_GAMEPAD_BUTTON_RIGHT_STICK: + pressedButtons.insert("r3"); + break; + case SDL_GAMEPAD_BUTTON_DPAD_UP: + pressedButtons.insert("pad_up"); + break; + case SDL_GAMEPAD_BUTTON_DPAD_DOWN: + pressedButtons.insert("pad_down"); + break; + case SDL_GAMEPAD_BUTTON_DPAD_LEFT: + pressedButtons.insert("pad_left"); + break; + case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: + pressedButtons.insert("pad_right"); + break; + case SDL_GAMEPAD_BUTTON_BACK: + pressedButtons.insert("back"); + break; + case SDL_GAMEPAD_BUTTON_LEFT_PADDLE1: + pressedButtons.insert("lpaddle_high"); + break; + case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1: + pressedButtons.insert("rpaddle_high"); + break; + case SDL_GAMEPAD_BUTTON_LEFT_PADDLE2: + pressedButtons.insert("lpaddle_low"); + break; + case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2: + pressedButtons.insert("rpaddle_low"); + break; + case SDL_GAMEPAD_BUTTON_START: + pressedButtons.insert("options"); + break; + default: + break; + } } - if (event.type == SDL_EVENT_GAMEPAD_ADDED) - CheckGamePad(); - - if (EnableButtonMapping) { - if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) { - switch (event.gbutton.button) { - case SDL_GAMEPAD_BUTTON_SOUTH: - pressedButtons.insert("cross"); - break; - case SDL_GAMEPAD_BUTTON_EAST: - pressedButtons.insert("circle"); - break; - case SDL_GAMEPAD_BUTTON_NORTH: - pressedButtons.insert("triangle"); - break; - case SDL_GAMEPAD_BUTTON_WEST: - pressedButtons.insert("square"); - break; - case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: - pressedButtons.insert("l1"); - break; - case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: - pressedButtons.insert("r1"); - break; - case SDL_GAMEPAD_BUTTON_LEFT_STICK: - pressedButtons.insert("l3"); - break; - case SDL_GAMEPAD_BUTTON_RIGHT_STICK: - pressedButtons.insert("r3"); - break; - case SDL_GAMEPAD_BUTTON_DPAD_UP: - pressedButtons.insert("pad_up"); - break; - case SDL_GAMEPAD_BUTTON_DPAD_DOWN: - pressedButtons.insert("pad_down"); - break; - case SDL_GAMEPAD_BUTTON_DPAD_LEFT: - pressedButtons.insert("pad_left"); - break; - case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: - pressedButtons.insert("pad_right"); - break; - case SDL_GAMEPAD_BUTTON_BACK: - pressedButtons.insert("back"); - break; - case SDL_GAMEPAD_BUTTON_LEFT_PADDLE1: - pressedButtons.insert("lpaddle_high"); - break; - case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1: - pressedButtons.insert("rpaddle_high"); - break; - case SDL_GAMEPAD_BUTTON_LEFT_PADDLE2: - pressedButtons.insert("lpaddle_low"); - break; - case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2: - pressedButtons.insert("rpaddle_low"); - break; - case SDL_GAMEPAD_BUTTON_START: - pressedButtons.insert("options"); - break; - default: - break; - } - } - - if (event.type == SDL_EVENT_GAMEPAD_BUTTON_UP) { - emit PushGamepadEvent(); - } - - if (event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION) { - // SDL trigger axis values range from 0 to 32000, set mapping on half movement - // Set zone for trigger release signal arbitrarily at 5000 - switch (event.gaxis.axis) { + if (Type == SDL_EVENT_GAMEPAD_AXIS_MOTION) { + // SDL trigger axis values range from 0 to 32000, set mapping on half movement + // Set zone for trigger release signal arbitrarily at 5000 + if (Value > 16000) { + switch (Input) { case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: - if (event.gaxis.value > 16000) { - pressedButtons.insert("l2"); - triggerWasPressed = true; - } else if (event.gaxis.value < 5000) { - if (triggerWasPressed) - emit PushGamepadEvent(); - } + pressedButtons.insert("l2"); break; case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: - if (event.gaxis.value > 16000) { - pressedButtons.insert("r2"); - triggerWasPressed = true; - } else if (event.gaxis.value < 5000) { - if (triggerWasPressed) - emit PushGamepadEvent(); - } + pressedButtons.insert("r2"); break; default: break; } } + } - } else if (EnableAxisMapping) { - if (event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION) { - // SDL stick axis values range from -32000 to 32000, set mapping on half movement - if (event.gaxis.value > 16000 || event.gaxis.value < -16000) { - switch (event.gaxis.axis) { - case SDL_GAMEPAD_AXIS_LEFTX: - SetMapping("axis_left_x"); - break; - case SDL_GAMEPAD_AXIS_LEFTY: - SetMapping("axis_left_y"); - break; - case SDL_GAMEPAD_AXIS_RIGHTX: - SetMapping("axis_right_x"); - break; - case SDL_GAMEPAD_AXIS_RIGHTY: - SetMapping("axis_right_y"); - break; - default: - break; - } + } else if (EnableAxisMapping) { + if (Type == SDL_EVENT_GAMEPAD_AXIS_MOTION) { + // SDL stick axis values range from -32000 to 32000, set mapping on half movement + if (Value > 16000 || Value < -16000) { + switch (Input) { + case SDL_GAMEPAD_AXIS_LEFTX: + SetMapping("axis_left_x"); + break; + case SDL_GAMEPAD_AXIS_LEFTY: + SetMapping("axis_left_y"); + break; + case SDL_GAMEPAD_AXIS_RIGHTX: + SetMapping("axis_right_x"); + break; + case SDL_GAMEPAD_AXIS_RIGHTY: + SetMapping("axis_right_y"); + break; + default: + break; } } } } } +void ControlSettings::pollSDLEvents() { + SDL_Event event; + while (SdlEventWrapper::Wrapper::wrapperActive) { + if (!SDL_WaitEvent(&event)) { + return; + } + + SdlEventWrapper::Wrapper::GetInstance()->Wrapper::ProcessEvent(&event); + } +} + ControlSettings::~ControlSettings() {} diff --git a/src/qt_gui/control_settings.h b/src/qt_gui/control_settings.h index f50e20cf2..4cb7ef88b 100644 --- a/src/qt_gui/control_settings.h +++ b/src/qt_gui/control_settings.h @@ -1,11 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include #include #include "game_info.h" +#include "sdl_event_wrapper.h" namespace Ui { class ControlSettings; @@ -14,7 +14,7 @@ class ControlSettings; class ControlSettings : public QDialog { Q_OBJECT public: - explicit ControlSettings(std::shared_ptr game_info_get, + explicit ControlSettings(std::shared_ptr game_info_get, bool GameRunning, QWidget* parent = nullptr); ~ControlSettings(); @@ -39,6 +39,7 @@ private: void SetUIValuestoMappings(); void GetGameTitle(); void CheckGamePad(); + void processSDLEvents(int Type, int Input, int Value); void pollSDLEvents(); void SetMapping(QString input); void DisableMappingButtons(); @@ -48,8 +49,7 @@ private: QList AxisList; QSet pressedButtons; - bool isRunning = true; - bool triggerWasPressed = false; + bool GameRunning; bool EnableButtonMapping = false; bool EnableAxisMapping = false; bool MappingCompleted = false; @@ -57,7 +57,8 @@ private: int MappingTimer; QTimer* timer; QPushButton* MappingButton; - SDL_Gamepad* m_gamepad = nullptr; + SDL_Gamepad* gamepad = nullptr; + SdlEventWrapper::Wrapper* RemapWrapper; const std::vector ControllerInputs = { "cross", "circle", "square", "triangle", "l1", diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 57e60b93e..0ecf4b76d 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -473,13 +473,8 @@ void MainWindow::CreateConnects() { }); connect(ui->controllerButton, &QPushButton::clicked, this, [this]() { - if (!isGameRunning) { - auto configWindow = new ControlSettings(m_game_info, this); - configWindow->exec(); - } else { - QMessageBox::information(this, tr("Remapping not available"), - tr("Cannot remap controller while game is running")); - } + ControlSettings* remapWindow = new ControlSettings(m_game_info, isGameRunning, this); + remapWindow->exec(); }); connect(ui->keyboardButton, &QPushButton::clicked, this, [this]() { diff --git a/src/qt_gui/sdl_event_wrapper.cpp b/src/qt_gui/sdl_event_wrapper.cpp new file mode 100644 index 000000000..41ca84c52 --- /dev/null +++ b/src/qt_gui/sdl_event_wrapper.cpp @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "sdl_event_wrapper.h" + +using namespace SdlEventWrapper; + +Wrapper* Wrapper::WrapperInstance = nullptr; +bool Wrapper::wrapperActive = false; + +Wrapper::Wrapper(QObject* parent) : QObject(parent) {} + +Wrapper* Wrapper::GetInstance() { + if (WrapperInstance == nullptr) { + WrapperInstance = new Wrapper(); + } + return WrapperInstance; +} + +bool Wrapper::ProcessEvent(SDL_Event* event) { + if (!wrapperActive) + return false; + + switch (event->type) { + case SDL_EVENT_QUIT: + emit SDLEvent(SDL_EVENT_QUIT, 0, 0); + return true; + case SDL_EVENT_GAMEPAD_ADDED: + emit SDLEvent(SDL_EVENT_GAMEPAD_ADDED, 0, 0); + return true; + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + emit SDLEvent(SDL_EVENT_GAMEPAD_BUTTON_DOWN, event->gbutton.button, 0); + return true; + case SDL_EVENT_GAMEPAD_BUTTON_UP: + emit SDLEvent(SDL_EVENT_GAMEPAD_BUTTON_UP, event->gbutton.button, 0); + return true; + case SDL_EVENT_GAMEPAD_AXIS_MOTION: + emit SDLEvent(SDL_EVENT_GAMEPAD_AXIS_MOTION, event->gaxis.axis, event->gaxis.value); + return true; + default: + return false; + } +} +Wrapper::~Wrapper() {} diff --git a/src/qt_gui/sdl_event_wrapper.h b/src/qt_gui/sdl_event_wrapper.h new file mode 100644 index 000000000..54d8c9cd1 --- /dev/null +++ b/src/qt_gui/sdl_event_wrapper.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include + +namespace SdlEventWrapper { + +class Wrapper : public QObject { + Q_OBJECT + +public: + explicit Wrapper(QObject* parent = nullptr); + ~Wrapper(); + bool ProcessEvent(SDL_Event* event); + static Wrapper* GetInstance(); + static bool wrapperActive; + static Wrapper* WrapperInstance; + +signals: + void SDLEvent(int Type, int Input, int Value); +}; + +} // namespace SdlEventWrapper diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 735f14639..69819a00f 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -20,6 +20,10 @@ #include "sdl_window.h" #include "video_core/renderdoc.h" +#ifdef ENABLE_QT_GUI +#include "qt_gui/sdl_event_wrapper.h" +#endif + #ifdef __APPLE__ #include "SDL3/SDL_metal.h" #endif @@ -340,6 +344,13 @@ void WindowSDL::WaitEvent() { return; } +#ifdef ENABLE_QT_GUI + if (SdlEventWrapper::Wrapper::wrapperActive) { + if (SdlEventWrapper::Wrapper::GetInstance()->ProcessEvent(&event)) + return; + } +#endif + if (ImGui::Core::ProcessEvent(&event)) { return; }