From 4f12f8b04f18cd303fc4695600e6c083021a0cb2 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 1 Apr 2022 03:44:05 +0200 Subject: [PATCH] Qt: fix GracefulShutdown regression By replacing Emu.Stop() with GracefulShutdown() in gs_frame::close(), the game window was now unknowingly closed recursively, causing RPCS3 to crash in some cases. Let's just ignore any consecutive calls to close() from now on. Also don't close the window internally on a close event. request a shutdown instead. --- rpcs3/rpcs3qt/gs_frame.cpp | 43 +++++++++++++++++++++++++++++------ rpcs3/rpcs3qt/gs_frame.h | 2 ++ rpcs3/rpcs3qt/main_window.cpp | 4 ++++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 81c9d16d3b..9c262373da 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -357,21 +357,34 @@ bool gs_frame::get_mouse_lock_state() return isActive() && m_mouse_hide_and_lock; } +void gs_frame::hide_on_close() +{ + if (!(+g_progr)) + { + // Hide the dialog before stopping if no progress bar is being shown. + // Otherwise users might think that the game softlocked if stopping takes too long. + QWindow::hide(); + } +} + void gs_frame::close() { + if (m_is_closing.exchange(true)) + { + gui_log.notice("Closing game window (ignored, already closing)"); + return; + } + gui_log.notice("Closing game window"); Emu.CallFromMainThread([this]() { - if (!(+g_progr)) - { - // Hide the dialog before stopping if no progress bar is being shown. - // Otherwise users might think that the game softlocked if stopping takes too long. - QWindow::hide(); - } + // Hide window if necessary + hide_on_close(); if (!Emu.IsStopped()) { + // Blocking shutdown request. Obsolete, but I'm keeping it here as last resort. Emu.GracefulShutdown(true, false); } @@ -838,7 +851,23 @@ bool gs_frame::event(QEvent* ev) } gui_log.notice("Game window close event issued"); - close(); + + if (Emu.IsStopped()) + { + // This should be unreachable, but never say never. Properly close the window anyway. + close(); + } + else + { + // Issue async shutdown + Emu.GracefulShutdown(true, true); + + // Hide window if necessary + hide_on_close(); + + // Do not propagate the close event. It will be closed by the rsx_thread. + return true; + } } else if (ev->type() == QEvent::MouseMove && (!m_show_mouse || m_mousehide_timer.isActive())) { diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index dec383267b..0d57b02e7a 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -41,6 +41,7 @@ private: u64 m_frames = 0; std::string m_window_title; QWindow::Visibility m_last_visibility = Visibility::Windowed; + atomic_t m_is_closing = false; atomic_t m_show_mouse = true; bool m_disable_mouse = false; bool m_disable_kb_hotkeys = false; @@ -97,6 +98,7 @@ protected: bool event(QEvent* ev) override; private: + void hide_on_close(); void toggle_mouselock(); void update_cursor(); void handle_cursor(QWindow::Visibility visibility, bool from_event, bool start_idle_timer); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 9d6b5ad4a0..1787ab334c 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -73,6 +73,10 @@ extern void process_qt_events() { if (thread_ctrl::is_main()) { + // NOTE: + // I noticed that calling this from an Emu callback can cause the + // caller to get stuck for a while during newly opened Qt dialogs. + // Adding a timeout here doesn't seem to do anything in that case. QApplication::processEvents(); } }