Files
ColorControl/ColorControl/CCD.cs
T

565 lines
19 KiB
C#

using NWin32;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace ColorControl
{
// This class takes care of wrapping "Connecting and Configuring Displays(CCD) Win32 API"
internal static class CCD
{
public enum DisplayTopology
{
Internal,
External,
Extend,
Clone
}
public static DisplayTopology GetDisplayTopology()
{
uint numPathArrayElements;
uint numModeInfoArrayElements;
NativeMethods.GetDisplayConfigBufferSizes(QueryDisplayFlags.DatabaseCurrent, out numPathArrayElements, out numModeInfoArrayElements);
var pathArray = new DisplayConfigPathInfo[numPathArrayElements];
var modeArray = new DisplayConfigModeInfo[numModeInfoArrayElements];
DisplayConfigTopologyId displayTopology;
NativeMethods.QueryDisplayConfig(QueryDisplayFlags.DatabaseCurrent, ref numPathArrayElements, pathArray, ref numModeInfoArrayElements, modeArray, out displayTopology);
switch (displayTopology)
{
case DisplayConfigTopologyId.External: return DisplayTopology.External;
case DisplayConfigTopologyId.Internal: return DisplayTopology.Internal;
case DisplayConfigTopologyId.Extend: return DisplayTopology.Extend;
}
return DisplayTopology.Clone;
}
public static void SetDisplayTopology(DisplayTopology displayTopology)
{
switch (displayTopology)
{
case DisplayTopology.External:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyExternal));
break;
case DisplayTopology.Internal:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyInternal));
break;
case DisplayTopology.Extend:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyExtend));
break;
case DisplayTopology.Clone:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyClone));
break;
}
}
public static void SetHDRState(bool enabled, string displayName = null)
{
uint pathCount, modeCount;
var err = NativeMethods.GetDisplayConfigBufferSizes(QueryDisplayFlags.OnlyActivePaths, out pathCount, out modeCount);
if (err == NativeConstants.ERROR_SUCCESS)
{
var pathsArray = new DisplayConfigPathInfo[pathCount];
var modesArray = new DisplayConfigModeInfo[modeCount];
DisplayConfigTopologyId displayTopology;
err = NativeMethods.QueryDisplayConfig(QueryDisplayFlags.DatabaseCurrent, ref pathCount, pathsArray, ref modeCount, modesArray, out displayTopology);
if (err == NativeConstants.ERROR_SUCCESS)
{
foreach (var path in pathsArray)
{
// get display name
var info = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
info.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
info.header.size = Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DEVICE_NAME>();
info.header.adapterId = path.sourceInfo.adapterId;
info.header.id = path.sourceInfo.id;
err = NativeMethods.DisplayConfigGetDeviceInfo(ref info);
if (err != NativeConstants.ERROR_SUCCESS)
{
break;
}
var deviceName = info.viewGdiDeviceName;
if (displayName != null && !EqualDisplayNames(deviceName, displayName))
{
continue;
}
var setpacket = new DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO();
setpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
setpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
setpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO>(); ;
var requestpacket = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
requestpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>(); ;
for (int i = 0; i < modeCount; i++)
{
if (modesArray[i].infoType == DisplayConfigModeInfoType.Target)
{
setpacket.header.adapterId = modesArray[i].adapterId;
setpacket.header.id = modesArray[i].id;
requestpacket.header.adapterId = modesArray[i].adapterId;
requestpacket.header.id = modesArray[i].id;
if (NativeMethods.DisplayConfigGetDeviceInfo(ref requestpacket) == NativeConstants.ERROR_SUCCESS)
{
if (requestpacket.advancedColorSupported)
{
setpacket.enableAdvancedColor = enabled ? 1U : 0;
NativeMethods.DisplayConfigSetDeviceInfo(ref setpacket);
}
}
}
}
}
}
}
if (err != NativeConstants.ERROR_SUCCESS)
{
throw new Win32Exception(err);
}
}
private static bool EqualDisplayNames(string displayName1, string displayName2)
{
displayName1 = displayName1.Replace("\\", string.Empty);
displayName2 = displayName2.Replace("\\", string.Empty);
return displayName1.Equals(displayName2, StringComparison.OrdinalIgnoreCase);
}
private enum DISPLAYCONFIG_MODE_INFO_TYPE
{
DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1,
DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2,
DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE = 3,
}
private enum DISPLAYCONFIG_DEVICE_INFO_TYPE
{
DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3,
DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4,
DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6,
DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7,
DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8,
DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9,
DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10,
DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11,
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_DEVICE_INFO_HEADER
{
public DISPLAYCONFIG_DEVICE_INFO_TYPE type;
public int size;
public LUID adapterId;
public uint id;
}
[StructLayout(LayoutKind.Sequential)]
struct LUID
{
uint LowPart;
uint HighPart;
}
[Flags]
enum DisplayConfigVideoOutputTechnology : uint
{
Other = 4294967295, // -1
Hd15 = 0,
Svideo = 1,
CompositeVideo = 2,
ComponentVideo = 3,
Dvi = 4,
Hdmi = 5,
Lvds = 6,
DJpn = 8,
Sdi = 9,
DisplayportExternal = 10,
DisplayportEmbedded = 11,
UdiExternal = 12,
UdiEmbedded = 13,
Sdtvdongle = 14,
Internal = 0x80000000,
ForceUint32 = 0xFFFFFFFF
}
#region SdcFlags enum
[Flags]
enum SdcFlags : uint
{
Zero = 0,
TopologyInternal = 0x00000001,
TopologyClone = 0x00000002,
TopologyExtend = 0x00000004,
TopologyExternal = 0x00000008,
TopologySupplied = 0x00000010,
UseSuppliedDisplayConfig = 0x00000020,
Validate = 0x00000040,
Apply = 0x00000080,
NoOptimization = 0x00000100,
SaveToDatabase = 0x00000200,
AllowChanges = 0x00000400,
PathPersistIfRequired = 0x00000800,
ForceModeEnumeration = 0x00001000,
AllowPathOrderChanges = 0x00002000,
UseDatabaseCurrent = TopologyInternal | TopologyClone | TopologyExtend | TopologyExternal
}
[Flags]
enum DisplayConfigFlags : uint
{
Zero = 0x0,
PathActive = 0x00000001
}
[Flags]
enum DisplayConfigSourceStatus
{
Zero = 0x0,
InUse = 0x00000001
}
[Flags]
enum DisplayConfigTargetStatus : uint
{
Zero = 0x0,
InUse = 0x00000001,
FORCIBLE = 0x00000002,
ForcedAvailabilityBoot = 0x00000004,
ForcedAvailabilityPath = 0x00000008,
ForcedAvailabilitySystem = 0x00000010,
}
[Flags]
enum DisplayConfigRotation : uint
{
Zero = 0x0,
Identity = 1,
Rotate90 = 2,
Rotate180 = 3,
Rotate270 = 4,
ForceUint32 = 0xFFFFFFFF
}
[Flags]
enum DisplayConfigPixelFormat : uint
{
Zero = 0x0,
Pixelformat8Bpp = 1,
Pixelformat16Bpp = 2,
Pixelformat24Bpp = 3,
Pixelformat32Bpp = 4,
PixelformatNongdi = 5,
PixelformatForceUint32 = 0xffffffff
}
[Flags]
enum DisplayConfigScaling : uint
{
Zero = 0x0,
Identity = 1,
Centered = 2,
Stretched = 3,
Aspectratiocenteredmax = 4,
Custom = 5,
Preferred = 128,
ForceUint32 = 0xFFFFFFFF
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigRational
{
uint numerator;
uint denominator;
}
[Flags]
enum DisplayConfigScanLineOrdering : uint
{
Unspecified = 0,
Progressive = 1,
Interlaced = 2,
InterlacedUpperfieldfirst = Interlaced,
InterlacedLowerfieldfirst = 3,
ForceUint32 = 0xFFFFFFFF
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigPathInfo
{
public DisplayConfigPathSourceInfo sourceInfo;
public DisplayConfigPathTargetInfo targetInfo;
public uint flags;
}
[Flags]
enum DisplayConfigModeInfoType : uint
{
Zero = 0,
Source = 1,
Target = 2,
ForceUint32 = 0xFFFFFFFF
}
[StructLayout(LayoutKind.Explicit)]
struct DisplayConfigModeInfo
{
[FieldOffset(0)]
public DisplayConfigModeInfoType infoType;
[FieldOffset(4)]
public uint id;
[FieldOffset(8)]
public LUID adapterId;
[FieldOffset(16)]
public DisplayConfigTargetMode targetMode;
[FieldOffset(16)]
public DisplayConfigSourceMode sourceMode;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfig2DRegion
{
uint cx;
uint cy;
}
[Flags]
enum D3DmdtVideoSignalStandard : uint
{
Uninitialized = 0,
VesaDmt = 1,
VesaGtf = 2,
VesaCvt = 3,
Ibm = 4,
Apple = 5,
NtscM = 6,
NtscJ = 7,
Ntsc443 = 8,
PalB = 9,
PalB1 = 10,
PalG = 11,
PalH = 12,
PalI = 13,
PalD = 14,
PalN = 15,
PalNc = 16,
SecamB = 17,
SecamD = 18,
SecamG = 19,
SecamH = 20,
SecamK = 21,
SecamK1 = 22,
SecamL = 23,
SecamL1 = 24,
Eia861 = 25,
Eia861A = 26,
Eia861B = 27,
PalK = 28,
PalK1 = 29,
PalL = 30,
PalM = 31,
Other = 255
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigVideoSignalInfo
{
long pixelRate;
DisplayConfigRational hSyncFreq;
DisplayConfigRational vSyncFreq;
DisplayConfig2DRegion activeSize;
DisplayConfig2DRegion totalSize;
D3DmdtVideoSignalStandard videoStandard;
DisplayConfigScanLineOrdering ScanLineOrdering;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigTargetMode
{
DisplayConfigVideoSignalInfo targetVideoSignalInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct PointL
{
int x;
int y;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigSourceMode
{
uint width;
uint height;
DisplayConfigPixelFormat pixelFormat;
PointL position;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigPathSourceInfo
{
public LUID adapterId;
public uint id;
public uint modeInfoIdx;
public DisplayConfigSourceStatus statusFlags;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigPathTargetInfo
{
public LUID adapterId;
public uint id;
public uint modeInfoIdx;
public DisplayConfigVideoOutputTechnology outputTechnology;
public DisplayConfigRotation rotation;
public DisplayConfigScaling scaling;
public DisplayConfigRational refreshRate;
public DisplayConfigScanLineOrdering scanLineOrdering;
public bool targetAvailable;
public DisplayConfigTargetStatus statusFlags;
}
[Flags]
enum QueryDisplayFlags : uint
{
Zero = 0x0,
AllPaths = 0x00000001,
OnlyActivePaths = 0x00000002,
DatabaseCurrent = 0x00000004
}
[Flags]
enum DisplayConfigTopologyId : uint
{
Zero = 0x0,
Internal = 0x00000001,
Clone = 0x00000002,
Extend = 0x00000004,
External = 0x00000008,
ForceUint32 = 0xFFFFFFFF
}
private enum DISPLAYCONFIG_COLOR_ENCODING
{
DISPLAYCONFIG_COLOR_ENCODING_RGB = 0,
DISPLAYCONFIG_COLOR_ENCODING_YCBCR444 = 1,
DISPLAYCONFIG_COLOR_ENCODING_YCBCR422 = 2,
DISPLAYCONFIG_COLOR_ENCODING_YCBCR420 = 3,
DISPLAYCONFIG_COLOR_ENCODING_INTENSITY = 4,
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
public uint value;
public DISPLAYCONFIG_COLOR_ENCODING colorEncoding;
public int bitsPerColorChannel;
public bool advancedColorSupported => (value & 0x1) == 0x1;
public bool advancedColorEnabled => (value & 0x2) == 0x2;
public bool wideColorEnforced => (value & 0x4) == 0x4;
public bool advancedColorForceDisabled => (value & 0x8) == 0x8;
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
public uint enableAdvancedColor;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS
{
public uint value;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DISPLAYCONFIG_TARGET_DEVICE_NAME
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
public DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags;
public DisplayConfigVideoOutputTechnology outputTechnology;
public ushort edidManufactureId;
public ushort edidProductCodeId;
public uint connectorInstance;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string monitorFriendlyDeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string monitorDevicePat;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DISPLAYCONFIG_SOURCE_DEVICE_NAME
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string viewGdiDeviceName;
}
#endregion
static class NativeMethods
{
[DllImport("User32.dll")]
public static extern int GetDisplayConfigBufferSizes(QueryDisplayFlags flags, out uint numPathArrayElements, out uint numModeInfoArrayElements);
[DllImport("User32.dll")]
public static extern int SetDisplayConfig(uint numPathArrayElements, [In] DisplayConfigPathInfo[] pathArray, uint numModeInfoArrayElements, [In] DisplayConfigModeInfo[] modeInfoArray, SdcFlags flags);
[DllImport("User32.dll")]
public static extern int QueryDisplayConfig(QueryDisplayFlags flags, ref uint numPathArrayElements, [In, Out] DisplayConfigPathInfo[] pathInfoArray, ref uint modeInfoArrayElements, [In, Out] DisplayConfigModeInfo[] modeInfoArray, out DisplayConfigTopologyId id);
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO requestPacket);
[DllImport("user32")]
public static extern int DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO setPacket);
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_TARGET_DEVICE_NAME requestPacket);
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SOURCE_DEVICE_NAME requestPacket);
}
}
}