diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index 9c01df79e..5afdbb72c 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -750,8 +750,9 @@ void HostInterface::SetDefaultSettings() m_settings.emulation_speed = 1.0f; m_settings.speed_limiter_enabled = true; - m_settings.start_paused = false; 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_resolution_scale = 1; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 5822ef651..6c7244cbc 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -13,6 +13,7 @@ void Settings::Load(SettingsInterface& si) speed_limiter_enabled = si.GetBoolValue("General", "SpeedLimiterEnabled", true); increase_timer_resolution = si.GetBoolValue("General", "IncreaseTimerResolution", true); 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()) .value_or(CPUExecutionMode::Interpreter); @@ -63,6 +64,7 @@ void Settings::Save(SettingsInterface& si) const si.SetBoolValue("General", "SpeedLimiterEnabled", speed_limiter_enabled); si.SetBoolValue("General", "IncreaseTimerResolution", increase_timer_resolution); si.SetBoolValue("General", "StartPaused", start_paused); + si.SetBoolValue("General", "SaveStateOnExit", save_state_on_exit); si.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode)); diff --git a/src/core/settings.h b/src/core/settings.h index 2f0d12616..4e0ac5147 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -36,9 +36,10 @@ struct Settings CPUExecutionMode cpu_execution_mode = CPUExecutionMode::Interpreter; float emulation_speed = 1.0f; - bool start_paused = false; bool speed_limiter_enabled = true; bool increase_timer_resolution = true; + bool start_paused = false; + bool save_state_on_exit = true; GPURenderer gpu_renderer = GPURenderer::Software; u32 gpu_resolution_scale = 1; diff --git a/src/duckstation-qt/consolesettingswidget.cpp b/src/duckstation-qt/consolesettingswidget.cpp index eb7a2b98b..6810f7216 100644 --- a/src/duckstation-qt/consolesettingswidget.cpp +++ b/src/duckstation-qt/consolesettingswidget.cpp @@ -25,6 +25,7 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW SettingWidgetBinder::BindWidgetToNormalizedSetting(m_host_interface, m_ui.emulationSpeed, "General/EmulationSpeed", 100.0f); 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", &Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName); diff --git a/src/duckstation-qt/consolesettingswidget.ui b/src/duckstation-qt/consolesettingswidget.ui index dd02d7a7e..cf2708cd3 100644 --- a/src/duckstation-qt/consolesettingswidget.ui +++ b/src/duckstation-qt/consolesettingswidget.ui @@ -146,6 +146,13 @@ + + + + Save State On Exit + + + diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index ac90a8a83..21deb9253 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -298,7 +298,7 @@ void MainWindow::connectSignals() &MainWindow::onChangeDiscFromGameListActionTriggered); connect(m_ui.actionAddGameDirectory, &QAction::triggered, [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.actionPause, &QAction::toggled, m_host_interface, &QtHostInterface::pauseSystem); connect(m_ui.actionLoadState, &QAction::triggered, this, [this]() { m_ui.menuLoadState->exec(QCursor::pos()); }); @@ -340,7 +340,10 @@ void MainWindow::connectSignals() QString path = QString::fromStdString(entry->path); if (!m_emulation_running) { - m_host_interface->bootSystemFromFile(path); + if (m_host_interface->getSettingValue("General/SaveStateOnExit", true).toBool()) + m_host_interface->resumeSystemFromState(path, true); + else + m_host_interface->bootSystemFromFile(path); } else { @@ -433,6 +436,6 @@ void MainWindow::updateDebugMenuGPURenderer() void MainWindow::closeEvent(QCloseEvent* event) { - m_host_interface->destroySystem(true, true); + m_host_interface->synchronousPowerOffSystem(); QMainWindow::closeEvent(event); } diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index 9e2facf97..c7f0702b1 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -76,10 +76,10 @@ void QtHostInterface::setDefaultSettings() updateQSettingsFromCoreSettings(); } -QVariant QtHostInterface::getSettingValue(const QString& name) +QVariant QtHostInterface::getSettingValue(const QString& name, const QVariant& default_value) { std::lock_guard 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) @@ -178,6 +178,21 @@ void QtHostInterface::bootSystemFromFile(const QString& filename) 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() { 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()) { - QMetaObject::invokeMethod(this, "destroySystem", - block_until_done ? Qt::BlockingQueuedConnection : Qt::QueuedConnection, - Q_ARG(bool, save_resume_state), Q_ARG(bool, block_until_done)); + QMetaObject::invokeMethod(this, "powerOffSystem", Qt::QueuedConnection); return; } if (!m_system) return; + if (m_settings.save_state_on_exit) + SaveResumeSaveState(); + DestroySystem(); } +void QtHostInterface::synchronousPowerOffSystem() +{ + if (!isOnWorkerThread()) + QMetaObject::invokeMethod(this, "powerOffSystem", Qt::BlockingQueuedConnection); + else + powerOffSystem(); +} + void QtHostInterface::resetSystem() { if (!isOnWorkerThread()) @@ -555,23 +579,26 @@ void QtHostInterface::populateSaveStateMenus(const char* game_code, QMenu* load_ if (!available_states.empty()) { bool last_global = available_states.front().global; + s32 last_slot = available_states.front().slot; for (const SaveStateInfo& ssi : available_states) { const s32 slot = ssi.slot; const bool global = ssi.global; const QDateTime timestamp(QDateTime::fromSecsSinceEpoch(static_cast(ssi.timestamp))); + const QString timestamp_str(timestamp.toString(Qt::SystemLocaleShortDate)); const QString path(QString::fromStdString(ssi.path)); - QString title = tr("%1 Save %2 (%3)") - .arg(global ? tr("Global") : tr("Game")) - .arg(slot) - .arg(timestamp.toString(Qt::SystemLocaleShortDate)); + QString title; + if (slot < 0) + title = tr("Resume Save (%1)").arg(timestamp_str); + 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(); - last_global = global; - } + + last_global = global; + last_slot = slot; QAction* action = load_menu->addAction(title); connect(action, &QAction::triggered, [this, path]() { loadState(path); }); diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h index 2b20e8352..e767a6080 100644 --- a/src/duckstation-qt/qthostinterface.h +++ b/src/duckstation-qt/qthostinterface.h @@ -35,7 +35,7 @@ public: void setDefaultSettings(); /// 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 removeSettingValue(const QString& name); @@ -77,8 +77,10 @@ Q_SIGNALS: public Q_SLOTS: void applySettings(); void bootSystemFromFile(const QString& filename); + void resumeSystemFromState(const QString& filename, bool boot_on_failure); void bootSystemFromBIOS(); - void destroySystem(bool save_resume_state = false, bool block_until_done = false); + void powerOffSystem(); + void synchronousPowerOffSystem(); void resetSystem(); void pauseSystem(bool paused); void changeDisc(const QString& new_disc_filename); diff --git a/src/duckstation-sdl/sdl_host_interface.cpp b/src/duckstation-sdl/sdl_host_interface.cpp index 8e494757e..3834c2c2b 100644 --- a/src/duckstation-sdl/sdl_host_interface.cpp +++ b/src/duckstation-sdl/sdl_host_interface.cpp @@ -1048,7 +1048,11 @@ void SDLHostInterface::DrawPoweredOffWindow() ImGui::PushStyleColor(ImGuiCol_ButtonHovered, 0xFF575757); ImGui::SetCursorPosX(button_left); - ImGui::Button("Resume", button_size); + if (ImGui::Button("Resume", button_size)) + { + ResumeSystemFromMostRecentState(); + ClearImGuiFocus(); + } ImGui::NewLine(); 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::Checkbox("Enable Speed Limiter", &m_settings.speed_limiter_enabled); 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(); @@ -1498,5 +1503,9 @@ void SDLHostInterface::Run() // Save state on exit so it can be resumed if (m_system) + { + if (m_settings.save_state_on_exit) + SaveResumeSaveState(); DestroySystem(); + } }