From 9416ac3346126a6f9308e47526509acbbc09f88b Mon Sep 17 00:00:00 2001 From: Terry MacDonald Date: Sat, 11 Dec 2021 00:15:49 +1300 Subject: [PATCH] Updated WinLibrary to latest version This corrects some errors with the WinLibrary and how it handles cloned displays. Also removed the 0.5 second delay between the end of the NVIDIALibrary call, and the start of the WinLibrary one, because they do not appear to be needed based on the NVIDIALibrary testing we've been doing with NVIDIAInfo. Also corrected some log entries and comments. --- DisplayMagicianShared/ProfileItem.cs | 14 +- DisplayMagicianShared/Windows/WinLibrary.cs | 135 ++++++++++++++++---- 2 files changed, 115 insertions(+), 34 deletions(-) diff --git a/DisplayMagicianShared/ProfileItem.cs b/DisplayMagicianShared/ProfileItem.cs index 8959dd1..324008d 100644 --- a/DisplayMagicianShared/ProfileItem.cs +++ b/DisplayMagicianShared/ProfileItem.cs @@ -664,8 +664,8 @@ namespace DisplayMagicianShared { SharedLogger.logger.Trace($"ProfileRepository/SetActive: The NVIDIA display settings within profile {Name} were successfully applied."); - SharedLogger.logger.Trace($"ProfileRepository/SetActive: Waiting 0.5 seconds to let the NVIDIA display change take place before setting the Windows CCD display settings"); - System.Threading.Thread.Sleep(500); + /*SharedLogger.logger.Trace($"ProfileRepository/SetActive: Waiting 0.5 seconds to let the NVIDIA display change take place before setting the Windows CCD display settings"); + System.Threading.Thread.Sleep(500);*/ // Lets update the screens so Windows knows whats happening // NVIDIA makes such large changes to the available screens in windows, we need to do this. @@ -730,16 +730,16 @@ namespace DisplayMagicianShared { SharedLogger.logger.Trace($"ProfileRepository/SetActive: The AMD display settings within profile {Name} were successfully applied."); - SharedLogger.logger.Trace($"ProfileRepository/SetActive: Waiting 0.5 seconds to let the AMD display change take place before setting the Windows CCD display settings"); + /*SharedLogger.logger.Trace($"ProfileRepository/SetActive: Waiting 0.5 seconds to let the AMD display change take place before setting the Windows CCD display settings"); System.Threading.Thread.Sleep(500); - +*/ // Lets update the screens so Windows knows whats happening - // NVIDIA makes such large changes to the available screens in windows, we need to do this. + // AMD makes such large changes to the available screens in windows, we need to do this. winLibrary.UpdateActiveConfig(); // Then let's try to also apply the windows changes - // Note: we are unable to check if the Windows CCD display config is possible, as it won't match if either the current display config is a Mosaic config, - // or if the display config we want to change to is a Mosaic config. So we just have to assume that it will work! + // Note: we are unable to check if the Windows CCD display config is possible, as it won't match if either the current display config is an Eyefinity config, + // or if the display config we want to change to is an Eyefinity config. So we just have to assume that it will work! bool itWorkedforWindows = winLibrary.SetActiveConfig(_windowsDisplayConfig); if (itWorkedforWindows) { diff --git a/DisplayMagicianShared/Windows/WinLibrary.cs b/DisplayMagicianShared/Windows/WinLibrary.cs index 68d1041..54c5cbb 100644 --- a/DisplayMagicianShared/Windows/WinLibrary.cs +++ b/DisplayMagicianShared/Windows/WinLibrary.cs @@ -359,28 +359,41 @@ namespace DisplayMagicianShared.Windows // Next, extract the UID entries for the displays as that's what the Path IDs are normally supposed to be // This is how we know the actual target id's ofd the monitors currently connected Regex rx = new Regex(@"UID(?\d+)#", RegexOptions.Compiled | RegexOptions.IgnoreCase); - HashSet physicalTargetIdsToFind = new HashSet(); + HashSet physicalTargetIdsAvailable = new HashSet(); foreach (string displayIdentifier in windowsDisplayConfig.DisplayIdentifiers) { MatchCollection mc = rx.Matches(displayIdentifier); if (mc.Count > 0) { - physicalTargetIdsToFind.Add(UInt32.Parse(mc[0].Groups["uid"].Value)); + physicalTargetIdsAvailable.Add(UInt32.Parse(mc[0].Groups["uid"].Value)); } } // Now cycle through the paths and grab the HDR state information // and map the adapter name to adapter id - HashSet targetIdsToChange = new HashSet(); + List targetPathIdsToChange = new List(); + List targetModeIdsToChange = new List(); + List targetIdsFound = new List(); + List replacementIds = new List(); + bool isClonedProfile = false; var hdrInfos = new ADVANCED_HDR_INFO_PER_PATH[pathCount]; int hdrInfoCount = 0; for (int i = 0; i < paths.Length; i++) { + bool gotSourceDeviceName = false; + bool gotAdapterName = false; + bool gotAdvancedColorInfo = false; + bool gotSdrWhiteLevel = false; + // Figure out if this path has a physical targetId, and if it doesn't store it - if (!physicalTargetIdsToFind.Contains(paths[i].TargetInfo.Id)) + if (physicalTargetIdsAvailable.Contains(paths[i].TargetInfo.Id)) + { + targetIdsFound.Add(paths[i].TargetInfo.Id); + } + else { // Add to the list of physical path target ids we need to patch later - targetIdsToChange.Add(paths[i].TargetInfo.Id); + targetPathIdsToChange.Add(paths[i].TargetInfo.Id); } // Track if this display is a cloned path @@ -394,12 +407,14 @@ namespace DisplayMagicianShared.Windows err = CCDImport.DisplayConfigGetDeviceInfo(ref sourceInfo); if (err == WIN32STATUS.ERROR_SUCCESS) { + gotSourceDeviceName = true; // Store it for later if (windowsDisplayConfig.DisplaySources.ContainsKey(sourceInfo.ViewGdiDeviceName)) { // We already have at least one display using this source, so we need to add the other cloned display to the existing list windowsDisplayConfig.DisplaySources[sourceInfo.ViewGdiDeviceName].Add(paths[i].SourceInfo.Id); isClonedPath = true; + isClonedProfile = true; windowsDisplayConfig.IsCloned = true; } else @@ -440,6 +455,7 @@ namespace DisplayMagicianShared.Windows err = CCDImport.DisplayConfigGetDeviceInfo(ref adapterInfo); if (err == WIN32STATUS.ERROR_SUCCESS) { + gotAdapterName = true; // Store it for later windowsDisplayConfig.DisplayAdapters.Add(paths[i].TargetInfo.AdapterId.Value, adapterInfo.AdapterDevicePath); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found adapter name {adapterInfo.AdapterDevicePath} for adapter {paths[i].TargetInfo.AdapterId.Value}."); @@ -460,6 +476,7 @@ namespace DisplayMagicianShared.Windows err = CCDImport.DisplayConfigGetDeviceInfo(ref colorInfo); if (err == WIN32STATUS.ERROR_SUCCESS) { + gotAdvancedColorInfo = true; SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found color info for display {paths[i].TargetInfo.Id}."); if (colorInfo.AdvancedColorSupported) { @@ -484,7 +501,7 @@ namespace DisplayMagicianShared.Windows } // get SDR white levels - SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get SDR white levels for adapter {paths[i].TargetInfo.AdapterId.Value}."); + SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get SDR white levels for display {paths[i].TargetInfo.Id}."); var whiteLevelInfo = new DISPLAYCONFIG_SDR_WHITE_LEVEL(); whiteLevelInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL; whiteLevelInfo.Header.Size = (uint)Marshal.SizeOf(); @@ -493,6 +510,7 @@ namespace DisplayMagicianShared.Windows err = CCDImport.DisplayConfigGetDeviceInfo(ref whiteLevelInfo); if (err == WIN32STATUS.ERROR_SUCCESS) { + gotSdrWhiteLevel = true; SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found SDR White levels for display {paths[i].TargetInfo.Id}."); } else @@ -503,39 +521,103 @@ namespace DisplayMagicianShared.Windows hdrInfos[hdrInfoCount] = new ADVANCED_HDR_INFO_PER_PATH(); hdrInfos[hdrInfoCount].AdapterId = paths[i].TargetInfo.AdapterId; hdrInfos[hdrInfoCount].Id = paths[i].TargetInfo.Id; - hdrInfos[hdrInfoCount].AdvancedColorInfo = colorInfo; - hdrInfos[hdrInfoCount].SDRWhiteLevel = whiteLevelInfo; + if (gotAdvancedColorInfo) + { + hdrInfos[hdrInfoCount].AdvancedColorInfo = colorInfo; + } + if (gotSdrWhiteLevel) + { + hdrInfos[hdrInfoCount].SDRWhiteLevel = whiteLevelInfo; + } hdrInfoCount++; } - // Now we need to figure out the maps between the cloned ID and the real physical target id - // the Advanced color info structure actually holds this information! So lets mine it. - Dictionary targetIdMap = new Dictionary(); - foreach (var hdrInfo in hdrInfos) - { - targetIdMap[hdrInfo.Id] = hdrInfo.AdvancedColorInfo.Header.Id; - } - // Now we need to go through the list of paths again and patch the 'cloned' displays with a real display ID so the config works - for (int i = 0; i < paths.Length; i++) + // Go through the list of physicalTargetIdsAvailable + // ignore the ones that were found + // if one was not found, then + // go through the modes + // patch the target + if (isClonedProfile) { - if (targetIdsToChange.Contains(paths[i].TargetInfo.Id)) + // Figure out which available displays are unused (in path priority order) + foreach (var physicalTargetId in physicalTargetIdsAvailable) { - // Patch the cloned ids with a real working one! - paths[i].TargetInfo.Id = targetIdMap[paths[i].TargetInfo.Id]; + if (!targetIdsFound.Contains(physicalTargetId)) + { + // this is a candidate physical target id to use as a replacement + replacementIds.Add(physicalTargetId); + } + } + + // Now go through and figure out a mapping of old target id to new replacement id + Dictionary targetIdMap = new Dictionary(); + for (int i = 0; i < targetPathIdsToChange.Count; i++) + { + uint targetPathId = targetPathIdsToChange[i]; + if (i < replacementIds.Count) + { + targetIdMap[targetPathId] = replacementIds[i]; + } + } + + + // Now we need to go through the list of paths again and patch the 'cloned' displays with a real display ID so the config works + for (int i = 0; i < paths.Length; i++) + { + if (targetIdMap.ContainsKey(paths[i].TargetInfo.Id)) + { + // Patch the cloned ids with a real working one! + paths[i].TargetInfo.Id = targetIdMap[paths[i].TargetInfo.Id]; + } + } + + // And then we need to go through the list of modes again and patch the 'cloned' displays with a real display ID so the display layout is right in cloned displays + for (int i = 0; i < modes.Length; i++) + { + // We only change the ids that match in InfoType for target displays + if (modes[i].InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_TARGET && targetIdMap.ContainsKey(modes[i].Id)) + { + // Patch the cloned ids with a real working one! + modes[i].Id = targetIdMap[modes[i].Id]; + } } } - // And then we need to go through the list of modes again and patch the 'cloned' displays with a real display ID so the display layout is right in cloned displays - for (int i = 0; i < modes.Length; i++) + + /*if (hdrInfos.Length > 0) { - // We only change the ids that match in InfoType for target displays - if (modes[i].InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_TARGET && targetIdsToChange.Contains(modes[i].Id)) + // If the screen + foreach (var hdrInfo in hdrInfos) { - // Patch the cloned ids with a real working one! - modes[i].Id = targetIdMap[modes[i].Id]; + targetIdMap[hdrInfo.Id] = hdrInfo.SDRWhiteLevel.Header.Id; + } + + // Now we need to go through the list of paths again and patch the 'cloned' displays with a real display ID so the config works + for (int i = 0; i < paths.Length; i++) + { + if (targetIdsToChange.Contains(paths[i].TargetInfo.Id)) + { + // Patch the cloned ids with a real working one! + paths[i].TargetInfo.Id = targetIdMap[paths[i].TargetInfo.Id]; + } + } + + // And then we need to go through the list of modes again and patch the 'cloned' displays with a real display ID so the display layout is right in cloned displays + for (int i = 0; i < modes.Length; i++) + { + // We only change the ids that match in InfoType for target displays + if (modes[i].InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_TARGET && targetIdsToChange.Contains(modes[i].Id)) + { + // Patch the cloned ids with a real working one! + modes[i].Id = targetIdMap[modes[i].Id]; + } } } + else + { + SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - There weren't any HDR Info objects created, so we have none to parse!"); + }*/ // Store the active paths and modes in our display config object windowsDisplayConfig.DisplayConfigPaths = paths; @@ -689,7 +771,6 @@ namespace DisplayMagicianShared.Windows return DisplaySources; } - private LUID AdapterValueToLUID(ulong adapterValue) { LUID luid = new LUID();