diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 4301313cfd..6b059a3ead 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -117,6 +117,14 @@ void ConfigureDialog::ApplyConfiguration() { Settings::LogSettings(); } +void ConfigureDialog::MainWindowFocusIn() { + input_tab->DisableConfiguration(); +} + +void ConfigureDialog::MainWindowFocusOut() { + input_tab->EnableConfiguration(); +} + void ConfigureDialog::changeEvent(QEvent* event) { if (event->type() == QEvent::LanguageChange) { RetranslateUI(); diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 1f724834aa..cd54b31be8 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -46,6 +46,10 @@ public: void ApplyConfiguration(); +public slots: + void MainWindowFocusIn(); + void MainWindowFocusOut(); + private slots: void OnLanguageChanged(const QString& locale); diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 1db374d4a2..d85417fb5f 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -181,6 +181,18 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, LoadConfiguration(); } +void ConfigureInput::EnableConfiguration() { + for (auto controller : player_controllers) { + controller->EnableConfiguration(); + } +} + +void ConfigureInput::DisableConfiguration() { + for (auto controller : player_controllers) { + controller->DisableConfiguration(); + } +} + QList ConfigureInput::GetSubTabs() const { return { ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5, diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index c89189c365..3380a51162 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -43,6 +43,12 @@ public: /// Initializes the input dialog with the given input subsystem. void Initialize(InputCommon::InputSubsystem* input_subsystem_, std::size_t max_players = 8); + /// Enables configuration on controllers + void EnableConfiguration(); + + /// Disables configuration on controllers + void DisableConfiguration(); + /// Save all button configurations to settings file. void ApplyConfiguration(); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 9e5a40fe74..508ddbd05f 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -274,26 +274,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i is_powered_on{is_powered_on_}, input_subsystem{input_subsystem_}, profiles(profiles_), timeout_timer(std::make_unique()), poll_timer(std::make_unique()), bottom_row{bottom_row_}, hid_core{hid_core_} { - if (player_index == 0) { - auto* emulated_controller_p1 = - hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); - auto* emulated_controller_handheld = - hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); - emulated_controller_p1->SaveCurrentConfig(); - emulated_controller_p1->EnableConfiguration(); - emulated_controller_handheld->SaveCurrentConfig(); - emulated_controller_handheld->EnableConfiguration(); - if (emulated_controller_handheld->IsConnected(true)) { - emulated_controller_p1->Disconnect(); - emulated_controller = emulated_controller_handheld; - } else { - emulated_controller = emulated_controller_p1; - } - } else { - emulated_controller = hid_core.GetEmulatedControllerByIndex(player_index); - emulated_controller->SaveCurrentConfig(); - emulated_controller->EnableConfiguration(); - } + EnableConfiguration(); ui->setupUi(this); setFocusPolicy(Qt::ClickFocus); @@ -771,6 +752,33 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } ConfigureInputPlayer::~ConfigureInputPlayer() { + DisableConfiguration(); +} + +void ConfigureInputPlayer::EnableConfiguration() { + if (player_index == 0) { + auto* emulated_controller_p1 = + hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); + auto* emulated_controller_handheld = + hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); + emulated_controller_p1->SaveCurrentConfig(); + emulated_controller_p1->EnableConfiguration(); + emulated_controller_handheld->SaveCurrentConfig(); + emulated_controller_handheld->EnableConfiguration(); + if (emulated_controller_handheld->IsConnected(true)) { + emulated_controller_p1->Disconnect(); + emulated_controller = emulated_controller_handheld; + } else { + emulated_controller = emulated_controller_p1; + } + } else { + emulated_controller = hid_core.GetEmulatedControllerByIndex(player_index); + emulated_controller->SaveCurrentConfig(); + emulated_controller->EnableConfiguration(); + } +} + +void ConfigureInputPlayer::DisableConfiguration() { if (player_index == 0) { auto* emulated_controller_p1 = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 79434fdd8d..fe885c6fb6 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -56,6 +56,12 @@ public: bool is_powered_on_, bool debug = false); ~ConfigureInputPlayer() override; + /// Enables configuration on controllers + void EnableConfiguration(); + + /// Disables configuration on controllers + void DisableConfiguration(); + /// Save all button configurations to settings file. void ApplyConfiguration(); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4081af391d..3456d0c338 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -72,6 +72,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include #include #include +#include #include #ifdef HAVE_SDL2 @@ -1246,6 +1247,16 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { } } +void GMainWindow::FocusWindowChanged(QWindow* focusWindow) { + if (focusWindow && focusWindow->winId() == winId() && !main_window_in_focus) { + emit FocusIn(); + main_window_in_focus = true; + } else if (main_window_in_focus) { + emit FocusOut(); + main_window_in_focus = false; + } +} + void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::BootGame, this, &GMainWindow::BootGame); connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); @@ -3023,16 +3034,33 @@ void GMainWindow::ResetWindowSize1080() { } void GMainWindow::OnConfigure() { - const auto old_theme = UISettings::values.theme; - const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); + old_configure_value = {UISettings::values.theme, + UISettings::values.enable_discord_presence.GetValue()}; Settings::SetConfiguringGlobal(true); - ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system, - !multiplayer_state->IsHostingPublicRoom()); - connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, - &GMainWindow::OnLanguageChanged); - const auto result = configure_dialog.exec(); + configure_dialog.reset(); // The destructor must run first + configure_dialog = + std::make_unique(this, hotkey_registry, input_subsystem.get(), *system, + !multiplayer_state->IsHostingPublicRoom()); + connect(configure_dialog.get(), &ConfigureDialog::LanguageChanged, this, + &GMainWindow::OnLanguageChanged); + connect(configure_dialog.get(), &ConfigureDialog::finished, this, + &GMainWindow::OnConfigureFinished); + + configure_dialog->show(); + + connect(this, &GMainWindow::FocusIn, configure_dialog.get(), + &ConfigureDialog::MainWindowFocusIn); + connect(this, &GMainWindow::FocusOut, configure_dialog.get(), + &ConfigureDialog::MainWindowFocusOut); +} + +void GMainWindow::OnConfigureFinished(int result) { + // Configure dialog needs to be deleted after this function finishes to run the appropriate + // destructors + std::unique_ptr config_dialog(std::move(configure_dialog)); + if (result != QDialog::Accepted && !UISettings::values.configuration_applied && !UISettings::values.reset_to_defaults) { // Runs if the user hit Cancel or closed the window, and did not ever press the Apply button @@ -3042,7 +3070,7 @@ void GMainWindow::OnConfigure() { // Only apply new changes if user hit Okay // This is here to avoid applying changes if the user hit Apply, made some changes, then hit // Cancel - configure_dialog.ApplyConfiguration(); + config_dialog->ApplyConfiguration(); } else if (UISettings::values.reset_to_defaults) { LOG_INFO(Frontend, "Resetting all settings to defaults"); if (!Common::FS::RemoveFile(config->GetConfigFilePath())) { @@ -3079,10 +3107,11 @@ void GMainWindow::OnConfigure() { } InitializeHotkeys(); - if (UISettings::values.theme != old_theme) { + if (UISettings::values.theme != old_configure_value.theme) { UpdateUITheme(); } - if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { + if (UISettings::values.enable_discord_presence.GetValue() != + old_configure_value.discord_presence) { SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); } @@ -4232,6 +4261,8 @@ int main(int argc, char* argv[]) { QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window, &GMainWindow::OnAppFocusStateChanged); + QObject::connect(&app, &QGuiApplication::focusWindowChanged, &main_window, + &GMainWindow::FocusWindowChanged); int result = app.exec(); detached_tasks.WaitForAllTasks(); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 6a9992d05b..60e776e566 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -23,6 +23,7 @@ class Config; class ClickableLabel; +class ConfigureDialog; class EmuThread; class GameList; class GImageInfo; @@ -122,6 +123,12 @@ class GMainWindow : public QMainWindow { UI_EMU_STOPPING, }; + // Used to save previous configure values to check for changes + struct OldConfigureValue { + QString theme; + bool discord_presence; + }; + public: void filterBarSetChecked(bool state); void UpdateUITheme(); @@ -132,6 +139,11 @@ public: void AcceptDropEvent(QDropEvent* event); signals: + /// Emitted when this main window receives focus + void FocusIn(); + + /// Emitted when this main window loses focus + void FocusOut(); /** * Signal that is emitted when a new EmuThread has been created and an emulation session is @@ -195,6 +207,7 @@ public slots: void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args, bool is_local); void OnAppFocusStateChanged(Qt::ApplicationState state); + void FocusWindowChanged(QWindow* focusWindow); void OnTasStateChanged(); private: @@ -303,6 +316,7 @@ private slots: void OnMenuInstallToNAND(); void OnMenuRecentFile(); void OnConfigure(); + void OnConfigureFinished(int result); void OnConfigureTas(); void OnTasStartStop(); void OnTasRecord(); @@ -430,6 +444,11 @@ private: // Install progress dialog QProgressDialog* install_progress; + // Configuration dialog, use unique_ptr as it is possible for the user to open the configure + // menu multiple times, thus we must delete the old menu + std::unique_ptr configure_dialog; + OldConfigureValue old_configure_value; + // Last game booted, used for multi-process apps QString last_filename_booted; @@ -445,6 +464,8 @@ private: // True if TAS recording dialog is visible bool is_tas_recording_dialog_active{}; + bool main_window_in_focus = false; + #ifdef __unix__ QSocketNotifier* sig_interrupt_notifier; static std::array sig_interrupt_fds;