diff --git a/pcsx2-qt/EmuThread.cpp b/pcsx2-qt/EmuThread.cpp index 80d4f22d92..da66be8e0c 100644 --- a/pcsx2-qt/EmuThread.cpp +++ b/pcsx2-qt/EmuThread.cpp @@ -139,27 +139,8 @@ void EmuThread::setVMPaused(bool paused) VMManager::SetPaused(paused); } -bool EmuThread::shutdownVM(bool allow_confirm /* = true */, bool allow_save_to_state /* = true */, bool blocking /* = false */) +bool EmuThread::shutdownVM(bool allow_save_to_state /* = true */) { - if (!isOnEmuThread()) - { - // only confirm on UI thread because we need to display a msgbox - if (allow_confirm && g_main_window && !g_main_window->confirmShutdown()) - return false; - - QMetaObject::invokeMethod(this, "shutdownVM", Qt::QueuedConnection, Q_ARG(bool, false), - Q_ARG(bool, allow_save_to_state), Q_ARG(bool, blocking)); - - if (blocking) - { - // we need to yield here, since the display gets destroyed - while (VMManager::HasValidVM()) - QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); - } - - return true; - } - const VMState state = VMManager::GetState(); if (state == VMState::Paused) m_event_loop->quit(); @@ -550,7 +531,7 @@ void EmuThread::connectDisplaySignals(DisplayWidget* widget) connect(widget, &DisplayWidget::windowFocusEvent, this, &EmuThread::onDisplayWindowFocused); connect(widget, &DisplayWidget::windowResizedEvent, this, &EmuThread::onDisplayWindowResized); // connect(widget, &DisplayWidget::windowRestoredEvent, this, &EmuThread::redrawDisplayWindow); - connect(widget, &DisplayWidget::windowClosedEvent, []() { g_emu_thread->shutdownVM(true, true); }); + connect(widget, &DisplayWidget::windowClosedEvent, []() { g_main_window->requestShutdown(); }); connect(widget, &DisplayWidget::windowKeyEvent, this, &EmuThread::onDisplayWindowKeyEvent); connect(widget, &DisplayWidget::windowMouseMoveEvent, this, &EmuThread::onDisplayWindowMouseMoveEvent); connect(widget, &DisplayWidget::windowMouseButtonEvent, this, &EmuThread::onDisplayWindowMouseButtonEvent); @@ -792,6 +773,14 @@ DEFINE_HOTKEY("Screenshot", "General", "Save Screenshot", [](bool pressed) { // TODO } }) +DEFINE_HOTKEY("ShutdownVM", "System", "Shut Down Virtual Machine", [](bool pressed) { + if (!pressed) + { + // 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, true), Q_ARG(bool, true), Q_ARG(bool, true)); + } +}) DEFINE_HOTKEY("TogglePause", "System", "Toggle Pause", [](bool pressed) { if (!pressed) g_emu_thread->setVMPaused(VMManager::GetState() != VMState::Paused); diff --git a/pcsx2-qt/EmuThread.h b/pcsx2-qt/EmuThread.h index a68d7261ac..0708827ef5 100644 --- a/pcsx2-qt/EmuThread.h +++ b/pcsx2-qt/EmuThread.h @@ -57,7 +57,7 @@ public Q_SLOTS: void startVM(std::shared_ptr boot_params); void resetVM(); void setVMPaused(bool paused); - bool shutdownVM(bool allow_confirm = true, bool allow_save_to_state = true, bool blocking = false); + bool shutdownVM(bool allow_save_to_state = true); void loadState(const QString& filename); void loadStateFromSlot(qint32 slot); void saveState(const QString& filename); diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index f987d6c4f2..85fc142fa4 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -27,6 +27,7 @@ #include "pcsx2/CDVD/CDVDaccess.h" #include "pcsx2/Frontend/GameList.h" +#include "pcsx2/GSDumpReplayer.h" #include "pcsx2/HostDisplay.h" #include "AboutDialog.h" @@ -131,7 +132,7 @@ void MainWindow::connectSignals() connect(m_ui.actionChangeDiscFromGameList, &QAction::triggered, this, &MainWindow::onChangeDiscFromGameListActionTriggered); connect(m_ui.menuChangeDisc, &QMenu::aboutToShow, this, &MainWindow::onChangeDiscMenuAboutToShow); connect(m_ui.menuChangeDisc, &QMenu::aboutToHide, this, &MainWindow::onChangeDiscMenuAboutToHide); - connect(m_ui.actionPowerOff, &QAction::triggered, []() { g_emu_thread->shutdownVM(); }); + connect(m_ui.actionPowerOff, &QAction::triggered, this, [this]() { requestShutdown(); }); connect(m_ui.actionLoadState, &QAction::triggered, this, [this]() { m_ui.menuLoadState->exec(QCursor::pos()); }); connect(m_ui.actionSaveState, &QAction::triggered, this, [this]() { m_ui.menuSaveState->exec(QCursor::pos()); }); connect(m_ui.actionExit, &QAction::triggered, this, &MainWindow::close); @@ -236,7 +237,7 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread) void MainWindow::recreate() { if (m_vm_valid) - g_emu_thread->shutdownVM(false, true, true); + requestShutdown(false, true, true); close(); g_main_window = nullptr; @@ -659,20 +660,38 @@ void MainWindow::reportError(const QString& title, const QString& message) QMessageBox::critical(this, title, message); } -bool MainWindow::confirmShutdown() +bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_save_to_state /* = true */, bool block_until_done /* = false */) { - if (!m_vm_valid || !QtHost::GetBaseBoolSettingValue("UI", "ConfirmShutdown", true)) + if (!VMManager::HasValidVM()) return true; - ScopedVMPause pauser(m_vm_paused); + // only confirm on UI thread because we need to display a msgbox + if (allow_confirm && !GSDumpReplayer::IsReplayingDump() && QtHost::GetBaseBoolSettingValue("UI", "ConfirmShutdown", true)) + { + ScopedVMPause pauser(m_vm_paused); + if (QMessageBox::question(g_main_window, tr("Confirm Shutdown"), + tr("Are you sure you want to shut down the virtual machine?\n\nAll unsaved progress will be lost.")) != QMessageBox::Yes) + { + return false; + } + } - return (QMessageBox::question(g_main_window, tr("Confirm Shutdown"), - tr("Are you sure you want to shut down the virtual machine?\n\nAll unsaved progress will be lost.")) == QMessageBox::Yes); + g_emu_thread->shutdownVM(allow_save_to_state); + + if (block_until_done) + { + // we need to yield here, since the display gets destroyed + while (VMManager::HasValidVM()) + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); + } + + return true; } void MainWindow::requestExit() { - if (!g_emu_thread->shutdownVM(true, true, false)) + // this is block, because otherwise closeEvent() will also prompt + if (!requestShutdown(true, true, true)) return; close(); @@ -1021,7 +1040,7 @@ void MainWindow::onGameChanged(const QString& path, const QString& serial, const void MainWindow::closeEvent(QCloseEvent* event) { - if (!g_emu_thread->shutdownVM(true, true, true)) + if (!requestShutdown(true, true, true)) { event->ignore(); return; diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h index 5ad1cb9181..891601a333 100644 --- a/pcsx2-qt/MainWindow.h +++ b/pcsx2-qt/MainWindow.h @@ -54,7 +54,7 @@ public Q_SLOTS: void refreshGameList(bool invalidate_cache); void invalidateSaveStateCache(); void reportError(const QString& title, const QString& message); - bool confirmShutdown(); + bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool block_until_done = false); void requestExit(); private Q_SLOTS: diff --git a/pcsx2/PAD/Host/PAD.cpp b/pcsx2/PAD/Host/PAD.cpp index 260e9a3083..ceb4589443 100644 --- a/pcsx2/PAD/Host/PAD.cpp +++ b/pcsx2/PAD/Host/PAD.cpp @@ -247,6 +247,7 @@ void PAD::SetDefaultConfig(SettingsInterface& si) si.SetStringValue("Hotkeys", "TogglePause", "Keyboard/Space"); si.SetStringValue("Hotkeys", "ToggleSlowMotion", "Keyboard/Shift & Keyboard/Backtab"); si.SetStringValue("Hotkeys", "ToggleTurbo", "Keyboard/Tab"); + si.SetStringValue("Hotkeys", "ShutdownVM", "Keyboard/Escape"); } void PAD::Update() diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 7d5d52bcad..484fea6a21 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -1349,6 +1349,10 @@ DEFINE_HOTKEY("DecreaseSpeed", "System", "Decrease Target Speed", [](bool presse if (!pressed) HotkeyAdjustTargetSpeed(-0.1); }) +DEFINE_HOTKEY("ResetVM", "System", "Reset Virtual Machine", [](bool pressed) { + if (!pressed && VMManager::HasValidVM()) + VMManager::Reset(); +}) DEFINE_HOTKEY("PreviousSaveStateSlot", "Save States", "Select Previous Save Slot", [](bool pressed) { if (!pressed)