Updated WinLibrary with latest GDI API changes

This will hopefully fix the issues #45 and #46.
This commit is contained in:
Terry MacDonald
2021-10-15 10:01:21 +13:00
parent a9527ffd6e
commit 4a8acd4b86
4 changed files with 873 additions and 4 deletions

View File

@ -7,6 +7,7 @@ using Microsoft.Win32.SafeHandles;
using DisplayMagicianShared;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Collections.Generic;
namespace DisplayMagicianShared.Windows
{
@ -41,6 +42,7 @@ namespace DisplayMagicianShared.Windows
public DISPLAYCONFIG_PATH_INFO[] DisplayConfigPaths;
public DISPLAYCONFIG_MODE_INFO[] DisplayConfigModes;
public ADVANCED_HDR_INFO_PER_PATH[] DisplayHDRStates;
public Dictionary<string, GDI_DISPLAY_SETTING> GdiDisplaySettings;
// Note: We purposely have left out the DisplaySources from the Equals as it's order keeps changing after each reboot and after each profile swap
// and it is informational only and doesn't contribute to the configuration (it's used for generating the Screens structure, and therefore for
// generating the profile icon.
@ -52,11 +54,12 @@ namespace DisplayMagicianShared.Windows
=> DisplayConfigPaths.SequenceEqual(other.DisplayConfigPaths) &&
DisplayConfigModes.SequenceEqual(other.DisplayConfigModes) &&
DisplayHDRStates.SequenceEqual(other.DisplayHDRStates) &&
GdiDisplaySettings.SequenceEqual(other.GdiDisplaySettings) &&
DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers);
public override int GetHashCode()
{
return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates).GetHashCode();
return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates, GdiDisplaySettings, DisplayIdentifiers).GetHashCode();
}
public static bool operator ==(WINDOWS_DISPLAY_CONFIG lhs, WINDOWS_DISPLAY_CONFIG rhs) => lhs.Equals(rhs);
@ -128,6 +131,24 @@ namespace DisplayMagicianShared.Windows
return _instance;
}
public WINDOWS_DISPLAY_CONFIG CreateDefaultConfig()
{
WINDOWS_DISPLAY_CONFIG myDefaultConfig = new WINDOWS_DISPLAY_CONFIG();
// Fill in the minimal amount we need to avoid null references
// so that we won't break json.net when we save a default config
myDefaultConfig.DisplayAdapters = new Dictionary<ulong, string>();
myDefaultConfig.DisplayConfigModes = new DISPLAYCONFIG_MODE_INFO[0];
myDefaultConfig.DisplayConfigPaths = new DISPLAYCONFIG_PATH_INFO[0];
myDefaultConfig.DisplayHDRStates = new ADVANCED_HDR_INFO_PER_PATH[0];
myDefaultConfig.DisplayIdentifiers = new List<string>();
myDefaultConfig.DisplaySources = new Dictionary<string, uint>();
myDefaultConfig.GdiDisplaySettings = new Dictionary<string, GDI_DISPLAY_SETTING>();
return myDefaultConfig;
}
private void PatchAdapterIDs(ref WINDOWS_DISPLAY_CONFIG savedDisplayConfig, Dictionary<ulong, string> currentAdapterMap)
{
@ -389,10 +410,55 @@ namespace DisplayMagicianShared.Windows
hdrInfoCount++;
}
// Get the list of all display adapters in this machine through GDI
Dictionary<string, GDI_DISPLAY_SETTING> gdiDeviceSettings = new Dictionary<string, GDI_DISPLAY_SETTING>();
UInt32 displayDeviceNum = 0;
DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE();
displayDevice.Size = (UInt32)Marshal.SizeOf<DISPLAY_DEVICE>();
while (GDIImport.EnumDisplayDevices(null, displayDeviceNum, ref displayDevice, 0))
{
// Now we try and grab the GDI Device Settings for each display device
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the current Display Settings for {displayDevice.DeviceName}");
if (displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.AttachedToDesktop) || displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.MultiDriver))
{
// If the display device is attached to the Desktop, or a type of display that is represented by a psudeo mirroring application, then skip this display
// e.g. some sort of software interfaced display that doesn't have a physical plug, or maybe uses USB for communication
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the current Display Settings for {displayDevice.DeviceName}");
DEVICE_MODE currentMode = new DEVICE_MODE();
currentMode.Size = (UInt16)Marshal.SizeOf<DEVICE_MODE>();
bool gdiWorked = GDIImport.EnumDisplaySettings(displayDevice.DeviceName, DISPLAY_SETTINGS_MODE.CurrentSettings, ref currentMode);
if (gdiWorked)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Got the current Display Settings from display {displayDevice.DeviceName}.");
GDI_DISPLAY_SETTING myDisplaySetting = new GDI_DISPLAY_SETTING();
myDisplaySetting.IsEnabled = true; // Always true if we get here
myDisplaySetting.Device = displayDevice;
myDisplaySetting.DeviceMode = currentMode;
if (displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.PrimaryDevice))
{
// This is a primary device, so we'll set that too.
myDisplaySetting.IsPrimary = true;
}
gdiDeviceSettings[displayDevice.DeviceName] = myDisplaySetting;
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get current display mode settings from display {displayDevice.DeviceName}.");
}
}
else
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: The display {displayDevice.DeviceName} is either not attached to the desktop, or is not a mirroring driver. Skipping this display device as we cannot use it.");
}
displayDeviceNum++;
}
// Store the active paths and modes in our display config object
windowsDisplayConfig.DisplayConfigPaths = paths;
windowsDisplayConfig.DisplayConfigModes = modes;
windowsDisplayConfig.DisplayHDRStates = hdrInfos;
windowsDisplayConfig.GdiDisplaySettings = gdiDeviceSettings;
windowsDisplayConfig.DisplayIdentifiers = GetCurrentDisplayIdentifiers();
return windowsDisplayConfig;
@ -620,6 +686,8 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the adapter device path for target #{path.TargetInfo.AdapterId}");
}
// show the
// get display target preferred mode
var targetPreferredInfo = new DISPLAYCONFIG_TARGET_PREFERRED_MODE();
targetPreferredInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE;
@ -745,6 +813,76 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to find out the SDL white level for display #{path.TargetInfo.Id}");
}
}
// Get the list of all display adapters in this machine through GDI
Dictionary<string, GDI_DISPLAY_SETTING> gdiDeviceSettings = new Dictionary<string, GDI_DISPLAY_SETTING>();
UInt32 displayDeviceNum = 0;
DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE();
displayDevice.Size = (UInt32)Marshal.SizeOf<DISPLAY_DEVICE>();
stringToReturn += $"----++++==== GDI Device Information ====++++----\n";
while (GDIImport.EnumDisplayDevices(null, displayDeviceNum, ref displayDevice, 0))
{
// Now we try and grab the GDI Device Info for each display device
stringToReturn += $"****** Display Device Info for Display {displayDevice.DeviceName} *******\n";
stringToReturn += $" Display Device ID: {displayDevice.DeviceId}\n";
stringToReturn += $" Display Device Key: {displayDevice.DeviceKey}\n";
stringToReturn += $" Display Device Name: {displayDevice.DeviceName}\n";
stringToReturn += $" Display Device String: {displayDevice.DeviceString}\n";
stringToReturn += $" Is Attached to Desktop: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.AttachedToDesktop)}\n";
stringToReturn += $" Is Disconnected: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.Disconnect)}\n";
stringToReturn += $" Is a Mirroing Device: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.MirroringDriver)}\n";
stringToReturn += $" Has Modes Pruned: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.ModesPruned)}\n";
stringToReturn += $" Is Multi-driver: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.MultiDriver)}\n";
stringToReturn += $" Is Primary Display Device: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.PrimaryDevice)}\n";
stringToReturn += $" Is Remote Display Device: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.Remote)}\n";
stringToReturn += $" Is Removable Display Device: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.Removable)}\n";
stringToReturn += $" Is VGA Compatible Display Device: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.VGACompatible)}\n";
stringToReturn += $"\n";
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the current Display Settings for {displayDevice.DeviceName}");
if (displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.AttachedToDesktop) || displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.MultiDriver))
{
// If the display device is attached to the Desktop, or a type of display that is represented by a psudeo mirroring application, then skip this display
// e.g. some sort of software interfaced display that doesn't have a physical plug, or maybe uses USB for communication
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the current Display Settings for {displayDevice.DeviceName}");
stringToReturn += $" Display Device Settings for attached Display {displayDevice.DeviceName} :\n";
DEVICE_MODE currentMode = new DEVICE_MODE();
currentMode.Size = (UInt16)Marshal.SizeOf<DEVICE_MODE>();
bool gdiWorked = GDIImport.EnumDisplaySettings(displayDevice.DeviceName, DISPLAY_SETTINGS_MODE.CurrentSettings, ref currentMode);
if (gdiWorked)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Got the current Display Settings from display {displayDevice.DeviceName}.");
// Now we try and grab the GDI Device Settings for each display device
stringToReturn += $" Bits Per Pixel: {currentMode.BitsPerPixel}\n";
stringToReturn += $" Device Name: {currentMode.DeviceName}\n";
stringToReturn += $" Display Fixed Output: {currentMode.DisplayFixedOutput}\n";
stringToReturn += $" Grayscale Display: {currentMode.DisplayFlags.HasFlag(DISPLAY_FLAGS.Grayscale)}\n";
stringToReturn += $" Interlaced Display: {currentMode.DisplayFlags.HasFlag(DISPLAY_FLAGS.Interlaced)}\n";
stringToReturn += $" Refresh Rate: {currentMode.DisplayFrequency}Hz\n";
stringToReturn += $" Display Rotation: {currentMode.DisplayOrientation.ToString("G")}\n";
stringToReturn += $" Driver Extra: {currentMode.DriverExtra}\n";
stringToReturn += $" Driver Version: {currentMode.DriverVersion}\n";
stringToReturn += $" All Display Fields populated by driver: {currentMode.Fields.HasFlag(DEVICE_MODE_FIELDS.AllDisplay)}\n";
stringToReturn += $" Display Width and Height in Pixels: {currentMode.PixelsWidth} x {currentMode.PixelsHeight}\n";
stringToReturn += $" Display Position: X:{currentMode.Position.X}, Y:{currentMode.Position.Y}\n";
stringToReturn += $" Specification Version: {currentMode.SpecificationVersion}\n";
stringToReturn += $"\n";
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get current display mode settings from display {displayDevice.DeviceName}.");
stringToReturn += $" No display settings found.\n\n";
}
}
else
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: The display {displayDevice.DeviceName} is either not attached to the desktop, or is not a mirroring driver. Skipping this display device as we cannot use it.");
}
displayDeviceNum++;
}
return stringToReturn;
}
@ -793,7 +931,7 @@ namespace DisplayMagicianShared.Windows
foreach (ADVANCED_HDR_INFO_PER_PATH myHDRstate in displayConfig.DisplayHDRStates)
{
SharedLogger.logger.Trace($"Trying to get information whether HDR color is in use now on Display {myHDRstate.Id}.");
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Trying to get information whether HDR color is in use now on Display {myHDRstate.Id}.");
// Get advanced HDR info
var colorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
colorInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
@ -807,7 +945,7 @@ namespace DisplayMagicianShared.Windows
if (myHDRstate.AdvancedColorInfo.AdvancedColorSupported && colorInfo.AdvancedColorEnabled != myHDRstate.AdvancedColorInfo.AdvancedColorEnabled)
{
SharedLogger.logger.Trace($"HDR is available for use on Display {myHDRstate.Id}, and we want it set to {myHDRstate.AdvancedColorInfo.AdvancedColorEnabled} but is currently {colorInfo.AdvancedColorEnabled}.");
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: HDR is available for use on Display {myHDRstate.Id}, and we want it set to {myHDRstate.AdvancedColorInfo.AdvancedColorEnabled} but is currently {colorInfo.AdvancedColorEnabled}.");
var setColorState = new DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE();
setColorState.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
@ -836,6 +974,61 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to find out if HDR is supported for display #{myHDRstate.Id}");
}
}
// Attempt to set the display adapters in this machine through GDI
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Attempting to change Display Device settings through GDI API");
foreach (var myGdiDisplaySettings in displayConfig.GdiDisplaySettings)
{
string displayDeviceName = myGdiDisplaySettings.Key;
GDI_DISPLAY_SETTING displayDeviceSettings = myGdiDisplaySettings.Value;
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Trying to change Device Mode for Display {displayDeviceName}.");
DEVICE_MODE modeToUse = displayDeviceSettings.DeviceMode;
CHANGE_DISPLAY_RESULTS result = GDIImport.ChangeDisplaySettingsEx(displayDeviceName, ref modeToUse, IntPtr.Zero, CHANGE_DISPLAY_SETTINGS_FLAGS.CDS_UPDATEREGISTRY, IntPtr.Zero);
if (result == CHANGE_DISPLAY_RESULTS.Successful)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Successfully changed display {displayDeviceName} to use the new mode!");
}
else if (result == CHANGE_DISPLAY_RESULTS.BadDualView)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: The settings change was unsuccessful because the system is DualView capable. Display {displayDeviceName} not updated to new mode.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.BadFlags)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: An invalid set of flags was passed in. Display {displayDeviceName} not updated to use the new mode.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.BadMode)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: The graphics mode is not supported. Display {displayDeviceName} not updated to use the new mode.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.BadParam)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: An invalid parameter was passed in. This can include an invalid flag or combination of flags. Display {displayDeviceName} not updated to use the new mode.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.Failed)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: The display driver failed to apply the specified graphics mode. Display {displayDeviceName} not updated to use the new mode.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.NotUpdated)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: Unable to write new settings to the registry. Display {displayDeviceName} not updated to use the new mode.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.Restart)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: The computer must be restarted for the graphics mode to work. Display {displayDeviceName} not updated to use the new mode.");
return false;
}
else
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Display {displayDeviceName} not updated to use the new mode.");
}
}
return true;
}