From 01d6c1b9a9fa9fef29a4ccc05b414419e22305e6 Mon Sep 17 00:00:00 2001 From: Terry MacDonald Date: Tue, 28 Jun 2022 09:29:18 +1200 Subject: [PATCH] First attempt at setting Process DPI Awareness This is required so that the undocumented DISPLAYCONFIG_SOURCE_DPI_SCALE_GET Windows CCD call is given the correct information by Windows 10/11. It gives an abnormal number on some hardware if this is not set. What we do now is set the process DPI context to "System Aware" on boot, but when we are either getting or setting the Windows DPI settings, we quickly swap to "Monitor Aware v2" DPI context, before swapping back to "System Aware" when we're done. This *should* return the correct per monitor settings. --- DisplayMagician/Properties/AssemblyInfo.cs | 4 +-- DisplayMagicianShared/Windows/CCD.cs | 16 ++++++++++++ DisplayMagicianShared/Windows/WinLibrary.cs | 29 ++++++++++++++++++--- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/DisplayMagician/Properties/AssemblyInfo.cs b/DisplayMagician/Properties/AssemblyInfo.cs index 2c748c1..6f470e8 100644 --- a/DisplayMagician/Properties/AssemblyInfo.cs +++ b/DisplayMagician/Properties/AssemblyInfo.cs @@ -26,8 +26,8 @@ using System.Resources; [assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")] // Version information -[assembly: AssemblyVersion("2.4.0.52")] -[assembly: AssemblyFileVersion("2.4.0.52")] +[assembly: AssemblyVersion("2.4.0.57")] +[assembly: AssemblyFileVersion("2.4.0.57")] [assembly: NeutralResourcesLanguageAttribute( "en" )] [assembly: CLSCompliant(true)] diff --git a/DisplayMagicianShared/Windows/CCD.cs b/DisplayMagicianShared/Windows/CCD.cs index fcc67f7..5ce48a4 100644 --- a/DisplayMagicianShared/Windows/CCD.cs +++ b/DisplayMagicianShared/Windows/CCD.cs @@ -19,6 +19,16 @@ namespace DisplayMagicianShared.Windows ERROR_BAD_CONFIGURATION = 1610, } + public enum DPI_AWARENESS_CONTEXT : Int32 + { + DPI_AWARENESS_CONTEXT_UNDEFINED = 0, + DPI_AWARENESS_CONTEXT_UNAWARE = -1, //' DPI unaware. This window does not scale for DPI changes and is always assumed to have a scale factor of 100% (96 DPI). It will be automatically scaled by the system on any other DPI setting. + DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2, //' System DPI aware. This window does not scale for DPI changes. It will query for the DPI once and use that value for the lifetime of the process. If the DPI changes, the process will not adjust to the new DPI value. It will be automatically scaled up or down by the system when the DPI changes from the system value. + DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = -3, // ' Per monitor DPI aware. This window checks for the DPI when it is created and adjusts the scale factor whenever the DPI changes. These processes are not automatically scaled by the system. + DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4, //' Also known as Per Monitor v2. An advancement over the original per-monitor DPI awareness mode, which enables applications to access new DPI-related scaling behaviors on a per top-level window basis. + DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = -5, //' DPI unaware with improved quality of GDI-based content. This mode behaves similarly to DPI_AWARENESS_CONTEXT_UNAWARE, but also enables the system to automatically improve the rendering quality of text and other GDI-based primitives when the window is displayed on a high-DPI monitor. + }; + public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : Int32 { // MS Private API (which seems to use negative numbers) @@ -1167,5 +1177,11 @@ namespace DisplayMagicianShared.Windows // SetDisplayConfig [DllImport("user32")] public static extern WIN32STATUS SetDisplayConfig([In] uint numPathArrayElements, [In] DISPLAYCONFIG_PATH_INFO[] pathArray, [In] uint numModeInfoArrayElements, [In] DISPLAYCONFIG_MODE_INFO[] modeInfoArray, [In] SDC flags); + + [DllImport("user32")] + public static extern bool SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT value); + + + } } \ No newline at end of file diff --git a/DisplayMagicianShared/Windows/WinLibrary.cs b/DisplayMagicianShared/Windows/WinLibrary.cs index 5b1ac85..0c04042 100644 --- a/DisplayMagicianShared/Windows/WinLibrary.cs +++ b/DisplayMagicianShared/Windows/WinLibrary.cs @@ -169,6 +169,10 @@ namespace DisplayMagicianShared.Windows SharedLogger.logger.Trace("WinLibrary/WinLibrary: Intialising Windows CCD library interface"); _initialised = true; + + // Set the DPI awareness for the process this thread is running within so that the DPI calls return the right values at the right times + CCDImport.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE); + _activeDisplayConfig = GetActiveConfig(); _allConnectedDisplayIdentifiers = GetAllConnectedDisplayIdentifiers(); } @@ -564,9 +568,13 @@ namespace DisplayMagicianShared.Windows } } - // Now cycle through the paths and grab the HDR state information + // Now cycle through the paths and grab the state information we need // and map the adapter name to adapter id // and populate the display source information + + // Set the DPI awareness for the process this thread is running within so that the DPI calls return the right values + CCDImport.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + List targetPathIdsToChange = new List(); List targetModeIdsToChange = new List(); List targetIdsFound = new List(); @@ -603,8 +611,18 @@ namespace DisplayMagicianShared.Windows err = CCDImport.DisplayConfigGetDeviceInfo(ref displayScalingInfo); if (err == WIN32STATUS.ERROR_SUCCESS) { - SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found DPI value for source {paths[i].SourceInfo.Id} is {CCDImport.DPI_VALUES[displayScalingInfo.CurrrentScaleRel]}%."); - sourceDpiScalingRel = displayScalingInfo.CurrrentScaleRel; + // Now we need to check if there is a strange value returned by the displayScalingInfo object. If so we reset it to 100% DPI + if (displayScalingInfo.CurrrentScaleRel >= 0 && displayScalingInfo.CurrrentScaleRel < CCDImport.DPI_VALUES.Length) + { + SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found DPI value for source {paths[i].SourceInfo.Id} is {CCDImport.DPI_VALUES[displayScalingInfo.CurrrentScaleRel]}%."); + sourceDpiScalingRel = displayScalingInfo.CurrrentScaleRel; + } + else + { + SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found DPI value for source {paths[i].SourceInfo.Id} is out of range. Windows CCD returned an abnormal value. Using the default Windows DPI Scaling value instead of 100%"); + sourceDpiScalingRel = 0; + } + } else { @@ -774,6 +792,9 @@ namespace DisplayMagicianShared.Windows } } + // Set the DPI awareness for the process this thread is running within so that the DPI calls return the right values + CCDImport.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE); + // Get all the DisplayAdapters currently in the system // This will be used for windows to translate the adapter details beween reboots windowsDisplayConfig.DisplayAdapters = GetAllAdapterIDs(); @@ -1510,6 +1531,7 @@ namespace DisplayMagicianShared.Windows System.Threading.Thread.Sleep(100); SharedLogger.logger.Trace($"WinLibrary/SetWindowsDisplayConfig: Attempting to set Windows DPI Scaling setting for display sources."); + CCDImport.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); foreach (var displaySourceEntry in displayConfig.DisplaySources) { // We only need to set the source on the first display source @@ -1530,6 +1552,7 @@ namespace DisplayMagicianShared.Windows SharedLogger.logger.Warn($"WinLibrary/SetWindowsDisplayConfig: WARNING - Unable to set DPI value for source {displaySourceEntry.Value[0].SourceId} to {CCDImport.DPI_VALUES[displayScalingInfo.ScaleRel]}%."); } } + CCDImport.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE); // NOTE: There is currently no way within Windows CCD API to set the HDR settings to any particular setting