Added Windows per source DPI scaling settings

This commit is contained in:
Terry MacDonald 2022-05-30 10:01:43 +12:00
parent f40640ccbd
commit a93c9e62d2
3 changed files with 147 additions and 14 deletions

View File

@ -26,8 +26,8 @@ using System.Resources;
[assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")]
// Version information
[assembly: AssemblyVersion("2.3.2.2")]
[assembly: AssemblyFileVersion("2.3.2.2")]
[assembly: AssemblyVersion("2.3.2.5")]
[assembly: AssemblyFileVersion("2.3.2.5")]
[assembly: NeutralResourcesLanguageAttribute( "en" )]
[assembly: CLSCompliant(true)]

View File

@ -19,8 +19,14 @@ namespace DisplayMagicianShared.Windows
ERROR_BAD_CONFIGURATION = 1610,
}
public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : UInt32
public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : Int32
{
// MS Private API (which seems to use negative numbers)
// See https://github.com/lihas/windows-DPI-scaling-sample/blob/master/DPIHelper/DpiHelper.h from Sahil Singh
DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE = -4, // Set current dpi scaling value for a display
DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE = -3, // Returns min, max, suggested, and currently applied DPI scaling values.
// MS Public API
Zero = 0,
DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1, // Specifies the source name of the display device. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns the source name in the DISPLAYCONFIG_SOURCE_DEVICE_NAME structure.
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2, // Specifies information about the monitor. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns info about the monitor in the DISPLAYCONFIG_TARGET_DEVICE_NAME structure.
@ -37,7 +43,7 @@ namespace DisplayMagicianShared.Windows
// Supported starting in Windows<77>10 Fall Creators Update (Version 1709).
DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_SPECIALIZATION = 12,
DISPLAYCONFIG_DEVICE_INFO_SET_MONITOR_SPECIALIZATION = 13,
DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF // Only here to
//DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF // Only here to
}
[Flags]
@ -269,6 +275,73 @@ namespace DisplayMagicianShared.Windows
Other = 255
}
/*
* OS reports DPI scaling values in relative terms, and not absolute terms.
* eg. if current DPI value is 250%, and recommended value is 200%, then
* OS will give us integer 2 for DPI scaling value (starting from recommended
* DPI scaling move 2 steps to the right in this list).
* values observed (and extrapolated) from system settings app (immersive control panel).
*/
/*public enum DPI_VALUES: UInt32
{
DPI_100 = 100,
DPI_125 = 125,
DPI_150 = 150,
DPI_175 = 175,
DPI_200 = 200,
DPI_225 = 225,
DPI_250 = 250,
DPI_300 = 300,
DPI_350 = 350,
DPI_400 = 400,
DPI_450 = 450,
DPI_500 = 500
};*/
/*
* struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET
* @brief used to fetch min, max, suggested, and currently applied DPI scaling values.
* All values are relative to the recommended DPI scaling value
* Note that DPI scaling is a property of the source, and not of target.
*/
[StructLayout(LayoutKind.Sequential)]
public struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER Header;
/*
* @brief min value of DPI scaling is always 100, minScaleRel gives no. of steps down from recommended scaling
* eg. if minScaleRel is -3 => 100 is 3 steps down from recommended scaling => recommended scaling is 175%
*/
public UInt32 MinScaleRel;
/*
* @brief currently applied DPI scaling value wrt the recommended value. eg. if recommended value is 175%,
* => if curScaleRel == 0 the current scaling is 175%, if curScaleRel == -1, then current scale is 150%
*/
public UInt32 CurrrentScaleRel;
/*
* @brief maximum supported DPI scaling wrt recommended value
*/
public UInt32 MaxScaleRel;
};
/*
* struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET
* @brief set DPI scaling value of a source
* Note that DPI scaling is a property of the source, and not of target.
*/
public struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER Header;
/*
* @brief The value we want to set. The value should be relative to the recommended DPI scaling value of source.
* eg. if scaleRel == 1, and recommended value is 175% => we are trying to set 200% scaling for the source
*/
public UInt32 ScaleRel;
};
[StructLayout(LayoutKind.Sequential)]
public struct DISPLAYCONFIG_DEVICE_INFO_HEADER : IEquatable<DISPLAYCONFIG_DEVICE_INFO_HEADER>
{
@ -1022,6 +1095,7 @@ namespace DisplayMagicianShared.Windows
// Set some useful constants
public const SDC SDC_CCD_TEST_IF_VALID = (SDC.SDC_VALIDATE | SDC.SDC_USE_SUPPLIED_DISPLAY_CONFIG);
public const uint DISPLAYCONFIG_PATH_MODE_IDX_INVALID = 0xffffffff;
public static readonly UInt32[] DPI_VALUES = { 100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500 };
// GetDisplayConfigBufferSizes
@ -1069,6 +1143,10 @@ namespace DisplayMagicianShared.Windows
[DllImport("user32")]
public static extern WIN32STATUS DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SDR_WHITE_LEVEL requestPacket);
[DllImport("user32")]
public static extern WIN32STATUS DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SOURCE_DPI_SCALE_GET requestPacket);
// DisplayConfigSetDeviceInfo
[DllImport("user32")]
public static extern WIN32STATUS DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SET_TARGET_PERSISTENCE requestPacket);
@ -1076,6 +1154,9 @@ namespace DisplayMagicianShared.Windows
[DllImport("user32")]
public static extern WIN32STATUS DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE requestPacket);
[DllImport("user32")]
public static extern WIN32STATUS DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SOURCE_DPI_SCALE_SET requestPacket);
// Have disabled the DisplayConfigSetDeviceInfo options except for SET_TARGET_PERSISTENCE, as per the note
// from https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-displayconfigsetdeviceinfo
@ -1086,6 +1167,5 @@ 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);
}
}

View File

@ -46,13 +46,17 @@ namespace DisplayMagicianShared.Windows
public UInt32 SourceId;
public UInt32 TargetId;
public string DevicePath;
//The value we want to set. The value should be relative to the recommended DPI scaling value of source.
// eg. if scaleRel == 1, and recommended value is 175% => we are trying to set 200% scaling for the source
public UInt32 SourceDpiScalingRel;
public override bool Equals(object obj) => obj is DISPLAY_SOURCE other && this.Equals(other);
public bool Equals(DISPLAY_SOURCE other)
=> true;
public override int GetHashCode()
{
return 300;
//return 300;
return (AdapterId, SourceId, TargetId, DevicePath, SourceDpiScalingRel).GetHashCode();
}
public static bool operator ==(DISPLAY_SOURCE lhs, DISPLAY_SOURCE rhs) => lhs.Equals(rhs);
@ -91,12 +95,14 @@ namespace DisplayMagicianShared.Windows
// NOTE: I have disabled the TaskBar specific matching for now due to errors I cannot fix
// WinLibrary will still track the location of the taskbars, but won't actually set them as the setting of the taskbars doesnt work at the moment.
/*&&
TaskBarLayout.SequenceEqual(other.TaskBarLayout) &&
TaskBarLayout.Values.SequenceEqual(other.TaskBarLayout.Values) &&
TaskBarSettings.Equals(other.TaskBarSettings);*/
public override int GetHashCode()
{
return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates, IsCloned, DisplayIdentifiers, TaskBarLayout, TaskBarSettings).GetHashCode();
// Temporarily disabled this to make sure that the hashcode generation matched the equality tests.
//return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates, IsCloned, DisplayIdentifiers, TaskBarLayout, TaskBarSettings).GetHashCode();
return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates, IsCloned, DisplayIdentifiers).GetHashCode();
}
public static bool operator ==(WINDOWS_DISPLAY_CONFIG lhs, WINDOWS_DISPLAY_CONFIG rhs) => lhs.Equals(rhs);
@ -285,7 +291,7 @@ namespace DisplayMagicianShared.Windows
{
SharedLogger.logger.Error(ex, "WinLibrary/PatchAdapterIDs: Exception while going through the display config paths to update the adapter id");
}
try
{
@ -315,7 +321,7 @@ namespace DisplayMagicianShared.Windows
{
SharedLogger.logger.Error(ex, "WinLibrary/PatchAdapterIDs: Exception while going through the display config modes to update the adapter id");
}
try
{
@ -350,7 +356,7 @@ namespace DisplayMagicianShared.Windows
hdrInfo.SDRWhiteLevel.Header.AdapterId = AdapterValueToLUID(newAdapterValue);
}
}
}
}
else
{
SharedLogger.logger.Warn($"WinLibrary/PatchAdapterIDs: There are no Display HDR states to update. Skipping.");
@ -360,7 +366,7 @@ namespace DisplayMagicianShared.Windows
{
SharedLogger.logger.Error(ex, "WinLibrary/PatchAdapterIDs: Exception while going through the display config HDR info to update the adapter id");
}
try
{
@ -398,7 +404,7 @@ namespace DisplayMagicianShared.Windows
{
SharedLogger.logger.Error(ex, "WinLibrary/PatchAdapterIDs: Exception while going through the display sources list info to update the adapter id");
}
}
public bool UpdateActiveConfig()
@ -525,6 +531,26 @@ namespace DisplayMagicianShared.Windows
// Track if this display is a cloned path
bool isClonedPath = false;
// Get the Windows Scaling DPI per display
UInt32 sourceDpiScalingRel = 0;
DISPLAYCONFIG_SOURCE_DPI_SCALE_GET displayScalingInfo = new DISPLAYCONFIG_SOURCE_DPI_SCALE_GET();
displayScalingInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE;
displayScalingInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DPI_SCALE_GET>(); ;
displayScalingInfo.Header.AdapterId = paths[i].SourceInfo.AdapterId;
displayScalingInfo.Header.Id = paths[i].SourceInfo.Id;
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;
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get advanced color settings for display {paths[i].TargetInfo.Id}.");
}
// get display source name
var sourceInfo = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
sourceInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
@ -534,6 +560,7 @@ 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))
@ -543,6 +570,7 @@ namespace DisplayMagicianShared.Windows
ds.AdapterId = paths[i].SourceInfo.AdapterId;
ds.SourceId = paths[i].SourceInfo.Id;
ds.TargetId = paths[i].TargetInfo.Id;
ds.SourceDpiScalingRel = sourceDpiScalingRel;
windowsDisplayConfig.DisplaySources[sourceInfo.ViewGdiDeviceName].Add(ds);
isClonedPath = true;
isClonedProfile = true;
@ -556,6 +584,7 @@ namespace DisplayMagicianShared.Windows
ds.AdapterId = paths[i].SourceInfo.AdapterId;
ds.SourceId = paths[i].SourceInfo.Id;
ds.TargetId = paths[i].TargetInfo.Id;
ds.SourceDpiScalingRel = sourceDpiScalingRel;
sources.Add(ds);
windowsDisplayConfig.DisplaySources.Add(sourceInfo.ViewGdiDeviceName, sources);
}
@ -607,6 +636,7 @@ namespace DisplayMagicianShared.Windows
//gotAdapterName = true;
}
// Get advanced color info
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get advanced color info for display {paths[i].TargetInfo.Id}.");
@ -1388,9 +1418,32 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: SUCCESS! The display configuration has been successfully applied");
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Waiting 0.1 second to let the display change take place before adjusting the Windows CCD HDR settings");
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Waiting 0.1 second to let the display change take place before adjusting the Windows CCD Source DPI scaling settings");
System.Threading.Thread.Sleep(100);
SharedLogger.logger.Trace($"WinLibrary/SetWindowsDisplayConfig: Attempting to set Windows DPI Scaling setting for display sources.");
foreach (var displaySourceEntry in displayConfig.DisplaySources)
{
// We only need to set the source on the first display source
// Set the Windows Scaling DPI per source
DISPLAYCONFIG_SOURCE_DPI_SCALE_SET displayScalingInfo = new DISPLAYCONFIG_SOURCE_DPI_SCALE_SET();
displayScalingInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE;
displayScalingInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DPI_SCALE_SET>(); ;
displayScalingInfo.Header.AdapterId = displaySourceEntry.Value[0].AdapterId;
displayScalingInfo.Header.Id = displaySourceEntry.Value[0].SourceId;
displayScalingInfo.ScaleRel = displaySourceEntry.Value[0].SourceDpiScalingRel;
err = CCDImport.DisplayConfigSetDeviceInfo(ref displayScalingInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/SetWindowsDisplayConfig: Setting DPI value for source {displaySourceEntry.Value[0].SourceId} to {CCDImport.DPI_VALUES[displayScalingInfo.ScaleRel]}%.");
}
else
{
SharedLogger.logger.Warn($"WinLibrary/SetWindowsDisplayConfig: WARNING - Unable to set DPI value for source {displaySourceEntry.Value[0].SourceId} to {CCDImport.DPI_VALUES[displayScalingInfo.ScaleRel]}%.");
}
}
// NOTE: There is currently no way within Windows CCD API to set the HDR settings to any particular setting
// This code will only turn on the HDR setting.
foreach (ADVANCED_HDR_INFO_PER_PATH myHDRstate in displayConfig.DisplayHDRStates)