Frontends: Implement auto save on exit/resume
This commit is contained in:
parent
e738b87a25
commit
e01cf0dccb
|
@ -750,8 +750,9 @@ void HostInterface::SetDefaultSettings()
|
||||||
|
|
||||||
m_settings.emulation_speed = 1.0f;
|
m_settings.emulation_speed = 1.0f;
|
||||||
m_settings.speed_limiter_enabled = true;
|
m_settings.speed_limiter_enabled = true;
|
||||||
m_settings.start_paused = false;
|
|
||||||
m_settings.increase_timer_resolution = true;
|
m_settings.increase_timer_resolution = true;
|
||||||
|
m_settings.start_paused = false;
|
||||||
|
m_settings.save_state_on_exit = true;
|
||||||
|
|
||||||
m_settings.gpu_renderer = Settings::DEFAULT_GPU_RENDERER;
|
m_settings.gpu_renderer = Settings::DEFAULT_GPU_RENDERER;
|
||||||
m_settings.gpu_resolution_scale = 1;
|
m_settings.gpu_resolution_scale = 1;
|
||||||
|
|
|
@ -13,6 +13,7 @@ void Settings::Load(SettingsInterface& si)
|
||||||
speed_limiter_enabled = si.GetBoolValue("General", "SpeedLimiterEnabled", true);
|
speed_limiter_enabled = si.GetBoolValue("General", "SpeedLimiterEnabled", true);
|
||||||
increase_timer_resolution = si.GetBoolValue("General", "IncreaseTimerResolution", true);
|
increase_timer_resolution = si.GetBoolValue("General", "IncreaseTimerResolution", true);
|
||||||
start_paused = si.GetBoolValue("General", "StartPaused", false);
|
start_paused = si.GetBoolValue("General", "StartPaused", false);
|
||||||
|
save_state_on_exit = si.GetBoolValue("General", "SaveStateOnExit", true);
|
||||||
|
|
||||||
cpu_execution_mode = ParseCPUExecutionMode(si.GetStringValue("CPU", "ExecutionMode", "Interpreter").c_str())
|
cpu_execution_mode = ParseCPUExecutionMode(si.GetStringValue("CPU", "ExecutionMode", "Interpreter").c_str())
|
||||||
.value_or(CPUExecutionMode::Interpreter);
|
.value_or(CPUExecutionMode::Interpreter);
|
||||||
|
@ -63,6 +64,7 @@ void Settings::Save(SettingsInterface& si) const
|
||||||
si.SetBoolValue("General", "SpeedLimiterEnabled", speed_limiter_enabled);
|
si.SetBoolValue("General", "SpeedLimiterEnabled", speed_limiter_enabled);
|
||||||
si.SetBoolValue("General", "IncreaseTimerResolution", increase_timer_resolution);
|
si.SetBoolValue("General", "IncreaseTimerResolution", increase_timer_resolution);
|
||||||
si.SetBoolValue("General", "StartPaused", start_paused);
|
si.SetBoolValue("General", "StartPaused", start_paused);
|
||||||
|
si.SetBoolValue("General", "SaveStateOnExit", save_state_on_exit);
|
||||||
|
|
||||||
si.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode));
|
si.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode));
|
||||||
|
|
||||||
|
|
|
@ -36,9 +36,10 @@ struct Settings
|
||||||
CPUExecutionMode cpu_execution_mode = CPUExecutionMode::Interpreter;
|
CPUExecutionMode cpu_execution_mode = CPUExecutionMode::Interpreter;
|
||||||
|
|
||||||
float emulation_speed = 1.0f;
|
float emulation_speed = 1.0f;
|
||||||
bool start_paused = false;
|
|
||||||
bool speed_limiter_enabled = true;
|
bool speed_limiter_enabled = true;
|
||||||
bool increase_timer_resolution = true;
|
bool increase_timer_resolution = true;
|
||||||
|
bool start_paused = false;
|
||||||
|
bool save_state_on_exit = true;
|
||||||
|
|
||||||
GPURenderer gpu_renderer = GPURenderer::Software;
|
GPURenderer gpu_renderer = GPURenderer::Software;
|
||||||
u32 gpu_resolution_scale = 1;
|
u32 gpu_resolution_scale = 1;
|
||||||
|
|
|
@ -25,6 +25,7 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW
|
||||||
SettingWidgetBinder::BindWidgetToNormalizedSetting(m_host_interface, m_ui.emulationSpeed, "General/EmulationSpeed",
|
SettingWidgetBinder::BindWidgetToNormalizedSetting(m_host_interface, m_ui.emulationSpeed, "General/EmulationSpeed",
|
||||||
100.0f);
|
100.0f);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pauseOnStart, "General/StartPaused");
|
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pauseOnStart, "General/StartPaused");
|
||||||
|
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.saveStateOnExit, "General/SaveStateOnExit");
|
||||||
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.cpuExecutionMode, "CPU/ExecutionMode",
|
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.cpuExecutionMode, "CPU/ExecutionMode",
|
||||||
&Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName);
|
&Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName);
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="6" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="saveStateOnExit">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save State On Exit</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -298,7 +298,7 @@ void MainWindow::connectSignals()
|
||||||
&MainWindow::onChangeDiscFromGameListActionTriggered);
|
&MainWindow::onChangeDiscFromGameListActionTriggered);
|
||||||
connect(m_ui.actionAddGameDirectory, &QAction::triggered,
|
connect(m_ui.actionAddGameDirectory, &QAction::triggered,
|
||||||
[this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
|
[this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
|
||||||
connect(m_ui.actionPowerOff, &QAction::triggered, [this]() { m_host_interface->destroySystem(true, false); });
|
connect(m_ui.actionPowerOff, &QAction::triggered, m_host_interface, &QtHostInterface::powerOffSystem);
|
||||||
connect(m_ui.actionReset, &QAction::triggered, m_host_interface, &QtHostInterface::resetSystem);
|
connect(m_ui.actionReset, &QAction::triggered, m_host_interface, &QtHostInterface::resetSystem);
|
||||||
connect(m_ui.actionPause, &QAction::toggled, m_host_interface, &QtHostInterface::pauseSystem);
|
connect(m_ui.actionPause, &QAction::toggled, m_host_interface, &QtHostInterface::pauseSystem);
|
||||||
connect(m_ui.actionLoadState, &QAction::triggered, this, [this]() { m_ui.menuLoadState->exec(QCursor::pos()); });
|
connect(m_ui.actionLoadState, &QAction::triggered, this, [this]() { m_ui.menuLoadState->exec(QCursor::pos()); });
|
||||||
|
@ -340,6 +340,9 @@ void MainWindow::connectSignals()
|
||||||
QString path = QString::fromStdString(entry->path);
|
QString path = QString::fromStdString(entry->path);
|
||||||
if (!m_emulation_running)
|
if (!m_emulation_running)
|
||||||
{
|
{
|
||||||
|
if (m_host_interface->getSettingValue("General/SaveStateOnExit", true).toBool())
|
||||||
|
m_host_interface->resumeSystemFromState(path, true);
|
||||||
|
else
|
||||||
m_host_interface->bootSystemFromFile(path);
|
m_host_interface->bootSystemFromFile(path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -433,6 +436,6 @@ void MainWindow::updateDebugMenuGPURenderer()
|
||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent* event)
|
void MainWindow::closeEvent(QCloseEvent* event)
|
||||||
{
|
{
|
||||||
m_host_interface->destroySystem(true, true);
|
m_host_interface->synchronousPowerOffSystem();
|
||||||
QMainWindow::closeEvent(event);
|
QMainWindow::closeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,10 +76,10 @@ void QtHostInterface::setDefaultSettings()
|
||||||
updateQSettingsFromCoreSettings();
|
updateQSettingsFromCoreSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant QtHostInterface::getSettingValue(const QString& name)
|
QVariant QtHostInterface::getSettingValue(const QString& name, const QVariant& default_value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
|
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
|
||||||
return m_qsettings.value(name);
|
return m_qsettings.value(name, default_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::putSettingValue(const QString& name, const QVariant& value)
|
void QtHostInterface::putSettingValue(const QString& name, const QVariant& value)
|
||||||
|
@ -178,6 +178,21 @@ void QtHostInterface::bootSystemFromFile(const QString& filename)
|
||||||
HostInterface::BootSystemFromFile(filename.toStdString().c_str());
|
HostInterface::BootSystemFromFile(filename.toStdString().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::resumeSystemFromState(const QString& filename, bool boot_on_failure)
|
||||||
|
{
|
||||||
|
if (!isOnWorkerThread())
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(this, "resumeSystemFromState", Qt::QueuedConnection, Q_ARG(const QString&, filename),
|
||||||
|
Q_ARG(bool, boot_on_failure));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename.isEmpty())
|
||||||
|
HostInterface::ResumeSystemFromMostRecentState();
|
||||||
|
else
|
||||||
|
HostInterface::ResumeSystemFromState(filename.toStdString().c_str(), boot_on_failure);
|
||||||
|
}
|
||||||
|
|
||||||
void QtHostInterface::bootSystemFromBIOS()
|
void QtHostInterface::bootSystemFromBIOS()
|
||||||
{
|
{
|
||||||
if (!isOnWorkerThread())
|
if (!isOnWorkerThread())
|
||||||
|
@ -485,22 +500,31 @@ void QtHostInterface::addButtonToInputMap(const QString& binding, InputButtonHan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::destroySystem(bool save_resume_state /* = false */, bool block_until_done /* = false */)
|
void QtHostInterface::powerOffSystem()
|
||||||
{
|
{
|
||||||
if (!isOnWorkerThread())
|
if (!isOnWorkerThread())
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(this, "destroySystem",
|
QMetaObject::invokeMethod(this, "powerOffSystem", Qt::QueuedConnection);
|
||||||
block_until_done ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
|
|
||||||
Q_ARG(bool, save_resume_state), Q_ARG(bool, block_until_done));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_system)
|
if (!m_system)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (m_settings.save_state_on_exit)
|
||||||
|
SaveResumeSaveState();
|
||||||
|
|
||||||
DestroySystem();
|
DestroySystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::synchronousPowerOffSystem()
|
||||||
|
{
|
||||||
|
if (!isOnWorkerThread())
|
||||||
|
QMetaObject::invokeMethod(this, "powerOffSystem", Qt::BlockingQueuedConnection);
|
||||||
|
else
|
||||||
|
powerOffSystem();
|
||||||
|
}
|
||||||
|
|
||||||
void QtHostInterface::resetSystem()
|
void QtHostInterface::resetSystem()
|
||||||
{
|
{
|
||||||
if (!isOnWorkerThread())
|
if (!isOnWorkerThread())
|
||||||
|
@ -555,23 +579,26 @@ void QtHostInterface::populateSaveStateMenus(const char* game_code, QMenu* load_
|
||||||
if (!available_states.empty())
|
if (!available_states.empty())
|
||||||
{
|
{
|
||||||
bool last_global = available_states.front().global;
|
bool last_global = available_states.front().global;
|
||||||
|
s32 last_slot = available_states.front().slot;
|
||||||
for (const SaveStateInfo& ssi : available_states)
|
for (const SaveStateInfo& ssi : available_states)
|
||||||
{
|
{
|
||||||
const s32 slot = ssi.slot;
|
const s32 slot = ssi.slot;
|
||||||
const bool global = ssi.global;
|
const bool global = ssi.global;
|
||||||
const QDateTime timestamp(QDateTime::fromSecsSinceEpoch(static_cast<qint64>(ssi.timestamp)));
|
const QDateTime timestamp(QDateTime::fromSecsSinceEpoch(static_cast<qint64>(ssi.timestamp)));
|
||||||
|
const QString timestamp_str(timestamp.toString(Qt::SystemLocaleShortDate));
|
||||||
const QString path(QString::fromStdString(ssi.path));
|
const QString path(QString::fromStdString(ssi.path));
|
||||||
|
|
||||||
QString title = tr("%1 Save %2 (%3)")
|
QString title;
|
||||||
.arg(global ? tr("Global") : tr("Game"))
|
if (slot < 0)
|
||||||
.arg(slot)
|
title = tr("Resume Save (%1)").arg(timestamp_str);
|
||||||
.arg(timestamp.toString(Qt::SystemLocaleShortDate));
|
else
|
||||||
|
title = tr("%1 Save %2 (%3)").arg(global ? tr("Global") : tr("Game")).arg(slot).arg(timestamp_str);
|
||||||
|
|
||||||
if (global != last_global)
|
if (global != last_global || last_slot < 0)
|
||||||
{
|
|
||||||
load_menu->addSeparator();
|
load_menu->addSeparator();
|
||||||
|
|
||||||
last_global = global;
|
last_global = global;
|
||||||
}
|
last_slot = slot;
|
||||||
|
|
||||||
QAction* action = load_menu->addAction(title);
|
QAction* action = load_menu->addAction(title);
|
||||||
connect(action, &QAction::triggered, [this, path]() { loadState(path); });
|
connect(action, &QAction::triggered, [this, path]() { loadState(path); });
|
||||||
|
|
|
@ -35,7 +35,7 @@ public:
|
||||||
void setDefaultSettings();
|
void setDefaultSettings();
|
||||||
|
|
||||||
/// Thread-safe QSettings access.
|
/// Thread-safe QSettings access.
|
||||||
QVariant getSettingValue(const QString& name);
|
QVariant getSettingValue(const QString& name, const QVariant& default_value = QVariant());
|
||||||
void putSettingValue(const QString& name, const QVariant& value);
|
void putSettingValue(const QString& name, const QVariant& value);
|
||||||
void removeSettingValue(const QString& name);
|
void removeSettingValue(const QString& name);
|
||||||
|
|
||||||
|
@ -77,8 +77,10 @@ Q_SIGNALS:
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void applySettings();
|
void applySettings();
|
||||||
void bootSystemFromFile(const QString& filename);
|
void bootSystemFromFile(const QString& filename);
|
||||||
|
void resumeSystemFromState(const QString& filename, bool boot_on_failure);
|
||||||
void bootSystemFromBIOS();
|
void bootSystemFromBIOS();
|
||||||
void destroySystem(bool save_resume_state = false, bool block_until_done = false);
|
void powerOffSystem();
|
||||||
|
void synchronousPowerOffSystem();
|
||||||
void resetSystem();
|
void resetSystem();
|
||||||
void pauseSystem(bool paused);
|
void pauseSystem(bool paused);
|
||||||
void changeDisc(const QString& new_disc_filename);
|
void changeDisc(const QString& new_disc_filename);
|
||||||
|
|
|
@ -1048,7 +1048,11 @@ void SDLHostInterface::DrawPoweredOffWindow()
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, 0xFF575757);
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, 0xFF575757);
|
||||||
|
|
||||||
ImGui::SetCursorPosX(button_left);
|
ImGui::SetCursorPosX(button_left);
|
||||||
ImGui::Button("Resume", button_size);
|
if (ImGui::Button("Resume", button_size))
|
||||||
|
{
|
||||||
|
ResumeSystemFromMostRecentState();
|
||||||
|
ClearImGuiFocus();
|
||||||
|
}
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
|
|
||||||
ImGui::SetCursorPosX(button_left);
|
ImGui::SetCursorPosX(button_left);
|
||||||
|
@ -1163,6 +1167,7 @@ void SDLHostInterface::DrawSettingsWindow()
|
||||||
settings_changed |= ImGui::SliderFloat("##speed", &m_settings.emulation_speed, 0.25f, 5.0f);
|
settings_changed |= ImGui::SliderFloat("##speed", &m_settings.emulation_speed, 0.25f, 5.0f);
|
||||||
settings_changed |= ImGui::Checkbox("Enable Speed Limiter", &m_settings.speed_limiter_enabled);
|
settings_changed |= ImGui::Checkbox("Enable Speed Limiter", &m_settings.speed_limiter_enabled);
|
||||||
settings_changed |= ImGui::Checkbox("Pause On Start", &m_settings.start_paused);
|
settings_changed |= ImGui::Checkbox("Pause On Start", &m_settings.start_paused);
|
||||||
|
settings_changed |= ImGui::Checkbox("Save State On Exit", &m_settings.save_state_on_exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
|
@ -1498,5 +1503,9 @@ void SDLHostInterface::Run()
|
||||||
|
|
||||||
// Save state on exit so it can be resumed
|
// Save state on exit so it can be resumed
|
||||||
if (m_system)
|
if (m_system)
|
||||||
|
{
|
||||||
|
if (m_settings.save_state_on_exit)
|
||||||
|
SaveResumeSaveState();
|
||||||
DestroySystem();
|
DestroySystem();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue