PowerControl: Base Profiles Implementation (#38)

Small refactor of menu with options

Fix protection error on menuwithoptions

Make profiles controller non static

Dynamicall set and load options

Use IsOSDForeground when retriveing current game name

Better alt-tab functionality

Get rid off thread.sleep

Merged #38
This commit is contained in:
maniman303 2022-12-20 22:52:56 +01:00 committed by Kamil Trzciński
parent ef94d24cfc
commit 3252e799cb
7 changed files with 252 additions and 12 deletions

View file

@ -7,11 +7,18 @@ namespace CommonHelpers
{
public static bool IsOSDForeground()
{
return IsOSDForeground(out _);
return IsOSDForeground(out _, out _);
}
public static bool IsOSDForeground(out int processId)
{
return IsOSDForeground(out processId, out _);
}
public static bool IsOSDForeground(out int processId, out string? applicationName)
{
applicationName = null;
try
{
var id = GetTopLevelProcessId();
@ -22,7 +29,10 @@ namespace CommonHelpers
foreach (var app in OSD.GetAppEntries(AppFlags.MASK))
{
if (app.ProcessId == processId)
{
applicationName = ExtractAppName(app.Name);
return true;
}
}
return false;
@ -76,6 +86,13 @@ namespace CommonHelpers
}
}
public static List<string> GetCurrentApps()
{
var apps = OSD.GetAppEntries(AppFlags.MASK).Select(e => ExtractAppName(e.Name)).ToList();
return apps;
}
public static uint EnableFlag(uint flag, bool status)
{
var current = SetFlags(~flag, status ? flag : 0);
@ -125,6 +142,18 @@ namespace CommonHelpers
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
private static string ExtractAppName(string fullName)
{
string res = fullName.Split('\\').Last();
if (res.ToLower().Contains(".exe"))
{
return res[..^4];
}
return res;
}
private static uint? GetTopLevelProcessId()
{
var hWnd = GetForegroundWindow();

View file

@ -33,6 +33,8 @@ namespace PowerControl
DateTime? neptuneDeviceNextKey;
System.Windows.Forms.Timer neptuneTimer;
ProfilesController profilesController;
SharedData<PowerControlSetting> sharedData = SharedData<PowerControlSetting>.CreateNew();
static Controller()
@ -110,6 +112,9 @@ namespace PowerControl
osdTimer.Interval = 250;
osdTimer.Enabled = true;
profilesController = new ProfilesController();
profilesController.Initialize();
GlobalHotKey.RegisterHotKey(Settings.Default.MenuUpKey, () =>
{
if (!RTSS.IsOSDForeground())
@ -409,6 +414,9 @@ namespace PowerControl
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(
new Action(() =>
{
Options.RefreshRate.Instance?.Reset();
Options.FPSLimit.Instance?.Reset();
rootMenu.Update();
})
);

View file

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using CommonHelpers;
namespace PowerControl.Helper
{
public class ProfileSettings : BaseSettings
{
private static string profilesPath = Path.Combine(Directory.GetCurrentDirectory(), "Profiles");
static ProfileSettings()
{
Directory.CreateDirectory(profilesPath);
}
public ProfileSettings(string profileName) : base("Profile")
{
this.TouchSettings = true;
this.ConfigFile = Path.Combine(profilesPath, profileName + ".ini");
this.SettingChanging += delegate { };
this.SettingChanged += delegate { };
}
public T Get<T>(string key, T defaultValue)
{
return base.Get(key, defaultValue);
}
public new bool Set<T>(string key, T value)
{
return base.Set(key, value);
}
public static bool CheckIfExists(string profileName)
{
foreach (FileInfo fi in Directory.CreateDirectory(profilesPath).GetFiles())
{
if (fi.Name[^4..].Equals(".ini") && fi.Name[..^4].Equals(profileName))
{
return true;
}
}
return false;
}
}
}

View file

@ -0,0 +1,137 @@
using CommonHelpers;
using PowerControl.Helper;
using PowerControl.Menu;
namespace PowerControl.Helpers
{
public class ProfilesController
{
private const string IsTroubledKey = "IsTroubled";
private const string DefaultName = "Default";
private string CurrentGame = string.Empty;
private ProfileSettings DefaultSettings = new ProfileSettings(DefaultName);
private ProfileSettings? CurrentSettings;
private static string[] troubledGames = { "dragonageinquisition" };
private System.Windows.Forms.Timer? timer;
public ProfilesController()
{
timer = new System.Windows.Forms.Timer();
}
public void Initialize()
{
MenuStack.Root.ValueChanged += OnOptionValueChange;
timer.Interval = 1000;
timer.Tick += (_, _) =>
{
timer.Stop();
RefreshProfiles();
timer.Start();
};
timer.Start();
}
private void RefreshProfiles()
{
if (!DeviceManager.IsDeckOnlyDisplay())
{
CurrentGame = string.Empty;
return;
}
string? gameName;
RTSS.IsOSDForeground(out _, out gameName);
// If there's no foreground games keep current profile if possible
if (gameName == null && RTSS.GetCurrentApps().Contains(CurrentGame))
{
gameName = CurrentGame;
}
if (gameName == null && CurrentGame != DefaultName)
{
CurrentGame = DefaultName;
CurrentSettings = null;
ApplyProfile();
}
if (gameName != null && CurrentGame != gameName)
{
CurrentGame = gameName;
CurrentSettings = ProfileSettings.CheckIfExists(CurrentGame) ?
new ProfileSettings(CurrentGame) : null;
ApplyProfile();
}
}
private void ApplyProfile()
{
int delay = GetBoolValue(IsTroubledKey) ? 5200 : 0;
var options = MenuStack.Root.Items.Where(o => o is MenuItemWithOptions).Select(o => (MenuItemWithOptions)o).ToList();
foreach (var option in options)
{
string? key = option.PersistentKey;
if (key != null)
{
option.Set(GetValue(option), delay, true);
}
}
}
private void OnOptionValueChange(MenuItemWithOptions options, string? oldValue, string newValue)
{
string? key = options.PersistentKey;
if (key != null)
{
SetValue(key, newValue);
}
}
private void SetBoolValue(string key, bool value)
{
var settings = CurrentSettings ?? DefaultSettings;
settings.Set(key, value);
}
private bool GetBoolValue(string key)
{
var settings = CurrentSettings ?? DefaultSettings;
return settings.Get(key, false);
}
private void SetValue(string key, string value)
{
var settings = CurrentSettings ?? DefaultSettings;
settings.Set(key, value);
}
private string GetValue(MenuItemWithOptions option)
{
if (CurrentSettings == null)
{
return GetDefaultValue(option);
}
return CurrentSettings.Get(option.PersistentKey, GetDefaultValue(option));
}
private string GetDefaultValue(MenuItemWithOptions option)
{
return DefaultSettings.Get(option.PersistentKey, option.ResetValue?.Invoke() ?? string.Empty);
}
}
}

View file

@ -7,10 +7,12 @@ namespace PowerControl.Menu
public string? ActiveOption { get; set; }
public int ApplyDelay { get; set; }
public bool CycleOptions { get; set; } = true;
public string? PersistentKey;
public Func<string?>? CurrentValue { get; set; }
public Func<string[]?>? OptionsValues { get; set; }
public Func<string, string?>? ApplyValue { get; set; }
public Action? AfterApply { get; set; }
public Func<string?>? ResetValue { get; set; }
public event Action<MenuItemWithOptions, String?, String> ValueChanged;
@ -29,7 +31,7 @@ namespace PowerControl.Menu
if (delayTimer != null)
delayTimer.Stop();
FinalizeSet();
FinalizeSet(delayTimer.Interval > ApplyDelay);
};
}
@ -42,7 +44,7 @@ namespace PowerControl.Menu
if (resetOption == null || resetOption == ActiveOption)
return;
Set(resetOption, true);
Set(resetOption, 0);
}
public override void Update()
@ -74,35 +76,40 @@ namespace PowerControl.Menu
ActiveOption = Options.First();
}
public void Set(String value, bool immediately = false)
public void Set(String value, int overrideDelay = -1, bool silent = false)
{
if (delayTimer != null)
delayTimer.Stop();
SelectedOption = value;
if (ApplyDelay == 0 || immediately)
if (ApplyDelay == 0 || overrideDelay == 0)
{
FinalizeSet();
FinalizeSet(silent);
return;
}
delayTimer.Interval = ApplyDelay > 0 ? ApplyDelay : 1;
delayTimer.Interval = overrideDelay > 0 ? overrideDelay : ApplyDelay > 0 ? ApplyDelay : 1;
delayTimer.Enabled = true;
}
private void FinalizeSet()
private void FinalizeSet(bool silent = false)
{
var wasOption = ActiveOption;
if (ApplyValue != null && SelectedOption != null)
{
ActiveOption = ApplyValue(SelectedOption);
if (AfterApply != null && !silent)
AfterApply();
}
else
ActiveOption = SelectedOption;
SelectedOption = null;
if (wasOption != ActiveOption && ActiveOption != null)
if (wasOption != ActiveOption && ActiveOption != null && !silent)
ValueChanged(this, wasOption, ActiveOption);
}
@ -120,7 +127,7 @@ namespace PowerControl.Menu
{
var item = new ToolStripMenuItem(option);
item.Checked = option == (SelectedOption ?? ActiveOption);
item.Click += delegate { Set(option, true); };
item.Click += delegate { Set(option, 0); };
toolStripItem.DropDownItems.Add(item);
}
@ -133,7 +140,7 @@ namespace PowerControl.Menu
if (Options.Count == 0)
return;
Set(Options[Math.Clamp(index, 0, Options.Count - 1)], false);
Set(Options[Math.Clamp(index, 0, Options.Count - 1)]);
}
public override void SelectNext(int change)

View file

@ -8,6 +8,7 @@ namespace PowerControl.Options
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "FPS Limit",
PersistentKey = "FPSLimit",
ApplyDelay = 500,
ResetValue = () => { return "Off"; },
OptionsValues = delegate ()

View file

@ -8,6 +8,7 @@ namespace PowerControl.Options
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "Refresh Rate",
PersistentKey = "RefreshRate",
ApplyDelay = 1000,
ResetValue = () => { return DisplayResolutionController.GetRefreshRates().Max().ToString(); },
OptionsValues = delegate ()
@ -24,10 +25,14 @@ namespace PowerControl.Options
ApplyValue = (selected) =>
{
DisplayResolutionController.SetRefreshRate(int.Parse(selected));
return DisplayResolutionController.GetRefreshRate().ToString();
},
AfterApply = () =>
{
// force reset and refresh of FPS limit
FPSLimit.Instance.Reset();
FPSLimit.Instance.Update();
return DisplayResolutionController.GetRefreshRate().ToString();
}
};
}