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

@ -85,6 +85,7 @@
<DependentUpon>DisplayView.cs</DependentUpon> <DependentUpon>DisplayView.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Windows\CCD.cs" /> <Compile Include="Windows\CCD.cs" />
<Compile Include="Windows\GDI.cs" />
<Compile Include="Windows\WinLibrary.cs" /> <Compile Include="Windows\WinLibrary.cs" />
<Compile Include="Wallpaper.cs" /> <Compile Include="Wallpaper.cs" />
</ItemGroup> </ItemGroup>

View File

@ -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
{
/// <summary>
/// Completed successfully
/// </summary>
Successful = 0,
/// <summary>
/// Changes needs restart
/// </summary>
Restart = 1,
/// <summary>
/// Failed to change and save setings
/// </summary>
Failed = -1,
/// <summary>
/// Invalid data provided
/// </summary>
BadMode = -2,
/// <summary>
/// Changes not updated
/// </summary>
NotUpdated = -3,
/// <summary>
/// Invalid flags provided
/// </summary>
BadFlags = -4,
/// <summary>
/// Bad parameters provided
/// </summary>
BadParam = -5,
/// <summary>
/// Bad Dual View mode used with mode
/// </summary>
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
{
/// <summary>
/// The device is part of the desktop.
/// </summary>
AttachedToDesktop = 0x1,
MultiDriver = 0x2,
/// <summary>
/// The device is part of the desktop.
/// </summary>
PrimaryDevice = 0x4,
/// <summary>
/// Represents a pseudo device used to mirror application drawing for remoting or other purposes.
/// </summary>
MirroringDriver = 0x8,
/// <summary>
/// The device is VGA compatible.
/// </summary>
VGACompatible = 0x10,
/// <summary>
/// The device is removable; it cannot be the primary display.
/// </summary>
Removable = 0x20,
/// <summary>
/// The device has more display modes than its output devices support.
/// </summary>
ModesPruned = 0x8000000,
Remote = 0x4000000,
Disconnect = 0x2000000
}
public enum DISPLAY_FIXED_OUTPUT : UInt32
{
/// <summary>
/// Default behavior
/// </summary>
Default = 0,
/// <summary>
/// Stretches the output to fit to the display
/// </summary>
Stretch = 1,
/// <summary>
/// Centers the output in the middle of the display
/// </summary>
Center = 2
}
[Flags]
public enum DISPLAY_FLAGS : UInt32
{
None = 0,
Grayscale = 1,
Interlaced = 2
}
public enum DISPLAY_ORIENTATION : UInt32
{
/// <summary>
/// No rotation
/// </summary>
Rotate0Degree = 0,
/// <summary>
/// 90 degree rotation
/// </summary>
Rotate90Degree = 1,
/// <summary>
/// 180 degree rotation
/// </summary>
Rotate180Degree = 2,
/// <summary>
/// 270 degree rotation
/// </summary>
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<DEVICE_MODE>
{
[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<DISPLAY_DEVICE>
{
[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<GAMMA_RAMP>
{
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<MONITOR_INFO>
{
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<GDI_DISPLAY_SETTING>
{
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
);
}
}

View File

@ -7,6 +7,7 @@ using Microsoft.Win32.SafeHandles;
using DisplayMagicianShared; using DisplayMagicianShared;
using System.ComponentModel; using System.ComponentModel;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Collections.Generic;
namespace DisplayMagicianShared.Windows namespace DisplayMagicianShared.Windows
{ {
@ -41,6 +42,7 @@ namespace DisplayMagicianShared.Windows
public DISPLAYCONFIG_PATH_INFO[] DisplayConfigPaths; public DISPLAYCONFIG_PATH_INFO[] DisplayConfigPaths;
public DISPLAYCONFIG_MODE_INFO[] DisplayConfigModes; public DISPLAYCONFIG_MODE_INFO[] DisplayConfigModes;
public ADVANCED_HDR_INFO_PER_PATH[] DisplayHDRStates; 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 // 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 // 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. // generating the profile icon.
@ -52,11 +54,12 @@ namespace DisplayMagicianShared.Windows
=> DisplayConfigPaths.SequenceEqual(other.DisplayConfigPaths) && => DisplayConfigPaths.SequenceEqual(other.DisplayConfigPaths) &&
DisplayConfigModes.SequenceEqual(other.DisplayConfigModes) && DisplayConfigModes.SequenceEqual(other.DisplayConfigModes) &&
DisplayHDRStates.SequenceEqual(other.DisplayHDRStates) && DisplayHDRStates.SequenceEqual(other.DisplayHDRStates) &&
GdiDisplaySettings.SequenceEqual(other.GdiDisplaySettings) &&
DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers); DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers);
public override int GetHashCode() 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); public static bool operator ==(WINDOWS_DISPLAY_CONFIG lhs, WINDOWS_DISPLAY_CONFIG rhs) => lhs.Equals(rhs);
@ -128,6 +131,24 @@ namespace DisplayMagicianShared.Windows
return _instance; 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) private void PatchAdapterIDs(ref WINDOWS_DISPLAY_CONFIG savedDisplayConfig, Dictionary<ulong, string> currentAdapterMap)
{ {
@ -389,10 +410,55 @@ namespace DisplayMagicianShared.Windows
hdrInfoCount++; 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 // Store the active paths and modes in our display config object
windowsDisplayConfig.DisplayConfigPaths = paths; windowsDisplayConfig.DisplayConfigPaths = paths;
windowsDisplayConfig.DisplayConfigModes = modes; windowsDisplayConfig.DisplayConfigModes = modes;
windowsDisplayConfig.DisplayHDRStates = hdrInfos; windowsDisplayConfig.DisplayHDRStates = hdrInfos;
windowsDisplayConfig.GdiDisplaySettings = gdiDeviceSettings;
windowsDisplayConfig.DisplayIdentifiers = GetCurrentDisplayIdentifiers(); windowsDisplayConfig.DisplayIdentifiers = GetCurrentDisplayIdentifiers();
return windowsDisplayConfig; 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}"); 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 // get display target preferred mode
var targetPreferredInfo = new DISPLAYCONFIG_TARGET_PREFERRED_MODE(); var targetPreferredInfo = new DISPLAYCONFIG_TARGET_PREFERRED_MODE();
targetPreferredInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_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}"); 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; return stringToReturn;
} }
@ -793,7 +931,7 @@ namespace DisplayMagicianShared.Windows
foreach (ADVANCED_HDR_INFO_PER_PATH myHDRstate in displayConfig.DisplayHDRStates) 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 // Get advanced HDR info
var colorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO(); var colorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
colorInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_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) 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(); var setColorState = new DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE();
setColorState.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_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}"); 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; return true;
} }