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.
This commit is contained in:
Terry MacDonald 2022-06-28 09:29:18 +12:00
parent e0d1d13c54
commit 01d6c1b9a9
3 changed files with 44 additions and 5 deletions

View File

@ -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)]

View File

@ -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);
}
}

View File

@ -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<uint> targetPathIdsToChange = new List<uint>();
List<uint> targetModeIdsToChange = new List<uint>();
List<uint> targetIdsFound = new List<uint>();
@ -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