2017-02-26 19:23:31 +00:00
using System ;
using System.Drawing ;
using System.Linq ;
using WindowsDisplayAPI.DisplayConfig ;
using NvAPIWrapper.Mosaic ;
using NvAPIWrapper.Native.Interfaces.Mosaic ;
2020-05-14 10:38:31 +00:00
using System.Collections.Generic ;
2017-02-26 19:23:31 +00:00
2020-12-20 07:42:04 +00:00
namespace DisplayMagicianShared.NVIDIA
2017-02-26 19:23:31 +00:00
{
2020-05-14 10:38:31 +00:00
public class SurroundTopology
2017-02-26 19:23:31 +00:00
{
public SurroundTopology ( GridTopology topology )
{
Rows = topology . Rows ;
Columns = topology . Columns ;
Resolution = new Size ( topology . Resolution . Width , topology . Resolution . Height ) ;
ColorDepth = topology . Resolution . ColorDepth ;
Frequency = topology . Frequency ;
Displays =
topology . Displays . Where (
display = >
2018-10-20 00:27:25 +00:00
Resolution . Width > display . Overlap . HorizontalOverlap & &
Resolution . Height > display . Overlap . VerticalOverlap )
2017-02-26 19:23:31 +00:00
. Select ( display = > new SurroundTopologyDisplay ( display ) )
. ToArray ( ) ;
ApplyWithBezelCorrectedResolution = topology . ApplyWithBezelCorrectedResolution ;
ImmersiveGaming = topology . ImmersiveGaming ;
BaseMosaicPanoramic = topology . BaseMosaicPanoramic ;
DriverReloadAllowed = topology . DriverReloadAllowed ;
AcceleratePrimaryDisplay = topology . AcceleratePrimaryDisplay ;
}
public SurroundTopology ( )
{
2017-08-10 14:21:45 +00:00
}
public bool AcceleratePrimaryDisplay { get ; set ; }
public bool ApplyWithBezelCorrectedResolution { get ; set ; }
public bool BaseMosaicPanoramic { get ; set ; }
public int ColorDepth { get ; set ; }
public int Columns { get ; set ; }
public SurroundTopologyDisplay [ ] Displays { get ; set ; }
public bool DriverReloadAllowed { get ; set ; }
public int Frequency { get ; set ; }
public bool ImmersiveGaming { get ; set ; }
public Size Resolution { get ; set ; }
public int Rows { get ; set ; }
2020-05-14 10:38:31 +00:00
2018-10-20 00:20:18 +00:00
// ReSharper disable once ExcessiveIndentation
2017-02-26 19:23:31 +00:00
public static SurroundTopology FromPathTargetInfo ( PathTargetInfo pathTargetInfo )
{
// We go through the code if only the path belongs to a NVIDIA virtual surround display
2021-05-19 09:32:49 +00:00
// and is not null
if ( pathTargetInfo = = null )
2018-10-20 00:20:18 +00:00
{
2021-05-19 09:32:49 +00:00
SharedLogger . logger . Trace ( $"SurroundTopology/FromPathTargetInfo: The PathTargetInfo object supplied was null, so we have to return null back." ) ;
return null ;
}
string EDIDManufactureCode = "" ;
string friendlyName = "" ;
bool devicePathContainsUID5120 = false ;
bool isNvidiaSurround = false ;
try
{
EDIDManufactureCode = pathTargetInfo . DisplayTarget . EDIDManufactureCode ;
SharedLogger . logger . Trace ( $"SurroundTopology/FromPathTargetInfo: Grabbed EDIDManufactureCode of {EDIDManufactureCode}." ) ;
if ( string . Equals ( EDIDManufactureCode , "NVS" , StringComparison . InvariantCultureIgnoreCase ) )
{
isNvidiaSurround = true ;
}
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"SurroundTopology/FromPathTargetInfo: Exception trying to access EDIDManufactureCode." ) ;
}
try
{
friendlyName = pathTargetInfo . DisplayTarget . FriendlyName ;
SharedLogger . logger . Trace ( $"SurroundTopology/FromPathTargetInfo: Grabbed Display FriendlyName of {friendlyName}." ) ;
if ( string . Equals ( friendlyName , "NV Surround" , StringComparison . InvariantCultureIgnoreCase ) )
{
isNvidiaSurround = true ;
}
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"SurroundTopology/FromPathTargetInfo: Exception trying to access friendlyName." ) ;
}
try
{
devicePathContainsUID5120 = pathTargetInfo . DisplayTarget . DevicePath . ToLower ( ) . Contains ( "&UID5120" . ToLower ( ) ) ;
SharedLogger . logger . Trace ( $"SurroundTopology/FromPathTargetInfo: Testing if the Display DevicePath contains UID5120 = {devicePathContainsUID5120}." ) ;
if ( devicePathContainsUID5120 )
{
isNvidiaSurround = true ;
}
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"SurroundTopology/FromPathTargetInfo: Exception trying to access friendlyName." ) ;
}
// If the checks haven't passed, then we return null
if ( ! isNvidiaSurround )
{
SharedLogger . logger . Trace ( $"SurroundTopology/FromPathTargetInfo: As far as we can tell, this isn't an NVIDIA Surround window, so we're returning null." ) ;
2017-02-26 19:23:31 +00:00
return null ;
2018-10-20 00:20:18 +00:00
}
2017-02-26 19:23:31 +00:00
try
{
2018-10-25 12:57:24 +00:00
// Get parent DisplayConfig PathInfo by checking display targets
2017-02-26 19:23:31 +00:00
var correspondingWindowsPathInfo =
2018-10-25 12:57:24 +00:00
PathInfo . GetActivePaths ( )
2017-08-10 14:21:45 +00:00
. FirstOrDefault (
info = >
info . TargetsInfo . Any (
2018-10-20 00:27:25 +00:00
targetInfo = >
2018-10-25 12:57:24 +00:00
targetInfo . DisplayTarget = = pathTargetInfo . DisplayTarget ) ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( correspondingWindowsPathInfo ! = null )
{
2018-10-25 12:57:24 +00:00
// Get corresponding NvAPI PathInfo
2017-02-26 19:23:31 +00:00
// If position is same, then the two paths are equal, after all position is whats important in path sources
var correspondingNvidiaPathInfo =
NvAPIWrapper . Display . PathInfo . GetDisplaysConfig ( )
. FirstOrDefault (
info = >
2018-10-20 00:27:25 +00:00
info . Position . X = = correspondingWindowsPathInfo . Position . X & &
info . Position . Y = = correspondingWindowsPathInfo . Position . Y & &
info . Resolution . Width = = correspondingWindowsPathInfo . Resolution . Width & &
info . Resolution . Height = = correspondingWindowsPathInfo . Resolution . Height ) ;
2017-02-26 19:23:31 +00:00
if ( correspondingNvidiaPathInfo ! = null )
{
2018-10-25 12:57:24 +00:00
// Get corresponding NvAPI PathTargetInfo
// We now assume that there is only one target for a NvAPI PathInfo, in an other word, for now, it is not possible to have a cloned surround display
2017-02-26 19:23:31 +00:00
var correspondingNvidiaTargetInfo = correspondingNvidiaPathInfo . TargetsInfo . FirstOrDefault ( ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( correspondingNvidiaTargetInfo ! = null )
{
2018-10-25 12:57:24 +00:00
// Get corresponding NvAPI Grid Topology
2018-10-20 00:20:18 +00:00
// We also assume that the NVS monitor uses a similar display id to one of real physical monitors
2017-02-26 19:23:31 +00:00
var correspondingNvidiaTopology =
GridTopology . GetGridTopologies ( )
. FirstOrDefault (
2018-10-20 00:20:18 +00:00
topology = > topology . Displays . Any ( display = >
2018-10-25 12:57:24 +00:00
display . DisplayDevice = = correspondingNvidiaTargetInfo . DisplayDevice ) ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( correspondingNvidiaTopology ! = null )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
return new SurroundTopology ( correspondingNvidiaTopology ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
}
}
}
}
2021-05-19 09:32:49 +00:00
catch ( Exception ex )
2017-02-26 19:23:31 +00:00
{
2021-05-19 09:32:49 +00:00
SharedLogger . logger . Warn ( ex , $"SurroundTopology/FromPathTargetInfo: Exception trying to get the Grid Topology from the NVIDIA driver." ) ;
2017-02-26 19:23:31 +00:00
}
2021-05-19 09:32:49 +00:00
// if we get here, then we've failed to get the NVIDIA Grid Topology
SharedLogger . logger . Warn ( $"SurroundTopology/FromPathTargetInfo: We've tried to get the Grid Topology from the NVIDIA driver but we can't!" ) ;
2017-02-26 19:23:31 +00:00
return null ;
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
/// <inheritdoc />
2021-01-28 08:34:45 +00:00
public override string ToString ( )
2017-02-26 19:23:31 +00:00
{
return $"SurroundTopology[{Rows}, {Columns}] ({Resolution.Width}, {Resolution.Height}) @ {Frequency}" ;
}
2018-10-20 00:08:30 +00:00
// ReSharper disable once ExcessiveIndentation
2017-02-26 19:23:31 +00:00
public GridTopology ToGridTopology ( )
{
var gridTopology = new GridTopology ( Rows , Columns ,
Displays . Select ( display = > display . ToGridTopologyDisplay ( ) ) . ToArray ( ) )
{
ApplyWithBezelCorrectedResolution = ApplyWithBezelCorrectedResolution ,
ImmersiveGaming = ImmersiveGaming ,
BaseMosaicPanoramic = BaseMosaicPanoramic ,
DriverReloadAllowed = DriverReloadAllowed ,
AcceleratePrimaryDisplay = AcceleratePrimaryDisplay
} ;
IDisplaySettings bestDisplaySettings = null ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
foreach ( var displaySetting in gridTopology . GetPossibleDisplaySettings ( ) )
2018-10-20 00:27:25 +00:00
{
if ( displaySetting . Width = = Resolution . Width & &
displaySetting . Height = = Resolution . Height )
2017-02-26 19:23:31 +00:00
{
if ( displaySetting . BitsPerPixel = = ColorDepth )
{
if ( displaySetting . Frequency = = Frequency )
{
bestDisplaySettings = displaySetting ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
break ;
}
2018-10-20 00:27:25 +00:00
if ( bestDisplaySettings = = null | | displaySetting . Frequency > bestDisplaySettings . Frequency )
{
2017-02-26 19:23:31 +00:00
bestDisplaySettings = displaySetting ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
}
2018-10-20 00:27:25 +00:00
else if ( bestDisplaySettings = = null | |
displaySetting . BitsPerPixel > bestDisplaySettings . BitsPerPixel )
2017-02-26 19:23:31 +00:00
{
bestDisplaySettings = displaySetting ;
}
}
2018-10-20 00:27:25 +00:00
else if ( bestDisplaySettings = = null | |
displaySetting . Width * displaySetting . Height >
bestDisplaySettings . Width * bestDisplaySettings . Height )
2017-02-26 19:23:31 +00:00
{
bestDisplaySettings = displaySetting ;
}
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if ( bestDisplaySettings ! = null )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
gridTopology . SetDisplaySettings ( bestDisplaySettings ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
return gridTopology ;
}
2020-05-14 10:38:31 +00:00
// The public override for the Object.Equals
public override bool Equals ( object obj )
{
return this . Equals ( obj as SurroundTopology ) ;
}
// SurroundTopoligies are equal if their contents (except name) are equal
public bool Equals ( SurroundTopology other )
{
// If parameter is null, return false.
2021-01-28 09:03:02 +00:00
if ( other is null )
2020-05-14 10:38:31 +00:00
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 ( AcceleratePrimaryDisplay . Equals ( other . AcceleratePrimaryDisplay ) & &
ApplyWithBezelCorrectedResolution . Equals ( other . ApplyWithBezelCorrectedResolution ) & &
BaseMosaicPanoramic . Equals ( other . BaseMosaicPanoramic ) & &
ColorDepth . Equals ( other . ColorDepth ) & &
Columns . Equals ( other . Columns ) & &
Displays . Length . Equals ( other . Displays . Length ) & &
DriverReloadAllowed . Equals ( other . DriverReloadAllowed ) & &
Frequency . Equals ( other . Frequency ) & &
ImmersiveGaming . Equals ( other . ImmersiveGaming ) & &
Resolution . Equals ( other . Resolution ) & &
Rows . Equals ( other . Rows ) )
{
// If the above all match, then we need to check the Displays matche
if ( Displays = = null & & other . Displays = = null )
return true ;
else if ( Displays ! = null & & other . Displays = = null )
return false ;
else if ( Displays = = null & & other . Displays ! = null )
return false ;
else if ( Displays . SequenceEqual ( other . Displays ) )
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 AcceleratePrimaryDisplay field if it is not null.
int hashAcceleratePrimaryDisplay = AcceleratePrimaryDisplay . GetHashCode ( ) ;
// Get hash code for the ApplyWithBezelCorrectedResolution field if it is not null.
int hashApplyWithBezelCorrectedResolution = ApplyWithBezelCorrectedResolution . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashBaseMosaicPanoramic = BaseMosaicPanoramic . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashColorDepth = ColorDepth . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashColumns = Columns . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashDriverReloadAllowed = DriverReloadAllowed . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashFrequency = Frequency . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashImmersiveGaming = ImmersiveGaming . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashResolution = Resolution . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashRows = Rows . GetHashCode ( ) ;
// Get hash code for the Displays field if it is not null.
int hashDisplays = Displays . GetHashCode ( ) ;
//Calculate the hash code for the product.
return hashAcceleratePrimaryDisplay ^ hashApplyWithBezelCorrectedResolution ^ hashBaseMosaicPanoramic ^
hashColorDepth ^ hashColumns ^ hashDriverReloadAllowed ^ hashFrequency ^ hashImmersiveGaming ^
hashResolution ^ hashRows ^ hashDisplays ;
}
}
// Custom comparer for the ProfileViewportTargetDisplay class
class SurroundTopologyComparer : IEqualityComparer < SurroundTopology >
{
// Products are equal if their names and product numbers are equal.
public bool Equals ( SurroundTopology x , SurroundTopology 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.
2021-01-28 09:03:02 +00:00
if ( x is null | | y is null )
2020-05-14 10:38:31 +00:00
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 . AcceleratePrimaryDisplay . Equals ( y . AcceleratePrimaryDisplay ) & &
x . ApplyWithBezelCorrectedResolution . Equals ( y . ApplyWithBezelCorrectedResolution ) & &
x . BaseMosaicPanoramic . Equals ( y . BaseMosaicPanoramic ) & &
x . ColorDepth . Equals ( y . ColorDepth ) & &
x . Columns . Equals ( y . Columns ) & &
x . Displays . Length . Equals ( y . Displays . Length ) & &
x . DriverReloadAllowed . Equals ( y . DriverReloadAllowed ) & &
x . Frequency . Equals ( y . Frequency ) & &
x . ImmersiveGaming . Equals ( y . ImmersiveGaming ) & &
x . Resolution . Equals ( y . Resolution ) & &
x . Rows . Equals ( y . Rows ) )
{
// If the above all match, then we need to check the Displays matche
if ( x . Displays = = null & & y . Displays = = null )
return true ;
else if ( x . Displays ! = null & & y . Displays = = null )
return false ;
else if ( x . Displays = = null & & y . Displays ! = null )
return false ;
2020-10-09 04:14:23 +00:00
else if ( x . Displays . SequenceEqual ( y . Displays ) )
2020-05-14 10:38:31 +00:00
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 ( SurroundTopology surroundTopology )
{
// Check whether the object is null
2021-01-28 09:03:02 +00:00
if ( surroundTopology is null ) return 0 ;
2020-05-14 10:38:31 +00:00
// Get hash code for the AcceleratePrimaryDisplay field if it is not null.
int hashAcceleratePrimaryDisplay = surroundTopology . AcceleratePrimaryDisplay . GetHashCode ( ) ;
// Get hash code for the ApplyWithBezelCorrectedResolution field if it is not null.
int hashApplyWithBezelCorrectedResolution = surroundTopology . ApplyWithBezelCorrectedResolution . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashBaseMosaicPanoramic = surroundTopology . BaseMosaicPanoramic . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashColorDepth = surroundTopology . ColorDepth . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashColumns = surroundTopology . Columns . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashDriverReloadAllowed = surroundTopology . DriverReloadAllowed . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashFrequency = surroundTopology . Frequency . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashImmersiveGaming = surroundTopology . ImmersiveGaming . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashResolution = surroundTopology . Resolution . GetHashCode ( ) ;
// Get hash code for the FrequencyInMillihertz field if it is not null.
int hashRows = surroundTopology . Rows . GetHashCode ( ) ;
// Get hash code for the Displays field if it is not null.
int hashDisplays = surroundTopology . Displays . GetHashCode ( ) ;
//Calculate the hash code for the product.
return hashAcceleratePrimaryDisplay ^ hashApplyWithBezelCorrectedResolution ^ hashBaseMosaicPanoramic ^
hashColorDepth ^ hashColumns ^ hashDriverReloadAllowed ^ hashFrequency ^ hashImmersiveGaming ^
hashResolution ^ hashRows ^ hashDisplays ;
}
2017-02-26 19:23:31 +00:00
}
}