diff --git a/src/core/system.h b/src/core/system.h index b451dc737..73004cef0 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -481,7 +481,7 @@ void RequestResizeHostDisplay(s32 width, s32 height); /// 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 RequestSystemShutdown(bool allow_confirm, bool save_state); diff --git a/src/duckstation-nogui/nogui_host.cpp b/src/duckstation-nogui/nogui_host.cpp index 617aeec7b..e0d6564e6 100644 --- a/src/duckstation-nogui/nogui_host.cpp +++ b/src/duckstation-nogui/nogui_host.cpp @@ -981,11 +981,11 @@ std::optional<WindowInfo> Host::GetTopLevelWindowInfo() return g_nogui_window->GetPlatformWindowInfo(); } -void Host::RequestExit(bool save_state_if_running) +void Host::RequestExit(bool allow_confirm) { if (System::IsValid()) { - Host::RunOnCPUThread([save_state_if_running]() { System::ShutdownSystem(save_state_if_running); }); + Host::RunOnCPUThread([]() { System::ShutdownSystem(g_settings.save_state_on_exit); }); } // clear the running flag, this'll break out of the main CPU loop once the VM is shutdown. @@ -1022,7 +1022,7 @@ static void SignalHandler(int signal) { std::fprintf(stderr, "Received CTRL+C, attempting graceful shutdown. Press CTRL+C again to force.\n"); graceful_shutdown_attempted = true; - Host::RequestExit(true); + Host::RequestExit(false); return; } diff --git a/src/duckstation-nogui/wayland_nogui_platform.cpp b/src/duckstation-nogui/wayland_nogui_platform.cpp index d5a05429c..c6d4ec217 100644 --- a/src/duckstation-nogui/wayland_nogui_platform.cpp +++ b/src/duckstation-nogui/wayland_nogui_platform.cpp @@ -270,7 +270,7 @@ void WaylandNoGUIPlatform::TopLevelConfigure(void* data, struct xdg_toplevel* xd void WaylandNoGUIPlatform::TopLevelClose(void* data, struct xdg_toplevel* xdg_toplevel) { - Host::RunOnCPUThread([]() { Host::RequestExit(g_settings.save_state_on_exit); }); + Host::RunOnCPUThread([]() { Host::RequestExit(false); }); } void WaylandNoGUIPlatform::SeatCapabilities(void* data, wl_seat* seat, uint32_t capabilities) diff --git a/src/duckstation-nogui/win32_nogui_platform.cpp b/src/duckstation-nogui/win32_nogui_platform.cpp index 43a506110..9e34935d7 100644 --- a/src/duckstation-nogui/win32_nogui_platform.cpp +++ b/src/duckstation-nogui/win32_nogui_platform.cpp @@ -399,7 +399,8 @@ LRESULT CALLBACK Win32NoGUIPlatform::WndProc(HWND hwnd, UINT msg, WPARAM wParam, case WM_CLOSE: case WM_QUIT: { - Host::RunOnCPUThread([]() { Host::RequestExit(g_settings.save_state_on_exit); }); + Host::RunOnCPUThread([]() { Host::RequestExit(false); }); + return 0; } break; diff --git a/src/duckstation-nogui/x11_nogui_platform.cpp b/src/duckstation-nogui/x11_nogui_platform.cpp index 68ac16ddf..760d0d555 100644 --- a/src/duckstation-nogui/x11_nogui_platform.cpp +++ b/src/duckstation-nogui/x11_nogui_platform.cpp @@ -233,7 +233,7 @@ void X11NoGUIPlatform::ProcessXEvents() case ClientMessage: { if (static_cast<Atom>(event.xclient.data.l[0]) == XInternAtom(m_display, "WM_DELETE_WINDOW", False)) - Host::RequestExit(true); + Host::RequestExit(false); } break; diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 488254969..fed124cc6 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -615,6 +615,15 @@ void MainWindow::onSystemDestroyed() s_system_valid = false; s_system_paused = false; + + // If we're closing or in batch mode, quit the whole application now. + if (m_is_closing || QtHost::InBatchMode()) + { + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); + QCoreApplication::quit(); + return; + } + updateEmulationActions(false, false, Achievements::ChallengeModeActive()); if (m_display_widget) updateDisplayWidgetCursor(); @@ -727,7 +736,12 @@ std::string MainWindow::getDeviceDiscPath(const QString& title) void MainWindow::recreate() { if (s_system_valid) - requestShutdown(false, true, true, true); + { + requestShutdown(false, true, true); + + while (s_system_valid) + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); + } // We need to close input sources, because e.g. DInput uses our window handle. g_emu_thread->closeInputSources(); @@ -2375,16 +2389,24 @@ void MainWindow::showEvent(QShowEvent* event) void MainWindow::closeEvent(QCloseEvent* event) { - if (!requestShutdown(true, true, true)) + // If there's no VM, we can just exit as normal. + if (!s_system_valid) { - event->ignore(); + QMainWindow::closeEvent(event); return; } + // But if there is, we have to cancel the action, regardless of whether we ended exiting + // or not. The window still needs to be visible while GS is shutting down. + event->ignore(); + + // Exit cancelled? + if (!requestShutdown(true, true, g_settings.save_state_on_exit)) + return; + + // Application will be exited in VM stopped handler. saveGeometryToConfig(); m_is_closing = true; - - QMainWindow::closeEvent(event); } void MainWindow::changeEvent(QEvent* event) @@ -2473,7 +2495,7 @@ void MainWindow::runOnUIThread(const std::function<void()>& func) } bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_save_to_state /* = true */, - bool save_state /* = true */, bool block_until_done /* = false */) + bool save_state /* = true */) { if (!s_system_valid) return true; @@ -2518,34 +2540,21 @@ bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_sav // Now we can actually shut down the VM. g_emu_thread->shutdownSystem(save_state); - - if (block_until_done || m_is_closing || QtHost::InBatchMode()) - { - // We need to yield here, since the display gets destroyed. - while (s_system_valid || System::GetState() != System::State::Shutdown) - QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); - } - - if (!m_is_closing && QtHost::InBatchMode()) - { - // Closing the window should shut down everything. 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(bool allow_save_to_state /* = true */) +void MainWindow::requestExit(bool allow_confirm /* = true */) { // this is block, because otherwise closeEvent() will also prompt - if (!requestShutdown(true, allow_save_to_state, g_settings.save_state_on_exit)) + if (!requestShutdown(allow_confirm, true, g_settings.save_state_on_exit)) 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 (s_system_valid) + m_is_closing = true; + else + QGuiApplication::quit(); } void MainWindow::checkForSettingChanges() diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index 42fee11a6..c0adae732 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -99,8 +99,8 @@ public Q_SLOTS: void cancelGameListRefresh(); void runOnUIThread(const std::function<void()>& func); - bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool save_state = true, bool block_until_done = false); - void requestExit(bool allow_save_to_state = true); + bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool save_state = true); + void requestExit(bool allow_confirm = true); void checkForSettingChanges(); void getWindowInfo(WindowInfo* wi); diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 1e4d50039..fd235b618 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -1746,12 +1746,12 @@ void Host::RequestSystemShutdown(bool allow_confirm, bool save_state) return; QMetaObject::invokeMethod(g_main_window, "requestShutdown", Qt::QueuedConnection, Q_ARG(bool, allow_confirm), - Q_ARG(bool, true), Q_ARG(bool, save_state), Q_ARG(bool, false)); + Q_ARG(bool, true), Q_ARG(bool, save_state)); } -void Host::RequestExit(bool save_state_if_running) +void Host::RequestExit(bool allow_confirm) { - QMetaObject::invokeMethod(g_main_window, "requestExit", Qt::QueuedConnection, Q_ARG(bool, save_state_if_running)); + QMetaObject::invokeMethod(g_main_window, "requestExit", Qt::QueuedConnection, Q_ARG(bool, allow_confirm)); } std::optional<WindowInfo> Host::GetTopLevelWindowInfo() diff --git a/src/frontend-common/fullscreen_ui.cpp b/src/frontend-common/fullscreen_ui.cpp index 09db22ba1..415e8523f 100644 --- a/src/frontend-common/fullscreen_ui.cpp +++ b/src/frontend-common/fullscreen_ui.cpp @@ -1073,7 +1073,7 @@ void FullscreenUI::DoToggleAnalogMode() void FullscreenUI::DoRequestExit() { - Host::RunOnCPUThread([]() { Host::RequestExit(g_settings.save_state_on_exit); }); + Host::RunOnCPUThread([]() { Host::RequestExit(true); }); } void FullscreenUI::DoToggleFullscreen()