diff --git a/doc/devdocs/modules/powerdisplay/design.md b/doc/devdocs/modules/powerdisplay/design.md index 5df28e5900..919a720fc5 100644 --- a/doc/devdocs/modules/powerdisplay/design.md +++ b/doc/devdocs/modules/powerdisplay/design.md @@ -4,15 +4,37 @@ 1. [Background](#background) 2. [Problem Statement](#problem-statement) -3. [Goals and Non-Goals](#goals-and-non-goals) +3. [Goals](#goals) 4. [Technical Terminology](#technical-terminology) + - [DDC/CI (Display Data Channel Command Interface)](#ddcci-display-data-channel-command-interface) + - [WMI (Windows Management Instrumentation)](#wmi-windows-management-instrumentation) 5. [Architecture Overview](#architecture-overview) + - [High-Level Component Architecture](#high-level-component-architecture) + - [Project Structure](#project-structure) 6. [Component Design](#component-design) + - [PowerDisplay Module Internal Structure](#powerdisplay-module-internal-structure) + - [DisplayChangeWatcher - Monitor Hot-Plug Detection](#displaychangewatcher---monitor-hot-plug-detection) + - [DDC/CI and WMI Interaction Architecture](#ddcci-and-wmi-interaction-architecture) + - [IMonitorController Interface Methods](#imonitorcontroller-interface-methods) + - [Why WmiLight Instead of System.Management](#why-wmilight-instead-of-systemmanagement) + - [Why We Need an MCCS Capabilities String Parser](#why-we-need-an-mccs-capabilities-string-parser) - [Monitor Identification: Handles, IDs, and Names](#monitor-identification-handles-ids-and-names) + - [Settings UI and PowerDisplay Interaction Architecture](#settings-ui-and-powerdisplay-interaction-architecture) + - [Windows Events for IPC](#windows-events-for-ipc) + - [LightSwitch Profile Integration Architecture](#lightswitch-profile-integration-architecture) + - [LightSwitch Settings JSON Structure](#lightswitch-settings-json-structure) 7. [Data Flow and Communication](#data-flow-and-communication) + - [Monitor Discovery Flow](#monitor-discovery-flow) 8. [Sequence Diagrams](#sequence-diagrams) -9. [Data Models](#data-models) -10. [Future Considerations](#future-considerations) + - [Sequence: Modifying Monitor Settings in Settings UI](#sequence-modifying-monitor-settings-in-settings-ui) + - [Sequence: Creating and Saving a Profile](#sequence-creating-and-saving-a-profile) + - [Sequence: Applying Profile via LightSwitch Theme Change](#sequence-applying-profile-via-lightswitch-theme-change) + - [Sequence: UI Slider Adjustment (Brightness)](#sequence-ui-slider-adjustment-brightness) + - [Sequence: Module Enable/Disable Lifecycle](#sequence-module-enabledisable-lifecycle) +9. [Future Considerations](#future-considerations) + - [Already Implemented](#already-implemented) + - [Potential Future Enhancements](#potential-future-enhancements) +10. [References](#references) --- @@ -88,10 +110,6 @@ PowerDisplay relies on the monitor-reported capabilities string to determine sup | `0x60` | Input Source | Active video input (HDMI, DP, USB-C, etc.) | | `0x62` | Volume | Speaker/headphone volume (0-100) | -**Official Documentation:** -- [VESA DDC/CI Standard](https://vesa.org/vesa-standards/) - - --- ### WMI (Windows Management Instrumentation) @@ -101,10 +119,6 @@ providing a standardized interface for accessing management information in Windo For display control, WMI is primarily used for laptop internal displays that may not support DDC/CI. -**Official Documentation:** -- [WMI Reference](https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmi-reference) -- [WmiMonitorBrightness](https://learn.microsoft.com/en-us/windows/win32/wmicoreprov/wmimonitorbrightness) - --- ## Architecture Overview @@ -525,6 +539,146 @@ classDiagram --- +### Why WmiLight Instead of System.Management + +PowerDisplay uses the [WmiLight](https://github.com/MartinKuschnik/WmiLight) NuGet package +for WMI operations instead of the built-in `System.Management` namespace. This decision was +driven by several technical requirements: + +#### Native AOT Compatibility + +PowerDisplay is built with Native AOT (Ahead-of-Time compilation) enabled for improved startup +performance and reduced memory footprint. The standard `System.Management` namespace is **not +compatible with Native AOT** because it relies heavily on runtime reflection and COM interop +patterns that cannot be statically analyzed. + +WmiLight provides Native AOT support since version 5.0.0, making it the appropriate choice for +AOT-compiled applications. + +```xml + + + true + + + + +``` + +#### Memory Leak Prevention + +The `System.Management` implementation has a known issue where it leaks memory on each WMI +operation. While this might be acceptable for short-lived applications, PowerDisplay runs as +a long-running background process that may perform frequent WMI queries (e.g., polling +brightness levels, responding to theme changes). WmiLight addresses this memory leak issue. + +#### Lightweight API + +WmiLight provides a simpler, more lightweight API compared to `System.Management`: + +```csharp +// WmiLight - Simple and direct +using (var connection = new WmiConnection(@"root\WMI")) +{ + var results = connection.CreateQuery("SELECT * FROM WmiMonitorBrightness"); + foreach (var obj in results) + { + var brightness = obj.GetPropertyValue("CurrentBrightness"); + } +} + +// System.Management - More verbose +using (var searcher = new ManagementObjectSearcher(@"root\WMI", "SELECT * FROM WmiMonitorBrightness")) +{ + foreach (ManagementObject obj in searcher.Get()) + { + var brightness = (byte)obj["CurrentBrightness"]; + } +} +``` + +#### Comparison Summary + +| Aspect | System.Management | WmiLight | +|--------|-------------------|----------| +| **Native AOT Support** | ❌ Not supported | ✅ Supported (v5.0.0+) | +| **Memory Leaks** | ⚠️ Leaks on remote operations | ✅ No known leaks | +| **API Complexity** | More verbose | Simpler, lighter | +| **Long-running Services** | Not recommended | ✅ Recommended | +| **Static Linking** | ❌ Not available | ✅ Optional (`PublishWmiLightStaticallyLinked`) | + +#### References + +- [WmiLight GitHub Repository](https://github.com/MartinKuschnik/WmiLight) +- [WmiLight NuGet Package](https://www.nuget.org/packages/WmiLight) + +--- + +### Why We Need an MCCS Capabilities String Parser + +DDC/CI monitors report their supported features via a **capabilities string** - a structured +text format defined by the VESA MCCS (Monitor Control Command Set) standard. This string +tells PowerDisplay which VCP codes the monitor supports and what values are valid for each. + +#### Example Capabilities String + +``` +(prot(monitor)type(lcd)model(PD3220U)cmds(01 02 03 07)vcp(10 12 14(04 05 06) 60(11 12 0F))mccs_ver(2.2)) +``` + +This string encodes: +- **Protocol**: monitor +- **Type**: LCD display +- **Model**: PD3220U +- **Supported commands**: 0x01, 0x02, 0x03, 0x07 +- **VCP codes**: 0x10 (brightness), 0x12 (contrast), 0x14 (color preset with values 4,5,6), 0x60 (input source with values 0x11, 0x12, 0x0F) +- **MCCS version**: 2.2 + +#### Why Parse It? + +| Use Case | How Parser Helps | +|----------|------------------| +| **Feature Detection** | Determine if monitor supports contrast, volume, color temperature, input switching | +| **Input Source Dropdown** | Extract valid input source values (e.g., HDMI-1=0x11, DP=0x0F) for UI dropdown | +| **Color Preset List** | Extract supported color presets (e.g., sRGB, 5000K, 6500K) | +| **Diagnostics** | Display raw VCP codes in Settings UI for troubleshooting | +| **PIP/PBP Support** | Parse window capabilities for Picture-in-Picture features | + +#### Why Not Use Regex? + +The MCCS capabilities string format has **nested parentheses** that regex cannot reliably handle: + +``` +vcp(10 12 14(04 05 06) 60(11 12 0F)) + ^^^^^^^^^^^^ nested values +``` + +A recursive descent parser properly handles: +- Nested parentheses at arbitrary depth +- Variable whitespace (some monitors use `01 02 03`, others use `010203`) +- Optional outer parentheses (some monitors omit them) +- Unknown segments (graceful skip without failing) + +#### Implementation + +PowerDisplay implements a **zero-allocation recursive descent parser** using `ref struct` and +`ReadOnlySpan` for optimal performance during monitor discovery. + +```csharp +// Usage in DdcCiController +var result = MccsCapabilitiesParser.Parse(capabilitiesString); +if (result.IsValid) +{ + monitor.VcpCapabilitiesInfo = result.Capabilities; + // Now we know which features this monitor supports +} +``` + +> **Detailed Design:** See [MCCS_PARSER_DESIGN.md](./MCCS_PARSER_DESIGN.md) for the complete +> parser architecture, grammar definition, and implementation details. + +--- + ### Monitor Identification: Handles, IDs, and Names Understanding how Windows identifies monitors is critical for PowerDisplay's operation. @@ -838,83 +992,78 @@ PowerDisplay stores `GdiDeviceName` in each `Monitor` object specifically for ro ### Settings UI and PowerDisplay Interaction Architecture ```mermaid -flowchart TB - subgraph SettingsProcess["Settings UI Process"] - SettingsPage["PowerDisplayPage.xaml"] - ViewModel["PowerDisplayViewModel"] - SettingsLib["Settings.UI.Library"] - - subgraph DataModels["Data Models"] - PowerDisplaySettings["PowerDisplaySettings"] - MonitorInfo["MonitorInfo"] - ProfileOperation["ProfileOperation"] - end +flowchart LR + subgraph SettingsUI["Settings UI Process"] + direction TB + Page["PowerDisplayPage.xaml"] + VM["PowerDisplayViewModel"] + Page --> VM end - subgraph RunnerProcess["Runner Process"] - Runner["PowerToys.exe"] - NamedPipe["Named Pipe IPC"] - ModuleInterface["PowerDisplayModuleInterface.dll"] + subgraph Runner["Runner Process"] + direction TB + Exe["PowerToys.exe"] + Pipe["Named Pipe IPC"] + Module["PowerDisplayModuleInterface.dll"] + Pipe --> Exe --> Module end - subgraph PowerDisplayProcess["PowerDisplay Process"] - App["PowerToys.PowerDisplay.exe"] + subgraph PDApp["PowerDisplay Process"] + direction TB MainVM["MainViewModel"] - - subgraph EventListeners["Event Listeners"] - RefreshEvent["RefreshMonitors Event"] - ApplyColorTempEvent["ApplyColorTemp Event"] - ApplyProfileEvent["ApplyProfile Event"] - end + Events["Event Listeners
Refresh / ColorTemp / Profile"] + Events --> MainVM end - subgraph FileSystem["File System"] - SettingsJson["PowerDisplay/settings.json"] - ProfilesJson["PowerDisplay/profiles.json"] + subgraph Storage["File System"] + direction TB + Settings[("settings.json")] + Profiles[("profiles.json")] end - %% Settings UI to Runner - SettingsPage --> ViewModel - ViewModel --> SettingsLib - ViewModel -->|"SendDefaultIPCMessage()"| NamedPipe - NamedPipe --> Runner - Runner -->|"set_config()"| ModuleInterface - Runner -->|"call_custom_action()"| ModuleInterface + %% Main flow: Settings UI → Runner → PowerDisplay + VM -->|"IPC Message"| Pipe + Module -->|"SetEvent()"| Events - %% Settings persistence - ViewModel <-->|"Read/Write"| SettingsJson - ViewModel <-->|"Read/Write"| ProfilesJson + %% File access + VM <-.->|"Read/Write"| Settings + VM <-.->|"Read/Write"| Profiles + MainVM <-.->|"Read"| Settings + MainVM <-.->|"Read/Write"| Profiles - %% Module Interface to PowerDisplay App - ModuleInterface -->|"SetEvent()"| RefreshEvent - ModuleInterface -->|"SetEvent()"| ApplyColorTempEvent - ModuleInterface -->|"SetEvent()"| ApplyProfileEvent - - %% PowerDisplay App event handling - RefreshEvent --> MainVM - ApplyColorTempEvent --> MainVM - ApplyProfileEvent --> MainVM - MainVM <-->|"Read Settings"| SettingsJson - MainVM <-->|"Read/Write Profiles"| ProfilesJson - - style SettingsProcess fill:#e3f2fd - style RunnerProcess fill:#fff3e0 - style PowerDisplayProcess fill:#e8f5e9 - style FileSystem fill:#fffde7 + style SettingsUI fill:#e3f2fd + style Runner fill:#fff3e0 + style PDApp fill:#e8f5e9 + style Storage fill:#fffde7 ``` +**Data Models (in Settings.UI.Library):** + +| Model | Purpose | +|-------|---------| +| `PowerDisplaySettings` | Main settings container with properties and pending operations | +| `MonitorInfo` | Per-monitor settings displayed in Settings UI | +| `ProfileOperation` | Pending profile apply operation | +| `ColorTemperatureOperation` | Pending color temperature change | + ### Windows Events for IPC -| Event Name | Constant | Direction | Purpose | -|------------|----------|-----------|---------| -| `Local\PowerToysPowerDisplay-ShowEvent-*` | `SHOW_POWER_DISPLAY_EVENT` | Runner → App | Show window | -| `Local\PowerToysPowerDisplay-ToggleEvent-*` | `TOGGLE_POWER_DISPLAY_EVENT` | Runner → App | Toggle visibility | -| `Local\PowerToysPowerDisplay-TerminateEvent-*` | `TERMINATE_POWER_DISPLAY_EVENT` | Runner → App | Terminate process | -| `Local\PowerToysPowerDisplay-RefreshMonitorsEvent-*` | `REFRESH_POWER_DISPLAY_MONITORS_EVENT` | Settings → App | Refresh monitor list | -| `Local\PowerToysPowerDisplay-ApplyColorTemperatureEvent-*` | `APPLY_COLOR_TEMPERATURE_POWER_DISPLAY_EVENT` | Settings → App | Apply color temp | -| `Local\PowerToysPowerDisplay-ApplyProfileEvent-*` | `APPLY_PROFILE_POWER_DISPLAY_EVENT` | Settings → App | Apply profile | -| `Local\PowerToys_LightSwitch_LightTheme` | `LightSwitchLightThemeEventName` | LightSwitch → App | Apply light mode profile | -| `Local\PowerToys_LightSwitch_DarkTheme` | `LightSwitchDarkThemeEventName` | LightSwitch → App | Apply dark mode profile | +Event names use fixed GUID suffixes to ensure uniqueness (defined in `shared_constants.h`). + +| Constant | Direction | Purpose | +|----------|-----------|---------| +| `SHOW_POWER_DISPLAY_EVENT` | Runner → App | Show window | +| `TOGGLE_POWER_DISPLAY_EVENT` | Runner → App | Toggle visibility | +| `TERMINATE_POWER_DISPLAY_EVENT` | Runner → App | Terminate process | +| `REFRESH_POWER_DISPLAY_MONITORS_EVENT` | Settings → App | Refresh monitor list | +| `APPLY_COLOR_TEMPERATURE_POWER_DISPLAY_EVENT` | Settings → App | Apply color temp | +| `APPLY_PROFILE_POWER_DISPLAY_EVENT` | Settings → App | Apply profile | +| `LightSwitchLightThemeEventName` | LightSwitch → App | Apply light mode profile | +| `LightSwitchDarkThemeEventName` | LightSwitch → App | Apply dark mode profile | + +**Event Name Format:** `Local\PowerToysPowerDisplay-{EventType}-{GUID}` + +Example: `Local\PowerToysPowerDisplay-ShowEvent-d8a4e0e3-2c5b-4a1c-9e7f-8b3d6c1a2f4e` --- @@ -1003,65 +1152,88 @@ flowchart TB ```mermaid flowchart TB - Start([Start Discovery]) --> Init["MonitorManager.DiscoverMonitorsAsync()"] + Start([Start Discovery]) + Start --> MM["MonitorManager.DiscoverMonitorsAsync()"] - Init --> ParallelDiscover["Parallel Discovery"] + MM --> DDC["DdcCiController.DiscoverMonitorsAsync()"] + MM --> WMI["WmiController.DiscoverMonitorsAsync()"] - subgraph ParallelDiscover["Parallel Controller Discovery"] - DDCDiscover["DdcCiController.DiscoverMonitorsAsync()"] - WMIDiscover["WmiController.DiscoverMonitorsAsync()"] - end + DDC --> Merge["Merge Results"] + WMI --> Merge - DDCDiscover --> DDCEnum["EnumDisplayMonitors()"] - DDCEnum --> DDCPhysical["GetPhysicalMonitorsFromHMONITOR()"] - DDCPhysical --> DDCCheck["Quick DDC/CI connection check"] + Merge --> Sort["Sort by MonitorNumber"] + Sort --> Update["Update _monitors Collection"] + Update --> Done([Discovery Complete]) - WMIDiscover --> WMIQuery["Query WmiMonitorBrightness"] - WMIQuery --> WMIFilter["Filter responsive displays"] - - DDCCheck --> Merge["Merge Results"] - WMIFilter --> Merge - - Merge --> InitLoop["For Each Monitor"] - - subgraph InitLoop["Initialize Single Monitor"] - direction TB - VerifyControl["Verify Controller Access"] - GetBrightness["Get Current Brightness"] - CheckType{"CommunicationMethod
contains 'DDC'?"} - - subgraph DDCPath[" "] - direction TB - GetCaps["Fetch VCP Capabilities"] - ParseCaps["Parse MCCS Capabilities String"] - InitInputSource["Get Current Input Source"] - end - - Done["Initialization Complete"] - end - - VerifyControl --> GetBrightness - GetBrightness --> CheckType - CheckType -->|"Yes (DDC/CI)"| GetCaps - GetCaps --> ParseCaps - ParseCaps --> InitInputSource - InitInputSource --> Done - CheckType -->|"No (WMI)"| Done - - InitLoop --> UpdateCollection["Update _monitors Collection"] - UpdateCollection --> FireEvent["Fire MonitorsChanged Event"] - FireEvent --> StartWatcher["Start DisplayChangeWatcher"] - StartWatcher --> End([Discovery Complete]) - - style ParallelDiscover fill:#e3f2fd - style InitLoop fill:#e8f5e9 - style CheckType fill:#fff3e0 + style Start fill:#e8f5e9 + style Done fill:#e8f5e9 + style DDC fill:#e3f2fd + style WMI fill:#fff3e0 ``` -**Note:** WMI monitors skip VCP capabilities fetching because: -1. WMI uses a different abstraction layer (`WmiMonitorBrightness` class) -2. `WmiController.GetCapabilitiesStringAsync()` returns an empty string -3. VCP codes are DDC/CI-specific and not applicable to WMI-controlled displays +> **Note:** DDC/CI and WMI discovery run in parallel via `Task.WhenAll`. + +#### DDC/CI Discovery (Three-Phase Approach) + +**Phase 1: Collect Candidates** + +```mermaid +flowchart LR + QDC["QueryDisplayConfig"] --> Match["Match by GDI Name"] + Enum["EnumDisplayMonitors"] --> GetPhys["GetPhysicalMonitors"] --> Match + Match --> Candidates["CandidateMonitor List"] + + style QDC fill:#e3f2fd + style Enum fill:#e3f2fd +``` + +**Phase 2: Fetch Capabilities (Parallel)** + +```mermaid +flowchart LR + Candidates["CandidateMonitor List"] --> Fetch["Task.WhenAll:
FetchCapabilities
~4s per monitor via I2C"] + Fetch --> Results["DdcCiValidationResult Array"] + + style Fetch fill:#fff3e0 +``` + +**Phase 3: Create Monitors** + +```mermaid +flowchart LR + Results["Validation Results"] --> Check{"IsValid?"} + Check -->|Yes| Create["Create Monitor"] + Create --> Init["Initialize VCP Values:
Brightness, ColorTemp, InputSource"] + Init --> Add["Add to List"] + Check -->|No| Skip([Skip]) + + style Create fill:#e8f5e9 + style Init fill:#e8f5e9 +``` + +#### WMI Discovery + +```mermaid +flowchart LR + Query["Query WmiMonitorBrightness"] --> Extract["Extract HardwareId
from InstanceName"] + QDC["QueryDisplayConfig"] --> Match["Match by HardwareId"] + Extract --> Match + Match --> Name["Get Display Name
via PnpIdHelper"] + Name --> Create["Create Monitor
Brightness + WMI"] + + style Query fill:#fff3e0 + style Create fill:#fff3e0 +``` + +#### Key Differences + +| Aspect | DDC/CI | WMI | +|--------|--------|-----| +| **Target** | External monitors | Internal laptop displays | +| **Capabilities** | Full VCP support (brightness, contrast, volume, color temp, input) | Brightness only | +| **Discovery** | Three-phase with parallel I2C fetching | Single WMI query | +| **Initialization** | Reads current values for all supported VCP codes | Brightness from query result | +| **Performance** | ~4s per monitor (I2C), parallelized | Fast (~100ms total) | --- @@ -1359,251 +1531,9 @@ sequenceDiagram --- -## Data Models - -### Core Models - -```mermaid -classDiagram - class Monitor { - +string Id - +string Name - +string CommunicationMethod - +string InstanceName - +string GdiDeviceName - +int MonitorNumber - +int CurrentBrightness - +int MinBrightness - +int MaxBrightness - +int CurrentContrast - +int MinContrast - +int MaxContrast - +int CurrentVolume - +int MinVolume - +int MaxVolume - +int CurrentColorTemperature - +string ColorTemperaturePresetName - +int CurrentInputSource - +string InputSourceName - +IReadOnlyList~int~ SupportedInputSources - +int Orientation - +bool IsAvailable - +bool SupportsContrast - +bool SupportsVolume - +bool SupportsColorTemperature - +bool SupportsInputSource - +MonitorCapabilities Capabilities - +VcpCapabilities VcpCapabilitiesInfo - +string CapabilitiesRaw - +IntPtr Handle - +DateTime LastUpdate - +UpdateStatus(brightness, isAvailable) - } - - class VcpCapabilities { - +string Raw - +string Model - +string Type - +string Protocol - +string MccsVersion - +List~byte~ SupportedCommands - +Dictionary~byte, VcpCodeInfo~ SupportedVcpCodes - +List~WindowCapability~ Windows - +bool HasWindowSupport - +static VcpCapabilities Empty$ - +SupportsVcpCode(code) bool - +GetVcpCodeInfo(code) VcpCodeInfo - +HasDiscreteValues(code) bool - +GetSupportedValues(code) IReadOnlyList~int~ - +GetVcpCodesAsHexStrings() List~string~ - +GetSortedVcpCodes() IEnumerable~VcpCodeInfo~ - } - - class VcpCodeInfo { - +byte Code - +string Name - +IReadOnlyList~int~ SupportedValues - +bool HasDiscreteValues - +bool IsContinuous - +string FormattedCode - +string FormattedTitle - } - - class WindowCapability { - <> - +int WindowNumber - +string Type - +WindowArea Area - +WindowSize MaxSize - +WindowSize MinSize - +int WindowId - } - - class WindowSize { - <> - +int Width - +int Height - } - - class WindowArea { - <> - +int X1 - +int Y1 - +int X2 - +int Y2 - +int Width - +int Height - } - - class VcpFeatureValue { - +int Current - +int Minimum - +int Maximum - +bool IsValid - +ToPercentage() int - +static Invalid VcpFeatureValue - } - - class MonitorCapabilities { - <> - None - Brightness - Contrast - Volume - ColorTemperature - InputSource - Wmi - DdcCi - } - - class PowerDisplayProfile { - +string Name - +DateTime CreatedDate - +DateTime LastModified - +List~ProfileMonitorSetting~ MonitorSettings - +IsValid() bool - } - - class ProfileMonitorSetting { - +string MonitorInternalName - +int MonitorNumber - +int? Brightness - +int? Contrast - +int? Volume - +int? ColorTemperatureVcp - +int? Orientation - } - - class PowerDisplayProfiles { - +List~PowerDisplayProfile~ Profiles - +DateTime LastUpdated - } - - Monitor "1" --> "0..1" VcpCapabilities - Monitor "1" --> "1" MonitorCapabilities - VcpCapabilities "1" --> "*" VcpCodeInfo - VcpCapabilities "1" --> "*" WindowCapability - WindowCapability "1" --> "1" WindowArea - WindowCapability "1" --> "1" WindowSize : MaxSize - WindowCapability "1" --> "1" WindowSize : MinSize - PowerDisplayProfiles "1" --> "*" PowerDisplayProfile - PowerDisplayProfile "1" --> "*" ProfileMonitorSetting -``` - -### Settings Models - -```mermaid -classDiagram - class PowerDisplaySettings { - +string Name - +PowerDisplayProperties Properties - +string Version - +ToJsonString() string - } - - class PowerDisplayProperties { - +bool Enabled - +bool HotkeyEnabled - +HotkeySettings ActivationShortcut - +string BrightnessUpdateRate - +bool RestoreSettingsOnStartup - +bool ShowSystemTrayIcon - +List~MonitorInfo~ Monitors - +ColorTemperatureOperation PendingColorTemperatureOperation - +ProfileOperation PendingProfileOperation - } - - class MonitorInfo { - +string Name - +string InternalName - +string HardwareId - +string CommunicationMethod - +int MonitorNumber - +int TotalMonitorCount - +string DisplayName - +string MonitorIconGlyph - +int CurrentBrightness - +int Contrast - +int Volume - +int ColorTemperatureVcp - +int Orientation - +bool SupportsBrightness - +bool SupportsContrast - +bool SupportsColorTemperature - +bool SupportsVolume - +bool SupportsInputSource - +bool EnableContrast - +bool EnableVolume - +bool EnableInputSource - +bool EnableRotation - +bool IsHidden - +string CapabilitiesRaw - +List~string~ VcpCodes - +List~VcpCodeDisplayInfo~ VcpCodesFormatted - +ObservableCollection~ColorPresetItem~ AvailableColorPresets - +ObservableCollection~ColorPresetItem~ ColorPresetsForDisplay - +bool HasCapabilities - +bool ShowCapabilitiesWarning - +GetVcpCodesAsText() string - +UpdateFrom(other) - } - - class VcpCodeDisplayInfo { - +string Code - +string Title - +string Values - +bool HasValues - +List~VcpValueInfo~ ValueList - } - - class VcpValueInfo { - +string Value - +string Name - } - - class ColorTemperatureOperation { - +string MonitorId - +int ColorTemperatureVcp - } - - class ProfileOperation { - +string ProfileName - +List~ProfileMonitorSetting~ MonitorSettings - } - - MonitorInfo "1" --> "*" VcpCodeDisplayInfo - VcpCodeDisplayInfo "1" --> "*" VcpValueInfo - PowerDisplaySettings "1" --> "1" PowerDisplayProperties - PowerDisplayProperties "1" --> "*" MonitorInfo - PowerDisplayProperties "1" --> "0..1" ColorTemperatureOperation - PowerDisplayProperties "1" --> "0..1" ProfileOperation -``` - ---- - ## Future Considerations -### Already Implemented (removed from backlog) +### Already Implemented - **Monitor Hot-Plug**: `DisplayChangeWatcher` uses WinRT DeviceWatcher + DisplayMonitor API with 1-second debouncing - **Display Rotation**: `DisplayRotationService` uses Windows ChangeDisplaySettingsEx API @@ -1613,15 +1543,9 @@ classDiagram ### Potential Future Enhancements -1. **Hardware Cursor Brightness**: Support for displays with hardware cursor brightness -2. **Multi-GPU Support**: Better handling of monitors across different GPUs -3. **Advanced Color Management**: Integration with Windows Color Management APIs (HDR, ICC profiles) -4. **Scheduled Profiles**: Time-based automatic profile switching (beyond LightSwitch integration) -5. **Monitor Groups**: Ability to control multiple monitors as a single entity -6. **Remote Control**: Network-based control for multi-system setups -7. **PIP/PBP Control**: Picture-in-Picture and Picture-by-Picture configuration (VcpCapabilities already parses window capabilities) -8. **Power State Control**: Monitor power on/off via VCP code 0xD6 -9. **Input Source Scheduling**: Automatic input switching based on time or application +1. **Advanced Color Management**: Integration with Windows Color Management APIs (HDR, ICC profiles) +2. **PIP/PBP Control**: Picture-in-Picture and Picture-by-Picture configuration (VcpCapabilities already parses window capabilities) +3. **Power State Control**: Monitor power on/off via VCP code 0xD6 ---