Gamma Init

This commit is contained in:
Serge
2024-02-16 15:55:37 +01:00
parent 42a598f177
commit cf84fa0616
103 changed files with 7907 additions and 41 deletions

View File

@@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using WindowsDisplayAPI.Native;
using WindowsDisplayAPI.Native.DisplayConfig.Structures;
using WindowsDisplayAPI.Native.Structures;
namespace WindowsDisplayAPI.DisplayConfig
{
/// <summary>
/// Represents a path display adapter
/// </summary>
public class PathDisplayAdapter : IEquatable<PathDisplayAdapter>
{
/// <summary>
/// Creates a new PathDisplayAdapter
/// </summary>
/// <param name="adapterId">The adapter local unique identification</param>
public PathDisplayAdapter(LUID adapterId)
{
AdapterId = adapterId;
}
/// <summary>
/// Gets the display adapter local identification LUID
/// </summary>
public LUID AdapterId { get; }
/// <summary>
/// Gets the display adapter device path
/// </summary>
/// <exception cref="Win32Exception">Error code can be retrieved from Win32Exception.NativeErrorCode property</exception>
public string DevicePath
{
get
{
var adapterName = new DisplayConfigAdapterName(AdapterId);
var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref adapterName);
if (result == Win32Status.Success)
{
return adapterName.AdapterDevicePath;
}
throw new Win32Exception((int) result);
}
}
/// <summary>
/// Gets a boolean value indicating the instance validity
/// </summary>
public bool IsInvalid
{
get => AdapterId.IsEmpty();
}
/// <inheritdoc />
public bool Equals(PathDisplayAdapter other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return AdapterId == other.AdapterId;
}
/// <summary>
/// Retrieving a list of all adapters from the currently active and inactive paths
/// </summary>
/// <returns>An array of PathDisplayAdapter instances</returns>
public static PathDisplayAdapter[] GetAdapters()
{
var adapters = new Dictionary<LUID, PathDisplayAdapter>();
foreach (var pathInfo in PathInfo.GetAllPaths())
{
if (!pathInfo.DisplaySource.Adapter.IsInvalid &&
!adapters.ContainsKey(pathInfo.DisplaySource.Adapter.AdapterId))
{
adapters.Add(pathInfo.DisplaySource.Adapter.AdapterId, pathInfo.DisplaySource.Adapter);
}
foreach (var pathTargetInfo in pathInfo.TargetsInfo)
{
if (!pathTargetInfo.DisplayTarget.Adapter.IsInvalid &&
!adapters.ContainsKey(pathTargetInfo.DisplayTarget.Adapter.AdapterId))
{
adapters.Add(pathTargetInfo.DisplayTarget.Adapter.AdapterId, pathTargetInfo.DisplayTarget.Adapter);
}
}
}
return adapters.Values.ToArray();
}
/// <summary>
/// Checks for equality of two PathDisplayAdapter instances
/// </summary>
/// <param name="left">The first instance</param>
/// <param name="right">The second instance</param>
/// <returns>true if both instances are equal, otherwise false</returns>
public static bool operator ==(PathDisplayAdapter left, PathDisplayAdapter right)
{
return Equals(left, right) || left?.Equals(right) == true;
}
/// <summary>
/// Checks for inequality of two PathDisplayAdapter instances
/// </summary>
/// <param name="left">The first instance</param>
/// <param name="right">The second instance</param>
/// <returns>true if both instances are not equal, otherwise false</returns>
public static bool operator !=(PathDisplayAdapter left, PathDisplayAdapter right)
{
return !(left == right);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj.GetType() == GetType() && Equals((PathDisplayAdapter) obj);
}
/// <inheritdoc />
public override int GetHashCode()
{
return AdapterId.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return DevicePath;
}
#if !NETSTANDARD
/// <summary>
/// Opens the registry key of the Windows PnP manager for this display adapter
/// </summary>
/// <returns>A RegistryKey instance for successful call, otherwise null</returns>
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
/// <summary>
/// Gets the corresponding DisplayAdapter instance
/// </summary>
/// <returns>An instance of DisplayAdapter, or null</returns>
public DisplayAdapter ToDisplayAdapter()
{
return DisplayAdapter.GetDisplayAdapters()
.FirstOrDefault(
adapter => DevicePath.StartsWith("\\\\?\\" + adapter.DevicePath.Replace("\\", "#"))
);
}
}
}

View File

@@ -0,0 +1,238 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using WindowsDisplayAPI.Native;
using WindowsDisplayAPI.Native.DisplayConfig;
using WindowsDisplayAPI.Native.DisplayConfig.Structures;
using WindowsDisplayAPI.Native.Structures;
namespace WindowsDisplayAPI.DisplayConfig
{
/// <summary>
/// Represents a display path source
/// </summary>
public class PathDisplaySource : IEquatable<PathDisplaySource>
{
private static readonly DisplayConfigSourceDPIScale[] DPIScales = Enum
.GetValues(typeof(DisplayConfigSourceDPIScale))
.Cast<DisplayConfigSourceDPIScale>()
.OrderBy(scaling => (uint) scaling)
.ToArray();
/// <summary>
/// Creates a new PathDisplaySource
/// </summary>
/// <param name="adapter">Display adapter</param>
/// <param name="sourceId">Display source identification</param>
public PathDisplaySource(PathDisplayAdapter adapter, uint sourceId)
{
Adapter = adapter;
SourceId = sourceId;
}
/// <summary>
/// Gets the path display adapter
/// </summary>
public PathDisplayAdapter Adapter { get; }
/// <summary>
/// Gets and sets the current source DPI scaling
/// </summary>
public DisplayConfigSourceDPIScale CurrentDPIScale
{
get
{
var dpiScale = new DisplayConfigGetSourceDPIScale(Adapter.AdapterId, SourceId);
var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref dpiScale);
if (result != Win32Status.Success)
{
throw new Win32Exception((int) result);
}
var currentScaleIndex = Math.Abs(dpiScale.MinimumScaleSteps) + dpiScale.CurrentScaleSteps;
return DPIScales[currentScaleIndex];
}
set
{
var currentScaleStep = Array.IndexOf(DPIScales, value) - Array.IndexOf(DPIScales, RecommendedDPIScale);
var dpiScale = new DisplayConfigSetSourceDPIScale(Adapter.AdapterId, SourceId, currentScaleStep);
var result = DisplayConfigApi.DisplayConfigSetDeviceInfo(ref dpiScale);
if (result != Win32Status.Success)
{
throw new Win32Exception((int) result);
}
}
}
/// <summary>
/// Returns the corresponding <see cref="DisplayScreen"/> instance.
/// </summary>
public DisplayScreen ToScreen()
{
return DisplayScreen.GetScreens().FirstOrDefault(info => info.ScreenName.Equals(DisplayName));
}
/// <summary>
/// Gets the display name
/// </summary>
/// <exception cref="Win32Exception">Error code can be retrieved from Win32Exception.NativeErrorCode property</exception>
public string DisplayName
{
get
{
var sourceName = new DisplayConfigSourceDeviceName(Adapter.AdapterId, SourceId);
var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref sourceName);
if (result == Win32Status.Success)
{
return sourceName.DeviceName;
}
throw new Win32Exception((int) result);
}
}
/// <summary>
/// Gets the maximum DPI scaling for this source
/// </summary>
public DisplayConfigSourceDPIScale MaximumDPIScale
{
get
{
var dpiScale = new DisplayConfigGetSourceDPIScale(Adapter.AdapterId, SourceId);
var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref dpiScale);
if (result != Win32Status.Success)
{
throw new Win32Exception((int) result);
}
var currentScaleIndex = Math.Abs(dpiScale.MinimumScaleSteps) + dpiScale.MaximumScaleSteps;
return DPIScales[currentScaleIndex];
}
}
/// <summary>
/// Gets the recommended DPI scaling for this source
/// </summary>
public DisplayConfigSourceDPIScale RecommendedDPIScale
{
get
{
var dpiScale = new DisplayConfigGetSourceDPIScale(Adapter.AdapterId, SourceId);
var result = DisplayConfigApi.DisplayConfigGetDeviceInfo(ref dpiScale);
if (result != Win32Status.Success)
{
throw new Win32Exception((int) result);
}
return DPIScales[Math.Abs(dpiScale.MinimumScaleSteps)];
}
}
/// <summary>
/// Gets the zero based display identification
/// </summary>
public uint SourceId { get; }
/// <inheritdoc />
public bool Equals(PathDisplaySource other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Adapter == other.Adapter && SourceId == other.SourceId;
}
/// <summary>
/// Retrieving a list of all display sources from the currently active and inactive paths
/// </summary>
/// <returns>An array of PathDisplaySource instances</returns>
public static PathDisplaySource[] GetDisplaySources()
{
var sources = new Dictionary<Tuple<LUID, uint>, PathDisplaySource>();
foreach (var pathInfo in PathInfo.GetAllPaths())
{
var key = Tuple.Create(
pathInfo.DisplaySource.Adapter.AdapterId,
pathInfo.DisplaySource.SourceId
);
if (!pathInfo.DisplaySource.Adapter.IsInvalid && !sources.ContainsKey(key))
{
sources.Add(key, pathInfo.DisplaySource);
}
}
return sources.Values.ToArray();
}
/// <summary>
/// Checks for equality of two PathDisplaySource instances
/// </summary>
/// <param name="left">The first instance</param>
/// <param name="right">The second instance</param>
/// <returns>true if both instances are equal, otherwise false</returns>
public static bool operator ==(PathDisplaySource left, PathDisplaySource right)
{
return Equals(left, right) || left?.Equals(right) == true;
}
/// <summary>
/// Checks for inequality of two PathDisplaySource instances
/// </summary>
/// <param name="left">The first instance</param>
/// <param name="right">The second instance</param>
/// <returns>true if both instances are not equal, otherwise false</returns>
public static bool operator !=(PathDisplaySource left, PathDisplaySource right)
{
return !(left == right);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj.GetType() == GetType() && Equals((PathDisplaySource) obj);
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return ((Adapter != null ? Adapter.GetHashCode() : 0) * 397) ^ (int) SourceId;
}
}
/// <inheritdoc />
public override string ToString()
{
return DisplayName;
}
}
}

View File

@@ -0,0 +1,481 @@
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
{
/// <summary>
/// Represents a display path target (Display Device)
/// </summary>
public class PathDisplayTarget : IEquatable<PathDisplayTarget>
{
/// <summary>
/// Creates a new PathDisplayTarget
/// </summary>
/// <param name="adapter">Display adapter</param>
/// <param name="targetId">Display target identification</param>
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;
}
/// <summary>
/// Gets the path display adapter
/// </summary>
public PathDisplayAdapter Adapter { get; }
/// <summary>
/// Sets the display boot persistence for the target display device
/// </summary>
/// <exception cref="TargetNotAvailableException"></exception>
/// <exception cref="Win32Exception"></exception>
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);
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <exception cref="Win32Exception">Error code can be retrieved from Win32Exception.NativeErrorCode property</exception>
/// <exception cref="TargetNotAvailableException">The target is not available</exception>
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);
}
}
/// <summary>
/// Gets the display device path
/// </summary>
/// <exception cref="Win32Exception">Error code can be retrieved from Win32Exception.NativeErrorCode property</exception>
/// <exception cref="TargetNotAvailableException">The target is not available</exception>
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);
}
}
/// <summary>
/// Gets the display manufacture 3 character code from the display EDID manufacture identification
/// </summary>
/// <exception cref="TargetNotAvailableException">The target is not available</exception>
/// <exception cref="Win32Exception">Error code can be retrieved from Win32Exception.NativeErrorCode property</exception>
/// <exception cref="InvalidEDIDInformation">The EDID information does not contain this value</exception>
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)}";
}
}
/// <summary>
/// Gets the display manufacture identification from the display EDID information
/// </summary>
/// <exception cref="TargetNotAvailableException">The target is not available</exception>
/// <exception cref="Win32Exception">Error code can be retrieved from Win32Exception.NativeErrorCode property</exception>
/// <exception cref="InvalidEDIDInformation">The EDID information does not contain this value</exception>
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);
}
}
/// <summary>
/// Gets the display product identification from the display EDID information
/// </summary>
/// <exception cref="TargetNotAvailableException">The target is not available</exception>
/// <exception cref="Win32Exception">Error code can be retrieved from Win32Exception.NativeErrorCode property</exception>
/// <exception cref="InvalidEDIDInformation">The EDID information does not contain this value</exception>
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);
}
}
/// <summary>
/// Gets the display friendly name from the display EDID information
/// </summary>
/// <exception cref="TargetNotAvailableException">The target is not available</exception>
/// <exception cref="Win32Exception">Error code can be retrieved from Win32Exception.NativeErrorCode property</exception>
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);
}
}
/// <summary>
/// Gets a boolean value indicating the device availability
/// </summary>
public bool IsAvailable { get; }
/// <summary>
/// Gets the display device preferred resolution
/// </summary>
/// <exception cref="TargetNotAvailableException">The target is not available</exception>
/// <exception cref="Win32Exception">Error code can be retrieved from Win32Exception.NativeErrorCode property</exception>
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);
}
}
/// <summary>
/// Gets the display device preferred signal information
/// </summary>
/// <exception cref="TargetNotAvailableException">The target is not available</exception>
/// <exception cref="Win32Exception">Error code can be retrieved from Win32Exception.NativeErrorCode property</exception>
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);
}
}
/// <summary>
/// Gets the target identification
/// </summary>
public uint TargetId { get; }
/// <summary>
/// Gets or sets the device virtual resolution support
/// </summary>
/// <exception cref="TargetNotAvailableException">The target is not available</exception>
/// <exception cref="Win32Exception">Error code can be retrieved from Win32Exception.NativeErrorCode property</exception>
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);
}
}
}
/// <inheritdoc />
public bool Equals(PathDisplayTarget other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Adapter == other.Adapter && TargetId == other.TargetId;
}
/// <summary>
/// Retrieving a list of all display targets from the currently active and inactive paths
/// </summary>
/// <returns>An array of PathDisplayTarget instances</returns>
public static PathDisplayTarget[] GetDisplayTargets()
{
var targets = new Dictionary<Tuple<LUID, uint>, 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();
}
/// <summary>
/// Checks for equality of two PathDisplayTarget instances
/// </summary>
/// <param name="left">The first instance</param>
/// <param name="right">The second instance</param>
/// <returns>true if both instances are equal, otherwise false</returns>
public static bool operator ==(PathDisplayTarget left, PathDisplayTarget right)
{
return Equals(left, right) || left?.Equals(right) == true;
}
/// <summary>
/// Checks for inequality of two PathDisplayTarget instances
/// </summary>
/// <param name="left">The first instance</param>
/// <param name="right">The second instance</param>
/// <returns>true if both instances are not equal, otherwise false</returns>
public static bool operator !=(PathDisplayTarget left, PathDisplayTarget right)
{
return !(left == right);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return ((Adapter != null ? Adapter.GetHashCode() : 0) * 397) ^ (int) TargetId;
}
}
/// <inheritdoc />
public override string ToString()
{
return FriendlyName;
}
#if !NETSTANDARD
/// <summary>
/// Opens the registry key of the Windows PnP manager for this display target
/// </summary>
/// <returns>A RegistryKey instance for successful call, otherwise null</returns>
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
/// <summary>
/// Returns the corresponding <see cref="DisplayDevice"/> instance
/// </summary>
/// <returns>An instance of <see cref="DisplayDevice"/>, or null</returns>
public DisplayDevice ToDisplayDevice()
{
return
DisplayAdapter.GetDisplayAdapters()
.SelectMany(adapter => adapter.GetDisplayDevices())
.FirstOrDefault(device => device.DevicePath.Equals(DevicePath));
}
}
}

View File

@@ -0,0 +1,910 @@
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
{
/// <summary>
/// Represents a path root information
/// </summary>
public class PathInfo
{
private readonly uint _cloneGroupId;
private readonly DisplayConfigPixelFormat _pixelFormat;
private readonly Point _position;
private readonly Size _resolution;
/// <summary>
/// Creates a new PathInfo
/// </summary>
/// <param name="displaySource">The display source</param>
/// <param name="position">The display position in desktop</param>
/// <param name="resolution">The display resolution</param>
/// <param name="pixelFormat">The display pixel format</param>
public PathInfo(
PathDisplaySource displaySource,
Point position,
Size resolution,
DisplayConfigPixelFormat pixelFormat
) : this(displaySource)
{
_position = position;
_resolution = resolution;
_pixelFormat = pixelFormat;
IsModeInformationAvailable = true;
}
/// <summary>
/// Creates a new PathInfo
/// </summary>
/// <param name="displaySource">The display source</param>
/// <param name="position">The display position in desktop</param>
/// <param name="resolution">The display resolution</param>
/// <param name="pixelFormat">The display pixel format</param>
/// <param name="cloneGroup">The display clone group, only valid for virtual aware paths</param>
public PathInfo(
PathDisplaySource displaySource,
Point position,
Size resolution,
DisplayConfigPixelFormat pixelFormat,
uint cloneGroup
) : this(displaySource, cloneGroup)
{
_position = position;
_resolution = resolution;
_pixelFormat = pixelFormat;
IsModeInformationAvailable = true;
}
/// <summary>
/// Creates a new PathInfo
/// </summary>
/// <param name="displaySource">The display source</param>
/// <param name="position">The display position in desktop</param>
/// <param name="resolution">The display resolution</param>
/// <param name="pixelFormat">The display pixel format</param>
/// <param name="pathTargetInfos">An array of target information</param>
public PathInfo(
PathDisplaySource displaySource,
Point position,
Size resolution,
DisplayConfigPixelFormat pixelFormat,
PathTargetInfo[] pathTargetInfos
) : this(displaySource, position, resolution, pixelFormat)
{
TargetsInfo = pathTargetInfos;
}
/// <summary>
/// Creates a new PathInfo
/// </summary>
/// <param name="displaySource">The display source</param>
/// <param name="position">The display position in desktop</param>
/// <param name="resolution">The display resolution</param>
/// <param name="pixelFormat">The display pixel format</param>
/// <param name="pathTargetInfos">An array of target information</param>
/// <param name="cloneGroup">The display clone group, only valid for virtual aware paths</param>
public PathInfo(
PathDisplaySource displaySource,
Point position,
Size resolution,
DisplayConfigPixelFormat pixelFormat,
PathTargetInfo[] pathTargetInfos,
uint cloneGroup
) : this(displaySource, position, resolution, pixelFormat, cloneGroup)
{
TargetsInfo = pathTargetInfos;
}
/// <summary>
/// Creates a new PathInfo
/// </summary>
/// <param name="displaySource">The display source</param>
public PathInfo(PathDisplaySource displaySource)
{
DisplaySource = displaySource;
}
/// <summary>
/// Creates a new PathInfo
/// </summary>
/// <param name="displaySource">The display source</param>
/// <param name="cloneGroup">The display clone group, only valid for virtual aware paths</param>
public PathInfo(PathDisplaySource displaySource, uint cloneGroup) : this(displaySource)
{
IsCloneMember = true;
_cloneGroupId = cloneGroup;
}
/// <summary>
/// Creates a new PathInfo
/// </summary>
/// <param name="displaySource">The display source</param>
/// <param name="pathTargetInfos">An array of target information</param>
public PathInfo(
PathDisplaySource displaySource,
PathTargetInfo[] pathTargetInfos
) : this(displaySource)
{
TargetsInfo = pathTargetInfos;
}
/// <summary>
/// Creates a new PathInfo
/// </summary>
/// <param name="displaySource">The display source</param>
/// <param name="pathTargetInfos">An array of target information</param>
/// <param name="cloneGroup">The display clone group, only valid for virtual aware paths</param>
public PathInfo(
PathDisplaySource displaySource,
PathTargetInfo[] pathTargetInfos,
uint cloneGroup
) : this(displaySource, cloneGroup)
{
TargetsInfo = pathTargetInfos;
}
private PathInfo(
DisplayConfigPathSourceInfo sourceInfo,
DisplayConfigSourceMode? sourceMode,
IEnumerable<
Tuple<
DisplayConfigPathInfoFlags,
DisplayConfigPathTargetInfo,
DisplayConfigTargetMode?,
DisplayConfigDesktopImageInfo?
>
> targets
)
{
DisplaySource = new PathDisplaySource(new PathDisplayAdapter(sourceInfo.AdapterId), sourceInfo.SourceId);
IsInUse = sourceInfo.StatusFlags.HasFlag(DisplayConfigPathSourceInfoFlags.InUse);
IsModeInformationAvailable = sourceMode.HasValue;
if (sourceMode.HasValue)
{
_resolution = new Size((int) sourceMode.Value.Width, (int) sourceMode.Value.Height);
_pixelFormat = sourceMode.Value.PixelFormat;
_position = new Point(sourceMode.Value.Position.X, sourceMode.Value.Position.Y);
}
TargetsInfo = targets.Select(t => new PathTargetInfo(t.Item1, t.Item2, t.Item3, t.Item4)).ToArray();
if (TargetsInfo.Any(info => info.IsVirtualModeSupportedByPath) &&
sourceInfo.CloneGroupId != DisplayConfigPathSourceInfo.InvalidCloneGroupId
)
{
_cloneGroupId = sourceInfo.CloneGroupId;
IsCloneMember = true;
}
}
/// <summary>
/// Gets a valid identifier used to show which clone group the path is a member of
/// </summary>
/// <exception cref="NotACloneMemberException">This path is not a clone member</exception>
public uint CloneGroupId
{
get
{
if (!IsCloneMember)
{
throw new NotACloneMemberException(
"The display source is not part of a clone group."
);
}
return _cloneGroupId;
}
}
/// <summary>
/// Gets extra information about the representing display source
/// </summary>
public PathDisplaySource DisplaySource { get; }
/// <summary>
/// Gets a boolean value indicating if this path is a member of a clone group
/// </summary>
public bool IsCloneMember { get; }
/// <summary>
/// Gets a boolean value indicating if this path is the primary GDI path
/// </summary>
/// <exception cref="MissingModeException">Source mode information is missing</exception>
public bool IsGDIPrimary
{
get => Position.IsEmpty;
}
/// <summary>
/// Gets a boolean value indicating if the source is in use by at least one active path
/// </summary>
public bool IsInUse { get; }
/// <summary>
/// Gets a boolean value indicating if the source mode information is available
/// </summary>
public bool IsModeInformationAvailable { get; }
/// <summary>
/// Gets a boolean value indicating the DisplayConfig (CCD API) availability on this system
/// </summary>
public static bool IsSupported
{
get
{
try
{
return DisplayConfigApi.GetDisplayConfigBufferSizes(
QueryDeviceConfigFlags.AllPaths,
out _,
out _
) == Win32Status.Success;
}
catch
{
return false;
}
}
}
/// <summary>
/// Gets a boolean value indicating the virtual display mode support on this system
/// </summary>
public static bool IsVirtualModeSupported
{
get
{
try
{
return PathDisplayTarget
.GetDisplayTargets()
.Any(t => t.VirtualResolutionSupport);
}
catch
{
return false;
}
}
}
/// <summary>
/// Gets the specifies the pixel format of the source mode
/// </summary>
/// <exception cref="MissingModeException">Source mode information is missing</exception>
public DisplayConfigPixelFormat PixelFormat
{
get
{
if (!IsModeInformationAvailable)
{
throw new MissingModeException(
"Source mode information is missing or not available.",
DisplayConfigModeInfoType.Source
);
}
return _pixelFormat;
}
}
/// <summary>
/// Gets the position in the desktop coordinate space of the upper-left corner of this source surface. The source
/// surface that is located at (0, 0) is always the primary source surface.
/// </summary>
/// <exception cref="MissingModeException">Source mode information is missing</exception>
public Point Position
{
get
{
if (!IsModeInformationAvailable)
{
throw new MissingModeException(
"Source mode information is missing or not available.",
DisplayConfigModeInfoType.Source
);
}
return _position;
}
}
/// <summary>
/// Gets the size of the source mode
/// </summary>
/// <exception cref="MissingModeException">Source mode information is missing</exception>
public Size Resolution
{
get
{
if (!IsModeInformationAvailable)
{
throw new MissingModeException(
"Source mode information is missing or not available.",
DisplayConfigModeInfoType.Source
);
}
return _resolution;
}
}
/// <summary>
/// Gets the list of target information
/// </summary>
public PathTargetInfo[] TargetsInfo { get; } = new PathTargetInfo[0];
/// <summary>
/// Applies an array of paths
/// </summary>
/// <param name="pathInfos">The array of paths</param>
/// <param name="allowChanges">true to allow changes and reordering of the provided paths, otherwise false</param>
/// <param name="saveToDatabase">true to save the paths to the persistence database if call succeed, otherwise false</param>
/// <param name="forceModeEnumeration">true to force driver mode enumeration before applying the paths</param>
/// <exception cref="PathChangeException">Error in changing paths</exception>
public static void ApplyPathInfos(
IEnumerable<PathInfo> pathInfos,
bool allowChanges = true,
bool saveToDatabase = false,
bool forceModeEnumeration = false
)
{
var pathInfosArray = pathInfos.ToArray();
if (!ValidatePathInfos(pathInfosArray, allowChanges))
{
throw new PathChangeException("Invalid paths information.");
}
var displayConfigPathInfos = GetDisplayConfigPathInfos(pathInfosArray, out var displayConfigModeInfos);
if (displayConfigPathInfos.Length <= 0)
{
return;
}
var flags = displayConfigModeInfos.Length == 0
? SetDisplayConfigFlags.TopologySupplied
: SetDisplayConfigFlags.UseSuppliedDisplayConfig;
if (allowChanges)
{
flags |= displayConfigModeInfos.Length == 0
? SetDisplayConfigFlags.AllowPathOrderChanges
: SetDisplayConfigFlags.AllowChanges;
}
else if (displayConfigModeInfos.Length > 0)
{
flags |= SetDisplayConfigFlags.NoOptimization;
}
if (saveToDatabase && displayConfigModeInfos.Length > 0)
{
flags |= SetDisplayConfigFlags.SaveToDatabase;
}
if (forceModeEnumeration && displayConfigModeInfos.Length > 0)
{
flags |= SetDisplayConfigFlags.ForceModeEnumeration;
}
var result =
DisplayConfigApi.SetDisplayConfig(
(uint) displayConfigPathInfos.Length,
displayConfigPathInfos,
(uint) displayConfigModeInfos.Length,
displayConfigModeInfos.Length > 0 ? displayConfigModeInfos : null,
SetDisplayConfigFlags.Apply | flags
);
if (result != Win32Status.Success)
{
throw new PathChangeException(
"An error occurred while applying the paths information.",
new Win32Exception((int) result)
);
}
}
/// <summary>
/// Applies a saved topology
/// </summary>
/// <param name="topology">The topology identification to apply</param>
/// <param name="allowPersistence">true to allows persistence of the changes, otherwise false</param>
/// <exception cref="PathChangeException">Error in changing paths</exception>
public static void ApplyTopology(DisplayConfigTopologyId topology, bool allowPersistence = false)
{
if (!ValidateTopology(topology))
{
throw new PathChangeException("Invalid topology request.");
}
var flags = (SetDisplayConfigFlags) topology;
if (allowPersistence)
{
flags |= SetDisplayConfigFlags.PathPersistIfRequired;
}
var result = DisplayConfigApi.SetDisplayConfig(
0,
null,
0,
null,
SetDisplayConfigFlags.Apply | flags
);
if (result != Win32Status.Success)
{
throw new PathChangeException(
"An error occurred while applying the requested topology.",
new Win32Exception((int) result)
);
}
}
/// <summary>
/// Retrieves the list of active paths
/// </summary>
/// <param name="virtualModeAware">true if the caller expects virtual mode settings, otherwise false</param>
/// <returns>An array of PathInfos</returns>
public static PathInfo[] GetActivePaths(bool virtualModeAware = false)
{
return GetPathInfos(
virtualModeAware
? QueryDeviceConfigFlags.OnlyActivePaths | QueryDeviceConfigFlags.VirtualModeAware
: QueryDeviceConfigFlags.OnlyActivePaths,
out _
);
}
/// <summary>
/// Retrieves the list of all paths, active or inactive
/// </summary>
/// <param name="virtualModeAware">true if the caller expects virtual mode settings, otherwise false</param>
/// <returns>An array of PathInfos</returns>
public static PathInfo[] GetAllPaths(bool virtualModeAware = false)
{
return GetPathInfos(
virtualModeAware
? QueryDeviceConfigFlags.AllPaths | QueryDeviceConfigFlags.VirtualModeAware
: QueryDeviceConfigFlags.AllPaths,
out _
);
}
/// <summary>
/// Retrieves the list of currently active topology paths
/// </summary>
/// <returns>An array of PathInfos</returns>
public static PathInfo[] GetCurrentDatabasePaths()
{
return GetPathInfos(QueryDeviceConfigFlags.DatabaseCurrent, out _);
}
/// <summary>
/// Gets the current active topology identification
/// </summary>
/// <returns>The topology identification</returns>
public static DisplayConfigTopologyId GetCurrentTopology()
{
GetPathInfos(QueryDeviceConfigFlags.DatabaseCurrent, out var currentDatabaseType);
return currentDatabaseType;
}
/// <summary>
/// Validates an array of paths before applying
/// </summary>
/// <param name="pathInfos">The array of paths</param>
/// <param name="allowChanges">true to allow changes and reordering of the provided paths, otherwise false</param>
/// <returns>true if the provided paths are valid, otherwise false</returns>
public static bool ValidatePathInfos(IEnumerable<PathInfo> pathInfos, bool allowChanges = true)
{
var displayConfigPathInfos = GetDisplayConfigPathInfos(pathInfos, out var displayConfigModeInfos);
if (displayConfigPathInfos.Length <= 0)
{
return false;
}
var flags = displayConfigModeInfos.Length == 0
? SetDisplayConfigFlags.TopologySupplied
: SetDisplayConfigFlags.UseSuppliedDisplayConfig;
if (allowChanges)
{
flags |= displayConfigModeInfos.Length == 0
? SetDisplayConfigFlags.AllowPathOrderChanges
: SetDisplayConfigFlags.AllowChanges;
}
return
DisplayConfigApi.SetDisplayConfig(
(uint) displayConfigPathInfos.Length,
displayConfigPathInfos,
(uint) displayConfigModeInfos.Length,
displayConfigModeInfos.Length > 0 ? displayConfigModeInfos : null,
SetDisplayConfigFlags.Validate | flags
) ==
Win32Status.Success;
}
/// <summary>
/// Validates a topology before applying
/// </summary>
/// <param name="topology">The topology identification</param>
/// <returns>true if topology is applicable, otherwise false</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static bool ValidateTopology(DisplayConfigTopologyId topology)
{
if (topology == DisplayConfigTopologyId.None)
{
throw new ArgumentOutOfRangeException(nameof(topology), "Topology should not be empty.");
}
var flags = (SetDisplayConfigFlags) topology;
return DisplayConfigApi.SetDisplayConfig(
0,
null,
0,
null,
SetDisplayConfigFlags.Validate | flags
) ==
Win32Status.Success;
}
private static uint AddMode(ref List<DisplayConfigModeInfo> modes, DisplayConfigModeInfo mode)
{
var existingMode = modes.FindIndex(info =>
info.InfoType == mode.InfoType &&
info.AdapterId == mode.AdapterId &&
info.Id == mode.Id
);
if (existingMode > 0)
{
if (modes[existingMode] == mode)
{
return (uint) existingMode;
}
throw new DuplicateModeException(
"Provided list of path information, contains one or more duplicate but not identical modes."
);
}
modes.Add(mode);
return (uint) modes.Count - 1;
}
// ReSharper disable once CyclomaticComplexity
private static DisplayConfigPathInfo[] GetDisplayConfigPathInfos(
IEnumerable<PathInfo> pathInfos,
out DisplayConfigModeInfo[] modeInfos)
{
var displayConfigPathInfos = new List<DisplayConfigPathInfo>();
var displayConfigModeInfos = new List<DisplayConfigModeInfo>();
foreach (var pathInfo in pathInfos)
{
var sourceMode = pathInfo.GetDisplayConfigSourceMode();
var sourceModeIndex = sourceMode.HasValue
? AddMode(
ref displayConfigModeInfos,
new DisplayConfigModeInfo(
pathInfo.DisplaySource.Adapter.AdapterId,
pathInfo.DisplaySource.SourceId,
sourceMode.Value
)
)
: 0u;
var sourceInfo = pathInfo.IsCloneMember
? new DisplayConfigPathSourceInfo(
pathInfo.DisplaySource.Adapter.AdapterId,
pathInfo.DisplaySource.SourceId,
sourceMode.HasValue ? (ushort) sourceModeIndex : DisplayConfigSourceMode.InvalidSourceModeIndex,
(ushort) pathInfo.CloneGroupId
)
: new DisplayConfigPathSourceInfo(
pathInfo.DisplaySource.Adapter.AdapterId,
pathInfo.DisplaySource.SourceId,
sourceMode.HasValue ? sourceModeIndex : DisplayConfigModeInfo.InvalidModeIndex
);
if (pathInfo.TargetsInfo == null || pathInfo.TargetsInfo.Length == 0)
{
displayConfigPathInfos.Add(
new DisplayConfigPathInfo(sourceInfo,
DisplayConfigPathInfoFlags.Active
)
);
}
else
{
foreach (var target in pathInfo.TargetsInfo)
{
var flags = DisplayConfigPathInfoFlags.None;
if (target.IsPathActive)
{
flags |= DisplayConfigPathInfoFlags.Active;
}
if (target.IsVirtualModeSupportedByPath)
{
flags |= DisplayConfigPathInfoFlags.SupportVirtualMode;
}
var targetMode = target.GetDisplayConfigTargetMode();
var targetModeIndex = targetMode.HasValue
? AddMode(ref displayConfigModeInfos,
new DisplayConfigModeInfo(
target.DisplayTarget.Adapter.AdapterId,
target.DisplayTarget.TargetId, targetMode.Value
)
)
: 0u;
DisplayConfigPathTargetInfo targetInfo;
if (target.IsVirtualModeSupportedByPath)
{
sourceInfo = new DisplayConfigPathSourceInfo(
pathInfo.DisplaySource.Adapter.AdapterId,
pathInfo.DisplaySource.SourceId,
sourceMode.HasValue
? (ushort) sourceModeIndex
: DisplayConfigSourceMode.InvalidSourceModeIndex,
pathInfo.IsCloneMember
? (ushort) pathInfo.CloneGroupId
: DisplayConfigPathSourceInfo.InvalidCloneGroupId
);
var desktopMode = target.GetDisplayConfigDesktopImageInfo();
var desktopModeIndex = desktopMode.HasValue
? AddMode(ref displayConfigModeInfos,
new DisplayConfigModeInfo(
target.DisplayTarget.Adapter.AdapterId,
target.DisplayTarget.TargetId,
desktopMode.Value
)
)
: 0u;
targetInfo = new DisplayConfigPathTargetInfo(
target.DisplayTarget.Adapter.AdapterId,
target.DisplayTarget.TargetId,
targetMode.HasValue
? (ushort) targetModeIndex
: DisplayConfigTargetMode.InvalidTargetModeIndex,
desktopMode.HasValue
? (ushort) desktopModeIndex
: DisplayConfigDesktopImageInfo.InvalidDesktopImageModeIndex,
target.OutputTechnology,
target.Rotation,
target.Scaling,
target.ScanLineOrdering == DisplayConfigScanLineOrdering.NotSpecified
? new DisplayConfigRational()
: new DisplayConfigRational(target.FrequencyInMillihertz, 1000, true),
target.ScanLineOrdering,
true
);
}
else
{
targetInfo = new DisplayConfigPathTargetInfo(
target.DisplayTarget.Adapter.AdapterId,
target.DisplayTarget.TargetId,
targetMode.HasValue ? targetModeIndex : DisplayConfigModeInfo.InvalidModeIndex,
target.OutputTechnology,
target.Rotation,
target.Scaling,
target.ScanLineOrdering == DisplayConfigScanLineOrdering.NotSpecified
? new DisplayConfigRational()
: new DisplayConfigRational(target.FrequencyInMillihertz, 1000, true),
target.ScanLineOrdering,
true
);
}
displayConfigPathInfos.Add(new DisplayConfigPathInfo(sourceInfo, targetInfo, flags));
}
}
}
modeInfos = displayConfigModeInfos.ToArray();
return displayConfigPathInfos.ToArray();
}
private static PathInfo[] GetPathInfos(QueryDeviceConfigFlags flags, out DisplayConfigTopologyId topologyId)
{
DisplayConfigPathInfo[] displayPaths;
DisplayConfigModeInfo[] displayModes;
uint pathCount;
while (true)
{
var error = DisplayConfigApi.GetDisplayConfigBufferSizes(flags,
out pathCount,
out var modeCount);
if (error != Win32Status.Success)
{
throw new Win32Exception((int) error);
}
displayPaths = new DisplayConfigPathInfo[pathCount];
displayModes = new DisplayConfigModeInfo[modeCount];
if (flags == QueryDeviceConfigFlags.DatabaseCurrent)
{
error = DisplayConfigApi.QueryDisplayConfig(
flags,
ref pathCount,
displayPaths,
ref modeCount,
displayModes,
out topologyId
);
}
else
{
topologyId = DisplayConfigTopologyId.None;
error = DisplayConfigApi.QueryDisplayConfig(
flags,
ref pathCount,
displayPaths,
ref modeCount,
displayModes,
IntPtr.Zero
);
}
if (error == Win32Status.Success)
{
break;
}
if (error != Win32Status.ErrorInsufficientBuffer)
{
throw new Win32Exception((int) error);
}
}
var pathInfos =
new Dictionary<
uint,
Tuple<
DisplayConfigPathSourceInfo,
DisplayConfigSourceMode?,
List<
Tuple<
DisplayConfigPathInfoFlags,
DisplayConfigPathTargetInfo,
DisplayConfigTargetMode?,
DisplayConfigDesktopImageInfo?
>
>
>
>();
var sourceId = uint.MaxValue;
for (var i = 0u; i < pathCount; i++)
{
var displayPath = displayPaths[i];
DisplayConfigSourceMode? sourceMode = null;
var key = sourceId;
var isVirtualSupported = displayPath.Flags.HasFlag(DisplayConfigPathInfoFlags.SupportVirtualMode);
if (isVirtualSupported &&
displayPath.SourceInfo.SourceModeInfoIndex != DisplayConfigSourceMode.InvalidSourceModeIndex &&
displayModes[displayPath.SourceInfo.SourceModeInfoIndex].InfoType ==
DisplayConfigModeInfoType.Source)
{
sourceMode = displayModes[displayPath.SourceInfo.SourceModeInfoIndex].SourceMode;
key = displayPath.SourceInfo.SourceModeInfoIndex;
}
else if (!isVirtualSupported &&
displayPath.SourceInfo.ModeInfoIndex != DisplayConfigModeInfo.InvalidModeIndex &&
displayModes[displayPath.SourceInfo.ModeInfoIndex].InfoType ==
DisplayConfigModeInfoType.Source)
{
sourceMode = displayModes[displayPath.SourceInfo.ModeInfoIndex].SourceMode;
key = displayPath.SourceInfo.ModeInfoIndex;
}
else
{
sourceId--;
}
if (!pathInfos.ContainsKey(key))
{
pathInfos.Add(
key,
Tuple.Create(
displayPath.SourceInfo,
sourceMode,
new List<
Tuple<
DisplayConfigPathInfoFlags,
DisplayConfigPathTargetInfo,
DisplayConfigTargetMode?,
DisplayConfigDesktopImageInfo?
>
>()
)
);
}
DisplayConfigTargetMode? targetMode = null;
if (isVirtualSupported &&
displayPath.TargetInfo.TargetModeInfoIndex != DisplayConfigTargetMode.InvalidTargetModeIndex &&
displayModes[displayPath.TargetInfo.TargetModeInfoIndex].InfoType == DisplayConfigModeInfoType.Target
)
{
targetMode = displayModes[displayPath.TargetInfo.TargetModeInfoIndex].TargetMode;
}
else if (!isVirtualSupported &&
displayPath.TargetInfo.ModeInfoIndex != DisplayConfigModeInfo.InvalidModeIndex &&
displayModes[displayPath.TargetInfo.ModeInfoIndex].InfoType == DisplayConfigModeInfoType.Target
)
{
targetMode = displayModes[displayPath.TargetInfo.ModeInfoIndex].TargetMode;
}
DisplayConfigDesktopImageInfo? desktopImageMode = null;
if (isVirtualSupported &&
displayPath.TargetInfo.DesktopModeInfoIndex !=
DisplayConfigDesktopImageInfo.InvalidDesktopImageModeIndex &&
displayModes[displayPath.TargetInfo.DesktopModeInfoIndex].InfoType ==
DisplayConfigModeInfoType.DesktopImage)
{
desktopImageMode = displayModes[displayPath.TargetInfo.DesktopModeInfoIndex].DesktopImageInfo;
}
pathInfos[key].Item3.Add(
Tuple.Create(displayPath.Flags, displayPath.TargetInfo, targetMode, desktopImageMode)
);
}
return pathInfos.Select(
pair => new PathInfo(pair.Value.Item1, pair.Value.Item2, pair.Value.Item3)
).ToArray();
}
/// <inheritdoc />
public override string ToString()
{
return $"{DisplaySource}: {Resolution} @ {Position}";
}
private DisplayConfigSourceMode? GetDisplayConfigSourceMode()
{
if (IsModeInformationAvailable)
{
return new DisplayConfigSourceMode(
(uint) Resolution.Width,
(uint) Resolution.Height,
PixelFormat,
new PointL(Position)
);
}
return null;
}
}
}

View File

@@ -0,0 +1,140 @@
using System;
using System.Drawing;
using WindowsDisplayAPI.Native.DisplayConfig.Structures;
using WindowsDisplayAPI.Native.Structures;
namespace WindowsDisplayAPI.DisplayConfig
{
/// <summary>
/// Contains information about the target desktop image
/// </summary>
public class PathTargetDesktopImage : IEquatable<PathTargetDesktopImage>
{
/// <summary>
/// Creates a new PathTargetDesktopImage
/// </summary>
/// <param name="monitorSurfaceSize">Size of the VidPn source surface that is being displayed on the monitor</param>
/// <param name="imageRegion">
/// Where the desktop image will be positioned within monitor surface size. Region must be
/// completely inside the bounds of the monitor surface size.
/// </param>
/// <param name="imageClip">
/// Which part of the desktop image for this clone group will be displayed on this path. This
/// currently must be set to the desktop size.
/// </param>
public PathTargetDesktopImage(Size monitorSurfaceSize, Rectangle imageRegion, Rectangle imageClip)
{
ImageClip = imageClip;
ImageRegion = imageRegion;
MonitorSurfaceSize = monitorSurfaceSize;
}
internal PathTargetDesktopImage(DisplayConfigDesktopImageInfo desktopImage)
{
MonitorSurfaceSize = desktopImage.PathSourceSize.ToSize();
ImageRegion = desktopImage.DesktopImageRegion.ToRectangle();
ImageClip = desktopImage.DesktopImageClip.ToRectangle();
}
/// <summary>
/// Gets part of the desktop image for this clone group that will be displayed on this path. This currently must be set
/// to the desktop size.
/// </summary>
public Rectangle ImageClip { get; }
/// <summary>
/// Gets the part that the desktop image will be positioned within monitor surface size. Region must be completely
/// inside the bounds of the monitor surface size.
/// </summary>
public Rectangle ImageRegion { get; }
/// <summary>
/// Gets the size of the VidPn source surface that is being displayed on the monitor
/// </summary>
public Size MonitorSurfaceSize { get; }
/// <inheritdoc />
public bool Equals(PathTargetDesktopImage other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return ImageClip == other.ImageClip &&
ImageRegion == other.ImageRegion &&
MonitorSurfaceSize == other.MonitorSurfaceSize;
}
/// <summary>
/// Checks for equality of two PathTargetDesktopImage instances
/// </summary>
/// <param name="left">The first instance</param>
/// <param name="right">The second instance</param>
/// <returns>true if both instances are equal, otherwise false</returns>
public static bool operator ==(PathTargetDesktopImage left, PathTargetDesktopImage right)
{
return Equals(left, right) || left?.Equals(right) == true;
}
/// <summary>
/// Checks for inequality of two PathTargetDesktopImage instances
/// </summary>
/// <param name="left">The first instance</param>
/// <param name="right">The second instance</param>
/// <returns>true if both instances are not equal, otherwise false</returns>
public static bool operator !=(PathTargetDesktopImage left, PathTargetDesktopImage right)
{
return !(left == right);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj.GetType() == GetType() && Equals((PathTargetDesktopImage) obj);
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
var hashCode = ImageClip.GetHashCode();
hashCode = (hashCode * 397) ^ ImageRegion.GetHashCode();
hashCode = (hashCode * 397) ^ MonitorSurfaceSize.GetHashCode();
return hashCode;
}
}
/// <inheritdoc />
public override string ToString()
{
return $"{ImageClip} => {ImageRegion} @ {MonitorSurfaceSize}";
}
internal DisplayConfigDesktopImageInfo GetDisplayConfigDesktopImageInfo()
{
return new DisplayConfigDesktopImageInfo(
new PointL(MonitorSurfaceSize),
new RectangleL(ImageRegion),
new RectangleL(ImageClip)
);
}
}
}

View File

@@ -0,0 +1,330 @@
using WindowsDisplayAPI.Exceptions;
using WindowsDisplayAPI.Native.DisplayConfig;
using WindowsDisplayAPI.Native.DisplayConfig.Structures;
namespace WindowsDisplayAPI.DisplayConfig
{
/// <summary>
/// Represents a path and its target
/// </summary>
public class PathTargetInfo
{
private readonly PathTargetDesktopImage _desktopImage;
private readonly PathTargetSignalInfo _signalInfo;
/// <summary>
/// Creates a new PathTargetInfo
/// </summary>
/// <param name="displayTarget">The display target device</param>
/// <param name="isVirtualModeSupported">A boolean value indicating the target virtual mode support</param>
public PathTargetInfo(PathDisplayTarget displayTarget, bool isVirtualModeSupported = false)
{
DisplayTarget = displayTarget;
IsVirtualModeSupportedByPath = isVirtualModeSupported;
}
/// <summary>
/// Creates a new PathTargetInfo
/// </summary>
/// <param name="displayTarget">The display target device</param>
/// <param name="frequencyInMillihertz">Display frequency in millihertz</param>
/// <param name="scanLineOrdering">Display scan line ordering</param>
/// <param name="rotation">Display rotation</param>
/// <param name="scaling">Display scaling</param>
/// <param name="isVirtualModeSupported">A boolean value indicating the target virtual mode support</param>
public PathTargetInfo(
PathDisplayTarget displayTarget,
ulong frequencyInMillihertz,
DisplayConfigScanLineOrdering scanLineOrdering = DisplayConfigScanLineOrdering.NotSpecified,
DisplayConfigRotation rotation = DisplayConfigRotation.NotSpecified,
DisplayConfigScaling scaling = DisplayConfigScaling.Preferred,
bool isVirtualModeSupported = false
) : this(displayTarget, isVirtualModeSupported)
{
FrequencyInMillihertz = frequencyInMillihertz;
ScanLineOrdering = scanLineOrdering;
Rotation = rotation;
Scaling = scaling;
}
/// <summary>
/// Creates a new PathTargetInfo
/// </summary>
/// <param name="displayTarget">The display target device</param>
/// <param name="signalInfo">The display signal information</param>
/// <param name="isVirtualModeSupported">A boolean value indicating the target virtual mode support</param>
public PathTargetInfo(
PathDisplayTarget displayTarget,
PathTargetSignalInfo signalInfo,
bool isVirtualModeSupported = false
) : this(displayTarget, isVirtualModeSupported)
{
_signalInfo = signalInfo;
FrequencyInMillihertz = signalInfo.VerticalSyncFrequencyInMillihertz;
ScanLineOrdering = signalInfo.ScanLineOrdering;
IsSignalInformationAvailable = true;
}
/// <summary>
/// Creates a new PathTargetInfo
/// </summary>
/// <param name="displayTarget">The display target device</param>
/// <param name="signalInfo">The display signal information</param>
/// <param name="rotation">Display rotation</param>
/// <param name="scaling">Display scaling</param>
/// <param name="isVirtualModeSupported">A boolean value indicating the target virtual mode support</param>
public PathTargetInfo(
PathDisplayTarget displayTarget,
PathTargetSignalInfo signalInfo,
DisplayConfigRotation rotation = DisplayConfigRotation.NotSpecified,
DisplayConfigScaling scaling = DisplayConfigScaling.Preferred,
bool isVirtualModeSupported = false
) : this(
displayTarget,
0,
DisplayConfigScanLineOrdering.NotSpecified,
rotation,
scaling,
isVirtualModeSupported
)
{
_signalInfo = signalInfo;
FrequencyInMillihertz = signalInfo.VerticalSyncFrequencyInMillihertz;
ScanLineOrdering = signalInfo.ScanLineOrdering;
IsSignalInformationAvailable = true;
}
/// <summary>
/// Creates a new PathTargetInfo
/// </summary>
/// <param name="displayTarget">The display target device</param>
/// <param name="signalInfo">The display signal information</param>
/// <param name="desktopImage">The display desktop image information</param>
/// <param name="isVirtualModeSupported">A boolean value indicating the target virtual mode support</param>
public PathTargetInfo(
PathDisplayTarget displayTarget,
PathTargetSignalInfo signalInfo,
PathTargetDesktopImage desktopImage,
bool isVirtualModeSupported = false
) : this(displayTarget, signalInfo, isVirtualModeSupported)
{
_desktopImage = desktopImage;
IsDesktopImageInformationAvailable = true;
}
/// <summary>
/// Creates a new PathTargetInfo
/// </summary>
/// <param name="displayTarget">The display target device</param>
/// <param name="signalInfo">The display signal information</param>
/// <param name="desktopImage">The display desktop image information</param>
/// <param name="rotation">Display rotation</param>
/// <param name="scaling">Display scaling</param>
/// <param name="isVirtualModeSupported">A boolean value indicating the target virtual mode support</param>
public PathTargetInfo(
PathDisplayTarget displayTarget,
PathTargetSignalInfo signalInfo,
PathTargetDesktopImage desktopImage,
DisplayConfigRotation rotation = DisplayConfigRotation.NotSpecified,
DisplayConfigScaling scaling = DisplayConfigScaling.Preferred,
bool isVirtualModeSupported = false
) : this(displayTarget, signalInfo, rotation, scaling, isVirtualModeSupported)
{
_desktopImage = desktopImage;
IsDesktopImageInformationAvailable = true;
}
internal PathTargetInfo(
DisplayConfigPathInfoFlags pathFlags,
DisplayConfigPathTargetInfo targetInfo,
DisplayConfigTargetMode? targetMode,
DisplayConfigDesktopImageInfo? desktopImageMode
)
{
IsPathActive = pathFlags.HasFlag(DisplayConfigPathInfoFlags.Active);
IsVirtualModeSupportedByPath = pathFlags.HasFlag(DisplayConfigPathInfoFlags.SupportVirtualMode);
DisplayTarget = new PathDisplayTarget(
new PathDisplayAdapter(targetInfo.AdapterId),
targetInfo.TargetId,
targetInfo.TargetAvailable
);
OutputTechnology = targetInfo.OutputTechnology;
Rotation = targetInfo.Rotation;
Scaling = targetInfo.Scaling;
ScanLineOrdering = targetInfo.ScanLineOrdering;
FrequencyInMillihertz = targetInfo.RefreshRate.ToValue(1000);
ForcedBootAvailability = targetInfo.StatusFlags.HasFlag(
DisplayConfigPathTargetInfoFlags.AvailabilityBoot
);
ForcedPathAvailability = targetInfo.StatusFlags.HasFlag(
DisplayConfigPathTargetInfoFlags.AvailabilityPath
);
ForcedSystemAvailability = targetInfo.StatusFlags.HasFlag(
DisplayConfigPathTargetInfoFlags.AvailabilitySystem
);
IsCurrentlyInUse = targetInfo.StatusFlags.HasFlag(
DisplayConfigPathTargetInfoFlags.InUse
);
IsForcible = targetInfo.StatusFlags.HasFlag(
DisplayConfigPathTargetInfoFlags.Forcible
);
IsSignalInformationAvailable = targetMode.HasValue;
if (targetMode.HasValue)
{
_signalInfo = new PathTargetSignalInfo(targetMode.Value.TargetVideoSignalInfo);
}
IsDesktopImageInformationAvailable = desktopImageMode.HasValue;
if (desktopImageMode.HasValue)
{
_desktopImage = new PathTargetDesktopImage(desktopImageMode.Value);
}
}
/// <summary>
/// Gets an instance of PathTargetDesktopImage containing information about this target desktop image
/// </summary>
/// <exception cref="MissingModeException">Target mode information is missing</exception>
public PathTargetDesktopImage DesktopImage
{
get
{
if (!IsDesktopImageInformationAvailable)
{
throw new MissingModeException(
"Desktop image information is missing or not available.",
DisplayConfigModeInfoType.DesktopImage
);
}
return _desktopImage;
}
}
/// <summary>
/// Gets extra information about the representing display target device
/// </summary>
public PathDisplayTarget DisplayTarget { get; }
/// <summary>
/// Gets a boolean value indicating that the output is currently being forced in a boot-persistent manner
/// </summary>
public bool ForcedBootAvailability { get; }
/// <summary>
/// Gets a boolean value indicating that the output is currently being forced in a path-persistent manner
/// </summary>
public bool ForcedPathAvailability { get; }
/// <summary>
/// Gets a boolean value indicating that the output is currently being forced in a non-persistent manner
/// </summary>
public bool ForcedSystemAvailability { get; }
/// <summary>
/// Gets a value that specifies the refresh rate of the target
/// </summary>
public ulong FrequencyInMillihertz { get; }
/// <summary>
/// Gets a boolean value indicating if the target is in use on an active path
/// </summary>
public bool IsCurrentlyInUse { get; }
/// <summary>
/// Gets a boolean value indicating the presence of the desktop image information
/// </summary>
public bool IsDesktopImageInformationAvailable { get; }
/// <summary>
/// Gets a boolean value indicating that the output can be forced on this target even if a monitor is not detected
/// </summary>
public bool IsForcible { get; }
/// <summary>
/// Gets a boolean value indicating if this path is or should be active
/// </summary>
public bool IsPathActive { get; } = true;
/// <summary>
/// Gets a boolean value indicating the presence of the signal information
/// </summary>
public bool IsSignalInformationAvailable { get; }
/// <summary>
/// Gets a boolean value that indicates if the path supports virtual mode
/// </summary>
public bool IsVirtualModeSupportedByPath { get; }
/// <summary>
/// Gets the type of the display device connection
/// </summary>
public DisplayConfigVideoOutputTechnology OutputTechnology { get; } = DisplayConfigVideoOutputTechnology.Other;
/// <summary>
/// Gets the rotation of the target
/// </summary>
public DisplayConfigRotation Rotation { get; }
/// <summary>
/// Gets the value that specifies how the source image is scaled to the target
/// </summary>
public DisplayConfigScaling Scaling { get; }
/// <summary>
/// Gets the value that specifies the scan-line ordering of the output on the target
/// </summary>
public DisplayConfigScanLineOrdering ScanLineOrdering { get; }
/// <summary>
/// Gets the target device signal information
/// </summary>
/// <exception cref="MissingModeException">Target mode information is missing</exception>
public PathTargetSignalInfo SignalInfo
{
get
{
if (!IsSignalInformationAvailable)
{
throw new MissingModeException(
"Target mode information is missing or not available.",
DisplayConfigModeInfoType.Target
);
}
return _signalInfo;
}
}
/// <inheritdoc />
public override string ToString()
{
return $"{DisplayTarget}: {FrequencyInMillihertz / 1000}hz{(IsCurrentlyInUse ? " [In Use]" : "")}";
}
internal DisplayConfigDesktopImageInfo? GetDisplayConfigDesktopImageInfo()
{
if (IsDesktopImageInformationAvailable)
{
return DesktopImage.GetDisplayConfigDesktopImageInfo();
}
return null;
}
internal DisplayConfigTargetMode? GetDisplayConfigTargetMode()
{
if (IsSignalInformationAvailable)
{
return new DisplayConfigTargetMode(SignalInfo.GetDisplayConfigVideoSignalInfo());
}
return null;
}
}
}

View File

@@ -0,0 +1,220 @@
using System;
using System.Drawing;
using WindowsDisplayAPI.Native.DisplayConfig;
using WindowsDisplayAPI.Native.DisplayConfig.Structures;
namespace WindowsDisplayAPI.DisplayConfig
{
/// <summary>
/// Contains information about the target signal info
/// </summary>
public class PathTargetSignalInfo : IEquatable<PathTargetSignalInfo>
{
/// <summary>
/// Creates a new PathTargetSignalInfo
/// </summary>
/// <param name="activeSize">Specifies the width and height (in pixels) of the active portion of the video signal.</param>
/// <param name="totalSize">Specifies the width and height (in pixels) of the entire video signal.</param>
/// <param name="verticalSyncFrequencyInMillihertz">Vertical synchronization frequency.</param>
/// <param name="scanLineOrdering">The scan-line ordering (for example, progressive or interlaced) of the video signal.</param>
/// <param name="videoStandard">
/// The video standard (if any) that defines the video signal. Supported by WDDM 1.3 and later
/// display mini-port drivers running on Windows 8.1 and later.
/// </param>
/// <param name="verticalSyncFrequencyDivider">
/// The ratio of the VSync rate of a monitor that displays through a Miracast
/// connected session to the VSync rate of the Miracast sink. The ratio of the VSync rate of a monitor that displays
/// through a Miracast connected session to the VSync rate of the Miracast sink. Supported by WDDM 1.3 and later
/// display mini-port drivers running on Windows 8.1 and later.
/// </param>
public PathTargetSignalInfo(
Size activeSize,
Size totalSize,
ulong verticalSyncFrequencyInMillihertz,
DisplayConfigScanLineOrdering scanLineOrdering,
VideoSignalStandard videoStandard = VideoSignalStandard.Uninitialized,
ushort verticalSyncFrequencyDivider = 0
)
{
ActiveSize = activeSize;
ScanLineOrdering = scanLineOrdering;
TotalSize = totalSize;
VerticalSyncFrequencyDivider = verticalSyncFrequencyDivider;
VerticalSyncFrequencyInMillihertz = verticalSyncFrequencyInMillihertz;
VideoStandard = videoStandard;
PixelRate = (ulong) (verticalSyncFrequencyInMillihertz / 1000d * totalSize.Width * totalSize.Height);
HorizontalSyncFrequencyInMillihertz = (ulong) totalSize.Height * verticalSyncFrequencyInMillihertz;
}
/// <summary>
/// Creates a new PathTargetSignalInfo
/// </summary>
/// <param name="displaySetting">A possible display settings</param>
/// <param name="totalSignalSize">Total signal size</param>
public PathTargetSignalInfo(DisplayPossibleSetting displaySetting, Size totalSignalSize) :
this(
displaySetting.Resolution, totalSignalSize,
(uint) (displaySetting.Frequency * 1000),
displaySetting.IsInterlaced
? DisplayConfigScanLineOrdering.InterlacedWithUpperFieldFirst
: DisplayConfigScanLineOrdering.Progressive
)
{
}
internal PathTargetSignalInfo(DisplayConfigVideoSignalInfo signalInfo)
{
PixelRate = signalInfo.PixelRate;
HorizontalSyncFrequencyInMillihertz = signalInfo.HorizontalSyncFrequency.ToValue(1000);
VerticalSyncFrequencyInMillihertz = signalInfo.VerticalSyncFrequency.ToValue(1000);
ActiveSize = new Size((int) signalInfo.ActiveSize.Width, (int) signalInfo.ActiveSize.Height);
TotalSize = new Size((int) signalInfo.TotalSize.Width, (int) signalInfo.TotalSize.Height);
VideoStandard = signalInfo.VideoStandard;
VerticalSyncFrequencyDivider = signalInfo.VerticalSyncFrequencyDivider;
ScanLineOrdering = signalInfo.ScanLineOrdering;
}
/// <summary>
/// Gets the width and height (in pixels) of the active portion of the video signal
/// </summary>
public Size ActiveSize { get; }
/// <summary>
/// Gets the horizontal synchronization frequency
/// </summary>
public ulong HorizontalSyncFrequencyInMillihertz { get; }
/// <summary>
/// Gets the pixel clock rate
/// </summary>
public ulong PixelRate { get; }
/// <summary>
/// Gets the scan-line ordering (for example, progressive or interlaced) of the video signal
/// </summary>
public DisplayConfigScanLineOrdering ScanLineOrdering { get; }
/// <summary>
/// Gets the width and height (in pixels) of the entire video signal
/// </summary>
public Size TotalSize { get; }
/// <summary>
/// Gets the ratio of the VSync rate of a monitor that displays through a Miracast connected session to the VSync rate
/// of the Miracast sink. The ratio of the VSync rate of a monitor that displays through a Miracast connected session
/// to the VSync rate of the Miracast sink. Supported by WDDM 1.3 and later display mini-port drivers running on Windows
/// 8.1 and later
/// </summary>
public ushort VerticalSyncFrequencyDivider { get; }
/// <summary>
/// Gets the vertical synchronization frequency
/// </summary>
public ulong VerticalSyncFrequencyInMillihertz { get; }
/// <summary>
/// Gets the video standard (if any) that defines the video signal. Supported by WDDM 1.3 and later display mini-port
/// drivers running on Windows 8.1 and later
/// </summary>
public VideoSignalStandard VideoStandard { get; }
/// <inheritdoc />
public bool Equals(PathTargetSignalInfo other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return ActiveSize == other.ActiveSize &&
HorizontalSyncFrequencyInMillihertz == other.HorizontalSyncFrequencyInMillihertz &&
PixelRate == other.PixelRate &&
ScanLineOrdering == other.ScanLineOrdering &&
TotalSize == other.TotalSize &&
VerticalSyncFrequencyDivider == other.VerticalSyncFrequencyDivider &&
VerticalSyncFrequencyInMillihertz == other.VerticalSyncFrequencyInMillihertz &&
VideoStandard == other.VideoStandard;
}
/// <summary>
/// Checks for equality of two PathTargetSignalInfo instances
/// </summary>
/// <param name="left">The first instance</param>
/// <param name="right">The second instance</param>
/// <returns>true if both instances are equal, otherwise false</returns>
public static bool operator ==(PathTargetSignalInfo left, PathTargetSignalInfo right)
{
return Equals(left, right) || left?.Equals(right) == true;
}
/// <summary>
/// Checks for inequality of two PathTargetSignalInfo instances
/// </summary>
/// <param name="left">The first instance</param>
/// <param name="right">The second instance</param>
/// <returns>true if both instances are not equal, otherwise false</returns>
public static bool operator !=(PathTargetSignalInfo left, PathTargetSignalInfo right)
{
return !(left == right);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj.GetType() == GetType() && Equals((PathTargetSignalInfo) obj);
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
var hashCode = ActiveSize.GetHashCode();
hashCode = (hashCode * 397) ^ (int) HorizontalSyncFrequencyInMillihertz;
hashCode = (hashCode * 397) ^ PixelRate.GetHashCode();
hashCode = (hashCode * 397) ^ (int) ScanLineOrdering;
hashCode = (hashCode * 397) ^ TotalSize.GetHashCode();
hashCode = (hashCode * 397) ^ VerticalSyncFrequencyDivider.GetHashCode();
hashCode = (hashCode * 397) ^ (int) VerticalSyncFrequencyInMillihertz;
hashCode = (hashCode * 397) ^ (int) VideoStandard;
return hashCode;
}
}
/// <inheritdoc />
public override string ToString()
{
return $"{ActiveSize} @ {VerticalSyncFrequencyInMillihertz / 1000}hz {VideoStandard}";
}
internal DisplayConfigVideoSignalInfo GetDisplayConfigVideoSignalInfo()
{
return new DisplayConfigVideoSignalInfo(
PixelRate,
new DisplayConfigRational(HorizontalSyncFrequencyInMillihertz, 1000, true),
new DisplayConfigRational(VerticalSyncFrequencyInMillihertz, 1000, true),
new DisplayConfig2DRegion((uint) ActiveSize.Width, (uint) ActiveSize.Height),
new DisplayConfig2DRegion((uint) TotalSize.Width, (uint) TotalSize.Height),
VideoStandard,
VerticalSyncFrequencyDivider,
ScanLineOrdering
);
}
}
}