// Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.ComponentModel; using System.Runtime.CompilerServices; using PowerDisplay.Common.Interfaces; using PowerDisplay.Common.Utils; namespace PowerDisplay.Common.Models { /// /// Monitor model that implements property change notification. /// Implements IMonitorData to provide a common interface for monitor hardware values. /// /// /// Monitor Identifier Hierarchy: /// /// : Runtime identifier for UI and IPC (e.g., "DDC_GSM5C6D", "WMI_DISPLAY\BOE...") /// : EDID-based identifier for persistent storage (e.g., "GSM5C6D") /// : Windows device path for handle management (e.g., "\\?\DISPLAY#...") /// /// Use for lookups, for saving state, for handle reuse. /// public partial class Monitor : INotifyPropertyChanged, IMonitorData { private int _currentBrightness; private int _currentColorTemperature = 0x05; // Default to 6500K preset (VCP 0x14 value) private int _currentInputSource; // VCP 0x60 value private bool _isAvailable = true; /// /// Runtime unique identifier for UI lookups and IPC communication. /// /// /// Format: "{Source}_{HardwareId}" where Source is "DDC" or "WMI". /// Examples: "DDC_GSM5C6D", "WMI_DISPLAY\BOE0900...". /// Use this for ViewModel lookups and MonitorManager method parameters. /// public string Id { get; set; } = string.Empty; /// /// EDID-based hardware identifier for persistent state storage. /// /// /// Format: Manufacturer code + product code from EDID (e.g., "GSM5C6D" for LG monitors). /// Use this for saving/loading monitor settings in MonitorStateManager. /// Stable across reboots but not guaranteed unique if multiple identical monitors are connected. /// public string HardwareId { get; set; } = string.Empty; /// /// Display name /// public string Name { get; set; } = string.Empty; /// /// Current brightness (0-100) /// public int CurrentBrightness { get => _currentBrightness; set { var clamped = Math.Clamp(value, MinBrightness, MaxBrightness); if (_currentBrightness != clamped) { _currentBrightness = clamped; OnPropertyChanged(); } } } /// /// Minimum brightness value /// public int MinBrightness { get; set; } /// /// Maximum brightness value /// public int MaxBrightness { get; set; } = 100; /// /// 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. /// public int CurrentColorTemperature { get => _currentColorTemperature; set { if (_currentColorTemperature != value) { _currentColorTemperature = value; OnPropertyChanged(); OnPropertyChanged(nameof(ColorTemperaturePresetName)); } } } /// /// 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 /// public bool SupportsColorTemperature { get; set; } /// /// 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. /// public int CurrentInputSource { get => _currentInputSource; set { if (_currentInputSource != value) { _currentInputSource = value; OnPropertyChanged(); OnPropertyChanged(nameof(InputSourceName)); } } } /// /// 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 /// public bool SupportsInputSource => VcpCapabilitiesInfo?.SupportsVcpCode(0x60) ?? false; /// /// 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" /// public string CapabilitiesStatus { get; set; } = "unknown"; /// /// Whether supports contrast adjustment /// public bool SupportsContrast => Capabilities.HasFlag(MonitorCapabilities.Contrast); /// /// Whether supports volume adjustment (for audio-capable monitors) /// public bool SupportsVolume => Capabilities.HasFlag(MonitorCapabilities.Volume); private int _currentContrast = 50; private int _currentVolume = 50; /// /// Current contrast (0-100) /// public int CurrentContrast { get => _currentContrast; set { var clamped = Math.Clamp(value, MinContrast, MaxContrast); if (_currentContrast != clamped) { _currentContrast = clamped; OnPropertyChanged(); } } } /// /// Minimum contrast value /// public int MinContrast { get; set; } /// /// Maximum contrast value /// public int MaxContrast { get; set; } = 100; /// /// Current volume (0-100) /// public int CurrentVolume { get => _currentVolume; set { var clamped = Math.Clamp(value, MinVolume, MaxVolume); if (_currentVolume != clamped) { _currentVolume = clamped; OnPropertyChanged(); } } } /// /// Minimum volume value /// public int MinVolume { get; set; } /// /// Maximum volume value /// public int MaxVolume { get; set; } = 100; /// /// Whether available/online /// public bool IsAvailable { get => _isAvailable; set { if (_isAvailable != value) { _isAvailable = value; OnPropertyChanged(); } } } /// /// Physical monitor handle (for DDC/CI) /// public IntPtr Handle { get; set; } = IntPtr.Zero; /// /// Windows device path fragment for physical monitor handle management. /// /// /// Format: Registry-style path from DisplayDeviceInfo (e.g., "\\?\DISPLAY#GSM5C6D#..."). /// Used by PhysicalMonitorHandleManager to reuse handles across monitor discovery cycles. /// Changes when monitor is reconnected to a different port. /// public string DeviceKey { get; set; } = string.Empty; /// /// 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.) /// public string CommunicationMethod { get; set; } = string.Empty; /// /// Supported control methods /// public MonitorCapabilities Capabilities { get; set; } = MonitorCapabilities.None; /// /// Raw DDC/CI capabilities string (MCCS format) /// public string? CapabilitiesRaw { get; set; } /// /// Parsed VCP capabilities information /// public VcpCapabilities? VcpCapabilitiesInfo { get; set; } /// /// Last update time /// public DateTime LastUpdate { get; set; } = DateTime.Now; public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public override string ToString() { return $"{Name} ({CommunicationMethod}) - {CurrentBrightness}%"; } /// /// Update monitor status /// public void UpdateStatus(int brightness, bool isAvailable = true) { IsAvailable = isAvailable; if (isAvailable) { CurrentBrightness = brightness; LastUpdate = DateTime.Now; } } /// int IMonitorData.Brightness { get => CurrentBrightness; set => CurrentBrightness = value; } /// int IMonitorData.Contrast { get => CurrentContrast; set => CurrentContrast = value; } /// int IMonitorData.Volume { get => CurrentVolume; set => CurrentVolume = value; } /// int IMonitorData.ColorTemperatureVcp { get => CurrentColorTemperature; set => CurrentColorTemperature = value; } /// /// Gets or sets monitor number (1, 2, 3...) /// public int MonitorNumber { get; set; } /// /// Gets or sets monitor orientation (0=0, 1=90, 2=180, 3=270) /// public int Orientation { get; set; } /// int IMonitorData.MonitorNumber { get => MonitorNumber; set => MonitorNumber = value; } /// int IMonitorData.Orientation { get => Orientation; set => Orientation = value; } } }