Merge pull request from spycrab/qt_safeshutdown

Qt: Implement safe shutdown
This commit is contained in:
Leo Lam 2017-06-26 21:55:27 +02:00 committed by GitHub
commit aa020040f6
6 changed files with 96 additions and 32 deletions

@ -2,6 +2,7 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QCloseEvent>
#include <QDir> #include <QDir>
#include <QFileDialog> #include <QFileDialog>
#include <QIcon> #include <QIcon>
@ -37,6 +38,8 @@
#include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "UICommon/UICommon.h"
MainWindow::MainWindow() : QMainWindow(nullptr) MainWindow::MainWindow() : QMainWindow(nullptr)
{ {
setWindowTitle(QString::fromStdString(scm_rev_str)); setWindowTitle(QString::fromStdString(scm_rev_str));
@ -92,6 +95,8 @@ void MainWindow::ShutdownControllers()
void MainWindow::InitCoreCallbacks() void MainWindow::InitCoreCallbacks()
{ {
Core::SetOnStoppedCallback([this] { emit EmulationStopped(); }); Core::SetOnStoppedCallback([this] { emit EmulationStopped(); });
installEventFilter(this);
m_render_widget->installEventFilter(this);
} }
static void InstallHotkeyFilter(QWidget* dialog) static void InstallHotkeyFilter(QWidget* dialog)
@ -198,6 +203,11 @@ void MainWindow::ConnectToolBar()
connect(this, &MainWindow::EmulationStarted, m_tool_bar, &ToolBar::EmulationStarted); connect(this, &MainWindow::EmulationStarted, m_tool_bar, &ToolBar::EmulationStarted);
connect(this, &MainWindow::EmulationPaused, m_tool_bar, &ToolBar::EmulationPaused); connect(this, &MainWindow::EmulationPaused, m_tool_bar, &ToolBar::EmulationPaused);
connect(this, &MainWindow::EmulationStopped, m_tool_bar, &ToolBar::EmulationStopped); connect(this, &MainWindow::EmulationStopped, m_tool_bar, &ToolBar::EmulationStopped);
connect(this, &MainWindow::EmulationStopped, [this] {
m_stop_requested = false;
m_render_widget->hide();
});
} }
void MainWindow::ConnectGameList() void MainWindow::ConnectGameList()
@ -272,25 +282,54 @@ void MainWindow::Pause()
bool MainWindow::Stop() bool MainWindow::Stop()
{ {
bool stop = true; if (!Core::IsRunning())
return true;
if (Settings::Instance().GetConfirmStop()) if (Settings::Instance().GetConfirmStop())
{ {
// We could pause the game here and resume it if they say no. const Core::State state = Core::GetState();
// Set to false when Netplay is running as a CPU thread
bool pause = true;
if (pause)
Core::SetState(Core::State::Paused);
QMessageBox::StandardButton confirm; QMessageBox::StandardButton confirm;
confirm = QMessageBox::question(m_render_widget, tr("Confirm"), tr("Stop emulation?")); confirm = QMessageBox::question(m_render_widget, tr("Confirm"),
stop = (confirm == QMessageBox::Yes); m_stop_requested ?
tr("A shutdown is already in progress. Unsaved data "
"may be lost if you stop the current emulation "
"before it completes. Force stop?") :
tr("Do you want to stop the current emulation?"));
if (confirm != QMessageBox::Yes)
return false;
if (pause)
Core::SetState(state);
} }
if (stop) // TODO: Add Movie shutdown
// TODO: Add Debugger shutdown
if (!m_stop_requested && UICommon::TriggerSTMPowerEvent())
{ {
ForceStop(); m_stop_requested = true;
// Unpause because gracefully shutting down needs the game to actually request a shutdown.
// Do not unpause in debug mode to allow debugging until the complete shutdown.
if (Core::GetState() == Core::State::Paused)
Core::SetState(Core::State::Running);
return false;
}
ForceStop();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
// Allow windows to idle or turn off display again // Allow windows to idle or turn off display again
SetThreadExecutionState(ES_CONTINUOUS); SetThreadExecutionState(ES_CONTINUOUS);
#endif #endif
} return true;
return stop;
} }
void MainWindow::ForceStop() void MainWindow::ForceStop()
@ -480,3 +519,14 @@ void MainWindow::SetStateSlot(int slot)
Settings::Instance().SetStateSlot(slot); Settings::Instance().SetStateSlot(slot);
m_state_slot = slot; m_state_slot = slot;
} }
bool MainWindow::eventFilter(QObject* object, QEvent* event)
{
if (event->type() == QEvent::Close && !Stop())
{
static_cast<QCloseEvent*>(event)->ignore();
return true;
}
return false;
}

@ -27,6 +27,8 @@ public:
explicit MainWindow(); explicit MainWindow();
~MainWindow(); ~MainWindow();
bool eventFilter(QObject* object, QEvent* event) override;
signals: signals:
void EmulationStarted(); void EmulationStarted();
void EmulationPaused(); void EmulationPaused();
@ -86,6 +88,7 @@ private:
GameList* m_game_list; GameList* m_game_list;
RenderWidget* m_render_widget; RenderWidget* m_render_widget;
bool m_rendering_to_main; bool m_rendering_to_main;
bool m_stop_requested = false;
int m_state_slot = 1; int m_state_slot = 1;
HotkeyScheduler* m_hotkey_scheduler; HotkeyScheduler* m_hotkey_scheduler;

@ -253,7 +253,6 @@ private:
void DoFullscreen(bool enable_fullscreen); void DoFullscreen(bool enable_fullscreen);
void DoExclusiveFullscreen(bool enable_fullscreen); void DoExclusiveFullscreen(bool enable_fullscreen);
void ToggleDisplayMode(bool bFullscreen); void ToggleDisplayMode(bool bFullscreen);
bool TriggerSTMPowerEvent();
void OnStopped(); void OnStopped();
void OnRenderWindowSizeRequest(int width, int height); void OnRenderWindowSizeRequest(int width, int height);
void UpdateTitle(const wxString& str); void UpdateTitle(const wxString& str);

@ -86,6 +86,7 @@
#include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "UICommon/UICommon.h"
#include "UICommon/WiiUtils.h" #include "UICommon/WiiUtils.h"
#include "VideoCommon/RenderBase.h" #include "VideoCommon/RenderBase.h"
@ -880,36 +881,24 @@ void CFrame::DoStop()
if (NetPlayDialog::GetNetPlayClient()) if (NetPlayDialog::GetNetPlayClient())
NetPlayDialog::GetNetPlayClient()->Stop(); NetPlayDialog::GetNetPlayClient()->Stop();
if (!m_tried_graceful_shutdown && TriggerSTMPowerEvent()) if (!m_tried_graceful_shutdown && UICommon::TriggerSTMPowerEvent())
{ {
m_tried_graceful_shutdown = true; m_tried_graceful_shutdown = true;
m_confirm_stop = false;
// Unpause because gracefully shutting down needs the game to actually request a shutdown.
// Do not unpause in debug mode to allow debugging until the complete shutdown.
if (Core::GetState() == Core::State::Paused && !m_use_debugger)
Core::SetState(Core::State::Running);
return; return;
} }
Core::Stop(); Core::Stop();
UpdateGUI(); UpdateGUI();
} }
} }
bool CFrame::TriggerSTMPowerEvent()
{
const auto ios = IOS::HLE::GetIOS();
if (!ios)
return false;
const auto stm = ios->GetDeviceByName("/dev/stm/eventhook");
if (!stm || !std::static_pointer_cast<IOS::HLE::Device::STMEventHook>(stm)->HasHookInstalled())
return false;
Core::DisplayMessage("Shutting down", 30000);
// Unpause because gracefully shutting down needs the game to actually request a shutdown.
// Do not unpause in debug mode to allow debugging until the complete shutdown.
if (Core::GetState() == Core::State::Paused && !m_use_debugger)
DoPause();
ProcessorInterface::PowerButton_Tap();
m_confirm_stop = false;
return true;
}
void CFrame::OnStopped() void CFrame::OnStopped()
{ {
m_confirm_stop = false; m_confirm_stop = false;

@ -2,6 +2,7 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <memory>
#ifdef _WIN32 #ifdef _WIN32
#include <shlobj.h> // for SHGetFolderPath #include <shlobj.h> // for SHGetFolderPath
#endif #endif
@ -14,7 +15,11 @@
#include "Core/ConfigLoaders/BaseConfigLoader.h" #include "Core/ConfigLoaders/BaseConfigLoader.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/Wiimote.h" #include "Core/HW/Wiimote.h"
#include "Core/IOS/IOS.h"
#include "Core/IOS/STM/STM.h"
#include "InputCommon/GCAdapter.h" #include "InputCommon/GCAdapter.h"
@ -225,4 +230,20 @@ void SaveWiimoteSources()
inifile.Save(ini_filename); inifile.Save(ini_filename);
} }
bool TriggerSTMPowerEvent()
{
const auto ios = IOS::HLE::GetIOS();
if (!ios)
return false;
const auto stm = ios->GetDeviceByName("/dev/stm/eventhook");
if (!stm || !std::static_pointer_cast<IOS::HLE::Device::STMEventHook>(stm)->HasHookInstalled())
return false;
Core::DisplayMessage("Shutting down", 30000);
ProcessorInterface::PowerButton_Tap();
return true;
}
} // namespace UICommon } // namespace UICommon

@ -12,5 +12,7 @@ void Shutdown();
void CreateDirectories(); void CreateDirectories();
void SetUserDirectory(const std::string& custom_path); void SetUserDirectory(const std::string& custom_path);
bool TriggerSTMPowerEvent();
void SaveWiimoteSources(); void SaveWiimoteSources();
} // namespace UICommon } // namespace UICommon