diff --git a/pcsx2-qt/AutoUpdaterDialog.cpp b/pcsx2-qt/AutoUpdaterDialog.cpp index cf9e39b0a3..eecbb13285 100644 --- a/pcsx2-qt/AutoUpdaterDialog.cpp +++ b/pcsx2-qt/AutoUpdaterDialog.cpp @@ -454,7 +454,8 @@ void AutoUpdaterDialog::checkIfUpdateNeeded() void AutoUpdaterDialog::skipThisUpdateClicked() { - QtHost::SetBaseStringSettingValue("AutoUpdater", "LastVersion", m_latest_version.toUtf8().constData()); + Host::SetBaseStringSettingValue("AutoUpdater", "LastVersion", m_latest_version.toUtf8().constData()); + Host::CommitBaseSettingChanges(); done(0); } diff --git a/pcsx2-qt/GameList/GameListWidget.cpp b/pcsx2-qt/GameList/GameListWidget.cpp index 99ae51b626..4412b62fe5 100644 --- a/pcsx2-qt/GameList/GameListWidget.cpp +++ b/pcsx2-qt/GameList/GameListWidget.cpp @@ -347,7 +347,8 @@ void GameListWidget::onTableViewHeaderSortIndicatorChanged(int, Qt::SortOrder) void GameListWidget::listZoom(float delta) { const float new_scale = std::clamp(m_model->getCoverScale() + delta, MIN_SCALE, MAX_SCALE); - QtHost::SetBaseFloatSettingValue("UI", "GameListCoverArtScale", new_scale); + Host::SetBaseFloatSettingValue("UI", "GameListCoverArtScale", new_scale); + Host::CommitBaseSettingChanges(); m_model->setCoverScale(new_scale); m_model->updateCacheSize(width(), height()); updateListFont(); @@ -368,7 +369,8 @@ void GameListWidget::gridIntScale(int int_scale) { const float new_scale = std::clamp(static_cast(int_scale) / 100.0f, MIN_SCALE, MAX_SCALE); - QtHost::SetBaseFloatSettingValue("UI", "GameListCoverArtScale", new_scale); + Host::SetBaseFloatSettingValue("UI", "GameListCoverArtScale", new_scale); + Host::CommitBaseSettingChanges(); m_model->setCoverScale(new_scale); m_model->updateCacheSize(width(), height()); updateListFont(); @@ -389,7 +391,8 @@ void GameListWidget::showGameList() return; } - QtHost::SetBaseBoolSettingValue("UI", "GameListGridView", false); + Host::SetBaseBoolSettingValue("UI", "GameListGridView", false); + Host::CommitBaseSettingChanges(); m_ui.stack->setCurrentIndex(0); resizeTableViewColumnsToFit(); updateToolbar(); @@ -405,7 +408,8 @@ void GameListWidget::showGameGrid() return; } - QtHost::SetBaseBoolSettingValue("UI", "GameListGridView", true); + Host::SetBaseBoolSettingValue("UI", "GameListGridView", true); + Host::CommitBaseSettingChanges(); m_ui.stack->setCurrentIndex(1); updateToolbar(); emit layoutChange(); @@ -416,7 +420,8 @@ void GameListWidget::setShowCoverTitles(bool enabled) if (m_model->getShowCoverTitles() == enabled) return; - QtHost::SetBaseBoolSettingValue("UI", "GameListShowCoverTitles", enabled); + Host::SetBaseBoolSettingValue("UI", "GameListShowCoverTitles", enabled); + Host::CommitBaseSettingChanges(); m_model->setShowCoverTitles(enabled); if (isShowingGameGrid()) m_model->refresh(); @@ -508,14 +513,16 @@ void GameListWidget::saveTableViewColumnVisibilitySettings() for (int column = 0; column < GameListModel::Column_Count; column++) { const bool visible = !m_table_view->isColumnHidden(column); - QtHost::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column).c_str(), visible); + Host::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column).c_str(), visible); + Host::CommitBaseSettingChanges(); } } void GameListWidget::saveTableViewColumnVisibilitySettings(int column) { const bool visible = !m_table_view->isColumnHidden(column); - QtHost::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column).c_str(), visible); + Host::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column).c_str(), visible); + Host::CommitBaseSettingChanges(); } void GameListWidget::loadTableViewColumnSortSettings() @@ -538,11 +545,12 @@ void GameListWidget::saveTableViewColumnSortSettings() if (sort_column >= 0 && sort_column < GameListModel::Column_Count) { - QtHost::SetBaseStringSettingValue( + Host::SetBaseStringSettingValue( "GameListTableView", "SortColumn", GameListModel::getColumnName(static_cast(sort_column))); } - QtHost::SetBaseBoolSettingValue("GameListTableView", "SortDescending", sort_descending); + Host::SetBaseBoolSettingValue("GameListTableView", "SortDescending", sort_descending); + Host::CommitBaseSettingChanges(); } const GameList::Entry* GameListWidget::getSelectedEntry() const diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index 879b8f64d1..f681e47acc 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -401,6 +401,37 @@ void MainWindow::recreate() deleteLater(); } +void MainWindow::recreateSettings() +{ + QString current_category; + if (m_settings_dialog) + { + current_category = m_settings_dialog->getCategory(); + m_settings_dialog->hide(); + m_settings_dialog->deleteLater(); + m_settings_dialog = nullptr; + } + + doSettings(current_category.toUtf8().constData()); +} + +void MainWindow::resetSettings(bool ui) +{ + Host::RequestResetSettings(false, true, false, false, ui); + + if (ui) + { + // UI reset includes theme (and eventually language). + // Just updating the theme here, when there's no change, causes Qt to get very confused.. + // So, we'll just tear down everything and recreate. We'll need to do that for language + // resets eventaully anyway. + recreate(); + } + + // g_main_window here for recreate() case above. + g_main_window->recreateSettings(); +} + void MainWindow::updateApplicationTheme() { if (!s_unthemed_style_name_set) @@ -734,7 +765,8 @@ void MainWindow::onBlockDumpActionToggled(bool checked) return; } - QtHost::SetBaseStringSettingValue("EmuCore", "BlockDumpSaveDirectory", new_dir.toUtf8().constData()); + Host::SetBaseStringSettingValue("EmuCore", "BlockDumpSaveDirectory", new_dir.toUtf8().constData()); + Host::CommitBaseSettingChanges(); } void MainWindow::saveStateToConfig() @@ -747,7 +779,10 @@ void MainWindow::saveStateToConfig() const QByteArray geometry_b64 = geometry.toBase64(); const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowGeometry"); if (old_geometry_b64 != geometry_b64.constData()) - QtHost::SetBaseStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData()); + { + Host::SetBaseStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData()); + Host::CommitBaseSettingChanges(); + } } { @@ -755,7 +790,10 @@ void MainWindow::saveStateToConfig() const QByteArray state_b64 = state.toBase64(); const std::string old_state_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowState"); if (old_state_b64 != state_b64.constData()) - QtHost::SetBaseStringSettingValue("UI", "MainWindowState", state_b64.constData()); + { + Host::SetBaseStringSettingValue("UI", "MainWindowState", state_b64.constData()); + Host::CommitBaseSettingChanges(); + } } } @@ -1306,19 +1344,22 @@ void MainWindow::onSaveStateMenuAboutToShow() void MainWindow::onViewToolbarActionToggled(bool checked) { - QtHost::SetBaseBoolSettingValue("UI", "ShowToolbar", checked); + Host::SetBaseBoolSettingValue("UI", "ShowToolbar", checked); + Host::CommitBaseSettingChanges(); m_ui.toolBar->setVisible(checked); } void MainWindow::onViewLockToolbarActionToggled(bool checked) { - QtHost::SetBaseBoolSettingValue("UI", "LockToolbar", checked); + Host::SetBaseBoolSettingValue("UI", "LockToolbar", checked); + Host::CommitBaseSettingChanges(); m_ui.toolBar->setMovable(!checked); } void MainWindow::onViewStatusBarActionToggled(bool checked) { - QtHost::SetBaseBoolSettingValue("UI", "ShowStatusBar", checked); + Host::SetBaseBoolSettingValue("UI", "ShowStatusBar", checked); + Host::CommitBaseSettingChanges(); m_ui.statusBar->setVisible(checked); } @@ -1386,7 +1427,8 @@ void MainWindow::onAboutActionTriggered() void MainWindow::onCheckForUpdatesActionTriggered() { // Wipe out the last version, that way it displays the update if we've previously skipped it. - QtHost::RemoveBaseSettingValue("AutoUpdater", "LastVersion"); + Host::RemoveBaseSettingValue("AutoUpdater", "LastVersion"); + Host::CommitBaseSettingChanges(); checkForUpdates(true); } @@ -2029,7 +2071,10 @@ void MainWindow::saveDisplayWindowGeometryToConfig() const QByteArray geometry_b64 = geometry.toBase64(); const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry"); if (old_geometry_b64 != geometry_b64.constData()) - QtHost::SetBaseStringSettingValue("UI", "DisplayWindowGeometry", geometry_b64.constData()); + { + Host::SetBaseStringSettingValue("UI", "DisplayWindowGeometry", geometry_b64.constData()); + Host::CommitBaseSettingChanges(); + } } void MainWindow::restoreDisplayWindowGeometryFromConfig() diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h index 38a8e45ff3..f63cb41890 100644 --- a/pcsx2-qt/MainWindow.h +++ b/pcsx2-qt/MainWindow.h @@ -85,6 +85,7 @@ public: void initialize(); void connectVMThreadSignals(EmuThread* thread); void startupUpdateCheck(); + void resetSettings(bool ui); /// Locks the VM by pausing it, while a popup dialog is displayed. VMLock pauseAndLockVM(); @@ -167,8 +168,6 @@ private Q_SLOTS: void onGameChanged(const QString& path, const QString& serial, const QString& name, quint32 crc); - void recreate(); - protected: void showEvent(QShowEvent* event) override; void closeEvent(QCloseEvent* event) override; @@ -186,6 +185,8 @@ private: void setupAdditionalUi(); void connectSignals(); + void recreate(); + void recreateSettings(); void saveStateToConfig(); void restoreStateFromConfig(); diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index 178279ad19..b0e3042149 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -24,8 +24,6 @@ #ifdef _WIN32 #include "common/RedtapeWindows.h" -#include -#include #endif #include "common/Assertions.h" @@ -39,6 +37,7 @@ #include "pcsx2/CDVD/CDVDcommon.h" #include "pcsx2/DebugTools/Debug.h" +#include "pcsx2/Frontend/CommonHost.h" #include "pcsx2/Frontend/GameList.h" #include "pcsx2/Frontend/INISettingsInterface.h" #include "pcsx2/Frontend/LogSink.h" @@ -52,7 +51,6 @@ #include "QtUtils.h" #include "svnrev.h" -static constexpr u32 SETTINGS_VERSION = 1; static constexpr u32 SETTINGS_SAVE_DELAY = 1000; ////////////////////////////////////////////////////////////////////////// @@ -64,14 +62,8 @@ static void PrintCommandLineHelp(const char* progname); static std::shared_ptr& AutoBoot(std::shared_ptr& autoboot); static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr& autoboot); static bool InitializeConfig(); -static bool ShouldUsePortableMode(); -static void SetAppRoot(); -static void SetResourcesDirectory(); -static void SetDataDirectory(); -static void HookSignals(); -static bool SetCriticalFolders(); -static void SetDefaultConfig(); static void SaveSettings(); +static void HookSignals(); } ////////////////////////////////////////////////////////////////////////// @@ -89,225 +81,54 @@ static bool s_start_fullscreen_ui_fullscreen = false; // Initialization/Shutdown ////////////////////////////////////////////////////////////////////////// -bool QtHost::SetCriticalFolders() -{ - SetAppRoot(); - SetResourcesDirectory(); - SetDataDirectory(); - - // logging of directories in case something goes wrong super early - Console.WriteLn("AppRoot Directory: %s", EmuFolders::AppRoot.c_str()); - Console.WriteLn("DataRoot Directory: %s", EmuFolders::DataRoot.c_str()); - Console.WriteLn("Resources Directory: %s", EmuFolders::Resources.c_str()); - - // allow SetDataDirectory() to change settings directory (if we want to split config later on) - if (EmuFolders::Settings.empty()) - EmuFolders::Settings = Path::Combine(EmuFolders::DataRoot, "inis"); - - // Write crash dumps to the data directory, since that'll be accessible for certain. - CrashHandler::SetWriteDirectory(EmuFolders::DataRoot); - - // the resources directory should exist, bail out if not - if (!FileSystem::DirectoryExists(EmuFolders::Resources.c_str())) - { - QMessageBox::critical(nullptr, QStringLiteral("Error"), - QStringLiteral("Resources directory is missing, your installation is incomplete.")); - return false; - } - - return true; -} - -bool QtHost::ShouldUsePortableMode() -{ - // Check whether portable.ini exists in the program directory. - return FileSystem::FileExists(Path::Combine(EmuFolders::AppRoot, "portable.ini").c_str()); -} - -void QtHost::SetAppRoot() -{ - std::string program_path(FileSystem::GetProgramPath()); - Console.WriteLn("Program Path: %s", program_path.c_str()); - - EmuFolders::AppRoot = Path::Canonicalize(Path::GetDirectory(program_path)); -} - -void QtHost::SetResourcesDirectory() -{ -#ifndef __APPLE__ - // On Windows/Linux, these are in the binary directory. - EmuFolders::Resources = Path::Combine(EmuFolders::AppRoot, "resources"); -#else - // On macOS, this is in the bundle resources directory. - EmuFolders::Resources = Path::Canonicalize(Path::Combine(EmuFolders::AppRoot, "../Resources")); -#endif -} - -void QtHost::SetDataDirectory() -{ - if (ShouldUsePortableMode()) - { - EmuFolders::DataRoot = EmuFolders::AppRoot; - return; - } - -#if defined(_WIN32) - // On Windows, use My Documents\PCSX2 to match old installs. - PWSTR documents_directory; - if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &documents_directory))) - { - if (std::wcslen(documents_directory) > 0) - EmuFolders::DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "PCSX2"); - CoTaskMemFree(documents_directory); - } -#elif defined(__linux__) - // Use $XDG_CONFIG_HOME/PCSX2 if it exists. - const char* xdg_config_home = getenv("XDG_CONFIG_HOME"); - if (xdg_config_home && Path::IsAbsolute(xdg_config_home)) - { - EmuFolders::DataRoot = Path::Combine(xdg_config_home, "PCSX2"); - } - else - { - // Use ~/PCSX2 for non-XDG, and ~/.config/PCSX2 for XDG. - // Maybe we should drop the former when Qt goes live. - const char* home_dir = getenv("HOME"); - if (home_dir) - { -#ifndef XDG_STD - EmuFolders::DataRoot = Path::Combine(home_dir, "PCSX2"); -#else - // ~/.config should exist, but just in case it doesn't and this is a fresh profile.. - const std::string config_dir(Path::Combine(home_dir, ".config")); - if (!FileSystem::DirectoryExists(config_dir.c_str())) - FileSystem::CreateDirectoryPath(config_dir.c_str(), false); - - EmuFolders::DataRoot = Path::Combine(config_dir, "PCSX2"); -#endif - } - } -#elif defined(__APPLE__) - static constexpr char MAC_DATA_DIR[] = "Library/Application Support/PCSX2"; - const char* home_dir = getenv("HOME"); - if (home_dir) - EmuFolders::DataRoot = Path::Combine(home_dir, MAC_DATA_DIR); -#endif - - // make sure it exists - if (!EmuFolders::DataRoot.empty() && !FileSystem::DirectoryExists(EmuFolders::DataRoot.c_str())) - { - // we're in trouble if we fail to create this directory... but try to hobble on with portable - if (!FileSystem::CreateDirectoryPath(EmuFolders::DataRoot.c_str(), false)) - EmuFolders::DataRoot.clear(); - } - - // couldn't determine the data directory? fallback to portable. - if (EmuFolders::DataRoot.empty()) - EmuFolders::DataRoot = EmuFolders::AppRoot; -} - bool QtHost::InitializeConfig() { - if (!SetCriticalFolders()) + if (!CommonHost::InitializeCriticalFolders()) + { + QMessageBox::critical(nullptr, QStringLiteral("PCSX2"), + QStringLiteral("One or more critical directories are missing, your installation may be incomplete.")); return false; + } const std::string path(Path::Combine(EmuFolders::Settings, "PCSX2.ini")); Console.WriteLn("Loading config from %s.", path.c_str()); + s_base_settings_interface = std::make_unique(std::move(path)); Host::Internal::SetBaseSettingsLayer(s_base_settings_interface.get()); - - uint settings_version; - if (!s_base_settings_interface->Load() || - !s_base_settings_interface->GetUIntValue("UI", "SettingsVersion", &settings_version) || - settings_version != SETTINGS_VERSION) + if (!s_base_settings_interface->Load() || !CommonHost::CheckSettingsVersion()) { - QMessageBox::critical( - g_main_window, qApp->translate("QtHost", "Settings Reset"), - qApp->translate("QtHost", "Settings do not exist or are the incorrect version, resetting to defaults.")); - SetDefaultConfig(); - s_base_settings_interface->Save(); + // If the config file doesn't exist, assume this is a new install and don't prompt to overwrite. + if (FileSystem::FileExists(s_base_settings_interface->GetFileName().c_str()) && + QMessageBox::question(nullptr, QStringLiteral("PCSX2"), + QStringLiteral("Settings failed to load, or are the incorrect version. Clicking Yes will reset all settings to defaults. Do you want to continue?")) != QMessageBox::Yes) + { + return false; + } + + CommonHost::SetDefaultSettings(*s_base_settings_interface, true, true, true, true, true); + SaveSettings(); } - // TODO: Handle reset to defaults if load fails. - EmuFolders::LoadConfig(*s_base_settings_interface.get()); - EmuFolders::EnsureFoldersExist(); + CommonHost::LoadStartupSettings(); Host::UpdateLogging(QtHost::InNoGUIMode()); return true; } -void QtHost::SetDefaultConfig() +void Host::SetDefaultUISettings(SettingsInterface& si) { - EmuFolders::SetDefaults(); - EmuFolders::EnsureFoldersExist(); + Host::SetDefaultLoggingSettings(si); - SettingsInterface& si = *s_base_settings_interface.get(); - si.SetUIntValue("UI", "SettingsVersion", SETTINGS_VERSION); - VMManager::SetDefaultSettings(si); - EmuFolders::Save(si); - PAD::SetDefaultControllerConfig(si); - PAD::SetDefaultHotkeyConfig(si); -} - -void QtHost::SetBaseBoolSettingValue(const char* section, const char* key, bool value) -{ - auto lock = Host::GetSettingsLock(); - s_base_settings_interface->SetBoolValue(section, key, value); - QueueSettingsSave(); -} - -void QtHost::SetBaseIntSettingValue(const char* section, const char* key, int value) -{ - auto lock = Host::GetSettingsLock(); - s_base_settings_interface->SetIntValue(section, key, value); - QueueSettingsSave(); -} - -void QtHost::SetBaseFloatSettingValue(const char* section, const char* key, float value) -{ - auto lock = Host::GetSettingsLock(); - s_base_settings_interface->SetFloatValue(section, key, value); - QueueSettingsSave(); -} - -void QtHost::SetBaseStringSettingValue(const char* section, const char* key, const char* value) -{ - auto lock = Host::GetSettingsLock(); - s_base_settings_interface->SetStringValue(section, key, value); - QueueSettingsSave(); -} - -void QtHost::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector& values) -{ - auto lock = Host::GetSettingsLock(); - s_base_settings_interface->SetStringList(section, key, values); - QueueSettingsSave(); -} - -bool QtHost::AddBaseValueToStringList(const char* section, const char* key, const char* value) -{ - auto lock = Host::GetSettingsLock(); - if (!s_base_settings_interface->AddToStringList(section, key, value)) - return false; - - QueueSettingsSave(); - return true; -} - -bool QtHost::RemoveBaseValueFromStringList(const char* section, const char* key, const char* value) -{ - auto lock = Host::GetSettingsLock(); - if (!s_base_settings_interface->RemoveFromStringList(section, key, value)) - return false; - - QueueSettingsSave(); - return true; -} - -void QtHost::RemoveBaseSettingValue(const char* section, const char* key) -{ - auto lock = Host::GetSettingsLock(); - s_base_settings_interface->DeleteValue(section, key); - QueueSettingsSave(); + si.SetBoolValue("UI", "InhibitScreensaver", true); + si.SetBoolValue("UI", "ConfirmShutdown", true); + si.SetBoolValue("UI", "StartPaused", false); + si.SetBoolValue("UI", "PauseOnFocusLoss", false); + si.SetBoolValue("UI", "StartFullscreen", false); + si.SetBoolValue("UI", "DoubleClickTogglesFullscreen", true); + si.SetBoolValue("UI", "HideMouseCursor", false); + si.SetBoolValue("UI", "RenderToSeparateWindow", false); + si.SetBoolValue("UI", "HideMainWindowWhenRunning", false); + si.SetBoolValue("UI", "DisableWindowResize", false); + si.SetStringValue("UI", "Theme", MainWindow::DEFAULT_THEME_NAME); } void QtHost::SaveSettings() @@ -324,13 +145,20 @@ void QtHost::SaveSettings() s_settings_save_timer.release(); } -void QtHost::QueueSettingsSave() +void Host::CommitBaseSettingChanges() { + if (!QtHost::IsOnUIThread()) + { + QtHost::RunOnUIThread(&Host::CommitBaseSettingChanges); + return; + } + + auto lock = Host::GetSettingsLock(); if (s_settings_save_timer) return; s_settings_save_timer = std::make_unique(); - s_settings_save_timer->connect(s_settings_save_timer.get(), &QTimer::timeout, SaveSettings); + s_settings_save_timer->connect(s_settings_save_timer.get(), &QTimer::timeout, &QtHost::SaveSettings); s_settings_save_timer->setSingleShot(true); s_settings_save_timer->start(SETTINGS_SAVE_DELAY); } @@ -345,6 +173,12 @@ bool QtHost::InNoGUIMode() return s_nogui_mode; } +bool QtHost::IsOnUIThread() +{ + QThread* ui_thread = qApp->thread(); + return (QThread::currentThread() == ui_thread); +} + void QtHost::RunOnUIThread(const std::function& func, bool block /*= false*/) { // main window always exists, so it's fine to attach it to that. @@ -353,6 +187,21 @@ void QtHost::RunOnUIThread(const std::function& func, bool block /*= fal Q_ARG(const std::function&, func)); } +bool Host::RequestResetSettings(bool folders, bool core, bool controllers, bool hotkeys, bool ui) +{ + { + auto lock = Host::GetSettingsLock(); + CommonHost::SetDefaultSettings(*s_base_settings_interface.get(), folders, core, controllers, hotkeys, ui); + } + Host::CommitBaseSettingChanges(); + + g_emu_thread->applySettings(); + if (folders) + g_emu_thread->updateEmuFolders(); + + return true; +} + QString QtHost::GetAppNameAndVersion() { QString ret; @@ -725,11 +574,7 @@ int main(int argc, char* argv[]) // Bail out if we can't find any config. if (!QtHost::InitializeConfig()) - { - // NOTE: No point translating this, because no config means the language won't be loaded anyway. - QMessageBox::critical(nullptr, QStringLiteral("Error"), QStringLiteral("Failed to initialize config.")); return EXIT_FAILURE; - } // Set theme before creating any windows. MainWindow::updateApplicationTheme(); diff --git a/pcsx2-qt/QtHost.h b/pcsx2-qt/QtHost.h index 08f8823678..fcd34989c2 100644 --- a/pcsx2-qt/QtHost.h +++ b/pcsx2-qt/QtHost.h @@ -46,6 +46,9 @@ namespace QtHost /// Sets NoGUI mode (implys batch mode, does not display main window, exits on shutdown). bool InNoGUIMode(); + /// Returns true if the calling thread is the UI thread. + bool IsOnUIThread(); + /// Executes a function on the UI thread. void RunOnUIThread(const std::function& func, bool block = false); @@ -58,17 +61,6 @@ namespace QtHost /// Returns the base path for resources. This may be : prefixed, if we're using embedded resources. QString GetResourcesBasePath(); - /// Thread-safe settings access. - void SetBaseBoolSettingValue(const char* section, const char* key, bool value); - void SetBaseIntSettingValue(const char* section, const char* key, int value); - void SetBaseFloatSettingValue(const char* section, const char* key, float value); - void SetBaseStringSettingValue(const char* section, const char* key, const char* value); - void SetBaseStringListSettingValue(const char* section, const char* key, const std::vector& values); - bool AddBaseValueToStringList(const char* section, const char* key, const char* value); - bool RemoveBaseValueFromStringList(const char* section, const char* key, const char* value); - void RemoveBaseSettingValue(const char* section, const char* key); - void QueueSettingsSave(); - /// VM state, safe to access on UI thread. bool IsVMValid(); bool IsVMPaused(); diff --git a/pcsx2-qt/SettingWidgetBinder.h b/pcsx2-qt/SettingWidgetBinder.h index f633141c27..2be37708b4 100644 --- a/pcsx2-qt/SettingWidgetBinder.h +++ b/pcsx2-qt/SettingWidgetBinder.h @@ -613,7 +613,8 @@ namespace SettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const bool new_value = Accessor::getBoolValue(widget); - QtHost::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } @@ -653,7 +654,8 @@ namespace SettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), option_offset]() { const int new_value = Accessor::getIntValue(widget); - QtHost::SetBaseIntSettingValue(section.c_str(), key.c_str(), new_value + option_offset); + Host::SetBaseIntSettingValue(section.c_str(), key.c_str(), new_value + option_offset); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } @@ -692,7 +694,8 @@ namespace SettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const float new_value = Accessor::getFloatValue(widget); - QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } @@ -732,7 +735,8 @@ namespace SettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), range]() { const float new_value = (static_cast(Accessor::getIntValue(widget)) / range); - QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } @@ -773,10 +777,11 @@ namespace SettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const QString new_value = Accessor::getStringValue(widget); if (!new_value.isEmpty()) - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); else - QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str()); + Host::RemoveBaseSettingValue(section.c_str(), key.c_str()); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } @@ -835,7 +840,8 @@ namespace SettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), to_string_function]() { const DataType value = static_cast(static_cast(Accessor::getIntValue(widget))); const char* string_value = to_string_function(value); - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } @@ -896,7 +902,8 @@ namespace SettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), enum_names]() { const UnderlyingType value = static_cast(Accessor::getIntValue(widget)); - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_names[value]); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_names[value]); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } @@ -959,7 +966,8 @@ namespace SettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), enum_values]() { const int value = Accessor::getIntValue(widget); - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_values[value]); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_values[value]); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } @@ -996,13 +1004,14 @@ namespace SettingWidgetBinder if (!new_value.empty()) { std::string relative_path(Path::MakeRelative(new_value, EmuFolders::DataRoot)); - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), relative_path.c_str()); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), relative_path.c_str()); } else { - QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str()); + Host::RemoveBaseSettingValue(section.c_str(), key.c_str()); } + Host::CommitBaseSettingChanges(); g_emu_thread->updateEmuFolders(); }); diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.cpp b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp index e87c00768a..7b2a80363d 100644 --- a/pcsx2-qt/Settings/BIOSSettingsWidget.cpp +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp @@ -130,7 +130,8 @@ void BIOSSettingsWidget::listRefreshed(const QVector& items) void BIOSSettingsWidget::listItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous) { - QtHost::SetBaseStringSettingValue("Filenames", "BIOS", current->text(0).toUtf8().constData()); + Host::SetBaseStringSettingValue("Filenames", "BIOS", current->text(0).toUtf8().constData()); + Host::CommitBaseSettingChanges(); } BIOSSettingsWidget::RefreshThread::RefreshThread(BIOSSettingsWidget* parent, const QString& directory) diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp index 5c4455975c..c579177b68 100644 --- a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp @@ -199,13 +199,17 @@ void ControllerBindingWidget::onClearBindingsClicked() { auto lock = Host::GetSettingsLock(); PAD::ClearPortBindings(*Host::Internal::GetBaseSettingsLayer(), m_port_number); + Host::CommitBaseSettingChanges(); } else { PAD::ClearPortBindings(*m_dialog->getProfileSettingsInterface(), m_port_number); + m_dialog->getProfileSettingsInterface()->Save(); } - saveAndRefresh(); + // force a refresh after clearing + g_emu_thread->applySettings(); + onTypeChanged(); } void ControllerBindingWidget::doDeviceAutomaticBinding(const QString& device) @@ -223,27 +227,27 @@ void ControllerBindingWidget::doDeviceAutomaticBinding(const QString& device) { auto lock = Host::GetSettingsLock(); result = PAD::MapController(*Host::Internal::GetBaseSettingsLayer(), m_port_number, mapping); + if (result) + Host::CommitBaseSettingChanges(); } else { result = PAD::MapController(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping); - m_dialog->getProfileSettingsInterface()->Save(); - g_emu_thread->reloadInputBindings(); + if (result) + { + m_dialog->getProfileSettingsInterface()->Save(); + g_emu_thread->reloadInputBindings(); + } } // force a refresh after mapping if (result) - saveAndRefresh(); + { + g_emu_thread->applySettings(); + onTypeChanged(); + } } -void ControllerBindingWidget::saveAndRefresh() -{ - onTypeChanged(); - QtHost::QueueSettingsSave(); - g_emu_thread->applySettings(); -} - - ////////////////////////////////////////////////////////////////////////// ControllerMacroWidget::ControllerMacroWidget(ControllerBindingWidget* parent) diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.h b/pcsx2-qt/Settings/ControllerBindingWidgets.h index a591c659a0..72b692b321 100644 --- a/pcsx2-qt/Settings/ControllerBindingWidgets.h +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.h @@ -58,7 +58,6 @@ private: void populateControllerTypes(); void updateHeaderToolButtons(); void doDeviceAutomaticBinding(const QString& device); - void saveAndRefresh(); Ui::ControllerBindingWidget m_ui; diff --git a/pcsx2-qt/Settings/ControllerSettingWidgetBinder.h b/pcsx2-qt/Settings/ControllerSettingWidgetBinder.h index 4d057e75cb..f9067c3ac1 100644 --- a/pcsx2-qt/Settings/ControllerSettingWidgetBinder.h +++ b/pcsx2-qt/Settings/ControllerSettingWidgetBinder.h @@ -63,7 +63,8 @@ namespace ControllerSettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const bool new_value = Accessor::getBoolValue(widget); - QtHost::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } @@ -94,7 +95,8 @@ namespace ControllerSettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const float new_value = Accessor::getFloatValue(widget); - QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } @@ -127,7 +129,8 @@ namespace ControllerSettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), range]() { const float new_value = (static_cast(Accessor::getIntValue(widget)) / range); - QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } @@ -166,10 +169,11 @@ namespace ControllerSettingWidgetBinder Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const QString new_value = Accessor::getStringValue(widget); if (!new_value.isEmpty()) - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); else - QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str()); + Host::RemoveBaseSettingValue(section.c_str(), key.c_str()); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); }); } diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.cpp b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp index 72761494d3..fb1dce38fb 100644 --- a/pcsx2-qt/Settings/ControllerSettingsDialog.cpp +++ b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp @@ -22,6 +22,7 @@ #include "Settings/ControllerBindingWidgets.h" #include "Settings/HotkeySettingsWidget.h" +#include "pcsx2/Frontend/CommonHost.h" #include "pcsx2/Frontend/INISettingsInterface.h" #include "pcsx2/PAD/Host/PAD.h" #include "pcsx2/Sio.h" @@ -162,7 +163,7 @@ void ControllerSettingsDialog::onLoadProfileClicked() { auto lock = Host::GetSettingsLock(); PAD::CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_profile_interface, true, true, false); - QtHost::QueueSettingsSave(); + Host::CommitBaseSettingChanges(); } g_emu_thread->applySettings(); @@ -206,9 +207,8 @@ void ControllerSettingsDialog::onRestoreDefaultsClicked() // actually restore it { auto lock = Host::GetSettingsLock(); - PAD::SetDefaultControllerConfig(*Host::Internal::GetBaseSettingsLayer()); - PAD::SetDefaultHotkeyConfig(*Host::Internal::GetBaseSettingsLayer()); - QtHost::QueueSettingsSave(); + CommonHost::SetDefaultSettings(*Host::Internal::GetBaseSettingsLayer(), false, false, true, true, false); + Host::CommitBaseSettingChanges(); } g_emu_thread->applySettings(); @@ -295,7 +295,8 @@ void ControllerSettingsDialog::setBoolValue(const char* section, const char* key } else { - QtHost::SetBaseBoolSettingValue(section, key, value); + Host::SetBaseBoolSettingValue(section, key, value); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); } } @@ -310,7 +311,8 @@ void ControllerSettingsDialog::setIntValue(const char* section, const char* key, } else { - QtHost::SetBaseIntSettingValue(section, key, value); + Host::SetBaseIntSettingValue(section, key, value); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); } } @@ -325,7 +327,8 @@ void ControllerSettingsDialog::setStringValue(const char* section, const char* k } else { - QtHost::SetBaseStringSettingValue(section, key, value); + Host::SetBaseStringSettingValue(section, key, value); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); } } @@ -340,7 +343,8 @@ void ControllerSettingsDialog::clearSettingValue(const char* section, const char } else { - QtHost::RemoveBaseSettingValue(section, key); + Host::RemoveBaseSettingValue(section, key); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); } } diff --git a/pcsx2-qt/Settings/GameListSettingsWidget.cpp b/pcsx2-qt/Settings/GameListSettingsWidget.cpp index 023ed98a83..4a2434bee4 100644 --- a/pcsx2-qt/Settings/GameListSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GameListSettingsWidget.cpp @@ -65,9 +65,10 @@ GameListSettingsWidget::~GameListSettingsWidget() = default; bool GameListSettingsWidget::addExcludedPath(const std::string& path) { - if (!QtHost::AddBaseValueToStringList("GameList", "ExcludedPaths", path.c_str())) + if (!Host::AddBaseValueToStringList("GameList", "ExcludedPaths", path.c_str())) return false; + Host::CommitBaseSettingChanges(); m_ui.excludedPaths->addItem(QString::fromStdString(path)); g_main_window->refreshGameList(false); return true; @@ -107,14 +108,15 @@ void GameListSettingsWidget::addPathToTable(const std::string& path, bool recurs const std::string path(item->text().toStdString()); if (state == Qt::Checked) { - QtHost::RemoveBaseValueFromStringList("GameList", "Paths", path.c_str()); - QtHost::AddBaseValueToStringList("GameList", "RecursivePaths", path.c_str()); + Host::RemoveBaseValueFromStringList("GameList", "Paths", path.c_str()); + Host::AddBaseValueToStringList("GameList", "RecursivePaths", path.c_str()); } else { - QtHost::RemoveBaseValueFromStringList("GameList", "RecursivePaths", path.c_str()); - QtHost::AddBaseValueToStringList("GameList", "Paths", path.c_str()); + Host::RemoveBaseValueFromStringList("GameList", "RecursivePaths", path.c_str()); + Host::AddBaseValueToStringList("GameList", "Paths", path.c_str()); } + Host::CommitBaseSettingChanges(); }); } @@ -138,8 +140,9 @@ void GameListSettingsWidget::refreshDirectoryList() void GameListSettingsWidget::addSearchDirectory(const QString& path, bool recursive) { const std::string spath(path.toStdString()); - QtHost::RemoveBaseValueFromStringList("GameList", recursive ? "Paths" : "RecursivePaths", spath.c_str()); - QtHost::AddBaseValueToStringList("GameList", recursive ? "RecursivePaths" : "Paths", spath.c_str()); + Host::RemoveBaseValueFromStringList("GameList", recursive ? "Paths" : "RecursivePaths", spath.c_str()); + Host::AddBaseValueToStringList("GameList", recursive ? "RecursivePaths" : "Paths", spath.c_str()); + Host::CommitBaseSettingChanges(); refreshDirectoryList(); g_main_window->refreshGameList(false); } @@ -147,12 +150,13 @@ void GameListSettingsWidget::addSearchDirectory(const QString& path, bool recurs void GameListSettingsWidget::removeSearchDirectory(const QString& path) { const std::string spath(path.toStdString()); - if (!QtHost::RemoveBaseValueFromStringList("GameList", "Paths", spath.c_str()) && - !QtHost::RemoveBaseValueFromStringList("GameList", "RecursivePaths", spath.c_str())) + if (!Host::RemoveBaseValueFromStringList("GameList", "Paths", spath.c_str()) && + !Host::RemoveBaseValueFromStringList("GameList", "RecursivePaths", spath.c_str())) { return; } + Host::CommitBaseSettingChanges(); refreshDirectoryList(); g_main_window->refreshGameList(false); } @@ -228,7 +232,9 @@ void GameListSettingsWidget::onRemoveExcludedPathButtonClicked() if (!item) return; - QtHost::RemoveBaseValueFromStringList("GameList", "ExcludedPaths", item->text().toUtf8().constData()); + if (Host::RemoveBaseValueFromStringList("GameList", "ExcludedPaths", item->text().toUtf8().constData())) + Host::CommitBaseSettingChanges(); + delete item; g_main_window->refreshGameList(false); diff --git a/pcsx2-qt/Settings/InputBindingDialog.cpp b/pcsx2-qt/Settings/InputBindingDialog.cpp index 9dbd1b13d8..42f6a2b8d2 100644 --- a/pcsx2-qt/Settings/InputBindingDialog.cpp +++ b/pcsx2-qt/Settings/InputBindingDialog.cpp @@ -259,9 +259,10 @@ void InputBindingDialog::saveListToSettings() else { if (!m_bindings.empty()) - QtHost::SetBaseStringListSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_bindings); + Host::SetBaseStringListSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_bindings); else - QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + Host::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + Host::CommitBaseSettingChanges(); g_emu_thread->reloadInputBindings(); } } diff --git a/pcsx2-qt/Settings/InputBindingWidget.cpp b/pcsx2-qt/Settings/InputBindingWidget.cpp index e6d13f87b7..0c818ecf21 100644 --- a/pcsx2-qt/Settings/InputBindingWidget.cpp +++ b/pcsx2-qt/Settings/InputBindingWidget.cpp @@ -236,7 +236,8 @@ void InputBindingWidget::setNewBinding() } else { - QtHost::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str()); + Host::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str()); + Host::CommitBaseSettingChanges(); g_emu_thread->reloadInputBindings(); } } @@ -256,7 +257,8 @@ void InputBindingWidget::clearBinding() } else { - QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + Host::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + Host::CommitBaseSettingChanges(); g_emu_thread->reloadInputBindings(); } reloadBinding(); @@ -413,7 +415,8 @@ void InputVibrationBindingWidget::setKey(ControllerSettingsDialog* dialog, std:: void InputVibrationBindingWidget::clearBinding() { m_binding = {}; - QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + Host::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + Host::CommitBaseSettingChanges(); g_emu_thread->reloadInputBindings(); setText(QString()); } @@ -448,7 +451,8 @@ void InputVibrationBindingWidget::onClicked() const QString new_value(input_dialog.textValue()); m_binding = new_value.toStdString(); - QtHost::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_binding.c_str()); + Host::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_binding.c_str()); + Host::CommitBaseSettingChanges(); setText(new_value); } diff --git a/pcsx2-qt/Settings/SettingsDialog.cpp b/pcsx2-qt/Settings/SettingsDialog.cpp index 5550973154..8299cd4229 100644 --- a/pcsx2-qt/Settings/SettingsDialog.cpp +++ b/pcsx2-qt/Settings/SettingsDialog.cpp @@ -146,9 +146,6 @@ void SettingsDialog::setupUi(const GameList::Entry* game) connect(m_ui.settingsCategory, &QListWidget::currentRowChanged, this, &SettingsDialog::onCategoryCurrentRowChanged); connect(m_ui.closeButton, &QPushButton::clicked, this, &SettingsDialog::accept); connect(m_ui.restoreDefaultsButton, &QPushButton::clicked, this, &SettingsDialog::onRestoreDefaultsClicked); - - // TODO: Remove this once they're implemented. - m_ui.restoreDefaultsButton->setVisible(false); } SettingsDialog::~SettingsDialog() @@ -164,6 +161,11 @@ void SettingsDialog::closeEvent(QCloseEvent*) deleteLater(); } +QString SettingsDialog::getCategory() const +{ + return m_ui.settingsCategory->item(m_ui.settingsCategory->currentRow())->text(); +} + void SettingsDialog::setCategory(const char* category) { // the titles in the category list will be translated. @@ -188,14 +190,20 @@ void SettingsDialog::onCategoryCurrentRowChanged(int row) void SettingsDialog::onRestoreDefaultsClicked() { - if (QMessageBox::question(this, tr("Confirm Restore Defaults"), - tr("Are you sure you want to restore the default settings? Any preferences will be lost."), QMessageBox::Yes, - QMessageBox::No) != QMessageBox::Yes) - { - return; - } + QMessageBox msgbox(this); + msgbox.setIcon(QMessageBox::Question); + msgbox.setWindowTitle(tr("Confirm Restore Defaults")); + msgbox.setText(tr("Are you sure you want to restore the default settings? Any preferences will be lost.")); - // TODO + QCheckBox* ui_cb = new QCheckBox(tr("Reset UI Settings"), &msgbox); + msgbox.setCheckBox(ui_cb); + msgbox.addButton(QMessageBox::Yes); + msgbox.addButton(QMessageBox::No); + msgbox.setDefaultButton(QMessageBox::Yes); + if (msgbox.exec() != QMessageBox::Yes) + return; + + g_main_window->resetSettings(ui_cb->isChecked()); } void SettingsDialog::addWidget(QWidget* widget, QString title, QString icon, QString help_text) @@ -376,7 +384,8 @@ void SettingsDialog::setBoolSettingValue(const char* section, const char* key, s } else { - value.has_value() ? QtHost::SetBaseBoolSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key); + value.has_value() ? Host::SetBaseBoolSettingValue(section, key, value.value()) : Host::RemoveBaseSettingValue(section, key); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); } } @@ -391,7 +400,8 @@ void SettingsDialog::setIntSettingValue(const char* section, const char* key, st } else { - value.has_value() ? QtHost::SetBaseIntSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key); + value.has_value() ? Host::SetBaseIntSettingValue(section, key, value.value()) : Host::RemoveBaseSettingValue(section, key); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); } } @@ -406,7 +416,8 @@ void SettingsDialog::setFloatSettingValue(const char* section, const char* key, } else { - value.has_value() ? QtHost::SetBaseFloatSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key); + value.has_value() ? Host::SetBaseFloatSettingValue(section, key, value.value()) : Host::RemoveBaseSettingValue(section, key); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); } } @@ -421,7 +432,8 @@ void SettingsDialog::setStringSettingValue(const char* section, const char* key, } else { - value.has_value() ? QtHost::SetBaseStringSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key); + value.has_value() ? Host::SetBaseStringSettingValue(section, key, value.value()) : Host::RemoveBaseSettingValue(section, key); + Host::CommitBaseSettingChanges(); g_emu_thread->applySettings(); } } diff --git a/pcsx2-qt/Settings/SettingsDialog.h b/pcsx2-qt/Settings/SettingsDialog.h index 0bc3d5b668..20533f031e 100644 --- a/pcsx2-qt/Settings/SettingsDialog.h +++ b/pcsx2-qt/Settings/SettingsDialog.h @@ -72,6 +72,7 @@ public: void registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text); bool eventFilter(QObject* object, QEvent* event) override; + QString getCategory() const; void setCategory(const char* category); // Helper functions for reading effective setting values (from game -> global settings). diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 8e20d49010..f1aacdc69b 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -1063,8 +1063,10 @@ endif() if(PCSX2_CORE) list(APPEND pcsx2FrontendSources + Frontend/CommonHost.cpp Frontend/FullscreenUI.cpp Frontend/GameList.cpp + Frontend/HostSettings.cpp Frontend/ImGuiFullscreen.cpp Frontend/INISettingsInterface.cpp Frontend/InputManager.cpp @@ -1072,10 +1074,10 @@ if(PCSX2_CORE) Frontend/LayeredSettingsInterface.cpp Frontend/LogSink.cpp GSDumpReplayer.cpp - HostSettings.cpp VMManager.cpp ) list(APPEND pcsx2FrontendHeaders + Frontend/CommonHost.h Frontend/FullscreenUI.h Frontend/GameList.h Frontend/ImGuiFullscreen.h diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 1c98df8b5d..c5ad9c2713 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -1048,10 +1048,9 @@ namespace EmuFolders extern std::string InputProfiles; // Assumes that AppRoot and DataRoot have been initialized. - void SetDefaults(); - bool EnsureFoldersExist(); + void SetDefaults(SettingsInterface& si); void LoadConfig(SettingsInterface& si); - void Save(SettingsInterface& si); + bool EnsureFoldersExist(); } // namespace EmuFolders ///////////////////////////////////////////////////////////////////////////////////////// diff --git a/pcsx2/Frontend/CommonHost.cpp b/pcsx2/Frontend/CommonHost.cpp new file mode 100644 index 0000000000..3048dd7022 --- /dev/null +++ b/pcsx2/Frontend/CommonHost.cpp @@ -0,0 +1,203 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" +#include "common/Assertions.h" +#include "common/CrashHandler.h" +#include "common/FileSystem.h" +#include "common/Path.h" +#include "Frontend/CommonHost.h" +#include "Frontend/LayeredSettingsInterface.h" +#include "GS.h" +#include "GS/Renderers/HW/GSTextureReplacements.h" +#include "Host.h" +#include "HostSettings.h" +#include "MemoryCardFile.h" +#include "PAD/Host/PAD.h" +#include "Sio.h" +#include "VMManager.h" + +#ifdef _WIN32 +#include "common/RedtapeWindows.h" +#include +#include +#endif + +static constexpr u32 SETTINGS_VERSION = 1; + +namespace CommonHost +{ + static void SetAppRoot(); + static void SetResourcesDirectory(); + static bool ShouldUsePortableMode(); + static void SetDataDirectory(); + static void SetCommonDefaultSettings(SettingsInterface& si); +} // namespace CommonHost + +bool CommonHost::InitializeCriticalFolders() +{ + SetAppRoot(); + SetResourcesDirectory(); + SetDataDirectory(); + + // logging of directories in case something goes wrong super early + Console.WriteLn("AppRoot Directory: %s", EmuFolders::AppRoot.c_str()); + Console.WriteLn("DataRoot Directory: %s", EmuFolders::DataRoot.c_str()); + Console.WriteLn("Resources Directory: %s", EmuFolders::Resources.c_str()); + + // allow SetDataDirectory() to change settings directory (if we want to split config later on) + if (EmuFolders::Settings.empty()) + EmuFolders::Settings = Path::Combine(EmuFolders::DataRoot, "inis"); + + // Write crash dumps to the data directory, since that'll be accessible for certain. + CrashHandler::SetWriteDirectory(EmuFolders::DataRoot); + + // the resources directory should exist, bail out if not + if (!FileSystem::DirectoryExists(EmuFolders::Resources.c_str())) + { + Console.Error("Resources directory is missing."); + return false; + } + + return true; +} + +void CommonHost::SetAppRoot() +{ + std::string program_path(FileSystem::GetProgramPath()); + Console.WriteLn("Program Path: %s", program_path.c_str()); + + EmuFolders::AppRoot = Path::Canonicalize(Path::GetDirectory(program_path)); +} + +void CommonHost::SetResourcesDirectory() +{ +#ifndef __APPLE__ + // On Windows/Linux, these are in the binary directory. + EmuFolders::Resources = Path::Combine(EmuFolders::AppRoot, "resources"); +#else + // On macOS, this is in the bundle resources directory. + EmuFolders::Resources = Path::Canonicalize(Path::Combine(EmuFolders::AppRoot, "../Resources")); +#endif +} + +bool CommonHost::ShouldUsePortableMode() +{ + // Check whether portable.ini exists in the program directory. + return FileSystem::FileExists(Path::Combine(EmuFolders::AppRoot, "portable.ini").c_str()); +} + +void CommonHost::SetDataDirectory() +{ + if (ShouldUsePortableMode()) + { + EmuFolders::DataRoot = EmuFolders::AppRoot; + return; + } + +#if defined(_WIN32) + // On Windows, use My Documents\PCSX2 to match old installs. + PWSTR documents_directory; + if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &documents_directory))) + { + if (std::wcslen(documents_directory) > 0) + EmuFolders::DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "PCSX2"); + CoTaskMemFree(documents_directory); + } +#elif defined(__linux__) + // Use $XDG_CONFIG_HOME/PCSX2 if it exists. + const char* xdg_config_home = getenv("XDG_CONFIG_HOME"); + if (xdg_config_home && Path::IsAbsolute(xdg_config_home)) + { + EmuFolders::DataRoot = Path::Combine(xdg_config_home, "PCSX2"); + } + else + { + // Use ~/PCSX2 for non-XDG, and ~/.config/PCSX2 for XDG. + // Maybe we should drop the former when Qt goes live. + const char* home_dir = getenv("HOME"); + if (home_dir) + { +#ifndef XDG_STD + EmuFolders::DataRoot = Path::Combine(home_dir, "PCSX2"); +#else + // ~/.config should exist, but just in case it doesn't and this is a fresh profile.. + const std::string config_dir(Path::Combine(home_dir, ".config")); + if (!FileSystem::DirectoryExists(config_dir.c_str())) + FileSystem::CreateDirectoryPath(config_dir.c_str(), false); + + EmuFolders::DataRoot = Path::Combine(config_dir, "PCSX2"); +#endif + } + } +#elif defined(__APPLE__) + static constexpr char MAC_DATA_DIR[] = "Library/Application Support/PCSX2"; + const char* home_dir = getenv("HOME"); + if (home_dir) + EmuFolders::DataRoot = Path::Combine(home_dir, MAC_DATA_DIR); +#endif + + // make sure it exists + if (!EmuFolders::DataRoot.empty() && !FileSystem::DirectoryExists(EmuFolders::DataRoot.c_str())) + { + // we're in trouble if we fail to create this directory... but try to hobble on with portable + if (!FileSystem::CreateDirectoryPath(EmuFolders::DataRoot.c_str(), false)) + EmuFolders::DataRoot.clear(); + } + + // couldn't determine the data directory? fallback to portable. + if (EmuFolders::DataRoot.empty()) + EmuFolders::DataRoot = EmuFolders::AppRoot; +} + +bool CommonHost::CheckSettingsVersion() +{ + SettingsInterface* bsi = Host::Internal::GetBaseSettingsLayer(); + + uint settings_version; + return (bsi->GetUIntValue("UI", "SettingsVersion", &settings_version) && settings_version == SETTINGS_VERSION); +} + +void CommonHost::LoadStartupSettings() +{ + SettingsInterface* bsi = Host::Internal::GetBaseSettingsLayer(); + EmuFolders::LoadConfig(*bsi); + EmuFolders::EnsureFoldersExist(); +} + +void CommonHost::SetDefaultSettings(SettingsInterface& si, bool folders, bool core, bool controllers, bool hotkeys, bool ui) +{ + if (si.GetUIntValue("UI", "SettingsVersion", 0u) != SETTINGS_VERSION) + si.SetUIntValue("UI", "SettingsVersion", SETTINGS_VERSION); + + if (folders) + EmuFolders::SetDefaults(si); + if (core) + { + VMManager::SetDefaultSettings(si); + SetCommonDefaultSettings(si); + } + if (controllers) + PAD::SetDefaultControllerConfig(si); + if (hotkeys) + PAD::SetDefaultHotkeyConfig(si); + if (ui) + Host::SetDefaultUISettings(si); +} + +void CommonHost::SetCommonDefaultSettings(SettingsInterface& si) +{ + // Nothing here yet. +} diff --git a/pcsx2/Frontend/CommonHost.h b/pcsx2/Frontend/CommonHost.h new file mode 100644 index 0000000000..5e6442f9ff --- /dev/null +++ b/pcsx2/Frontend/CommonHost.h @@ -0,0 +1,43 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include "common/Pcsx2Defs.h" +#include +#include + +class SettingsInterface; + +namespace Host +{ + /// Sets host-specific default settings. + void SetDefaultUISettings(SettingsInterface& si); +} // namespace Host + +namespace CommonHost +{ + /// Initializes critical folders (AppRoot, DataRoot, Settings). Call once on startup. + bool InitializeCriticalFolders(); + + /// Checks settings version. Call once on startup. If it returns false, you should prompt the user to reset. + bool CheckSettingsVersion(); + + /// Loads early settings. Call once on startup. + void LoadStartupSettings(); + + /// Sets default settings for the specified categories. + void SetDefaultSettings(SettingsInterface& si, bool folders, bool core, bool controllers, bool hotkeys, bool ui); +} // namespace CommonHost diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index 9c6fdfb164..1ce9dbd852 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -119,7 +119,9 @@ using ImGuiFullscreen::MenuImageButton; using ImGuiFullscreen::NavButton; using ImGuiFullscreen::NavTitle; using ImGuiFullscreen::OpenChoiceDialog; +using ImGuiFullscreen::OpenConfirmMessageDialog; using ImGuiFullscreen::OpenFileSelector; +using ImGuiFullscreen::OpenInfoMessageDialog; using ImGuiFullscreen::OpenInputStringDialog; using ImGuiFullscreen::PopPrimaryColor; using ImGuiFullscreen::PushPrimaryColor; @@ -279,6 +281,7 @@ namespace FullscreenUI static void DoLoadInputProfile(); static void DoSaveInputProfile(); static void DoSaveInputProfile(const std::string& name); + static void DoResetSettings(); static bool DrawToggleSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key, bool default_value, bool enabled = true, bool allow_tristate = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, @@ -2022,6 +2025,13 @@ void FullscreenUI::DrawInterfaceSettingsPage() DrawToggleSetting(bsi, ICON_FA_PLAY " Show Status Indicators", "Shows indicators when fast forwarding, pausing, and other abnormal states are active.", "EmuCore/GS", "OsdShowIndicators", true); + MenuHeading("Operations"); + if (MenuButton(ICON_FA_FOLDER_MINUS " Reset Settings", "Resets configuration to defaults (excluding controller settings).", + !IsEditingGameSettings(bsi))) + { + DoResetSettings(); + } + EndMenuButtons(); } @@ -3006,6 +3016,18 @@ void FullscreenUI::DoSaveInputProfile() }); } +void FullscreenUI::DoResetSettings() +{ + OpenConfirmMessageDialog(ICON_FA_FOLDER_MINUS " Reset Settings", + "Are you sure you want to restore the default settings? Any preferences will be lost.", [](bool result) { + if (result) + { + Host::RunOnCPUThread([]() { Host::RequestResetSettings(false, true, false, false, false); }); + ShowToast(std::string(), "Settings reset to defaults."); + } + }); +} + void FullscreenUI::DrawControllerSettingsPage() { BeginMenuButtons(); @@ -4177,98 +4199,6 @@ void FullscreenUI::DrawAboutWindow() ImGui::PopFont(); } -bool FullscreenUI::DrawErrorWindow(const char* message) -{ - bool is_open = true; - - ImGuiFullscreen::BeginLayout(); - - ImGui::SetNextWindowSize(LayoutScale(500.0f, 0.0f)); - ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::OpenPopup("ReportError"); - - ImGui::PushFont(g_large_font); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(10.0f, 10.0f)); - - if (ImGui::BeginPopupModal("ReportError", &is_open, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize)) - { - ImGui::SetCursorPos(LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); - ImGui::TextWrapped("%s", message); - ImGui::GetCurrentWindow()->DC.CursorPos.y += LayoutScale(5.0f); - - BeginMenuButtons(); - - if (ActiveButton(ICON_FA_WINDOW_CLOSE " Close", false)) - { - ImGui::CloseCurrentPopup(); - is_open = false; - } - EndMenuButtons(); - - ImGui::EndPopup(); - } - - ImGui::PopStyleVar(2); - ImGui::PopFont(); - - ImGuiFullscreen::EndLayout(); - return !is_open; -} - -bool FullscreenUI::DrawConfirmWindow(const char* message, bool* result) -{ - bool is_open = true; - - ImGuiFullscreen::BeginLayout(); - - ImGui::SetNextWindowSize(LayoutScale(500.0f, 0.0f)); - ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::OpenPopup("ConfirmMessage"); - - ImGui::PushFont(g_large_font); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(10.0f, 10.0f)); - - if (ImGui::BeginPopupModal("ConfirmMessage", &is_open, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize)) - { - ImGui::SetCursorPos(LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); - ImGui::TextWrapped("%s", message); - ImGui::GetCurrentWindow()->DC.CursorPos.y += LayoutScale(5.0f); - - BeginMenuButtons(); - - bool done = false; - - if (ActiveButton(ICON_FA_CHECK " Yes", false)) - { - *result = true; - done = true; - } - - if (ActiveButton(ICON_FA_TIMES " No", false)) - { - *result = false; - done = true; - } - if (done) - { - ImGui::CloseCurrentPopup(); - is_open = false; - } - - EndMenuButtons(); - - ImGui::EndPopup(); - } - - ImGui::PopStyleVar(2); - ImGui::PopFont(); - - ImGuiFullscreen::EndLayout(); - return !is_open; -} - FullscreenUI::ProgressCallback::ProgressCallback(std::string name) : BaseProgressCallback() , m_name(std::move(name)) diff --git a/pcsx2/Frontend/FullscreenUI.h b/pcsx2/Frontend/FullscreenUI.h index e57e523875..c08276830d 100644 --- a/pcsx2/Frontend/FullscreenUI.h +++ b/pcsx2/Frontend/FullscreenUI.h @@ -36,10 +36,6 @@ namespace FullscreenUI void Shutdown(); void Render(); - // Returns true if the message has been dismissed. - bool DrawErrorWindow(const char* message); - bool DrawConfirmWindow(const char* message, bool* result); - class ProgressCallback final : public BaseProgressCallback { public: diff --git a/pcsx2/HostSettings.cpp b/pcsx2/Frontend/HostSettings.cpp similarity index 92% rename from pcsx2/HostSettings.cpp rename to pcsx2/Frontend/HostSettings.cpp index 7844864e61..bbfc91005e 100644 --- a/pcsx2/HostSettings.cpp +++ b/pcsx2/Frontend/HostSettings.cpp @@ -15,6 +15,9 @@ #include "PrecompiledHeader.h" #include "common/Assertions.h" +#include "common/CrashHandler.h" +#include "common/FileSystem.h" +#include "common/Path.h" #include "Frontend/LayeredSettingsInterface.h" #include "GS.h" #include "GS/Renderers/HW/GSTextureReplacements.h" @@ -121,18 +124,24 @@ void Host::SetBaseStringListSettingValue(const char* section, const char* key, c s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetStringList(section, key, values); } -void Host::DeleteBaseSettingValue(const char* section, const char* key) +bool Host::AddBaseValueToStringList(const char* section, const char* key, const char* value) +{ + std::unique_lock lock(s_settings_mutex); + return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->AddToStringList(section, key, value); +} + +bool Host::RemoveBaseValueFromStringList(const char* section, const char* key, const char* value) +{ + std::unique_lock lock(s_settings_mutex); + return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->RemoveFromStringList(section, key, value); +} + +void Host::RemoveBaseSettingValue(const char* section, const char* key) { std::unique_lock lock(s_settings_mutex); s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->DeleteValue(section, key); } -void Host::CommitBaseSettingChanges() -{ - std::unique_lock lock(s_settings_mutex); - s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->Save(); -} - std::string Host::GetStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/) { std::unique_lock lock(s_settings_mutex); @@ -221,8 +230,7 @@ void Host::Internal::UpdateEmuFolders() if (VMManager::HasValidVM()) { - if (EmuFolders::Cheats != old_cheats_directory || - EmuFolders::CheatsWS != old_cheats_ws_directory || + if (EmuFolders::Cheats != old_cheats_directory || EmuFolders::CheatsWS != old_cheats_ws_directory || EmuFolders::CheatsNI != old_cheats_ni_directory) { VMManager::ReloadPatches(true, true); diff --git a/pcsx2/Frontend/INISettingsInterface.h b/pcsx2/Frontend/INISettingsInterface.h index 76d0a81788..2a3c0ca33c 100644 --- a/pcsx2/Frontend/INISettingsInterface.h +++ b/pcsx2/Frontend/INISettingsInterface.h @@ -29,6 +29,7 @@ public: ~INISettingsInterface() override; const std::string& GetFileName() const { return m_filename; } + bool IsDirty() const { return m_dirty; } bool Load(); bool Save() override; diff --git a/pcsx2/Frontend/ImGuiFullscreen.cpp b/pcsx2/Frontend/ImGuiFullscreen.cpp index 1fcb544813..b664dd3dc0 100644 --- a/pcsx2/Frontend/ImGuiFullscreen.cpp +++ b/pcsx2/Frontend/ImGuiFullscreen.cpp @@ -32,12 +32,16 @@ #include "HostDisplay.h" #include "imgui_internal.h" #include "misc/cpp/imgui_stdlib.h" +#include #include #include #include +#include namespace ImGuiFullscreen { + using MessageDialogCallbackVariant = std::variant; + static std::optional LoadTextureImage(const char* path); static std::shared_ptr UploadTexture(const char* path, const Common::RGBA8Image& image); static void TextureLoaderThread(); @@ -45,6 +49,7 @@ namespace ImGuiFullscreen static void DrawFileSelector(); static void DrawChoiceDialog(); static void DrawInputDialog(); + static void DrawMessageDialog(); static void DrawBackgroundProgressDialogs(ImVec2& position, float spacing); static void DrawNotifications(ImVec2& position, float spacing); static void DrawToast(); @@ -110,6 +115,12 @@ namespace ImGuiFullscreen static std::string s_input_dialog_ok_text; static InputStringDialogCallback s_input_dialog_callback; + static bool s_message_dialog_open = false; + static std::string s_message_dialog_title; + static std::string s_message_dialog_message; + static std::array s_message_dialog_buttons; + static MessageDialogCallbackVariant s_message_dialog_callback; + struct FileSelectorItem { FileSelectorItem() = default; @@ -215,6 +226,7 @@ void ImGuiFullscreen::Shutdown() s_notifications.clear(); s_background_progress_dialogs.clear(); CloseInputDialog(); + CloseMessageDialog(); s_choice_dialog_open = false; s_choice_dialog_checkable = false; s_choice_dialog_title = {}; @@ -459,6 +471,7 @@ void ImGuiFullscreen::EndLayout() DrawFileSelector(); DrawChoiceDialog(); DrawInputDialog(); + DrawMessageDialog(); const float notification_margin = LayoutScale(10.0f); const float spacing = LayoutScale(10.0f); @@ -1839,9 +1852,13 @@ void ImGuiFullscreen::DrawInputDialog() ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::OpenPopup(s_input_dialog_title.c_str()); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(20.0f, 20.0f)); ImGui::PushFont(g_large_font); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); + ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); + ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor); + ImGui::PushStyleColor(ImGuiCol_PopupBg, UIBackgroundColor); bool is_open = true; if (ImGui::BeginPopupModal(s_input_dialog_title.c_str(), &is_open, @@ -1884,8 +1901,9 @@ void ImGuiFullscreen::DrawInputDialog() if (!is_open) CloseInputDialog(); - ImGui::PopFont(); + ImGui::PopStyleColor(4); ImGui::PopStyleVar(2); + ImGui::PopFont(); } void ImGuiFullscreen::CloseInputDialog() @@ -1902,6 +1920,125 @@ void ImGuiFullscreen::CloseInputDialog() s_input_dialog_callback = {}; } +bool ImGuiFullscreen::IsMessageBoxDialogOpen() +{ + return s_message_dialog_open; +} + +void ImGuiFullscreen::OpenConfirmMessageDialog( + std::string title, std::string message, ConfirmMessageDialogCallback callback, std::string yes_button_text, std::string no_button_text) +{ + CloseMessageDialog(); + + s_message_dialog_open = true; + s_message_dialog_title = std::move(title); + s_message_dialog_message = std::move(message); + s_message_dialog_callback = std::move(callback); + s_message_dialog_buttons[0] = std::move(yes_button_text); + s_message_dialog_buttons[1] = std::move(no_button_text); +} + +void ImGuiFullscreen::OpenInfoMessageDialog( + std::string title, std::string message, InfoMessageDialogCallback callback, std::string button_text) +{ + CloseMessageDialog(); + + s_message_dialog_open = true; + s_message_dialog_title = std::move(title); + s_message_dialog_message = std::move(message); + s_message_dialog_callback = std::move(callback); + s_message_dialog_buttons[0] = std::move(button_text); +} + +void ImGuiFullscreen::OpenMessageDialog(std::string title, std::string message, MessageDialogCallback callback, + std::string first_button_text, std::string second_button_text, std::string third_button_text) +{ + CloseMessageDialog(); + + s_message_dialog_open = true; + s_message_dialog_title = std::move(title); + s_message_dialog_message = std::move(message); + s_message_dialog_callback = std::move(callback); + s_message_dialog_buttons[0] = std::move(first_button_text); + s_message_dialog_buttons[1] = std::move(second_button_text); + s_message_dialog_buttons[2] = std::move(third_button_text); +} + +void ImGuiFullscreen::CloseMessageDialog() +{ + if (!s_message_dialog_open) + return; + + s_message_dialog_open = false; + s_message_dialog_title = {}; + s_message_dialog_message = {}; + s_message_dialog_buttons = {}; + s_message_dialog_callback = {}; +} + +void ImGuiFullscreen::DrawMessageDialog() +{ + if (!s_message_dialog_open) + return; + + const char* win_id = s_message_dialog_title.empty() ? "##messagedialog" : s_message_dialog_title.c_str(); + + ImGui::SetNextWindowSize(LayoutScale(700.0f, 0.0f)); + ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); + ImGui::OpenPopup(win_id); + + ImGui::PushFont(g_large_font); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(20.0f, 20.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); + ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); + ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor); + ImGui::PushStyleColor(ImGuiCol_PopupBg, UIBackgroundColor); + + bool is_open = true; + const u32 flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | + (s_message_dialog_title.empty() ? ImGuiWindowFlags_NoTitleBar : 0); + std::optional result; + + if (ImGui::BeginPopupModal(win_id, &is_open, flags)) + { + BeginMenuButtons(); + + ImGui::TextWrapped("%s", s_message_dialog_message.c_str()); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); + + for (s32 button_index = 0; button_index < static_cast(s_message_dialog_buttons.size()); button_index++) + { + if (!s_message_dialog_buttons[button_index].empty() && ActiveButton(s_message_dialog_buttons[button_index].c_str(), false)) + { + result = button_index; + ImGui::CloseCurrentPopup(); + } + } + + EndMenuButtons(); + + ImGui::EndPopup(); + } + + ImGui::PopStyleColor(4); + ImGui::PopStyleVar(3); + ImGui::PopFont(); + + if (!is_open || result.has_value()) + { + // have to move out in case they open another dialog in the callback + auto cb = (std::move(s_message_dialog_callback)); + CloseMessageDialog(); + + if (std::holds_alternative(cb)) + std::get(cb)(); + else if (std::holds_alternative(cb)) + std::get(cb)(result.value_or(1) == 0); + } +} + static float s_notification_vertical_position = 0.3f; static float s_notification_vertical_direction = -1.0f; diff --git a/pcsx2/Frontend/ImGuiFullscreen.h b/pcsx2/Frontend/ImGuiFullscreen.h index e8db08153e..7aaa1e4f13 100644 --- a/pcsx2/Frontend/ImGuiFullscreen.h +++ b/pcsx2/Frontend/ImGuiFullscreen.h @@ -15,6 +15,7 @@ #pragma once #include "common/Pcsx2Defs.h" +#include "IconsFontAwesome5.h" #include "imgui.h" #include "imgui_internal.h" #include @@ -90,10 +91,7 @@ namespace ImGuiFullscreen return ImVec2(g_layout_padding_left + x * g_layout_scale, g_layout_padding_top + y * g_layout_scale); } - static __fi ImVec4 ModAlpha(const ImVec4& v, float a) - { - return ImVec4(v.x, v.y, v.z, a); - } + static __fi ImVec4 ModAlpha(const ImVec4& v, float a) { return ImVec4(v.x, v.y, v.z, a); } /// Centers an image within the specified bounds, scaling up or down as needed. ImRect CenterImage(const ImVec2& fit_size, const ImVec2& image_size); @@ -223,6 +221,18 @@ namespace ImGuiFullscreen std::string title, std::string message, std::string caption, std::string ok_button_text, InputStringDialogCallback callback); void CloseInputDialog(); + using ConfirmMessageDialogCallback = std::function; + using InfoMessageDialogCallback = std::function; + using MessageDialogCallback = std::function; + bool IsMessageBoxDialogOpen(); + void OpenConfirmMessageDialog(std::string title, std::string message, ConfirmMessageDialogCallback callback, + std::string yes_button_text = ICON_FA_CHECK " Yes", std::string no_button_text = ICON_FA_TIMES " No"); + void OpenInfoMessageDialog(std::string title, std::string message, InfoMessageDialogCallback callback, + std::string button_text = ICON_FA_WINDOW_CLOSE " Close"); + void OpenMessageDialog(std::string title, std::string message, MessageDialogCallback callback, std::string first_button_text, + std::string second_button_text, std::string third_button_text); + void CloseMessageDialog(); + float GetNotificationVerticalPosition(); float GetNotificationVerticalDirection(); void SetNotificationVerticalPosition(float position, float direction); diff --git a/pcsx2/Frontend/LogSink.cpp b/pcsx2/Frontend/LogSink.cpp index e4bf81dde0..9450f7fbe0 100644 --- a/pcsx2/Frontend/LogSink.cpp +++ b/pcsx2/Frontend/LogSink.cpp @@ -23,6 +23,7 @@ #include "common/Console.h" #include "common/FileSystem.h" #include "common/Path.h" +#include "common/SettingsInterface.h" #include "common/StringUtil.h" #include "common/Timer.h" @@ -386,3 +387,15 @@ void Host::UpdateLogging(bool disable_system_console) UpdateLoggingSinks(system_console_enabled, file_logging_enabled); } + +void Host::SetDefaultLoggingSettings(SettingsInterface& si) +{ + si.SetBoolValue("Logging", "EnableSystemConsole", false); + si.SetBoolValue("Logging", "EnableFileLogging", false); + si.SetBoolValue("Logging", "EnableTimestamps", true); + si.SetBoolValue("Logging", "EnableVerbose", false); + si.SetBoolValue("Logging", "EnableEEConsole", false); + si.SetBoolValue("Logging", "EnableIOPConsole", false); + si.SetBoolValue("Logging", "EnableInputRecordingLogs", true); + si.SetBoolValue("Logging", "EnableControllerLogs", false); +} diff --git a/pcsx2/Frontend/LogSink.h b/pcsx2/Frontend/LogSink.h index 9f82d282c4..8e032a43eb 100644 --- a/pcsx2/Frontend/LogSink.h +++ b/pcsx2/Frontend/LogSink.h @@ -15,6 +15,8 @@ #pragma once +class SettingsInterface; + namespace Host { /// Updates the Console handler based on the current configuration. @@ -22,4 +24,7 @@ namespace Host /// Initializes early console logging (for printing command line arguments). void InitializeEarlyConsole(); + + /// Stores default logging settings to the specified file. + void SetDefaultLoggingSettings(SettingsInterface& si); } diff --git a/pcsx2/Host.h b/pcsx2/Host.h index 997105db87..ab2660c749 100644 --- a/pcsx2/Host.h +++ b/pcsx2/Host.h @@ -17,6 +17,7 @@ #include "common/Pcsx2Defs.h" +#include #include #include #include @@ -73,4 +74,32 @@ namespace Host /// Copies the provided text to the host's clipboard, if present. bool CopyTextToClipboard(const std::string_view& text); + + /// Requests settings reset. Can be called from any thread, will call back and apply on the CPU thread. + bool RequestResetSettings(bool folders, bool core, bool controllers, bool hotkeys, bool ui); + + /// Requests a specific display window size. + void RequestResizeHostDisplay(s32 width, s32 height); + + /// Safely executes a function on the VM thread. + void RunOnCPUThread(std::function function, bool block = false); + + /// Asynchronously starts refreshing the game list. + void RefreshGameListAsync(bool invalidate_cache); + + /// Cancels game list refresh, if there is one in progress. + void CancelGameListRefresh(); + + /// Requests shut down and exit of the hosting application. This may not actually exit, + /// if the user cancels the shutdown confirmation. + void RequestExit(bool save_state_if_running); + + /// Requests shut down of the current virtual machine. + void RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool default_save_state); + + /// Returns true if the hosting application is currently fullscreen. + bool IsFullscreen(); + + /// Alters fullscreen state of hosting application. + void SetFullscreen(bool enabled); } // namespace Host diff --git a/pcsx2/HostSettings.h b/pcsx2/HostSettings.h index e23f6b938f..d8d57813db 100644 --- a/pcsx2/HostSettings.h +++ b/pcsx2/HostSettings.h @@ -40,7 +40,9 @@ namespace Host void SetBaseFloatSettingValue(const char* section, const char* key, float value); void SetBaseStringSettingValue(const char* section, const char* key, const char* value); void SetBaseStringListSettingValue(const char* section, const char* key, const std::vector& values); - void DeleteBaseSettingValue(const char* section, const char* key); + bool AddBaseValueToStringList(const char* section, const char* key, const char* value); + bool RemoveBaseValueFromStringList(const char* section, const char* key, const char* value); + void RemoveBaseSettingValue(const char* section, const char* key); void CommitBaseSettingChanges(); // Settings access, thread-safe. diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index dfa115619c..2011b25c6f 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -1231,21 +1231,19 @@ void Pcsx2Config::CopyConfig(const Pcsx2Config& cfg) LimiterMode = cfg.LimiterMode; } -void EmuFolders::SetDefaults() +void EmuFolders::SetDefaults(SettingsInterface& si) { - Bios = Path::Combine(DataRoot, "bios"); - Snapshots = Path::Combine(DataRoot, "snaps"); - Savestates = Path::Combine(DataRoot, "sstates"); - MemoryCards = Path::Combine(DataRoot, "memcards"); - Logs = Path::Combine(DataRoot, "logs"); - Cheats = Path::Combine(DataRoot, "cheats"); - CheatsWS = Path::Combine(DataRoot, "cheats_ws"); - CheatsNI = Path::Combine(DataRoot, "cheats_ni"); - Covers = Path::Combine(DataRoot, "covers"); - GameSettings = Path::Combine(DataRoot, "gamesettings"); - Cache = Path::Combine(DataRoot, "cache"); - Textures = Path::Combine(DataRoot, "textures"); - InputProfiles = Path::Combine(DataRoot, "inputprofiles"); + si.SetStringValue("Folders", "Bios", "bios"); + si.SetStringValue("Folders", "Snapshots", "snaps"); + si.SetStringValue("Folders", "Savestates", "sstates"); + si.SetStringValue("Folders", "MemoryCards", "memcards"); + si.SetStringValue("Folders", "Logs", "logs"); + si.SetStringValue("Folders", "Cheats", "cheats"); + si.SetStringValue("Folders", "CheatsWS", "cheats_ws"); + si.SetStringValue("Folders", "CheatsNI", "cheats_ni"); + si.SetStringValue("Folders", "Cache", "cache"); + si.SetStringValue("Folders", "Textures", "textures"); + si.SetStringValue("Folders", "InputProfiles", "inputprofiles"); } static std::string LoadPathFromSettings(SettingsInterface& si, const std::string& root, const char* name, const char* def) @@ -1287,22 +1285,6 @@ void EmuFolders::LoadConfig(SettingsInterface& si) Console.WriteLn("Input Profile Directory: %s", InputProfiles.c_str()); } -void EmuFolders::Save(SettingsInterface& si) -{ - // convert back to relative - si.SetStringValue("Folders", "Bios", Path::MakeRelative(Bios, DataRoot).c_str()); - si.SetStringValue("Folders", "Snapshots", Path::MakeRelative(Snapshots, DataRoot).c_str()); - si.SetStringValue("Folders", "Savestates", Path::MakeRelative(Savestates, DataRoot).c_str()); - si.SetStringValue("Folders", "MemoryCards", Path::MakeRelative(MemoryCards, DataRoot).c_str()); - si.SetStringValue("Folders", "Logs", Path::MakeRelative(Logs, DataRoot).c_str()); - si.SetStringValue("Folders", "Cheats", Path::MakeRelative(Cheats, DataRoot).c_str()); - si.SetStringValue("Folders", "CheatsWS", Path::MakeRelative(CheatsWS, DataRoot).c_str()); - si.SetStringValue("Folders", "CheatsNI", Path::MakeRelative(CheatsNI, DataRoot).c_str()); - si.SetStringValue("Folders", "Cache", Path::MakeRelative(Cache, DataRoot).c_str()); - si.SetStringValue("Folders", "Textures", Path::MakeRelative(Textures, DataRoot).c_str()); - si.SetStringValue("Folders", "InputProfiles", Path::MakeRelative(InputProfiles, DataRoot).c_str()); -} - bool EmuFolders::EnsureFoldersExist() { bool result = FileSystem::CreateDirectoryPath(Bios.c_str(), false); diff --git a/pcsx2/VMManager.h b/pcsx2/VMManager.h index c6844cddba..8c76be72f1 100644 --- a/pcsx2/VMManager.h +++ b/pcsx2/VMManager.h @@ -234,29 +234,4 @@ namespace Host /// Provided by the host; called when a state is saved, and the frontend should invalidate its save state cache. void InvalidateSaveStateCache(); - - /// Requests a specific display window size. - void RequestResizeHostDisplay(s32 width, s32 height); - - /// Safely executes a function on the VM thread. - void RunOnCPUThread(std::function function, bool block = false); - - /// Asynchronously starts refreshing the game list. - void RefreshGameListAsync(bool invalidate_cache); - - /// Cancels game list refresh, if there is one in progress. - void CancelGameListRefresh(); - - /// Requests shut down and exit of the hosting application. This may not actually exit, - /// if the user cancels the shutdown confirmation. - void RequestExit(bool save_state_if_running); - - /// Requests shut down of the current virtual machine. - void RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool default_save_state); - - /// Returns true if the hosting application is currently fullscreen. - bool IsFullscreen(); - - /// Alters fullscreen state of hosting application. - void SetFullscreen(bool enabled); } diff --git a/pcsx2/pcsx2core.vcxproj b/pcsx2/pcsx2core.vcxproj index be8a425101..feb16f3393 100644 --- a/pcsx2/pcsx2core.vcxproj +++ b/pcsx2/pcsx2core.vcxproj @@ -186,6 +186,7 @@ + @@ -218,7 +219,7 @@ - + @@ -508,6 +509,7 @@ + diff --git a/pcsx2/pcsx2core.vcxproj.filters b/pcsx2/pcsx2core.vcxproj.filters index bdbbb39d64..f8bcffceb4 100644 --- a/pcsx2/pcsx2core.vcxproj.filters +++ b/pcsx2/pcsx2core.vcxproj.filters @@ -1205,7 +1205,7 @@ System\Ps2\USB - + Host @@ -1281,6 +1281,9 @@ Host + + Host + @@ -2131,6 +2134,9 @@ Host + + Host +