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