Files
archived-g-helper/app/WindowsDisplayAPI/DisplayConfig/PathInfo.cs
2024-02-16 15:55:37 +01:00

910 lines
35 KiB
C#

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