diff --git a/RELEASE.md b/RELEASE.md index 138f34e..8367fa0 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -9,6 +9,7 @@ ## 0.6.x +- SteamController: Improve detection of Steam processes (especially latest controller UI changes) - SteamController: Add configuration wizard for the first time or when configuration was lost - PowerControl: Show current time - PowerControl: Consider the foreground process to be holding profile configuration as long as it is running diff --git a/SteamController/Helpers/SteamConfiguration.cs b/SteamController/Helpers/SteamConfiguration.cs index a419fe5..079857f 100644 --- a/SteamController/Helpers/SteamConfiguration.cs +++ b/SteamController/Helpers/SteamConfiguration.cs @@ -51,52 +51,6 @@ namespace SteamController.Helpers } } - public static bool IsGamePadUI - { - get - { - var steamWindow = FindWindow("SDL_app", "SP"); - // Support Steam client released around 2023-01-20, version: 1674182294 - if (steamWindow == IntPtr.Zero) - steamWindow = FindWindow("SDL_app", "Steam Big Picture Mode"); - if (steamWindow == IntPtr.Zero) - return false; - - return GetForegroundWindow() == steamWindow; - } - } - - public static bool IsPossibleGamePadUI - { - get - { - IntPtr hWnd = GetForegroundWindow(); - if (hWnd == IntPtr.Zero) - return false; - - StringBuilder className = new StringBuilder(256); - if (GetClassName(hWnd, className, className.Capacity) == 0) - return false; - if (className.ToString() != "SDL_app") - return false; - - StringBuilder windowText = new StringBuilder(256); - if (GetWindowText(hWnd, windowText, windowText.Capacity) == 0) - return false; - - bool valid = false; - - // Support old Steam Clients - valid = valid || windowText.ToString().StartsWith("SP"); - - // Support Steam client released around 2023-01-20, version: 1674182294 - // TODO: It appears that controller is not consumed when outside of big picture mode - // valid = valid || windowText.ToString().StartsWith("Controller Layout"); - - return valid && ForegroundProcess.Find(hWnd)?.ProcessName == "steamwebhelper"; - } - } - public static String? SteamExe { get { return GetValue2(SteamKey, SteamExeValue); } @@ -446,17 +400,5 @@ namespace SteamController.Helpers return null; } } - - [DllImport("user32.dll")] - private static extern IntPtr GetForegroundWindow(); - - [DllImport("user32.dll", SetLastError = true)] - private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); - - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); - - [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] - private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); } } \ No newline at end of file diff --git a/SteamController/Managers/SteamManager.cs b/SteamController/Managers/SteamManager.cs index 17ad8e1..ce19def 100644 --- a/SteamController/Managers/SteamManager.cs +++ b/SteamController/Managers/SteamManager.cs @@ -1,10 +1,23 @@ using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; using SteamController.Helpers; namespace SteamController.Managers { public sealed class SteamManager : Manager { + private static readonly Classifier[] Classifiers = new Classifier[] + { + new Classifier() { Type = "gamepadui", ClassName = "SDL_app", WindowText = "SP", ProcessName = "steamwebhelper" }, + new Classifier() { Type = "possiblegamepadui", ClassName = "SDL_app", WindowTextPrefix = "SP", ProcessName = "steamwebhelper" }, + + // Support Steam client released around 2023-01-20, version: 1674182294 + new Classifier() { Type = "gamepadui_2023_01_20", ClassName = "SDL_app", WindowText = "Steam Big Picture Mode", ProcessName = "steamwebhelper" }, + new Classifier() { Type = "controllerui_2023_01_20", ClassName = "CUIEngineWin32", ProcessName = "steam" }, + // new Classifier() { Type = "possiblegamepadui_2023_01_20", ClassName = "SDL_app", WindowTextSuffix = "Controller Layout", ProcessName = "steamwebhelper" }, + }; + public const int DebounceStates = 1; private string? lastState; @@ -73,11 +86,73 @@ namespace SteamController.Managers return "bigpicture"; if (SteamConfiguration.IsRunningGame.GetValueOrDefault(false)) return "game"; - if (SteamConfiguration.IsGamePadUI) - return "gamepadui"; - if (SteamConfiguration.IsPossibleGamePadUI) - return "possiblegamepadui"; + return ClassifyForegroundProcess(); + } + + private string? ClassifyForegroundProcess() + { + IntPtr hWnd = GetForegroundWindow(); + if (hWnd == IntPtr.Zero) + return null; + + StringBuilder classNameBuilder = new StringBuilder(256); + if (GetClassName(hWnd, classNameBuilder, classNameBuilder.Capacity) == 0) + return null; + var className = classNameBuilder.ToString(); + + StringBuilder windowTextBuilder = new StringBuilder(256); + if (GetWindowText(hWnd, windowTextBuilder, windowTextBuilder.Capacity) == 0) + return null; + var windowText = windowTextBuilder.ToString(); + + var processName = ForegroundProcess.Find(hWnd)?.ProcessName; + if (processName is null) + return null; + + foreach (var classifier in Classifiers) + { + if (classifier.Match(className, windowText, processName)) + return classifier.Type; + } + return null; } + + private struct Classifier + { + public String Type { get; set; } + public String? ClassName { get; set; } + public String? WindowText { get; set; } + public String? WindowTextPrefix { get; set; } + public String? WindowTextSuffix { get; set; } + public String? ProcessName { get; set; } + + public bool Match(string className, string windowText, string processName) + { + if (ClassName is not null && className != ClassName) + return false; + if (WindowText is not null && windowText != WindowText) + return false; + if (WindowTextPrefix is not null && !windowText.StartsWith(WindowTextPrefix)) + return false; + if (WindowTextSuffix is not null && !windowText.EndsWith(WindowTextSuffix)) + return false; + if (ProcessName is not null && !processName.EndsWith(ProcessName)) + return false; + return true; + } + } + + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll", SetLastError = true)] + private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); } }