// 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;
}
}
}