diff --git a/pcsx2-gsrunner/Main.cpp b/pcsx2-gsrunner/Main.cpp index 9f34f9e50d..82fee15521 100644 --- a/pcsx2-gsrunner/Main.cpp +++ b/pcsx2-gsrunner/Main.cpp @@ -373,7 +373,7 @@ void Host::SetFullscreen(bool enabled) { } -void Host::RequestExit(bool save_state_if_running) +void Host::RequestExit(bool allow_confirm) { } diff --git a/pcsx2-qt/AutoUpdaterDialog.cpp b/pcsx2-qt/AutoUpdaterDialog.cpp index c9f060f010..d21ea69c94 100644 --- a/pcsx2-qt/AutoUpdaterDialog.cpp +++ b/pcsx2-qt/AutoUpdaterDialog.cpp @@ -465,7 +465,7 @@ void AutoUpdaterDialog::downloadUpdateClicked() else if (result == 1) { // updater started. since we're a modal on the main window, we have to queue this. - QMetaObject::invokeMethod(g_main_window, &MainWindow::requestExit, Qt::QueuedConnection); + QMetaObject::invokeMethod(g_main_window, "requestExit", Qt::QueuedConnection, Q_ARG(bool, true)); done(0); } diff --git a/pcsx2-qt/DisplayWidget.cpp b/pcsx2-qt/DisplayWidget.cpp index 8ca26267a3..e4a2511352 100644 --- a/pcsx2-qt/DisplayWidget.cpp +++ b/pcsx2-qt/DisplayWidget.cpp @@ -156,7 +156,7 @@ void DisplayWidget::handleCloseEvent(QCloseEvent* event) } else { - QMetaObject::invokeMethod(g_main_window, &MainWindow::requestExit); + QMetaObject::invokeMethod(g_main_window, "requestExit", Q_ARG(bool, true)); } // Cancel the event from closing the window. diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index 1359e9ace9..7888f7d9a5 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -1275,8 +1275,7 @@ void MainWindow::runOnUIThread(const std::function& func) func(); } -bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_save_to_state /* = true */, - bool default_save_to_state /* = true */, bool block_until_done /* = false */) +bool MainWindow::requestShutdown(bool allow_confirm, bool allow_save_to_state, bool default_save_to_state) { if (!s_vm_valid) return true; @@ -1321,34 +1320,21 @@ bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_sav // Now we can actually shut down the VM. g_emu_thread->shutdownVM(save_state); - - if (block_until_done || m_is_closing || QtHost::InBatchMode()) - { - // We need to yield here, since the display gets destroyed. - while (VMManager::GetState() != VMState::Shutdown) - QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); - } - - if (!m_is_closing && QtHost::InBatchMode()) - { - // If we don't set the closing flag here, the VM shutdown may not complete by the time closeEvent() is called, - // leading to a confirm. - m_is_closing = true; - QGuiApplication::quit(); - } - return true; } -void MainWindow::requestExit() +void MainWindow::requestExit(bool allow_confirm) { // this is block, because otherwise closeEvent() will also prompt - if (!requestShutdown(true, true, EmuConfig.SaveStateOnShutdown, true)) + if (!requestShutdown(allow_confirm, true, EmuConfig.SaveStateOnShutdown)) return; - // We could use close here, but if we're not visible (e.g. quitting from fullscreen), closing the window - // doesn't quit the application. - QGuiApplication::quit(); + // VM stopped signal won't have fired yet, so queue an exit if we still have one. + // Otherwise, immediately exit, because there's no VM to exit us later. + if (QtHost::IsVMValid()) + m_is_closing = true; + else + QGuiApplication::quit(); } void MainWindow::checkForSettingChanges() @@ -1943,6 +1929,13 @@ void MainWindow::onVMStopped() updateStatusBarWidgetVisibility(); updateInputRecordingActions(false); + // If we're closing or in batch mode, quit the whole application now. + if (m_is_closing || QtHost::InBatchMode()) + { + QCoreApplication::quit(); + return; + } + if (m_display_widget) updateDisplayWidgetCursor(); else @@ -1982,7 +1975,7 @@ void MainWindow::showEvent(QShowEvent* event) void MainWindow::closeEvent(QCloseEvent* event) { - if (!requestShutdown(true, true, EmuConfig.SaveStateOnShutdown, true)) + if (!requestShutdown(true, true, EmuConfig.SaveStateOnShutdown)) { event->ignore(); return; diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h index ff902159fa..e9ec8c2981 100644 --- a/pcsx2-qt/MainWindow.h +++ b/pcsx2-qt/MainWindow.h @@ -117,9 +117,8 @@ public Q_SLOTS: void reportError(const QString& title, const QString& message); bool confirmMessage(const QString& title, const QString& message); void runOnUIThread(const std::function& func); - bool requestShutdown( - bool allow_confirm = true, bool allow_save_to_state = true, bool default_save_to_state = true, bool block_until_done = false); - void requestExit(); + bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool default_save_to_state = true); + void requestExit(bool allow_confirm = true); void checkForSettingChanges(); std::optional getWindowInfo(); diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index aa02c20548..d174985509 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -1228,12 +1228,9 @@ void Host::CancelGameListRefresh() QMetaObject::invokeMethod(g_main_window, "cancelGameListRefresh", Qt::BlockingQueuedConnection); } -void Host::RequestExit(bool save_state_if_running) +void Host::RequestExit(bool allow_confirm) { - if (VMManager::HasValidVM()) - g_emu_thread->shutdownVM(save_state_if_running); - - QMetaObject::invokeMethod(g_main_window, "requestExit", Qt::QueuedConnection); + QMetaObject::invokeMethod(g_main_window, "requestExit", Qt::QueuedConnection, Q_ARG(bool, allow_confirm)); } void Host::RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool default_save_state) @@ -1241,16 +1238,24 @@ void Host::RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool def if (!VMManager::HasValidVM()) return; - // MainWindow handles close-on-exit for batch mode. - if (allow_confirm || QtHost::InBatchMode()) + // This is a bit messy here - we want to shut down immediately (in case it was requested by the game), + // but we also need to exit-on-shutdown for batch mode. So, if we're running on the CPU thread, destroy + // the VM, then request the main window to exit. + if (allow_confirm || !g_emu_thread->isOnEmuThread()) { // Run it on the host thread, that way we get the confirm prompt (if enabled). QMetaObject::invokeMethod(g_main_window, "requestShutdown", Qt::QueuedConnection, Q_ARG(bool, allow_confirm), - Q_ARG(bool, allow_save_state), Q_ARG(bool, default_save_state), Q_ARG(bool, false)); + Q_ARG(bool, allow_save_state), Q_ARG(bool, default_save_state)); } else { + // Change state to stopping -> return -> shut down VM. g_emu_thread->shutdownVM(allow_save_state && default_save_state); + + // This will probably call shutdownVM() again, but by the time it runs, we'll have already shut down + // and it'll be a noop. + if (QtHost::InBatchMode()) + QMetaObject::invokeMethod(g_main_window, "requestExit", Qt::QueuedConnection, Q_ARG(bool, false)); } } @@ -1559,7 +1564,7 @@ static void SignalHandler(int signal) graceful_shutdown_attempted = true; // This could be a bit risky invoking from a signal handler... hopefully it's okay. - QMetaObject::invokeMethod(g_main_window, &MainWindow::requestExit, Qt::QueuedConnection); + QMetaObject::invokeMethod(g_main_window, "requestExit", Qt::QueuedConnection, Q_ARG(bool, false)); return; } diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index 161cb47bb1..d24647d1ef 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -1062,7 +1062,7 @@ void FullscreenUI::DoChangeDisc() void FullscreenUI::DoRequestExit() { - Host::RunOnCPUThread([]() { Host::RequestExit(EmuConfig.SaveStateOnShutdown); }); + Host::RunOnCPUThread([]() { Host::RequestExit(true); }); } void FullscreenUI::DoToggleFullscreen() diff --git a/pcsx2/Host.h b/pcsx2/Host.h index 1b40b8045a..4398029c53 100644 --- a/pcsx2/Host.h +++ b/pcsx2/Host.h @@ -83,7 +83,7 @@ namespace Host /// Requests shut down and exit of the hosting application. This may not actually exit, /// if the user cancels the shutdown confirmation. - void RequestExit(bool save_state_if_running); + void RequestExit(bool allow_confirm); /// Requests shut down of the current virtual machine. void RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool default_save_state);