From 88d1bef1cec21f8e89d22b2c35ed74df6c4af4fa Mon Sep 17 00:00:00 2001 From: David Griswold Date: Tue, 31 Mar 2026 11:53:57 +0300 Subject: [PATCH] Bugfix: All desktop hotkeys now work in dual-window mode. The fullscreen hotkey now works differently. It always changes the fullscreen setting, and attempts to fullscreen both windows in dual-screen mode *if* they are on different screens. Othewise it only fullscreens the main screen for consistency between layouts. Geometry is always saved and restored from UISettings for more consistency. --- CMakeModules/GenerateSettingKeys.cmake | 1 + src/citra_qt/citra_qt.cpp | 54 +++++++++++++++++--------- src/citra_qt/configuration/config.cpp | 46 ++++++++++++---------- src/citra_qt/uisettings.h | 2 +- 4 files changed, 62 insertions(+), 41 deletions(-) diff --git a/CMakeModules/GenerateSettingKeys.cmake b/CMakeModules/GenerateSettingKeys.cmake index 90110cd33..4060dba68 100644 --- a/CMakeModules/GenerateSettingKeys.cmake +++ b/CMakeModules/GenerateSettingKeys.cmake @@ -194,6 +194,7 @@ if (ENABLE_QT) "geometry" "state" "geometryRenderWindow" + "geometrySecondaryWindow" "gameListHeaderState" "microProfileDialogGeometry" "name" diff --git a/src/citra_qt/citra_qt.cpp b/src/citra_qt/citra_qt.cpp index 9bb0b2e34..62ea02612 100644 --- a/src/citra_qt/citra_qt.cpp +++ b/src/citra_qt/citra_qt.cpp @@ -786,11 +786,19 @@ void GMainWindow::InitializeHotkeys() { const auto link_action_shortcut = [&](QAction* action, const QString& action_name, const bool primary_only = false) { static const QString main_window = QStringLiteral("Main Window"); - action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); + auto context = hotkey_registry.GetShortcutContext(main_window, action_name); + auto shortcut = hotkey_registry.GetKeySequence(main_window, action_name); + action->setShortcut(shortcut); + action->setShortcutContext(context); action->setAutoRepeat(false); this->addAction(action); - if (!primary_only) - secondary_window->addAction(action); + // handle the shortcuts that are different per-screen + if (context == Qt::WidgetShortcut) { + render_window->addAction(action); + if (!primary_only) { + secondary_window->addAction(action); + } + } }; link_action_shortcut(ui->action_Load_File, QStringLiteral("Load File")); @@ -802,7 +810,7 @@ void GMainWindow::InitializeHotkeys() { link_action_shortcut(ui->action_Stop, QStringLiteral("Stop Emulation")); link_action_shortcut(ui->action_Show_Filter_Bar, QStringLiteral("Toggle Filter Bar")); link_action_shortcut(ui->action_Show_Status_Bar, QStringLiteral("Toggle Status Bar")); - link_action_shortcut(ui->action_Fullscreen, fullscreen, true); + link_action_shortcut(ui->action_Fullscreen, fullscreen); link_action_shortcut(ui->action_Capture_Screenshot, QStringLiteral("Capture Screenshot")); link_action_shortcut(ui->action_Screen_Layout_Swap_Screens, QStringLiteral("Swap Screens")); link_action_shortcut(ui->action_Screen_Layout_Upright_Screens, @@ -824,10 +832,7 @@ void GMainWindow::InitializeHotkeys() { // QShortcut Hotkeys const auto connect_shortcut = [&](const QString& action_name, const auto& function) { const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this); - const auto* secondary_hotkey = - hotkey_registry.GetHotkey(main_window, action_name, secondary_window); connect(hotkey, &QShortcut::activated, this, function); - connect(secondary_hotkey, &QShortcut::activated, this, function); }; connect_shortcut(QStringLiteral("Toggle Screen Layout"), &GMainWindow::ToggleScreenLayout); @@ -897,11 +902,6 @@ void GMainWindow::InitializeHotkeys() { connect(action, SIGNAL(triggered()), this, slot); secondary_window->addAction(action); }; - - // Use the same fullscreen hotkey as the main window - const auto fullscreen_hotkey = hotkey_registry.GetKeySequence(main_window, fullscreen); - add_secondary_window_hotkey(action_secondary_fullscreen, fullscreen_hotkey, - SLOT(ToggleSecondaryFullscreen())); } void GMainWindow::SetDefaultUIGeometry() { @@ -920,6 +920,7 @@ void GMainWindow::RestoreUIState() { restoreGeometry(UISettings::values.geometry); restoreState(UISettings::values.state); render_window->restoreGeometry(UISettings::values.renderwindow_geometry); + secondary_window->restoreGeometry(UISettings::values.secondarywindow_geometry); #if MICROPROFILE_ENABLED microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); microProfileDialog->setVisible(UISettings::values.microprofile_visible.GetValue()); @@ -2592,14 +2593,25 @@ void GMainWindow::ToggleSecondaryFullscreen() { } void GMainWindow::ShowFullscreen() { + QWidget* window_to_change = + ui->action_Single_Window_Mode->isChecked() ? static_cast(this) : render_window; + if (ui->action_Single_Window_Mode->isChecked()) { UISettings::values.geometry = saveGeometry(); ui->menubar->hide(); statusBar()->hide(); - showFullScreen(); } else { UISettings::values.renderwindow_geometry = render_window->saveGeometry(); - render_window->showFullScreen(); + } + + bool secondary_on_same = window_to_change->screen() == secondary_window->screen(); + window_to_change->showFullScreen(); + + // try to fullscreen the second window if it is visible and on a different screen from main + if (secondary_window->isVisible() && !secondary_on_same) { + UISettings::values.secondarywindow_geometry = secondary_window->saveGeometry(); + LOG_INFO(Frontend, "Attempting to fullscreen secondary window"); + secondary_window->showFullScreen(); } } @@ -2613,12 +2625,16 @@ void GMainWindow::HideFullscreen() { render_window->showNormal(); render_window->restoreGeometry(UISettings::values.renderwindow_geometry); } + if (secondary_window->isFullScreen()) { + secondary_window->restoreGeometry(UISettings::values.secondarywindow_geometry); + secondary_window->showNormal(); + } } void GMainWindow::ToggleWindowMode() { if (ui->action_Single_Window_Mode->isChecked()) { // Render in the main window... - render_window->BackupGeometry(); + UISettings::values.renderwindow_geometry = render_window->saveGeometry(); ui->horizontalLayout->addWidget(render_window); render_window->setFocusPolicy(Qt::StrongFocus); if (emulation_running) { @@ -2634,7 +2650,7 @@ void GMainWindow::ToggleWindowMode() { render_window->setFocusPolicy(Qt::NoFocus); if (emulation_running) { render_window->setVisible(true); - render_window->RestoreGeometry(); + render_window->restoreGeometry(UISettings::values.renderwindow_geometry); game_list->show(); } } @@ -2645,10 +2661,10 @@ void GMainWindow::UpdateSecondaryWindowVisibility() { return; } if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows) { - secondary_window->RestoreGeometry(); + secondary_window->restoreGeometry(UISettings::values.secondarywindow_geometry); secondary_window->show(); } else { - secondary_window->BackupGeometry(); + UISettings::values.secondarywindow_geometry = secondary_window->saveGeometry(); secondary_window->hide(); } } @@ -3075,7 +3091,6 @@ void GMainWindow::OnCaptureScreenshot() { .toString(QStringLiteral("dd.MM.yy_hh.mm.ss.z")) .toStdString(); path.append(fmt::format("/{}_{}.png", filename, timestamp)); - auto* const screenshot_window = secondary_window->HasFocus() ? secondary_window : render_window; screenshot_window->CaptureScreenshot( @@ -4137,6 +4152,7 @@ void GMainWindow::UpdateUISettings() { if (!ui->action_Fullscreen->isChecked()) { UISettings::values.geometry = saveGeometry(); UISettings::values.renderwindow_geometry = render_window->saveGeometry(); + UISettings::values.secondarywindow_geometry = secondary_window->saveGeometry(); } UISettings::values.state = saveState(); #if MICROPROFILE_ENABLED diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index a5aa9896b..89d8388e7 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -59,41 +59,41 @@ const std::array, Settings::NativeAnalog::NumAnalogs> QtConfi // clang-format off const std::array QtConfig::default_hotkeys {{ {QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, - {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, - {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}}, - {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}}, - {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}}, - {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, + {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::ApplicationShortcut}}, + {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, + {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, + {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, + {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::ApplicationShortcut}}, {QStringLiteral("Decrease 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+-"), Qt::ApplicationShortcut}}, {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, - {QStringLiteral("Exit Azahar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, - {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}}, - {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, + {QStringLiteral("Exit Azahar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::ApplicationShortcut}}, + {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::ApplicationShortcut}}, + {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::ApplicationShortcut}}, {QStringLiteral("Increase 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl++"), Qt::ApplicationShortcut}}, {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, - {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}}, - {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}}, - {QStringLiteral("Load from Newest Non-Quicksave Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}}, + {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, + {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::ApplicationShortcut}}, + {QStringLiteral("Load from Newest Non-Quicksave Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::ApplicationShortcut}}, {QStringLiteral("Multiplayer Browse Public Rooms"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+B"), Qt::ApplicationShortcut}}, {QStringLiteral("Multiplayer Create Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+N"), Qt::ApplicationShortcut}}, {QStringLiteral("Multiplayer Direct Connect to Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Shift"), Qt::ApplicationShortcut}}, {QStringLiteral("Multiplayer Leave Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+L"), Qt::ApplicationShortcut}}, {QStringLiteral("Multiplayer Show Current Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+R"), Qt::ApplicationShortcut}}, - {QStringLiteral("Quick Save"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}}, - {QStringLiteral("Quick Load"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}}, + {QStringLiteral("Quick Save"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, + {QStringLiteral("Quick Load"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, {QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}}, - {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, - {QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}}, - {QStringLiteral("Save to Oldest Non-Quicksave Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), Qt::WindowShortcut}}, - {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, - {QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}}, + {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::ApplicationShortcut}}, + {QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::ApplicationShortcut}}, + {QStringLiteral("Save to Oldest Non-Quicksave Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), Qt::ApplicationShortcut}}, + {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::ApplicationShortcut}}, + {QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle 3D"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+3"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Custom Textures"), QStringLiteral("Main Window"), {QStringLiteral("F7"), Qt::ApplicationShortcut}}, - {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, + {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Per-Application Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, - {QStringLiteral("Toggle Screen Layout"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::WindowShortcut}}, - {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, + {QStringLiteral("Toggle Screen Layout"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, + {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Turbo Mode"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, }}; @@ -886,6 +886,8 @@ void QtConfig::ReadUILayoutValues() { UISettings::values.state = ReadSetting(Settings::QKeys::state).toByteArray(); UISettings::values.renderwindow_geometry = ReadSetting(Settings::QKeys::geometryRenderWindow).toByteArray(); + UISettings::values.secondarywindow_geometry = + ReadSetting(Settings::QKeys::geometrySecondaryWindow).toByteArray(); UISettings::values.gamelist_header_state = ReadSetting(Settings::QKeys::gameListHeaderState).toByteArray(); UISettings::values.microprofile_geometry = @@ -1413,6 +1415,8 @@ void QtConfig::SaveUILayoutValues() { WriteSetting(Settings::QKeys::geometry, UISettings::values.geometry); WriteSetting(Settings::QKeys::state, UISettings::values.state); WriteSetting(Settings::QKeys::geometryRenderWindow, UISettings::values.renderwindow_geometry); + WriteSetting(Settings::QKeys::geometrySecondaryWindow, + UISettings::values.secondarywindow_geometry); WriteSetting(Settings::QKeys::gameListHeaderState, UISettings::values.gamelist_header_state); WriteSetting(Settings::QKeys::microProfileDialogGeometry, UISettings::values.microprofile_geometry); diff --git a/src/citra_qt/uisettings.h b/src/citra_qt/uisettings.h index ef87b3fe3..d1ce04c0a 100644 --- a/src/citra_qt/uisettings.h +++ b/src/citra_qt/uisettings.h @@ -70,7 +70,7 @@ struct Values { QByteArray state; QByteArray renderwindow_geometry; - + QByteArray secondarywindow_geometry; QByteArray gamelist_header_state; QByteArray microprofile_geometry;