Qt: Better handle VM-requesting-shutdown case

This was a bit wonky in batch mode before. Now all possible exit paths
close the application at the same point.
This commit is contained in:
Stenzek 2023-01-28 13:33:51 +10:00 committed by refractionpcsx2
parent c596a51593
commit f447aded57
8 changed files with 38 additions and 41 deletions

View File

@ -373,7 +373,7 @@ void Host::SetFullscreen(bool enabled)
{
}
void Host::RequestExit(bool save_state_if_running)
void Host::RequestExit(bool allow_confirm)
{
}

View File

@ -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);
}

View File

@ -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.

View File

@ -1275,8 +1275,7 @@ void MainWindow::runOnUIThread(const std::function<void()>& 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;

View File

@ -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<void()>& 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<WindowInfo> getWindowInfo();

View File

@ -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;
}

View File

@ -1062,7 +1062,7 @@ void FullscreenUI::DoChangeDisc()
void FullscreenUI::DoRequestExit()
{
Host::RunOnCPUThread([]() { Host::RequestExit(EmuConfig.SaveStateOnShutdown); });
Host::RunOnCPUThread([]() { Host::RequestExit(true); });
}
void FullscreenUI::DoToggleFullscreen()

View File

@ -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);