From 4a8acd4b863f298437a5c6b9b8b493ba1d58ac21 Mon Sep 17 00:00:00 2001 From: Terry MacDonald Date: Fri, 15 Oct 2021 10:01:21 +1300 Subject: [PATCH] Updated WinLibrary with latest GDI API changes This will hopefully fix the issues #45 and #46. --- .../DisplayMagicianShared.csproj | 1 + DisplayMagicianShared/Windows/CCD.cs | 2 +- DisplayMagicianShared/Windows/GDI.cs | 675 ++++++++++++++++++ DisplayMagicianShared/Windows/WinLibrary.cs | 199 +++++- 4 files changed, 873 insertions(+), 4 deletions(-) create mode 100644 DisplayMagicianShared/Windows/GDI.cs diff --git a/DisplayMagicianShared/DisplayMagicianShared.csproj b/DisplayMagicianShared/DisplayMagicianShared.csproj index e9902b1..bd68597 100644 --- a/DisplayMagicianShared/DisplayMagicianShared.csproj +++ b/DisplayMagicianShared/DisplayMagicianShared.csproj @@ -85,6 +85,7 @@ DisplayView.cs + diff --git a/DisplayMagicianShared/Windows/CCD.cs b/DisplayMagicianShared/Windows/CCD.cs index 58b301e..a311708 100644 --- a/DisplayMagicianShared/Windows/CCD.cs +++ b/DisplayMagicianShared/Windows/CCD.cs @@ -393,7 +393,7 @@ namespace DisplayMagicianShared.Windows public override bool Equals(object obj) => obj is DISPLAYCONFIG_RATIONAL other && this.Equals(other); public bool Equals(DISPLAYCONFIG_RATIONAL other) => Numerator == other.Numerator && - Denominator == other.Denominator; + Denominator == other.Denominator; public override int GetHashCode() { diff --git a/DisplayMagicianShared/Windows/GDI.cs b/DisplayMagicianShared/Windows/GDI.cs new file mode 100644 index 0000000..9007019 --- /dev/null +++ b/DisplayMagicianShared/Windows/GDI.cs @@ -0,0 +1,675 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace DisplayMagicianShared.Windows +{ + // 90% of this file is cribbed from WindowsDisplayAPI by Soroush Falahati + // The other 10% is from MikedouglasDev's ChangeScreenResolution + // https://github.com/mikedouglasdev/changescreenresolution/blob/master/ChangeScreenResolutionSolution/ChangeScreenResolution/SafeNativeMethods.cs + // and GemingLeader here: https://www.c-sharpcorner.com/uploadfile/GemingLeader/changing-display-settings-programmatically/ + + + public enum CHANGE_DISPLAY_RESULTS + { + /// + /// Completed successfully + /// + Successful = 0, + + /// + /// Changes needs restart + /// + Restart = 1, + + /// + /// Failed to change and save setings + /// + Failed = -1, + + /// + /// Invalid data provided + /// + BadMode = -2, + + /// + /// Changes not updated + /// + NotUpdated = -3, + + /// + /// Invalid flags provided + /// + BadFlags = -4, + + /// + /// Bad parameters provided + /// + BadParam = -5, + + /// + /// Bad Dual View mode used with mode + /// + BadDualView = -6 + } + + [Flags] + public enum CHANGE_DISPLAY_SETTINGS_FLAGS : UInt32 + { + CDS_NONE = 0, + CDS_UPDATEREGISTRY = 0x00000001, + CDS_TEST = 0x00000002, + CDS_FULLSCREEN = 0x00000004, + CDS_GLOBAL = 0x00000008, + CDS_SET_PRIMARY = 0x00000010, + CDS_VIDEOPARAMETERS = 0x00000020, + CDS_ENABLE_UNSAFE_MODES = 0x00000100, + CDS_DISABLE_UNSAFE_MODES = 0x00000200, + CDS_RESET = 0x40000000, + CDS_RESET_EX = 0x20000000, + CDS_NORESET = 0x10000000 + } + + public enum DEVICE_CAPABILITY : Int32 + { + DriverVersion = 0, + Technology = 2, + HorizontalSizeInMM = 4, + VerticalSizeInMM = 6, + HorizontalResolution = 8, + VerticalResolution = 10, + BitsPerPixel = 12, + Planes = 14, + NumberOfBrushes = 16, + NumberOfPens = 18, + NumberOfMarkers = 20, + NumberOfFonts = 22, + NumberOfColors = 24, + DeviceDescriptorSize = 26, + CurveCapabilities = 28, + LineCapabilities = 30, + PolygonalCapabilities = 32, + TextCapabilities = 34, + ClipCapabilities = 36, + RasterCapabilities = 38, + HorizontalAspect = 40, + VerticalAspect = 42, + HypotenuseAspect = 44, + //ShadeBlendingCapabilities = 45, + HorizontalLogicalPixels = 88, + VerticalLogicalPixels = 90, + PaletteSize = 104, + ReservedPaletteSize = 106, + ColorResolution = 108, + + // Printer Only + PhysicalWidth = 110, + PhysicalHeight = 111, + PhysicalHorizontalMargin = 112, + PhysicalVerticalMargin = 113, + HorizontalScalingFactor = 114, + VerticalScalingFactor = 115, + + // Display Only + VerticalRefreshRateInHz = 116, + DesktopVerticalResolution = 117, + DesktopHorizontalResolution = 118, + PreferredBLTAlignment = 119, + ShadeBlendingCapabilities = 120, + ColorManagementCapabilities = 121, + } + + [Flags] + public enum DEVICE_MODE_FIELDS : UInt32 + { + None = 0, + Position = 0x20, + DisplayOrientation = 0x80, + Color = 0x800, + Duplex = 0x1000, + YResolution = 0x2000, + TtOption = 0x4000, + Collate = 0x8000, + FormName = 0x10000, + LogPixels = 0x20000, + BitsPerPixel = 0x40000, + PelsWidth = 0x80000, + PelsHeight = 0x100000, + DisplayFlags = 0x200000, + DisplayFrequency = 0x400000, + DisplayFixedOutput = 0x20000000, + AllDisplay = Position | + DisplayOrientation | + YResolution | + BitsPerPixel | + PelsWidth | + PelsHeight | + DisplayFlags | + DisplayFrequency | + DisplayFixedOutput, + } + + [Flags] + public enum DISPLAY_DEVICE_STATE_FLAGS : UInt32 + { + /// + /// The device is part of the desktop. + /// + AttachedToDesktop = 0x1, + MultiDriver = 0x2, + + /// + /// The device is part of the desktop. + /// + PrimaryDevice = 0x4, + + /// + /// Represents a pseudo device used to mirror application drawing for remoting or other purposes. + /// + MirroringDriver = 0x8, + + /// + /// The device is VGA compatible. + /// + VGACompatible = 0x10, + + /// + /// The device is removable; it cannot be the primary display. + /// + Removable = 0x20, + + /// + /// The device has more display modes than its output devices support. + /// + ModesPruned = 0x8000000, + Remote = 0x4000000, + Disconnect = 0x2000000 + } + + public enum DISPLAY_FIXED_OUTPUT : UInt32 + { + /// + /// Default behavior + /// + Default = 0, + + /// + /// Stretches the output to fit to the display + /// + Stretch = 1, + + /// + /// Centers the output in the middle of the display + /// + Center = 2 + } + + [Flags] + public enum DISPLAY_FLAGS : UInt32 + { + None = 0, + Grayscale = 1, + Interlaced = 2 + } + + public enum DISPLAY_ORIENTATION : UInt32 + { + /// + /// No rotation + /// + Rotate0Degree = 0, + + /// + /// 90 degree rotation + /// + Rotate90Degree = 1, + + /// + /// 180 degree rotation + /// + Rotate180Degree = 2, + + /// + /// 270 degree rotation + /// + Rotate270Degree = 3 + } + + public enum DISPLAY_SETTINGS_MODE : Int32 + { + CurrentSettings = -1, // Retrieves current display mode + RegistrySettings = -2 // Retrieves current display mode stored within the registry. + } + + public enum DISPLAY_TECHNOLOGY : Int32 + { + Plotter = 0, + RasterDisplay = 1, + RasterPrinter = 2, + RasterCamera = 3, + CharacterStream = 4, + MetaFile = 5, + DisplayFile = 6, + } + + public enum MONITOR_FROM_FLAG : UInt32 + { + DefaultToNull = 0, + DefaultToPrimary = 1, + DefaultToNearest = 2, + } + + [Flags] + public enum MONITOR_INFO_FLAGS : UInt32 + { + None = 0, + Primary = 1 + } + + + + // https://msdn.microsoft.com/en-us/library/windows/desktop/dd183565(v=vs.85).aspx + // https://www.c-sharpcorner.com/uploadfile/GemingLeader/changing-display-settings-programmatically/ + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)] + public struct DEVICE_MODE : IEquatable + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + [FieldOffset(0)] + public string DeviceName; + + [MarshalAs(UnmanagedType.U2)] + [FieldOffset(32)] + public UInt16 SpecificationVersion; + + [MarshalAs(UnmanagedType.U2)] + [FieldOffset(34)] + public UInt16 DriverVersion; + + [MarshalAs(UnmanagedType.U2)] + [FieldOffset(36)] + public UInt16 Size; + + [MarshalAs(UnmanagedType.U2)] + [FieldOffset(38)] + public UInt16 DriverExtra; + + [MarshalAs(UnmanagedType.U4)] + [FieldOffset(40)] + public DEVICE_MODE_FIELDS Fields; + + [MarshalAs(UnmanagedType.Struct)] + [FieldOffset(44)] + public POINTL Position; + + [MarshalAs(UnmanagedType.U4)] + [FieldOffset(52)] + public DISPLAY_ORIENTATION DisplayOrientation; + + [MarshalAs(UnmanagedType.U4)] + [FieldOffset(56)] + public DISPLAY_FIXED_OUTPUT DisplayFixedOutput; + + [MarshalAs(UnmanagedType.I2)] + [FieldOffset(60)] + public Int16 Color; + + [MarshalAs(UnmanagedType.I2)] + [FieldOffset(62)] + public Int16 Duplex; + + [MarshalAs(UnmanagedType.I2)] + [FieldOffset(64)] + public Int16 YResolution; + + [MarshalAs(UnmanagedType.I2)] + [FieldOffset(66)] + public Int16 TrueTypeOption; + + [MarshalAs(UnmanagedType.I2)] + [FieldOffset(68)] + public Int16 Collate; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + [FieldOffset(72)] + public string FormName; + + [MarshalAs(UnmanagedType.U2)] + [FieldOffset(102)] + public UInt16 LogicalInchPixels; + + [MarshalAs(UnmanagedType.U4)] + [FieldOffset(104)] + public UInt32 BitsPerPixel; + + [MarshalAs(UnmanagedType.U4)] + [FieldOffset(108)] + public UInt32 PixelsWidth; + + [MarshalAs(UnmanagedType.U4)] + [FieldOffset(112)] + public UInt32 PixelsHeight; + + [MarshalAs(UnmanagedType.U4)] + [FieldOffset(116)] + public DISPLAY_FLAGS DisplayFlags; + + [MarshalAs(UnmanagedType.U4)] + [FieldOffset(120)] + public UInt32 DisplayFrequency; + + public override bool Equals(object obj) => obj is DEVICE_MODE other && this.Equals(other); + + public bool Equals(DEVICE_MODE other) + => DeviceName.Equals(other.DeviceName) && + SpecificationVersion == other.SpecificationVersion && + DriverVersion.Equals(other.DriverVersion) && + Size.Equals(other.Size) && + DriverExtra.Equals(other.DriverExtra) && + Fields.Equals(other.Fields) && + Position.Equals(other.Position) && + DisplayOrientation.Equals(other.DisplayOrientation) && + DisplayFixedOutput.Equals(other.DisplayFixedOutput) && + Color.Equals(other.Color) && + Duplex.Equals(other.Duplex) && + YResolution.Equals(other.YResolution) && + TrueTypeOption.Equals(other.TrueTypeOption) && + Collate.Equals(other.Collate) && + FormName.Equals(other.FormName) && + LogicalInchPixels.Equals(other.LogicalInchPixels) && + BitsPerPixel.Equals(other.BitsPerPixel) && + PixelsWidth.Equals(other.PixelsWidth) && + PixelsHeight.Equals(other.PixelsHeight) && + DisplayFlags.Equals(other.DisplayFlags) && + DisplayFrequency == other.DisplayFrequency; + + public override int GetHashCode() + { + return (DeviceName, SpecificationVersion, DriverVersion, Size, DriverExtra, Fields, Position, DisplayOrientation, DisplayFixedOutput, Color, Duplex, + YResolution, TrueTypeOption, Collate, FormName, LogicalInchPixels, BitsPerPixel, PixelsWidth, PixelsHeight, DisplayFlags, DisplayFrequency).GetHashCode(); + } + + public static bool operator ==(DEVICE_MODE lhs, DEVICE_MODE rhs) => lhs.Equals(rhs); + + public static bool operator !=(DEVICE_MODE lhs, DEVICE_MODE rhs) => !(lhs == rhs); + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct DISPLAY_DEVICE : IEquatable + { + [MarshalAs(UnmanagedType.U4)] + public UInt32 Size; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string DeviceName; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string DeviceString; + + [MarshalAs(UnmanagedType.U4)] + public DISPLAY_DEVICE_STATE_FLAGS StateFlags; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string DeviceId; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string DeviceKey; + + public override bool Equals(object obj) => obj is DISPLAY_DEVICE other && this.Equals(other); + + public bool Equals(DISPLAY_DEVICE other) + => Size == other.Size && + DeviceName == other.DeviceName && + DeviceString == other.DeviceString && + StateFlags == other.StateFlags && + DeviceId == other.DeviceId && + DeviceKey == other.DeviceKey; + + public override int GetHashCode() + { + return (Size, DeviceName, DeviceString, StateFlags, DeviceId, DeviceKey).GetHashCode(); + } + + public static bool operator ==(DISPLAY_DEVICE lhs, DISPLAY_DEVICE rhs) => lhs.Equals(rhs); + + public static bool operator !=(DISPLAY_DEVICE lhs, DISPLAY_DEVICE rhs) => !(lhs == rhs); + + } + + [StructLayout(LayoutKind.Sequential)] + public struct GAMMA_RAMP : IEquatable + { + public const int DataPoints = 256; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = DataPoints)] + public UInt16[] Red; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = DataPoints)] + public UInt16[] Green; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = DataPoints)] + public UInt16[] Blue; + + public override bool Equals(object obj) => obj is GAMMA_RAMP other && this.Equals(other); + + public bool Equals(GAMMA_RAMP other) + => Red.SequenceEqual(other.Red) && + Green.SequenceEqual(other.Green) && + Blue.SequenceEqual(other.Blue); + + public override int GetHashCode() + { + return (Red, Green, Blue).GetHashCode(); + } + + public static bool operator ==(GAMMA_RAMP lhs, GAMMA_RAMP rhs) => lhs.Equals(rhs); + + public static bool operator !=(GAMMA_RAMP lhs, GAMMA_RAMP rhs) => !(lhs == rhs); + + } + + [StructLayout(LayoutKind.Sequential)] + public struct MONITOR_INFO : IEquatable + { + internal UInt32 Size; + public RECTL Bounds; + public RECTL WorkingArea; + public MONITOR_INFO_FLAGS Flags; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string DisplayName; + + public override bool Equals(object obj) => obj is MONITOR_INFO other && this.Equals(other); + + public bool Equals(MONITOR_INFO other) + => Size == other.Size && + Bounds.Equals(other.Bounds) && + WorkingArea.Equals(other.WorkingArea) && + Flags == other.Flags && + DisplayName == other.DisplayName; + + public override int GetHashCode() + { + return (Size, Bounds, WorkingArea, Flags, DisplayName).GetHashCode(); + } + + public static bool operator ==(MONITOR_INFO lhs, MONITOR_INFO rhs) => lhs.Equals(rhs); + + public static bool operator !=(MONITOR_INFO lhs, MONITOR_INFO rhs) => !(lhs == rhs); + } + + public struct GDI_DISPLAY_SETTING : IEquatable + { + public bool IsEnabled; + public bool IsPrimary; + public DISPLAY_DEVICE Device; + public DEVICE_MODE DeviceMode; + + public override bool Equals(object obj) => obj is GDI_DISPLAY_SETTING other && this.Equals(other); + + public bool Equals(GDI_DISPLAY_SETTING other) + => IsEnabled == other.IsEnabled && + IsPrimary == other.IsPrimary && + Device.Equals(other.Device) && + DeviceMode.Equals(other.DeviceMode); + + public override int GetHashCode() + { + return (IsEnabled, IsPrimary, Device, DeviceMode).GetHashCode(); + } + + public static bool operator ==(GDI_DISPLAY_SETTING lhs, GDI_DISPLAY_SETTING rhs) => lhs.Equals(rhs); + + public static bool operator !=(GDI_DISPLAY_SETTING lhs, GDI_DISPLAY_SETTING rhs) => !(lhs == rhs); + } + + + internal class DCHandle : SafeHandle + { + private readonly bool _created; + + private DCHandle(IntPtr handle, bool created) : base(handle, true) + { + _created = created; + } + + public override bool IsInvalid + { + get => handle == IntPtr.Zero; + } + + public static DCHandle CreateFromDevice(string screenName, string devicePath) + { + return new DCHandle( + GDIImport.CreateDC(screenName, devicePath, null, IntPtr.Zero), + true + ); + } + + public static DCHandle CreateFromScreen(string screenName) + { + return CreateFromDevice(screenName, screenName); + } + + public static DCHandle CreateFromWindow(IntPtr windowHandle) + { + return new DCHandle( + GDIImport.GetDC(windowHandle), + true + ); + } + + public static DCHandle CreateGlobal() + { + return new DCHandle( + GDIImport.CreateDC("DISPLAY", null, null, IntPtr.Zero), + true + ); + } + + protected override bool ReleaseHandle() + { + return _created + ? GDIImport.DeleteDC(this) + : GDIImport.ReleaseDC(IntPtr.Zero, this); + } + } + + class GDIImport + { + [DllImport("user32", CharSet = CharSet.Ansi)] + public static extern CHANGE_DISPLAY_RESULTS ChangeDisplaySettingsEx( + string deviceName, + ref DEVICE_MODE devMode, + IntPtr handler, + CHANGE_DISPLAY_SETTINGS_FLAGS flags, + IntPtr param + ); + + [DllImport("user32", CharSet = CharSet.Ansi)] + public static extern CHANGE_DISPLAY_RESULTS ChangeDisplaySettingsEx( + string deviceName, + IntPtr devModePointer, + IntPtr handler, + CHANGE_DISPLAY_SETTINGS_FLAGS flags, + IntPtr param + ); + + [DllImport("user32", CharSet = CharSet.Ansi)] + public static extern bool EnumDisplaySettings( + string deviceName, + DISPLAY_SETTINGS_MODE mode, + ref DEVICE_MODE devMode + ); + + [DllImport("gdi32", CharSet = CharSet.Unicode)] + internal static extern IntPtr CreateDC(string driver, string device, string port, IntPtr deviceMode); + + [DllImport("gdi32")] + internal static extern bool DeleteDC(DCHandle dcHandle); + + + [DllImport("user32", CharSet = CharSet.Unicode)] + internal static extern bool EnumDisplayDevices( + string deviceName, + UInt32 deviceNumber, + ref DISPLAY_DEVICE displayDevice, + UInt32 flags + ); + + [DllImport("user32")] + internal static extern bool EnumDisplayMonitors( + [In] IntPtr dcHandle, + [In] IntPtr clip, + MonitorEnumProcedure callback, + IntPtr callbackObject + ); + + [DllImport("user32")] + internal static extern IntPtr GetDC(IntPtr windowHandle); + + [DllImport("gdi32")] + internal static extern int GetDeviceCaps(DCHandle dcHandle, DEVICE_CAPABILITY index); + + [DllImport("gdi32")] + internal static extern bool GetDeviceGammaRamp(DCHandle dcHandle, ref GAMMA_RAMP ramp); + + [DllImport("user32")] + internal static extern bool GetMonitorInfo( + IntPtr monitorHandle, + ref MONITOR_INFO monitorInfo + ); + + [DllImport("user32")] + internal static extern IntPtr MonitorFromPoint( + [In] POINTL point, + MONITOR_FROM_FLAG flag + ); + + [DllImport("user32")] + internal static extern IntPtr MonitorFromRect( + [In] RECTL rectangle, + MONITOR_FROM_FLAG flag + ); + + [DllImport("user32")] + internal static extern IntPtr MonitorFromWindow( + [In] IntPtr windowHandle, + MONITOR_FROM_FLAG flag + ); + + [DllImport("user32")] + internal static extern bool ReleaseDC([In] IntPtr windowHandle, [In] DCHandle dcHandle); + + [DllImport("gdi32")] + internal static extern bool SetDeviceGammaRamp(DCHandle dcHandle, ref GAMMA_RAMP ramp); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int MonitorEnumProcedure( + IntPtr monitorHandle, + IntPtr dcHandle, + ref RECTL rect, + IntPtr callbackObject + ); + } +} \ No newline at end of file diff --git a/DisplayMagicianShared/Windows/WinLibrary.cs b/DisplayMagicianShared/Windows/WinLibrary.cs index 685e9d7..c97b337 100644 --- a/DisplayMagicianShared/Windows/WinLibrary.cs +++ b/DisplayMagicianShared/Windows/WinLibrary.cs @@ -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 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(); + 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(); + myDefaultConfig.DisplaySources = new Dictionary(); + myDefaultConfig.GdiDisplaySettings = new Dictionary(); + + return myDefaultConfig; + } + private void PatchAdapterIDs(ref WINDOWS_DISPLAY_CONFIG savedDisplayConfig, Dictionary currentAdapterMap) { @@ -389,10 +410,55 @@ namespace DisplayMagicianShared.Windows hdrInfoCount++; } + // Get the list of all display adapters in this machine through GDI + Dictionary gdiDeviceSettings = new Dictionary(); + UInt32 displayDeviceNum = 0; + DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE(); + displayDevice.Size = (UInt32)Marshal.SizeOf(); + 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(); + 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 gdiDeviceSettings = new Dictionary(); + UInt32 displayDeviceNum = 0; + DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE(); + displayDevice.Size = (UInt32)Marshal.SizeOf(); + 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(); + 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; }