[WIP] Added video card detection

Added ability for ProfileRepository to know what mode it is in when DisplayMagician starts.
This commit is contained in:
Terry MacDonald 2021-08-22 15:42:12 +12:00
parent 01ffda8428
commit 40b8525dd8
6 changed files with 97 additions and 777 deletions

View File

@ -235,35 +235,17 @@ namespace DisplayMagicianShared.AMD
public override void RefreshPossbility()
{
// Check each display in this profile and make sure it's currently available
int validDisplayCount = 0;
//validDisplayCount = (from connectedDisplay in ProfileRepository.ConnectedDisplayIdentifiers select connectedDisplay == profileDisplayIdentifier).Count();
foreach (string profileDisplayIdentifier in ProfileDisplayIdentifiers)
{
// If this profile has a display that isn't currently available then we need to say it's a no!
if (ProfileRepository.ConnectedDisplayIdentifiers.Any(s => profileDisplayIdentifier.Equals(s)))
{
SharedLogger.logger.Trace($"ProfileItem/RefreshPossbility: We found the display in the profile {Name} with profileDisplayIdentifier {profileDisplayIdentifier} is connected now.");
validDisplayCount++;
}
else
{
SharedLogger.logger.Warn($"ProfileItem/RefreshPossbility: We found the display in the profile {Name} with profileDisplayIdentifier {profileDisplayIdentifier} is NOT currently connected, so this profile cannot be used.");
}
}
if (validDisplayCount == ProfileDisplayIdentifiers.Count)
// Check whether this profile is possible
if (AMDLibrary.GetLibrary().IsPossibleConfig(_amdDisplayConfig))
{
SharedLogger.logger.Debug($"ProfileRepository/IsPossibleRefresh: The profile {Name} is possible!");
SharedLogger.logger.Debug($"ProfileRepository/IsPossibleRefresh: The AMD profile {Name} is possible!");
_isPossible = true;
}
else
{
SharedLogger.logger.Debug($"ProfileRepository/IsPossibleRefresh: The profile {Name} is NOT possible!");
SharedLogger.logger.Debug($"ProfileRepository/IsPossibleRefresh: The AMD profile {Name} is NOT possible!");
_isPossible = false;
}
@ -304,11 +286,11 @@ namespace DisplayMagicianShared.AMD
// Now we create the screens structure from the AMD profile information
_screens = new List<ScreenPosition>();
if ( _displayConfig.W.Count > 0)
if ( _amdDisplayConfig.AdapterConfigs.Count > 0)
{
foreach ( var adapter in _displayConfig.AdapterConfigs)
foreach ( var adapter in _amdDisplayConfig.AdapterConfigs)
{
foreach (var display in adapter.SLSMapIndex)
foreach (var display in adapter)
{
foreach (var mode in display.DisplayModes)
{
@ -515,26 +497,6 @@ namespace DisplayMagicianShared.AMD
// Allows us to use 'Contains'
class AMDProfileComparer : IEqualityComparer<AMDProfileItem>
{
// Products are equal if their names and product numbers are equal.
/*public bool Equals(AMDProfileItem x, AMDProfileItem y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (x is null || y is null)
return false;
// Check whether the profiles' properties are equal
// We need to exclude the name as the name is solely for saving to disk
// and displaying to the user.
// Two profiles are equal only when they have the same viewport data
if (x.Paths.SequenceEqual(y.Paths))
return true;
else
return false;
}*/
public bool Equals(AMDProfileItem x, AMDProfileItem y)
{
@ -585,21 +547,6 @@ namespace DisplayMagicianShared.AMD
return true;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
/*public int GetHashCode(AMDProfileItem profile)
{
// Check whether the object is null
if (profile is null) return 0;
// Get hash code for the Viewports field if it is not null.
int hashPaths = profile.Paths == null ? 0 : profile.Paths.GetHashCode();
//Calculate the hash code for the product.
return hashPaths;
}*/
// Modified the GetHashCode to compare the displayidentifier
public int GetHashCode(AMDProfileItem profile)
{

View File

@ -68,10 +68,6 @@ namespace DisplayMagicianShared.NVIDIA
public override string Name { get; set; }
//public Topology.Path[] Paths { get; set; } = new Topology.Path[0];
//public NVIDIALibrary.NVIDIAProfile ProfileData { get; set; } = new NVIDIALibrary.NVIDIAProfile();
[JsonRequired]
public NVIDIA_DISPLAY_CONFIG NVIDIADisplayConfig
{
@ -225,35 +221,17 @@ namespace DisplayMagicianShared.NVIDIA
public override void RefreshPossbility()
{
// Check each display in this profile and make sure it's currently available
int validDisplayCount = 0;
//validDisplayCount = (from connectedDisplay in ProfileRepository.ConnectedDisplayIdentifiers select connectedDisplay == profileDisplayIdentifier).Count();
foreach (string profileDisplayIdentifier in ProfileDisplayIdentifiers)
{
// If this profile has a display that isn't currently available then we need to say it's a no!
if (ProfileRepository.ConnectedDisplayIdentifiers.Any(s => profileDisplayIdentifier.Equals(s)))
{
SharedLogger.logger.Trace($"ProfileItem/RefreshPossbility: We found the display in the profile {Name} with profileDisplayIdentifier {profileDisplayIdentifier} is connected now.");
validDisplayCount++;
}
else
{
SharedLogger.logger.Warn($"ProfileItem/RefreshPossbility: We found the display in the profile {Name} with profileDisplayIdentifier {profileDisplayIdentifier} is NOT currently connected, so this profile cannot be used.");
}
}
if (validDisplayCount == ProfileDisplayIdentifiers.Count)
// Check whether this profile is possible
if (NVIDIALibrary.GetLibrary().IsPossibleConfig(_nvidiaDisplayConfig))
{
SharedLogger.logger.Debug($"ProfileRepository/IsPossibleRefresh: The profile {Name} is possible!");
SharedLogger.logger.Debug($"ProfileRepository/IsPossibleRefresh: The NVIDIA profile {Name} is possible!");
_isPossible = true;
}
else
{
SharedLogger.logger.Debug($"ProfileRepository/IsPossibleRefresh: The profile {Name} is NOT possible!");
SharedLogger.logger.Debug($"ProfileRepository/IsPossibleRefresh: The NVIDIA profile {Name} is NOT possible!");
_isPossible = false;
}
@ -294,7 +272,7 @@ namespace DisplayMagicianShared.NVIDIA
// Now we create the screens structure from the AMD profile information
_screens = new List<ScreenPosition>();
if (_displayConfig. .Count > 0)
if (_displayConfig.Count > 0)
{
foreach (var adapter in _displayConfig.AdapterConfigs)
{
@ -390,7 +368,12 @@ namespace DisplayMagicianShared.NVIDIA
if (this.GetType() != other.GetType())
return false;
if (Paths.Length != other.Paths.Length)
// If NVIDIA Display Config is different then return false.
if (!NVIDIADisplayConfig.Equals(other.NVIDIADisplayConfig))
return false;
// If Windows Display Config is different then return false.
if (!WindowsDisplayConfig.Equals(other.WindowsDisplayConfig))
return false;
// Check if the profile identifiers are not the same, then return false

View File

@ -13,7 +13,16 @@ using DisplayMagicianShared.Windows;
namespace DisplayMagicianShared
{
// This enum sets the video card mode used within DisplayMagician
// It effectively controls what video card library is used to store profiles on the computer
// We look up the PCI vendor ID for the video cards, and then we look for them in the order from most commonly
// sold video card to the least, followed by the generic 'catch-all' windows mode.
public enum VIDEO_MODE : Int32
{
WINDOWS = 0,
NVIDIA = 1,
AMD = 2,
}
public static class ProfileRepository
{
@ -26,9 +35,11 @@ namespace DisplayMagicianShared
private static ProfileItem _currentProfile;
private static List<string> _connectedDisplayIdentifiers = new List<string>();
private static bool notifiedEDIDErrorToUser = false;
private static AMDLibrary AMDLibrary;
private static NVIDIALibrary NVIDIALibrary;
//private static bool _isLoading = false;
private static AMDLibrary amdLibrary;
private static NVIDIALibrary nvidiaLibrary;
private static WinLibrary winLibrary;
// Make th default video mode Windows
public static VIDEO_MODE _videoMode = VIDEO_MODE.WINDOWS;
// Other constants that are useful
public static string AppDataPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DisplayMagician");
@ -43,29 +54,41 @@ namespace DisplayMagicianShared
#region Class Constructors
static ProfileRepository()
{
// Initialise the the NVIDIA NvAPIWrapper
try
// Figure out the Video Cards and see what mode we want
// Get a list of all the PCI Vendor IDs
List<string> videoCardVendors = WinLibrary.GetLibrary().GetCurrentPCIVideoCardVendors();
// This sets the order in which the different modes have been chosen.
// NVIDIA Video cards are the most common, so go first
_videoMode = VIDEO_MODE.WINDOWS;
if (!NVIDIALibrary.GetLibrary().PCIVendorIDs.All(value => videoCardVendors.Contains(value)))
{
SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Initialising the NVIDIA NVAPI library.");
NVIDIALibrary = new NVIDIALibrary();
// Initialise the the NVIDIA NvAPI Library
try
{
SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Initialising the NVIDIA NVAPI library.");
nvidiaLibrary = new NVIDIALibrary();
_videoMode = VIDEO_MODE.NVIDIA;
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"ProfileRepository/ProfileRepository: Initialising NVIDIA NVAPI caused an exception.");
}
}
catch (Exception ex)
else if (!NVIDIALibrary.GetLibrary().PCIVendorIDs.All(value => videoCardVendors.Contains(value)))
{
SharedLogger.logger.Warn(ex, $"ProfileRepository/ProfileRepository: Initialising NVIDIA NVAPI caused an exception.");
// Initialise the the AMD ADL Library
try
{
SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Initialising the AMD ADL library.");
amdLibrary = new AMDLibrary();
_videoMode = VIDEO_MODE.AMD;
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"ProfileRepository/ProfileRepository: Initialising AMD ADL caused an exception.");
}
}
// Initialise the the AMD ADLWrapper
try
{
SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Initialising the AMD ADL library.");
AMDLibrary = new AMDLibrary();
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"ProfileRepository/ProfileRepository: Initialising AMD ADL caused an exception.");
}
try
{
// Create the Profile Storage Path if it doesn't exist so that it's avilable for all the program
@ -157,6 +180,18 @@ namespace DisplayMagicianShared
}
}
public static VIDEO_MODE VideoMode
{
get
{
return _videoMode;
}
set
{
_videoMode = value;
}
}
public static List<string> ConnectedDisplayIdentifiers
{
@ -667,12 +702,12 @@ namespace DisplayMagicianShared
SharedLogger.logger.Error(ex, $"ProfileRepository/LoadProfiles: Tried to parse the JSON in the {_profileStorageJsonFileName} but the JsonConvert threw an exception.");
}
// Populate the Current Profile now so we have stuff to compare against
ProfileItem myCurrentProfile = new NVIDIAProfileItem
{
Name = "Current Display Profile",
Paths = PathInfo.GetActivePaths().Select(info => new DisplayMagicianShared.Topology.Path(info)).ToArray()
};
myCurrentProfile.CreateProfileFromCurrentDisplaySettings();
_currentProfile = myCurrentProfile;
SharedLogger.logger.Debug($"ProfileRepository/LoadProfiles: Finding the current profile in the Profile Repository");
@ -683,14 +718,28 @@ namespace DisplayMagicianShared
{
if (loadedProfile.Driver.Equals("AMD"))
{
// NVIDIA config!
NVIDIAProfileItem nvidiaLoadedProfile = (NVIDIAProfileItem)loadedProfile;
nvidiaLoadedProfile.PerformPostLoadingTasks();
if (ProfileRepository.IsActiveProfile(nvidiaLoadedProfile))
_currentProfile = nvidiaLoadedProfile;
}
else if (loadedProfile.Driver.Equals("AMD"))
{
// AMD config!
AMDProfileItem amdLoadedProfile = (AMDProfileItem) loadedProfile;
amdLoadedProfile.PerformPostLoadingTasks();
if (ProfileRepository.IsActiveProfile(amdLoadedProfile))
_currentProfile = amdLoadedProfile;
}
if (ProfileRepository.IsActiveProfile(loadedProfile))
_currentProfile = loadedProfile;
else
{
// Windows CCD config!
WinProfileItem winLoadedProfile = (WinProfileItem)loadedProfile;
winLoadedProfile.PerformPostLoadingTasks();
if (ProfileRepository.IsActiveProfile(winLoadedProfile))
_currentProfile = winLoadedProfile;
}
}
// Sort the profiles alphabetically

View File

@ -1,239 +0,0 @@
using System;
using System.Drawing;
using System.Linq;
using WindowsDisplayAPI.DisplayConfig;
using WindowsDisplayAPI.Native.DisplayConfig;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Collections.Generic;
namespace DisplayMagicianShared.Topology
{
public class Path
{
public Path(PathInfo pathInfo)
{
SourceId = pathInfo.DisplaySource.SourceId;
PixelFormat = pathInfo.PixelFormat;
Position = pathInfo.Position;
Resolution = pathInfo.Resolution;
TargetDisplays = pathInfo.TargetsInfo.Select(targetDisplay => new PathTarget(targetDisplay)).ToArray();
}
public Path()
{
}
[JsonConverter(typeof(StringEnumConverter))]
public DisplayConfigPixelFormat PixelFormat { get; set; }
public Point Position { get; set; }
public Size Resolution { get; set; }
public uint SourceId { get; set; }
public PathTarget[] TargetDisplays { get; set; }
public override string ToString()
{
return $"\\\\.\\DISPLAY{SourceId}";
}
public PathInfo ToPathInfo()
{
var targetDisplays = TargetDisplays.Select(target => target.ToPathTargetInfo()).Where(info => info != null).ToArray();
if (targetDisplays.Any())
{
return new PathInfo(new PathDisplaySource(targetDisplays.First().DisplayTarget.Adapter, SourceId), Position,
Resolution, PixelFormat, targetDisplays);
}
return null;
}
// The public override for the Object.Equals
public override bool Equals(object obj)
{
return this.Equals(obj as Path);
}
// Profiles are equal if their contents (except name) are equal
public bool Equals(Path other)
{
// If parameter is null, return false.
if (Object.ReferenceEquals(other, null))
return false;
// Optimization for a common success case.
if (Object.ReferenceEquals(this, other))
return true;
// If run-time types are not exactly the same, return false.
if (this.GetType() != other.GetType())
return false;
// Check whether the Profile Viewport properties are equal
// Two profiles are equal only when they have the same viewport data exactly
if (PixelFormat == other.PixelFormat &&
Position.Equals(other.Position) &&
Resolution.Equals(other.Resolution) &&
SourceId == other.SourceId)
{
// If the above all match, then we need to check the DisplayTargets
foreach (PathTarget myTargetDisplay in TargetDisplays)
{
if (!other.TargetDisplays.Contains(myTargetDisplay))
return false;
}
return true;
}
else
return false;
}
// If Equals() returns true for this object compared to another
// then GetHashCode() must return the same value for these objects.
public override int GetHashCode()
{
// Get hash code for the PixelFormat field if it is not null.
int hashPixelFormat = PixelFormat.GetHashCode();
// Get hash code for the Position field if it is not null.
int hashPosition = Position == null ? 0 : Position.GetHashCode();
// Get hash code for the Resolution field if it is not null.
int hashResolution = Resolution == null ? 0 : Resolution.GetHashCode();
// Get hash code for the SourceId field if it is not null.
int hashSourceId = SourceId.GetHashCode();
// Get hash code for the TargetDisplays field if it is not null.
int hashTargetDisplays = TargetDisplays == null ? 0 : TargetDisplays.GetHashCode();
//Calculate the hash code for the product.
return hashPixelFormat ^ hashPosition ^ hashResolution ^ hashSourceId ^ hashTargetDisplays;
}
public bool IsPossible(Path other)
{
// If parameter is null, return false.
if (Object.ReferenceEquals(other, null))
return false;
// Optimization for a common success case.
if (Object.ReferenceEquals(this, other))
return true;
// If run-time types are not exactly the same, return false.
if (this.GetType() != other.GetType())
return false;
// Check whether the Profile Viewport properties are equal
// Two profiles are equal only when they have the same viewport data exactly
/*if (PixelFormat == other.PixelFormat &&
Position.Equals(other.Position) &&
Resolution.Equals(other.Resolution) &&
SourceId == other.SourceId)*/
// Note: Removed the source ID as it changes on boot sometimes!
// It can change and the profiles can still be the same
if (PixelFormat == other.PixelFormat &&
Position.Equals(other.Position) &&
Resolution.Equals(other.Resolution))
return true;
else
return false;
}
public bool ContainsSurround()
{
foreach (PathTarget pathTarget in TargetDisplays)
{
if (pathTarget.SurroundTopology == null)
return false;
}
return true;
}
}
// Custom comparer for the ProfileViewport class
class PathComparer : IEqualityComparer<Path>
{
// Products are equal if their names and product numbers are equal.
public bool Equals(Path x, Path y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
// Check whether the Profile Viewport properties are equal
// Two profiles are equal only when they have the same viewport data exactly
/*if (x.PixelFormat == y.PixelFormat &&
x.Position.Equals(y.Position) &&
x.Resolution.Equals(y.Resolution) &&
x.SourceId == y.SourceId)*/
// Note: Removed the source ID as it changes on boot sometimes!
// It can change and the profiles can still be the same
if (x.PixelFormat == y.PixelFormat &&
x.Position.Equals(y.Position) &&
x.Resolution.Equals(y.Resolution) &&
x.SourceId == y.SourceId)
{
// If the above all match, then we need to check the DisplayTargets
// If they aren't equal then we need to return false;
/*if (!x.TargetDisplays.SequenceEqual(y.TargetDisplays))
return false;
else
return true;*/
foreach (PathTarget xTargetDisplay in x.TargetDisplays)
{
if (!y.TargetDisplays.Contains(xTargetDisplay))
return false;
}
/*foreach (PathTarget yTargetDisplay in y.TargetDisplays)
{
if (!x.TargetDisplays.Contains(yTargetDisplay))
return false;
}*/
return true;
}
else
return false;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Path profileViewport)
{
// Check whether the object is null
if (Object.ReferenceEquals(profileViewport, null)) return 0;
// Get hash code for the PixelFormat field if it is not null.
int hashPixelFormat = profileViewport.PixelFormat.GetHashCode();
// Get hash code for the Position field if it is not null.
int hashPosition = profileViewport.Position == null ? 0 : profileViewport.Position.GetHashCode();
// Get hash code for the Resolution field if it is not null.
int hashResolution = profileViewport.Resolution == null ? 0 : profileViewport.Resolution.GetHashCode();
// Get hash code for the SourceId field if it is not null.
int hashSourceId = profileViewport.SourceId.GetHashCode();
// Get hash code for the TargetDisplays field if it is not null.
int hashTargetDisplays = profileViewport.TargetDisplays == null ? 0 : profileViewport.TargetDisplays.GetHashCode();
//Calculate the hash code for the product.
return hashPixelFormat ^ hashPosition ^ hashResolution ^ hashSourceId ^ hashTargetDisplays;
}
}
}

View File

@ -1,147 +0,0 @@
using WindowsDisplayAPI.Native.DisplayConfig;
namespace DisplayMagicianShared.Topology
{
internal static class PathHelper
{
public static DisplayConfigRotation ToDisplayConfigRotation(this Rotation rotation)
{
switch (rotation)
{
case Rotation.Identity:
return DisplayConfigRotation.Identity;
case Rotation.Rotate90:
return DisplayConfigRotation.Rotate90;
case Rotation.Rotate180:
return DisplayConfigRotation.Rotate180;
case Rotation.Rotate270:
return DisplayConfigRotation.Rotate270;
default:
return DisplayConfigRotation.NotSpecified;
}
}
public static DisplayConfigScaling ToDisplayConfigScaling(this Scaling scaling)
{
switch (scaling)
{
case Scaling.Identity:
return DisplayConfigScaling.Identity;
case Scaling.Centered:
return DisplayConfigScaling.Centered;
case Scaling.Stretched:
return DisplayConfigScaling.Stretched;
case Scaling.AspectRatioCenteredMax:
return DisplayConfigScaling.AspectRatioCenteredMax;
case Scaling.Custom:
return DisplayConfigScaling.Custom;
case Scaling.Preferred:
return DisplayConfigScaling.Preferred;
default:
return DisplayConfigScaling.NotSpecified;
}
}
public static DisplayConfigScanLineOrdering ToDisplayConfigScanLineOrdering(
this ScanLineOrdering scanLineOrdering)
{
switch (scanLineOrdering)
{
case ScanLineOrdering.Progressive:
return DisplayConfigScanLineOrdering.Progressive;
case ScanLineOrdering.InterlacedWithUpperFieldFirst:
return DisplayConfigScanLineOrdering.InterlacedWithUpperFieldFirst;
case ScanLineOrdering.InterlacedWithLowerFieldFirst:
return DisplayConfigScanLineOrdering.InterlacedWithLowerFieldFirst;
default:
return DisplayConfigScanLineOrdering.NotSpecified;
}
}
public static Rotation ToRotation(this DisplayConfigRotation rotation)
{
switch (rotation)
{
case DisplayConfigRotation.Identity:
return Rotation.Identity;
case DisplayConfigRotation.Rotate90:
return Rotation.Rotate90;
case DisplayConfigRotation.Rotate180:
return Rotation.Rotate180;
case DisplayConfigRotation.Rotate270:
return Rotation.Rotate270;
default:
return Rotation.Unknown;
}
}
public static Scaling ToScaling(this DisplayConfigScaling scaling)
{
switch (scaling)
{
case DisplayConfigScaling.Identity:
return Scaling.Identity;
case DisplayConfigScaling.Centered:
return Scaling.Centered;
case DisplayConfigScaling.Stretched:
return Scaling.Stretched;
case DisplayConfigScaling.AspectRatioCenteredMax:
return Scaling.AspectRatioCenteredMax;
case DisplayConfigScaling.Custom:
return Scaling.Custom;
case DisplayConfigScaling.Preferred:
return Scaling.Preferred;
default:
return Scaling.NotSpecified;
}
}
public static ScanLineOrdering ToScanLineOrdering(this DisplayConfigScanLineOrdering scanLineOrdering)
{
switch (scanLineOrdering)
{
case DisplayConfigScanLineOrdering.Progressive:
return ScanLineOrdering.Progressive;
case DisplayConfigScanLineOrdering.InterlacedWithUpperFieldFirst:
return ScanLineOrdering.InterlacedWithUpperFieldFirst;
case DisplayConfigScanLineOrdering.InterlacedWithLowerFieldFirst:
return ScanLineOrdering.InterlacedWithLowerFieldFirst;
default:
return ScanLineOrdering.NotSpecified;
}
}
}
}

View File

@ -1,273 +0,0 @@
using System;
using System.Linq;
using WindowsDisplayAPI.DisplayConfig;
using DisplayMagicianShared.NVIDIA;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Collections.Generic;
namespace DisplayMagicianShared.Topology
{
public class PathTarget : IEquatable<PathTarget>
{
public PathTarget(PathTargetInfo targetInfo, SurroundTopology surround = null)
{
DevicePath = targetInfo.DisplayTarget.DevicePath;
var index = DevicePath.IndexOf("{", StringComparison.InvariantCultureIgnoreCase);
if (index > 0)
{
DevicePath = DevicePath.Substring(0, index).TrimEnd('#');
SharedLogger.logger.Trace($"PathTarget/PathTarget: Grabbed the DevicePath of {DevicePath}.");
}
FrequencyInMillihertz = targetInfo.FrequencyInMillihertz;
SharedLogger.logger.Trace($"PathTarget/PathTarget: Grabbed the FrequencyInMillihertz of {FrequencyInMillihertz}.");
Rotation = targetInfo.Rotation.ToRotation();
SharedLogger.logger.Trace($"PathTarget/PathTarget: Grabbed the Rotation of {Rotation}.");
Scaling = targetInfo.Scaling.ToScaling();
SharedLogger.logger.Trace($"PathTarget/PathTarget: Grabbed the Scaling of {Scaling}.");
ScanLineOrdering = targetInfo.ScanLineOrdering.ToScanLineOrdering();
SharedLogger.logger.Trace($"PathTarget/PathTarget: Grabbed the ScanLineOrdering of {ScanLineOrdering}.");
try
{
DisplayName = targetInfo.DisplayTarget.FriendlyName;
SharedLogger.logger.Trace($"PathTarget/PathTarget: Grabbed the DisplayName of {DisplayName}.");
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"PathTarget/PathTarget: Exception grabbing the DisplayName of {DisplayName} from the TargetInfo DisplayTarget.");
DisplayName = null;
}
if (surround != null)
{
SharedLogger.logger.Trace($"PathTarget/PathTarget: This PathTarget supplied an NVIDIA surround object, so using the SurroundTopology supplied.");
SurroundTopology = surround;
}
else
{
try
{
SurroundTopology = SurroundTopology.FromPathTargetInfo(targetInfo);
if (SurroundTopology != null)
{
SharedLogger.logger.Trace($"PathTarget/PathTarget: The SurroundTopology object supplied was null, so we created one ourselves. We found {SurroundTopology.Displays.Count()} displays involved in it {SurroundTopology.Columns}x{SurroundTopology.Rows}");
}
else
{
SharedLogger.logger.Trace($"PathTarget/PathTarget: The SurroundTopology object supplied was null, so we tried to create one ourselves and failed. This is likely a non NVIDIA surround display profile.");
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"PathTarget/PathTarget: A SurroundTopology object was not supplied, but we had an exception getting our own SurroundTopology from the PathTargetInfo object.");
}
}
}
public PathTarget()
{
}
public string DevicePath { get; set; }
public string DisplayName { get; set; }
public ulong FrequencyInMillihertz { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public Rotation Rotation { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public Scaling Scaling { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public ScanLineOrdering ScanLineOrdering { get; set; }
public SurroundTopology SurroundTopology { get; set; }
/// <inheritdoc />
public override string ToString()
{
return DisplayName ?? $"PathTarget {DevicePath}";
}
public PathTargetInfo ToPathTargetInfo()
{
var targetDevice =
PathDisplayTarget.GetDisplayTargets()
.FirstOrDefault(
target => target.DevicePath.StartsWith(DevicePath,
StringComparison.InvariantCultureIgnoreCase));
if (targetDevice == null)
{
return null;
}
return new PathTargetInfo(new PathDisplayTarget(targetDevice.Adapter, targetDevice.TargetId),
FrequencyInMillihertz, ScanLineOrdering.ToDisplayConfigScanLineOrdering(),
Rotation.ToDisplayConfigRotation(), Scaling.ToDisplayConfigScaling());
}
// The public override for the Object.Equals
public override bool Equals(object obj)
{
return this.Equals(obj as PathTarget);
}
// Profiles are equal if their contents (except name) are equal
public bool Equals(PathTarget other)
{
// If parameter is null, return false.
if (Object.ReferenceEquals(other, null))
return false;
// Optimization for a common success case.
if (Object.ReferenceEquals(this, other))
return true;
// If run-time types are not exactly the same, return false.
if (this.GetType() != other.GetType())
return false;
// Check whether the Profile Viewport properties are equal
// Two profiles are equal only when they have the same viewport data exactly
if (FrequencyInMillihertz == other.FrequencyInMillihertz &&
Rotation.Equals(other.Rotation) &&
Scaling.Equals(other.Scaling) &&
ScanLineOrdering.Equals(other.ScanLineOrdering) &&
DisplayName.Equals(other.DisplayName) &&
DevicePath.Equals(other.DevicePath))
{
// If the above all match, then we need to check the SurroundTopology matches
if (SurroundTopology == null && other.SurroundTopology == null)
return true;
else if (SurroundTopology != null && other.SurroundTopology == null)
return false;
else if (SurroundTopology == null && other.SurroundTopology != null)
return false;
else if (SurroundTopology.Equals(other.SurroundTopology))
return true;
return false;
}
else
return false;
}
// If Equals() returns true for this object compared to another
// then GetHashCode() must return the same value for these objects.
public override int GetHashCode()
{
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashFrequencyInMillihertz = FrequencyInMillihertz.GetHashCode();
// Get hash code for the Position field if it is not null.
int hashRotation = Rotation.GetHashCode();
// Get hash code for the Scaling field if it is not null.
int hashScaling = Scaling.GetHashCode();
// Get hash code for the ScanLineOrdering field if it is not null.
int hashScanLineOrdering = ScanLineOrdering.GetHashCode();
// Get hash code for the hashDisplayName field if it is not null.
int hashDisplayName = DisplayName == null ? 0 : DisplayName.GetHashCode();
// Get hash code for the DevicePath field if it is not null.
int hashDevicePath = DevicePath == null ? 0 : DevicePath.GetHashCode();
// Get hash code for the SurroundTopology field if it is not null.
int hashSurroundTopology = SurroundTopology == null ? 0 : SurroundTopology.GetHashCode();
//Calculate the hash code for the product.
return hashFrequencyInMillihertz ^ hashRotation ^ hashScaling ^ hashScanLineOrdering ^
hashDisplayName ^ hashDevicePath ^ hashSurroundTopology;
}
}
// Custom comparer for the ProfileViewportTargetDisplay class
class ProfileViewportTargetDisplayComparer : IEqualityComparer<PathTarget>
{
// Products are equal if their names and product numbers are equal.
public bool Equals(PathTarget x, PathTarget y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
// Check whether the Profile Viewport properties are equal
// Two profiles are equal only when they have the same viewport data exactly
if (x.FrequencyInMillihertz == y.FrequencyInMillihertz &&
x.Rotation.Equals(y.Rotation) &&
x.Scaling.Equals(y.Scaling) &&
x.ScanLineOrdering.Equals(y.ScanLineOrdering) &&
x.DisplayName.Equals(y.DisplayName) &&
x.DevicePath.Equals(y.DevicePath))
{
// If the above all match, then we need to check the SurroundTopology matches
if (x.SurroundTopology == null && y.SurroundTopology == null)
return true;
else if (x.SurroundTopology != null && y.SurroundTopology == null)
return false;
else if (x.SurroundTopology == null && y.SurroundTopology != null)
return false;
else if (x.SurroundTopology.Equals(y.SurroundTopology))
return true;
return false;
}
else
return false;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(PathTarget profileViewport)
{
// Check whether the object is null
if (Object.ReferenceEquals(profileViewport, null)) return 0;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashFrequencyInMillihertz = profileViewport.FrequencyInMillihertz.GetHashCode();
// Get hash code for the Position field if it is not null.
int hashRotation = profileViewport.Rotation.GetHashCode();
// Get hash code for the Scaling field if it is not null.
int hashScaling = profileViewport.Scaling.GetHashCode();
// Get hash code for the ScanLineOrdering field if it is not null.
int hashScanLineOrdering = profileViewport.ScanLineOrdering.GetHashCode();
// Get hash code for the hashDisplayName field if it is not null.
int hashDisplayName = profileViewport.DisplayName == null ? 0 : profileViewport.DisplayName.GetHashCode();
// Get hash code for the DevicePath field if it is not null.
int hashDevicePath = profileViewport.DevicePath == null ? 0 : profileViewport.DevicePath.GetHashCode();
// Get hash code for the SurroundTopology field if it is not null.
int hashSurroundTopology = profileViewport.SurroundTopology == null ? 0 : profileViewport.SurroundTopology.GetHashCode();
//Calculate the hash code for the product.
return hashFrequencyInMillihertz ^ hashRotation ^ hashScaling ^ hashScanLineOrdering ^
hashDisplayName ^ hashDevicePath ^ hashSurroundTopology;
}
}
}