using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using WindowsDisplayAPI.Exceptions; using WindowsDisplayAPI.Native; using WindowsDisplayAPI.Native.DisplayConfig; using WindowsDisplayAPI.Native.DisplayConfig.Structures; using WindowsDisplayAPI.Native.Structures; namespace WindowsDisplayAPI.DisplayConfig { /// /// Represents a display path target (Display Device) /// public class PathDisplayTarget : IEquatable { /// /// Creates a new PathDisplayTarget /// /// Display adapter /// Display target identification public PathDisplayTarget(PathDisplayAdapter adapter, uint targetId) : this(adapter, targetId, false) { IsAvailable = GetDisplayTargets().Any(target => target == this); } internal PathDisplayTarget(PathDisplayAdapter adapter, uint targetId, bool isAvailable) { Adapter = adapter; TargetId = targetId; IsAvailable = isAvailable; } /// /// Gets the path display adapter /// public PathDisplayAdapter Adapter { get; } /// /// Sets the display boot persistence for the target display device /// /// /// public bool BootPersistence { set { if (!IsAvailable) { throw new TargetNotAvailableException("Extra information about the target is not available.", Adapter.AdapterId, TargetId); } var targetPersistence = new DisplayConfigSetTargetPersistence(Adapter.AdapterId, TargetId, value); var result = DisplayConfigApi.DisplayConfigSetDeviceInfo(ref targetPersistence); if (result != Win32Status.Success) { throw new Win32Exception((int) result); } } } /// /// Gets the one-based instance number of this particular target only when the adapter has multiple targets of this /// type. The connector instance is a consecutive one-based number that is unique within each adapter. If this is the /// only target of this type on the adapter, this value is zero. /// /// Error code can be retrieved from Win32Exception.NativeErrorCode property /// The target is not available public int ConnectorInstance { get { if (!IsAvailable) { throw new TargetNotAvailableException("Extra information about the target is not available.", Adapter.AdapterId, TargetId); } var targetName = new DisplayConfigTargetDeviceName(Adapter.AdapterId, TargetId); var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref targetName); if (result == Win32Status.Success) { return (int) targetName.ConnectorInstance; } throw new Win32Exception((int) result); } } /// /// Gets the display device path /// /// Error code can be retrieved from Win32Exception.NativeErrorCode property /// The target is not available public string DevicePath { get { if (!IsAvailable) { throw new TargetNotAvailableException("Extra information about the target is not available.", Adapter.AdapterId, TargetId); } var targetName = new DisplayConfigTargetDeviceName(Adapter.AdapterId, TargetId); var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref targetName); if (result == Win32Status.Success) { return targetName.MonitorDevicePath; } throw new Win32Exception((int) result); } } /// /// Gets the display manufacture 3 character code from the display EDID manufacture identification /// /// The target is not available /// Error code can be retrieved from Win32Exception.NativeErrorCode property /// The EDID information does not contain this value public string EDIDManufactureCode { get { var edidCode = EDIDManufactureId; edidCode = ((edidCode & 0xff00) >> 8) | ((edidCode & 0x00ff) << 8); var byte1 = (byte) 'A' + (edidCode & 0x1f) - 1; var byte2 = (byte) 'A' + ((edidCode >> 5) & 0x1f) - 1; var byte3 = (byte) 'A' + ((edidCode >> 10) & 0x1f) - 1; return $"{Convert.ToChar(byte3)}{Convert.ToChar(byte2)}{Convert.ToChar(byte1)}"; } } /// /// Gets the display manufacture identification from the display EDID information /// /// The target is not available /// Error code can be retrieved from Win32Exception.NativeErrorCode property /// The EDID information does not contain this value public int EDIDManufactureId { get { if (!IsAvailable) { throw new TargetNotAvailableException("Extra information about the target is not available.", Adapter.AdapterId, TargetId); } var targetName = new DisplayConfigTargetDeviceName(Adapter.AdapterId, TargetId); var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref targetName); if (result == Win32Status.Success) { if (targetName.Flags.HasFlag(DisplayConfigTargetDeviceNameFlags.EDIDIdsValid)) { return targetName.EDIDManufactureId; } throw new InvalidEDIDInformation("EDID does not contain necessary information."); } throw new Win32Exception((int) result); } } /// /// Gets the display product identification from the display EDID information /// /// The target is not available /// Error code can be retrieved from Win32Exception.NativeErrorCode property /// The EDID information does not contain this value public int EDIDProductCode { get { if (!IsAvailable) { throw new TargetNotAvailableException("Extra information about the target is not available.", Adapter.AdapterId, TargetId); } var targetName = new DisplayConfigTargetDeviceName(Adapter.AdapterId, TargetId); var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref targetName); if (result == Win32Status.Success) { if (targetName.Flags.HasFlag(DisplayConfigTargetDeviceNameFlags.EDIDIdsValid)) { return targetName.EDIDProductCodeId; } throw new InvalidEDIDInformation("EDID does not contain necessary information."); } throw new Win32Exception((int) result); } } /// /// Gets the display friendly name from the display EDID information /// /// The target is not available /// Error code can be retrieved from Win32Exception.NativeErrorCode property public string FriendlyName { get { if (!IsAvailable) { throw new TargetNotAvailableException("Extra information about the target is not available.", Adapter.AdapterId, TargetId); } var targetName = new DisplayConfigTargetDeviceName(Adapter.AdapterId, TargetId); var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref targetName); if (result == Win32Status.Success) { return targetName.MonitorFriendlyDeviceName; } throw new Win32Exception((int) result); } } /// /// Gets a boolean value indicating the device availability /// public bool IsAvailable { get; } /// /// Gets the display device preferred resolution /// /// The target is not available /// Error code can be retrieved from Win32Exception.NativeErrorCode property public Size PreferredResolution { get { if (!IsAvailable) { throw new TargetNotAvailableException("Extra information about the target is not available.", Adapter.AdapterId, TargetId); } var targetPreferredMode = new DisplayConfigTargetPreferredMode(Adapter.AdapterId, TargetId); var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref targetPreferredMode); if (result == Win32Status.Success) { return new Size((int) targetPreferredMode.Width, (int) targetPreferredMode.Height); } throw new Win32Exception((int) result); } } /// /// Gets the display device preferred signal information /// /// The target is not available /// Error code can be retrieved from Win32Exception.NativeErrorCode property public PathTargetSignalInfo PreferredSignalMode { get { if (!IsAvailable) { throw new TargetNotAvailableException("Extra information about the target is not available.", Adapter.AdapterId, TargetId); } var targetPreferredMode = new DisplayConfigTargetPreferredMode(Adapter.AdapterId, TargetId); var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref targetPreferredMode); if (result == Win32Status.Success) { return new PathTargetSignalInfo(targetPreferredMode.TargetMode.TargetVideoSignalInfo); } throw new Win32Exception((int) result); } } /// /// Gets the target identification /// public uint TargetId { get; } /// /// Gets or sets the device virtual resolution support /// /// The target is not available /// Error code can be retrieved from Win32Exception.NativeErrorCode property public bool VirtualResolutionSupport { get { if (!IsAvailable) { throw new TargetNotAvailableException("Extra information about the target is not available.", Adapter.AdapterId, TargetId); } var targetSupportVirtualResolution = new DisplayConfigSupportVirtualResolution(Adapter.AdapterId, TargetId); var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref targetSupportVirtualResolution); if (result == Win32Status.Success) { return !targetSupportVirtualResolution.DisableMonitorVirtualResolution; } throw new Win32Exception((int) result); } set { if (!IsAvailable) { throw new TargetNotAvailableException("Extra information about the target is not available.", Adapter.AdapterId, TargetId); } var targetSupportVirtualResolution = new DisplayConfigSupportVirtualResolution(Adapter.AdapterId, TargetId, !value); var result = DisplayConfigApi.DisplayConfigSetDeviceInfo(ref targetSupportVirtualResolution); if (result != Win32Status.Success) { throw new Win32Exception((int) result); } } } /// public bool Equals(PathDisplayTarget other) { if (ReferenceEquals(null, other)) { return false; } if (ReferenceEquals(this, other)) { return true; } return Adapter == other.Adapter && TargetId == other.TargetId; } /// /// Retrieving a list of all display targets from the currently active and inactive paths /// /// An array of PathDisplayTarget instances public static PathDisplayTarget[] GetDisplayTargets() { var targets = new Dictionary, PathDisplayTarget>(); foreach (var pathInfo in PathInfo.GetAllPaths()) foreach (var pathTargetInfo in pathInfo.TargetsInfo.Where(info => info.DisplayTarget.IsAvailable)) { var key = Tuple.Create( pathTargetInfo.DisplayTarget.Adapter.AdapterId, pathTargetInfo.DisplayTarget.TargetId ); if (!pathTargetInfo.DisplayTarget.Adapter.IsInvalid && !targets.ContainsKey(key)) { targets.Add(key, pathTargetInfo.DisplayTarget); } } return targets.Values.ToArray(); } /// /// Checks for equality of two PathDisplayTarget instances /// /// The first instance /// The second instance /// true if both instances are equal, otherwise false public static bool operator ==(PathDisplayTarget left, PathDisplayTarget right) { return Equals(left, right) || left?.Equals(right) == true; } /// /// Checks for inequality of two PathDisplayTarget instances /// /// The first instance /// The second instance /// true if both instances are not equal, otherwise false public static bool operator !=(PathDisplayTarget left, PathDisplayTarget right) { return !(left == right); } /// public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } return obj.GetType() == GetType() && Equals((PathDisplayTarget) obj); } /// public override int GetHashCode() { unchecked { return ((Adapter != null ? Adapter.GetHashCode() : 0) * 397) ^ (int) TargetId; } } /// public override string ToString() { return FriendlyName; } #if !NETSTANDARD /// /// Opens the registry key of the Windows PnP manager for this display target /// /// A RegistryKey instance for successful call, otherwise null public Microsoft.Win32.RegistryKey OpenDevicePnPKey() { if (string.IsNullOrWhiteSpace(DevicePath)) { return null; } var path = DevicePath; if (path.StartsWith("\\\\?\\")) { path = path.Substring(4).Replace("#", "\\"); if (path.EndsWith("}")) { var guidIndex = path.LastIndexOf("{", StringComparison.InvariantCulture); if (guidIndex > 0) { path = path.Substring(0, guidIndex); } } } return Microsoft.Win32.Registry.LocalMachine.OpenSubKey( "SYSTEM\\CurrentControlSet\\Enum\\" + path, Microsoft.Win32.RegistryKeyPermissionCheck.ReadSubTree ); } #endif /// /// Returns the corresponding instance /// /// An instance of , or null public DisplayDevice ToDisplayDevice() { return DisplayAdapter.GetDisplayAdapters() .SelectMany(adapter => adapter.GetDisplayDevices()) .FirstOrDefault(device => device.DevicePath.Equals(DevicePath)); } } }