diff --git a/src/core/hotkeys.cpp b/src/core/hotkeys.cpp index 8f5ffa433..64351ab25 100644 --- a/src/core/hotkeys.cpp +++ b/src/core/hotkeys.cpp @@ -141,20 +141,6 @@ static void HotkeyToggleOSD() #ifndef __ANDROID__ -static bool CanPause() -{ - const u32 frames_until_pause_allowed = Achievements::GetPauseThrottleFrames(); - if (frames_until_pause_allowed == 0) - return true; - - const float seconds = static_cast(frames_until_pause_allowed) / System::GetVideoFrameRate(); - Host::AddIconOSDMessage("PauseCooldown", ICON_FA_CLOCK, - TRANSLATE_PLURAL_STR("Hotkeys", "You cannot pause until another %n second(s) have passed.", - "", static_cast(std::ceil(seconds))), - std::max(seconds, Host::OSD_QUICK_DURATION)); - return false; -} - #define DEFINE_NON_ANDROID_HOTKEY(name, category, display_name, handler) \ DEFINE_HOTKEY(name, category, display_name, handler) @@ -168,25 +154,25 @@ BEGIN_HOTKEY_LIST(g_common_hotkeys) DEFINE_NON_ANDROID_HOTKEY("OpenPauseMenu", TRANSLATE_NOOP("Hotkeys", "Interface"), TRANSLATE_NOOP("Hotkeys", "Open Pause Menu"), [](s32 pressed) { - if (!pressed && CanPause()) + if (!pressed && System::CanPauseSystem(true)) FullscreenUI::OpenPauseMenu(); }) DEFINE_NON_ANDROID_HOTKEY("OpenCheatsMenu", TRANSLATE_NOOP("Hotkeys", "Interface"), TRANSLATE_NOOP("Hotkeys", "Open Cheat Settings"), [](s32 pressed) { - if (!pressed && CanPause()) + if (!pressed && System::CanPauseSystem(true)) FullscreenUI::OpenCheatsMenu(); }) DEFINE_NON_ANDROID_HOTKEY("OpenAchievements", TRANSLATE_NOOP("Hotkeys", "Interface"), TRANSLATE_NOOP("Hotkeys", "Open Achievement List"), [](s32 pressed) { - if (!pressed && CanPause()) + if (!pressed && System::CanPauseSystem(true)) FullscreenUI::OpenAchievementsWindow(); }) DEFINE_NON_ANDROID_HOTKEY("OpenLeaderboards", TRANSLATE_NOOP("Hotkeys", "Interface"), TRANSLATE_NOOP("Hotkeys", "Open Leaderboard List"), [](s32 pressed) { - if (!pressed && CanPause()) + if (!pressed && System::CanPauseSystem(true)) FullscreenUI::OpenLeaderboardsWindow(); }) @@ -198,7 +184,7 @@ DEFINE_NON_ANDROID_HOTKEY("Screenshot", TRANSLATE_NOOP("Hotkeys", "Interface"), DEFINE_NON_ANDROID_HOTKEY("TogglePause", TRANSLATE_NOOP("Hotkeys", "Interface"), TRANSLATE_NOOP("Hotkeys", "Toggle Pause"), [](s32 pressed) { - if (!pressed && CanPause()) + if (!pressed && System::CanPauseSystem(true)) System::PauseSystem(!System::IsPaused()); }) @@ -235,7 +221,7 @@ DEFINE_HOTKEY("ToggleTurbo", TRANSLATE_NOOP("Hotkeys", "System"), TRANSLATE_NOOP DEFINE_NON_ANDROID_HOTKEY("PowerOff", TRANSLATE_NOOP("Hotkeys", "System"), TRANSLATE_NOOP("Hotkeys", "Power Off System"), [](s32 pressed) { - if (!pressed && CanPause()) + if (!pressed && System::CanPauseSystem(true)) Host::RequestSystemShutdown(true, g_settings.save_state_on_exit, true); }) diff --git a/src/core/system.cpp b/src/core/system.cpp index 43b69f568..078cd47bd 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1632,6 +1632,24 @@ void System::PauseSystem(bool paused) } } +bool System::CanPauseSystem(bool display_message) +{ + const u32 frames_until_pause_allowed = Achievements::GetPauseThrottleFrames(); + if (frames_until_pause_allowed == 0) + return true; + + if (display_message) + { + const float seconds = static_cast(frames_until_pause_allowed) / System::GetVideoFrameRate(); + Host::AddIconOSDMessage("PauseCooldown", ICON_FA_CLOCK, + TRANSLATE_PLURAL_STR("Hotkeys", "You cannot pause until another %n second(s) have passed.", + "", static_cast(std::ceil(seconds))), + std::max(seconds, Host::OSD_QUICK_DURATION)); + } + + return false; +} + bool System::SaveResumeState(Error* error) { if (s_state.running_game_serial.empty()) diff --git a/src/core/system.h b/src/core/system.h index 89a997570..7dcbb5738 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -260,6 +260,9 @@ bool BootSystem(SystemBootParameters parameters, Error* error); void PauseSystem(bool paused); void ResetSystem(); +/// Returns true if the system can be paused, i.e. not subject to achievement restrictions. +bool CanPauseSystem(bool display_message); + /// Returns the maximum size of a save state, considering the current configuration. size_t GetMaxSaveStateSize(); diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 926327be4..f21ed844b 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -1338,6 +1338,35 @@ void MainWindow::onFullscreenUIStartedOrStopped(bool running) m_ui.actionStartFullscreenUI2->setText(running ? tr("Exit Big Picture") : tr("Big Picture")); } +void MainWindow::onPauseActionToggled(bool checked) +{ + if (s_system_paused == checked) + return; + + if (checked && s_achievements_hardcore_mode) + { + // Need to check restrictions. + Host::RunOnCPUThread([]() { + if (System::CanPauseSystem(true)) + { + g_emu_thread->setSystemPaused(true); + } + else + { + // Restore action state. + Host::RunOnUIThread([]() { + QSignalBlocker sb(g_main_window->m_ui.actionPause); + g_main_window->m_ui.actionPause->setChecked(false); + }); + } + }); + } + else + { + g_emu_thread->setSystemPaused(checked); + } +} + void MainWindow::onRemoveDiscActionTriggered() { g_emu_thread->changeDisc(QString(), false, true); @@ -2274,7 +2303,7 @@ void MainWindow::connectSignals() connect(m_ui.actionPowerOffWithoutSaving, &QAction::triggered, this, [this]() { requestShutdown(false, false, false, true, false, false); }); connect(m_ui.actionReset, &QAction::triggered, this, []() { g_emu_thread->resetSystem(true); }); - connect(m_ui.actionPause, &QAction::toggled, this, [](bool active) { g_emu_thread->setSystemPaused(active); }); + connect(m_ui.actionPause, &QAction::toggled, this, &MainWindow::onPauseActionToggled); connect(m_ui.actionScreenshot, &QAction::triggered, g_emu_thread, &EmuThread::saveScreenshot); connect(m_ui.actionScanForNewGames, &QAction::triggered, this, &MainWindow::onScanForNewGamesTriggered); connect(m_ui.actionRescanAllGames, &QAction::triggered, this, [this]() { refreshGameList(true); }); @@ -2821,6 +2850,24 @@ void MainWindow::requestShutdown(bool allow_confirm, bool allow_save_to_state, b // Only confirm on UI thread because we need to display a msgbox. if (!m_is_closing && s_system_valid && allow_confirm && Host::GetBoolSettingValue("Main", "ConfirmPowerOff", true)) { + // Hardcore mode restrictions. + if (!s_system_paused && s_achievements_hardcore_mode && allow_confirm) + { + Host::RunOnCPUThread( + [allow_confirm, allow_save_to_state, save_state, check_safety, exit_fullscreen_ui, quit_afterwards]() { + if (!System::CanPauseSystem(true)) + return; + + Host::RunOnUIThread( + [allow_confirm, allow_save_to_state, save_state, check_safety, exit_fullscreen_ui, quit_afterwards]() { + g_main_window->requestShutdown(allow_confirm, allow_save_to_state, save_state, check_safety, + exit_fullscreen_ui, quit_afterwards); + }); + }); + + return; + } + SystemLock lock(pauseAndLockSystem()); QMessageBox msgbox(lock.getDialogParent()); diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index a91eb15b3..29c76e4f8 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -184,6 +184,7 @@ private Q_SLOTS: void onCheatsActionTriggered(); void onCheatsMenuAboutToShow(); void onStartFullscreenUITriggered(); + void onPauseActionToggled(bool checked); void onFullscreenUIStartedOrStopped(bool running); void onRemoveDiscActionTriggered(); void onScanForNewGamesTriggered();