mirror of https://github.com/PCSX2/pcsx2.git
Qt: Implement CTRL+C and confirm shutdown
This commit is contained in:
parent
56c8843406
commit
2f028c5f40
|
@ -16,6 +16,7 @@
|
|||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
|
@ -137,12 +138,16 @@ void EmuThread::setVMPaused(bool paused)
|
|||
VMManager::SetPaused(paused);
|
||||
}
|
||||
|
||||
void EmuThread::shutdownVM(bool allow_save_to_state /* = true */, bool blocking /* = false */)
|
||||
bool EmuThread::shutdownVM(bool allow_confirm /* = true */, bool allow_save_to_state /* = true */, bool blocking /* = false */)
|
||||
{
|
||||
if (!isOnEmuThread())
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "shutdownVM", Qt::QueuedConnection, Q_ARG(bool, allow_save_to_state),
|
||||
Q_ARG(bool, blocking));
|
||||
// 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)
|
||||
{
|
||||
|
@ -151,16 +156,17 @@ void EmuThread::shutdownVM(bool allow_save_to_state /* = true */, bool blocking
|
|||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
|
||||
}
|
||||
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
const VMState state = VMManager::GetState();
|
||||
if (state == VMState::Paused)
|
||||
m_event_loop->quit();
|
||||
else if (state != VMState::Running)
|
||||
return;
|
||||
return true;
|
||||
|
||||
VMManager::SetState(VMState::Stopping);
|
||||
return true;
|
||||
}
|
||||
|
||||
void EmuThread::loadState(const QString& filename)
|
||||
|
@ -741,7 +747,7 @@ ScopedVMPause::ScopedVMPause(bool was_paused)
|
|||
|
||||
ScopedVMPause::~ScopedVMPause()
|
||||
{
|
||||
if (m_was_paused)
|
||||
if (!m_was_paused)
|
||||
g_emu_thread->setVMPaused(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ public Q_SLOTS:
|
|||
void startVM(std::shared_ptr<VMBootParameters> boot_params);
|
||||
void resetVM();
|
||||
void setVMPaused(bool paused);
|
||||
void shutdownVM(bool allow_save_to_state = true, bool blocking = false);
|
||||
bool shutdownVM(bool allow_confirm = true, bool allow_save_to_state = true, bool blocking = false);
|
||||
void loadState(const QString& filename);
|
||||
void loadStateFromSlot(qint32 slot);
|
||||
void saveState(const QString& filename);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <cstdlib>
|
||||
#include <csignal>
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "EmuThread.h"
|
||||
|
@ -212,7 +213,6 @@ int main(int argc, char* argv[])
|
|||
|
||||
const int result = app.exec();
|
||||
|
||||
EmuThread::stop();
|
||||
QtHost::Shutdown();
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtGui/QCloseEvent>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QProgressBar>
|
||||
|
@ -229,7 +230,7 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
|
|||
void MainWindow::recreate()
|
||||
{
|
||||
if (m_vm_valid)
|
||||
g_emu_thread->shutdownVM(true, true);
|
||||
g_emu_thread->shutdownVM(false, true, true);
|
||||
|
||||
close();
|
||||
g_main_window = nullptr;
|
||||
|
@ -258,7 +259,7 @@ void MainWindow::setStyleFromSettings()
|
|||
qApp->setStyle(QStyleFactory::create("Fusion"));
|
||||
|
||||
const QColor black(25, 25, 25);
|
||||
const QColor teal(0, 128, 128);
|
||||
const QColor teal(0, 128, 128);
|
||||
const QColor tameTeal(160, 190, 185);
|
||||
const QColor grayBlue(160, 180, 190);
|
||||
|
||||
|
@ -640,6 +641,25 @@ void MainWindow::invalidateSaveStateCache() { m_save_states_invalidated = true;
|
|||
|
||||
void MainWindow::reportError(const QString& title, const QString& message) { QMessageBox::critical(this, title, message); }
|
||||
|
||||
bool MainWindow::confirmShutdown()
|
||||
{
|
||||
if (!m_vm_valid || !QtHost::GetBaseBoolSettingValue("UI", "ConfirmShutdown", true))
|
||||
return true;
|
||||
|
||||
ScopedVMPause pauser(m_vm_paused);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void MainWindow::requestExit()
|
||||
{
|
||||
if (!g_emu_thread->shutdownVM(true, true, false))
|
||||
return;
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
void Host::InvalidateSaveStateCache() { QMetaObject::invokeMethod(g_main_window, &MainWindow::invalidateSaveStateCache, Qt::QueuedConnection); }
|
||||
|
||||
void MainWindow::onGameListRefreshProgress(const QString& status, int current, int total)
|
||||
|
@ -966,7 +986,12 @@ void MainWindow::onGameChanged(const QString& path, const QString& serial, const
|
|||
|
||||
void MainWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
g_emu_thread->shutdownVM(true, true);
|
||||
if (!g_emu_thread->shutdownVM(true, true, true))
|
||||
{
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
saveStateToConfig();
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
|
|
@ -54,6 +54,8 @@ public Q_SLOTS:
|
|||
void refreshGameList(bool invalidate_cache);
|
||||
void invalidateSaveStateCache();
|
||||
void reportError(const QString& title, const QString& message);
|
||||
bool confirmShutdown();
|
||||
void requestExit();
|
||||
|
||||
private Q_SLOTS:
|
||||
DisplayWidget* createDisplay(bool fullscreen, bool render_to_main);
|
||||
|
|
|
@ -15,6 +15,15 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include <csignal>
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "common/RedtapeWindows.h"
|
||||
#endif
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/FileSystem.h"
|
||||
|
@ -26,9 +35,6 @@
|
|||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/PAD/Host/PAD.h"
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
#include "EmuThread.h"
|
||||
#include "GameList/GameListWidget.h"
|
||||
#include "MainWindow.h"
|
||||
|
@ -42,11 +48,15 @@ static constexpr u32 SETTINGS_SAVE_DELAY = 1000;
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
// Local function declarations
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
namespace QtHost {
|
||||
static void InitializeWxRubbish();
|
||||
static bool InitializeConfig();
|
||||
static void HookSignals();
|
||||
static bool SetCriticalFolders();
|
||||
static void SetDefaultConfig();
|
||||
static void QueueSettingsSave();
|
||||
static void SaveSettings();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Local variable declarations
|
||||
|
@ -72,12 +82,21 @@ bool QtHost::Initialize()
|
|||
return false;
|
||||
}
|
||||
|
||||
HookSignals();
|
||||
return true;
|
||||
}
|
||||
|
||||
void QtHost::Shutdown() {}
|
||||
void QtHost::Shutdown()
|
||||
{
|
||||
EmuThread::stop();
|
||||
if (g_main_window)
|
||||
{
|
||||
g_main_window->close();
|
||||
delete g_main_window;
|
||||
}
|
||||
}
|
||||
|
||||
static bool SetCriticalFolders()
|
||||
bool QtHost::SetCriticalFolders()
|
||||
{
|
||||
std::string program_path(FileSystem::GetProgramPath());
|
||||
EmuFolders::AppRoot = wxDirName(wxFileName(StringUtil::UTF8StringToWxString(program_path)));
|
||||
|
@ -110,7 +129,7 @@ void QtHost::UpdateFolders()
|
|||
EmuFolders::EnsureFoldersExist();
|
||||
}
|
||||
|
||||
bool InitializeConfig()
|
||||
bool QtHost::InitializeConfig()
|
||||
{
|
||||
if (!SetCriticalFolders())
|
||||
return false;
|
||||
|
@ -138,7 +157,7 @@ bool InitializeConfig()
|
|||
return true;
|
||||
}
|
||||
|
||||
void SetDefaultConfig()
|
||||
void QtHost::SetDefaultConfig()
|
||||
{
|
||||
EmuConfig = Pcsx2Config();
|
||||
EmuFolders::SetDefaults();
|
||||
|
@ -247,7 +266,7 @@ void QtHost::RemoveBaseSettingValue(const char* section, const char* key)
|
|||
QueueSettingsSave();
|
||||
}
|
||||
|
||||
void SaveSettings()
|
||||
void QtHost::SaveSettings()
|
||||
{
|
||||
pxAssertRel(!g_emu_thread->isOnEmuThread(), "Saving should happen on the UI thread.");
|
||||
|
||||
|
@ -261,7 +280,7 @@ void SaveSettings()
|
|||
s_settings_save_timer.release();
|
||||
}
|
||||
|
||||
void QueueSettingsSave()
|
||||
void QtHost::QueueSettingsSave()
|
||||
{
|
||||
if (s_settings_save_timer)
|
||||
return;
|
||||
|
@ -338,9 +357,38 @@ void PatchesVerboseReset()
|
|||
// FIXME
|
||||
}
|
||||
|
||||
static void SignalHandler(int signal)
|
||||
{
|
||||
// First try the normal (graceful) shutdown/exit.
|
||||
static bool graceful_shutdown_attempted = false;
|
||||
if (!graceful_shutdown_attempted && g_main_window)
|
||||
{
|
||||
std::fprintf(stderr, "Received CTRL+C, attempting graceful shutdown. Press CTRL+C again to force.\n");
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
std::signal(signal, SIG_DFL);
|
||||
|
||||
// MacOS is missing std::quick_exit() despite it being C++11...
|
||||
#ifndef __APPLE__
|
||||
std::quick_exit(1);
|
||||
#else
|
||||
_Exit(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void QtHost::HookSignals()
|
||||
{
|
||||
std::signal(SIGINT, SignalHandler);
|
||||
std::signal(SIGTERM, SignalHandler);
|
||||
}
|
||||
|
||||
// Replacement for Console so we actually get output to our console window on Windows.
|
||||
#ifdef _WIN32
|
||||
#include "common/RedtapeWindows.h"
|
||||
|
||||
static bool s_debugger_attached = false;
|
||||
static bool s_console_handle_set = false;
|
||||
|
@ -433,6 +481,17 @@ static const IConsoleWriter ConsoleWriter_WinQt =
|
|||
ConsoleWinQt_Newline,
|
||||
ConsoleWinQt_SetTitle,
|
||||
};
|
||||
|
||||
static BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
|
||||
{
|
||||
Console.WriteLn("Handler %u", dwCtrlType);
|
||||
if (dwCtrlType != CTRL_C_EVENT)
|
||||
return FALSE;
|
||||
|
||||
SignalHandler(SIGTERM);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void QtHost::UpdateLogging()
|
||||
|
@ -445,9 +504,9 @@ void QtHost::UpdateLogging()
|
|||
SysConsole.eeConsole.Enabled = any_logging_sinks && QtHost::GetBaseBoolSettingValue("Logging", "EnableEEConsole", true);
|
||||
SysConsole.iopConsole.Enabled = any_logging_sinks && QtHost::GetBaseBoolSettingValue("Logging", "EnableIOPConsole", true);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (system_console_enabled)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
s_debugger_attached = IsDebuggerPresent();
|
||||
if (!s_console_handle_set)
|
||||
{
|
||||
|
@ -468,17 +527,31 @@ void QtHost::UpdateLogging()
|
|||
}
|
||||
|
||||
if (!s_console_handle && !s_debugger_attached)
|
||||
{
|
||||
Console_SetActiveHandler(ConsoleWriter_Null);
|
||||
SetConsoleCtrlHandler(ConsoleCtrlHandler, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console_SetActiveHandler(ConsoleWriter_WinQt);
|
||||
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console_SetActiveHandler(ConsoleWriter_Null);
|
||||
SetConsoleCtrlHandler(ConsoleCtrlHandler, FALSE);
|
||||
}
|
||||
#else
|
||||
if (system_console_enabled)
|
||||
{
|
||||
Console_SetActiveHandler(ConsoleWriter_Stdout);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
Console_SetActiveHandler(ConsoleWriter_Null);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#include <wx/module.h>
|
||||
|
@ -488,7 +561,7 @@ extern "C" HINSTANCE wxGetInstance();
|
|||
extern void wxSetInstance(HINSTANCE hInst);
|
||||
#endif
|
||||
|
||||
void InitializeWxRubbish()
|
||||
void QtHost::InitializeWxRubbish()
|
||||
{
|
||||
wxLog::DoCreateOnDemand();
|
||||
wxLog::GetActiveTarget();
|
||||
|
|
|
@ -41,7 +41,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget
|
|||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.inhibitScreensaver, "UI", "InhibitScreensaver", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.discordPresence, "UI", "DiscordPresence", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.confirmPowerOff, "UI", "ConfirmPowerOff", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.confirmShutdown, "UI", "ConfirmShutdown", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.saveStateOnExit, "EmuCore", "AutoStateLoadSave", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnStart, "UI", "StartPaused", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", false);
|
||||
|
@ -84,8 +84,8 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget
|
|||
}
|
||||
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.confirmPowerOff, tr("Confirm Power Off"), tr("Checked"),
|
||||
tr("Determines whether a prompt will be displayed to confirm shutting down the emulator/game "
|
||||
m_ui.confirmShutdown, tr("Confirm Shutdown"), tr("Checked"),
|
||||
tr("Determines whether a prompt will be displayed to confirm shutting down the virtual machine "
|
||||
"when the hotkey is pressed."));
|
||||
dialog->registerWidgetHelp(m_ui.saveStateOnExit, tr("Save State On Exit"), tr("Checked"),
|
||||
tr("Automatically saves the emulator state when powering down or exiting. You can then "
|
||||
|
|
|
@ -61,9 +61,9 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="confirmPowerOff">
|
||||
<widget class="QCheckBox" name="confirmShutdown">
|
||||
<property name="text">
|
||||
<string>Confirm Power Off</string>
|
||||
<string>Confirm Shutdown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
Loading…
Reference in New Issue