Updated NVIDIALibrary and WinLibrary to handle win display reordering

NVIDIALibrary and WinLibrary were too rigid in their equality testing, which meant that there was a problem when Windows reordered the displays in the path. This happens randomly when changing to cloned displays, and after a reboot. This would muck up the equality testing, which would prevent users selecting the display profiles. This has now been corrected (as far as we can tell so far).

There is a slight chance that further testing may find other parts of the Windows Display Config that randomly change and need to be updated. Thanks Microsoft.
This commit is contained in:
Terry MacDonald 2022-06-03 21:47:33 +12:00
parent aee177ecb7
commit aede23a83d
3 changed files with 185 additions and 14 deletions

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
@ -2258,12 +2259,25 @@ namespace DisplayMagicianShared.NVIDIA
public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_INFO_V2 other && this.Equals(other);
public bool Equals(NV_DISPLAYCONFIG_PATH_INFO_V2 other)
=> Version == other.Version &&
SourceId == other.SourceId &&
TargetInfoCount == other.TargetInfoCount &&
TargetInfo.SequenceEqual(other.TargetInfo) &&
SourceModeInfo.Equals(other.SourceModeInfo) &&
Flags == other.Flags;
{
if (!(Version == other.Version &&
SourceId == other.SourceId &&
TargetInfoCount == other.TargetInfoCount &&
SourceModeInfo.Equals(other.SourceModeInfo) &&
Flags == other.Flags))
{
return false;
}
// Now we need to go through the HDR states comparing vaues, as the order changes if there is a cloned display
if (!NVImport.EqualButDifferentOrder<NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2>(TargetInfo, other.TargetInfo))
{
return false;
}
return true;
}
public override Int32 GetHashCode()
{
@ -7032,5 +7046,52 @@ namespace DisplayMagicianShared.NVIDIA
// NVAPI_INTERFACE NvAPI_DRS_DeleteProfile(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile)
public static bool EqualButDifferentOrder<T>(IList<T> list1, IList<T> list2)
{
if (list1.Count != list2.Count)
{
return false;
}
// Now we need to go through the list1, checking that all it's items are in list2
foreach (T item1 in list1)
{
bool foundIt = false;
foreach (T item2 in list2)
{
if (item1.Equals(item2))
{
foundIt = true;
break;
}
}
if (!foundIt)
{
return false;
}
}
// Now we need to go through the list2, checking that all it's items are in list1
foreach (T item2 in list2)
{
bool foundIt = false;
foreach (T item1 in list1)
{
if (item1.Equals(item2))
{
foundIt = true;
break;
}
}
if (!foundIt)
{
return false;
}
}
return true;
}
}
}

View File

@ -170,14 +170,25 @@ namespace DisplayMagicianShared.NVIDIA
public override bool Equals(object obj) => obj is NVIDIA_DISPLAY_CONFIG other && this.Equals(other);
public bool Equals(NVIDIA_DISPLAY_CONFIG other)
=> IsCloned == other.IsCloned &&
{
if (!(IsCloned == other.IsCloned &&
IsOptimus == other.IsOptimus &&
PhysicalAdapters.SequenceEqual(other.PhysicalAdapters) &&
MosaicConfig.Equals(other.MosaicConfig) &&
DisplayConfigs.SequenceEqual(other.DisplayConfigs) &&
DRSSettings.SequenceEqual(other.DRSSettings) &&
DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers);
DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers)))
{
return false;
}
// Now we need to go through the display configscomparing values, as the order changes if there is a cloned display
if (!NVIDIALibrary.EqualButDifferentOrder<NV_DISPLAYCONFIG_PATH_INFO_V2>(DisplayConfigs, other.DisplayConfigs))
{
return false;
}
return true;
}
public override int GetHashCode()
{
@ -4306,6 +4317,53 @@ namespace DisplayMagicianShared.NVIDIA
return false;
}
}
public static bool EqualButDifferentOrder<T>(IList<T> list1, IList<T> list2)
{
if (list1.Count != list2.Count)
{
return false;
}
// Now we need to go through the list1, checking that all it's items are in list2
foreach (T item1 in list1)
{
bool foundIt = false;
foreach (T item2 in list2)
{
if (item1.Equals(item2))
{
foundIt = true;
break;
}
}
if (!foundIt)
{
return false;
}
}
// Now we need to go through the list2, checking that all it's items are in list1
foreach (T item2 in list2)
{
bool foundIt = false;
foreach (T item1 in list1)
{
if (item1.Equals(item2))
{
foundIt = true;
break;
}
}
if (!foundIt)
{
return false;
}
}
return true;
}
}

View File

@ -52,15 +52,15 @@ namespace DisplayMagicianShared.Windows
public override bool Equals(object obj) => obj is DISPLAY_SOURCE other && this.Equals(other);
public bool Equals(DISPLAY_SOURCE other)
=> SourceId.Equals(other.SourceId) &&
TargetId.Equals(other.TargetId) &&
=> //SourceId.Equals(other.SourceId) && // Source ID needs to be ignored in this case, as windows moves the source ids around :(
TargetId.Equals(other.TargetId) &&
DevicePath.Equals(other.DevicePath) &&
SourceDpiScalingRel.Equals(other.SourceDpiScalingRel);
//=> true;
public override int GetHashCode()
{
//return 300;
return (SourceId, TargetId, DevicePath, SourceDpiScalingRel).GetHashCode();
//return (SourceId, TargetId, DevicePath, SourceDpiScalingRel).GetHashCode(); // Source ID needs to be ignored in this case, as windows moves the source ids around :(
return (TargetId, DevicePath, SourceDpiScalingRel).GetHashCode();
}
public static bool operator ==(DISPLAY_SOURCE lhs, DISPLAY_SOURCE rhs) => lhs.Equals(rhs);
@ -91,7 +91,6 @@ namespace DisplayMagicianShared.Windows
if (!(IsCloned == other.IsCloned &&
DisplayConfigPaths.SequenceEqual(other.DisplayConfigPaths) &&
DisplayConfigModes.SequenceEqual(other.DisplayConfigModes) &&
DisplayHDRStates.SequenceEqual(other.DisplayHDRStates) &&
// The dictionary keys sometimes change after returning from NVIDIA Surround, so we need to only focus on comparing the values of the GDISettings.
// Additionally, we had to disable the DEviceKey from the equality testing within the GDI library itself as that waould also change after changing back from NVIDIA surround
// This still allows us to detect when refresh rates change, which will allow DisplayMagician to detect profile differences.
@ -101,6 +100,12 @@ namespace DisplayMagicianShared.Windows
return false;
}
// Now we need to go through the HDR states comparing vaues, as the order changes if there is a cloned display
if (!WinLibrary.EqualButDifferentOrder<ADVANCED_HDR_INFO_PER_PATH>(DisplayHDRStates, other.DisplayHDRStates))
{
return false;
}
// Now we need to go through the values to make sure they are the same, but ignore the keys (as they change after each reboot!)
for (int i = 0; i < DisplaySources.Count; i++)
{
@ -2361,6 +2366,53 @@ namespace DisplayMagicianShared.Windows
Utils.SendMessage(windowHandle, Utils.WM_MOUSEMOVE, 0, (y << 16) + x);
}
public static bool EqualButDifferentOrder<T>(IList<T> list1, IList<T> list2)
{
if (list1.Count != list2.Count)
{
return false;
}
// Now we need to go through the list1, checking that all it's items are in list2
foreach (T item1 in list1)
{
bool foundIt = false;
foreach (T item2 in list2)
{
if (item1.Equals(item2))
{
foundIt = true;
break;
}
}
if (!foundIt)
{
return false;
}
}
// Now we need to go through the list2, checking that all it's items are in list1
foreach (T item2 in list2)
{
bool foundIt = false;
foreach (T item1 in list1)
{
if (item1.Equals(item2))
{
foundIt = true;
break;
}
}
if (!foundIt)
{
return false;
}
}
return true;
}
}
[global::System.Serializable]