PowerDisplay: overhaul design docs, remove obsolete IPC code

Expanded and clarified PowerDisplay design documentation:
- Added detailed section on monitor identification (handles, IDs, MST, clone mode, etc.) with diagrams and tables.
- Updated architecture, flowcharts, and class diagrams to reflect new helpers, services, and improved data flows.
- Expanded internal structure details for PowerDisplay.Lib and app, listing new helpers, models, and utilities.
- Updated sequence diagrams for LightSwitch integration and Settings UI interaction.
- Revised future considerations, marking hot-plug and rotation as implemented, and adding new feature ideas.

Removed all obsolete PowerDisplay IPC forwarding code from settings_window.cpp/h, including the send_powerdisplay_message_to_settings_ui function and related JSON dispatch logic. This reflects a refactor or deprecation of the previous custom IPC mechanism.
This commit is contained in:
Yu Leng
2025-12-12 09:44:28 +08:00
parent 865dd60a83
commit 4c93fe01d0
3 changed files with 529 additions and 179 deletions

View File

@@ -8,6 +8,7 @@
4. [Technical Terminology](#technical-terminology) 4. [Technical Terminology](#technical-terminology)
5. [Architecture Overview](#architecture-overview) 5. [Architecture Overview](#architecture-overview)
6. [Component Design](#component-design) 6. [Component Design](#component-design)
- [Monitor Identification: Handles, IDs, and Names](#monitor-identification-handles-ids-and-names)
7. [Data Flow and Communication](#data-flow-and-communication) 7. [Data Flow and Communication](#data-flow-and-communication)
8. [Sequence Diagrams](#sequence-diagrams) 8. [Sequence Diagrams](#sequence-diagrams)
9. [Data Models](#data-models) 9. [Data Models](#data-models)
@@ -119,26 +120,28 @@ flowchart TB
end end
subgraph PowerDisplayModule["PowerDisplay Module"] subgraph PowerDisplayModule["PowerDisplay Module"]
ModuleInterface["Module Interface (C++ DLL)"] ModuleInterface["Module Interface<br/>(PowerDisplayModuleInterface.dll)"]
PowerDisplayApp["PowerDisplay App (WinUI 3)"] PowerDisplayApp["PowerDisplay App<br/>(PowerToys.PowerDisplay.exe)"]
PowerDisplayLib["PowerDisplay.Lib"] PowerDisplayLib["PowerDisplay.Lib<br/>(Shared Library)"]
end end
subgraph External["External"] subgraph External["External"]
Hardware["Display Hardware"] Hardware["Display Hardware<br/>(External + Internal)"]
Storage["Persistent Storage (JSON)"] Storage["Persistent Storage<br/>(settings.json, profiles.json)"]
end end
Runner -->|"Loads DLL"| ModuleInterface Runner -->|"Loads DLL"| ModuleInterface
Runner -->|"Hotkey Events"| ModuleInterface Runner -->|"Hotkey Events"| ModuleInterface
SettingsUI <-->|"Named Pipes"| Runner SettingsUI <-->|"Named Pipes"| Runner
SettingsUI -->|"Custom Actions"| ModuleInterface SettingsUI -->|"Custom Actions<br/>(Launch, RefreshMonitors,<br/>ApplyColorTemperature, ApplyProfile)"| ModuleInterface
ModuleInterface <-->|"Windows Events"| PowerDisplayApp ModuleInterface <-->|"Windows Events<br/>(Show/Toggle/Terminate/Refresh)"| PowerDisplayApp
LightSwitch -->|"Theme Changed Event"| PowerDisplayApp LightSwitch -->|"Theme Events<br/>(Light/Dark)"| PowerDisplayApp
PowerDisplayApp --> PowerDisplayLib PowerDisplayApp --> PowerDisplayLib
PowerDisplayLib -->|"DDC/CI + WMI"| Hardware PowerDisplayLib -->|"DDC/CI (Dxva2.dll)"| Hardware
PowerDisplayLib -->|"WMI (WmiLight)"| Hardware
PowerDisplayLib -->|"ChangeDisplaySettingsEx"| Hardware
PowerDisplayApp <--> Storage PowerDisplayApp <--> Storage
style Runner fill:#e1f5fe style Runner fill:#e1f5fe
@@ -164,60 +167,100 @@ src/modules/powerdisplay/
│ ├── Drivers/ │ ├── Drivers/
│ │ ├── DDC/ │ │ ├── DDC/
│ │ │ ├── DdcCiController.cs # DDC/CI implementation │ │ │ ├── DdcCiController.cs # DDC/CI implementation
│ │ │ ├── DdcCiNative.cs # P/Invoke declarations │ │ │ ├── DdcCiNative.cs # P/Invoke declarations & QueryDisplayConfig
│ │ │ ├── MonitorDiscoveryHelper.cs │ │ │ ├── MonitorDiscoveryHelper.cs
│ │ │ └── PhysicalMonitorHandleManager.cs │ │ │ └── PhysicalMonitorHandleManager.cs
│ │ ── WMI/ │ │ ── WMI/
│ │ └── WmiController.cs # WMI implementation │ │ └── WmiController.cs # WMI implementation (WmiLight library)
│ │ ├── NativeConstants.cs # Win32 constants (VCP codes, etc.)
│ │ ├── NativeDelegates.cs # P/Invoke delegate types
│ │ ├── NativeStructures.cs # Win32 structures
│ │ └── PInvoke.cs # P/Invoke declarations
│ ├── Helpers/
│ │ └── PnpIdHelper.cs # PnP manufacturer ID lookup
│ ├── Interfaces/ │ ├── Interfaces/
│ │ ── IMonitorController.cs # Controller abstraction │ │ ── IMonitorController.cs # Controller abstraction
│ │ ├── IMonitorData.cs # Monitor data interface
│ │ └── IProfileService.cs # Profile service interface
│ ├── Models/ │ ├── Models/
│ │ ├── Monitor.cs # Runtime monitor data │ │ ├── Monitor.cs # Runtime monitor data
│ │ ├── MonitorOperationResult.cs # Operation result enum │ │ ├── MonitorCapabilities.cs # Monitor capability flags
│ │ ├── MonitorOperationResult.cs # Operation result
│ │ ├── MonitorStateEntry.cs # Persisted monitor state
│ │ ├── MonitorStateFile.cs # State file schema
│ │ ├── PowerDisplayProfile.cs # Profile definition │ │ ├── PowerDisplayProfile.cs # Profile definition
│ │ ├── PowerDisplayProfiles.cs # Profile collection │ │ ├── PowerDisplayProfiles.cs # Profile collection
│ │ ── ProfileMonitorSetting.cs # Per-monitor settings │ │ ── ProfileMonitorSetting.cs # Per-monitor profile settings
│ │ ├── ProfileOperation.cs # Profile operation for IPC
│ │ ├── ColorTemperatureOperation.cs # Color temp operation for IPC
│ │ ├── ColorPresetItem.cs # Color preset UI item
│ │ ├── VcpCapabilities.cs # Parsed VCP capabilities
│ │ └── VcpFeatureValue.cs # VCP feature value (current/min/max)
│ ├── Serialization/
│ │ └── ProfileSerializationContext.cs # JSON source generation
│ ├── Services/ │ ├── Services/
│ │ ├── LightSwitchListener.cs # Theme change listener │ │ ├── DisplayRotationService.cs # Display rotation via ChangeDisplaySettingsEx
│ │ ├── MonitorStateManager.cs # State persistence (debounced) │ │ ├── MonitorStateManager.cs # State persistence (debounced)
│ │ └── ProfileService.cs # Profile persistence │ │ └── ProfileService.cs # Profile persistence
── Utils/ ── Utils/
├── ColorTemperatureHelper.cs # Color temp utilities ├── ColorTemperatureHelper.cs # Color temp utilities
├── MccsCapabilitiesParser.cs # DDC/CI capabilities parser ├── EventHelper.cs # Windows Event utilities
── VcpCapabilities.cs # VCP capabilities model ── MccsCapabilitiesParser.cs # DDC/CI capabilities parser
│ │ ├── MonitorFeatureHelper.cs # Monitor feature utilities
│ │ ├── MonitorMatchingHelper.cs # Profile-to-monitor matching
│ │ ├── MonitorValueConverter.cs # Value conversion utilities
│ │ ├── ProfileHelper.cs # Profile helper utilities
│ │ ├── SimpleDebouncer.cs # Generic debouncer
│ │ ├── VcpCodeNames.cs # VCP code name lookup
│ │ └── VcpValueNames.cs # VCP value name lookup
│ └── PathConstants.cs # File path constants
├── PowerDisplay/ # WinUI 3 application ├── PowerDisplay/ # WinUI 3 application
│ ├── Assets/ # App icons and images │ ├── Assets/ # App icons and images
│ ├── Common/ │ ├── Configuration/
│ │ ── Debouncer/ │ │ ── AppConstants.cs # Application constants
│ │ │ └── SimpleDebouncer.cs # Slider input debouncing
│ │ └── Models/
│ │ └── Monitor.cs # UI-layer monitor model
│ ├── Converters/ # XAML value converters │ ├── Converters/ # XAML value converters
│ ├── Helpers/ │ ├── Helpers/
│ │ ├── DisplayChangeWatcher.cs # Monitor hot-plug detection (WinRT DeviceWatcher) │ │ ├── DisplayChangeWatcher.cs # Monitor hot-plug detection (WinRT DeviceWatcher)
│ │ ├── DisplayRotationService.cs # Display rotation control
│ │ ├── MonitorManager.cs # Discovery orchestrator │ │ ├── MonitorManager.cs # Discovery orchestrator
│ │ ├── NativeMethodsHelper.cs # Window positioning │ │ ├── NativeEventWaiter.cs # Windows Event waiting
│ │ ├── ResourceLoaderInstance.cs # Resource loader singleton
│ │ ├── SettingsDeepLink.cs # Deep link to Settings UI
│ │ ├── TrayIconService.cs # System tray integration │ │ ├── TrayIconService.cs # System tray integration
│ │ ── WindowHelpers.cs # Window utilities │ │ ── TypePreservation.cs # AOT type preservation
├── Strings/ # Localization resources │ └── WindowHelper.cs # Window utilities
│ ├── PowerDisplayXAML/
│ │ ├── App.xaml / App.xaml.cs # Application entry point
│ │ ├── MainWindow.xaml / .cs # Main UI window
│ │ ├── IdentifyWindow.xaml / .cs # Monitor identify overlay
│ │ └── MonitorIcon.xaml / .cs # Monitor icon control
│ ├── Serialization/
│ │ └── JsonSourceGenerationContext.cs # JSON source generation
│ ├── Services/
│ │ └── LightSwitchService.cs # LightSwitch theme change handling
│ ├── Strings/ # Localization resources (en-us)
│ ├── Styles/ # Custom control styles │ ├── Styles/ # Custom control styles
│ ├── Telemetry/
│ │ └── Events/
│ │ └── PowerDisplayStartEvent.cs # Telemetry event
│ ├── ViewModels/ │ ├── ViewModels/
│ │ ├── InputSourceItem.cs # Input source dropdown item
│ │ ├── MainViewModel.cs # Main VM (partial class) │ │ ├── MainViewModel.cs # Main VM (partial class)
│ │ ├── MainViewModel.Monitors.cs # Monitor discovery methods │ │ ├── MainViewModel.Monitors.cs # Monitor discovery methods
│ │ ├── MainViewModel.Profiles.cs # Profile management methods
│ │ ├── MainViewModel.Settings.cs # Settings persistence methods │ │ ├── MainViewModel.Settings.cs # Settings persistence methods
│ │ └── MonitorViewModel.cs # Per-monitor VM │ │ └── MonitorViewModel.cs # Per-monitor VM
│ └── Views/ │ └── Program.cs # Application entry point
├── MainWindow.xaml # Main UI window
│ └── MainWindow.xaml.cs ├── PowerDisplay.Lib.UnitTests/ # Unit tests
│ ├── MccsCapabilitiesParserTests.cs
│ └── MonitorMatchingHelperTests.cs
└── PowerDisplayModuleInterface/ # C++ DLL (module interface) └── PowerDisplayModuleInterface/ # C++ DLL (module interface)
├── dllmain.cpp # PowertoyModuleIface impl ├── dllmain.cpp # PowertoyModuleIface impl
├── Constants.h # Module constants ├── Constants.h # Module constants (event names, timeouts)
├── resource.h # Resource definitions
├── pch.h / pch.cpp # Precompiled headers ├── pch.h / pch.cpp # Precompiled headers
└── trace.h / trace.cpp # Telemetry tracing └── Trace.h / Trace.cpp # ETW telemetry tracing
``` ```
--- ---
@@ -236,7 +279,7 @@ flowchart TB
subgraph WindowsEvents["Windows Events (IPC)"] subgraph WindowsEvents["Windows Events (IPC)"]
direction LR direction LR
ShowToggleEvents["Show/Toggle/Terminate<br/>Events"] ShowToggleEvents["Show/Toggle/Terminate<br/>Events"]
ThemeChangedEvent["ThemeChanged<br/>Event"] ThemeChangedEvent["ThemeChanged<br/>Events"]
end end
subgraph PowerDisplayModule["PowerDisplay Module"] subgraph PowerDisplayModule["PowerDisplay Module"]
@@ -245,18 +288,22 @@ flowchart TB
MonitorViewModel MonitorViewModel
MonitorManager MonitorManager
DisplayChangeWatcher["DisplayChangeWatcher<br/>(Hot-Plug Detection)"] DisplayChangeWatcher["DisplayChangeWatcher<br/>(Hot-Plug Detection)"]
LightSwitchService["LightSwitchService<br/>(Theme Handler)"]
end end
subgraph PowerDisplayLib["PowerDisplay.Lib"] subgraph PowerDisplayLib["PowerDisplay.Lib"]
subgraph Services subgraph Services
LightSwitchListener
ProfileService ProfileService
MonitorStateManager MonitorStateManager
DisplayRotationService
end end
subgraph Drivers subgraph Drivers
DdcCiController DdcCiController
WmiController WmiController
end end
subgraph Helpers
PnpIdHelper["PnpIdHelper<br/>(Manufacturer Names)"]
end
end end
end end
@@ -277,10 +324,10 @@ flowchart TB
%% Windows Events to App %% Windows Events to App
ShowToggleEvents --> MainViewModel ShowToggleEvents --> MainViewModel
ThemeChangedEvent --> LightSwitchListener ThemeChangedEvent --> LightSwitchService
%% App internal (MainViewModel owns LightSwitchListener) %% App internal
LightSwitchListener -.->|"ThemeChanged event"| MainViewModel LightSwitchService -.->|"Get profile name"| MainViewModel
MainViewModel --> MonitorViewModel MainViewModel --> MonitorViewModel
MonitorViewModel --> MonitorManager MonitorViewModel --> MonitorManager
DisplayChangeWatcher -.->|"DisplayChanged event"| MainViewModel DisplayChangeWatcher -.->|"DisplayChanged event"| MainViewModel
@@ -289,6 +336,10 @@ flowchart TB
MainViewModel --> ProfileService MainViewModel --> ProfileService
MonitorViewModel --> MonitorStateManager MonitorViewModel --> MonitorStateManager
MonitorManager --> Drivers MonitorManager --> Drivers
MonitorManager --> DisplayRotationService
%% Helpers used during discovery
WmiController --> PnpIdHelper
%% Services to Storage %% Services to Storage
ProfileService --> ProfilesJson ProfileService --> ProfilesJson
@@ -297,6 +348,8 @@ flowchart TB
%% Drivers to Hardware %% Drivers to Hardware
DdcCiController -->|"DDC/CI"| ExternalMonitor DdcCiController -->|"DDC/CI"| ExternalMonitor
WmiController -->|"WMI"| LaptopDisplay WmiController -->|"WMI"| LaptopDisplay
DisplayRotationService -->|"ChangeDisplaySettingsEx"| ExternalMonitor
DisplayRotationService -->|"ChangeDisplaySettingsEx"| LaptopDisplay
%% Styling %% Styling
style ExternalInputs fill:#e3f2fd,stroke:#1976d2 style ExternalInputs fill:#e3f2fd,stroke:#1976d2
@@ -306,6 +359,7 @@ flowchart TB
style PowerDisplayLib fill:#c8e6c9,stroke:#388e3c style PowerDisplayLib fill:#c8e6c9,stroke:#388e3c
style Services fill:#a5d6a7,stroke:#2e7d32 style Services fill:#a5d6a7,stroke:#2e7d32
style Drivers fill:#ffccbc,stroke:#e64a19 style Drivers fill:#ffccbc,stroke:#e64a19
style Helpers fill:#dcedc8,stroke:#689f38
style Storage fill:#e1bee7,stroke:#8e24aa style Storage fill:#e1bee7,stroke:#8e24aa
style Hardware fill:#b2dfdb,stroke:#00897b style Hardware fill:#b2dfdb,stroke:#00897b
``` ```
@@ -371,12 +425,12 @@ flowchart TB
end end
subgraph WMIStack["WMI Stack"] subgraph WMIStack["WMI Stack"]
WmiLight["WmiLight Library<br/>(Native AOT compatible)"] WmiLight["WmiLight Library<br/>(Native AOT compatible,<br/>NuGet package)"]
PnpHelper["PnpIdHelper<br/>(Manufacturer name lookup)"]
subgraph WMIClasses["WMI Classes (root\\WMI)"] subgraph WMIClasses["WMI Classes (root\\WMI)"]
WmiMonBright["WmiMonitorBrightness"] WmiMonBright["WmiMonitorBrightness"]
WmiMonBrightMethods["WmiMonitorBrightnessMethods"] WmiMonBrightMethods["WmiMonitorBrightnessMethods"]
WmiMonID["WmiMonitorID"]
end end
end end
@@ -402,9 +456,9 @@ flowchart TB
Dxva2 -->|"I2C/DDC"| ExtMon Dxva2 -->|"I2C/DDC"| ExtMon
WMI --> WmiLight WMI --> WmiLight
WMI --> PnpHelper
WmiLight --> WmiMonBright WmiLight --> WmiMonBright
WmiLight --> WmiMonBrightMethods WmiLight --> WmiMonBrightMethods
WmiLight --> WmiMonID
WmiMonBrightMethods -->|"WMI Provider"| LaptopMon WmiMonBrightMethods -->|"WMI Provider"| LaptopMon
@@ -420,36 +474,38 @@ classDiagram
class IMonitorController { class IMonitorController {
<<interface>> <<interface>>
+Name: string +Name: string
+DiscoverMonitorsAsync() IEnumerable~Monitor~ +DiscoverMonitorsAsync(cancellationToken) IEnumerable~Monitor~
+CanControlMonitorAsync(monitor) bool +GetBrightnessAsync(monitor, cancellationToken) VcpFeatureValue
+GetBrightnessAsync(monitor) BrightnessInfo +SetBrightnessAsync(monitor, brightness, cancellationToken) MonitorOperationResult
+SetBrightnessAsync(monitor, brightness) MonitorOperationResult +SetContrastAsync(monitor, contrast, cancellationToken) MonitorOperationResult
+SetContrastAsync(monitor, contrast) MonitorOperationResult +SetVolumeAsync(monitor, volume, cancellationToken) MonitorOperationResult
+SetVolumeAsync(monitor, volume) MonitorOperationResult +GetColorTemperatureAsync(monitor, cancellationToken) VcpFeatureValue
+GetColorTemperatureAsync(monitor) BrightnessInfo +SetColorTemperatureAsync(monitor, vcpValue, cancellationToken) MonitorOperationResult
+SetColorTemperatureAsync(monitor, vcpValue) MonitorOperationResult +GetInputSourceAsync(monitor, cancellationToken) VcpFeatureValue
+GetInputSourceAsync(monitor) BrightnessInfo +SetInputSourceAsync(monitor, inputSource, cancellationToken) MonitorOperationResult
+SetInputSourceAsync(monitor, inputSource) MonitorOperationResult
+GetCapabilitiesStringAsync(monitor) string
+Dispose() +Dispose()
} }
class DdcCiController { class DdcCiController {
-_handleManager: PhysicalMonitorHandleManager -_handleManager: PhysicalMonitorHandleManager
+Name: "DDC/CI" -_discoveryHelper: MonitorDiscoveryHelper
+Name: "DDC/CI Monitor Controller"
+DiscoverMonitorsAsync() +DiscoverMonitorsAsync()
+SetBrightnessAsync() +GetCapabilitiesStringAsync(monitor) string
-GetVcpFeatureAsync() -GetVcpFeatureAsync(monitor, vcpCode, featureName)
-SetVcpFeatureAsync() -SetVcpFeatureAsync(monitor, vcpCode, value)
-QuickConnectionCheck() -CollectCandidateMonitorsAsync()
-FetchCapabilitiesInParallelAsync()
-CreateValidMonitors()
} }
class WmiController { class WmiController {
+Name: "WMI" +Name: "WMI Monitor Controller"
+DiscoverMonitorsAsync() +DiscoverMonitorsAsync()
+SetBrightnessAsync() -BuildInstanceNameQuery()
-GetWmiMonitorBrightness() -ExtractHardwareIdFromInstanceName()
-SetWmiMonitorBrightness() -GetMonitorDisplayInfoByHardwareId()
-ClassifyWmiError()
} }
IMonitorController <|.. DdcCiController IMonitorController <|.. DdcCiController
@@ -458,6 +514,313 @@ classDiagram
--- ---
### Monitor Identification: Handles, IDs, and Names
Understanding how Windows identifies monitors is critical for PowerDisplay's operation.
Different Windows APIs use different identifiers, and PowerDisplay must correlate these
to provide a unified view across DDC/CI and WMI subsystems.
#### Windows Display Subsystem Overview
```mermaid
flowchart TB
subgraph WindowsAPIs["Windows Display APIs"]
EnumDisplayMonitors["EnumDisplayMonitors<br/>(User32.dll)"]
QueryDisplayConfig["QueryDisplayConfig<br/>(User32.dll)"]
GetPhysicalMonitors["GetPhysicalMonitorsFromHMONITOR<br/>(Dxva2.dll)"]
WmiMonitor["WMI root\\WMI<br/>(WmiLight)"]
end
subgraph Identifiers["Monitor Identifiers"]
HMONITOR["HMONITOR<br/>(Logical Monitor Handle)"]
GdiDeviceName["GDI Device Name<br/>(e.g., \\\\.\\DISPLAY1)"]
PhysicalHandle["Physical Monitor Handle<br/>(IntPtr for DDC/CI)"]
DevicePath["Device Path<br/>(Unique per target)"]
HardwareId["Hardware ID<br/>(e.g., DEL41B4)"]
InstanceName["WMI Instance Name<br/>(e.g., DISPLAY\\BOE0900\\...)"]
MonitorNumber["Monitor Number<br/>(1-based, matches Windows Settings)"]
end
EnumDisplayMonitors --> HMONITOR
HMONITOR --> GdiDeviceName
GetPhysicalMonitors --> PhysicalHandle
QueryDisplayConfig --> GdiDeviceName
QueryDisplayConfig --> DevicePath
QueryDisplayConfig --> HardwareId
QueryDisplayConfig --> MonitorNumber
WmiMonitor --> InstanceName
InstanceName --> HardwareId
style HMONITOR fill:#e3f2fd
style GdiDeviceName fill:#fff3e0
style PhysicalHandle fill:#c8e6c9
style DevicePath fill:#f3e5f5
style HardwareId fill:#ffccbc
style InstanceName fill:#ffe0b2
style MonitorNumber fill:#b2dfdb
```
#### Identifier Definitions
| Identifier | Source | Format | Example | Scope |
|------------|--------|--------|---------|-------|
| **HMONITOR** | `EnumDisplayMonitors` | `IntPtr` | `0x00010001` | Logical monitor (may represent multiple physical monitors in clone mode) |
| **GDI Device Name** | `GetMonitorInfo` / `QueryDisplayConfig` | String | `\\.\DISPLAY1` | Adapter output; multiple targets can share same GDI name in mirror mode |
| **Physical Monitor Handle** | `GetPhysicalMonitorsFromHMONITOR` | `IntPtr` | `0x00000B14` | DDC/CI communication handle; valid for `GetVCPFeature` / `SetVCPFeature` |
| **Device Path** | `QueryDisplayConfig` | String | `\\?\DISPLAY#DEL41B4#5&12a3b4c&0&UID123#{...}` | Unique per target; used as primary key in `MonitorDisplayInfo` |
| **Hardware ID** | EDID (via `QueryDisplayConfig`) | String | `DEL41B4` | Manufacturer (3-char PnP ID) + Product Code (4-char hex); identifies monitor model |
| **WMI Instance Name** | `WmiMonitorBrightness` | String | `DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0` | WMI object identifier; contains hardware ID in second segment |
| **Monitor Number** | `QueryDisplayConfig` path index | Integer | `1`, `2`, `3` | 1-based; matches Windows Settings → Display → "Identify" feature |
#### DDC/CI Monitor Discovery Flow
```mermaid
sequenceDiagram
participant App as PowerDisplay
participant Enum as EnumDisplayMonitors
participant Info as GetMonitorInfo
participant QDC as QueryDisplayConfig
participant Phys as GetPhysicalMonitors
participant DDC as DDC/CI (I2C)
App->>Enum: EnumDisplayMonitors(callback)
Enum-->>App: HMONITOR handles
loop For each HMONITOR
App->>Info: GetMonitorInfo(hMonitor)
Info-->>App: GDI Device Name (e.g., "\\.\DISPLAY1")
App->>Phys: GetPhysicalMonitorsFromHMONITOR(hMonitor)
Phys-->>App: Physical Monitor Handle(s) + Description
end
App->>QDC: QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS)
QDC-->>App: MonitorDisplayInfo[] (DevicePath, GdiDeviceName, HardwareId, MonitorNumber)
Note over App: Match Physical Handles to MonitorDisplayInfo<br/>using GDI Device Name
loop For each Physical Handle
App->>DDC: GetCapabilitiesStringLength(handle)
DDC-->>App: Capabilities length
App->>DDC: CapabilitiesRequestAndCapabilitiesReply(handle)
DDC-->>App: Capabilities string (MCCS format)
end
Note over App: Create Monitor objects with:<br/>- Handle (Physical Monitor Handle)<br/>- MonitorNumber (from QueryDisplayConfig)<br/>- GdiDeviceName (for rotation APIs)
```
#### WMI Monitor Discovery Flow
```mermaid
sequenceDiagram
participant App as PowerDisplay
participant WMI as WmiLight
participant QDC as QueryDisplayConfig
participant PnP as PnpIdHelper
App->>WMI: Query WmiMonitorBrightness
WMI-->>App: InstanceName, CurrentBrightness
Note over App: Extract HardwareId from InstanceName<br/>"DISPLAY\BOE0900\..." → "BOE0900"
App->>QDC: GetAllMonitorDisplayInfo()
QDC-->>App: MonitorDisplayInfo[] (keyed by DevicePath)
Note over App: Match WMI monitor to QueryDisplayConfig<br/>by comparing HardwareId
App->>PnP: GetBuiltInDisplayName("BOE0900")
PnP-->>App: "BOE Built-in Display"
Note over App: Create Monitor objects with:<br/>- InstanceName (for WMI queries)<br/>- MonitorNumber (from QueryDisplayConfig)<br/>- GdiDeviceName (for rotation APIs)
```
#### Key Relationships
##### GDI Device Name ↔ Physical Monitors
```
HMONITOR (Logical)
├── GetMonitorInfo() → GDI Device Name "\\.\DISPLAY1"
└── GetPhysicalMonitorsFromHMONITOR()
├── Physical Monitor 0 (Handle: 0x0B14, Desc: "Dell U2722D")
└── Physical Monitor 1 (Handle: 0x0B18, Desc: "Dell U2722D") [Mirror mode]
```
In **mirror/clone mode**, multiple physical monitors share the same GDI device name.
QueryDisplayConfig returns multiple paths with the same `GdiDeviceName` but different
`DevicePath` values, allowing us to distinguish them.
##### DisplayPort Daisy Chain (MST - Multi-Stream Transport)
**Daisy chaining** allows multiple monitors to be connected in series through a single
DisplayPort output using MST (Multi-Stream Transport) technology. This creates unique
challenges for monitor identification.
```
┌─────────────┐ DP ┌─────────────┐ DP ┌─────────────┐
│ GPU │─────────▶│ Monitor A │─────────▶│ Monitor B │
│ │ │ (MST Hub) │ │ (End) │
└─────────────┘ └─────────────┘ └─────────────┘
│ Single physical DP port
Multiple logical displays (DISPLAY1, DISPLAY2)
```
**How Windows Handles MST:**
| Aspect | Behavior |
|--------|----------|
| **HMONITOR** | Each daisy-chained monitor gets its own HMONITOR |
| **GDI Device Name** | Each monitor gets a unique GDI name (e.g., `\\.\DISPLAY1`, `\\.\DISPLAY2`) |
| **Physical Monitor Handle** | Each monitor has its own physical handle for DDC/CI |
| **Device Path** | Unique for each monitor in the chain |
| **Hardware ID** | Different if monitors are different models; same if identical models |
**MST vs Clone Mode Comparison:**
```
MST Daisy Chain (Extended Desktop):
┌──────────────────────────────────────────────────────────────┐
│ HMONITOR_1 │ HMONITOR_2 │ HMONITOR_3 │
│ GDI: \\.\DISPLAY1 │ GDI: \\.\DISPLAY2 │ GDI: \\.\DISPLAY3│
│ Physical Handle: A │ Physical Handle: B │ Physical Handle: C│
│ DevicePath: unique1 │ DevicePath: unique2 │ DevicePath: unique3│
└──────────────────────────────────────────────────────────────┘
Each monitor = independent logical display with unique identifiers
Clone/Mirror Mode:
┌──────────────────────────────────────────────────────────────┐
│ HMONITOR_1 │
│ GDI: \\.\DISPLAY1 │
│ Physical Handle: A Physical Handle: B │
│ DevicePath: unique1 DevicePath: unique2 │
└──────────────────────────────────────────────────────────────┘
Multiple monitors share same HMONITOR and GDI name
```
**PowerDisplay Handling of MST:**
1. **Discovery**: `EnumDisplayMonitors` returns separate HMONITOR for each MST monitor
2. **Physical Handles**: `GetPhysicalMonitorsFromHMONITOR` returns one handle per HMONITOR
3. **Matching**: QueryDisplayConfig provides unique DevicePath for each MST target
4. **DDC/CI**: Each monitor in the chain can be controlled independently via its handle
**Identifying Same-Model Monitors in Daisy Chain:**
When multiple identical monitors are daisy-chained (same Hardware ID), PowerDisplay
distinguishes them using:
- **MonitorNumber**: Different path index in QueryDisplayConfig (1, 2, 3...)
- **DevicePath**: Unique system-generated path for each target
- **Monitor.Id**: Format `DDC_{HardwareId}_{MonitorNumber}` ensures uniqueness
Example with two identical Dell U2722D monitors:
```
Monitor 1: Id = "DDC_DEL41B4_1", MonitorNumber = 1
Monitor 2: Id = "DDC_DEL41B4_2", MonitorNumber = 2
```
##### Connection Mode Summary
| Mode | HMONITOR | GDI Device Name | Physical Handles | Use Case |
|------|----------|-----------------|------------------|----------|
| **Standard** (separate cables) | 1 per monitor | Unique per monitor | 1 per HMONITOR | Most common setup |
| **Clone/Mirror** | 1 shared | Shared | Multiple per HMONITOR | Presentation, duplication |
| **MST Daisy Chain** | 1 per monitor | Unique per monitor | 1 per HMONITOR | Reduced cable clutter |
| **USB-C/Thunderbolt Hub** | 1 per monitor | Unique per monitor | 1 per HMONITOR | Laptop docking |
**Key Insight**: From PowerDisplay's perspective, MST daisy chain and standard multi-cable
setups behave identically - each monitor appears as an independent display with unique
identifiers. Only clone/mirror mode requires special handling due to shared HMONITOR/GDI names.
##### Hardware ID Composition
```
Hardware ID: "DEL41B4"
───┬───
┌────────────┴────────────┐
│ │
"DEL" "41B4"
│ │
PnP Manufacturer ID Product Code
(3 chars, EDID bytes (4 hex chars,
8-9, compressed) EDID bytes 10-11)
```
The **PnP Manufacturer ID** is a 3-character code assigned by Microsoft (formerly UEFI Forum).
Common laptop display manufacturers:
| PnP ID | Manufacturer |
|--------|--------------|
| `BOE` | BOE Technology |
| `LGD` | LG Display |
| `AUO` | AU Optronics |
| `CMN` | Chi Mei Innolux |
| `SDC` | Samsung Display |
| `SHP` | Sharp |
| `LEN` | Lenovo |
| `DEL` | Dell |
##### WMI Instance Name Parsing
```
WMI InstanceName: "DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0"
───────┬─────── ──────────┬───────────
│ │
Segment 1: "DISPLAY" Segment 3: Device instance
(Constant prefix)
Segment 2: "BOE0900"
Hardware ID
(Used for matching with QueryDisplayConfig)
```
##### Monitor Number (Windows Display Settings)
The `MonitorNumber` in PowerDisplay corresponds exactly to the number shown in:
- Windows Settings → System → Display → "Identify" button
- The number overlay that appears on each display
This is derived from the **path index** in `QueryDisplayConfig`:
- `paths[0]` → Monitor 1
- `paths[1]` → Monitor 2
- etc.
#### Display Rotation and GDI Device Name
The `ChangeDisplaySettingsEx` API requires the **GDI Device Name** to target a specific display:
```cpp
// Correct: Target specific display by GDI name
ChangeDisplaySettingsEx("\\.\DISPLAY2", &devMode, NULL, 0, NULL);
// Wrong: NULL affects primary display only
ChangeDisplaySettingsEx(NULL, &devMode, NULL, 0, NULL);
```
PowerDisplay stores `GdiDeviceName` in each `Monitor` object specifically for rotation operations.
#### Cross-Reference Summary
| PowerDisplay Property | DDC/CI Source | WMI Source |
|-----------------------|---------------|------------|
| `Monitor.Id` | `"DDC_{HardwareId}_{MonitorNumber}"` | `"WMI_{HardwareId}_{MonitorNumber}"` |
| `Monitor.Handle` | Physical Monitor Handle | N/A (uses InstanceName) |
| `Monitor.InstanceName` | N/A | WMI InstanceName |
| `Monitor.GdiDeviceName` | QueryDisplayConfig | QueryDisplayConfig |
| `Monitor.MonitorNumber` | QueryDisplayConfig path index | QueryDisplayConfig (matched by HardwareId) |
| `Monitor.Name` | EDID FriendlyName or Description | PnpIdHelper.GetBuiltInDisplayName() |
---
### Settings UI and PowerDisplay Interaction Architecture ### Settings UI and PowerDisplay Interaction Architecture
```mermaid ```mermaid
@@ -553,13 +916,12 @@ flowchart TB
end end
subgraph PowerDisplayModule["PowerDisplay Module (C#)"] subgraph PowerDisplayModule["PowerDisplay Module (C#)"]
subgraph Listener["LightSwitchListener Service"] subgraph App["PowerDisplay App"]
EventWait["WaitAny([lightEvent, darkEvent])<br/>(Background Thread)"] EventWaiter["NativeEventWaiter<br/>(Background Thread)"]
ReadSettings["ReadProfileFromLightSwitchSettings(isLightMode)"] LightSwitchSvc["LightSwitchService<br/>(Static Helper)"]
ThemeChangedEvent["ThemeChanged Event"] MainViewModel["MainViewModel"]
end end
MainViewModel["MainViewModel"]
ProfileService["ProfileService"] ProfileService["ProfileService"]
MonitorVMs["MonitorViewModels"] MonitorVMs["MonitorViewModels"]
Controllers["IMonitorController"] Controllers["IMonitorController"]
@@ -586,13 +948,12 @@ flowchart TB
NotifyPD -->|"isLight=true"| LightEvent NotifyPD -->|"isLight=true"| LightEvent
NotifyPD -->|"isLight=false"| DarkEvent NotifyPD -->|"isLight=false"| DarkEvent
%% PowerDisplay flow - theme determined from event, not registry %% PowerDisplay flow - theme determined from event
LightEvent -->|"WaitAny index=0"| EventWait LightEvent -->|"Event signaled"| EventWaiter
DarkEvent -->|"WaitAny index=1"| EventWait DarkEvent -->|"Event signaled"| EventWaiter
EventWait -->|"isLightMode from event"| ReadSettings EventWaiter -->|"isLightMode"| LightSwitchSvc
ReadSettings -->|"Get profile for theme"| LSSettingsJson LightSwitchSvc -->|"GetProfileForTheme()"| LSSettingsJson
ReadSettings --> ThemeChangedEvent LightSwitchSvc -->|"Profile name"| MainViewModel
ThemeChangedEvent --> MainViewModel
MainViewModel -->|"LoadProfiles()"| ProfileService MainViewModel -->|"LoadProfiles()"| ProfileService
ProfileService <--> PDProfilesJson ProfileService <--> PDProfilesJson
MainViewModel -->|"ApplyProfileAsync()"| MonitorVMs MainViewModel -->|"ApplyProfileAsync()"| MonitorVMs
@@ -601,6 +962,7 @@ flowchart TB
style LightSwitchModule fill:#ffccbc style LightSwitchModule fill:#ffccbc
style PowerDisplayModule fill:#c8e6c9 style PowerDisplayModule fill:#c8e6c9
style App fill:#a5d6a7
style WindowsEvents fill:#e3f2fd style WindowsEvents fill:#e3f2fd
style FileSystem fill:#fffde7 style FileSystem fill:#fffde7
``` ```
@@ -803,7 +1165,8 @@ sequenceDiagram
participant System as Windows System participant System as Windows System
participant LightSwitch as LightSwitchStateManager (C++) participant LightSwitch as LightSwitchStateManager (C++)
participant WinEvent as Windows Events participant WinEvent as Windows Events
participant Listener as LightSwitchListener participant EventWaiter as NativeEventWaiter
participant LSSvc as LightSwitchService
participant MainVM as MainViewModel participant MainVM as MainViewModel
participant ProfileService participant ProfileService
participant MonitorVM as MonitorViewModel participant MonitorVM as MonitorViewModel
@@ -825,24 +1188,21 @@ sequenceDiagram
LightSwitch->>WinEvent: SetEvent("Local\\PowerToys_LightSwitch_DarkTheme") LightSwitch->>WinEvent: SetEvent("Local\\PowerToys_LightSwitch_DarkTheme")
end end
Note over Listener: Background thread waiting<br/>on both Light and Dark events Note over EventWaiter: Background thread waiting<br/>on both Light and Dark events
Listener->>WinEvent: WaitAny([lightEvent, darkEvent]) returns index EventWaiter->>WinEvent: WaitAny([lightEvent, darkEvent]) returns index
Note over Listener: Theme determined from event:<br/>index 0 = Light, index 1 = Dark Note over EventWaiter: Theme determined from event:<br/>index 0 = Light, index 1 = Dark
Listener->>Listener: ProcessThemeChange(isLightMode) EventWaiter->>LSSvc: GetProfileForTheme(isLightMode)
Listener->>Listener: ReadProfileFromLightSwitchSettings(isLightMode) LSSvc->>LSSvc: Read LightSwitch/settings.json
Note over Listener: Read LightSwitch/settings.json<br/>Get profile for known theme LSSvc-->>EventWaiter: profileName (or null)
Listener->>MainVM: ThemeChanged?.Invoke(ThemeChangedEventArgs) EventWaiter->>MainVM: Dispatch to UI thread with profileName
MainVM->>MainVM: OnLightSwitchThemeChanged()
MainVM->>ProfileService: LoadProfiles() MainVM->>ProfileService: LoadProfiles()
ProfileService-->>MainVM: PowerDisplayProfiles ProfileService-->>MainVM: PowerDisplayProfiles
MainVM->>ProfileService: GetProfile(profileName) MainVM->>MainVM: Find profile by name
ProfileService-->>MainVM: PowerDisplayProfile MainVM->>MainVM: ApplyProfileAsync(profile.MonitorSettings)
MainVM->>MainVM: ApplyProfileAsync(profile)
loop For each ProfileMonitorSetting loop For each ProfileMonitorSetting
MainVM->>MainVM: Find MonitorViewModel by InternalName MainVM->>MainVM: Find MonitorViewModel by InternalName
@@ -871,6 +1231,12 @@ sequenceDiagram
MonitorVM->>Controller: SetColorTemperatureAsync(monitor, vcpValue) MonitorVM->>Controller: SetColorTemperatureAsync(monitor, vcpValue)
Controller->>Monitor: SetVCPFeature(0x14, vcpValue) Controller->>Monitor: SetVCPFeature(0x14, vcpValue)
end end
alt Orientation specified
MainVM->>MonitorVM: SetOrientationAsync(orientation)
MonitorVM->>Controller: SetRotationAsync(monitor, orientation)
Controller->>Monitor: ChangeDisplaySettingsEx
end
end end
Note over MainVM: await Task.WhenAll(updateTasks) Note over MainVM: await Task.WhenAll(updateTasks)
@@ -988,31 +1354,76 @@ classDiagram
class Monitor { class Monitor {
+string Id +string Id
+string Name +string Name
+string HardwareId
+string DeviceKey
+string CommunicationMethod +string CommunicationMethod
+string InstanceName
+string GdiDeviceName
+int MonitorNumber
+int CurrentBrightness +int CurrentBrightness
+int MinBrightness
+int MaxBrightness
+int CurrentContrast +int CurrentContrast
+int CurrentVolume +int CurrentVolume
+int CurrentColorTemperature +int CurrentColorTemperature
+int CurrentInputSource +int CurrentInputSource
+int Orientation
+bool IsAvailable
+bool SupportsContrast
+bool SupportsVolume
+bool SupportsColorTemperature
+bool SupportsInputSource
+MonitorCapabilities Capabilities
+VcpCapabilities VcpCapabilitiesInfo +VcpCapabilities VcpCapabilitiesInfo
+IntPtr PhysicalMonitorHandle +string CapabilitiesRaw
+PropertyChanged event +IntPtr Handle
+DateTime LastUpdate
+UpdateStatus(brightness, isAvailable)
} }
class VcpCapabilities { class VcpCapabilities {
+Dictionary~int, VcpCodeInfo~ SupportedVcpCodes +string Raw
+string RawCapabilitiesString +string Model
+string Type
+string Protocol
+string MccsVersion
+List~byte~ SupportedCommands
+Dictionary~byte, VcpCodeInfo~ SupportedVcpCodes
+List~WindowCapability~ Windows
+bool HasWindowSupport
+SupportsVcpCode(code) bool +SupportsVcpCode(code) bool
+GetSupportedValues(code) List~int~ +GetVcpCodeInfo(code) VcpCodeInfo
+GetSupportedValues(code) IReadOnlyList~int~
+GetVcpCodesAsHexStrings() List~string~
} }
class VcpCodeInfo { class VcpCodeInfo {
+int Code +byte Code
+string Name +string Name
+List~int~ SupportedValues +IReadOnlyList~int~ SupportedValues
+bool IsReadOnly +bool HasDiscreteValues
+bool IsContinuous
+string FormattedCode
+string FormattedTitle
}
class VcpFeatureValue {
+int Current
+int Minimum
+int Maximum
+bool IsValid
+ToPercentage() int
+static Invalid VcpFeatureValue
}
class MonitorCapabilities {
<<flags enum>>
None
Brightness
Contrast
Volume
ColorTemperature
InputSource
Wmi
DdcCi
} }
class PowerDisplayProfile { class PowerDisplayProfile {
@@ -1024,23 +1435,22 @@ classDiagram
} }
class ProfileMonitorSetting { class ProfileMonitorSetting {
+string HardwareId
+string MonitorInternalName +string MonitorInternalName
+int MonitorNumber
+int? Brightness +int? Brightness
+int? Contrast +int? Contrast
+int? Volume +int? Volume
+int? ColorTemperatureVcp +int? ColorTemperatureVcp
+int? Orientation
} }
class PowerDisplayProfiles { class PowerDisplayProfiles {
+List~PowerDisplayProfile~ Profiles +List~PowerDisplayProfile~ Profiles
+DateTime LastUpdated +DateTime LastUpdated
+GetProfile(name) PowerDisplayProfile
+SetProfile(profile)
+RemoveProfile(name)
} }
Monitor "1" --> "0..1" VcpCapabilities Monitor "1" --> "0..1" VcpCapabilities
Monitor "1" --> "1" MonitorCapabilities
VcpCapabilities "1" --> "*" VcpCodeInfo VcpCapabilities "1" --> "*" VcpCodeInfo
PowerDisplayProfiles "1" --> "*" PowerDisplayProfile PowerDisplayProfiles "1" --> "*" PowerDisplayProfile
PowerDisplayProfile "1" --> "*" ProfileMonitorSetting PowerDisplayProfile "1" --> "*" ProfileMonitorSetting
@@ -1111,14 +1521,25 @@ classDiagram
## Future Considerations ## Future Considerations
### Already Implemented (removed from backlog)
- **Monitor Hot-Plug**: `DisplayChangeWatcher` uses WinRT DeviceWatcher + DisplayMonitor API with 1-second debouncing
- **Display Rotation**: `DisplayRotationService` uses Windows ChangeDisplaySettingsEx API
- **LightSwitch Integration**: Automatic profile application on theme changes via `LightSwitchService`
- **Monitor Identification**: Overlay windows showing monitor numbers via `IdentifyWindow`
- **Mirror Mode Support**: Correct orientation sync for multiple monitors sharing the same GDI device name
### Potential Future Enhancements
1. **Hardware Cursor Brightness**: Support for displays with hardware cursor brightness 1. **Hardware Cursor Brightness**: Support for displays with hardware cursor brightness
2. **Multi-GPU Support**: Better handling of monitors across different GPUs 2. **Multi-GPU Support**: Better handling of monitors across different GPUs
3. ~~**Monitor Hot-Plug**: Improved detection and recovery for monitor connect/disconnect~~ **Implemented** - `DisplayChangeWatcher` uses WinRT DeviceWatcher + DisplayMonitor API with 1-second debouncing 3. **Advanced Color Management**: Integration with Windows Color Management APIs (HDR, ICC profiles)
4. **Advanced Color Management**: Integration with Windows Color Management 4. **Scheduled Profiles**: Time-based automatic profile switching (beyond LightSwitch integration)
5. **Scheduled Profiles**: Time-based automatic profile switching (beyond LightSwitch) 5. **Monitor Groups**: Ability to control multiple monitors as a single entity
6. **Monitor Groups**: Ability to control multiple monitors as a single entity 6. **Remote Control**: Network-based control for multi-system setups
7. **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. ~~**Display Rotation**: Control display orientation~~ **Implemented** - `DisplayRotationService` uses Windows ChangeDisplaySettingsEx API 8. **Power State Control**: Monitor power on/off via VCP code 0xD6
9. **Input Source Scheduling**: Automatic input switching based on time or application
--- ---

View File

@@ -323,48 +323,6 @@ void dispatch_received_json(const std::wstring& json_to_parse)
Logger::error(L"Failed to process get all hotkey conflicts request"); Logger::error(L"Failed to process get all hotkey conflicts request");
} }
} }
else if (name == L"powerdisplay_response")
{
try
{
// Forward PowerDisplay response messages to Settings UI
// PowerDisplay sends monitor information via IPC
std::unique_lock lock{ ipc_mutex };
if (current_settings_ipc)
{
current_settings_ipc->send(value.Stringify().c_str());
}
}
catch (...)
{
Logger::error(L"Failed to forward PowerDisplay response to Settings");
}
}
else if (name == L"powerdisplay_command")
{
try
{
// Forward command from Settings UI to PowerDisplay module
Logger::trace(L"Received command from Settings UI to PowerDisplay");
// Find PowerDisplay module and send the command
auto moduleIt = modules().find(L"PowerDisplay");
if (moduleIt != modules().end())
{
// Use call_custom_action to send the command
// The command should contain an action field
moduleIt->second->call_custom_action(value.Stringify().c_str());
}
else
{
Logger::warn(L"PowerDisplay module not found, cannot send command");
}
}
catch (...)
{
Logger::error(L"Failed to forward command to PowerDisplay");
}
}
} }
return; return;
} }
@@ -1004,29 +962,3 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
return ESettingsWindowNames::Dashboard; return ESettingsWindowNames::Dashboard;
} }
// Global function for PowerDisplay module to send messages to Settings UI
void send_powerdisplay_message_to_settings_ui(const wchar_t* message)
{
try
{
Logger::trace(L"Sending PowerDisplay message to Settings UI");
std::unique_lock lock{ ipc_mutex };
if (current_settings_ipc)
{
// Wrap the message in powerdisplay_response format
json::JsonObject wrapper;
wrapper.SetNamedValue(L"powerdisplay_response", json::JsonValue::Parse(message));
current_settings_ipc->send(wrapper.Stringify().c_str());
}
else
{
Logger::warn(L"current_settings_ipc is null, cannot send to Settings UI");
}
}
catch (const std::exception&)
{
Logger::error(L"Exception while sending PowerDisplay message to Settings UI");
}
}

View File

@@ -48,6 +48,3 @@ void close_settings_window();
void open_oobe_window(); void open_oobe_window();
void open_scoobe_window(); void open_scoobe_window();
void open_flyout(); void open_flyout();
// PowerDisplay IPC support
void send_powerdisplay_message_to_settings_ui(const wchar_t* message);