diff --git a/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/MonitorDiscoveryHelper.cs b/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/MonitorDiscoveryHelper.cs index 6e56b687fa..fe37bd36b5 100644 --- a/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/MonitorDiscoveryHelper.cs +++ b/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/DDC/MonitorDiscoveryHelper.cs @@ -140,9 +140,7 @@ namespace PowerDisplay.Common.Drivers.DDC IsAvailable = true, Handle = physicalMonitor.HPhysicalMonitor, Capabilities = MonitorCapabilities.DdcCi, - ConnectionType = "External", CommunicationMethod = "DDC/CI", - Manufacturer = ExtractManufacturer(name), CapabilitiesStatus = "unknown", MonitorNumber = monitorInfo.MonitorNumber, GdiDeviceName = monitorInfo.GdiDeviceName ?? string.Empty, @@ -160,32 +158,5 @@ namespace PowerDisplay.Common.Drivers.DDC return null; } } - - /// - /// Extract manufacturer from name - /// - private static string ExtractManufacturer(string name) - { - if (string.IsNullOrEmpty(name)) - { - return "Unknown"; - } - - // Common manufacturer prefixes - var manufacturers = new[] { "DELL", "HP", "LG", "Samsung", "ASUS", "Acer", "BenQ", "AOC", "ViewSonic" }; - var upperName = name.ToUpperInvariant(); - - foreach (var manufacturer in manufacturers) - { - if (upperName.Contains(manufacturer)) - { - return manufacturer; - } - } - - // Return first word as manufacturer - var firstWord = name.Split(' ')[0]; - return firstWord.Length > 2 ? firstWord : "Unknown"; - } } } diff --git a/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/WMI/WmiController.cs b/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/WMI/WmiController.cs index 87bd957e67..a919874536 100644 --- a/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/WMI/WmiController.cs +++ b/src/modules/powerdisplay/PowerDisplay.Lib/Drivers/WMI/WmiController.cs @@ -34,8 +34,6 @@ namespace PowerDisplay.Common.Drivers.WMI private const int WbemEInvalidQuery = unchecked((int)0x80041017); private const int WmiFeatureNotSupported = 0x1068; - private bool _disposed; - /// /// Classifies WMI exceptions into user-friendly error messages. /// @@ -54,14 +52,6 @@ namespace PowerDisplay.Common.Drivers.WMI }; } - /// - /// Determines if the WMI error is expected for systems without WMI brightness support. - /// - private static bool IsExpectedUnsupportedError(WmiException ex) - { - return ex.HResult == WmiFeatureNotSupported || ex.HResult == WbemENotFound; - } - /// /// Escape special characters in WMI query strings. /// WMI requires backslashes and single quotes to be escaped in WHERE clauses. @@ -106,6 +96,19 @@ namespace PowerDisplay.Common.Drivers.WMI return string.Empty; } + /// + /// Build a WMI query filtered by monitor instance name. + /// + /// The WMI class to query. + /// The monitor instance name to filter by. + /// Optional SELECT clause fields (defaults to "*"). + /// The formatted WMI query string. + private static string BuildInstanceNameQuery(string wmiClass, string instanceName, string selectClause = "*") + { + var escapedInstanceName = EscapeWmiString(instanceName); + return $"SELECT {selectClause} FROM {wmiClass} WHERE InstanceName = '{escapedInstanceName}'"; + } + /// /// Get MonitorDisplayInfo from dictionary by matching HardwareId. /// Uses QueryDisplayConfig path index which matches Windows Display Settings "Identify" feature. @@ -120,21 +123,21 @@ namespace PowerDisplay.Common.Drivers.WMI return null; } - foreach (var kvp in monitorDisplayInfos) + var match = monitorDisplayInfos.Values.FirstOrDefault( + v => hardwareId.Equals(v.HardwareId, StringComparison.OrdinalIgnoreCase)); + + // Check if match was found (struct default has null/empty HardwareId) + if (!string.IsNullOrEmpty(match.HardwareId)) { - if (!string.IsNullOrEmpty(kvp.Value.HardwareId) && - kvp.Value.HardwareId.Equals(hardwareId, StringComparison.OrdinalIgnoreCase)) - { - Logger.LogDebug($"WMI: Matched HardwareId '{hardwareId}' to MonitorNumber {kvp.Value.MonitorNumber}, GdiDeviceName={kvp.Value.GdiDeviceName}"); - return kvp.Value; - } + Logger.LogDebug($"WMI: Matched HardwareId '{hardwareId}' to MonitorNumber {match.MonitorNumber}, GdiDeviceName={match.GdiDeviceName}"); + return match; } Logger.LogWarning($"WMI: Could not find MonitorDisplayInfo for HardwareId '{hardwareId}'"); return null; } - public string Name => "WMI Monitor Controller (WmiLight)"; + public string Name => "WMI Monitor Controller"; /// /// Get monitor brightness @@ -149,10 +152,7 @@ namespace PowerDisplay.Common.Drivers.WMI try { using var connection = new WmiConnection(WmiNamespace); - - // Filter by InstanceName to target the specific monitor - var escapedInstanceName = EscapeWmiString(monitor.InstanceName); - var query = $"SELECT CurrentBrightness FROM {BrightnessQueryClass} WHERE InstanceName = '{escapedInstanceName}'"; + var query = BuildInstanceNameQuery(BrightnessQueryClass, monitor.InstanceName, "CurrentBrightness"); var results = connection.CreateQuery(query); foreach (var obj in results) @@ -194,10 +194,7 @@ namespace PowerDisplay.Common.Drivers.WMI try { using var connection = new WmiConnection(WmiNamespace); - - // Filter by InstanceName to target the specific monitor - var escapedInstanceName = EscapeWmiString(monitor.InstanceName); - var query = $"SELECT * FROM {BrightnessMethodClass} WHERE InstanceName = '{escapedInstanceName}'"; + var query = BuildInstanceNameQuery(BrightnessMethodClass, monitor.InstanceName); var results = connection.CreateQuery(query); foreach (var obj in results) @@ -221,10 +218,8 @@ namespace PowerDisplay.Common.Drivers.WMI { return MonitorOperationResult.Success(); } - else - { - return MonitorOperationResult.Failure($"WMI method returned error code: {result}", (int)result); - } + + return MonitorOperationResult.Failure($"WMI method returned error code: {result}", (int)result); } } @@ -338,9 +333,7 @@ namespace PowerDisplay.Common.Drivers.WMI IsAvailable = true, InstanceName = instanceName, Capabilities = MonitorCapabilities.Brightness | MonitorCapabilities.Wmi, - ConnectionType = "Internal", CommunicationMethod = "WMI", - Manufacturer = edidId.Length >= 3 ? edidId.Substring(0, 3) : "Internal", SupportsColorTemperature = false, MonitorNumber = monitorNumber, GdiDeviceName = gdiDeviceName, @@ -404,38 +397,6 @@ namespace PowerDisplay.Common.Drivers.WMI return null; } - /// - /// Check WMI service availability - /// - public static bool IsWmiAvailable() - { - try - { - using var connection = new WmiConnection(WmiNamespace); - var query = $"SELECT * FROM {BrightnessQueryClass}"; - var results = connection.CreateQuery(query).ToList(); - return results.Count > 0; - } - catch (WmiException ex) when (IsExpectedUnsupportedError(ex)) - { - // Expected on systems without WMI brightness support (desktops, some laptops) - Logger.LogInfo("WMI brightness control not supported on this system (expected for desktops)"); - return false; - } - catch (WmiException ex) - { - // Unexpected WMI error - log with details for debugging - Logger.LogWarning($"WMI availability check failed: {ex.Message} (HResult: 0x{ex.HResult:X})"); - return false; - } - catch (Exception ex) - { - // Unexpected non-WMI error - Logger.LogDebug($"WMI availability check failed: {ex.Message}"); - return false; - } - } - // Extended features not supported by WMI public Task SetContrastAsync(Monitor monitor, int contrast, CancellationToken cancellationToken = default) { @@ -471,17 +432,9 @@ namespace PowerDisplay.Common.Drivers.WMI public void Dispose() { - Dispose(true); + // WmiLight objects are created per-operation and disposed immediately via using statements. + // No instance-level resources require cleanup. GC.SuppressFinalize(this); } - - protected virtual void Dispose(bool disposing) - { - if (!_disposed && disposing) - { - // WmiLight objects are automatically cleaned up, no specific cleanup needed here - _disposed = true; - } - } } } diff --git a/src/modules/powerdisplay/PowerDisplay.Lib/Interfaces/IMonitorController.cs b/src/modules/powerdisplay/PowerDisplay.Lib/Interfaces/IMonitorController.cs index 54cebc1dcc..965e62d453 100644 --- a/src/modules/powerdisplay/PowerDisplay.Lib/Interfaces/IMonitorController.cs +++ b/src/modules/powerdisplay/PowerDisplay.Lib/Interfaces/IMonitorController.cs @@ -102,8 +102,4 @@ namespace PowerDisplay.Common.Interfaces /// void Dispose(); } - - // IMonitorManager interface removed - YAGNI principle - // Only one implementation exists (MonitorManager), so interface abstraction is unnecessary - // This simplifies the codebase and eliminates maintenance overhead } diff --git a/src/modules/powerdisplay/PowerDisplay.Lib/Models/Monitor.cs b/src/modules/powerdisplay/PowerDisplay.Lib/Models/Monitor.cs index dd573cd843..3db2bd1310 100644 --- a/src/modules/powerdisplay/PowerDisplay.Lib/Models/Monitor.cs +++ b/src/modules/powerdisplay/PowerDisplay.Lib/Models/Monitor.cs @@ -26,7 +26,7 @@ namespace PowerDisplay.Common.Models private bool _isAvailable = true; /// - /// Unique identifier for all purposes: UI lookups, IPC, persistent storage, and handle management. + /// Gets or sets unique identifier for all purposes: UI lookups, IPC, persistent storage, and handle management. /// /// /// Format: "{Source}_{EdidId}_{MonitorNumber}" where Source is "DDC" or "WMI". @@ -36,12 +36,12 @@ namespace PowerDisplay.Common.Models public string Id { get; set; } = string.Empty; /// - /// Display name + /// Gets or sets display name /// public string Name { get; set; } = string.Empty; /// - /// Current brightness (0-100) + /// Gets or sets current brightness (0-100) /// public int CurrentBrightness { @@ -58,17 +58,17 @@ namespace PowerDisplay.Common.Models } /// - /// Minimum brightness value + /// Gets or sets minimum brightness value /// public int MinBrightness { get; set; } /// - /// Maximum brightness value + /// Gets or sets maximum brightness value /// public int MaxBrightness { get; set; } = 100; /// - /// Current color temperature VCP preset value (from VCP code 0x14). + /// Gets or sets current color temperature VCP preset value (from VCP code 0x14). /// This stores the raw VCP value (e.g., 0x05 for 6500K), not Kelvin temperature. /// Use ColorTemperaturePresetName to get human-readable name. /// @@ -87,18 +87,18 @@ namespace PowerDisplay.Common.Models } /// - /// Human-readable color temperature preset name (e.g., "6500K (0x05)", "sRGB (0x01)") + /// Gets human-readable color temperature preset name (e.g., "6500K (0x05)", "sRGB (0x01)") /// public string ColorTemperaturePresetName => VcpValueNames.GetFormattedName(0x14, CurrentColorTemperature); /// - /// Whether supports color temperature adjustment via VCP 0x14 + /// Gets or sets a value indicating whether whether supports color temperature adjustment via VCP 0x14 /// public bool SupportsColorTemperature { get; set; } /// - /// Current input source VCP value (from VCP code 0x60). + /// Gets or sets current input source VCP value (from VCP code 0x60). /// This stores the raw VCP value (e.g., 0x11 for HDMI-1). /// Use InputSourceName to get human-readable name. /// @@ -117,35 +117,35 @@ namespace PowerDisplay.Common.Models } /// - /// Human-readable input source name (e.g., "HDMI-1", "DisplayPort-1") + /// Gets human-readable input source name (e.g., "HDMI-1", "DisplayPort-1") /// Returns just the name without hex value for cleaner UI display. /// public string InputSourceName => VcpValueNames.GetName(0x60, CurrentInputSource) ?? $"Source 0x{CurrentInputSource:X2}"; /// - /// Whether supports input source switching via VCP 0x60 + /// Gets a value indicating whether whether supports input source switching via VCP 0x60 /// public bool SupportsInputSource => VcpCapabilitiesInfo?.SupportsVcpCode(0x60) ?? false; /// - /// Get supported input sources from capabilities (as list of VCP values) + /// Gets get supported input sources from capabilities (as list of VCP values) /// public System.Collections.Generic.IReadOnlyList? SupportedInputSources => VcpCapabilitiesInfo?.GetSupportedValues(0x60); /// - /// Capabilities detection status: "available", "unavailable", or "unknown" + /// Gets or sets capabilities detection status: "available", "unavailable", or "unknown" /// public string CapabilitiesStatus { get; set; } = "unknown"; /// - /// Whether supports contrast adjustment + /// Gets a value indicating whether whether supports contrast adjustment /// public bool SupportsContrast => Capabilities.HasFlag(MonitorCapabilities.Contrast); /// - /// Whether supports volume adjustment (for audio-capable monitors) + /// Gets a value indicating whether whether supports volume adjustment (for audio-capable monitors) /// public bool SupportsVolume => Capabilities.HasFlag(MonitorCapabilities.Volume); @@ -153,7 +153,7 @@ namespace PowerDisplay.Common.Models private int _currentVolume = 50; /// - /// Current contrast (0-100) + /// Gets or sets current contrast (0-100) /// public int CurrentContrast { @@ -170,17 +170,17 @@ namespace PowerDisplay.Common.Models } /// - /// Minimum contrast value + /// Gets or sets minimum contrast value /// public int MinContrast { get; set; } /// - /// Maximum contrast value + /// Gets or sets maximum contrast value /// public int MaxContrast { get; set; } = 100; /// - /// Current volume (0-100) + /// Gets or sets current volume (0-100) /// public int CurrentVolume { @@ -197,17 +197,17 @@ namespace PowerDisplay.Common.Models } /// - /// Minimum volume value + /// Gets or sets minimum volume value /// public int MinVolume { get; set; } /// - /// Maximum volume value + /// Gets or sets maximum volume value /// public int MaxVolume { get; set; } = 100; /// - /// Whether available/online + /// Gets or sets a value indicating whether whether available/online /// public bool IsAvailable { @@ -223,47 +223,37 @@ namespace PowerDisplay.Common.Models } /// - /// Physical monitor handle (for DDC/CI) + /// Gets or sets physical monitor handle (for DDC/CI) /// public IntPtr Handle { get; set; } = IntPtr.Zero; /// - /// Instance name (used by WMI) + /// Gets or sets instance name (used by WMI) /// public string InstanceName { get; set; } = string.Empty; /// - /// Manufacturer information - /// - public string Manufacturer { get; set; } = string.Empty; - - /// - /// Connection type (HDMI, DP, VGA, etc.) - /// - public string ConnectionType { get; set; } = string.Empty; - - /// - /// Communication method (DDC/CI, WMI, HDR API, etc.) + /// Gets or sets communication method (DDC/CI, WMI, HDR API, etc.) /// public string CommunicationMethod { get; set; } = string.Empty; /// - /// Supported control methods + /// Gets or sets supported control methods /// public MonitorCapabilities Capabilities { get; set; } = MonitorCapabilities.None; /// - /// Raw DDC/CI capabilities string (MCCS format) + /// Gets or sets raw DDC/CI capabilities string (MCCS format) /// public string? CapabilitiesRaw { get; set; } /// - /// Parsed VCP capabilities information + /// Gets or sets parsed VCP capabilities information /// public VcpCapabilities? VcpCapabilitiesInfo { get; set; } /// - /// Last update time + /// Gets or sets last update time /// public DateTime LastUpdate { get; set; } = DateTime.Now; diff --git a/src/modules/powerdisplay/PowerDisplay.Lib/Models/MonitorOperationResult.cs b/src/modules/powerdisplay/PowerDisplay.Lib/Models/MonitorOperationResult.cs index 49f3a84928..dffd0f747b 100644 --- a/src/modules/powerdisplay/PowerDisplay.Lib/Models/MonitorOperationResult.cs +++ b/src/modules/powerdisplay/PowerDisplay.Lib/Models/MonitorOperationResult.cs @@ -12,22 +12,22 @@ namespace PowerDisplay.Common.Models public readonly struct MonitorOperationResult { /// - /// Whether the operation was successful + /// Gets a value indicating whether whether the operation was successful /// public bool IsSuccess { get; } /// - /// Error message + /// Gets error message /// public string? ErrorMessage { get; } /// - /// System error code + /// Gets system error code /// public int? ErrorCode { get; } /// - /// Operation timestamp + /// Gets operation timestamp /// public DateTime Timestamp { get; } diff --git a/src/modules/powerdisplay/PowerDisplay.Lib/Models/VcpCapabilities.cs b/src/modules/powerdisplay/PowerDisplay.Lib/Models/VcpCapabilities.cs index 6ddbcd81e1..fa67152bfb 100644 --- a/src/modules/powerdisplay/PowerDisplay.Lib/Models/VcpCapabilities.cs +++ b/src/modules/powerdisplay/PowerDisplay.Lib/Models/VcpCapabilities.cs @@ -13,47 +13,47 @@ namespace PowerDisplay.Common.Models public class VcpCapabilities { /// - /// Raw capabilities string (MCCS format) + /// Gets or sets raw capabilities string (MCCS format) /// public string Raw { get; set; } = string.Empty; /// - /// Monitor model name from capabilities + /// Gets or sets monitor model name from capabilities /// public string? Model { get; set; } /// - /// Monitor type from capabilities (e.g., "LCD") + /// Gets or sets monitor type from capabilities (e.g., "LCD") /// public string? Type { get; set; } /// - /// MCCS protocol version + /// Gets or sets mCCS protocol version /// public string? Protocol { get; set; } /// - /// MCCS version (e.g., "2.2", "2.1") + /// Gets or sets mCCS version (e.g., "2.2", "2.1") /// public string? MccsVersion { get; set; } /// - /// Supported command codes + /// Gets or sets supported command codes /// public List SupportedCommands { get; set; } = new(); /// - /// Supported VCP codes with their information + /// Gets or sets supported VCP codes with their information /// public Dictionary SupportedVcpCodes { get; set; } = new(); /// - /// Window capabilities for PIP/PBP support + /// Gets or sets window capabilities for PIP/PBP support /// public List Windows { get; set; } = new(); /// - /// Check if display supports PIP/PBP windows + /// Gets a value indicating whether check if display supports PIP/PBP windows /// public bool HasWindowSupport => Windows.Count > 0; @@ -119,7 +119,7 @@ namespace PowerDisplay.Common.Models } /// - /// Creates an empty capabilities object + /// Gets creates an empty capabilities object /// public static VcpCapabilities Empty => new(); @@ -135,27 +135,27 @@ namespace PowerDisplay.Common.Models public readonly struct VcpCodeInfo { /// - /// VCP code (e.g., 0x10 for brightness) + /// Gets vCP code (e.g., 0x10 for brightness) /// public byte Code { get; } /// - /// Human-readable name of the VCP code + /// Gets human-readable name of the VCP code /// public string Name { get; } /// - /// Supported discrete values (empty if continuous range) + /// Gets supported discrete values (empty if continuous range) /// public IReadOnlyList SupportedValues { get; } /// - /// Whether this VCP code has discrete values + /// Gets a value indicating whether whether this VCP code has discrete values /// public bool HasDiscreteValues => SupportedValues.Count > 0; /// - /// Whether this VCP code supports a continuous range + /// Gets a value indicating whether whether this VCP code supports a continuous range /// public bool IsContinuous => SupportedValues.Count == 0; @@ -193,12 +193,12 @@ namespace PowerDisplay.Common.Models public readonly struct WindowSize { /// - /// Width in pixels + /// Gets width in pixels /// public int Width { get; } /// - /// Height in pixels + /// Gets height in pixels /// public int Height { get; } @@ -217,32 +217,32 @@ namespace PowerDisplay.Common.Models public readonly struct WindowArea { /// - /// Top-left X coordinate + /// Gets top-left X coordinate /// public int X1 { get; } /// - /// Top-left Y coordinate + /// Gets top-left Y coordinate /// public int Y1 { get; } /// - /// Bottom-right X coordinate + /// Gets bottom-right X coordinate /// public int X2 { get; } /// - /// Bottom-right Y coordinate + /// Gets bottom-right Y coordinate /// public int Y2 { get; } /// - /// Width of the area + /// Gets width of the area /// public int Width => X2 - X1; /// - /// Height of the area + /// Gets height of the area /// public int Height => Y2 - Y1; @@ -263,32 +263,32 @@ namespace PowerDisplay.Common.Models public readonly struct WindowCapability { /// - /// Window number (1, 2, 3, etc.) + /// Gets window number (1, 2, 3, etc.) /// public int WindowNumber { get; } /// - /// Window type (e.g., "PIP", "PBP") + /// Gets window type (e.g., "PIP", "PBP") /// public string Type { get; } /// - /// Window area coordinates + /// Gets window area coordinates /// public WindowArea Area { get; } /// - /// Maximum window size + /// Gets maximum window size /// public WindowSize MaxSize { get; } /// - /// Minimum window size + /// Gets minimum window size /// public WindowSize MinSize { get; } /// - /// Window identifier + /// Gets window identifier /// public int WindowId { get; } diff --git a/src/modules/powerdisplay/PowerDisplay.Lib/Services/LightSwitchListener.cs b/src/modules/powerdisplay/PowerDisplay.Lib/Services/LightSwitchListener.cs index a3a1c9326e..14da36adc1 100644 --- a/src/modules/powerdisplay/PowerDisplay.Lib/Services/LightSwitchListener.cs +++ b/src/modules/powerdisplay/PowerDisplay.Lib/Services/LightSwitchListener.cs @@ -20,7 +20,7 @@ namespace PowerDisplay.Common.Services /// public sealed partial class LightSwitchListener : IDisposable { - private const string LogPrefix = "[LightSwitch Integration]"; + private const string LogPrefix = "[LightSwitch Listener]"; private Thread? _listenerThread; private CancellationTokenSource? _cancellationTokenSource; diff --git a/src/modules/powerdisplay/PowerDisplay/Helpers/MonitorManager.cs b/src/modules/powerdisplay/PowerDisplay/Helpers/MonitorManager.cs index 7c2bb3aa6c..2e0c6b8a7c 100644 --- a/src/modules/powerdisplay/PowerDisplay/Helpers/MonitorManager.cs +++ b/src/modules/powerdisplay/PowerDisplay/Helpers/MonitorManager.cs @@ -61,15 +61,8 @@ namespace PowerDisplay.Helpers try { // WMI controller (internal monitors) - // First check if WMI is available - if (WmiController.IsWmiAvailable()) - { - _wmiController = new WmiController(); - } - else - { - Logger.LogWarning("WMI brightness control not available on this system"); - } + // Always create - DiscoverMonitorsAsync returns empty list if WMI is unavailable + _wmiController = new WmiController(); } catch (Exception ex) { diff --git a/src/modules/powerdisplay/PowerDisplay/ViewModels/MonitorViewModel.cs b/src/modules/powerdisplay/PowerDisplay/ViewModels/MonitorViewModel.cs index a2a2f065a9..68ae76c6d7 100644 --- a/src/modules/powerdisplay/PowerDisplay/ViewModels/MonitorViewModel.cs +++ b/src/modules/powerdisplay/PowerDisplay/ViewModels/MonitorViewModel.cs @@ -286,8 +286,6 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable } } - public string Manufacturer => _monitor.Manufacturer; - public string CommunicationMethod => _monitor.CommunicationMethod; public bool IsInternal => _monitor.CommunicationMethod == "WMI";