From 0ed9c8542b9d3ea353fdc1aba1fc7bf15b79f1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Oct 2026 17:23:10 +0100 Subject: [PATCH] Settings: Move the System tab's builder to UI/Settings/SystemPage.cpp Like the other tabs, except that the screen keeps what the page edits that isn't a config setting (the compatibility reports flag, which is written back when the screen closes, and the Windows memstick checkboxes' state) in a SystemPageState it passes in. The page's handlers move with it, as functions taking the page host or that state. Co-Authored-By: Claude Opus 5.5 (1M context) --- UI/CMakeLists.txt | 1 + UI/GameSettingsScreen.cpp | 500 +-------------------------- UI/GameSettingsScreen.h | 31 +- UI/Settings/MainSettingsPages.h | 22 +- UI/Settings/SystemPage.cpp | 551 ++++++++++++++++++++++++++++++ UI/UI.vcxproj | 1 + UI/UI.vcxproj.filters | 3 + UWP/UI_UWP/UI_UWP.vcxproj | 1 + UWP/UI_UWP/UI_UWP.vcxproj.filters | 3 + android/jni/Android.mk | 1 + 10 files changed, 590 insertions(+), 524 deletions(-) create mode 100644 UI/Settings/SystemPage.cpp diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 9663b55621..8bdcee94b7 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -68,6 +68,7 @@ list(APPEND UISource Settings/ControlsPage.cpp Settings/AudioPage.cpp Settings/NetworkingPage.cpp + Settings/SystemPage.cpp Settings/ToolsPage.cpp Settings/VRPage.cpp SettingsPagesDialogScreen.h diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 850aa887ac..bc940ef875 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -123,17 +123,11 @@ GameSettingsScreen::GameSettingsScreen(const Path &gamePath, std::string gameID, g_Config.LoadGameConfig(gameID_); } - ReadConfigTemporaries(); -} - -// Also called after restoring defaults, so the old values aren't written back over them. -void GameSettingsScreen::ReadConfigTemporaries() { - // Written back on exit, so it must be read even if the System tab is never opened. - enableReports_ = Reporting::IsEnabled(); + ReadSystemPageState(&systemState_); } GameSettingsScreen::~GameSettingsScreen() { - Reporting::Enable(enableReports_, "report.ppsspp.org"); + Reporting::Enable(systemState_.enableReports, "report.ppsspp.org"); Reporting::UpdateConfig(); if (!g_Config.Save("GameSettingsScreen::onFinish")) { std::string message = "Failed to save settings!\n"; @@ -170,55 +164,10 @@ void GameSettingsScreen::PreCreateViews() { ReloadAllPostShaderInfo(screenManager()->getDrawContext()); ReloadAllThemeInfo(); #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) - CheckMemstickInstallState(); + CheckMemstickInstallState(&systemState_); #endif } -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) -// Works out where the memstick is (installed.txt next to the exe means My Documents or a custom -// folder), and which of the two checkboxes can be changed. -void GameSettingsScreen::CheckMemstickInstallState() { - const Path &PPSSPPpath = File::GetExeDirectory(); - const Path installedFile = PPSSPPpath / "installed.txt"; - const bool myDocsExists = W32Util::UserDocumentsPath().size() != 0; - - installed_ = File::Exists(installedFile); - otherinstalled_ = false; - memstickMyDocEnabled_ = !PSP_IsInited(); - memstickOtherEnabled_ = false; - if (!installed_ && myDocsExists) { - // Disable the setting if we can't create & delete a file next to the exe. - if (File::CreateEmptyFile(PPSSPPpath / "installedTEMP.txt")) { - if (!(File::Delete(PPSSPPpath / "installedTEMP.txt"))) - memstickMyDocEnabled_ = false; - else - memstickOtherEnabled_ = !PSP_IsInited(); - } else - memstickMyDocEnabled_ = false; - } else { - if (installed_ && myDocsExists) { - FILE *testInstalled = File::OpenCFile(installedFile, "rt"); - if (testInstalled) { - char temp[2048]; - char *tempStr = fgets(temp, sizeof(temp), testInstalled); - // Skip UTF-8 encoding bytes if there are any. There are 3 of them. - if (tempStr && strncmp(tempStr, "\xEF\xBB\xBF", 3) == 0) { - tempStr += 3; - } - memstickOtherEnabled_ = !PSP_IsInited(); - if (tempStr && strlen(tempStr) != 0 && strcmp(tempStr, "\n") != 0) { - installed_ = false; - otherinstalled_ = true; - } - fclose(testInstalled); - } - } else if (!myDocsExists) { - memstickMyDocEnabled_ = false; - } - } -} -#endif - const std::vector &GameSettingsScreen::PageDefs() { static const std::vector pages = { { "GameSettingsGraphics", "Graphics", "I_DISPLAY", "graphics", I18NCat::GRAPHICS, &BuildGraphicsSettings }, @@ -246,429 +195,13 @@ void GameSettingsScreen::CreateTabs() { std::function build = def.build; if (!build) { build = [this](SettingsPage *page) { - BuildSystemSettings(page); + BuildSystemSettings(page, &systemState_); }; } AddSettingsTab(def.tag, ms->T(def.title), def.icon ? ImageID(def.icon) : ImageID::invalid(), def.cat, build, def.helpTag); } } -void GameSettingsScreen::BuildSystemSettings(SettingsPage *page) { - using namespace UI; - - page->Header("UI"); - - page->Custom("Language", [this](UI::ViewGroup *parent) -> UI::View * { - auto langCodeToName = [](std::string_view value) -> std::string { - auto &mapping = GetLangValuesMapping(); - auto iter = mapping.find(value); - if (iter != mapping.end()) { - return iter->second.first; - } - return std::string(value); - }; - - Choice *language = parent->Add(new ChoiceWithValueDisplay(&g_Config.sLanguageIni, T(I18NCat::SYSTEM, "Language"), langCodeToName)); - language->OnClick.Add([this](UI::EventParams &e) { - auto sy = GetI18NCategory(I18NCat::SYSTEM); - auto langScreen = new NewLanguageScreen(sy->T("Language")); - // The actual switching is handled in OnCompleted in NewLanguageScreen. - if (e.v) - langScreen->SetPopupOrigin(e.v); - screenManager()->push(langScreen); - }); - return language; - }); - -#if PPSSPP_PLATFORM(IOS) - static const char *indicator[] = { - "Swipe once to switch app (indicator auto-hides)", - "Swipe twice to switch app (indicator stays visible)" - }; - page->Setting(&g_Config.iAppSwitchMode); - - { - // Note: On iPhone, iOS hides the status bar in landscape no matter what this is set to. - DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation()); - page->Setting(&config.bImmersiveMode).OnChange([](EventParams &e) { - System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE); - }); - } -#endif - -#if PPSSPP_PLATFORM(ANDROID) - // Hide Immersive Mode on pre-kitkat Android - if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19) { - DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation()); - page->Setting(&config.bImmersiveMode).OnChange([this](EventParams &e) { - OnImmersiveModeChange(e); - }); - } -#endif - - UIContext *ctx = screenManager()->getUIContext(); - page->Setting(&g_Config.iUIScaleFactor).OnChange([ctx](UI::EventParams &e) { - const float dpiMul = UIScaleFactorToMultiplier(g_Config.iUIScaleFactor); - g_display.Recalculate(-1, -1, -1, -1, dpiMul); - ctx->InvalidateAtlas(); - NativeResized(); - }); - - const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png"; - const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg"; - if (File::Exists(bgPng) || File::Exists(bgJpg)) { - page->Choice("Clear UI background").OnClick([this](UI::EventParams &e) { - OnChangeBackground(e); - }); - } else if (System_GetPropertyBool(SYSPROP_HAS_IMAGE_BROWSER) || System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { - page->Choice("Set UI background...").OnClick([this](UI::EventParams &e) { - OnChangeBackground(e); - }); - } - - page->Setting(&g_Config.bTransparentBackground); - - // Shared with achievements. - - page->Setting(&g_Config.iNotificationPos); - - page->Setting(&g_Config.iBackgroundAnimation); - - page->Custom("Theme", [this](UI::ViewGroup *parent) -> UI::View * { - PopupMultiChoiceDynamic *theme = parent->Add(new PopupMultiChoiceDynamic(&g_Config.sThemeName, T(I18NCat::SYSTEM, "Theme"), GetThemeInfoNames(), I18NCat::THEMES, screenManager())); - theme->OnChoice.Add([](EventParams &e) { - UpdateTheme(); - // Reset the tint/saturation if the theme changed. - if (e.b) { - g_Config.fUITint = 0.0f; - g_Config.fUISaturation = 1.0f; - } - }); - return theme; - }); - - Draw::DrawContext *draw = screenManager()->getDrawContext(); - - if (!draw->GetBugs().Has(Draw::Bugs::RASPBERRY_SHADER_COMP_HANG)) { - // We use shaders without tint capability on hardware with this driver bug. - auto noDropShadow = [](View *view) { - static_cast(view)->SetHasDropShadow(false); - }; - page->Setting(&g_Config.fUITint).LiveUpdate().Setup(noDropShadow); - page->Setting(&g_Config.fUISaturation).LiveUpdate().Setup(noDropShadow); - } - - page->Header("PSP Memory Stick"); - - if (System_GetPropertyBool(SYSPROP_HAS_OPEN_DIRECTORY)) { - page->Choice("Show Memory Stick folder").OnClick([](UI::EventParams &p) { - System_LaunchUrl(LaunchUrlType::LOCAL_FOLDER, g_Config.memStickDirectory.ToString()); - }); - } - -#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) - bool showItHere = true; -#if PPSSPP_PLATFORM(IOS_APP_STORE) - if (g_Config.memStickDirectory == DarwinFileSystemServices::defaultMemoryStickPath()) { - // We still keep a way to access it on the developer tools screen. - showItHere = false; - } -#endif - if (showItHere) { - page->Choice("Set Memory Stick folder").OnClick([this](UI::EventParams &) { - SetMemStickDirDarwin(GetRequesterToken()); - }); - } -#endif - - auto addMemstickPath = [this, &page]() { - page->Custom("Memory Stick folder", [this](UI::ViewGroup *parent) -> UI::View * { - Choice *memstickPath = parent->Add(new ChoiceWithCallbackValueDisplay(T(I18NCat::SYSTEM, "Memory Stick folder"), []() { - return g_Config.memStickDirectory.ToVisualString(); - })); - memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnShowMemstickScreen); - return memstickPath; - }).EnabledFunc([] { - return !PSP_IsInited(); - }); - }; - -#if PPSSPP_PLATFORM(ANDROID) - if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_VR) { - addMemstickPath(); - } -#elif defined(_WIN32) -#if PPSSPP_PLATFORM(UWP) - addMemstickPath(); -#else - page->Bool(&installed_, "Memory Stick in My Documents").Enabled(&memstickMyDocEnabled_).OnChange([this](UI::EventParams &e) { - OnMemoryStickMyDoc(e); - }); - page->Bool(&otherinstalled_, "Memory Stick in installed.txt").Enabled(&memstickOtherEnabled_).OnChange([this](UI::EventParams &e) { - OnMemoryStickOther(e); - }); - - if (!System_GetPropertyBool(SYSPROP_CAN_GET_FREE_SPACE_FAST)) { - // It might be slow, so use an option. - page->Setting(&g_Config.bReportAccurateFreeStorageSpace); - } -#endif -#endif - page->Setting(&g_Config.bMemStickInserted); - page->Setting(&g_Config.iMemStickSizeGB); - - page->Header("Help the PPSSPP team"); - page->Bool(&enableReports_, "Enable Compatibility Server Reports").EnabledFunc([]() { - return Reporting::IsSupported(); - }); - - page->Header("Emulation"); - - page->Setting(&g_Config.bFastMemory).OnChange([this](UI::EventParams &e) { - OnJitAffectingSetting(e); - }); - page->Setting(&g_Config.bIgnoreBadMemAccess); - - page->Setting(&g_Config.iIOTimingMethod); - page->Setting(&g_Config.bForceLagSync).Disabled(&g_Config.bAutoFrameSkip); - page->Setting(&g_Config.iLockedCPUSpeed); - - page->Header({ I18NCat::SAVEDATA, "Save states" }); // Borrow this string from the savedata manager - - page->Setting(&g_Config.bEnableStateUndo); - page->Setting(&g_Config.bConfirmLoadState); - - page->Setting(&g_Config.iSaveStateSlotCount); - - // NOTE: We will soon support more states, but we'll keep this niche feature limited to the first five. - page->Setting(&g_Config.iAutoLoadSaveState).Setup([](View *view) { - if (g_Config.iAutoLoadSaveState != 1) { - static_cast(view)->HideChoice(1); // Hide "Oldest Save" if not using that mode. It doesn't make sense. - } - }); - - page->Setting(&g_Config.iRewindSnapshotInterval); - - page->Header("General"); - - page->Setting(&g_Config.iAskForExitConfirmationAfterSeconds); - - if (System_GetPropertyBool(SYSPROP_CAN_RESTRICT_ORIENTATION)) { - // Display rotation - page->Custom({ I18NCat::CONTROLS, "Screen Rotation" }, [this](UI::ViewGroup *parent) -> UI::View * { - AddRotationPicker(screenManager(), parent, true); - return parent->GetViewByIndex(parent->GetNumSubviews() - 1); - }); - } - - if (System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE)) { - page->Setting(&g_Config.bSustainedPerformanceMode).OnChange([this](UI::EventParams &e) { - OnSustainedPerformanceModeChange(e); - }); - } - - page->Choice("Restore Default Settings").OnClick([this](UI::EventParams &e) { - OnRestoreDefaultSettings(e); - }); - - if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) - page->Setting(&g_Config.bBypassOSKWithKeyboard); - - if (System_GetPropertyBool(SYSPROP_ENOUGH_RAM_FOR_FULL_ISO)) { - page->Setting(&g_Config.bCacheFullIsoInRam).EnabledFunc([] { - return !PSP_IsInited(); - }); - } - - page->Setting(&g_Config.bCheckForNewVersion); - page->Setting(&g_Config.bScreenshotsAsPNG); - page->Setting(&g_Config.iScreenshotMode); - // TODO: Make this setting available on Mac too. -#if PPSSPP_PLATFORM(WINDOWS) || (defined (SDL) && !defined(MOBILE_DEVICE)) - page->Setting(&g_Config.bPauseOnLostFocus); -#endif - - std::vector cameraList = Camera::getDeviceList(); - if (cameraList.size() >= 1) { - page->Header({ I18NCat::GRAPHICS, "Camera" }); - page->Setting(&g_Config.sCameraDevice, std::move(cameraList)); -#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) - page->Setting(&g_Config.bCameraMirrorHorizontal); -#endif - } - - page->Header("Cheats"); - page->Setting(&g_Config.bEnableCheats); - page->Setting(&g_Config.bEnablePlugins); - - page->Header("PSP Settings"); - - // The ordering here is simply mapping directly to PSP_SYSTEMPARAM_LANGUAGE_*. - page->Setting(&g_Config.iLanguage); - page->Setting(&g_Config.iPSPModel).EnabledFunc([] { - return !PSP_IsInited(); - }); - page->Custom("Nickname", [this](UI::ViewGroup *parent) { - return AddNicknameChoice(this, parent); - }); - page->Setting(&g_Config.bDayLightSavings); - page->Setting(&g_Config.iDateFormat); - page->Setting(&g_Config.iTimeFormat); - page->Setting(&g_Config.iButtonPreference); - -#if defined(_WIN32) - page->Header("Recording"); - page->Setting(&g_Config.bDumpFrames); - page->Setting(&g_Config.bUseFFV1); - page->Setting(&g_Config.bDumpVideoOutput); - page->Setting(&g_Config.bDumpAudio); - page->Setting(&g_Config.bSaveLoadResetsAVdumping); -#endif -} - -void GameSettingsScreen::OnImmersiveModeChange(UI::EventParams &e) { - System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE); - if (g_Config.iAndroidHwScale != 0) { - System_RecreateActivity(); - } -} - -void GameSettingsScreen::OnSustainedPerformanceModeChange(UI::EventParams &e) { - System_Notify(SystemNotification::SUSTAINED_PERF_CHANGE); -} - -void GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) { - System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT); -} - -void GameSettingsScreen::OnShowMemstickScreen(UI::EventParams &e) { - screenManager()->push(new MemStickScreen(false)); -} - -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) - -void GameSettingsScreen::OnMemoryStickMyDoc(UI::EventParams &e) { - const Path &PPSSPPpath = File::GetExeDirectory(); - const Path installedFile = PPSSPPpath / "installed.txt"; - installed_ = File::Exists(installedFile); - if (otherinstalled_) { - File::Delete(PPSSPPpath / "installed.txt"); - File::CreateEmptyFile(PPSSPPpath / "installed.txt"); - otherinstalled_ = false; - const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/"; - g_Config.memStickDirectory = Path(myDocsPath); - } else if (installed_) { - File::Delete(PPSSPPpath / "installed.txt"); - installed_ = false; - g_Config.memStickDirectory = PPSSPPpath / "memstick"; - } else { - FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb"); - if (f) { - fclose(f); - } - - const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/"; - g_Config.memStickDirectory = Path(myDocsPath); - installed_ = true; - } -} - -void GameSettingsScreen::OnMemoryStickOther(UI::EventParams &e) { - const Path &PPSSPPpath = File::GetExeDirectory(); - if (otherinstalled_) { - auto di = GetI18NCategory(I18NCat::DIALOG); - std::string initialPath = g_Config.memStickDirectory.ToCString(); - std::string folder = W32Util::BrowseForFolder2(MainWindow::GetHWND(), di->T("Choose PPSSPP save folder"), initialPath); - if (folder.size()) { - g_Config.memStickDirectory = Path(folder); - FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb"); - if (f) { - std::string utfstring("\xEF\xBB\xBF"); - utfstring.append(folder); - fwrite(utfstring.c_str(), 1, utfstring.length(), f); - fclose(f); - } - installed_ = false; - } - else - otherinstalled_ = false; - } - else { - File::Delete(PPSSPPpath / "installed.txt"); - memstickMyDocEnabled_ = true; - otherinstalled_ = false; - installed_ = false; - g_Config.memStickDirectory = PPSSPPpath / "memstick"; - } -} - -#endif - -void GameSettingsScreen::OnChangeBackground(UI::EventParams &e) { - const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png"; - const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg"; - - if (File::Exists(bgPng) || File::Exists(bgJpg)) { - INFO_LOG(Log::UI, "Clearing background image."); - // The button is in clear mode. - File::Delete(bgPng); - File::Delete(bgJpg); - UIBackgroundShutdown(); - RecreateViews(); - return; - } - - auto sy = GetI18NCategory(I18NCat::SYSTEM); - System_BrowseForImage(GetRequesterToken(), sy->T("Set UI background..."), bgJpg, [this](std::string_view value, int converted) { - if (converted == 1) { - // The platform code converted and saved the file to the desired path already. - INFO_LOG(Log::UI, "Platform converted the file: %.*s", STR_VIEW(value)); - } else if (!value.empty()) { - Path path(value); - - // Check the file format. Don't rely on the file extension here due to scoped storage URLs. - FILE *f = File::OpenCFile(path, "rb"); - uint8_t buffer[8]; - ImageFileType type = ImageFileType::UNKNOWN; - if (f != nullptr && 8 == fread(buffer, 1, ARRAY_SIZE(buffer), f)) { - type = DetectImageFileType(buffer, ARRAY_SIZE(buffer)); - } - if (f) { - fclose(f); - } - - std::string filename; - switch (type) { - case ImageFileType::JPEG: - filename = "background.jpg"; - break; - case ImageFileType::PNG: - filename = "background.png"; - break; - default: - break; - } - - if (!filename.empty()) { - Path dest = GetSysDirectory(DIRECTORY_SYSTEM) / filename; - File::Copy(path, dest); - if (path.FilePathContainsNoCase("temp_import.jpg")) { - INFO_LOG(Log::UI, "Deleting temp file: %s", GetFriendlyPath(path).c_str()); - File::Delete(path); - } - } else { - auto sy = GetI18NCategory(I18NCat::SYSTEM); - g_OSD.Show(OSDType::MESSAGE_ERROR, sy->T("Only JPG and PNG images are supported"), path.GetFilename(), 5.0); - } - } - // It will init again automatically. We can't init outside a frame on Vulkan. - UIBackgroundShutdown(); - RecreateViews(); - }); - - // Change to a browse or clear button. -} - void GameSettingsScreen::dialogFinished(const Screen *dialog, DialogResult result) { if (equals(dialog->tag(), "NewLanguage") && result == DR_OK) { screenManager()->RecreateAllViews(); @@ -686,7 +219,7 @@ void GameSettingsScreen::dialogFinished(const Screen *dialog, DialogResult resul if (result == DialogResult::DR_OK) { if (equals(dialog->tag(), "RestoreSettingsScreen")) { - ReadConfigTemporaries(); + ReadSystemPageState(&systemState_); } recreate = true; } @@ -738,29 +271,6 @@ void GameSettingsScreen::RestartOrDo(std::function callback) { })); } -void GameSettingsScreen::CallbackRestoreDefaults(bool yes) { - if (yes) { - g_Config.RestoreDefaults(RestoreSettingsBits::SETTINGS); - ReadConfigTemporaries(); - } - System_Notify(SystemNotification::UI); -} - -void GameSettingsScreen::OnRestoreDefaultSettings(UI::EventParams &e) { - auto sy = GetI18NCategory(I18NCat::SYSTEM); - if (g_Config.IsGameSpecific()) { - auto dev = GetI18NCategory(I18NCat::DEVELOPER); - auto di = GetI18NCategory(I18NCat::DIALOG); - auto sy = GetI18NCategory(I18NCat::SYSTEM); - screenManager()->push( - new UI::MessagePopupScreen(sy->T("Restore Default Settings"), dev->T("RestoreGameDefaultSettings", "Are you sure you want to restore the game-specific settings back to the ppsspp defaults?\n"), - di->T("OK"), di->T("Cancel"), [this](bool yes) { CallbackRestoreDefaults(yes); })); - } else { - std::string_view title = sy->T("Restore Default Settings"); - screenManager()->push(new RestoreSettingsScreen(title)); - } -} - void GestureMappingScreen::CreateTabs() { auto di = GetI18NCategory(I18NCat::DIALOG); AddTab("Gesture", di->T("Left side"), [this](UI::LinearLayout *parent) { CreateGestureTab(parent, 0, GetDeviceOrientation() == DeviceOrientation::Portrait); }); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 156ec93e47..47fd159291 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -27,6 +27,7 @@ #include "Common/UI/PopupScreens.h" #include "Core/ConfigValues.h" #include "UI/BaseScreens.h" +#include "UI/Settings/MainSettingsPages.h" #include "UI/SettingsPagesDialogScreen.h" #include "UI/TabbedDialogScreen.h" @@ -46,7 +47,6 @@ public: void OpenDeveloperTools(); protected: - void CallbackRestoreDefaults(bool yes); void dialogFinished(const Screen *dialog, DialogResult result) override; void CreateTabs() override; @@ -61,40 +61,15 @@ private: const char *icon; const char *helpTag; // If null, there's no help button. I18NCat cat; - void (*build)(SettingsPage *page); // Null for the System tab, which the screen builds. + void (*build)(SettingsPage *page); // Null for the System tab, which also takes systemState_. }; static const std::vector &PageDefs(); - void BuildSystemSettings(SettingsPage *page); - - std::string gameID_; -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) - void CheckMemstickInstallState(); - - bool installed_ = false; - bool otherinstalled_ = false; - bool memstickMyDocEnabled_ = false; - bool memstickOtherEnabled_ = false; -#endif - - // Global settings handlers - void OnChangeBackground(UI::EventParams &e); - void OnRestoreDefaultSettings(UI::EventParams &e); - void OnJitAffectingSetting(UI::EventParams &e); - void OnShowMemstickScreen(UI::EventParams &e); -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) - void OnMemoryStickMyDoc(UI::EventParams &e); - void OnMemoryStickOther(UI::EventParams &e); -#endif - void OnImmersiveModeChange(UI::EventParams &e); - void OnSustainedPerformanceModeChange(UI::EventParams &e); void RestartOrDo(std::function callback) override; - void ReadConfigTemporaries(); - // Temporaries to convert setting types, cache enabled, etc. - bool enableReports_ = false; + SystemPageState systemState_; // edit the game-specific settings and restore the global settings after exiting bool editGameSpecificThenRestore_ = false; diff --git a/UI/Settings/MainSettingsPages.h b/UI/Settings/MainSettingsPages.h index d9b16de9ca..6e16218d31 100644 --- a/UI/Settings/MainSettingsPages.h +++ b/UI/Settings/MainSettingsPages.h @@ -17,6 +17,8 @@ #pragma once +#include "ppsspp_config.h" + namespace UI { class View; class ViewGroup; @@ -25,7 +27,7 @@ class ViewGroup; class SettingsPage; class SettingsPageHost; -// The settings screen's tabs, except System, which needs more of the screen. +// The settings screen's tabs. void BuildGraphicsSettings(SettingsPage *page); void BuildControlsSettings(SettingsPage *page); void BuildAudioSettings(SettingsPage *page); @@ -33,5 +35,23 @@ void BuildNetworkingSettings(SettingsPage *page); void BuildToolsSettings(SettingsPage *page); void BuildVRSettings(SettingsPage *page); +// What the System tab edits that isn't a config setting. The settings screen keeps it, and writes +// enableReports back when it closes. +struct SystemPageState { + bool enableReports = false; + // Windows: where the memstick is, and which of the two checkboxes can be changed. + bool installed = false; + bool otherinstalled = false; + bool memstickMyDocEnabled = false; + bool memstickOtherEnabled = false; +}; + +void BuildSystemSettings(SettingsPage *page, SystemPageState *state); +// Also called after restoring defaults, so the old values aren't written back over them. +void ReadSystemPageState(SystemPageState *state); +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +void CheckMemstickInstallState(SystemPageState *state); +#endif + // The nickname is shown on both the Networking and System tabs. UI::View *AddNicknameChoice(SettingsPageHost *host, UI::ViewGroup *parent); diff --git a/UI/Settings/SystemPage.cpp b/UI/Settings/SystemPage.cpp new file mode 100644 index 0000000000..163a5f4730 --- /dev/null +++ b/UI/Settings/SystemPage.cpp @@ -0,0 +1,551 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" + +#include +#include +#include +#include + +#include "Common/Common.h" +#include "Common/Data/Text/I18n.h" +#include "Common/File/FileUtil.h" +#include "Common/File/Path.h" +#include "Common/GPU/thin3d.h" +#include "Common/Log.h" +#include "Common/Render/ManagedTexture.h" +#include "Common/System/Display.h" +#include "Common/System/NativeApp.h" +#include "Common/System/OSD.h" +#include "Common/System/Request.h" +#include "Common/System/System.h" +#include "Common/UI/Context.h" +#include "Common/UI/PopupScreens.h" +#include "Common/UI/ScreenManager.h" +#include "Common/UI/View.h" +#include "Core/Config.h" +#include "Core/ConfigValues.h" +#include "Core/HLE/sceUsbCam.h" +#include "Core/HLE/sceUtility.h" +#include "Core/Reporting.h" +#include "Core/System.h" +#include "Core/Util/PathUtil.h" +#include "UI/Background.h" +#include "UI/GameSettingsScreen.h" +#include "UI/MemStickScreen.h" +#include "UI/MiscScreens.h" +#include "UI/MiscViews.h" +#include "UI/SettingsPage.h" +#include "UI/Settings/MainSettingsPages.h" +#include "UI/Theme.h" + +#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) +#include "Core/Util/DarwinFileSystemServices.h" +#endif + +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +#pragma warning(disable:4091) // workaround bug in VS2015 headers +#include "Windows/MainWindow.h" +#include "Windows/W32Util/ShellUtil.h" +#endif + +void ReadSystemPageState(SystemPageState *state) { + // Written back on exit, so it must be read even if the System tab is never opened. + state->enableReports = Reporting::IsEnabled(); +} + +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +// Works out where the memstick is (installed.txt next to the exe means My Documents or a custom +// folder), and which of the two checkboxes can be changed. +void CheckMemstickInstallState(SystemPageState *state) { + const Path &PPSSPPpath = File::GetExeDirectory(); + const Path installedFile = PPSSPPpath / "installed.txt"; + const bool myDocsExists = W32Util::UserDocumentsPath().size() != 0; + + state->installed = File::Exists(installedFile); + state->otherinstalled = false; + state->memstickMyDocEnabled = !PSP_IsInited(); + state->memstickOtherEnabled = false; + if (!state->installed && myDocsExists) { + // Disable the setting if we can't create & delete a file next to the exe. + if (File::CreateEmptyFile(PPSSPPpath / "installedTEMP.txt")) { + if (!(File::Delete(PPSSPPpath / "installedTEMP.txt"))) + state->memstickMyDocEnabled = false; + else + state->memstickOtherEnabled = !PSP_IsInited(); + } else + state->memstickMyDocEnabled = false; + } else { + if (state->installed && myDocsExists) { + FILE *testInstalled = File::OpenCFile(installedFile, "rt"); + if (testInstalled) { + char temp[2048]; + char *tempStr = fgets(temp, sizeof(temp), testInstalled); + // Skip UTF-8 encoding bytes if there are any. There are 3 of them. + if (tempStr && strncmp(tempStr, "\xEF\xBB\xBF", 3) == 0) { + tempStr += 3; + } + state->memstickOtherEnabled = !PSP_IsInited(); + if (tempStr && strlen(tempStr) != 0 && strcmp(tempStr, "\n") != 0) { + state->installed = false; + state->otherinstalled = true; + } + fclose(testInstalled); + } + } else if (!myDocsExists) { + state->memstickMyDocEnabled = false; + } + } +} + +static void OnMemoryStickMyDoc(SystemPageState *state) { + const Path &PPSSPPpath = File::GetExeDirectory(); + const Path installedFile = PPSSPPpath / "installed.txt"; + state->installed = File::Exists(installedFile); + if (state->otherinstalled) { + File::Delete(PPSSPPpath / "installed.txt"); + File::CreateEmptyFile(PPSSPPpath / "installed.txt"); + state->otherinstalled = false; + const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/"; + g_Config.memStickDirectory = Path(myDocsPath); + } else if (state->installed) { + File::Delete(PPSSPPpath / "installed.txt"); + state->installed = false; + g_Config.memStickDirectory = PPSSPPpath / "memstick"; + } else { + FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb"); + if (f) { + fclose(f); + } + + const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/"; + g_Config.memStickDirectory = Path(myDocsPath); + state->installed = true; + } +} + +static void OnMemoryStickOther(SystemPageState *state) { + const Path &PPSSPPpath = File::GetExeDirectory(); + if (state->otherinstalled) { + auto di = GetI18NCategory(I18NCat::DIALOG); + std::string initialPath = g_Config.memStickDirectory.ToCString(); + std::string folder = W32Util::BrowseForFolder2(MainWindow::GetHWND(), di->T("Choose PPSSPP save folder"), initialPath); + if (folder.size()) { + g_Config.memStickDirectory = Path(folder); + FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb"); + if (f) { + std::string utfstring("\xEF\xBB\xBF"); + utfstring.append(folder); + fwrite(utfstring.c_str(), 1, utfstring.length(), f); + fclose(f); + } + state->installed = false; + } + else + state->otherinstalled = false; + } + else { + File::Delete(PPSSPPpath / "installed.txt"); + state->memstickMyDocEnabled = true; + state->otherinstalled = false; + state->installed = false; + g_Config.memStickDirectory = PPSSPPpath / "memstick"; + } +} + +#endif + +static void OnImmersiveModeChange() { + System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE); + if (g_Config.iAndroidHwScale != 0) { + System_RecreateActivity(); + } +} + +static void OnSustainedPerformanceModeChange() { + System_Notify(SystemNotification::SUSTAINED_PERF_CHANGE); +} + +static void OnJitAffectingSetting() { + System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT); +} + +static void OnChangeBackground(SettingsPageHost *host) { + const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png"; + const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg"; + + if (File::Exists(bgPng) || File::Exists(bgJpg)) { + INFO_LOG(Log::UI, "Clearing background image."); + // The button is in clear mode. + File::Delete(bgPng); + File::Delete(bgJpg); + UIBackgroundShutdown(); + host->RecreatePageViews(); + return; + } + + auto sy = GetI18NCategory(I18NCat::SYSTEM); + System_BrowseForImage(host->PageRequesterToken(), sy->T("Set UI background..."), bgJpg, [host](std::string_view value, int converted) { + if (converted == 1) { + // The platform code converted and saved the file to the desired path already. + INFO_LOG(Log::UI, "Platform converted the file: %.*s", STR_VIEW(value)); + } else if (!value.empty()) { + Path path(value); + + // Check the file format. Don't rely on the file extension here due to scoped storage URLs. + FILE *f = File::OpenCFile(path, "rb"); + uint8_t buffer[8]; + ImageFileType type = ImageFileType::UNKNOWN; + if (f != nullptr && 8 == fread(buffer, 1, ARRAY_SIZE(buffer), f)) { + type = DetectImageFileType(buffer, ARRAY_SIZE(buffer)); + } + if (f) { + fclose(f); + } + + std::string filename; + switch (type) { + case ImageFileType::JPEG: + filename = "background.jpg"; + break; + case ImageFileType::PNG: + filename = "background.png"; + break; + default: + break; + } + + if (!filename.empty()) { + Path dest = GetSysDirectory(DIRECTORY_SYSTEM) / filename; + File::Copy(path, dest); + if (path.FilePathContainsNoCase("temp_import.jpg")) { + INFO_LOG(Log::UI, "Deleting temp file: %s", GetFriendlyPath(path).c_str()); + File::Delete(path); + } + } else { + auto sy = GetI18NCategory(I18NCat::SYSTEM); + g_OSD.Show(OSDType::MESSAGE_ERROR, sy->T("Only JPG and PNG images are supported"), path.GetFilename(), 5.0); + } + } + // It will init again automatically. We can't init outside a frame on Vulkan. + UIBackgroundShutdown(); + host->RecreatePageViews(); + }); + + // Change to a browse or clear button. +} + +static void CallbackRestoreDefaults(SystemPageState *state, bool yes) { + if (yes) { + g_Config.RestoreDefaults(RestoreSettingsBits::SETTINGS); + ReadSystemPageState(state); + } + System_Notify(SystemNotification::UI); +} + +static void OnRestoreDefaultSettings(SettingsPageHost *host, SystemPageState *state) { + auto sy = GetI18NCategory(I18NCat::SYSTEM); + if (g_Config.IsGameSpecific()) { + auto dev = GetI18NCategory(I18NCat::DEVELOPER); + auto di = GetI18NCategory(I18NCat::DIALOG); + auto sy = GetI18NCategory(I18NCat::SYSTEM); + host->PageScreenManager()->push( + new UI::MessagePopupScreen(sy->T("Restore Default Settings"), dev->T("RestoreGameDefaultSettings", "Are you sure you want to restore the game-specific settings back to the ppsspp defaults?\n"), + di->T("OK"), di->T("Cancel"), [state](bool yes) { CallbackRestoreDefaults(state, yes); })); + } else { + std::string_view title = sy->T("Restore Default Settings"); + host->PageScreenManager()->push(new RestoreSettingsScreen(title)); + } +} + +void BuildSystemSettings(SettingsPage *page, SystemPageState *state) { + using namespace UI; + + SettingsPageHost *host = page->Host(); + + page->Header("UI"); + + page->Custom("Language", [host](UI::ViewGroup *parent) -> UI::View * { + auto langCodeToName = [](std::string_view value) -> std::string { + auto &mapping = GetLangValuesMapping(); + auto iter = mapping.find(value); + if (iter != mapping.end()) { + return iter->second.first; + } + return std::string(value); + }; + + Choice *language = parent->Add(new ChoiceWithValueDisplay(&g_Config.sLanguageIni, T(I18NCat::SYSTEM, "Language"), langCodeToName)); + language->OnClick.Add([host](UI::EventParams &e) { + auto sy = GetI18NCategory(I18NCat::SYSTEM); + auto langScreen = new NewLanguageScreen(sy->T("Language")); + // The actual switching is handled in OnCompleted in NewLanguageScreen. + if (e.v) + langScreen->SetPopupOrigin(e.v); + host->PageScreenManager()->push(langScreen); + }); + return language; + }); + +#if PPSSPP_PLATFORM(IOS) + static const char *indicator[] = { + "Swipe once to switch app (indicator auto-hides)", + "Swipe twice to switch app (indicator stays visible)" + }; + page->Setting(&g_Config.iAppSwitchMode); + + { + // Note: On iPhone, iOS hides the status bar in landscape no matter what this is set to. + DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(g_display.GetDeviceOrientation()); + page->Setting(&config.bImmersiveMode).OnChange([](EventParams &e) { + System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE); + }); + } +#endif + +#if PPSSPP_PLATFORM(ANDROID) + // Hide Immersive Mode on pre-kitkat Android + if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19) { + DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(g_display.GetDeviceOrientation()); + page->Setting(&config.bImmersiveMode).OnChange([](EventParams &e) { + OnImmersiveModeChange(); + }); + } +#endif + + UIContext *ctx = host->PageScreenManager()->getUIContext(); + page->Setting(&g_Config.iUIScaleFactor).OnChange([ctx](UI::EventParams &e) { + const float dpiMul = UIScaleFactorToMultiplier(g_Config.iUIScaleFactor); + g_display.Recalculate(-1, -1, -1, -1, dpiMul); + ctx->InvalidateAtlas(); + NativeResized(); + }); + + const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png"; + const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg"; + if (File::Exists(bgPng) || File::Exists(bgJpg)) { + page->Choice("Clear UI background").OnClick([host](UI::EventParams &e) { + OnChangeBackground(host); + }); + } else if (System_GetPropertyBool(SYSPROP_HAS_IMAGE_BROWSER) || System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { + page->Choice("Set UI background...").OnClick([host](UI::EventParams &e) { + OnChangeBackground(host); + }); + } + + page->Setting(&g_Config.bTransparentBackground); + + // Shared with achievements. + + page->Setting(&g_Config.iNotificationPos); + + page->Setting(&g_Config.iBackgroundAnimation); + + page->Custom("Theme", [host](UI::ViewGroup *parent) -> UI::View * { + PopupMultiChoiceDynamic *theme = parent->Add(new PopupMultiChoiceDynamic(&g_Config.sThemeName, T(I18NCat::SYSTEM, "Theme"), GetThemeInfoNames(), I18NCat::THEMES, host->PageScreenManager())); + theme->OnChoice.Add([](EventParams &e) { + UpdateTheme(); + // Reset the tint/saturation if the theme changed. + if (e.b) { + g_Config.fUITint = 0.0f; + g_Config.fUISaturation = 1.0f; + } + }); + return theme; + }); + + Draw::DrawContext *draw = host->PageScreenManager()->getDrawContext(); + + if (!draw->GetBugs().Has(Draw::Bugs::RASPBERRY_SHADER_COMP_HANG)) { + // We use shaders without tint capability on hardware with this driver bug. + auto noDropShadow = [](View *view) { + static_cast(view)->SetHasDropShadow(false); + }; + page->Setting(&g_Config.fUITint).LiveUpdate().Setup(noDropShadow); + page->Setting(&g_Config.fUISaturation).LiveUpdate().Setup(noDropShadow); + } + + page->Header("PSP Memory Stick"); + + if (System_GetPropertyBool(SYSPROP_HAS_OPEN_DIRECTORY)) { + page->Choice("Show Memory Stick folder").OnClick([](UI::EventParams &p) { + System_LaunchUrl(LaunchUrlType::LOCAL_FOLDER, g_Config.memStickDirectory.ToString()); + }); + } + +#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) + bool showItHere = true; +#if PPSSPP_PLATFORM(IOS_APP_STORE) + if (g_Config.memStickDirectory == DarwinFileSystemServices::defaultMemoryStickPath()) { + // We still keep a way to access it on the developer tools screen. + showItHere = false; + } +#endif + if (showItHere) { + page->Choice("Set Memory Stick folder").OnClick([host](UI::EventParams &) { + SetMemStickDirDarwin(host->PageRequesterToken()); + }); + } +#endif + + auto addMemstickPath = [host, &page]() { + page->Custom("Memory Stick folder", [host](UI::ViewGroup *parent) -> UI::View * { + Choice *memstickPath = parent->Add(new ChoiceWithCallbackValueDisplay(T(I18NCat::SYSTEM, "Memory Stick folder"), []() { + return g_Config.memStickDirectory.ToVisualString(); + })); + memstickPath->OnClick.Add([host](UI::EventParams &e) { + host->PageScreenManager()->push(new MemStickScreen(false)); + }); + return memstickPath; + }).EnabledFunc([] { + return !PSP_IsInited(); + }); + }; + +#if PPSSPP_PLATFORM(ANDROID) + if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_VR) { + addMemstickPath(); + } +#elif defined(_WIN32) +#if PPSSPP_PLATFORM(UWP) + addMemstickPath(); +#else + page->Bool(&state->installed, "Memory Stick in My Documents").Enabled(&state->memstickMyDocEnabled).OnChange([state](UI::EventParams &e) { + OnMemoryStickMyDoc(state); + }); + page->Bool(&state->otherinstalled, "Memory Stick in installed.txt").Enabled(&state->memstickOtherEnabled).OnChange([state](UI::EventParams &e) { + OnMemoryStickOther(state); + }); + + if (!System_GetPropertyBool(SYSPROP_CAN_GET_FREE_SPACE_FAST)) { + // It might be slow, so use an option. + page->Setting(&g_Config.bReportAccurateFreeStorageSpace); + } +#endif +#endif + page->Setting(&g_Config.bMemStickInserted); + page->Setting(&g_Config.iMemStickSizeGB); + + page->Header("Help the PPSSPP team"); + page->Bool(&state->enableReports, "Enable Compatibility Server Reports").EnabledFunc([]() { + return Reporting::IsSupported(); + }); + + page->Header("Emulation"); + + page->Setting(&g_Config.bFastMemory).OnChange([](UI::EventParams &e) { + OnJitAffectingSetting(); + }); + page->Setting(&g_Config.bIgnoreBadMemAccess); + + page->Setting(&g_Config.iIOTimingMethod); + page->Setting(&g_Config.bForceLagSync).Disabled(&g_Config.bAutoFrameSkip); + page->Setting(&g_Config.iLockedCPUSpeed); + + page->Header({ I18NCat::SAVEDATA, "Save states" }); // Borrow this string from the savedata manager + + page->Setting(&g_Config.bEnableStateUndo); + page->Setting(&g_Config.bConfirmLoadState); + + page->Setting(&g_Config.iSaveStateSlotCount); + + // NOTE: We will soon support more states, but we'll keep this niche feature limited to the first five. + page->Setting(&g_Config.iAutoLoadSaveState).Setup([](View *view) { + if (g_Config.iAutoLoadSaveState != 1) { + static_cast(view)->HideChoice(1); // Hide "Oldest Save" if not using that mode. It doesn't make sense. + } + }); + + page->Setting(&g_Config.iRewindSnapshotInterval); + + page->Header("General"); + + page->Setting(&g_Config.iAskForExitConfirmationAfterSeconds); + + if (System_GetPropertyBool(SYSPROP_CAN_RESTRICT_ORIENTATION)) { + // Display rotation + page->Custom({ I18NCat::CONTROLS, "Screen Rotation" }, [host](UI::ViewGroup *parent) -> UI::View * { + AddRotationPicker(host->PageScreenManager(), parent, true); + return parent->GetViewByIndex(parent->GetNumSubviews() - 1); + }); + } + + if (System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE)) { + page->Setting(&g_Config.bSustainedPerformanceMode).OnChange([](UI::EventParams &e) { + OnSustainedPerformanceModeChange(); + }); + } + + page->Choice("Restore Default Settings").OnClick([host, state](UI::EventParams &e) { + OnRestoreDefaultSettings(host, state); + }); + + if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) + page->Setting(&g_Config.bBypassOSKWithKeyboard); + + if (System_GetPropertyBool(SYSPROP_ENOUGH_RAM_FOR_FULL_ISO)) { + page->Setting(&g_Config.bCacheFullIsoInRam).EnabledFunc([] { + return !PSP_IsInited(); + }); + } + + page->Setting(&g_Config.bCheckForNewVersion); + page->Setting(&g_Config.bScreenshotsAsPNG); + page->Setting(&g_Config.iScreenshotMode); + // TODO: Make this setting available on Mac too. +#if PPSSPP_PLATFORM(WINDOWS) || (defined (SDL) && !defined(MOBILE_DEVICE)) + page->Setting(&g_Config.bPauseOnLostFocus); +#endif + + std::vector cameraList = Camera::getDeviceList(); + if (cameraList.size() >= 1) { + page->Header({ I18NCat::GRAPHICS, "Camera" }); + page->Setting(&g_Config.sCameraDevice, std::move(cameraList)); +#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) + page->Setting(&g_Config.bCameraMirrorHorizontal); +#endif + } + + page->Header("Cheats"); + page->Setting(&g_Config.bEnableCheats); + page->Setting(&g_Config.bEnablePlugins); + + page->Header("PSP Settings"); + + // The ordering here is simply mapping directly to PSP_SYSTEMPARAM_LANGUAGE_*. + page->Setting(&g_Config.iLanguage); + page->Setting(&g_Config.iPSPModel).EnabledFunc([] { + return !PSP_IsInited(); + }); + page->Custom("Nickname", [host](UI::ViewGroup *parent) { + return AddNicknameChoice(host, parent); + }); + page->Setting(&g_Config.bDayLightSavings); + page->Setting(&g_Config.iDateFormat); + page->Setting(&g_Config.iTimeFormat); + page->Setting(&g_Config.iButtonPreference); + +#if defined(_WIN32) + page->Header("Recording"); + page->Setting(&g_Config.bDumpFrames); + page->Setting(&g_Config.bUseFFV1); + page->Setting(&g_Config.bDumpVideoOutput); + page->Setting(&g_Config.bDumpAudio); + page->Setting(&g_Config.bSaveLoadResetsAVdumping); +#endif +} diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index b9d6247c44..eca41e795a 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -82,6 +82,7 @@ + diff --git a/UI/UI.vcxproj.filters b/UI/UI.vcxproj.filters index e79229eb59..ee9c3dc0b1 100644 --- a/UI/UI.vcxproj.filters +++ b/UI/UI.vcxproj.filters @@ -117,6 +117,9 @@ Settings + + Settings + Settings diff --git a/UWP/UI_UWP/UI_UWP.vcxproj b/UWP/UI_UWP/UI_UWP.vcxproj index b1e306cead..d8c493d884 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj +++ b/UWP/UI_UWP/UI_UWP.vcxproj @@ -196,6 +196,7 @@ + diff --git a/UWP/UI_UWP/UI_UWP.vcxproj.filters b/UWP/UI_UWP/UI_UWP.vcxproj.filters index 43ce18d962..2f3d51c119 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj.filters +++ b/UWP/UI_UWP/UI_UWP.vcxproj.filters @@ -146,6 +146,9 @@ Settings + + Settings + Settings diff --git a/android/jni/Android.mk b/android/jni/Android.mk index b5eae5ad78..02de0f4a93 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -1029,6 +1029,7 @@ LOCAL_SRC_FILES := \ $(SRC)/UI/Settings/ControlsPage.cpp \ $(SRC)/UI/Settings/AudioPage.cpp \ $(SRC)/UI/Settings/NetworkingPage.cpp \ + $(SRC)/UI/Settings/SystemPage.cpp \ $(SRC)/UI/Settings/ToolsPage.cpp \ $(SRC)/UI/Settings/VRPage.cpp \ $(SRC)/UI/SettingsPagesDialogScreen.cpp \