From 71b38e58e688ba39701b62041883ea957cedbd2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 22 Oct 2026 15:28:18 +0200 Subject: [PATCH 1/4] Config: Store the alternative speeds in percent The settings screen showed them in percent but they were stored as frame rates out of 60, so the screen kept converted copies and wrote them back. Now they're stored in percent under new ini keys (AltSpeedPercent, AltSpeedPercent2, AnalogAltSpeedPercent), migrated from the old frame rate keys like the audio volumes were, and converted to a frame rate where the timing needs one. Low percentages no longer round to a 0 fps limit, which meant unlimited. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/Config.cpp | 27 ++++++++++++++++++++++++--- Core/Config.h | 20 +++++++++++++++++--- Core/HLE/sceDisplay.cpp | 4 ++-- UI/EmuScreen.cpp | 16 ++++++++-------- UI/GameSettingsScreen.cpp | 18 +++--------------- UI/GameSettingsScreen.h | 3 --- 6 files changed, 54 insertions(+), 34 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index ab7a631017..799f77c726 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -715,6 +715,23 @@ static const ConfigSetting gestureControlSettings[] = { ConfigSetting("AnalogGestureSensibility", SETTING(g_Config.gestureControls[0], fAnalogGestureSensitivity), 1.0f, CfgFlag::PER_GAME), }; +// The alternative speeds used to be stored as frame rates, out of 60. +static int LegacyFpsLimitToPercent(int fps) { + return fps < 0 ? -1 : (fps * 100 + 30) / 60; +} + +static int DefaultAltSpeedPercent1() { + return LegacyFpsLimitToPercent(g_Config.iLegacyFpsLimit1); +} + +static int DefaultAltSpeedPercent2() { + return LegacyFpsLimitToPercent(g_Config.iLegacyFpsLimit2); +} + +static int DefaultAnalogAltSpeedPercent() { + return LegacyFpsLimitToPercent(g_Config.iLegacyAnalogFpsLimit); +} + static const ConfigSetting graphicsSettings[] = { ConfigSetting("iShowStatusFlags", SETTING(g_Config, iShowStatusFlags), 0, CfgFlag::PER_GAME), ConfigSetting("GraphicsBackend", SETTING(g_Config, iGPUBackend), &DefaultGPUBackend, &GPUBackendTranslator::To, &GPUBackendTranslator::From, CfgFlag::DEFAULT | CfgFlag::REPORT), @@ -744,9 +761,13 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("AutoFrameSkip", SETTING(g_Config, bAutoFrameSkip), IsVREnabled(), CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("StereoRendering", SETTING(g_Config, bStereoRendering), false, CfgFlag::PER_GAME), ConfigSetting("StereoToMonoShader", SETTING(g_Config, sStereoToMonoShader), "RedBlue", CfgFlag::PER_GAME), - ConfigSetting("FrameRate", SETTING(g_Config, iFpsLimit1), 0, CfgFlag::PER_GAME), - ConfigSetting("FrameRate2", SETTING(g_Config, iFpsLimit2), -1, CfgFlag::PER_GAME), - ConfigSetting("AnalogFrameRate", SETTING(g_Config, iAnalogFpsLimit), 240, CfgFlag::PER_GAME), + // Read before the new settings, which use them as defaults. + ConfigSetting("FrameRate", SETTING(g_Config, iLegacyFpsLimit1), 0, CfgFlag::PER_GAME | CfgFlag::DONT_SAVE), + ConfigSetting("FrameRate2", SETTING(g_Config, iLegacyFpsLimit2), -1, CfgFlag::PER_GAME | CfgFlag::DONT_SAVE), + ConfigSetting("AnalogFrameRate", SETTING(g_Config, iLegacyAnalogFpsLimit), 240, CfgFlag::PER_GAME | CfgFlag::DONT_SAVE), + ConfigSetting("AltSpeedPercent", SETTING(g_Config, iAltSpeedPercent1), &DefaultAltSpeedPercent1, CfgFlag::PER_GAME), + ConfigSetting("AltSpeedPercent2", SETTING(g_Config, iAltSpeedPercent2), &DefaultAltSpeedPercent2, CfgFlag::PER_GAME), + ConfigSetting("AnalogAltSpeedPercent", SETTING(g_Config, iAnalogAltSpeedPercent), &DefaultAnalogAltSpeedPercent, CfgFlag::PER_GAME), #if PPSSPP_PLATFORM(WINDOWS) ConfigSetting("RestartRequired", SETTING(g_Config, bRestartRequired), false, CfgFlag::DONT_SAVE), #endif diff --git a/Core/Config.h b/Core/Config.h index fcc38aef28..86bcd5322c 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -360,9 +360,14 @@ public: int iTexScalingType; // 0 = xBRZ, 1 = Hybrid bool bTexDeposterize; bool bTexHardwareScaling; - int iFpsLimit1; - int iFpsLimit2; - int iAnalogFpsLimit; + // In percent of normal speed. 0 means unlimited, and negative (not for analog) disabled. + int iAltSpeedPercent1; + int iAltSpeedPercent2; + int iAnalogAltSpeedPercent; + // The old frame rate based settings. Only read, to migrate them. + int iLegacyFpsLimit1; + int iLegacyFpsLimit2; + int iLegacyAnalogFpsLimit; int iMaxRecent; int iCurrentStateSlot; int iRewindSnapshotInterval; @@ -836,6 +841,15 @@ private: std::string CreateRandMAC(); std::string DefaultProAdhocServer(); +// The frame rate limit for an alternative speed. Unlimited (0) and disabled (negative) stay as they are. +inline int AltSpeedPercentToFps(int percent) { + if (percent <= 0) { + return percent; + } + int fps = (percent * 60 + 50) / 100; + return fps < 1 ? 1 : fps; +} + // TODO: Find a better place for this. extern http::RequestManager g_DownloadManager; extern Config g_Config; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index d4ea955bf7..05f245e257 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -424,9 +424,9 @@ static int FrameTimingLimit() { return 0; // Can't slow down in hardcore mode. if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1) - return fixRate(g_Config.iFpsLimit1); + return fixRate(AltSpeedPercentToFps(g_Config.iAltSpeedPercent1)); if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) - return fixRate(g_Config.iFpsLimit2); + return fixRate(AltSpeedPercentToFps(g_Config.iAltSpeedPercent2)); if (PSP_CoreParameter().fpsLimit == FPSLimit::ANALOG) return fixRate(PSP_CoreParameter().analogFpsLimit); if (PSP_CoreParameter().fpsLimit == FPSLimit::DEBUGGER) diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index e401c974f1..8b47928e1e 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -697,14 +697,14 @@ void EmuScreen::sendMessage(UIMessage message, const char *value) { // TODO: We should replace the "fpsLimit" system with a speed factor. static void ShowFpsLimitNotice() { - int fpsLimit = 60; + int percent = 100; switch (PSP_CoreParameter().fpsLimit) { case FPSLimit::CUSTOM1: - fpsLimit = g_Config.iFpsLimit1; + percent = g_Config.iAltSpeedPercent1; break; case FPSLimit::CUSTOM2: - fpsLimit = g_Config.iFpsLimit2; + percent = g_Config.iAltSpeedPercent2; break; default: break; @@ -713,7 +713,7 @@ static void ShowFpsLimitNotice() { // Now display it. char temp[51]; - snprintf(temp, sizeof(temp), "%d%%", (int)((float)fpsLimit / 60.0f * 100.0f)); + snprintf(temp, sizeof(temp), "%d%%", percent); g_OSD.Show(OSDType::STATUS_ICON, temp, "", "I_FAST_FORWARD", 1.5f, "altspeed"); g_OSD.SetFlags("altspeed", OSDMessageFlags::Transparent); } @@ -994,9 +994,9 @@ void EmuScreen::ProcessVKey(VirtKey virtKey, bool down) { if (down) { if (!NetworkWarnUserIfOnlineAndCantSpeed()) { // Cycle through enabled speeds. - if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && g_Config.iFpsLimit1 >= 0) { + if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && g_Config.iAltSpeedPercent1 >= 0) { PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1; - } else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 && g_Config.iFpsLimit2 >= 0) { + } else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 && g_Config.iAltSpeedPercent2 >= 0) { PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2; } else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 || PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) { PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; @@ -1145,7 +1145,7 @@ void EmuScreen::OnVKeyAnalog(VirtKey virtualKeyCode, float value) { if (limitMode != FPSLimit::NORMAL && limitMode != FPSLimit::ANALOG) return; // Don't even try if the limit is invalid. - if (g_Config.iAnalogFpsLimit <= 0) + if (g_Config.iAnalogAltSpeedPercent <= 0) return; // Apply a small deadzone (against the resting position.) @@ -1153,7 +1153,7 @@ void EmuScreen::OnVKeyAnalog(VirtKey virtualKeyCode, float value) { // If target is above 60, value is how much to speed up over 60. Otherwise, it's how much slower. // So normalize the target. - int target = g_Config.iAnalogFpsLimit - 60; + int target = AltSpeedPercentToFps(g_Config.iAnalogAltSpeedPercent) - 60; PSP_CoreParameter().analogFpsLimit = 60 + (int)(target * value); // If we've reset back to normal, turn it off. diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 87baaed7fa..e74a0c7707 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -131,9 +131,6 @@ GameSettingsScreen::GameSettingsScreen(const Path &gamePath, std::string gameID, void GameSettingsScreen::ReadConfigTemporaries() { // Written back on exit, so it must be read even if the System tab is never opened. enableReports_ = Reporting::IsEnabled(); - iAlternateSpeedPercent1_ = g_Config.iFpsLimit1 < 0 ? -1 : (g_Config.iFpsLimit1 * 100) / 60; - iAlternateSpeedPercent2_ = g_Config.iFpsLimit2 < 0 ? -1 : (g_Config.iFpsLimit2 * 100) / 60; - iAlternateSpeedPercentAnalog_ = (g_Config.iAnalogFpsLimit * 100) / 60; } GameSettingsScreen::~GameSettingsScreen() { @@ -467,28 +464,19 @@ void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings) graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control"))); - PopupSliderChoice *altSpeed1 = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent1_, 0, 1000, UI::NO_DEFAULT_INT, gr->T("Alternative Speed", "Alternative speed"), 5, screenManager(), gr->T("%, 0:unlimited"))); + PopupSliderChoice *altSpeed1 = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iAltSpeedPercent1, 0, 1000, UI::NO_DEFAULT_INT, gr->T("Alternative Speed", "Alternative speed"), 5, screenManager(), gr->T("%, 0:unlimited"))); altSpeed1->SetFormat("%i%%"); altSpeed1->SetZeroLabel(gr->T("Unlimited")); altSpeed1->SetNegativeDisable(gr->T("Disabled")); - altSpeed1->OnChange.Add([this](UI::EventParams &) { - g_Config.iFpsLimit1 = iAlternateSpeedPercent1_ < 0 ? -1 : (iAlternateSpeedPercent1_ * 60) / 100; - }); - PopupSliderChoice *altSpeed2 = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent2_, 0, 1000, UI::NO_DEFAULT_INT, gr->T("Alternative Speed 2", "Alternative speed 2 (in %, 0 = unlimited)"), 5, screenManager(), gr->T("%, 0:unlimited"))); + PopupSliderChoice *altSpeed2 = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iAltSpeedPercent2, 0, 1000, UI::NO_DEFAULT_INT, gr->T("Alternative Speed 2", "Alternative speed 2 (in %, 0 = unlimited)"), 5, screenManager(), gr->T("%, 0:unlimited"))); altSpeed2->SetFormat("%i%%"); altSpeed2->SetZeroLabel(gr->T("Unlimited")); altSpeed2->SetNegativeDisable(gr->T("Disabled")); - altSpeed2->OnChange.Add([this](UI::EventParams &) { - g_Config.iFpsLimit2 = iAlternateSpeedPercent2_ < 0 ? -1 : (iAlternateSpeedPercent2_ * 60) / 100; - }); if (analogSpeedMapped_) { - PopupSliderChoice *analogSpeed = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercentAnalog_, 1, 1000, UI::NO_DEFAULT_INT, gr->T("Analog alternative speed", "Analog alternative speed (in %)"), 5, screenManager(), "%")); + PopupSliderChoice *analogSpeed = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iAnalogAltSpeedPercent, 1, 1000, UI::NO_DEFAULT_INT, gr->T("Analog alternative speed", "Analog alternative speed (in %)"), 5, screenManager(), "%")); analogSpeed->SetFormat("%i%%"); - analogSpeed->OnChange.Add([this](UI::EventParams &) { - g_Config.iAnalogFpsLimit = (iAlternateSpeedPercentAnalog_ * 60) / 100; - }); } graphicsSettings->Add(new ItemHeader(gr->T("Speed Hacks", "Speed Hacks (can cause rendering errors!)"))); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 7c15ca0a44..2d04e0d1f8 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -89,9 +89,6 @@ private: void ReadConfigTemporaries(); // Temporaries to convert setting types, cache enabled, etc. - int iAlternateSpeedPercent1_ = 0; - int iAlternateSpeedPercent2_ = 0; - int iAlternateSpeedPercentAnalog_ = 0; int prevInflightFrames_ = -1; bool enableReports_ = false; bool analogSpeedMapped_ = false; From 729047d4495a3e232b5359fd87a2cb127a566a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 22 Oct 2026 15:34:47 +0200 Subject: [PATCH 2/4] Config: Make the setting tables constexpr SETTING now takes offsets with offsetof and passes them along with the setting's type, instead of the block and member pointers, and the ConfigSetting constructors are constexpr. So the tables are built at compile time, and later per-setting metadata can be checked by the compiler too. Defaults that weren't constants had to change: - AutoFrameSkip called IsVREnabled() during static initialization, before VR is set up. It's now a default callback, evaluated when the config is loaded or reset, which can turn it on by default on VR devices. - DisplayRefreshRate read g_Config's own initial value, which is 60. Path defaults now share the string member of the default union. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/Config.cpp | 66 ++++++++------ Core/ConfigSettings.cpp | 6 +- Core/ConfigSettings.h | 193 +++++++++++++++------------------------- 3 files changed, 115 insertions(+), 150 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 799f77c726..d78ef38082 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -16,10 +16,12 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include #include #include #include +#include #include #include @@ -249,11 +251,19 @@ static int DefaultScreenRotation() { #endif } -#define SETTING(a, x) &a, &a.x -#define SETTING_IDX(a, x, i) &a, &a.x[i] +#if defined(__GNUC__) || defined(__clang__) +// offsetof on classes with virtual functions (like ConfigBlock) is conditionally supported, and our compilers do support it. +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +// The type of the block a setting lives in, from an expression like g_Config or g_Config.gestureControls[0]. +#define CONFIG_BLOCK_TYPE(a) std::remove_reference_t +// Offsets are taken with offsetof, so the tables can be constexpr. +#define SETTING(a, x) ConfigSettingOffset{ (uint32_t)offsetof(CONFIG_BLOCK_TYPE(a), x) } +#define SETTING_IDX(a, x, i) ConfigSettingOffset>{ (uint32_t)(offsetof(CONFIG_BLOCK_TYPE(a), x) + (i) * sizeof(std::remove_extent_t)) } // All relative to g_Config. -static const ConfigSetting generalSettings[] = { +static constexpr ConfigSetting generalSettings[] = { ConfigSetting("FirstRun", SETTING(g_Config, bFirstRun), true, CfgFlag::DEFAULT), ConfigSetting("RunCount", SETTING(g_Config, iRunCount), 0, CfgFlag::DEFAULT), ConfigSetting("Enable Logging", SETTING(g_Config, bEnableLogging), true, CfgFlag::PER_GAME), @@ -405,7 +415,7 @@ static bool DefaultSasThread() { return cpu_info.num_cores > 1; } -static const ConfigSetting achievementSettings[] = { +static constexpr ConfigSetting achievementSettings[] = { // Core settings ConfigSetting("AchievementsEnable", SETTING(g_Config, bAchievementsEnable), false, CfgFlag::PER_GAME), ConfigSetting("AchievementsEnableRAIntegration", SETTING(g_Config, bAchievementsEnableRAIntegration), false, CfgFlag::DEFAULT), @@ -435,7 +445,7 @@ static const ConfigSetting achievementSettings[] = { ConfigSetting("AchievementsUnlockedPos", SETTING(g_Config, iAchievementsUnlockedPos), (int)ScreenEdgePosition::TOP_CENTER, CfgFlag::PER_GAME | CfgFlag::DEFAULT), }; -static const ConfigSetting cpuSettings[] = { +static constexpr ConfigSetting cpuSettings[] = { ConfigSetting("CPUCore", SETTING(g_Config, iCpuCore), &DefaultCpuCore, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("SeparateSASThread", SETTING(g_Config, bSeparateSASThread), &DefaultSasThread, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("IOTimingMethod", SETTING(g_Config, iIOTimingMethod), IOTIMING_FAST, CfgFlag::PER_GAME | CfgFlag::REPORT), @@ -676,7 +686,7 @@ bool DisplayLayoutConfig::ResetToDefault(std::string_view blockName) { // These were previously part of Graphics. // It's instantiated into g_Config.displayLayoutLandscape and g_Config.displayLayoutPortrait. // Defaults are set directly on the struct declaration (and adjusted per instance in ResetToDefault above). -static const ConfigSetting displayLayoutSettings[] = { +static constexpr ConfigSetting displayLayoutSettings[] = { ConfigSetting("BufferFiltering", SETTING(g_Config.displayLayoutLandscape, iDisplayFilter), CfgFlag::PER_GAME), ConfigSetting("DisplayStretch", SETTING(g_Config.displayLayoutLandscape, bDisplayStretch), CfgFlag::PER_GAME), ConfigSetting("DisplayOffsetX", SETTING(g_Config.displayLayoutLandscape, fDisplayOffsetX), CfgFlag::PER_GAME), @@ -702,7 +712,7 @@ bool GestureControlConfig::ResetToDefault(std::string_view blockName) { } // These were previously mostly part of General (which was wrong). -static const ConfigSetting gestureControlSettings[] = { +static constexpr ConfigSetting gestureControlSettings[] = { ConfigSetting("SwipeUp", SETTING(g_Config.gestureControls[0], iSwipeUp), 0, CfgFlag::PER_GAME), ConfigSetting("SwipeDown", SETTING(g_Config.gestureControls[0], iSwipeDown), 0, CfgFlag::PER_GAME), ConfigSetting("SwipeLeft", SETTING(g_Config.gestureControls[0], iSwipeLeft), 0, CfgFlag::PER_GAME), @@ -732,7 +742,7 @@ static int DefaultAnalogAltSpeedPercent() { return LegacyFpsLimitToPercent(g_Config.iLegacyAnalogFpsLimit); } -static const ConfigSetting graphicsSettings[] = { +static constexpr ConfigSetting graphicsSettings[] = { ConfigSetting("iShowStatusFlags", SETTING(g_Config, iShowStatusFlags), 0, CfgFlag::PER_GAME), ConfigSetting("GraphicsBackend", SETTING(g_Config, iGPUBackend), &DefaultGPUBackend, &GPUBackendTranslator::To, &GPUBackendTranslator::From, CfgFlag::DEFAULT | CfgFlag::REPORT), #if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64) @@ -758,7 +768,7 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("AndroidHwScale", SETTING(g_Config, iAndroidHwScale), 0, CfgFlag::DEFAULT), ConfigSetting("HighQualityDepth", SETTING(g_Config, bHighQualityDepth), true, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("FrameSkip", SETTING(g_Config, iFrameSkip), 0, CfgFlag::PER_GAME | CfgFlag::REPORT), - ConfigSetting("AutoFrameSkip", SETTING(g_Config, bAutoFrameSkip), IsVREnabled(), CfgFlag::PER_GAME | CfgFlag::REPORT), + ConfigSetting("AutoFrameSkip", SETTING(g_Config, bAutoFrameSkip), &IsVREnabled, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("StereoRendering", SETTING(g_Config, bStereoRendering), false, CfgFlag::PER_GAME), ConfigSetting("StereoToMonoShader", SETTING(g_Config, sStereoToMonoShader), "RedBlue", CfgFlag::PER_GAME), // Read before the new settings, which use them as defaults. @@ -823,7 +833,7 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("UberShaderVertex", SETTING(g_Config, bUberShaderVertex), true, CfgFlag::DEFAULT), - ConfigSetting("DisplayRefreshRate", SETTING(g_Config, iDisplayRefreshRate), g_Config.iDisplayRefreshRate, CfgFlag::PER_GAME), + ConfigSetting("DisplayRefreshRate", SETTING(g_Config, iDisplayRefreshRate), 60, CfgFlag::PER_GAME), }; static int LegacyVolumeToNewVolume(int legacy, int max) { @@ -864,7 +874,7 @@ bool DefaultAudioMixWithOthers() { #endif } -static const ConfigSetting soundSettings[] = { +static constexpr ConfigSetting soundSettings[] = { ConfigSetting("Enable", SETTING(g_Config, bEnableSound), true, CfgFlag::PER_GAME), ConfigSetting("ExtraAudioBuffering", SETTING(g_Config, bExtraAudioBuffering), false, CfgFlag::DEFAULT), ConfigSetting("AudioBufferSize", SETTING(g_Config, iSDLAudioBufferSize), 256, CfgFlag::DEFAULT), @@ -904,9 +914,9 @@ static bool DefaultShowTouchControls() { } } -static const float defaultControlScale = 1.15f; -static const ConfigTouchPos defaultTouchPosShow = { -1.0f, -1.0f, defaultControlScale, true }; -static const ConfigTouchPos defaultTouchPosHide = { -1.0f, -1.0f, defaultControlScale, false }; +static constexpr float defaultControlScale = 1.15f; +static constexpr ConfigTouchPos defaultTouchPosShow = { -1.0f, -1.0f, defaultControlScale, true }; +static constexpr ConfigTouchPos defaultTouchPosHide = { -1.0f, -1.0f, defaultControlScale, false }; void TouchControlConfig::ResetLayout() { // reset puts the settings in a state so they'll then get properly reinitialized in InitPadLayout. @@ -954,7 +964,7 @@ bool TouchControlConfig::ResetToDefault(std::string_view blockName) { return true; } -static const ConfigSetting touchControlSettings[] = { +static constexpr ConfigSetting touchControlSettings[] = { ConfigSetting("ShowTouchCross", SETTING(g_Config.touchControlsLandscape, bShowTouchCross), true, CfgFlag::PER_GAME), ConfigSetting("ShowTouchCircle", SETTING(g_Config.touchControlsLandscape, bShowTouchCircle), true, CfgFlag::PER_GAME), ConfigSetting("ShowTouchSquare", SETTING(g_Config.touchControlsLandscape, bShowTouchSquare), true, CfgFlag::PER_GAME), @@ -1003,7 +1013,7 @@ static const ConfigSetting touchControlSettings[] = { ConfigSetting("HideStickBackground", SETTING(g_Config.touchControlsLandscape, bHideStickBackground), CfgFlag::PER_GAME), }; -static const ConfigSetting controlSettings[] = { +static constexpr ConfigSetting controlSettings[] = { ConfigSetting("HapticFeedback", SETTING(g_Config, bHapticFeedback), false, CfgFlag::PER_GAME), #if PPSSPP_PLATFORM(WINDOWS) @@ -1094,7 +1104,7 @@ static const ConfigSetting controlSettings[] = { static const std::vector emptyList; -static const ConfigSetting networkSettings[] = { +static constexpr ConfigSetting networkSettings[] = { ConfigSetting("EnableWlan", SETTING(g_Config, bEnableWlan), false, CfgFlag::PER_GAME), ConfigSetting("EnableAdhocServer", SETTING(g_Config, bEnableAdhocServer), false, CfgFlag::PER_GAME), ConfigSetting("proAdhocServer", SETTING(g_Config, sProAdhocServer), &DefaultProAdhocServer, CfgFlag::PER_GAME), @@ -1121,14 +1131,14 @@ static const ConfigSetting networkSettings[] = { ConfigSetting("ChatScreenPosition", SETTING(g_Config, iChatScreenPosition), (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME), ConfigSetting("ChatTimestamps", SETTING(g_Config, bChatTimestamps), true, CfgFlag::PER_GAME), ConfigSetting("EnableQuickChat", SETTING(g_Config, bEnableQuickChat), true, CfgFlag::PER_GAME), - ConfigSetting("QuickChat1", SETTING(g_Config, sQuickChat[0]), "Quick Chat 1", CfgFlag::PER_GAME), - ConfigSetting("QuickChat2", SETTING(g_Config, sQuickChat[1]), "Quick Chat 2", CfgFlag::PER_GAME), - ConfigSetting("QuickChat3", SETTING(g_Config, sQuickChat[2]), "Quick Chat 3", CfgFlag::PER_GAME), - ConfigSetting("QuickChat4", SETTING(g_Config, sQuickChat[3]), "Quick Chat 4", CfgFlag::PER_GAME), - ConfigSetting("QuickChat5", SETTING(g_Config, sQuickChat[4]), "Quick Chat 5", CfgFlag::PER_GAME), + ConfigSetting("QuickChat1", SETTING_IDX(g_Config, sQuickChat, 0), "Quick Chat 1", CfgFlag::PER_GAME), + ConfigSetting("QuickChat2", SETTING_IDX(g_Config, sQuickChat, 1), "Quick Chat 2", CfgFlag::PER_GAME), + ConfigSetting("QuickChat3", SETTING_IDX(g_Config, sQuickChat, 2), "Quick Chat 3", CfgFlag::PER_GAME), + ConfigSetting("QuickChat4", SETTING_IDX(g_Config, sQuickChat, 3), "Quick Chat 4", CfgFlag::PER_GAME), + ConfigSetting("QuickChat5", SETTING_IDX(g_Config, sQuickChat, 4), "Quick Chat 5", CfgFlag::PER_GAME), }; -static const ConfigSetting systemParamSettings[] = { +static constexpr ConfigSetting systemParamSettings[] = { ConfigSetting("PSPModel", SETTING(g_Config, iPSPModel), PSP_MODEL_SLIM, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("PSPFirmwareVersion", SETTING(g_Config, iFirmwareVersion), PSP_DEFAULT_FIRMWARE, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("NickName", SETTING(g_Config, sNickName), "PPSSPP", CfgFlag::PER_GAME), @@ -1149,7 +1159,7 @@ static const ConfigSetting systemParamSettings[] = { ConfigSetting("FuseID", SETTING(g_Config, sFuseID), "", CfgFlag::DEFAULT), }; -static const ConfigSetting debuggerSettings[] = { +static constexpr ConfigSetting debuggerSettings[] = { ConfigSetting("DisasmWindowX", SETTING(g_Config, iDisasmWindowX), -1, CfgFlag::DEFAULT), ConfigSetting("DisasmWindowY", SETTING(g_Config, iDisasmWindowY), -1, CfgFlag::DEFAULT), ConfigSetting("DisasmWindowW", SETTING(g_Config, iDisasmWindowW), -1, CfgFlag::DEFAULT), @@ -1174,21 +1184,21 @@ static const ConfigSetting debuggerSettings[] = { ConfigSetting("AutoSaveLoadSymbols", SETTING(g_Config, bAutoSaveLoadSymbols), false, CfgFlag::DEFAULT), }; -static const ConfigSetting jitSettings[] = { +static constexpr ConfigSetting jitSettings[] = { ConfigSetting("DiscardRegsOnJRRA", SETTING(g_Config, bDiscardRegsOnJRRA), false, CfgFlag::DONT_SAVE | CfgFlag::REPORT), }; -static const ConfigSetting upgradeSettings[] = { +static constexpr ConfigSetting upgradeSettings[] = { ConfigSetting("UpgradeMessage", SETTING(g_Config, sUpgradeMessage), "", CfgFlag::DEFAULT), ConfigSetting("UpgradeVersion", SETTING(g_Config, sUpgradeVersion), "", CfgFlag::DEFAULT), ConfigSetting("DismissedVersion", SETTING(g_Config, sDismissedVersion), "", CfgFlag::DEFAULT), }; -static const ConfigSetting themeSettings[] = { +static constexpr ConfigSetting themeSettings[] = { ConfigSetting("ThemeName", SETTING(g_Config, sThemeName), "Default", CfgFlag::DEFAULT), }; -static const ConfigSetting vrSettings[] = { +static constexpr ConfigSetting vrSettings[] = { ConfigSetting("VREnable", SETTING(g_Config, bEnableVR), true, CfgFlag::PER_GAME), ConfigSetting("VREnable6DoF", SETTING(g_Config, bEnable6DoF), false, CfgFlag::PER_GAME), ConfigSetting("VREnableStereo", SETTING(g_Config, bEnableStereo), false, CfgFlag::PER_GAME), diff --git a/Core/ConfigSettings.cpp b/Core/ConfigSettings.cpp index 9a866b1ea7..a4a4e1062e 100644 --- a/Core/ConfigSettings.cpp +++ b/Core/ConfigSettings.cpp @@ -139,7 +139,7 @@ bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section * if (defaultCallback_.p) { *target = defaultCallback_.p(); } else { - *target = Path(default_.p); + *target = Path(default_.s); } } return false; @@ -351,8 +351,8 @@ bool ConfigSetting::RestoreToDefault(ConfigBlock *configBlock, bool log) const { if (defaultCallback_.p) { *ptr_path = defaultCallback_.p(); break; - } else if (default_.p) { - *ptr_path = Path(default_.p); + } else if (default_.s) { + *ptr_path = Path(default_.s); } else { ptr_path->clear(); } diff --git a/Core/ConfigSettings.h b/Core/ConfigSettings.h index 81d311f42d..5951608003 100644 --- a/Core/ConfigSettings.h +++ b/Core/ConfigSettings.h @@ -1,7 +1,10 @@ #pragma once #include +#include +#include #include +#include #include "Core/ConfigValues.h" @@ -19,6 +22,12 @@ ENUM_CLASS_BITOPS(CfgFlag); struct ConfigBlock; +// Where a setting is within its ConfigBlock, along with its type. Made by the SETTING macro in Config.cpp. +template +struct ConfigSettingOffset { + uint32_t offset; +}; + struct ConfigSetting { enum class Type { TYPE_TERMINATOR, @@ -34,13 +43,23 @@ struct ConfigSetting { TYPE_CUSTOM_BUTTON }; union DefaultValue { + constexpr DefaultValue() : i(0) {} + constexpr DefaultValue(bool v) : b(v) {} + constexpr DefaultValue(int v) : i(v) {} + constexpr DefaultValue(uint32_t v) : u(v) {} + constexpr DefaultValue(uint64_t v) : lu(v) {} + constexpr DefaultValue(float v) : f(v) {} + constexpr DefaultValue(const char *v) : s(v) {} // Strings and paths. + constexpr DefaultValue(const std::vector *v) : v(v) {} + constexpr DefaultValue(ConfigTouchPos v) : touchPos(v) {} + constexpr DefaultValue(ConfigCustomButton v) : customButton(v) {} + bool b; int i; uint32_t u; uint64_t lu; float f; const char *s; - const char *p; // not sure how much point.. const std::vector *v; ConfigTouchPos touchPos; ConfigCustomButton customButton; @@ -57,6 +76,17 @@ struct ConfigSetting { typedef ConfigCustomButton (*CustomButtonDefaultCallback)(); union DefaultCallback { + constexpr DefaultCallback() : b(nullptr) {} + constexpr DefaultCallback(BoolDefaultCallback cb) : b(cb) {} + constexpr DefaultCallback(IntDefaultCallback cb) : i(cb) {} + constexpr DefaultCallback(Uint32DefaultCallback cb) : u(cb) {} + constexpr DefaultCallback(Uint64DefaultCallback cb) : lu(cb) {} + constexpr DefaultCallback(FloatDefaultCallback cb) : f(cb) {} + constexpr DefaultCallback(StringDefaultCallback cb) : s(cb) {} + constexpr DefaultCallback(PathDefaultCallback cb) : p(cb) {} + constexpr DefaultCallback(TouchPosDefaultCallback cb) : touchPos(cb) {} + constexpr DefaultCallback(CustomButtonDefaultCallback cb) : customButton(cb) {} + BoolDefaultCallback b; IntDefaultCallback i; Uint32DefaultCallback u; @@ -68,125 +98,50 @@ struct ConfigSetting { CustomButtonDefaultCallback customButton; }; - // The configBlock you pass in here is just to be able to calculate the offset of the setting within the block so it can be - // applied to other blocks. - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, bool *v, bool def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_BOOL), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.b = nullptr; - default_.b = def; - } + // The offset says where the setting is within its ConfigBlock, so it can be applied to any block of that type. + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, bool def, CfgFlag flags) noexcept + : type_(Type::TYPE_BOOL), iniKey_(ini), flags_(flags), default_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, CfgFlag flags) noexcept + : type_(Type::TYPE_BOOL), iniKey_(ini), flags_(flags), default_(false), offset_(v.offset) {} // The default is unused with this constructor. + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, int def, CfgFlag flags) noexcept + : type_(Type::TYPE_INT), iniKey_(ini), flags_(flags), default_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, CfgFlag flags) noexcept + : type_(Type::TYPE_INT), iniKey_(ini), flags_(flags), default_(0), offset_(v.offset) {} // The default is unused with this constructor. + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, int def, std::string (*transTo)(int), int (*transFrom)(const std::string &), CfgFlag flags) noexcept + : type_(Type::TYPE_INT), iniKey_(ini), flags_(flags), default_(def), offset_(v.offset), translateTo_(transTo), translateFrom_(transFrom) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, uint32_t def, CfgFlag flags) noexcept + : type_(Type::TYPE_UINT32), iniKey_(ini), flags_(flags), default_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, uint64_t def, CfgFlag flags) noexcept + : type_(Type::TYPE_UINT64), iniKey_(ini), flags_(flags), default_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, float def, CfgFlag flags) noexcept + : type_(Type::TYPE_FLOAT), iniKey_(ini), flags_(flags), default_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, CfgFlag flags) noexcept + : type_(Type::TYPE_FLOAT), iniKey_(ini), flags_(flags), default_(0.0f), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, const char *def, CfgFlag flags) noexcept + : type_(Type::TYPE_STRING), iniKey_(ini), flags_(flags), default_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset> v, const std::vector *def, CfgFlag flags) noexcept + : type_(Type::TYPE_STRING_VECTOR), iniKey_(ini), flags_(flags), default_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, const char *def, CfgFlag flags) noexcept + : type_(Type::TYPE_PATH), iniKey_(ini), flags_(flags), default_(def), offset_(v.offset) {} + constexpr ConfigSetting(const char *iniX, const char *iniY, const char *iniScale, const char *iniShow, ConfigSettingOffset v, ConfigTouchPos def, CfgFlag flags) noexcept + : ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(Type::TYPE_TOUCH_POS), iniKey_(iniX), flags_(flags), default_(def), offset_(v.offset) {} + constexpr ConfigSetting(const char *iniKey, const char *iniImage, const char *iniShape, const char *iniToggle, const char *iniRepeat, ConfigSettingOffset v, ConfigCustomButton def, CfgFlag flags) noexcept + : ini2_(iniImage), ini3_(iniShape), ini4_(iniToggle), ini5_(iniRepeat), type_(Type::TYPE_CUSTOM_BUTTON), iniKey_(iniKey), flags_(flags), default_(def), offset_(v.offset) {} - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, bool *v, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_BOOL), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.b = nullptr; - default_.b = false; // unused with this constructor - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, int *v, int def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.i = nullptr; - default_.i = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, int *v, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.i = nullptr; - default_.i = 0; // unused with this constructor - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, int *v, int def, std::string (*transTo)(int), int (*transFrom)(const std::string &), CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), translateTo_(transTo), translateFrom_(transFrom), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.i = nullptr; - default_.i = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, uint32_t *v, uint32_t def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_UINT32), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.u = nullptr; - default_.u = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, uint64_t *v, uint64_t def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_UINT64), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.lu = nullptr; - default_.lu = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, float *v, float def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_FLOAT), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.f = nullptr; - default_.f = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, float *v, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_FLOAT), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.f = nullptr; - default_.f = 0.0f; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, std::string *v, const char *def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_STRING), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.s = nullptr; - default_.s = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, std::vector *v, const std::vector *def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_STRING_VECTOR), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - default_.v = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, Path *v, const char *def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_PATH), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.p = nullptr; - default_.p = def; - } - - ConfigSetting(const char *iniX, const char *iniY, const char *iniScale, const char *iniShow, ConfigBlock *configBlock, ConfigTouchPos *v, ConfigTouchPos def, CfgFlag flags) noexcept - : iniKey_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(Type::TYPE_TOUCH_POS), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.touchPos = nullptr; - default_.touchPos = def; - } - - ConfigSetting(const char *iniKey, const char *iniImage, const char *iniShape, const char *iniToggle, const char *iniRepeat, ConfigBlock *configBlock, ConfigCustomButton *v, ConfigCustomButton def, CfgFlag flags) noexcept - : iniKey_(iniKey), ini2_(iniImage), ini3_(iniShape), ini4_(iniToggle), ini5_(iniRepeat), type_(Type::TYPE_CUSTOM_BUTTON), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.customButton = nullptr; - default_.customButton = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, bool *v, BoolDefaultCallback def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_BOOL), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.b = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, int *v, IntDefaultCallback def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.i = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, int *v, IntDefaultCallback def, std::string(*transTo)(int), int(*transFrom)(const std::string &), CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - (const char *)configBlock), translateTo_(transTo), translateFrom_(transFrom) { - defaultCallback_.i = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, uint32_t *v, Uint32DefaultCallback def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_UINT32), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.u = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, float *v, FloatDefaultCallback def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_FLOAT), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.f = def; - } - - ConfigSetting(std::string_view ini, ConfigBlock *configBlock, std::string *v, StringDefaultCallback def, CfgFlag flags) noexcept - : iniKey_(ini), type_(Type::TYPE_STRING), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.s = def; - } - - ConfigSetting(std::string_view iniX, const char *iniY, const char *iniScale, const char *iniShow, ConfigBlock *configBlock, ConfigTouchPos *v, TouchPosDefaultCallback def, CfgFlag flags) noexcept - : iniKey_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(Type::TYPE_TOUCH_POS), flags_(flags), offset_((const char *)v - (const char *)configBlock) { - defaultCallback_.touchPos = def; - } + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, BoolDefaultCallback def, CfgFlag flags) noexcept + : type_(Type::TYPE_BOOL), iniKey_(ini), flags_(flags), defaultCallback_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, IntDefaultCallback def, CfgFlag flags) noexcept + : type_(Type::TYPE_INT), iniKey_(ini), flags_(flags), defaultCallback_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, IntDefaultCallback def, std::string(*transTo)(int), int(*transFrom)(const std::string &), CfgFlag flags) noexcept + : type_(Type::TYPE_INT), iniKey_(ini), flags_(flags), defaultCallback_(def), offset_(v.offset), translateTo_(transTo), translateFrom_(transFrom) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, Uint32DefaultCallback def, CfgFlag flags) noexcept + : type_(Type::TYPE_UINT32), iniKey_(ini), flags_(flags), defaultCallback_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, FloatDefaultCallback def, CfgFlag flags) noexcept + : type_(Type::TYPE_FLOAT), iniKey_(ini), flags_(flags), defaultCallback_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view ini, ConfigSettingOffset v, StringDefaultCallback def, CfgFlag flags) noexcept + : type_(Type::TYPE_STRING), iniKey_(ini), flags_(flags), defaultCallback_(def), offset_(v.offset) {} + constexpr ConfigSetting(std::string_view iniX, const char *iniY, const char *iniScale, const char *iniShow, ConfigSettingOffset v, TouchPosDefaultCallback def, CfgFlag flags) noexcept + : ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(Type::TYPE_TOUCH_POS), iniKey_(iniX), flags_(flags), defaultCallback_(def), offset_(v.offset) {} bool ReadFromIniSection(ConfigBlock *configBlock, const Section *section, bool applyDefaultIfMissing) const; From 5d8db2a22d6402e5f2ad49dd8071219a7bbd087e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 22 Oct 2026 14:38:47 +0200 Subject: [PATCH 3/4] UI: Give views a pointer to their parent group Set by ViewGroup when a view is added, inserted or swapped in. Lets a view find the scroll view it's in, for example. Co-Authored-By: Claude Opus 5.5 (1M context) --- Common/UI/View.h | 6 ++++++ Common/UI/ViewGroup.cpp | 4 ++-- Common/UI/ViewGroup.h | 5 +++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Common/UI/View.h b/Common/UI/View.h index 0a2b4b4137..23f85bd0fd 100644 --- a/Common/UI/View.h +++ b/Common/UI/View.h @@ -471,6 +471,9 @@ public: const std::string &Tag() const { return tag_; } void SetTag(std::string_view str) { tag_ = str; } + // The group this view was added to, if any. + ViewGroup *Parent() const { return parent_; } + // Fake RTTI virtual bool IsViewGroup() const { return false; } virtual bool ContainsSubview(const View *view) const { return false; } @@ -518,6 +521,9 @@ protected: DialogResult autoResult_ = DR_OK; private: + friend class ViewGroup; // Sets parent_. + ViewGroup *parent_ = nullptr; + std::function enabledFunc_; bool *enabledPtr_ = nullptr; bool enabled_ = true; diff --git a/Common/UI/ViewGroup.cpp b/Common/UI/ViewGroup.cpp index 6835ece25d..e7fcd9c37a 100644 --- a/Common/UI/ViewGroup.cpp +++ b/Common/UI/ViewGroup.cpp @@ -164,6 +164,7 @@ bool ViewGroup::ReplaceSubview(View *view, View *newView) { for (int i = 0; i < (int)views_.size(); i++) { if (views_[i] == view) { views_[i] = newView; + SetParentOf(newView); delete view; return true; } @@ -1042,8 +1043,7 @@ CollapsibleSection::CollapsibleSection(std::string_view title, LayoutParams *lay open_ = &localOpen_; SetSpacing(0.0f); - header_ = new CollapsibleHeader(open_, title); - views_.push_back(header_); + header_ = Add(new CollapsibleHeader(open_, title)); header_->OnClick.Add([=](UI::EventParams &) { // Change the visibility of all children except the first one. // Later maybe try something more ambitious. diff --git a/Common/UI/ViewGroup.h b/Common/UI/ViewGroup.h index 7ed8258e8f..8605d3deaa 100644 --- a/Common/UI/ViewGroup.h +++ b/Common/UI/ViewGroup.h @@ -53,11 +53,13 @@ public: template T *Add(T *view) { views_.push_back(view); + SetParentOf(view); return view; } template T *Insert(int index, T *view) { views_.insert(views_.begin() + index, view); + SetParentOf(view); return view; } @@ -108,6 +110,9 @@ protected: std::string DescribeListUnordered(std::string_view heading) const; std::string DescribeListOrdered(std::string_view heading) const; + // Anything that adds to views_ without Add or Insert must call this. + void SetParentOf(View *view) { view->parent_ = this; } + std::vector views_; View *defaultFocusView_ = nullptr; Drawable bg_; From 44b5dc1b9564dabbcdad2b45cb4fd61fc9ef35cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 22 Oct 2026 18:00:17 +0200 Subject: [PATCH 4/4] Config: Default AutoFrameSkip to false Its default called IsVREnabled() during static initialization, before VR is set up, so it was always false in practice. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/Config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index d78ef38082..b1d6f7cffc 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -768,7 +768,7 @@ static constexpr ConfigSetting graphicsSettings[] = { ConfigSetting("AndroidHwScale", SETTING(g_Config, iAndroidHwScale), 0, CfgFlag::DEFAULT), ConfigSetting("HighQualityDepth", SETTING(g_Config, bHighQualityDepth), true, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("FrameSkip", SETTING(g_Config, iFrameSkip), 0, CfgFlag::PER_GAME | CfgFlag::REPORT), - ConfigSetting("AutoFrameSkip", SETTING(g_Config, bAutoFrameSkip), &IsVREnabled, CfgFlag::PER_GAME | CfgFlag::REPORT), + ConfigSetting("AutoFrameSkip", SETTING(g_Config, bAutoFrameSkip), false, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("StereoRendering", SETTING(g_Config, bStereoRendering), false, CfgFlag::PER_GAME), ConfigSetting("StereoToMonoShader", SETTING(g_Config, sStereoToMonoShader), "RedBlue", CfgFlag::PER_GAME), // Read before the new settings, which use them as defaults.