mirror of
https://github.com/jkocon/g-helper.git
synced 2026-02-23 13:00:52 +01:00
Gamma Init
This commit is contained in:
190
app/WindowsDisplayAPI/DisplayConfig/PathDisplayAdapter.cs
Normal file
190
app/WindowsDisplayAPI/DisplayConfig/PathDisplayAdapter.cs
Normal 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("\\", "#"))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
238
app/WindowsDisplayAPI/DisplayConfig/PathDisplaySource.cs
Normal file
238
app/WindowsDisplayAPI/DisplayConfig/PathDisplaySource.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
481
app/WindowsDisplayAPI/DisplayConfig/PathDisplayTarget.cs
Normal file
481
app/WindowsDisplayAPI/DisplayConfig/PathDisplayTarget.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
910
app/WindowsDisplayAPI/DisplayConfig/PathInfo.cs
Normal file
910
app/WindowsDisplayAPI/DisplayConfig/PathInfo.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
140
app/WindowsDisplayAPI/DisplayConfig/PathTargetDesktopImage.cs
Normal file
140
app/WindowsDisplayAPI/DisplayConfig/PathTargetDesktopImage.cs
Normal 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
330
app/WindowsDisplayAPI/DisplayConfig/PathTargetInfo.cs
Normal file
330
app/WindowsDisplayAPI/DisplayConfig/PathTargetInfo.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
220
app/WindowsDisplayAPI/DisplayConfig/PathTargetSignalInfo.cs
Normal file
220
app/WindowsDisplayAPI/DisplayConfig/PathTargetSignalInfo.cs
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user