From 2ff2864f234841fbe9428a76fc412f081684e625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Tue, 10 Jan 2023 11:12:31 +0100 Subject: [PATCH] PowerControl: Expose all settings and apply them in order Since some settings impact others, the application will apply them in a correct order with a fixed delay. This additionally exposes all settings, just some of them are not persisted on create, only on change. --- PowerControl/Menu/MenuItemWithOptions.cs | 58 +++++++++++++++++++--- PowerControl/Options/CPUFrequency.cs | 2 + PowerControl/Options/FanControl.cs | 2 + PowerControl/Options/GPUFrequency.cs | 2 + PowerControl/Options/GPUScalingItem.cs | 16 ++++-- PowerControl/Options/PerformanceOverlay.cs | 1 + PowerControl/Options/RefreshRate.cs | 4 ++ PowerControl/Options/Resolution.cs | 10 +++- PowerControl/Options/SteamController.cs | 2 + PowerControl/Options/TDP.cs | 2 + PowerControl/ProfilesController.cs | 48 ++++++++++-------- 11 files changed, 115 insertions(+), 32 deletions(-) diff --git a/PowerControl/Menu/MenuItemWithOptions.cs b/PowerControl/Menu/MenuItemWithOptions.cs index 3d1a4bd..cd12065 100644 --- a/PowerControl/Menu/MenuItemWithOptions.cs +++ b/PowerControl/Menu/MenuItemWithOptions.cs @@ -8,7 +8,10 @@ namespace PowerControl.Menu public string? ProfileOption { get; set; } public int ApplyDelay { get; set; } public bool CycleOptions { get; set; } = true; - public string? PersistentKey; + public string? PersistentKey { get; set; } + public bool PersistOnCreate { get; set; } = true; + + public IList Impacts { get; set; } = new List(); public Func? CurrentValue { get; set; } public Func? OptionsValues { get; set; } @@ -20,6 +23,7 @@ namespace PowerControl.Menu private System.Windows.Forms.Timer delayTimer = new System.Windows.Forms.Timer(); private ToolStripMenuItem toolStripItem = new ToolStripMenuItem(); + private bool runAfterApply = false; public MenuItemWithOptions() { @@ -32,7 +36,7 @@ namespace PowerControl.Menu if (delayTimer != null) delayTimer.Stop(); - FinalizeSet(delayTimer.Interval > ApplyDelay); + FinalizeSet(); }; } @@ -77,16 +81,17 @@ namespace PowerControl.Menu ActiveOption = Options.First(); } - public void Set(String value, int overrideDelay = -1, bool silent = false) + public void Set(String value, int overrideDelay = -1, bool refresh = true) { if (delayTimer != null) delayTimer.Stop(); SelectedOption = value; + runAfterApply = refresh; if (ApplyDelay == 0 || overrideDelay == 0) { - FinalizeSet(silent); + FinalizeSet(); return; } @@ -94,7 +99,7 @@ namespace PowerControl.Menu delayTimer.Enabled = true; } - private void FinalizeSet(bool silent = false) + private void FinalizeSet() { var wasOption = ActiveOption; @@ -102,7 +107,7 @@ namespace PowerControl.Menu { ActiveOption = ApplyValue(SelectedOption); - if (AfterApply != null && !silent) + if (AfterApply != null && runAfterApply) AfterApply(); } else @@ -110,7 +115,7 @@ namespace PowerControl.Menu SelectedOption = null; - if (wasOption != ActiveOption && ActiveOption != null && !silent) + if (wasOption != ActiveOption && ActiveOption != null) ValueChanged(this, wasOption, ActiveOption); } @@ -202,5 +207,44 @@ namespace PowerControl.Menu return text; } + + public static IEnumerable Order(IEnumerable items) + { + HashSet processed = new HashSet(); + + // Try to run iteratively up to 10 times + for (int i = 0; i < 10; i++) + { + List leftItems = new List(); + + foreach (var item in items) + { + bool valid = item.Impacts.All((impactsItem) => processed.Contains(impactsItem)); + + if (valid) + { + processed.Add(item); + yield return item; + } + else + { + leftItems.Add(item); + } + } + + if (leftItems.Count() == 0) + yield break; + + items = leftItems; + } + + CommonHelpers.Log.TraceLine("PowerControl: Failed to order items: {0}", + string.Join(", ", items.Select((item) => item.Name))); + + foreach (var item in items) + { + yield return item; + } + } } } diff --git a/PowerControl/Options/CPUFrequency.cs b/PowerControl/Options/CPUFrequency.cs index f4d26fe..9bd7f99 100644 --- a/PowerControl/Options/CPUFrequency.cs +++ b/PowerControl/Options/CPUFrequency.cs @@ -7,6 +7,8 @@ namespace PowerControl.Options public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions() { Name = "CPU", + PersistentKey = "CPUFrequency", + PersistOnCreate = false, Options = { "Default", "Power-Save", "Balanced", "Max" }, ApplyDelay = 1000, ActiveOption = "?", diff --git a/PowerControl/Options/FanControl.cs b/PowerControl/Options/FanControl.cs index fbba0f3..82a6897 100644 --- a/PowerControl/Options/FanControl.cs +++ b/PowerControl/Options/FanControl.cs @@ -7,6 +7,8 @@ namespace PowerControl.Options public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions() { Name = "FAN", + PersistentKey = "FANMode", + PersistOnCreate = false, ApplyDelay = 500, OptionsValues = delegate () { diff --git a/PowerControl/Options/GPUFrequency.cs b/PowerControl/Options/GPUFrequency.cs index 508ebe6..5716c47 100644 --- a/PowerControl/Options/GPUFrequency.cs +++ b/PowerControl/Options/GPUFrequency.cs @@ -7,6 +7,8 @@ namespace PowerControl.Options public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions() { Name = "GPU", + PersistentKey = "GPUFrequency", + PersistOnCreate = false, Options = { "Default", "400MHz", "800MHz", "1200MHz", "1600MHz" }, ApplyDelay = 1000, Visible = VangoghGPU.IsSupported, diff --git a/PowerControl/Options/GPUScalingItem.cs b/PowerControl/Options/GPUScalingItem.cs index 21d7192..eb4ed6d 100644 --- a/PowerControl/Options/GPUScalingItem.cs +++ b/PowerControl/Options/GPUScalingItem.cs @@ -31,14 +31,22 @@ namespace PowerControl.Options // Since the RadeonSoftware will try to revert values RadeonSoftware.Kill(); + if (!GPUScaling.Enabled) + return "Off"; + return GPUScaling.Mode.ToString(); + }, + Impacts = + { + Resolution.Instance, + RefreshRate.Instance, + FPSLimit.Instance + }, + AfterApply = () => + { Resolution.Instance.Update(); RefreshRate.Instance.Update(); FPSLimit.Instance.Reset(); FPSLimit.Instance.Update(); - - if (!GPUScaling.Enabled) - return "Off"; - return GPUScaling.Mode.ToString(); } }; } diff --git a/PowerControl/Options/PerformanceOverlay.cs b/PowerControl/Options/PerformanceOverlay.cs index 19bfb9e..a0c5934 100644 --- a/PowerControl/Options/PerformanceOverlay.cs +++ b/PowerControl/Options/PerformanceOverlay.cs @@ -33,6 +33,7 @@ namespace PowerControl.Options public static Menu.MenuItemWithOptions ModeInstance = new Menu.MenuItemWithOptions() { Name = "OSD Mode", + PersistentKey = "PerformanceOverlayMode", ApplyDelay = 500, OptionsValues = delegate () { diff --git a/PowerControl/Options/RefreshRate.cs b/PowerControl/Options/RefreshRate.cs index bb29a81..a607342 100644 --- a/PowerControl/Options/RefreshRate.cs +++ b/PowerControl/Options/RefreshRate.cs @@ -28,6 +28,10 @@ namespace PowerControl.Options return DisplayResolutionController.GetRefreshRate().ToString(); }, + Impacts = + { + FPSLimit.Instance + }, AfterApply = () => { // force reset and refresh of FPS limit diff --git a/PowerControl/Options/Resolution.cs b/PowerControl/Options/Resolution.cs index 3f41805..a212542 100644 --- a/PowerControl/Options/Resolution.cs +++ b/PowerControl/Options/Resolution.cs @@ -33,12 +33,20 @@ namespace PowerControl.Options { var selectedResolution = new DisplayResolutionController.DisplayResolution(selected); DisplayResolutionController.SetResolution(selectedResolution); + return DisplayResolutionController.GetResolution().ToString(); + }, + Impacts = + { + RefreshRate.Instance, + FPSLimit.Instance + }, + AfterApply = () => + { // force refresh Refresh Rate RefreshRate.Instance.Update(); // force reset and refresh of FPS limit FPSLimit.Instance.Reset(); FPSLimit.Instance.Update(); - return DisplayResolutionController.GetResolution().ToString(); } }; } diff --git a/PowerControl/Options/SteamController.cs b/PowerControl/Options/SteamController.cs index d2fb1e4..b43a47f 100644 --- a/PowerControl/Options/SteamController.cs +++ b/PowerControl/Options/SteamController.cs @@ -7,6 +7,8 @@ namespace PowerControl.Options public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions() { Name = "Controller", + PersistentKey = "SteamController", + PersistOnCreate = false, ApplyDelay = 500, OptionsValues = delegate () { diff --git a/PowerControl/Options/TDP.cs b/PowerControl/Options/TDP.cs index 4aeaed0..548481d 100644 --- a/PowerControl/Options/TDP.cs +++ b/PowerControl/Options/TDP.cs @@ -8,6 +8,8 @@ namespace PowerControl.Options public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions() { Name = "TDP", + PersistentKey = "TDP", + PersistOnCreate = false, Options = { "3W", "4W", "5W", "6W", "7W", "8W", "10W", "12W", "15W" }, ApplyDelay = 1000, ResetValue = () => { return "15W"; }, diff --git a/PowerControl/ProfilesController.cs b/PowerControl/ProfilesController.cs index 224eb70..fb194c5 100644 --- a/PowerControl/ProfilesController.cs +++ b/PowerControl/ProfilesController.cs @@ -9,6 +9,8 @@ namespace PowerControl public class ProfilesController : IDisposable { public const bool AutoCreateProfiles = true; + public const int ApplyProfileDelayMs = 500; + public const int ResetProfileDelayMs = 500; private Dictionary watchedProcesses = new Dictionary(); private Dictionary? changedSettings; @@ -154,11 +156,9 @@ namespace PowerControl private void ProfileChanged() { - foreach (var menuItem in MenuStack.Root.AllMenuItemOptions()) + foreach (var menuItem in PersistableOptions()) { - if (menuItem.PersistentKey is null) - continue; - menuItem.ProfileOption = CurrentProfileSettings?.GetValue(menuItem.PersistentKey); + menuItem.ProfileOption = CurrentProfileSettings?.GetValue(menuItem.PersistentKey ?? ""); } } @@ -174,11 +174,11 @@ namespace PowerControl if (!saveAll) return; - foreach (var menuItem in MenuStack.Root.AllMenuItemOptions()) + foreach (var menuItem in PersistableOptions()) { - if (menuItem.PersistentKey is null || menuItem.ActiveOption is null) + if (menuItem.ActiveOption is null || !menuItem.PersistOnCreate) continue; - profileSettings?.SetValue(menuItem.PersistentKey, menuItem.ActiveOption); + profileSettings?.SetValue(menuItem.PersistentKey ?? "", menuItem.ActiveOption); } ProfileChanged(); @@ -200,20 +200,17 @@ namespace PowerControl if (CurrentProfileSettings is null || CurrentProfileSettings?.Exists != true) return; - int delay = CurrentProfileSettings.GetInt("ApplyDelay", -1); + int delay = CurrentProfileSettings.GetInt("ApplyDelay", ApplyProfileDelayMs); - foreach (var menuItem in MenuStack.Root.AllMenuItemOptions()) + foreach (var menuItem in PersistableOptions()) { - if (menuItem.PersistentKey is null) - continue; - - var persistedValue = CurrentProfileSettings.GetValue(menuItem.PersistentKey); + var persistedValue = CurrentProfileSettings.GetValue(menuItem.PersistentKey ?? ""); if (persistedValue is null) continue; try { - menuItem.Set(persistedValue, delay, true); + menuItem.Set(persistedValue, delay, false); Log.TraceLine("ProfilesController: Applied from Profile: {0}: {1} = {2}", CurrentProfileSettings.ProfileName, menuItem.PersistentKey, persistedValue); @@ -223,7 +220,7 @@ namespace PowerControl Log.TraceLine("ProfilesController: Exception Profile: {0}: {1} = {2} => {3}", CurrentProfileSettings.ProfileName, menuItem.PersistentKey, persistedValue, e); - CurrentProfileSettings.DeleteKey(menuItem.PersistentKey); + CurrentProfileSettings.DeleteKey(menuItem.PersistentKey ?? ""); menuItem.ProfileOption = null; } } @@ -241,21 +238,32 @@ namespace PowerControl var appliedSettings = changedSettings; changedSettings = null; - foreach (var setting in appliedSettings) + foreach (var menuItem in PersistableOptions()) { + if (!appliedSettings.TryGetValue(menuItem, out var setting)) + continue; + try { - setting.Key.Set(setting.Value); + menuItem.Set(setting, ResetProfileDelayMs, true); - Log.TraceLine("ProfilesController: Reset: {0} = {1} => {2}", - setting.Key.PersistentKey, setting.Value); + Log.TraceLine("ProfilesController: Reset: {0} = {1}", + menuItem.PersistentKey, setting); } catch (Exception e) { Log.TraceLine("ProfilesController: Reset Exception: {0} = {1} => {2}", - setting.Key.PersistentKey, setting.Value, e); + menuItem.PersistentKey, setting, e); } } } + + private IEnumerable PersistableOptions() + { + return MenuItemWithOptions. + Order(MenuStack.Root.AllMenuItemOptions()). + Where((item) => item.PersistentKey is not null). + Reverse(); + } } }