From 67c1015730caee2ce1b02bdabf321a013bbc17d4 Mon Sep 17 00:00:00 2001 From: Terry MacDonald Date: Thu, 24 Mar 2022 22:36:42 +1300 Subject: [PATCH] Updated the NVIDIALibrary and WinLibrary to v1.7.0 This is the version used within NVIDIAInfo v1.7.0 --- DisplayMagician/Processes/ProcessUtils.cs | 6 + DisplayMagician/Properties/AssemblyInfo.cs | 4 +- .../DisplayMagicianShared.csproj | 2 +- DisplayMagicianShared/NVIDIA/NVAPI.cs | 1220 ++++++++++++++--- DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs | 1218 ++++++++++------ DisplayMagicianShared/ProfileRepository.cs | 38 +- DisplayMagicianShared/Utils.cs | 431 +++++- DisplayMagicianShared/Windows/CCD.cs | 59 +- .../Windows/TaskBarLayout.cs | 861 ++++++++++++ .../Windows/TaskBarSettings.cs | 137 -- .../Windows/TaskBarStuckRectangle.cs | 616 ++------- DisplayMagicianShared/Windows/WinLibrary.cs | 345 ++++- 12 files changed, 3518 insertions(+), 1419 deletions(-) create mode 100644 DisplayMagicianShared/Windows/TaskBarLayout.cs delete mode 100644 DisplayMagicianShared/Windows/TaskBarSettings.cs diff --git a/DisplayMagician/Processes/ProcessUtils.cs b/DisplayMagician/Processes/ProcessUtils.cs index 0de6ff7..b476e8c 100644 --- a/DisplayMagician/Processes/ProcessUtils.cs +++ b/DisplayMagician/Processes/ProcessUtils.cs @@ -601,6 +601,12 @@ namespace DisplayMagician.Processes { if (ex.ErrorCode == -2147467259) { + if (runAsAdministrator) + { + logger.Error(ex, $"ProcessUtils/TryExecute: Exception while trying to start {executable} for a second time with administrative rights. Giving up."); + return false; + } + logger.Error(ex, $"ProcessUtils/TryExecute: Exception while trying to start {executable}. The process requires elevation. Attempting again with admin rights."); if (TryExecute(executable, arguments, out processCreated, true, priorityClass, maxWaitMs)) { diff --git a/DisplayMagician/Properties/AssemblyInfo.cs b/DisplayMagician/Properties/AssemblyInfo.cs index deae989..00d8dce 100644 --- a/DisplayMagician/Properties/AssemblyInfo.cs +++ b/DisplayMagician/Properties/AssemblyInfo.cs @@ -26,8 +26,8 @@ using System.Resources; [assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")] // Version information -[assembly: AssemblyVersion("2.2.0.250")] -[assembly: AssemblyFileVersion("2.2.0.250")] +[assembly: AssemblyVersion("2.2.0.251")] +[assembly: AssemblyFileVersion("2.2.0.251")] [assembly: NeutralResourcesLanguageAttribute( "en" )] [assembly: CLSCompliant(true)] diff --git a/DisplayMagicianShared/DisplayMagicianShared.csproj b/DisplayMagicianShared/DisplayMagicianShared.csproj index bec17fa..2dd220d 100644 --- a/DisplayMagicianShared/DisplayMagicianShared.csproj +++ b/DisplayMagicianShared/DisplayMagicianShared.csproj @@ -82,7 +82,7 @@ True - + UserControl diff --git a/DisplayMagicianShared/NVIDIA/NVAPI.cs b/DisplayMagicianShared/NVIDIA/NVAPI.cs index 006c733..b6bdb16 100644 --- a/DisplayMagicianShared/NVIDIA/NVAPI.cs +++ b/DisplayMagicianShared/NVIDIA/NVAPI.cs @@ -755,7 +755,7 @@ namespace DisplayMagicianShared.NVIDIA // ================================== [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct DisplayHandle : IEquatable + public struct DisplayHandle : IEquatable, ICloneable { public IntPtr Ptr; @@ -772,10 +772,16 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(DisplayHandle lhs, DisplayHandle rhs) => lhs.Equals(rhs); public static bool operator !=(DisplayHandle lhs, DisplayHandle rhs) => !(lhs == rhs); + + public object Clone() + { + DisplayHandle other = (DisplayHandle)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct UnAttachedDisplayHandle : IEquatable + public struct UnAttachedDisplayHandle : IEquatable, ICloneable { public IntPtr Ptr; @@ -792,10 +798,16 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(UnAttachedDisplayHandle lhs, UnAttachedDisplayHandle rhs) => lhs.Equals(rhs); public static bool operator !=(UnAttachedDisplayHandle lhs, UnAttachedDisplayHandle rhs) => !(lhs == rhs); + + public object Clone() + { + UnAttachedDisplayHandle other = (UnAttachedDisplayHandle)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct PhysicalGpuHandle : IEquatable + public struct PhysicalGpuHandle : IEquatable, ICloneable { public IntPtr Ptr; @@ -811,11 +823,17 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(PhysicalGpuHandle lhs, PhysicalGpuHandle rhs) => lhs.Equals(rhs); public static bool operator !=(PhysicalGpuHandle lhs, PhysicalGpuHandle rhs) => !(lhs == rhs); + + public object Clone() + { + PhysicalGpuHandle other = (PhysicalGpuHandle)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct LogicalGpuHandle : IEquatable + public struct LogicalGpuHandle : IEquatable, ICloneable { public IntPtr Ptr; @@ -831,10 +849,48 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(LogicalGpuHandle lhs, LogicalGpuHandle rhs) => lhs.Equals(rhs); public static bool operator !=(LogicalGpuHandle lhs, LogicalGpuHandle rhs) => !(lhs == rhs); + + public object Clone() + { + LogicalGpuHandle other = (LogicalGpuHandle)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_BOARD_INFO_V1 : IEquatable // Note: Version 1 of NV_BOARD_INFO_V1 structure + public struct NV_LOGICAL_GPU_DATA_V1 : IEquatable, ICloneable // Note: Version 1 of NV_BOARD_INFO_V1 structure + { + public UInt32 Version; //!< structure version + public IntPtr OSAdapterId; + public UInt32 PhysicalGPUCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NVImport.NVAPI_MAX_PHYSICAL_GPUS)] + public PhysicalGpuHandle[] PhysicalGPUHandles; + public UInt32 Reserved; + + public override bool Equals(object obj) => obj is NV_LOGICAL_GPU_DATA_V1 other && this.Equals(other); + + public bool Equals(NV_LOGICAL_GPU_DATA_V1 other) + => Version == other.Version && + PhysicalGPUCount == other.PhysicalGPUCount && + PhysicalGPUHandles.SequenceEqual(other.PhysicalGPUHandles); + + public override Int32 GetHashCode() + { + return (Version, PhysicalGPUCount, PhysicalGPUHandles).GetHashCode(); + } + public static bool operator ==(NV_LOGICAL_GPU_DATA_V1 lhs, NV_LOGICAL_GPU_DATA_V1 rhs) => lhs.Equals(rhs); + + public static bool operator !=(NV_LOGICAL_GPU_DATA_V1 lhs, NV_LOGICAL_GPU_DATA_V1 rhs) => !(lhs == rhs); + public object Clone() + { + NV_LOGICAL_GPU_DATA_V1 other = (NV_LOGICAL_GPU_DATA_V1)MemberwiseClone(); + return other; + } + } + + + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct NV_BOARD_INFO_V1 : IEquatable, ICloneable // Note: Version 1 of NV_BOARD_INFO_V1 structure { public UInt32 Version; //!< structure version [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] @@ -853,10 +909,15 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_BOARD_INFO_V1 lhs, NV_BOARD_INFO_V1 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_BOARD_INFO_V1 lhs, NV_BOARD_INFO_V1 rhs) => !(lhs == rhs); + public object Clone() + { + NV_BOARD_INFO_V1 other = (NV_BOARD_INFO_V1)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_EDID_V3 : IEquatable // Note: Version 3 of NV_EDID_V3 structure + public struct NV_EDID_V3 : IEquatable, ICloneable // Note: Version 3 of NV_EDID_V3 structure { public UInt32 Version; //!< Structure version [MarshalAs(UnmanagedType.ByValArray, SizeConst = (Int32)NVImport.NV_EDID_DATA_SIZE)] @@ -885,11 +946,17 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_EDID_V3 lhs, NV_EDID_V3 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_EDID_V3 lhs, NV_EDID_V3 rhs) => !(lhs == rhs); + + public object Clone() + { + NV_EDID_V3 other = (NV_EDID_V3)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi)] - public struct NV_TIMING_EXTRA : IEquatable + public struct NV_TIMING_EXTRA : IEquatable, ICloneable { public UInt32 Flags; //!< Reserved for NVIDIA hardware-based enhancement, such as double-scan. public ushort RefreshRate; //!< Logical refresh rate to present @@ -921,10 +988,32 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_TIMING_EXTRA lhs, NV_TIMING_EXTRA rhs) => lhs.Equals(rhs); public static bool operator !=(NV_TIMING_EXTRA lhs, NV_TIMING_EXTRA rhs) => !(lhs == rhs); + + public object Clone() + { + NV_TIMING_EXTRA other = (NV_TIMING_EXTRA)MemberwiseClone(); + return other; + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi)] + public struct NV_TIMING_EXTRA_INTERNAL + { + public UInt32 Flags; //!< Reserved for NVIDIA hardware-based enhancement, such as double-scan. + public ushort RefreshRate; //!< Logical refresh rate to present + public UInt32 FrequencyInMillihertz; //!< Physical vertical refresh rate in 0.001Hz + public ushort VerticalAspect; //!< Display aspect ratio Hi(aspect):horizontal-aspect, Low(aspect):vertical-aspect + public ushort HorizontalAspect; //!< Display aspect ratio Hi(aspect):horizontal-aspect, Low(aspect):vertical-aspect + public ushort HorizontalPixelRepetition; //!< Bit-wise pixel repetition factor: 0x1:no pixel repetition; 0x2:each pixel repeats twice horizontally,.. + public UInt32 TimingStandard; //!< Timing standard + //[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)] + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)] + public string Name; //!< Timing name + } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_TIMING : IEquatable + public struct NV_TIMING : IEquatable, ICloneable { // VESA scan out timing parameters: public ushort HVisible; //!< horizontal visible @@ -973,10 +1062,43 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_TIMING lhs, NV_TIMING rhs) => lhs.Equals(rhs); public static bool operator !=(NV_TIMING lhs, NV_TIMING rhs) => !(lhs == rhs); + + public object Clone() + { + NV_TIMING other = (NV_TIMING)MemberwiseClone(); + other.Extra = (NV_TIMING_EXTRA)Extra.Clone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_RECT : IEquatable + public struct NV_TIMING_INTERNAL + { + // VESA scan out timing parameters: + public ushort HVisible; //!< horizontal visible + public ushort HBorder; //!< horizontal border + public ushort HFrontPorch; //!< horizontal front porch + public ushort HSyncWidth; //!< horizontal sync width + public ushort HTotal; //!< horizontal total + public TIMING_HORIZONTAL_SYNC_POLARITY HSyncPol; //!< horizontal sync polarity: 1-negative, 0-positive + + public ushort VVisible; //!< vertical visible + public ushort VBorder; //!< vertical border + public ushort VFrontPorch; //!< vertical front porch + public ushort VSyncWidth; //!< vertical sync width + public ushort VTotal; //!< vertical total + public TIMING_VERTICAL_SYNC_POLARITY VSyncPol; //!< vertical sync polarity: 1-negative, 0-positive + + public TIMING_SCAN_MODE ScanMode; //!< 1-Int32erlaced, 0-progressive + public UInt32 Pclk; //!< pixel clock in 10 kHz + + //other timing related extras - points to a NV_TIMING_EXTRA_INTERNAL + public IntPtr Extra; + + } + + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct NV_RECT : IEquatable, ICloneable { public UInt32 Left; public UInt32 Top; @@ -998,10 +1120,16 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_RECT lhs, NV_RECT rhs) => lhs.Equals(rhs); public static bool operator !=(NV_RECT lhs, NV_RECT rhs) => !(lhs == rhs); + + public object Clone() + { + NV_RECT other = (NV_RECT)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_LUID : IEquatable + public struct NV_LUID : IEquatable, ICloneable { public UInt32 LowPart; public UInt32 HighPart; @@ -1019,11 +1147,17 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_LUID lhs, NV_LUID rhs) => lhs.Equals(rhs); public static bool operator !=(NV_LUID lhs, NV_LUID rhs) => !(lhs == rhs); + + public object Clone() + { + NV_LUID other = (NV_LUID)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_POSITION : IEquatable + public struct NV_POSITION : IEquatable, ICloneable { public Int32 X; public Int32 Y; @@ -1041,11 +1175,23 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_POSITION lhs, NV_POSITION rhs) => lhs.Equals(rhs); public static bool operator !=(NV_POSITION lhs, NV_POSITION rhs) => !(lhs == rhs); + public object Clone() + { + NV_POSITION other = (NV_POSITION)MemberwiseClone(); + return other; + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct NV_POSITION_INTERNAL + { + public Int32 X; + public Int32 Y; } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_RESOLUTION : IEquatable + public struct NV_RESOLUTION : IEquatable, ICloneable { public UInt32 Width; public UInt32 Height; @@ -1065,10 +1211,25 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_RESOLUTION lhs, NV_RESOLUTION rhs) => lhs.Equals(rhs); public static bool operator !=(NV_RESOLUTION lhs, NV_RESOLUTION rhs) => !(lhs == rhs); + + public object Clone() + { + NV_RESOLUTION other = (NV_RESOLUTION)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_VIEWPORTF : IEquatable + public struct NV_RESOLUTION_INTERNAL + { + public UInt32 Width; + public UInt32 Height; + public UInt32 ColorDepth; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct NV_VIEWPORTF : IEquatable, ICloneable { public float X; //!< x-coordinate of the viewport top-left point public float Y; //!< y-coordinate of the viewport top-left point @@ -1104,10 +1265,16 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_VIEWPORTF lhs, NV_VIEWPORTF rhs) => lhs.Equals(rhs); public static bool operator !=(NV_VIEWPORTF lhs, NV_VIEWPORTF rhs) => !(lhs == rhs); + + public object Clone() + { + NV_VIEWPORTF other = (NV_VIEWPORTF)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO : IEquatable // Requires Version 1 + public struct NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 : IEquatable, ICloneable // Requires Version 1 { public UInt32 Version; @@ -1143,9 +1310,9 @@ namespace DisplayMagicianShared.NVIDIA //!< The value NV_TIMING::NV_TIMINGEXT::rrx1k is obtained from the EDID. The driver may //!< tweak this value for HDTV, stereo, etc., before reporting it to the OS. - public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO other && this.Equals(other); + public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 other && this.Equals(other); - public bool Equals(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO other) + public bool Equals(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 other) => Version == other.Version && Rotation == other.Rotation && Scaling == other.Scaling && @@ -1160,16 +1327,63 @@ namespace DisplayMagicianShared.NVIDIA { return (Version, Rotation, Scaling, RefreshRateInMillihertz, Flags, ConnectorType, TvFormat, TimingOverride, Timing).GetHashCode(); } - public static bool operator ==(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO lhs, NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO rhs) => lhs.Equals(rhs); + public static bool operator ==(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 lhs, NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 rhs) => lhs.Equals(rhs); - public static bool operator !=(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO lhs, NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO rhs) => !(lhs == rhs); + public static bool operator !=(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 lhs, NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 rhs) => !(lhs == rhs); + + public object Clone() + { + NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 other = (NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1)MemberwiseClone(); + other.Timing = (NV_TIMING)Timing.Clone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 : IEquatable + public struct NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1_INTERNAL + { + public UInt32 Version; + + // Rotation and Scaling + public NV_ROTATE Rotation; //!< (IN) rotation setting. + public NV_SCALING Scaling; //!< (IN) scaling setting. + + // Refresh Rate + public UInt32 RefreshRateInMillihertz; //!< (IN) Non-Int32erlaced Refresh Rate of the mode, multiplied by 1000, 0 = ignored + //!< This is the value which driver reports to the OS. + // Flags + //public UInt32 Int32erlaced:1; //!< (IN) Interlaced mode flag, ignored if refreshRate == 0 + //public UInt32 primary:1; //!< (IN) Declares primary display in clone configuration. This is *NOT* GDI Primary. + //!< Only one target can be primary per source. If no primary is specified, the first + //!< target will automatically be primary. + //public UInt32 isPanAndScanTarget:1; //!< Whether on this target Pan and Scan is enabled or has to be enabled. Valid only + //!< when the target is part of clone topology. + //public UInt32 disableVirtualModeSupport:1; + //public UInt32 isPreferredUnscaledTarget:1; + //public UInt32 reserved:27; + public UInt32 Flags; + // TV format information + public NV_GPU_CONNECTOR_TYPE ConnectorType; //!< Specify connector type. For TV only, ignored if tvFormat == NV_DISPLAY_TV_FORMAT_NONE + public NV_DISPLAY_TV_FORMAT TvFormat; //!< (IN) to choose the last TV format set this value to NV_DISPLAY_TV_FORMAT_NONE + //!< In case of NvAPI_DISP_GetDisplayConfig(), this field will indicate the currently applied TV format; + //!< if no TV format is applied, this field will have NV_DISPLAY_TV_FORMAT_NONE value. + //!< In case of NvAPI_DISP_SetDisplayConfig(), this field should only be set in case of TVs; + //!< for other displays this field will be ignored and resolution & refresh rate specified in input will be used to apply the TV format. + + // Backend (raster) timing standard + public NV_TIMING_OVERRIDE TimingOverride; //!< Ignored if timingOverride == NV_TIMING_OVERRIDE_CURRENT + public IntPtr Timing; // Points to a NV_TIMING_INTERNAL object + //!< Scan out timing, valid only if timingOverride == NV_TIMING_OVERRIDE_CUST + //!< The value NV_TIMING::NV_TIMINGEXT::rrx1k is obtained from the EDID. The driver may + //!< tweak this value for HDTV, stereo, etc., before reporting it to the OS. + + } + + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 : IEquatable, ICloneable { public UInt32 DisplayId; //!< Display ID - public NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO Details; //!< May be NULL if no advanced settings are required + public NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 Details; //!< NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO - May be NULL if no advanced settings are required public UInt32 WindowsCCDTargetId; //!< Windows CCD target ID. Must be present only for non-NVIDIA adapter, for NVIDIA adapter this parameter is ignored. public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 other && this.Equals(other); @@ -1186,13 +1400,21 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 lhs, NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 lhs, NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 rhs) => !(lhs == rhs); + + public object Clone() + { + NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 otherTargetInfo = (NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2)MemberwiseClone(); + otherTargetInfo.Details = (NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1)Details.Clone(); + return otherTargetInfo; + } } + [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 : IEquatable + public struct NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 : IEquatable, ICloneable { public UInt32 DisplayId; //!< Display ID - public NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO Details; //!< May be NULL if no advanced settings are required + public NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 Details; //!< May be NULL if no advanced settings are required public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 other && this.Equals(other); @@ -1207,18 +1429,25 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 lhs, NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 lhs, NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 rhs) => !(lhs == rhs); + + public object Clone() + { + NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 other = (NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1)MemberwiseClone(); + other.Details = (NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1)Details.Clone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_DISPLAYCONFIG_PATH_INFO_V2 : IEquatable // Version is 2 + public struct NV_DISPLAYCONFIG_PATH_INFO_V2 : IEquatable, ICloneable // Version is 2 { public UInt32 Version; public UInt32 SourceId; //!< Identifies sourceId used by Windows CCD. This can be optionally set. public UInt32 TargetInfoCount; //!< Number of elements in targetInfo array //[MarshalAs(UnmanagedType.ByValArray)] - public IntPtr TargetInfo; - public IntPtr SourceModeInfo; //!< May be NULL if mode info is not important + public NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2[] TargetInfo; + public NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 SourceModeInfo; //!< May be NULL if mode info is not important //public IntPtr SourceModeInfo; //!< May be NULL if mode info is not important //public UInt32 IsNonNVIDIAAdapter : 1; //!< True for non-NVIDIA adapter. //public UInt32 reserved : 31; //!< Must be 0 @@ -1235,7 +1464,7 @@ namespace DisplayMagicianShared.NVIDIA => Version == other.Version && SourceId == other.SourceId && TargetInfoCount == other.TargetInfoCount && - TargetInfo.Equals(other.TargetInfo) && + TargetInfo.SequenceEqual(other.TargetInfo) && SourceModeInfo.Equals(other.SourceModeInfo) && Flags == other.Flags; @@ -1246,10 +1475,50 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_DISPLAYCONFIG_PATH_INFO_V2 lhs, NV_DISPLAYCONFIG_PATH_INFO_V2 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_DISPLAYCONFIG_PATH_INFO_V2 lhs, NV_DISPLAYCONFIG_PATH_INFO_V2 rhs) => !(lhs == rhs); + public object Clone() + { + NV_DISPLAYCONFIG_PATH_INFO_V2 other = (NV_DISPLAYCONFIG_PATH_INFO_V2)MemberwiseClone(); + other.TargetInfo = new NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2[TargetInfoCount]; + for (int x = 0; x < (int)TargetInfoCount; x++) + { + other.TargetInfo[x] = (NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2)TargetInfo[x].Clone(); + } + other.SourceModeInfo = (NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)SourceModeInfo.Clone(); ; + return other; + } } + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL // Version is 2 - This is for processing pass 2 only! + { + public UInt32 Version; + public UInt32 SourceId; //!< Identifies sourceId used by Windows CCD. This can be optionally set. + public UInt32 TargetInfoCount; //!< Number of elements in targetInfo array + public IntPtr TargetInfo; + public IntPtr SourceModeInfo; //!< May be NULL if mode info is not important + //public UInt32 IsNonNVIDIAAdapter : 1; //!< True for non-NVIDIA adapter. + //public UInt32 reserved : 31; //!< Must be 0 + public UInt32 Flags; + //!< Used by Non-NVIDIA adapter for pointer to OS Adapter of LUID + //!< type, type casted to void *. + public IntPtr OSAdapterID; + + public bool IsNonNVIDIAAdapter => Flags.GetBit(0); //!< if bit is set then this path uses a non-nvidia adapter + } + + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2_INTERNAL + { + public UInt32 DisplayId; //!< Display ID + public IntPtr Details; // Points to an NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1_INTERNAL object + //!< NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO - May be NULL if no advanced settings are required + public UInt32 WindowsCCDTargetId; //!< Windows CCD target ID. Must be present only for non-NVIDIA adapter, for NVIDIA adapter this parameter is ignored. + + } + + [StructLayout(LayoutKind.Sequential)] - public struct NV_DISPLAYCONFIG_PATH_INFO_V1 : IEquatable // Version is 1 + public struct NV_DISPLAYCONFIG_PATH_INFO_V1 : IEquatable, ICloneable // Version is 1 { public UInt32 Version; public UInt32 SourceId; //!< Identifies sourceId used by Windows CCD. This can be optionally set. @@ -1269,7 +1538,7 @@ namespace DisplayMagicianShared.NVIDIA => Version == other.Version && SourceId == other.SourceId && TargetInfoCount == other.TargetInfoCount && - TargetInfo.Equals(other.TargetInfo) && + TargetInfo.SequenceEqual(other.TargetInfo) && SourceModeInfo.Equals(other.SourceModeInfo); public override Int32 GetHashCode() @@ -1279,11 +1548,22 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_DISPLAYCONFIG_PATH_INFO_V1 lhs, NV_DISPLAYCONFIG_PATH_INFO_V1 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_DISPLAYCONFIG_PATH_INFO_V1 lhs, NV_DISPLAYCONFIG_PATH_INFO_V1 rhs) => !(lhs == rhs); + public object Clone() + { + NV_DISPLAYCONFIG_PATH_INFO_V2 other = (NV_DISPLAYCONFIG_PATH_INFO_V2)MemberwiseClone(); + other.TargetInfo = new NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2[TargetInfoCount]; + for (int x = 0; x < (int)TargetInfoCount; x++) + { + other.TargetInfo[x] = (NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2)TargetInfo[x].Clone(); + } + other.SourceModeInfo = (NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)SourceModeInfo.Clone(); ; + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 : IEquatable + public struct NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 : IEquatable, ICloneable { public NV_RESOLUTION Resolution; public NV_FORMAT ColorFormat; //!< Ignored at present, must be NV_FORMAT_UNKNOWN (0) @@ -1301,44 +1581,42 @@ namespace DisplayMagicianShared.NVIDIA public bool Equals(NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 other) => Resolution.Equals(other.Resolution) && ColorFormat == other.ColorFormat && - Position.Equals(other.Position); + Position.Equals(other.Position) && + Flags == other.Flags; public override Int32 GetHashCode() { - return (Resolution, ColorFormat, Position).GetHashCode(); + return (Resolution, ColorFormat, Position, Flags).GetHashCode(); } public static bool operator ==(NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 lhs, NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 lhs, NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 rhs) => !(lhs == rhs); - } - - [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_DISPLAYCONFIG_PATH_TARGET_INFO : IEquatable - { - public UInt32 DisplayId; //!< Display ID - public NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO Details; //!< May be NULL if no advanced settings are required - public UInt32 TargetId; //!< Windows CCD target ID. Must be present only for non-NVIDIA adapter, for NVIDIA adapter this parameter is ignored. - - public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_TARGET_INFO other && this.Equals(other); - - public bool Equals(NV_DISPLAYCONFIG_PATH_TARGET_INFO other) - => DisplayId == other.DisplayId && - Details.Equals(other.Details) && - TargetId == other.TargetId; - - public override Int32 GetHashCode() + public object Clone() { - return (DisplayId, Details, TargetId).GetHashCode(); + NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 other = (NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)MemberwiseClone(); + other.Resolution = (NV_RESOLUTION)Resolution.Clone(); + other.Position = (NV_POSITION)Position.Clone(); + return other; } - public static bool operator ==(NV_DISPLAYCONFIG_PATH_TARGET_INFO lhs, NV_DISPLAYCONFIG_PATH_TARGET_INFO rhs) => lhs.Equals(rhs); + } - public static bool operator !=(NV_DISPLAYCONFIG_PATH_TARGET_INFO lhs, NV_DISPLAYCONFIG_PATH_TARGET_INFO rhs) => !(lhs == rhs); + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1_INTERNAL + { + public IntPtr Resolution; // Points to a NV_RESOLUTION_INTERNAL object + public NV_FORMAT ColorFormat; //!< Ignored at present, must be NV_FORMAT_UNKNOWN (0) + public IntPtr Position; // Points to a NV_POSITION_INTERNAL object + //!< Is all positions are 0 or invalid, displays will be automatically + //!< positioned from left to right with GDI Primary at 0,0, and all + //!< other displays in the order of the path array. + public NV_DISPLAYCONFIG_SPANNING_ORIENTATION SpanningOrientation; //!< Spanning is only supported on XP + public UInt32 Flags; } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_TOPO_BRIEF : IEquatable // Note: Version 1 of NV_MOSAIC_TOPO_BRIEF structure + public struct NV_MOSAIC_TOPO_BRIEF : IEquatable, ICloneable // Note: Version 1 of NV_MOSAIC_TOPO_BRIEF structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 1 public NV_MOSAIC_TOPO Topo; //!< The topology @@ -1364,6 +1642,11 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_TOPO_BRIEF lhs, NV_MOSAIC_TOPO_BRIEF rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_TOPO_BRIEF lhs, NV_MOSAIC_TOPO_BRIEF rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_TOPO_BRIEF other = (NV_MOSAIC_TOPO_BRIEF)MemberwiseClone(); + return other; + } } // @@ -1384,7 +1667,7 @@ namespace DisplayMagicianShared.NVIDIA //! You can then look at the detailed values within the structure. There are no //! entrypoInt32s which take this structure as input (effectively making it read-only). [StructLayout(LayoutKind.Sequential)] - public struct NV_MOSAIC_TOPO_GROUP : IEquatable // Note: Version 1 of NV_MOSAIC_TOPO_GROUP structure + public struct NV_MOSAIC_TOPO_GROUP : IEquatable, ICloneable // Note: Version 1 of NV_MOSAIC_TOPO_GROUP structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 1 public NV_MOSAIC_TOPO_BRIEF Brief; //!< The brief details of this topo @@ -1407,11 +1690,22 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_TOPO_GROUP lhs, NV_MOSAIC_TOPO_GROUP rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_TOPO_GROUP lhs, NV_MOSAIC_TOPO_GROUP rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_TOPO_GROUP other = (NV_MOSAIC_TOPO_GROUP)MemberwiseClone(); + other.Brief = (NV_MOSAIC_TOPO_BRIEF)Brief.Clone(); + other.Topos = new NV_MOSAIC_TOPO_DETAILS[Topos.Length]; + for (int x = 0; x < (int)Topos.Length; x++) + { + other.Topos[x] = (NV_MOSAIC_TOPO_DETAILS)Topos[x].Clone(); + } + return other; + } } [StructLayout(LayoutKind.Sequential)] - public struct NV_MOSAIC_TOPO_DETAILS : IEquatable // Note: Version 1 of NV_MOSAIC_TOPO_DETAILS structure + public struct NV_MOSAIC_TOPO_DETAILS : IEquatable, ICloneable // Note: Version 1 of NV_MOSAIC_TOPO_DETAILS structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 1 public LogicalGpuHandle LogicalGPUHandle; //!< Logical GPU for this topology @@ -1478,10 +1772,31 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_TOPO_DETAILS lhs, NV_MOSAIC_TOPO_DETAILS rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_TOPO_DETAILS lhs, NV_MOSAIC_TOPO_DETAILS rhs) => !(lhs == rhs); + + public object Clone() + { + NV_MOSAIC_TOPO_DETAILS other = (NV_MOSAIC_TOPO_DETAILS)MemberwiseClone(); + other.LogicalGPUHandle = (LogicalGpuHandle)LogicalGPUHandle.Clone(); + other.GPULayout1D = new NV_MOSAIC_TOPO_GPU_LAYOUT_CELL[GPULayout1D.Length]; + for (int x = 0; x < (int)GPULayout1D.Length; x++) + { + other.GPULayout1D[x] = (NV_MOSAIC_TOPO_GPU_LAYOUT_CELL)GPULayout1D[x].Clone(); + } + + other.GPULayout = new NV_MOSAIC_TOPO_GPU_LAYOUT_CELL[GPULayout.GetLength(0), GPULayout.GetLength(1)]; + for (int x = 0; x < (int)GPULayout.GetLength(0); x++) + { + for (int y = 0; y < (int)GPULayout.GetLength(1); y++) + { + other.GPULayout[x, y] = (NV_MOSAIC_TOPO_GPU_LAYOUT_CELL)GPULayout[x, y].Clone(); + } + } + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_TOPO_GPU_LAYOUT_CELL : IEquatable + public struct NV_MOSAIC_TOPO_GPU_LAYOUT_CELL : IEquatable, ICloneable { public PhysicalGpuHandle PhysicalGPUHandle; //!< Physical GPU to be used in the topology (0 if GPU missing) size is 8 public UInt32 DisplayOutputId; //!< Connected display target(0 if no display connected) size is 8 @@ -1503,10 +1818,16 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_TOPO_GPU_LAYOUT_CELL lhs, NV_MOSAIC_TOPO_GPU_LAYOUT_CELL rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_TOPO_GPU_LAYOUT_CELL lhs, NV_MOSAIC_TOPO_GPU_LAYOUT_CELL rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_TOPO_GPU_LAYOUT_CELL other = (NV_MOSAIC_TOPO_GPU_LAYOUT_CELL)MemberwiseClone(); + other.PhysicalGPUHandle = (PhysicalGpuHandle)PhysicalGPUHandle.Clone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_DISPLAY_SETTING_V1 : IEquatable // Note: Version 1 of NV_MOSAIC_DISPLAY_SETTING structure + public struct NV_MOSAIC_DISPLAY_SETTING_V1 : IEquatable, ICloneable // Note: Version 1 of NV_MOSAIC_DISPLAY_SETTING structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 1 public UInt32 Width; //!< Per-display width @@ -1530,10 +1851,15 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_DISPLAY_SETTING_V1 lhs, NV_MOSAIC_DISPLAY_SETTING_V1 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_DISPLAY_SETTING_V1 lhs, NV_MOSAIC_DISPLAY_SETTING_V1 rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_DISPLAY_SETTING_V1 other = (NV_MOSAIC_DISPLAY_SETTING_V1)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_DISPLAY_SETTING_V2 : IEquatable // Note: Version 2 of NV_MOSAIC_DISPLAY_SETTING structure + public struct NV_MOSAIC_DISPLAY_SETTING_V2 : IEquatable, ICloneable // Note: Version 2 of NV_MOSAIC_DISPLAY_SETTING structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 2 public UInt32 Width; //!< Per-display width @@ -1559,10 +1885,15 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_DISPLAY_SETTING_V2 lhs, NV_MOSAIC_DISPLAY_SETTING_V2 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_DISPLAY_SETTING_V2 lhs, NV_MOSAIC_DISPLAY_SETTING_V2 rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_DISPLAY_SETTING_V2 other = (NV_MOSAIC_DISPLAY_SETTING_V2)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_GRID_TOPO_V1 : IEquatable // Note: Version 1 of NV_MOSAIC_GRID_TOPO structure + public struct NV_MOSAIC_GRID_TOPO_V1 : IEquatable, ICloneable // Note: Version 1 of NV_MOSAIC_GRID_TOPO structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 1 public UInt32 Rows; //!< Per-display width @@ -1597,10 +1928,21 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_GRID_TOPO_V1 lhs, NV_MOSAIC_GRID_TOPO_V1 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_GRID_TOPO_V1 lhs, NV_MOSAIC_GRID_TOPO_V1 rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_GRID_TOPO_V1 other = (NV_MOSAIC_GRID_TOPO_V1)MemberwiseClone(); + other.DisplaySettings = (NV_MOSAIC_DISPLAY_SETTING_V1)DisplaySettings.Clone(); + other.Displays = new NV_MOSAIC_GRID_TOPO_DISPLAY_V1[Displays.Length]; + for (int x = 0; x < (int)Displays.Length; x++) + { + other.Displays[x] = (NV_MOSAIC_GRID_TOPO_DISPLAY_V1)Displays[x].Clone(); + } + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_GRID_TOPO_V2 : IEquatable // Note: Version 2 of NV_MOSAIC_GRID_TOPO structure + public struct NV_MOSAIC_GRID_TOPO_V2 : IEquatable, ICloneable // Note: Version 2 of NV_MOSAIC_GRID_TOPO structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 2 public UInt32 Rows; //!< Per-display width @@ -1636,10 +1978,21 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_GRID_TOPO_V2 lhs, NV_MOSAIC_GRID_TOPO_V2 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_GRID_TOPO_V2 lhs, NV_MOSAIC_GRID_TOPO_V2 rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_GRID_TOPO_V2 other = (NV_MOSAIC_GRID_TOPO_V2)MemberwiseClone(); + other.DisplaySettings = (NV_MOSAIC_DISPLAY_SETTING_V1)DisplaySettings.Clone(); + other.Displays = new NV_MOSAIC_GRID_TOPO_DISPLAY_V2[Displays.Length]; + for (int x = 0; x < (int)Displays.Length; x++) + { + other.Displays[x] = (NV_MOSAIC_GRID_TOPO_DISPLAY_V2)Displays[x].Clone(); + } + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_GRID_TOPO_DISPLAY_V1 : IEquatable // Note: Version 1 of NV_MOSAIC_GRID_TOPO_DISPLAY structure + public struct NV_MOSAIC_GRID_TOPO_DISPLAY_V1 : IEquatable, ICloneable // Note: Version 1 of NV_MOSAIC_GRID_TOPO_DISPLAY structure { public UInt32 DisplayId; //!< DisplayID of the display public Int32 OverlapX; //!< (+overlap, -gap) @@ -1662,10 +2015,15 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_GRID_TOPO_DISPLAY_V1 lhs, NV_MOSAIC_GRID_TOPO_DISPLAY_V1 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_GRID_TOPO_DISPLAY_V1 lhs, NV_MOSAIC_GRID_TOPO_DISPLAY_V1 rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_GRID_TOPO_DISPLAY_V1 other = (NV_MOSAIC_GRID_TOPO_DISPLAY_V1)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_GRID_TOPO_DISPLAY_V2 : IEquatable // Note: Version 2 of NV_MOSAIC_GRID_TOPO_DISPLAY structure + public struct NV_MOSAIC_GRID_TOPO_DISPLAY_V2 : IEquatable, ICloneable // Note: Version 2 of NV_MOSAIC_GRID_TOPO_DISPLAY structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 2 public UInt32 DisplayId; //!< DisplayID of the display @@ -1692,11 +2050,16 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_GRID_TOPO_DISPLAY_V2 lhs, NV_MOSAIC_GRID_TOPO_DISPLAY_V2 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_GRID_TOPO_DISPLAY_V2 lhs, NV_MOSAIC_GRID_TOPO_DISPLAY_V2 rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_GRID_TOPO_DISPLAY_V2 other = (NV_MOSAIC_GRID_TOPO_DISPLAY_V2)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_SUPPORTED_TOPO_INFO_V1 : IEquatable // Note: Version 1 of NV_MOSAIC_SUPPORTED_TOPO_INFO structure + public struct NV_MOSAIC_SUPPORTED_TOPO_INFO_V1 : IEquatable, ICloneable // Note: Version 1 of NV_MOSAIC_SUPPORTED_TOPO_INFO structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 1 public UInt32 TopoBriefsCount; //!< Number of topologies in below array @@ -1722,10 +2085,25 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_SUPPORTED_TOPO_INFO_V1 lhs, NV_MOSAIC_SUPPORTED_TOPO_INFO_V1 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_SUPPORTED_TOPO_INFO_V1 lhs, NV_MOSAIC_SUPPORTED_TOPO_INFO_V1 rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_SUPPORTED_TOPO_INFO_V1 other = (NV_MOSAIC_SUPPORTED_TOPO_INFO_V1)MemberwiseClone(); + other.TopoBriefs = new NV_MOSAIC_TOPO_BRIEF[TopoBriefs.Length]; + for (int x = 0; x < (int)TopoBriefs.Length; x++) + { + other.TopoBriefs[x] = (NV_MOSAIC_TOPO_BRIEF)TopoBriefs[x].Clone(); + } + other.DisplaySettings = new NV_MOSAIC_DISPLAY_SETTING_V1[DisplaySettings.Length]; + for (int x = 0; x < (int)DisplaySettings.Length; x++) + { + other.DisplaySettings[x] = (NV_MOSAIC_DISPLAY_SETTING_V1)DisplaySettings[x].Clone(); + } + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_SUPPORTED_TOPO_INFO_V2 : IEquatable // Note: Version 2 of NV_MOSAIC_SUPPORTED_TOPO_INFO structure + public struct NV_MOSAIC_SUPPORTED_TOPO_INFO_V2 : IEquatable, ICloneable // Note: Version 2 of NV_MOSAIC_SUPPORTED_TOPO_INFO structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 2 public UInt32 TopoBriefsCount; //!< Number of topologies in below array @@ -1750,10 +2128,25 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_SUPPORTED_TOPO_INFO_V2 lhs, NV_MOSAIC_SUPPORTED_TOPO_INFO_V2 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_SUPPORTED_TOPO_INFO_V2 lhs, NV_MOSAIC_SUPPORTED_TOPO_INFO_V2 rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_SUPPORTED_TOPO_INFO_V2 other = (NV_MOSAIC_SUPPORTED_TOPO_INFO_V2)MemberwiseClone(); + other.TopoBriefs = new NV_MOSAIC_TOPO_BRIEF[TopoBriefs.Length]; + for (int x = 0; x < (int)TopoBriefs.Length; x++) + { + other.TopoBriefs[x] = (NV_MOSAIC_TOPO_BRIEF)TopoBriefs[x].Clone(); + } + other.DisplaySettings = new NV_MOSAIC_DISPLAY_SETTING_V2[DisplaySettings.Length]; + for (int x = 0; x < (int)DisplaySettings.Length; x++) + { + other.DisplaySettings[x] = (NV_MOSAIC_DISPLAY_SETTING_V2)DisplaySettings[x].Clone(); + } + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_GPU_DISPLAYIDS_V2 : IEquatable // Note: Version 2 of NV_GPU_DISPLAYIDS_V2 structure + public struct NV_GPU_DISPLAYIDS_V2 : IEquatable, ICloneable // Note: Version 2 of NV_GPU_DISPLAYIDS_V2 structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 2 (NOTE R470 contains a bug, and sets this to 3!) public NV_MONITOR_CONN_TYPE ConnectorType; //!< out: vga, tv, dvi, hdmi and dp.This is reserved for future use and clients should not rely on this information.Instead get the @@ -1787,11 +2180,16 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_GPU_DISPLAYIDS_V2 lhs, NV_GPU_DISPLAYIDS_V2 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_GPU_DISPLAYIDS_V2 lhs, NV_GPU_DISPLAYIDS_V2 rhs) => !(lhs == rhs); + public object Clone() + { + NV_GPU_DISPLAYIDS_V2 other = (NV_GPU_DISPLAYIDS_V2)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_DISPLAY_TOPO_STATUS_V1 : IEquatable // Note: Version 1 of NV_MOSAIC_DISPLAY_TOPO_STATUS_V1 structure + public struct NV_MOSAIC_DISPLAY_TOPO_STATUS_V1 : IEquatable, ICloneable // Note: Version 1 of NV_MOSAIC_DISPLAY_TOPO_STATUS_V1 structure { public UInt32 Version; public NV_MOSAIC_DISPLAYCAPS_PROBLEM_FLAGS ErrorFlags; //!< (OUT) Any of the NV_MOSAIC_DISPLAYTOPO_ERROR_* flags. @@ -1816,10 +2214,20 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_DISPLAY_TOPO_STATUS_V1 lhs, NV_MOSAIC_DISPLAY_TOPO_STATUS_V1 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_DISPLAY_TOPO_STATUS_V1 lhs, NV_MOSAIC_DISPLAY_TOPO_STATUS_V1 rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_DISPLAY_TOPO_STATUS_V1 other = (NV_MOSAIC_DISPLAY_TOPO_STATUS_V1)MemberwiseClone(); + other.Displays = new NV_MOSAIC_DISPLAY_TOPO_STATUS_DISPLAY[Displays.Length]; + for (int x = 0; x < (int)Displays.Length; x++) + { + other.Displays[x] = (NV_MOSAIC_DISPLAY_TOPO_STATUS_DISPLAY)Displays[x].Clone(); + } + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_MOSAIC_DISPLAY_TOPO_STATUS_DISPLAY : IEquatable + public struct NV_MOSAIC_DISPLAY_TOPO_STATUS_DISPLAY : IEquatable, ICloneable { public UInt32 DisplayId; //!< (OUT) The DisplayID of this display. public NV_MOSAIC_DISPLAYCAPS_PROBLEM_FLAGS ErrorFlags; //!< (OUT) Any of the NV_MOSAIC_DISPLAYCAPS_PROBLEM_* flags. @@ -1842,11 +2250,16 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_MOSAIC_DISPLAY_TOPO_STATUS_DISPLAY lhs, NV_MOSAIC_DISPLAY_TOPO_STATUS_DISPLAY rhs) => lhs.Equals(rhs); public static bool operator !=(NV_MOSAIC_DISPLAY_TOPO_STATUS_DISPLAY lhs, NV_MOSAIC_DISPLAY_TOPO_STATUS_DISPLAY rhs) => !(lhs == rhs); + public object Clone() + { + NV_MOSAIC_DISPLAY_TOPO_STATUS_DISPLAY other = (NV_MOSAIC_DISPLAY_TOPO_STATUS_DISPLAY)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_HDR_CAPABILITIES_V2 : IEquatable // Note: Version 2 of NV_HDR_CAPABILITIES structure + public struct NV_HDR_CAPABILITIES_V2 : IEquatable, ICloneable // Note: Version 2 of NV_HDR_CAPABILITIES structure { public UInt32 Version; // Version of this structure - MUST BE SET TO 2 public NV_HDR_CAPABILITIES_V2_FLAGS SupportFlags; //!< Various flags indicating HDR support @@ -1877,10 +2290,15 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_HDR_CAPABILITIES_V2 lhs, NV_HDR_CAPABILITIES_V2 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_HDR_CAPABILITIES_V2 lhs, NV_HDR_CAPABILITIES_V2 rhs) => !(lhs == rhs); + public object Clone() + { + NV_POSITION other = (NV_POSITION)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_HDR_DV_STATIC_METADATA : IEquatable + public struct NV_HDR_DV_STATIC_METADATA : IEquatable, ICloneable { public UInt32 Flags; public UInt16 TargetMinLuminance; @@ -1916,10 +2334,15 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_HDR_DV_STATIC_METADATA lhs, NV_HDR_DV_STATIC_METADATA rhs) => lhs.Equals(rhs); public static bool operator !=(NV_HDR_DV_STATIC_METADATA lhs, NV_HDR_DV_STATIC_METADATA rhs) => !(lhs == rhs); + public object Clone() + { + NV_POSITION other = (NV_POSITION)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_HDR_CAPABILITIES_DISPLAY_DATA : IEquatable + public struct NV_HDR_CAPABILITIES_DISPLAY_DATA : IEquatable, ICloneable { public UInt16 DisplayPrimaryX0; public UInt16 DisplayPrimaryY0; @@ -1955,10 +2378,15 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_HDR_CAPABILITIES_DISPLAY_DATA lhs, NV_HDR_CAPABILITIES_DISPLAY_DATA rhs) => lhs.Equals(rhs); public static bool operator !=(NV_HDR_CAPABILITIES_DISPLAY_DATA lhs, NV_HDR_CAPABILITIES_DISPLAY_DATA rhs) => !(lhs == rhs); + public object Clone() + { + NV_POSITION other = (NV_POSITION)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_HDR_COLOR_DATA_V2 : IEquatable + public struct NV_HDR_COLOR_DATA_V2 : IEquatable, ICloneable { public UInt32 Version; //!< Version of this structure public NV_HDR_CMD Cmd; //!< Command get/set @@ -1990,10 +2418,15 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_HDR_COLOR_DATA_V2 lhs, NV_HDR_COLOR_DATA_V2 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_HDR_COLOR_DATA_V2 lhs, NV_HDR_COLOR_DATA_V2 rhs) => !(lhs == rhs); + public object Clone() + { + NV_POSITION other = (NV_POSITION)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_HDR_COLOR_DISPLAY_DATA : IEquatable + public struct NV_HDR_COLOR_DISPLAY_DATA : IEquatable, ICloneable { public UInt16 DisplayPrimaryX0; public UInt16 DisplayPrimaryY0; @@ -2031,10 +2464,15 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_HDR_COLOR_DISPLAY_DATA lhs, NV_HDR_COLOR_DISPLAY_DATA rhs) => lhs.Equals(rhs); public static bool operator !=(NV_HDR_COLOR_DISPLAY_DATA lhs, NV_HDR_COLOR_DISPLAY_DATA rhs) => !(lhs == rhs); + public object Clone() + { + NV_POSITION other = (NV_POSITION)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_COLOR_DATA_V5 : IEquatable + public struct NV_COLOR_DATA_V5 : IEquatable, ICloneable { public UInt32 Version; //!< Version of this structure public UInt16 Size; //!< Size of this structure @@ -2065,10 +2503,15 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_COLOR_DATA_V5 lhs, NV_COLOR_DATA_V5 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_COLOR_DATA_V5 lhs, NV_COLOR_DATA_V5 rhs) => !(lhs == rhs); + public object Clone() + { + NV_POSITION other = (NV_POSITION)MemberwiseClone(); + return other; + } } [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct NV_CUSTOM_DISPLAY_V1 : IEquatable + public struct NV_CUSTOM_DISPLAY_V1 : IEquatable, ICloneable { public UInt32 Version; //!< Version of this structure public UInt32 Width; //!< Source surface(source mode) width @@ -2104,6 +2547,86 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator ==(NV_CUSTOM_DISPLAY_V1 lhs, NV_CUSTOM_DISPLAY_V1 rhs) => lhs.Equals(rhs); public static bool operator !=(NV_CUSTOM_DISPLAY_V1 lhs, NV_CUSTOM_DISPLAY_V1 rhs) => !(lhs == rhs); + public object Clone() + { + NV_POSITION other = (NV_POSITION)MemberwiseClone(); + return other; + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct NV_GET_ADAPTIVE_SYNC_DATA_V1 : IEquatable, ICloneable + { + public UInt32 Version; // Must be V1 + public UInt32 MaxFrameInterval; //!< maximum frame interval in micro seconds as set previously using NvAPI_DISP_SetAdaptiveSyncData function. If default values from EDID are used, this parameter returns 0. + public UInt32 Flags; + public UInt32 LastFlipRefreshCount; //!< Number of times the last flip was shown on the screen + public UInt64 LastFlipTimeStamp; //!< Timestamp for the lastest flip on the screen + public UInt32 ReservedEx1; + public UInt32 ReservedEx2; + public UInt32 ReservedEx3; + public UInt32 ReservedEx4; + + public bool DisableAdaptiveSync => (Flags & 0x1) == 0x1; //!< Indicates if adaptive sync is disabled on the display. + public bool DisableFrameSplitting => (Flags & 0x1) == 0x1; //!< Indicates if frame splitting is disabled on the display. + + public override bool Equals(object obj) => obj is NV_GET_ADAPTIVE_SYNC_DATA_V1 other && this.Equals(other); + + public bool Equals(NV_GET_ADAPTIVE_SYNC_DATA_V1 other) + => MaxFrameInterval == other.MaxFrameInterval && + Flags == other.Flags && + LastFlipRefreshCount == other.LastFlipRefreshCount && + LastFlipTimeStamp == other.LastFlipTimeStamp; + + public override Int32 GetHashCode() + { + return (MaxFrameInterval, Flags, LastFlipRefreshCount, LastFlipTimeStamp).GetHashCode(); + } + public static bool operator ==(NV_GET_ADAPTIVE_SYNC_DATA_V1 lhs, NV_GET_ADAPTIVE_SYNC_DATA_V1 rhs) => lhs.Equals(rhs); + + public static bool operator !=(NV_GET_ADAPTIVE_SYNC_DATA_V1 lhs, NV_GET_ADAPTIVE_SYNC_DATA_V1 rhs) => !(lhs == rhs); + public object Clone() + { + NV_GET_ADAPTIVE_SYNC_DATA_V1 other = (NV_GET_ADAPTIVE_SYNC_DATA_V1)MemberwiseClone(); + return other; + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct NV_SET_ADAPTIVE_SYNC_DATA_V1 : IEquatable, ICloneable + { + public UInt32 Version; // Must be V1 + public UInt32 MaxFrameInterval; //!< maximum frame interval in micro seconds as set previously using NvAPI_DISP_SetAdaptiveSyncData function. If default values from EDID are used, this parameter returns 0. + public UInt32 Flags; + public UInt32 ReservedEx1; //!< Number of times the last flip was shown on the screen + public UInt64 ReservedEx2; //!< Timestamp for the lastest flip on the screen + public UInt32 ReservedEx3; + public UInt32 ReservedEx4; + public UInt32 ReservedEx5; + public UInt32 ReservedEx6; + public UInt32 ReservedEx7; + + public bool DisableAdaptiveSync => (Flags & 0x1) == 0x1; //!< Indicates if adaptive sync is disabled on the display. + public bool DisableFrameSplitting => (Flags & 0x1) == 0x1; //!< Indicates if frame splitting is disabled on the display. + + public override bool Equals(object obj) => obj is NV_SET_ADAPTIVE_SYNC_DATA_V1 other && this.Equals(other); + + public bool Equals(NV_SET_ADAPTIVE_SYNC_DATA_V1 other) + => MaxFrameInterval == other.MaxFrameInterval && + Flags == other.Flags; + + public override Int32 GetHashCode() + { + return (MaxFrameInterval, Flags).GetHashCode(); + } + public static bool operator ==(NV_SET_ADAPTIVE_SYNC_DATA_V1 lhs, NV_SET_ADAPTIVE_SYNC_DATA_V1 rhs) => lhs.Equals(rhs); + + public static bool operator !=(NV_SET_ADAPTIVE_SYNC_DATA_V1 lhs, NV_SET_ADAPTIVE_SYNC_DATA_V1 rhs) => !(lhs == rhs); + public object Clone() + { + NV_SET_ADAPTIVE_SYNC_DATA_V1 other = (NV_SET_ADAPTIVE_SYNC_DATA_V1)MemberwiseClone(); + return other; + } } // ================================== @@ -2187,7 +2710,16 @@ namespace DisplayMagicianShared.NVIDIA public static UInt32 NV_EDID_V3_VER = MAKE_NVAPI_VERSION(3); public static UInt32 NV_DISPLAYCONFIG_PATH_INFO_V1_VER = MAKE_NVAPI_VERSION(1); public static UInt32 NV_DISPLAYCONFIG_PATH_INFO_V2_VER = MAKE_NVAPI_VERSION(2); + public static UInt32 NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1_VER = MAKE_NVAPI_VERSION(1); public static UInt32 NV_CUSTOM_DISPLAY_V1_VER = MAKE_NVAPI_VERSION(1); + public static UInt32 NV_LOGICAL_GPU_DATA_V1_VER = MAKE_NVAPI_VERSION(1); + public static UInt32 NV_GET_ADAPTIVE_SYNC_DATA_V1_VER = MAKE_NVAPI_VERSION(1); + public static UInt32 NV_SET_ADAPTIVE_SYNC_DATA_V1_VER = MAKE_NVAPI_VERSION(1); + + public static UInt32 NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL_VER = MAKE_NVAPI_VERSION(2); + public static UInt32 NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1_INTERNAL_VER = MAKE_NVAPI_VERSION(1); + + #region Internal Constant @@ -2324,8 +2856,11 @@ namespace DisplayMagicianShared.NVIDIA GetDelegate(NvId_Disp_ColorControl, out Disp_ColorControlInternal); GetDelegate(NvId_DISP_GetDisplayConfig, out DISP_GetDisplayConfigInternal); GetDelegate(NvId_DISP_GetDisplayConfig, out DISP_GetDisplayConfigInternalNull); // null version of the submission + GetDelegate(NvId_DISP_SetDisplayConfig, out DISP_SetDisplayConfigInternal); GetDelegate(NvId_DISP_GetDisplayIdByDisplayName, out DISP_GetDisplayIdByDisplayNameInternal); GetDelegate(NvId_DISP_EnumCustomDisplay, out Disp_EnumCustomDisplayInternal); + GetDelegate(NvId_DISP_GetAdaptiveSyncData, out DISP_GetAdaptiveSyncDataInternal); + GetDelegate(NvId_DISP_SetAdaptiveSyncData, out DISP_SetAdaptiveSyncDataInternal); // GPUs GetDelegate(NvId_EnumPhysicalGPUs, out EnumPhysicalGPUsInternal); @@ -2337,6 +2872,9 @@ namespace DisplayMagicianShared.NVIDIA GetDelegate(NvId_GPU_GetBusType, out GPU_GetBusTypeInternal); GetDelegate(NvId_GPU_GetBusId, out GPU_GetBusIdInternal); GetDelegate(NvId_GPU_GetEDID, out GPU_GetEDIDInternal); + GetDelegate(NvId_GPU_GetEDID, out GPU_GetEDIDInternal); + GetDelegate(NvId_GetLogicalGPUFromPhysicalGPU, out GetLogicalGPUFromPhysicalGPUInternal); + GetDelegate(NvId_GPU_GetLogicalGpuInfo, out GPU_GetLogicalGpuInfoInternal); // Mosaic GetDelegate(NvId_Mosaic_EnableCurrentTopo, out Mosaic_EnableCurrentTopoInternal); @@ -3400,42 +3938,136 @@ namespace DisplayMagicianShared.NVIDIA /// public static NVAPI_STATUS NvAPI_DISP_GetDisplayConfig(ref UInt32 PathInfoCount, ref NV_DISPLAYCONFIG_PATH_INFO_V2[] PathInfos, bool thirdPass = false) { - NVAPI_STATUS status; - IntPtr pathInfoBuffer = IntPtr.Zero; - IntPtr currentPathInfoBuffer = IntPtr.Zero; + NVAPI_STATUS status = NVAPI_STATUS.NVAPI_OK; if (thirdPass) { - // Copy the supplied object for the third pass (when we have the pathInfoCount and the targetInfoCount for each pathInfo, but we want the details) - // Third Pass(Optional, only required if target information is required): Allocate memory for targetInfo with respect - //! to number of targetInfoCount(from Second Pass). - NV_DISPLAYCONFIG_PATH_INFO_V2[] passedPathInfo = PathInfos; - PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2[PathInfoCount]; - // Go through the array and create the structure - int overallTargetCount = 0; + NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL[] pass2PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL[PathInfoCount]; + int totalTargetInfoCount = 0; for (Int32 x = 0; x < (Int32)PathInfoCount; x++) { - // Copy the information passed in, into the buffer we want to pass - - PathInfos[x].SourceId = passedPathInfo[x].SourceId; - PathInfos[x].TargetInfoCount = passedPathInfo[x].TargetInfoCount; - PathInfos[x].TargetInfo = passedPathInfo[x].TargetInfo; - PathInfos[x].SourceModeInfo = passedPathInfo[x].SourceModeInfo; - overallTargetCount += (int)PathInfos[x].TargetInfoCount; - PathInfos[x].Version = MAKE_NVAPI_VERSION(Marshal.SizeOf(passedPathInfo[x]), 1); + totalTargetInfoCount += (int)PathInfos[x].TargetInfoCount; } - // Initialize unmanged memory to hold the unmanaged array of structs - int memorySizeRequired = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_INFO_V2)) * (int)PathInfoCount; - pathInfoBuffer = Marshal.AllocCoTaskMem(memorySizeRequired); + + int onePathInfoMemSize = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL)); + int oneSourceModeMemSize = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)); + int onePathTargetMemSize = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2)); + int oneAdvTargetMemSize = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1)); + IntPtr pathInfoPointer = Marshal.AllocHGlobal(onePathInfoMemSize * (int)PathInfoCount); + IntPtr sourceModeInfoPointer = Marshal.AllocHGlobal(oneSourceModeMemSize * (int)PathInfoCount); + IntPtr targetInfoPointer = Marshal.AllocHGlobal(onePathTargetMemSize * totalTargetInfoCount); + IntPtr advTargetPointer = Marshal.AllocHGlobal(oneAdvTargetMemSize * totalTargetInfoCount); // Also set another memory pointer to the same place so that we can do the memory copying item by item // as we have to do it ourselves (there isn't an easy to use Marshal equivalent) - currentPathInfoBuffer = pathInfoBuffer; - // Go through the array and copy things from managed code to unmanaged code - for (Int32 x = 0; x < (Int32)PathInfoCount; x++) + IntPtr currentPathInfoPointer = pathInfoPointer; + IntPtr currentSourceModeInfoPointer = sourceModeInfoPointer; + IntPtr currentTargetInfoPointer = targetInfoPointer; + IntPtr currentAdvTargetPointer = advTargetPointer; + + try { - // Marshal a single gridtopology into unmanaged code ready for sending to the unmanaged NVAPI function - Marshal.StructureToPtr(PathInfos[x], currentPathInfoBuffer, false); - // advance the buffer forwards to the next object - currentPathInfoBuffer = (IntPtr)((long)currentPathInfoBuffer + Marshal.SizeOf(PathInfos[x])); + // Go through the array and copy things from managed code to unmanaged code + for (Int32 x = 0; x < (Int32)PathInfoCount; x++) + { + // Set up the fields in the path info + pass2PathInfos[x].Version = NVImport.NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL_VER; + pass2PathInfos[x].TargetInfoCount = PathInfos[x].TargetInfoCount; + pass2PathInfos[x].Flags = PathInfos[x].Flags; + pass2PathInfos[x].OSAdapterID = PathInfos[x].OSAdapterID; + pass2PathInfos[x].SourceId = PathInfos[x].SourceId; + // Create a target info array and copy it over + NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2_INTERNAL[] targetInforArray = new NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2_INTERNAL[PathInfos[x].TargetInfoCount]; + pass2PathInfos[x].TargetInfo = currentTargetInfoPointer; + //for (Int32 y = 0; y < (Int32)PathInfos[x].TargetInfoCount; y++) + for (Int32 y = 0; y < (Int32)PathInfos[x].TargetInfoCount; y++) + { + NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 advInfo = new NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1(); + advInfo.Version = NVImport.NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1_VER; + Marshal.StructureToPtr(advInfo, currentAdvTargetPointer, true); + targetInforArray[y].Details = currentAdvTargetPointer; + Marshal.StructureToPtr(targetInforArray[y], currentTargetInfoPointer, true); + currentTargetInfoPointer = new IntPtr(currentTargetInfoPointer.ToInt64() + onePathTargetMemSize); + currentAdvTargetPointer = new IntPtr(currentAdvTargetPointer.ToInt64() + oneAdvTargetMemSize); + } + + // Create a source mode info object and copy it over + NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 sourceModeInfo = (NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)PathInfos[x].SourceModeInfo.Clone(); + Marshal.StructureToPtr(sourceModeInfo, currentSourceModeInfoPointer, true); + pass2PathInfos[x].SourceModeInfo = currentSourceModeInfoPointer; + + // Marshal a single gridtopology into unmanaged code ready for sending to the unmanaged NVAPI function + Marshal.StructureToPtr(pass2PathInfos[x], currentPathInfoPointer, true); + + // advance the buffer forwards to the next object for each object + currentPathInfoPointer = new IntPtr(currentPathInfoPointer.ToInt64() + onePathInfoMemSize); + currentSourceModeInfoPointer = new IntPtr(currentSourceModeInfoPointer.ToInt64() + oneSourceModeMemSize); + } + + if (DISP_GetDisplayConfigInternal != null) + { + // Use the unmanaged buffer in the unmanaged C call + status = DISP_GetDisplayConfigInternal(ref PathInfoCount, pathInfoPointer); + + if (status == NVAPI_STATUS.NVAPI_OK) + { + // If everything worked, then copy the data back from the unmanaged array into the managed array + // So that we can use it in C# land + // Reset the memory pointer we're using for tracking where we are back to the start of the unmanaged memory buffer + currentPathInfoPointer = pathInfoPointer; + // Create a managed array to store the received information within + PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2[PathInfoCount]; + NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL[] returnedPass2PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL[PathInfoCount]; + // Go through the memory buffer item by item and copy the items into the managed array + for (int i = 0; i < PathInfoCount; i++) + { + // fill the returned pass2 array slot structure with the data from the buffer + // This lets us get the information and then copy it across to the one we want to return! + returnedPass2PathInfos[i] = (NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL)Marshal.PtrToStructure(currentPathInfoPointer, typeof(NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL)); + + // Next copy the information across to the PathInfo we actually want to return + PathInfos[i].SourceId = returnedPass2PathInfos[i].SourceId; + PathInfos[i].Flags = returnedPass2PathInfos[i].Flags; + PathInfos[i].OSAdapterID = returnedPass2PathInfos[i].OSAdapterID; + PathInfos[i].TargetInfoCount = returnedPass2PathInfos[i].TargetInfoCount; + PathInfos[i].TargetInfo = new NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2[PathInfos[i].TargetInfoCount]; + PathInfos[i].Version = returnedPass2PathInfos[i].Version; + + // And turn the memory pointer to NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 into an actual object and populate the object. + PathInfos[i].SourceModeInfo = (NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)Marshal.PtrToStructure(returnedPass2PathInfos[i].SourceModeInfo, typeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)); + + currentTargetInfoPointer = returnedPass2PathInfos[i].TargetInfo; + for (Int32 y = 0; y < (Int32)PathInfos[i].TargetInfoCount; y++) + { + NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2_INTERNAL targetInfo; + NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 targetInfoDetails; + + // And turn the memory pointer to NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 into an actual object and populate the object. + targetInfo = (NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2_INTERNAL)Marshal.PtrToStructure(currentTargetInfoPointer, typeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2_INTERNAL)); + PathInfos[i].TargetInfo[y].DisplayId = targetInfo.DisplayId; + PathInfos[i].TargetInfo[y].WindowsCCDTargetId = targetInfo.WindowsCCDTargetId; + + // Next we need to get access to the details object. + targetInfoDetails = (NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1)Marshal.PtrToStructure(targetInfo.Details, typeof(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1)); + PathInfos[i].TargetInfo[y].Details = targetInfoDetails; + currentTargetInfoPointer = new IntPtr(currentTargetInfoPointer.ToInt64() + onePathTargetMemSize); + + } + + // advance the buffer forwards to the next object + currentPathInfoPointer = (IntPtr)((long)currentPathInfoPointer + Marshal.SizeOf(returnedPass2PathInfos[i])); + } + } + } + else + { + status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; + } + } + finally + { + Marshal.FreeCoTaskMem(pathInfoPointer); + Marshal.FreeCoTaskMem(sourceModeInfoPointer); + Marshal.FreeCoTaskMem(targetInfoPointer); + Marshal.FreeCoTaskMem(advTargetPointer); } } @@ -3446,80 +4078,92 @@ namespace DisplayMagicianShared.NVIDIA // targetInfoCount. If sourceModeInfo is needed allocate memory or it can be initialized to NULL. // Build a new blank object for the second pass (when we have the pathInfoCount, but want the targetInfoCount for each pathInfo) // Build a managed structure for us to use as a data source for another object that the unmanaged NVAPI C library can use - PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2[PathInfoCount]; - // Prepare the struct for second pass duties - for (Int32 x = 0; x < (Int32)PathInfoCount; x++) - { - NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 sourceMode = new NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1(); - IntPtr sourceModeBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1))); - Marshal.StructureToPtr(sourceMode, sourceModeBuffer, true); - PathInfos[x].Version = NVImport.NV_DISPLAYCONFIG_PATH_INFO_V2_VER; - PathInfos[x].SourceModeInfo = sourceModeBuffer; - /*PathInfos[x].SourceModeInfo.Resolution = new NV_RESOLUTION(); - PathInfos[x].SourceModeInfo.Position = new NV_POSITION(); - //PathInfos[x].SourceModeInfo = null; - PathInfos[x].TargetInfoCount = 0; - PathInfos[x].TargetInfo = IntPtr.Zero; - //!< This field is reserved. There is ongoing debate if we need this field. - //!< Identifies sourceIds used by Windows. If all sourceIds are 0, - //!< these will be computed automatically. - PathInfos[x].SourceId = 0; - PathInfos[x].Flags = 0; - PathInfos[x].OSAdapterID = new NV_LUID(); - //PathInfos[x].OSAdapterID = IntPtr.Zero;*/ - } - // Initialize unmanged memory to hold the unmanaged array of structs - int sizeOfOneStruct = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_INFO_V2)); - int sizeOfAllStructs = sizeOfOneStruct * (int)PathInfoCount; - //int sizeOfOneStruct = Marshal.SizeOf(PathInfos); - pathInfoBuffer = Marshal.AllocCoTaskMem(sizeOfAllStructs); + NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL[] pass2PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL[PathInfoCount]; + + int onePathInfoMemSize = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL)); + int oneSourceModeMemSize = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)); + IntPtr pathInfoPointer = Marshal.AllocHGlobal(onePathInfoMemSize * (int)PathInfoCount); + IntPtr sourceModeInfoPointer = Marshal.AllocHGlobal(oneSourceModeMemSize * (int)PathInfoCount); // Also set another memory pointer to the same place so that we can do the memory copying item by item // as we have to do it ourselves (there isn't an easy to use Marshal equivalent) - currentPathInfoBuffer = pathInfoBuffer; - // Go through the array and copy things from managed code to unmanaged code - for (Int32 x = 0; x < (Int32)PathInfoCount; x++) + IntPtr currentPathInfoPointer = pathInfoPointer; + IntPtr currentSourceModeInfoPointer = sourceModeInfoPointer; + + try { - // Marshal a single gridtopology into unmanaged code ready for sending to the unmanaged NVAPI function - Marshal.StructureToPtr(PathInfos[x], currentPathInfoBuffer, true); - // advance the buffer forwards to the next object - currentPathInfoBuffer = (IntPtr)((long)currentPathInfoBuffer.ToInt64() + Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2))); - } - } - - - if (DISP_GetDisplayConfigInternal != null) - { - // Use the unmanaged buffer in the unmanaged C call - status = DISP_GetDisplayConfigInternal(ref PathInfoCount, pathInfoBuffer); - - if (status == NVAPI_STATUS.NVAPI_OK) - { - // If everything worked, then copy the data back from the unmanaged array into the managed array - // So that we can use it in C# land - // Reset the memory pointer we're using for tracking where we are back to the start of the unmanaged memory buffer - currentPathInfoBuffer = pathInfoBuffer; - // Create a managed array to store the received information within - PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2[PathInfoCount]; - // Go through the memory buffer item by item and copy the items into the managed array - for (int i = 0; i < PathInfoCount; i++) + // Go through the array and copy things from managed code to unmanaged code + for (Int32 x = 0; x < (Int32)PathInfoCount; x++) { - // build a structure in the array slot - PathInfos[i] = new NV_DISPLAYCONFIG_PATH_INFO_V2(); - // fill the array slot structure with the data from the buffer - PathInfos[i] = (NV_DISPLAYCONFIG_PATH_INFO_V2)Marshal.PtrToStructure(currentPathInfoBuffer, typeof(NV_DISPLAYCONFIG_PATH_INFO_V2)); - // destroy the bit of memory we no longer need - Marshal.DestroyStructure(currentPathInfoBuffer, typeof(NV_DISPLAYCONFIG_PATH_INFO_V2)); - // advance the buffer forwards to the next object - currentPathInfoBuffer = (IntPtr)((long)currentPathInfoBuffer + Marshal.SizeOf(PathInfos[i])); + // Set up the fields in the path info + pass2PathInfos[x].Version = NVImport.NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL_VER; + pass2PathInfos[x].TargetInfoCount = 0; + pass2PathInfos[x].TargetInfo = IntPtr.Zero; + + // Create a source mode info object and copy it over + NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 sourceModeInfo = new NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1(); + Marshal.StructureToPtr(sourceModeInfo, currentSourceModeInfoPointer, true); + pass2PathInfos[x].SourceModeInfo = currentSourceModeInfoPointer; + + // Marshal a single gridtopology into unmanaged code ready for sending to the unmanaged NVAPI function + Marshal.StructureToPtr(pass2PathInfos[x], currentPathInfoPointer, true); + + // advance the buffer forwards to the next object for each object + //currentPathInfoPointer = new IntPtr(currentPathInfoPointer.ToInt64() + onePathInfoMemSize + oneSourceModeMemSize); + currentPathInfoPointer = new IntPtr(currentPathInfoPointer.ToInt64() + onePathInfoMemSize); + currentSourceModeInfoPointer = new IntPtr(currentSourceModeInfoPointer.ToInt64() + oneSourceModeMemSize); + } + + if (DISP_GetDisplayConfigInternal != null) + { + // Use the unmanaged buffer in the unmanaged C call + status = DISP_GetDisplayConfigInternal(ref PathInfoCount, pathInfoPointer); + + if (status == NVAPI_STATUS.NVAPI_OK) + { + // If everything worked, then copy the data back from the unmanaged array into the managed array + // So that we can use it in C# land + // Reset the memory pointer we're using for tracking where we are back to the start of the unmanaged memory buffer + currentPathInfoPointer = pathInfoPointer; + // Create a managed array to store the received information within + PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2[PathInfoCount]; + NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL[] returnedPass2PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL[PathInfoCount]; + // Go through the memory buffer item by item and copy the items into the managed array + for (int i = 0; i < PathInfoCount; i++) + { + // fill the returned pass2 array slot structure with the data from the buffer + // This lets us get the information and then copy it across to the one we want to return! + returnedPass2PathInfos[i] = (NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL)Marshal.PtrToStructure(currentPathInfoPointer, typeof(NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL)); + + // Next copy the information across to the PathInfo we actually want to return + PathInfos[i].SourceId = returnedPass2PathInfos[i].SourceId; + PathInfos[i].Flags = returnedPass2PathInfos[i].Flags; + PathInfos[i].OSAdapterID = returnedPass2PathInfos[i].OSAdapterID; + PathInfos[i].TargetInfo = new NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2[0]; + PathInfos[i].TargetInfoCount = returnedPass2PathInfos[i].TargetInfoCount; + PathInfos[i].Version = returnedPass2PathInfos[i].Version; + + // And turn the memory pointer to NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 into an actual object and populate the object. + PathInfos[i].SourceModeInfo = (NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)Marshal.PtrToStructure(returnedPass2PathInfos[i].SourceModeInfo, typeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)); + + // destroy the bit of memory we no longer need + //Marshal.DestroyStructure(currentPathInfoPointer, typeof(NV_DISPLAYCONFIG_PATH_INFO_V2)); + // advance the buffer forwards to the next object + currentPathInfoPointer = (IntPtr)((long)currentPathInfoPointer + Marshal.SizeOf(returnedPass2PathInfos[i])); + } + } + } + else + { + status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; } } - } - else - { - status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; - } + finally + { + Marshal.FreeCoTaskMem(pathInfoPointer); + Marshal.FreeCoTaskMem(sourceModeInfoPointer); + } - Marshal.FreeCoTaskMem(pathInfoBuffer); + } return status; } @@ -3554,6 +4198,137 @@ namespace DisplayMagicianShared.NVIDIA return status; } + // ******** IMPORTANT! This code has an error when attempting to perform the third pass as required by NVAPI documentation ********* + // ******** FOr this reason I have disabled the code as I don't actually need to get it going. ******** + // NVAPI_INTERFACE NvAPI_DISP_SetDisplayConfig ( __in NvU32 pathInfoCount, __in_ecount(pathInfoCount) NV_DISPLAYCONFIG_PATH_INFO* pathInfo,__in NvU32 flags ) + private delegate NVAPI_STATUS DISP_SetDisplayConfigDelegate( + [In] UInt32 pathInfoCount, + [In] IntPtr pathInfoBuffer, + [In] NV_DISPLAYCONFIG_FLAGS flags); + private static readonly DISP_SetDisplayConfigDelegate DISP_SetDisplayConfigInternal; + + /// + /// DESCRIPTION: This API lets caller apply a global display configuration across multiple GPUs. + /// If all sourceIds are zero, then NvAPI will pick up sourceId's based on the following criteria : + /// If user provides sourceModeInfo then we are trying to assign 0th sourceId always to GDIPrimary.This is needed since active windows always moves along with 0th sourceId. + /// For rest of the paths, we are incrementally assigning the sourceId per adapter basis. + /// If user doesn't provide sourceModeInfo then NVAPI just picks up some default sourceId's in incremental order. Note : NVAPI will not intelligently choose the sourceIDs for any configs that does not need a modeset. + /// SUPPORTED OS: Windows 7 and higher + /// + /// + /// + /// + /// + public static NVAPI_STATUS NvAPI_DISP_SetDisplayConfig(UInt32 pathInfoCount, NV_DISPLAYCONFIG_PATH_INFO_V2[] pathInfos, NV_DISPLAYCONFIG_FLAGS flags) + { + NVAPI_STATUS status = NVAPI_STATUS.NVAPI_OK; + NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL[] pass2PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL[pathInfoCount]; + + int totalTargetInfoCount = 0; + for (Int32 x = 0; x < (Int32)pathInfoCount; x++) + { + totalTargetInfoCount += (int)pathInfos[x].TargetInfoCount; + } + + int onePathInfoMemSize = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL)); + int oneSourceModeMemSize = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)); + int onePathTargetMemSize = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2_INTERNAL)); + int oneAdvTargetMemSize = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1)); + + IntPtr pathInfoPointer = Marshal.AllocCoTaskMem(onePathInfoMemSize * (int)pathInfoCount); + IntPtr sourceModeInfoPointer = Marshal.AllocCoTaskMem(oneSourceModeMemSize * (int)pathInfoCount); + IntPtr targetInfoPointer = Marshal.AllocCoTaskMem(onePathTargetMemSize * totalTargetInfoCount); + IntPtr advTargetPointer = Marshal.AllocCoTaskMem(oneAdvTargetMemSize * totalTargetInfoCount); + + // Also set another memory pointer to the same place so that we can do the memory copying item by item + // as we have to do it ourselves (there isn't an easy to use Marshal equivalent) + IntPtr currentPathInfoPointer = pathInfoPointer; + IntPtr currentSourceModeInfoPointer = sourceModeInfoPointer; + IntPtr currentTargetInfoPointer = targetInfoPointer; + IntPtr currentAdvTargetPointer = advTargetPointer; + + // Go through the array and copy things from managed code to unmanaged code + for (Int32 x = 0; x < (Int32)pathInfoCount; x++) + { + // Create a target info array and copy it over + NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2_INTERNAL[] targetInfoArray = new NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2_INTERNAL[pathInfos[x].TargetInfoCount]; + pass2PathInfos[x].TargetInfo = currentTargetInfoPointer; + for (Int32 y = 0; y < (Int32)pathInfos[x].TargetInfoCount; y++) + { + + NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1 advInfo = new NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1(); + advInfo.ConnectorType = pathInfos[x].TargetInfo[y].Details.ConnectorType; + advInfo.Flags = pathInfos[x].TargetInfo[y].Details.Flags; + advInfo.RefreshRateInMillihertz = pathInfos[x].TargetInfo[y].Details.RefreshRateInMillihertz; + advInfo.Rotation = pathInfos[x].TargetInfo[y].Details.Rotation; + advInfo.Scaling = pathInfos[x].TargetInfo[y].Details.Scaling; + advInfo.Timing = (NV_TIMING)pathInfos[x].TargetInfo[y].Details.Timing.Clone(); + advInfo.TimingOverride = pathInfos[x].TargetInfo[y].Details.TimingOverride; + advInfo.TvFormat = pathInfos[x].TargetInfo[y].Details.TvFormat; + //advInfo.Version = NVImport.NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1_INTERNAL_VER; + advInfo.Version = NVImport.NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1_VER; + Marshal.StructureToPtr(advInfo, currentAdvTargetPointer, true); + + // Fill in this target info array item + targetInfoArray[y].Details = currentAdvTargetPointer; + targetInfoArray[y].DisplayId = pathInfos[x].TargetInfo[y].DisplayId; + targetInfoArray[y].WindowsCCDTargetId = pathInfos[x].TargetInfo[y].WindowsCCDTargetId; + Marshal.StructureToPtr(targetInfoArray[y], currentTargetInfoPointer, true); + + // Prepare the pointers for the next objects + currentTargetInfoPointer = new IntPtr(currentTargetInfoPointer.ToInt64() + onePathTargetMemSize); + currentAdvTargetPointer = new IntPtr(currentAdvTargetPointer.ToInt64() + oneAdvTargetMemSize); + } + + + // Create a source mode info object and copy it over + NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 sourceModeInfo = new NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1(); + sourceModeInfo.ColorFormat = pathInfos[x].SourceModeInfo.ColorFormat; + sourceModeInfo.Flags = pathInfos[x].SourceModeInfo.Flags; + sourceModeInfo.Position = (NV_POSITION)pathInfos[x].SourceModeInfo.Position.Clone(); + sourceModeInfo.Resolution = (NV_RESOLUTION)pathInfos[x].SourceModeInfo.Resolution.Clone(); + sourceModeInfo.SpanningOrientation = pathInfos[x].SourceModeInfo.SpanningOrientation; + Marshal.StructureToPtr(sourceModeInfo, currentSourceModeInfoPointer, true); + + // Set up the fields in the path info + pass2PathInfos[x].Version = NVImport.NV_DISPLAYCONFIG_PATH_INFO_V2_INTERNAL_VER; + pass2PathInfos[x].TargetInfoCount = pathInfos[x].TargetInfoCount; + pass2PathInfos[x].Flags = pathInfos[x].Flags; + pass2PathInfos[x].OSAdapterID = pathInfos[x].OSAdapterID; + pass2PathInfos[x].SourceId = pathInfos[x].SourceId; + pass2PathInfos[x].SourceModeInfo = currentSourceModeInfoPointer; + + // Marshal a single gridtopology into unmanaged code ready for sending to the unmanaged NVAPI function + Marshal.StructureToPtr(pass2PathInfos[x], currentPathInfoPointer, true); + + // advance the buffer forwards to the next object for each object + currentPathInfoPointer = new IntPtr(currentPathInfoPointer.ToInt64() + onePathInfoMemSize); + currentSourceModeInfoPointer = new IntPtr(currentSourceModeInfoPointer.ToInt64() + oneSourceModeMemSize); + } + + if (DISP_SetDisplayConfigInternal != null) + { + // Use the unmanaged buffer in the unmanaged C call + status = DISP_SetDisplayConfigInternal(pathInfoCount, pathInfoPointer, 0); + } + else + { + status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; + } + + Marshal.FreeCoTaskMem(pathInfoPointer); + Marshal.FreeCoTaskMem(sourceModeInfoPointer); + Marshal.FreeCoTaskMem(targetInfoPointer); + Marshal.FreeCoTaskMem(advTargetPointer); + //Marshal.FreeCoTaskMem(timingPointer); + //Marshal.FreeCoTaskMem(timingExtraPointer); + //Marshal.FreeCoTaskMem(positionPointer); + //Marshal.FreeCoTaskMem(resolutionPointer); + + return status; + } + + // NVAPI_INTERFACE NvAPI_DISP_GetDisplayIdByDisplayName(const char *displayName, NvU32* displayId); private delegate NVAPI_STATUS DISP_GetDisplayIdByDisplayNameDelegate( @@ -3657,7 +4432,7 @@ namespace DisplayMagicianShared.NVIDIA // Build a managed structure for us to use as a data source for another object that the unmanaged NVAPI C library can use displaySettings = new NV_MOSAIC_DISPLAY_SETTING_V2[displayCount]; // Initialize unmanged memory to hold the unmanaged array of structs - IntPtr displaySettingsBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NV_MOSAIC_DISPLAY_SETTING_V2)) * (int)displayCount); + IntPtr displaySettingsBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NV_MOSAIC_DISPLAY_SETTING_V2)) * (int)displayCount); // Also set another memory pointer to the same place so that we can do the memory copying item by item // as we have to do it ourselves (there isn't an easy to use Marshal equivalent) IntPtr currentDisplaySettingsBuffer = displaySettingsBuffer; @@ -3761,7 +4536,7 @@ namespace DisplayMagicianShared.NVIDIA // Build a managed structure for us to use as a data source for another object that the unmanaged NVAPI C library can use GridTopologies = new NV_MOSAIC_GRID_TOPO_V2[GridCount]; // Initialize unmanged memory to hold the unmanaged array of structs - IntPtr gridTopologiesBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NV_MOSAIC_GRID_TOPO_V2)) * (int)GridCount); + IntPtr gridTopologiesBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NV_MOSAIC_GRID_TOPO_V2)) * (int)GridCount); // Also set another memory pointer to the same place so that we can do the memory copying item by item // as we have to do it ourselves (there isn't an easy to use Marshal equivalent) IntPtr currentGridTopologiesBuffer = gridTopologiesBuffer; @@ -3867,7 +4642,7 @@ namespace DisplayMagicianShared.NVIDIA // Warning! - This function still has some errors with it. It errors with an NVAPI_INCOMPATIBLE_STRUCT_VERSION error. Still needs troubleshooting. NVAPI_STATUS status; // Initialize unmanged memory to hold the unmanaged array of structs - IntPtr gridTopologiesBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NV_MOSAIC_GRID_TOPO_V2)) * (int)gridCount); + IntPtr gridTopologiesBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NV_MOSAIC_GRID_TOPO_V2)) * (int)gridCount); // Also set another memory pointer to the same place so that we can do the memory copying item by item // as we have to do it ourselves (there isn't an easy to use Marshal equivalent) IntPtr currentGridTopologiesBuffer = gridTopologiesBuffer; @@ -3882,7 +4657,7 @@ namespace DisplayMagicianShared.NVIDIA // Build a managed structure for us to use as a data source for another object that the unmanaged NVAPI C library can use topoStatuses = new NV_MOSAIC_DISPLAY_TOPO_STATUS_V1[gridCount]; // Initialize unmanged memory to hold the unmanaged array of structs - IntPtr topoStatusesBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NV_MOSAIC_DISPLAY_TOPO_STATUS_V1)) * (int)gridCount); + IntPtr topoStatusesBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NV_MOSAIC_DISPLAY_TOPO_STATUS_V1)) * (int)gridCount); // Also set another memory pointer to the same place so that we can do the memory copying item by item // as we have to do it ourselves (there isn't an easy to use Marshal equivalent) IntPtr currentTopoStatusesBuffer = topoStatusesBuffer; @@ -3958,7 +4733,7 @@ namespace DisplayMagicianShared.NVIDIA NVAPI_STATUS status; // Initialize unmanged memory to hold the unmanaged array of structs - IntPtr gridTopologiesBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NV_MOSAIC_GRID_TOPO_V2)) * (int)GridCount); + IntPtr gridTopologiesBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NV_MOSAIC_GRID_TOPO_V2)) * (int)GridCount); // Also set another memory pointer to the same place so that we can do the memory copying item by item // as we have to do it ourselves (there isn't an easy to use Marshal equivalent) IntPtr currentGridTopologiesBuffer = gridTopologiesBuffer; @@ -4215,6 +4990,47 @@ namespace DisplayMagicianShared.NVIDIA return status; } + //NVAPI_INTERFACE NvAPI_DISP_GetAdaptiveSyncData (__in NvU32 displayId, __inout NV_GET_ADAPTIVE_SYNC_DATA *pAdaptiveSyncData) + private delegate NVAPI_STATUS DISP_GetAdaptiveSyncDataDelegate( + [In] UInt32 displayId, + [In, Out] ref NV_GET_ADAPTIVE_SYNC_DATA_V1 adaptiveSyncData); + private static readonly DISP_GetAdaptiveSyncDataDelegate DISP_GetAdaptiveSyncDataInternal; + /// + /// This function is used to get data for the Adaptive Sync Display. + /// + /// + /// + /// + public static NVAPI_STATUS NvAPI_DISP_GetAdaptiveSyncData(UInt32 displayId, ref NV_GET_ADAPTIVE_SYNC_DATA_V1 adaptiveSyncData) + { + NVAPI_STATUS status = NVAPI_STATUS.NVAPI_ERROR; + if (DISP_GetAdaptiveSyncDataInternal != null) { status = DISP_GetAdaptiveSyncDataInternal(displayId, ref adaptiveSyncData); } + else { status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; } + + return status; + } + + //NVAPI_INTERFACE NvAPI_DISP_SetAdaptiveSyncData (__in NvU32 displayId, __in NV_SET_ADAPTIVE_SYNC_DATA *pAdaptiveSyncData) + private delegate NVAPI_STATUS DISP_SetAdaptiveSyncDataDelegate( + [In] UInt32 displayId, + [In] ref NV_SET_ADAPTIVE_SYNC_DATA_V1 adaptiveSyncData); + private static readonly DISP_SetAdaptiveSyncDataDelegate DISP_SetAdaptiveSyncDataInternal; + /// + /// This function is used to set data for Adaptive Sync Display. + /// + /// + /// + /// + public static NVAPI_STATUS NvAPI_DISP_SetAdaptiveSyncData(UInt32 displayId, ref NV_SET_ADAPTIVE_SYNC_DATA_V1 adaptiveSyncData) + { + NVAPI_STATUS status = NVAPI_STATUS.NVAPI_ERROR; + if (DISP_SetAdaptiveSyncDataInternal != null) { status = DISP_SetAdaptiveSyncDataInternal(displayId, ref adaptiveSyncData); } + else { status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; } + + return status; + } + + //NVAPI_INTERFACE NvAPI_DISP_GetGDIPrimaryDisplayId(NvU32* displayId); private delegate NVAPI_STATUS DISP_GetGDIPrimaryDisplayIdDelegate( [Out] out UInt32 displayId); @@ -4264,7 +5080,7 @@ namespace DisplayMagicianShared.NVIDIA // Build a managed structure for us to use as a data source for another object that the unmanaged NVAPI C library can use displayIds = new NV_GPU_DISPLAYIDS_V2[displayCount]; // Initialize unmanged memory to hold the unmanaged array of structs - IntPtr displayIdBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NV_GPU_DISPLAYIDS_V2)) * (int)displayCount); + IntPtr displayIdBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NV_GPU_DISPLAYIDS_V2)) * (int)displayCount); // Also set another memory pointer to the same place so that we can do the memory copying item by item // as we have to do it ourselves (there isn't an easy to use Marshal equivalent) IntPtr currentDisplayIdBuffer = displayIdBuffer; @@ -4582,5 +5398,61 @@ namespace DisplayMagicianShared.NVIDIA return status; } + + //NVAPI_INTERFACE NvAPI_GPU_GetLogicalGpuInfo(__in NvLogicalGpuHandle hLogicalGpu, __inout NV_LOGICAL_GPU_DATA * pLogicalGpuData) + private delegate NVAPI_STATUS GPU_GetLogicalGpuInfoDelegate( + [In] LogicalGpuHandle gpuHandle, + [In][Out] ref NV_LOGICAL_GPU_DATA_V1 logicalGPUData); + private static readonly GPU_GetLogicalGpuInfoDelegate GPU_GetLogicalGpuInfoInternal; + /// + /// This function is used to query Logical GPU information. + /// SUPPORTED OS: Windows 7 and higher + /// + /// + /// + public static NVAPI_STATUS NvAPI_GPU_GetLogicalGpuInfo(LogicalGpuHandle gpuHandle, ref NV_LOGICAL_GPU_DATA_V1 logicalGPUData) + { + NVAPI_STATUS status; + + logicalGPUData = new NV_LOGICAL_GPU_DATA_V1(); + logicalGPUData.Version = NVImport.NV_LOGICAL_GPU_DATA_V1_VER; + logicalGPUData.OSAdapterId = IntPtr.Zero; + logicalGPUData.PhysicalGPUHandles = new PhysicalGpuHandle[(int)NVImport.NVAPI_MAX_PHYSICAL_GPUS]; + + if (GPU_GetEDIDInternal != null) { status = GPU_GetLogicalGpuInfoInternal(gpuHandle, ref logicalGPUData); } + else { status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; } + + return status; + } + + //NVAPI_INTERFACE NvAPI_GetLogicalGPUFromPhysicalGPU(NvPhysicalGpuHandle hPhysicalGPU, NvLogicalGpuHandle* pLogicalGPU) + private delegate NVAPI_STATUS GetLogicalGPUFromPhysicalGPUDelegate( + [In] PhysicalGpuHandle physicalGPUHandle, + [Out] out LogicalGpuHandle logicalGPUHandle); + private static readonly GetLogicalGPUFromPhysicalGPUDelegate GetLogicalGPUFromPhysicalGPUInternal; + /// + /// This function is used to query Logical GPU information. + /// SUPPORTED OS: Windows 7 and higher + /// + /// + /// + public static NVAPI_STATUS NvAPI_GetLogicalGPUFromPhysicalGPU(PhysicalGpuHandle physicalGPUHandle, out LogicalGpuHandle logicalGPUHandle) + { + NVAPI_STATUS status; + + if (GPU_GetEDIDInternal != null) + { + status = GetLogicalGPUFromPhysicalGPUInternal(physicalGPUHandle, out LogicalGpuHandle lgpu); + logicalGPUHandle = lgpu; + } + else + { + status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; + logicalGPUHandle = new LogicalGpuHandle(); + } + + return status; + } + } } \ No newline at end of file diff --git a/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs b/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs index e364538..1fb6b6a 100644 --- a/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs +++ b/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs @@ -50,44 +50,40 @@ namespace DisplayMagicianShared.NVIDIA } [StructLayout(LayoutKind.Sequential)] - public struct NVIDIA_HDR_CONFIG : IEquatable + public struct NVIDIA_PER_DISPLAY_CONFIG : IEquatable { - public Dictionary HdrCapabilities; - public Dictionary HdrColorData; - public bool IsNvHdrEnabled; + public bool HasNvHdrEnabled; + public NV_HDR_CAPABILITIES_V2 HdrCapabilities; + public NV_HDR_COLOR_DATA_V2 HdrColorData; + public bool HasAdaptiveSync; + public NV_SET_ADAPTIVE_SYNC_DATA_V1 AdaptiveSyncConfig; + public bool HasColorData; + public NV_COLOR_DATA_V5 ColorData; + public bool HasCustomDisplay; + public List CustomDisplays; - public override bool Equals(object obj) => obj is NVIDIA_HDR_CONFIG other && this.Equals(other); - public bool Equals(NVIDIA_HDR_CONFIG other) - => HdrCapabilities.SequenceEqual(other.HdrCapabilities) && - HdrColorData.SequenceEqual(other.HdrColorData) && - IsNvHdrEnabled == other.IsNvHdrEnabled; + + public override bool Equals(object obj) => obj is NVIDIA_PER_DISPLAY_CONFIG other && this.Equals(other); + public bool Equals(NVIDIA_PER_DISPLAY_CONFIG other) + => HasNvHdrEnabled == other.HasNvHdrEnabled && + HdrCapabilities.Equals(other.HdrCapabilities) && + HdrColorData.Equals(other.HdrColorData) && + HasAdaptiveSync == other.HasAdaptiveSync && + AdaptiveSyncConfig.Equals(other.AdaptiveSyncConfig) && + HasColorData == other.HasColorData && + ColorData.Equals(other.ColorData) && + HasCustomDisplay == other.HasCustomDisplay && + CustomDisplays.SequenceEqual(other.CustomDisplays); public override int GetHashCode() { - return (HdrCapabilities, HdrColorData, IsNvHdrEnabled).GetHashCode(); + return (HasNvHdrEnabled, HdrCapabilities, HdrColorData, HasAdaptiveSync, AdaptiveSyncConfig, HasColorData, ColorData, HasCustomDisplay, CustomDisplays).GetHashCode(); } - public static bool operator ==(NVIDIA_HDR_CONFIG lhs, NVIDIA_HDR_CONFIG rhs) => lhs.Equals(rhs); + public static bool operator ==(NVIDIA_PER_DISPLAY_CONFIG lhs, NVIDIA_PER_DISPLAY_CONFIG rhs) => lhs.Equals(rhs); - public static bool operator !=(NVIDIA_HDR_CONFIG lhs, NVIDIA_HDR_CONFIG rhs) => !(lhs == rhs); + public static bool operator !=(NVIDIA_PER_DISPLAY_CONFIG lhs, NVIDIA_PER_DISPLAY_CONFIG rhs) => !(lhs == rhs); } - [StructLayout(LayoutKind.Sequential)] - public struct NVIDIA_COLOR_CONFIG : IEquatable - { - public Dictionary ColorData; - - public override bool Equals(object obj) => obj is NVIDIA_COLOR_CONFIG other && this.Equals(other); - public bool Equals(NVIDIA_COLOR_CONFIG other) - => ColorData.SequenceEqual(other.ColorData); - - public override int GetHashCode() - { - return (ColorData).GetHashCode(); - } - public static bool operator ==(NVIDIA_COLOR_CONFIG lhs, NVIDIA_COLOR_CONFIG rhs) => lhs.Equals(rhs); - - public static bool operator !=(NVIDIA_COLOR_CONFIG lhs, NVIDIA_COLOR_CONFIG rhs) => !(lhs == rhs); - } /*[StructLayout(LayoutKind.Sequential)] public struct NVIDIA_CUSTOM_DISPLAY_CONFIG : IEquatable @@ -107,13 +103,38 @@ namespace DisplayMagicianShared.NVIDIA public static bool operator !=(NVIDIA_CUSTOM_DISPLAY_CONFIG lhs, NVIDIA_CUSTOM_DISPLAY_CONFIG rhs) => !(lhs == rhs); }*/ + [StructLayout(LayoutKind.Sequential)] + public struct NVIDIA_PER_ADAPTER_CONFIG : IEquatable + { + public bool IsQuadro; + public bool HasLogicalGPU; + public NV_LOGICAL_GPU_DATA_V1 LogicalGPU; + public UInt32 DisplayCount; + public Dictionary Displays; + + public override bool Equals(object obj) => obj is NVIDIA_PER_ADAPTER_CONFIG other && this.Equals(other); + public bool Equals(NVIDIA_PER_ADAPTER_CONFIG other) + => IsQuadro == other.IsQuadro && + HasLogicalGPU == other.HasLogicalGPU && + LogicalGPU.Equals(other.LogicalGPU) && + DisplayCount == other.DisplayCount && + Displays.SequenceEqual(other.Displays); + + public override int GetHashCode() + { + return (IsQuadro, HasLogicalGPU, LogicalGPU, DisplayCount, Displays).GetHashCode(); + } + public static bool operator ==(NVIDIA_PER_ADAPTER_CONFIG lhs, NVIDIA_PER_ADAPTER_CONFIG rhs) => lhs.Equals(rhs); + + public static bool operator !=(NVIDIA_PER_ADAPTER_CONFIG lhs, NVIDIA_PER_ADAPTER_CONFIG rhs) => !(lhs == rhs); + } + [StructLayout(LayoutKind.Sequential)] public struct NVIDIA_DISPLAY_CONFIG : IEquatable { + public bool IsCloned; public NVIDIA_MOSAIC_CONFIG MosaicConfig; - public NVIDIA_HDR_CONFIG HdrConfig; - public NVIDIA_COLOR_CONFIG ColorConfig; - public Dictionary> CustomDisplays; + public Dictionary PhysicalAdapters; public List DisplayConfigs; // Note: We purposely have left out the DisplayNames 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 @@ -124,16 +145,16 @@ namespace DisplayMagicianShared.NVIDIA public override bool Equals(object obj) => obj is NVIDIA_DISPLAY_CONFIG other && this.Equals(other); public bool Equals(NVIDIA_DISPLAY_CONFIG other) - => MosaicConfig.Equals(other.MosaicConfig) && - HdrConfig.Equals(other.HdrConfig) && - ColorConfig.Equals(other.ColorConfig) && - CustomDisplays.SequenceEqual(other.CustomDisplays) && - DisplayConfigs.SequenceEqual(other.DisplayConfigs) && - DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers); + => IsCloned == other.IsCloned && + PhysicalAdapters.SequenceEqual(other.PhysicalAdapters) && + MosaicConfig.Equals(other.MosaicConfig) && + DisplayConfigs.SequenceEqual(other.DisplayConfigs) && + DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers); + public override int GetHashCode() { - return (MosaicConfig, HdrConfig, CustomDisplays, DisplayConfigs, DisplayIdentifiers, DisplayNames).GetHashCode(); + return (IsCloned, MosaicConfig, PhysicalAdapters, DisplayConfigs, DisplayIdentifiers, DisplayNames).GetHashCode(); } public static bool operator ==(NVIDIA_DISPLAY_CONFIG lhs, NVIDIA_DISPLAY_CONFIG rhs) => lhs.Equals(rhs); @@ -161,6 +182,15 @@ namespace DisplayMagicianShared.NVIDIA static NVIDIALibrary() { } public NVIDIALibrary() { + // Populate the list of ConnectionTypes we want to skip as they don't support querying + SkippedColorConnectionTypes = new List { + NV_MONITOR_CONN_TYPE.VGA, + NV_MONITOR_CONN_TYPE.COMPONENT, + NV_MONITOR_CONN_TYPE.SVIDEO, + NV_MONITOR_CONN_TYPE.DVI, + NV_MONITOR_CONN_TYPE.COMPOSITE, + }; + _activeDisplayConfig = CreateDefaultConfig(); try { @@ -199,15 +229,6 @@ namespace DisplayMagicianShared.NVIDIA SharedLogger.logger.Info(ex, $"NVIDIALibrary/NVIDIALibrary: Exception trying to load the NVIDIA NVAPI DLL. This generally means you don't have the NVIDIA driver installed."); } - // Populate the list of ConnectionTypes we want to skip as they don't support querying - SkippedColorConnectionTypes = new List { - NV_MONITOR_CONN_TYPE.VGA, - NV_MONITOR_CONN_TYPE.COMPONENT, - NV_MONITOR_CONN_TYPE.SVIDEO, - NV_MONITOR_CONN_TYPE.DVI, - NV_MONITOR_CONN_TYPE.COMPOSITE, - }; - } ~NVIDIALibrary() @@ -282,15 +303,14 @@ namespace DisplayMagicianShared.NVIDIA // 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.MosaicConfig.IsMosaicEnabled = false; myDefaultConfig.MosaicConfig.MosaicGridTopos = new NV_MOSAIC_GRID_TOPO_V2[0]; myDefaultConfig.MosaicConfig.MosaicViewports = new List(); - myDefaultConfig.HdrConfig.HdrCapabilities = new Dictionary(); - myDefaultConfig.HdrConfig.HdrColorData = new Dictionary(); - myDefaultConfig.ColorConfig.ColorData = new Dictionary(); - myDefaultConfig.CustomDisplays = new Dictionary>(); + myDefaultConfig.PhysicalAdapters = new Dictionary(); myDefaultConfig.DisplayConfigs = new List(); myDefaultConfig.DisplayNames = new Dictionary(); myDefaultConfig.DisplayIdentifiers = new List(); + myDefaultConfig.IsCloned = false; return myDefaultConfig; } @@ -320,10 +340,14 @@ namespace DisplayMagicianShared.NVIDIA private NVIDIA_DISPLAY_CONFIG GetNVIDIADisplayConfig(bool allDisplays = false) { - NVIDIA_DISPLAY_CONFIG myDisplayConfig = new NVIDIA_DISPLAY_CONFIG(); + NVIDIA_DISPLAY_CONFIG myDisplayConfig = CreateDefaultConfig(); if (_initialised) { + + // Store all the found display IDs so we can use them later + List foundDisplayIds = new List(); + // Enumerate all the Physical GPUs PhysicalGpuHandle[] physicalGpus = new PhysicalGpuHandle[NVImport.NVAPI_MAX_PHYSICAL_GPUS]; uint physicalGpuCount = 0; @@ -340,6 +364,13 @@ namespace DisplayMagicianShared.NVIDIA // Go through the Physical GPUs one by one for (uint physicalGpuIndex = 0; physicalGpuIndex < physicalGpuCount; physicalGpuIndex++) { + // Prepare the physicalGPU per adapter structure to use later + NVIDIA_PER_ADAPTER_CONFIG myAdapter = new NVIDIA_PER_ADAPTER_CONFIG(); + myAdapter.LogicalGPU.PhysicalGPUHandles = new PhysicalGpuHandle[0]; + myAdapter.IsQuadro = false; + myAdapter.HasLogicalGPU = false; + myAdapter.Displays = new Dictionary(); + //This function retrieves the Quadro status for the GPU (1 if Quadro, 0 if GeForce) uint quadroStatus = 0; NVStatus = NVImport.NvAPI_GPU_GetQuadroStatus(physicalGpus[physicalGpuIndex], out quadroStatus); @@ -352,6 +383,7 @@ namespace DisplayMagicianShared.NVIDIA else if (quadroStatus == 1) { SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: NVIDIA Video Card is one from the Quadro range"); + myAdapter.IsQuadro = true; } else { @@ -362,6 +394,36 @@ namespace DisplayMagicianShared.NVIDIA { SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Error getting Quadro status. NvAPI_GPU_GetQuadroStatus() returned error code {NVStatus}"); } + + // Firstly let's get the logical GPU from the Physical handle + LogicalGpuHandle logicalGPUHandle; + NVStatus = NVImport.NvAPI_GetLogicalGPUFromPhysicalGPU(physicalGpus[physicalGpuIndex], out logicalGPUHandle); + if (NVStatus == NVAPI_STATUS.NVAPI_OK) + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Successfully got Logical GPU Handle from physical GPU."); + NV_LOGICAL_GPU_DATA_V1 logicalGPUData = new NV_LOGICAL_GPU_DATA_V1(); + NVStatus = NVImport.NvAPI_GPU_GetLogicalGpuInfo(logicalGPUHandle, ref logicalGPUData); + if (NVStatus == NVAPI_STATUS.NVAPI_OK) + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Successfully got the Logical GPU information from the NVIDIA driver!"); + myAdapter.HasLogicalGPU = true; + myAdapter.LogicalGPU = logicalGPUData; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_POINTER) + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: No Logical GPU found so no logicalGPUData available. NvAPI_GPU_GetLogicalGpuInfo() returned error code {NVStatus}"); + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Error getting Logical GPU information from NVIDIA driver. NvAPI_GPU_GetLogicalGpuInfo() returned error code {NVStatus}"); + } + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Error getting Logical GPU handle from Physical GPU. NvAPI_GetLogicalGPUFromPhysicalGPU() returned error code {NVStatus}"); + } + + myDisplayConfig.PhysicalAdapters[physicalGpuIndex] = myAdapter; } // Get current Supported Mosaic Topology info (check whether Mosaic is on) @@ -573,7 +635,7 @@ namespace DisplayMagicianShared.NVIDIA { SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Some non standard error occurred while getting Mosaic Topology! NvAPI_Mosaic_EnumDisplayGrids() returned error code {NVStatus}"); } - + myDisplayConfig.MosaicConfig.MosaicGridTopos = mosaicGridTopos; myDisplayConfig.MosaicConfig.MosaicGridCount = mosaicGridCount; @@ -700,9 +762,7 @@ namespace DisplayMagicianShared.NVIDIA //! to number of targetInfoCount(from Second Pass). //! SUPPORTED OS: Windows 7 and higher // First pass: Figure out how many pathInfo objects there are - List allDisplayConfigs = new List(); - - /*uint pathInfoCount = 0; + uint pathInfoCount = 0; NVStatus = NVImport.NvAPI_DISP_GetDisplayConfig(ref pathInfoCount); if (NVStatus == NVAPI_STATUS.NVAPI_OK && pathInfoCount > 0) { @@ -717,6 +777,19 @@ namespace DisplayMagicianShared.NVIDIA NVStatus = NVImport.NvAPI_DISP_GetDisplayConfig(ref pathInfoCount, ref pathInfos, true); if (NVStatus == NVAPI_STATUS.NVAPI_OK) { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: NvAPI_DISP_GetDisplayConfig returned OK on third and final pass."); + // If this worked, we need to check for and handle cloned displays if there are any + // They need to be set in a special way (see DisplayConfiguration.cpp from the DisplayConfiguration sample from NVIDIA) + for (int x = 0; x < pathInfoCount; x++) + { + if (pathInfos[x].TargetInfoCount > 1) + { + // This is a cloned display, we need to mark this NVIDIA display profile as cloned so we correct the profile later + myDisplayConfig.IsCloned = true; + } + } + + myDisplayConfig.DisplayConfigs = pathInfos.ToList(); SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: NvAPI_DISP_GetDisplayConfig returned OK on third pass."); } else if (NVStatus == NVAPI_STATUS.NVAPI_NOT_SUPPORTED) @@ -739,6 +812,10 @@ namespace DisplayMagicianShared.NVIDIA { SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: ModeSet has not yet completed. Please wait and call it again. NvAPI_DISP_GetDisplayConfig() returned error code {NVStatus} on third pass."); } + else if (NVStatus == NVAPI_STATUS.NVAPI_INCOMPATIBLE_STRUCT_VERSION) + { + SharedLogger.logger.Error($"NVIDIALibrary/GetNVIDIADisplayConfig: The version of the structure passed in is not compatible with this entrypoint. NvAPI_DISP_GetDisplayConfig() returned error code {NVStatus} on third pass."); + } else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) { SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: A miscellaneous error occurred. NvAPI_DISP_GetDisplayConfig() returned error code {NVStatus} on third pass."); @@ -768,6 +845,10 @@ namespace DisplayMagicianShared.NVIDIA { SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: ModeSet has not yet completed. Please wait and call it again. NvAPI_DISP_GetDisplayConfig() returned error code {NVStatus} on second pass."); } + else if (NVStatus == NVAPI_STATUS.NVAPI_INCOMPATIBLE_STRUCT_VERSION) + { + SharedLogger.logger.Error($"NVIDIALibrary/GetNVIDIADisplayConfig: The version of the structure passed in is not compatible with this entrypoint. NvAPI_DISP_GetDisplayConfig() returned error code {NVStatus} on second pass."); + } else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) { SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: A miscellaneous error occurred. NvAPI_DISP_GetDisplayConfig() returned error code {NVStatus} on second pass."); @@ -775,7 +856,7 @@ namespace DisplayMagicianShared.NVIDIA else { SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Some non standard error occurred while getting NVIDIA Display Config! NvAPI_DISP_GetDisplayConfig() returned error code {NVStatus} on second pass."); - } + } } else if (NVStatus == NVAPI_STATUS.NVAPI_OK && pathInfoCount == 0) @@ -798,6 +879,10 @@ namespace DisplayMagicianShared.NVIDIA { SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: ModeSet has not yet completed. Please wait and call it again. NvAPI_DISP_GetDisplayConfig() returned error code {NVStatus} on first pass"); } + else if (NVStatus == NVAPI_STATUS.NVAPI_INCOMPATIBLE_STRUCT_VERSION) + { + SharedLogger.logger.Error($"NVIDIALibrary/GetNVIDIADisplayConfig: The version of the structure passed in is not compatible with this entrypoint. NvAPI_DISP_GetDisplayConfig() returned error code {NVStatus} on first pass."); + } else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) { SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: A miscellaneous error occurred. NvAPI_DISP_GetDisplayConfig() returned error code {NVStatus} on first pass"); @@ -805,7 +890,7 @@ namespace DisplayMagicianShared.NVIDIA else { SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Some non standard error occurred while getting NVIDIA Display Config! NvAPI_DISP_GetDisplayConfig() returned error code {NVStatus} on first pass"); - }*/ + } // We want to get the primary monitor NVStatus = NVImport.NvAPI_DISP_GetGDIPrimaryDisplayId(out UInt32 primaryDisplayId); @@ -843,6 +928,12 @@ namespace DisplayMagicianShared.NVIDIA // Go through the Physical GPUs one by one for (uint physicalGpuIndex = 0; physicalGpuIndex < physicalGpuCount; physicalGpuIndex++) { + + // Get a new variable to the PhysicalAdapters to make easier to use + // NOTE: This struct was filled in earlier by code further up + NVIDIA_PER_ADAPTER_CONFIG myAdapter = myDisplayConfig.PhysicalAdapters[physicalGpuIndex]; + myAdapter.Displays = new Dictionary(); + //This function retrieves the number of display IDs we know about UInt32 displayCount = 0; NVStatus = NVImport.NvAPI_GPU_GetConnectedDisplayIds(physicalGpus[physicalGpuIndex], ref displayCount, 0); @@ -882,7 +973,7 @@ namespace DisplayMagicianShared.NVIDIA NVStatus = NVImport.NvAPI_GPU_GetConnectedDisplayIds(physicalGpus[physicalGpuIndex], ref displayIds, ref displayCount, 0); if (NVStatus == NVAPI_STATUS.NVAPI_OK) { - SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: NvAPI_GPU_GetConnectedDisplayIds returned OK on second pass. We have {displayCount} physical GPUs"); + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: NvAPI_GPU_GetConnectedDisplayIds returned OK on second pass. We have {displayCount} physical displays"); } else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) { @@ -912,10 +1003,6 @@ namespace DisplayMagicianShared.NVIDIA // Time to get the color settings, HDR capabilities and settings for each display bool isNvHdrEnabled = false; - Dictionary allHdrCapabilities = new Dictionary(); - Dictionary allHdrColorData = new Dictionary(); - Dictionary allColorData = new Dictionary(); - Dictionary> allCustomDisplays = new Dictionary>(); for (int displayIndex = 0; displayIndex < displayCount; displayIndex++) { if (allDisplays) @@ -935,6 +1022,20 @@ namespace DisplayMagicianShared.NVIDIA } } + // Record this as an active display ID + foundDisplayIds.Add(displayIds[displayIndex].DisplayId); + + // Prepare the config structure for us to fill it in + NVIDIA_PER_DISPLAY_CONFIG myDisplay = new NVIDIA_PER_DISPLAY_CONFIG(); + myDisplay.ColorData = new NV_COLOR_DATA_V5(); + myDisplay.HdrColorData = new NV_HDR_COLOR_DATA_V2(); + myDisplay.HdrCapabilities = new NV_HDR_CAPABILITIES_V2(); + myDisplay.AdaptiveSyncConfig = new NV_SET_ADAPTIVE_SYNC_DATA_V1(); + myDisplay.CustomDisplays = new List(); + myDisplay.HasNvHdrEnabled = false; + myDisplay.HasAdaptiveSync = false; + myDisplay.HasCustomDisplay = false; + // We need to skip recording anything that doesn't support color communication if (!SkippedColorConnectionTypes.Contains(displayIds[displayIndex].ConnectorType)) { @@ -947,7 +1048,8 @@ namespace DisplayMagicianShared.NVIDIA if (NVStatus == NVAPI_STATUS.NVAPI_OK) { SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Your monitor {displayIds[displayIndex].DisplayId} has the following color settings set. BPC = {colorData.Bpc.ToString("G")}. Color Format = {colorData.ColorFormat.ToString("G")}. Colorimetry = {colorData.Colorimetry.ToString("G")}. Color Selection Policy = {colorData.ColorSelectionPolicy.ToString("G")}. Color Depth = {colorData.Depth.ToString("G")}. Dynamic Range = {colorData.DynamicRange.ToString("G")}. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); - allColorData.Add(displayIds[displayIndex].DisplayId.ToString(), colorData); + myDisplay.ColorData = colorData; + myDisplay.HasColorData = true; } else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) { @@ -1034,7 +1136,7 @@ namespace DisplayMagicianShared.NVIDIA SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Display {displayIds[displayIndex].DisplayId} DOES NOT support Driver Expanded Default HDR Parameters "); } - allHdrCapabilities.Add(displayIds[displayIndex].DisplayId.ToString(), hdrCapabilities); + myDisplay.HdrCapabilities = hdrCapabilities; } else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) { @@ -1071,8 +1173,9 @@ namespace DisplayMagicianShared.NVIDIA if (hdrColorData.HdrMode != NV_HDR_MODE.OFF) { isNvHdrEnabled = true; + myDisplay.HasNvHdrEnabled = true; } - allHdrColorData.Add(displayIds[displayIndex].DisplayId.ToString(), hdrColorData); + myDisplay.HdrColorData = hdrColorData; } else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) { @@ -1099,75 +1202,125 @@ namespace DisplayMagicianShared.NVIDIA SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Some non standard error occurred while getting HDR color settings! NvAPI_Disp_HdrColorControl() returned error code {NVStatus}. It's most likely that your monitor {displayIds[displayIndex].DisplayId} doesn't support HDR."); } - } - - - // TEMPORARILY DISABLING THE CUSTOM DISPLAY CODE FOR NOW, AS NOT SURE WHAT NVIDIA SETTINGS IT TRACKS - // KEEPING IT IN CASE I NEED IT FOR LATER. I ORIGINALLY THOUGHT THAT IS WHERE INTEGER SCALING SETTINGS LIVED< BUT WAS WRONG - /*// Now we get the Custom Display settings of the display (if there are any) - //NVIDIA_CUSTOM_DISPLAY_CONFIG customDisplayConfig = new NVIDIA_CUSTOM_DISPLAY_CONFIG(); - List customDisplayConfig = new List(); - for (UInt32 d = 0; d < UInt32.MaxValue; d++) - { - NV_CUSTOM_DISPLAY_V1 customDisplay = new NV_CUSTOM_DISPLAY_V1(); - NVStatus = NVImport.NvAPI_DISP_EnumCustomDisplay(displayIds[displayIndex].DisplayId, d, ref customDisplay); + // Now we get the Adaptive Sync Settings from the display + NV_GET_ADAPTIVE_SYNC_DATA_V1 getAdaptiveSyncData = new NV_GET_ADAPTIVE_SYNC_DATA_V1(); + getAdaptiveSyncData.Version = NVImport.NV_GET_ADAPTIVE_SYNC_DATA_V1_VER; + NVStatus = NVImport.NvAPI_DISP_GetAdaptiveSyncData(displayIds[displayIndex].DisplayId, ref getAdaptiveSyncData); if (NVStatus == NVAPI_STATUS.NVAPI_OK) { - SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: NvAPI_DISP_EnumCustomDisplay returned OK. Custom Display settings retrieved."); - customDisplayConfig.Add(customDisplay); + // Copy the AdaptiveSync Data we got into a NV_SET_ADAPTIVE_SYNC_DATA_V1 object so that it can be used without conversion + NV_SET_ADAPTIVE_SYNC_DATA_V1 setAdaptiveSyncData = new NV_SET_ADAPTIVE_SYNC_DATA_V1(); + setAdaptiveSyncData.Version = NVImport.NV_SET_ADAPTIVE_SYNC_DATA_V1_VER; + setAdaptiveSyncData.Flags = getAdaptiveSyncData.Flags; + setAdaptiveSyncData.MaxFrameInterval = getAdaptiveSyncData.MaxFrameInterval; + + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: NvAPI_DISP_GetAdaptiveSyncData returned OK."); + if (getAdaptiveSyncData.DisableAdaptiveSync) + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: AdaptiveSync is DISABLED for Display {displayIds[displayIndex].DisplayId} ."); + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: AdaptiveSync is ENABLED for Display {displayIds[displayIndex].DisplayId} ."); + } + if (getAdaptiveSyncData.DisableFrameSplitting) + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: FrameSplitting is DISABLED for Display {displayIds[displayIndex].DisplayId} ."); + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: FrameSplitting is ENABLED for Display {displayIds[displayIndex].DisplayId} ."); + } + myDisplay.AdaptiveSyncConfig = setAdaptiveSyncData; + myDisplay.HasAdaptiveSync = true; } - else if (NVStatus == NVAPI_STATUS.NVAPI_END_ENUMERATION) + else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) { - SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: We've reached the end of the list of Custom Displays. Breaking the polling loop."); - break; + SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: The input buffer is not large enough to hold it's contents. NvAPI_DISP_GetAdaptiveSyncData() returned error code {NVStatus}"); } else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_DISPLAY_ID) { - SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: The input monitor is either not connected or is not a DP or HDMI panel. NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}"); - break; + SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: The input monitor {displayIds[displayIndex].DisplayId} is either not connected or is not a DP or HDMI panel. NvAPI_DISP_GetAdaptiveSyncData() returned error code {NVStatus}"); } else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED) { - SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: The NvAPI API needs to be initialized first. NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}"); - break; + SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: The NvAPI API needs to be initialized first. NvAPI_DISP_GetAdaptiveSyncData() returned error code {NVStatus}"); } else if (NVStatus == NVAPI_STATUS.NVAPI_NO_IMPLEMENTATION) { - SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: This entry point not available in this NVIDIA Driver. NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}"); - break; - } - else if (NVStatus == NVAPI_STATUS.NVAPI_INCOMPATIBLE_STRUCT_VERSION) - { - SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: The supplied struct is incompatible. NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}"); - break; + SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: This entry point not available in this NVIDIA Driver. NvAPI_DISP_GetAdaptiveSyncData() returned error code {NVStatus}"); } else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) { - SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: A miscellaneous error occurred. NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}."); - break; + SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: A miscellaneous error occurred. NvAPI_DISP_GetAdaptiveSyncData() returned error code {NVStatus}."); } else { - SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Some non standard error occurred while enumerating the custom displays! NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}."); - break; + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Some non standard error occurred while getting HDR color settings! NvAPI_DISP_GetAdaptiveSyncData() returned error code {NVStatus}. It's most likely that your monitor {displayIds[displayIndex].DisplayId} doesn't support HDR."); } - } - if (customDisplayConfig.Count > 0) - { - allCustomDisplays.Add(displayIds[displayIndex].DisplayId, customDisplayConfig); - }*/ - } - // Store the HDR information - myDisplayConfig.HdrConfig.IsNvHdrEnabled = isNvHdrEnabled; - myDisplayConfig.HdrConfig.HdrCapabilities = allHdrCapabilities; - myDisplayConfig.HdrConfig.HdrColorData = allHdrColorData; - myDisplayConfig.ColorConfig.ColorData = allColorData; - myDisplayConfig.CustomDisplays = allCustomDisplays; - myDisplayConfig.DisplayConfigs = allDisplayConfigs; + // TEMPORARILY DISABLING THE CUSTOM DISPLAY CODE FOR NOW, AS NOT SURE WHAT NVIDIA SETTINGS IT TRACKS + // KEEPING IT IN CASE I NEED IT FOR LATER. I ORIGINALLY THOUGHT THAT IS WHERE INTEGER SCALING SETTINGS LIVED< BUT WAS WRONG + /*// Now we get the Custom Display settings of the display (if there are any) + //NVIDIA_CUSTOM_DISPLAY_CONFIG customDisplayConfig = new NVIDIA_CUSTOM_DISPLAY_CONFIG(); + List customDisplayConfig = new List(); + for (UInt32 d = 0; d < UInt32.MaxValue; d++) + { + NV_CUSTOM_DISPLAY_V1 customDisplay = new NV_CUSTOM_DISPLAY_V1(); + NVStatus = NVImport.NvAPI_DISP_EnumCustomDisplay(displayIds[displayIndex].DisplayId, d, ref customDisplay); + if (NVStatus == NVAPI_STATUS.NVAPI_OK) + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: NvAPI_DISP_EnumCustomDisplay returned OK. Custom Display settings retrieved."); + myDisplay.CustomDisplay = customDisplay; + myDisplay.HasCustomDisplay = true; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_END_ENUMERATION) + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: We've reached the end of the list of Custom Displays. Breaking the polling loop."); + break; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_DISPLAY_ID) + { + SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: The input monitor is either not connected or is not a DP or HDMI panel. NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}"); + break; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED) + { + SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: The NvAPI API needs to be initialized first. NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}"); + break; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_NO_IMPLEMENTATION) + { + SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: This entry point not available in this NVIDIA Driver. NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}"); + break; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INCOMPATIBLE_STRUCT_VERSION) + { + SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: The supplied struct is incompatible. NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}"); + break; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) + { + SharedLogger.logger.Warn($"NVIDIALibrary/GetNVIDIADisplayConfig: A miscellaneous error occurred. NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}."); + break; + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/GetNVIDIADisplayConfig: Some non standard error occurred while enumerating the custom displays! NvAPI_DISP_EnumCustomDisplay() returned error code {NVStatus}."); + break; + } + + }*/ + + myAdapter.Displays.Add(displayIds[displayIndex].DisplayId, myDisplay); + } + } } + myAdapter.DisplayCount = (UInt32)myAdapter.Displays.Count(); + myDisplayConfig.PhysicalAdapters[physicalGpuIndex] = myAdapter; + } @@ -1213,6 +1366,59 @@ namespace DisplayMagicianShared.NVIDIA // Get the display identifiers myDisplayConfig.DisplayIdentifiers = GetCurrentDisplayIdentifiers(); + + // Go through and find the list of displayIDs + // ignore the ones that were found + // if one was not found, then + // go through the modes + // patch the target + if (myDisplayConfig.IsCloned) + { + List clonedIdsWeKnow = new List(); + List missingIdsWeWant = new List(); + // Find all displays in the displayconfig + foreach (var displayConfig in myDisplayConfig.DisplayConfigs) + { + foreach (var targetInfo in displayConfig.TargetInfo) + { + if (foundDisplayIds.Contains(targetInfo.DisplayId)) + { + // We have this foundId + clonedIdsWeKnow.Add(targetInfo.DisplayId); + } + } + } + + // Now go through and figure out which foundDisplayId we're missing + foreach (var foundDisplayId in foundDisplayIds) + { + if (!clonedIdsWeKnow.Contains(foundDisplayId)) + { + // We found a cloned display id \o/ + missingIdsWeWant.Add(foundDisplayId); + } + } + + int clonedIdOffset = 0; + // Now we go through the list of missing cloned id's and we fill them in + for (int x = 0; x < myDisplayConfig.DisplayConfigs.Count; x++) + { + // We go through all the displayconfigs, but we want to only change the cloned displays (those with > 1 targetInfo) + if (myDisplayConfig.DisplayConfigs[x].TargetInfoCount > 1) + { + // We only want to change the cloned displays, so we start at index 1 (the clones themselves) + for (int y = 1; y < myDisplayConfig.DisplayConfigs[x].TargetInfoCount; y++) + { + // We want to assign the cloned display the display ID from the missing display + myDisplayConfig.DisplayConfigs[x].TargetInfo[y].DisplayId = missingIdsWeWant[clonedIdOffset++]; + // We also want to clone the Details struct from the base display (the first display) and replicate them on the clone + // This copies the process used within the DisplayCOnfiguration C++ Sample released by NVIDIA + myDisplayConfig.DisplayConfigs[x].TargetInfo[y].Details = (NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_V1)myDisplayConfig.DisplayConfigs[0].TargetInfo[0].Details.Clone(); + } + } + } + } + } else { @@ -1341,86 +1547,89 @@ namespace DisplayMagicianShared.NVIDIA stringToReturn += $"NVIDIA Surround/Mosaic is Disabled\n"; } - // Start printing out things - stringToReturn += $"\n****** NVIDIA COLOR CONFIG *******\n"; - foreach (KeyValuePair colorData in displayConfig.ColorConfig.ColorData) + // Start printing out things for the physical GPU + foreach (KeyValuePair physicalGPU in displayConfig.PhysicalAdapters) { - string displayId = colorData.Key.ToString(); - stringToReturn += $"Display {displayId} BPC is {colorData.Value.Bpc.ToString("G")}.\n"; - stringToReturn += $"Display {displayId} ColorFormat is {colorData.Value.ColorFormat.ToString("G")}.\n"; - stringToReturn += $"Display {displayId} Colorimetry is {colorData.Value.Colorimetry.ToString("G")}.\n"; - stringToReturn += $"Display {displayId} ColorSelectionPolicy is {colorData.Value.ColorSelectionPolicy.ToString("G")}.\n"; - stringToReturn += $"Display {displayId} Depth is {colorData.Value.Depth.ToString("G")}.\n"; - stringToReturn += $"Display {displayId} DynamicRange is {colorData.Value.DynamicRange.ToString("G")}.\n"; - } + stringToReturn += $"\n****** NVIDIA PHYSICAL ADAPTER {physicalGPU.Key} *******\n"; - // Start printing out HDR things - stringToReturn += $"\n****** NVIDIA HDR CONFIG *******\n"; - if (displayConfig.HdrConfig.IsNvHdrEnabled) - { - stringToReturn += $"NVIDIA HDR is Enabled\n"; - if (displayConfig.HdrConfig.HdrCapabilities.Count > 0) - { - stringToReturn += $"There are {displayConfig.HdrConfig.HdrCapabilities.Count} NVIDIA HDR devices in use.\n"; - } - if (displayConfig.MosaicConfig.MosaicGridTopos.Length == 1) - { - stringToReturn += $"There is 1 NVIDIA HDR devices in use.\n"; - } - else - { - stringToReturn += $"There are no NVIDIA HDR devices in use.\n"; - } + NVIDIA_PER_ADAPTER_CONFIG myAdapter = physicalGPU.Value; - foreach (KeyValuePair hdrCapabilityItem in displayConfig.HdrConfig.HdrCapabilities) + foreach (KeyValuePair myDisplayItem in myAdapter.Displays) { - string displayId = hdrCapabilityItem.Key.ToString(); - if (hdrCapabilityItem.Value.IsDolbyVisionSupported) + string displayId = myDisplayItem.Key.ToString(); + NVIDIA_PER_DISPLAY_CONFIG myDisplay = myDisplayItem.Value; + + stringToReturn += $"\n****** NVIDIA PER DISPLAY CONFIG {displayId} *******\n"; + + stringToReturn += $"\n****** NVIDIA COLOR CONFIG *******\n"; + stringToReturn += $"Display {displayId} BPC is {myDisplay.ColorData.Bpc.ToString("G")}.\n"; + stringToReturn += $"Display {displayId} ColorFormat is {myDisplay.ColorData.ColorFormat.ToString("G")}.\n"; + stringToReturn += $"Display {displayId} Colorimetry is {myDisplay.ColorData.Colorimetry.ToString("G")}.\n"; + stringToReturn += $"Display {displayId} ColorSelectionPolicy is {myDisplay.ColorData.ColorSelectionPolicy.ToString("G")}.\n"; + stringToReturn += $"Display {displayId} Depth is {myDisplay.ColorData.Depth.ToString("G")}.\n"; + stringToReturn += $"Display {displayId} DynamicRange is {myDisplay.ColorData.DynamicRange.ToString("G")}.\n"; + + // Start printing out HDR things + stringToReturn += $"\n****** NVIDIA HDR CONFIG *******\n"; + if (myDisplay.HasNvHdrEnabled) { - stringToReturn += $"Display {displayId} supports DolbyVision HDR.\n"; + stringToReturn += $"NVIDIA HDR is Enabled\n"; + if (displayConfig.MosaicConfig.MosaicGridTopos.Length == 1) + { + stringToReturn += $"There is 1 NVIDIA HDR devices in use.\n"; + } + else + { + stringToReturn += $"There are no NVIDIA HDR devices in use.\n"; + } + + if (myDisplay.HdrCapabilities.IsDolbyVisionSupported) + { + stringToReturn += $"Display {displayId} supports DolbyVision HDR.\n"; + } + else + { + stringToReturn += $"Display {displayId} DOES NOT support DolbyVision HDR.\n"; + } + if (myDisplay.HdrCapabilities.IsST2084EotfSupported) + { + stringToReturn += $"Display {displayId} supports ST2084EOTF HDR Mode.\n"; + } + else + { + stringToReturn += $"Display {displayId} DOES NOT support ST2084EOTF HDR Mode.\n"; + } + if (myDisplay.HdrCapabilities.IsTraditionalHdrGammaSupported) + { + stringToReturn += $"Display {displayId} supports Traditional HDR Gamma.\n"; + } + else + { + stringToReturn += $"Display {displayId} DOES NOT support Traditional HDR Gamma.\n"; + } + if (myDisplay.HdrCapabilities.IsEdrSupported) + { + stringToReturn += $"Display {displayId} supports EDR.\n"; + } + else + { + stringToReturn += $"Display {displayId} DOES NOT support EDR.\n"; + } + if (myDisplay.HdrCapabilities.IsTraditionalSdrGammaSupported) + { + stringToReturn += $"Display {displayId} supports SDR Gamma.\n"; + } + else + { + stringToReturn += $"Display {displayId} DOES NOT support SDR Gamma.\n"; + } } else { - stringToReturn += $"Display {displayId} DOES NOT support DolbyVision HDR.\n"; - } - if (hdrCapabilityItem.Value.IsST2084EotfSupported) - { - stringToReturn += $"Display {displayId} supports ST2084EOTF HDR Mode.\n"; - } - else - { - stringToReturn += $"Display {displayId} DOES NOT support ST2084EOTF HDR Mode.\n"; - } - if (hdrCapabilityItem.Value.IsTraditionalHdrGammaSupported) - { - stringToReturn += $"Display {displayId} supports Traditional HDR Gamma.\n"; - } - else - { - stringToReturn += $"Display {displayId} DOES NOT support Traditional HDR Gamma.\n"; - } - if (hdrCapabilityItem.Value.IsEdrSupported) - { - stringToReturn += $"Display {displayId} supports EDR.\n"; - } - else - { - stringToReturn += $"Display {displayId} DOES NOT support EDR.\n"; - } - if (hdrCapabilityItem.Value.IsTraditionalSdrGammaSupported) - { - stringToReturn += $"Display {displayId} supports SDR Gamma.\n"; - } - else - { - stringToReturn += $"Display {displayId} DOES NOT support SDR Gamma.\n"; + stringToReturn += $"NVIDIA HDR is Disabled (HDR may still be enabled within Windows itself)\n"; } } } - else - { - stringToReturn += $"NVIDIA HDR is Disabled (HDR may still be enabled within Windows itself)\n"; - } stringToReturn += $"\n\n"; // Now we also get the Windows CCD Library info, and add it to the above @@ -1439,177 +1648,235 @@ namespace DisplayMagicianShared.NVIDIA // Remove any custom NVIDIA Colour settings SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to turn off colour if it's default set colour."); - // Now we try to set each display color - foreach (var colorDataDict in displayConfig.ColorConfig.ColorData) + foreach (var physicalGPU in displayConfig.PhysicalAdapters) { - NV_COLOR_DATA_V5 colorData = colorDataDict.Value; - string displayId = colorDataDict.Key; - try + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Processing settings for Physical GPU #{physicalGPU.Key}"); + NVIDIA_PER_ADAPTER_CONFIG myAdapter = physicalGPU.Value; + UInt32 myAdapterIndex = physicalGPU.Key; + foreach (var displayDict in myAdapter.Displays) { - UInt32 displayIdAsUInt32 = UInt32.Parse(displayId); + NVIDIA_PER_DISPLAY_CONFIG myDisplay = displayDict.Value; + UInt32 displayId = displayDict.Key; - if (!ActiveDisplayConfig.ColorConfig.ColorData.ContainsKey(displayId)) + if (!ActiveDisplayConfig.PhysicalAdapters[myAdapterIndex].Displays.ContainsKey(displayId)) { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Display {displayId} doesn't exist in this setup, so skipping turning off prior NVIDIA Color Settings."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Display {displayId} doesn't exist in this setup, so skipping changing any NVIDIA display Settings."); continue; } - // If the setting for this display is not the same as we want, then we set it to NV_COLOR_SELECTION_POLICY_BEST_QUALITY - if (ActiveDisplayConfig.ColorConfig.ColorData[displayId].ColorSelectionPolicy != NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_BEST_QUALITY) + // Remove any custom NVIDIA Colour settings + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to turn off colour if it's user set colour."); + + NV_COLOR_DATA_V5 colorData = myDisplay.ColorData; + try { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to turn off NVIDIA customer colour settings for display {displayId}."); - - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want the standard colour settings to be {displayConfig.ColorConfig.ColorData[displayId].ColorSelectionPolicy.ToString("G")} for Mosaic display {displayId}."); - // Force the colorData to be NV_COLOR_SELECTION_POLICY_BEST_QUALITY so that we return the color control to Windows - // We will change the colorData to whatever is required later on - colorData = displayConfig.ColorConfig.ColorData[displayId]; - colorData.ColorSelectionPolicy = NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_BEST_QUALITY; - - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want the standard colour settings to be {displayConfig.ColorConfig.ColorData[displayId].ColorSelectionPolicy.ToString("G")} and they are {ActiveDisplayConfig.ColorConfig.ColorData[displayId].ColorSelectionPolicy.ToString("G")} for Mosaic display {displayId}."); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to turn off standard colour mode for Mosaic display {displayId}."); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings Color selection policy {colorData.ColorSelectionPolicy.ToString("G")} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings BPC {colorData.Bpc} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings colour format {colorData.ColorFormat} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings colourimetry {colorData.Colorimetry} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings colour depth {colorData.Depth} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings dynamic range {colorData.DynamicRange} for Mosaic display {displayId}"); - - // Set the command as a 'SET' - colorData.Cmd = NV_COLOR_CMD.NV_COLOR_CMD_SET; - NVStatus = NVImport.NvAPI_Disp_ColorControl(displayIdAsUInt32, ref colorData); - if (NVStatus == NVAPI_STATUS.NVAPI_OK) + // If the setting for this display is not the same as we want, then we set it to NV_COLOR_SELECTION_POLICY_BEST_QUALITY + if (ActiveDisplayConfig.PhysicalAdapters[myAdapterIndex].Displays[displayId].ColorData.ColorSelectionPolicy != NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_BEST_QUALITY) { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: NvAPI_Disp_ColorControl returned OK. BPC is set to {colorData.Bpc.ToString("G")}. Color Format is set to {colorData.ColorFormat.ToString("G")}. Colorimetry is set to {colorData.Colorimetry.ToString("G")}. Color Selection Policy is set to {colorData.ColorSelectionPolicy.ToString("G")}. Color Depth is set to {colorData.Depth.ToString("G")}. Dynamic Range is set to {colorData.DynamicRange.ToString("G")}"); - switch (colorData.ColorSelectionPolicy) + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to turn off NVIDIA customer colour settings for display {displayId}."); + + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want the standard colour settings to be {myDisplay.ColorData.ColorSelectionPolicy.ToString("G")} for Mosaic display {displayId}."); + // Force the colorData to be NV_COLOR_SELECTION_POLICY_BEST_QUALITY so that we return the color control to Windows + // We will change the colorData to whatever is required later on + //colorData = myDisplay.ColorData; + colorData.ColorSelectionPolicy = NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_BEST_QUALITY; + + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want the standard colour settings to be {myDisplay.ColorData.ColorSelectionPolicy.ToString("G")} and they are currently {ActiveDisplayConfig.PhysicalAdapters[myAdapterIndex].Displays[displayId].ColorData.ColorSelectionPolicy.ToString("G")} for Mosaic display {displayId}."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to turn off standard colour mode for Mosaic display {displayId}."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings Color selection policy {colorData.ColorSelectionPolicy.ToString("G")} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings BPC {colorData.Bpc} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings colour format {colorData.ColorFormat} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings colourimetry {colorData.Colorimetry} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings colour depth {colorData.Depth} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want standard colour settings dynamic range {colorData.DynamicRange} for Mosaic display {displayId}"); + + // Set the command as a 'SET' + colorData.Cmd = NV_COLOR_CMD.NV_COLOR_CMD_SET; + NVStatus = NVImport.NvAPI_Disp_ColorControl(displayId, ref colorData); + if (NVStatus == NVAPI_STATUS.NVAPI_OK) { - case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_USER: - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_USER so the color settings have been set by the user in the NVIDIA Control Panel."); - break; - case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_BEST_QUALITY: // Also matches NV_COLOR_SELECTION_POLICY_DEFAULT as it is 1 - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_BEST_QUALITY so the color settings are being handled by the Windows OS."); - break; - case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_UNKNOWN: - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_UNKNOWN so the color settings aren't being handled by either the Windows OS or the NVIDIA Setup!"); - break; + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: NvAPI_Disp_ColorControl returned OK. BPC is set to {colorData.Bpc.ToString("G")}. Color Format is set to {colorData.ColorFormat.ToString("G")}. Colorimetry is set to {colorData.Colorimetry.ToString("G")}. Color Selection Policy is set to {colorData.ColorSelectionPolicy.ToString("G")}. Color Depth is set to {colorData.Depth.ToString("G")}. Dynamic Range is set to {colorData.DynamicRange.ToString("G")}"); + switch (colorData.ColorSelectionPolicy) + { + case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_USER: + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_USER so the color settings have been set by the user in the NVIDIA Control Panel."); + break; + case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_BEST_QUALITY: // Also matches NV_COLOR_SELECTION_POLICY_DEFAULT as it is 1 + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_BEST_QUALITY so the color settings are being handled by the Windows OS."); + break; + case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_UNKNOWN: + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_UNKNOWN so the color settings aren't being handled by either the Windows OS or the NVIDIA Setup!"); + break; + } + } + else if (NVStatus == NVAPI_STATUS.NVAPI_NOT_SUPPORTED) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: Your monitor {displayId} doesn't support the requested color settings. BPC = {colorData.Bpc.ToString("G")}. Color Format = {colorData.ColorFormat.ToString("G")}. Colorimetry = {colorData.Colorimetry.ToString("G")}. Color Selection Policy = {colorData.ColorSelectionPolicy.ToString("G")}. Color Depth = {colorData.Depth.ToString("G")}. Dynamic Range = {colorData.DynamicRange.ToString("G")}. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The input buffer is not large enough to hold it's contents. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_DISPLAY_ID) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The input monitor is either not connected or is not a DP or HDMI panel. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The NvAPI API needs to be initialized first. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_NO_IMPLEMENTATION) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: This entry point not available in this NVIDIA Driver. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: A miscellaneous error occurred. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Some non standard error occurred while seting the color settings! NvAPI_Disp_ColorControl() returned error code {NVStatus}. It's most likely that your monitor {displayId} doesn't support this color mode."); } } - else if (NVStatus == NVAPI_STATUS.NVAPI_NOT_SUPPORTED) + else { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: Your monitor {displayId} doesn't support the requested color settings. BPC = {colorData.Bpc.ToString("G")}. Color Format = {colorData.ColorFormat.ToString("G")}. Colorimetry = {colorData.Colorimetry.ToString("G")}. Color Selection Policy = {colorData.ColorSelectionPolicy.ToString("G")}. Color Depth = {colorData.Depth.ToString("G")}. Dynamic Range = {colorData.DynamicRange.ToString("G")}. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want only want to turn off custom NVIDIA colour settings if needed for display {displayId}, and that currently isn't required. Skipping changing NVIDIA colour mode."); } - else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) + } + catch (Exception ex) + { + SharedLogger.logger.Error(ex, $"NVIDIALibrary/SetActiveConfig: Exception caused while turning off prior NVIDIA specific colour settings for display {displayId}."); + } + + // Remove any custom NVIDIA HDR Colour settings + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to turn off HDR colour if it's user set HDR colour."); + + NV_HDR_COLOR_DATA_V2 hdrColorData = myDisplay.HdrColorData; + try + { + + // if it's not the same HDR we want, then we turn off HDR (and will apply it if needed later on in SetActiveOverride) + if (ActiveDisplayConfig.PhysicalAdapters[myAdapterIndex].Displays[displayId].HdrColorData.HdrMode != NV_HDR_MODE.OFF) { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The input buffer is not large enough to hold it's contents. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_DISPLAY_ID) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The input monitor is either not connected or is not a DP or HDMI panel. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The NvAPI API needs to be initialized first. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_NO_IMPLEMENTATION) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: This entry point not available in this NVIDIA Driver. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: A miscellaneous error occurred. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to turn on custom HDR mode for display {displayId}."); + + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: HDR mode is currently {ActiveDisplayConfig.PhysicalAdapters[myAdapterIndex].Displays[displayId].HdrColorData.HdrMode.ToString("G")} for Mosaic display {displayId}."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings BPC {hdrColorData.HdrBpc} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings HDR Colour Format {hdrColorData.HdrColorFormat} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings HDR dynamic range {hdrColorData.HdrDynamicRange} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings HDR Mode {hdrColorData.HdrMode} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings Mastering Display Data {hdrColorData.MasteringDisplayData} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings Static Meradata Description ID {hdrColorData.StaticMetadataDescriptorId} for Mosaic display {displayId}"); + // Apply the HDR removal + hdrColorData.Cmd = NV_HDR_CMD.CMD_SET; + hdrColorData.HdrMode = NV_HDR_MODE.OFF; + NVStatus = NVImport.NvAPI_Disp_HdrColorControl(displayId, ref hdrColorData); + if (NVStatus == NVAPI_STATUS.NVAPI_OK) + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: NvAPI_Disp_HdrColorControl returned OK. We just successfully turned off the HDR mode for Mosaic display {displayId}."); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The input buffer is not large enough to hold it's contents. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_DISPLAY_ID) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The input monitor is either not connected or is not a DP or HDMI panel. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The NvAPI API needs to be initialized first. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_NO_IMPLEMENTATION) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: This entry point not available in this NVIDIA Driver. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: A miscellaneous error occurred. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Some non standard error occurred while getting Mosaic Topology! NvAPI_Disp_HdrColorControl() returned error code {NVStatus}. It's most likely that your monitor {displayId} doesn't support HDR."); + } } else { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Some non standard error occurred while seting the color settings! NvAPI_Disp_ColorControl() returned error code {NVStatus}. It's most likely that your monitor {displayId} doesn't support this color mode."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want only want to turn off custom NVIDIA HDR settings if needed for display {displayId}, and that currently isn't required. Skipping changing NVIDIA HDR mode."); } + } - else + catch (Exception ex) { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want only want to turn off custom NVIDIA colour settings if needed for display {displayId}, and that currently isn't required. Skipping changing NVIDIA colour mode."); - } - } - catch (Exception ex) - { - SharedLogger.logger.Error(ex, $"NVIDIALibrary/SetActiveConfig: Exception caused while turning off prior NVIDIA specific colour settings for display {displayId}."); - } - } - - // Remove any custom NVIDIA HDR Colour settings - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to turn off HDR colour if it's user set HDR colour."); - // Now we try to set each display color - foreach (var hdrColorDataDict in displayConfig.HdrConfig.HdrColorData) - { - NV_HDR_COLOR_DATA_V2 hdrColorData = hdrColorDataDict.Value; - string displayId = hdrColorDataDict.Key; - - try - { - - UInt32 displayIdAsUInt32 = UInt32.Parse(displayId); - - if (!ActiveDisplayConfig.HdrConfig.HdrColorData.ContainsKey(displayId)) - { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Display {displayId} doesn't exist in this setup, so skipping turning off HDR."); - continue; + SharedLogger.logger.Error(ex, $"NVIDIALibrary/SetActiveConfig: Exception caused while turning off prior NVIDIA HDR colour settings for display {displayId}."); } - // if it's not the same HDR we want, then we turn off HDR (and will apply it if needed later on in SetActiveOverride) - if (ActiveDisplayConfig.HdrConfig.HdrColorData[displayId].HdrMode != NV_HDR_MODE.OFF) - { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to turn on custom HDR mode for display {displayId}."); + // Set any AdaptiveSync settings + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to set any adaptive Sync settings if in use."); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: HDR mode is currently {ActiveDisplayConfig.HdrConfig.HdrColorData[displayId].HdrMode.ToString("G")} for Mosaic display {displayId}."); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings BPC {hdrColorData.HdrBpc} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings HDR Colour Format {hdrColorData.HdrColorFormat} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings HDR dynamic range {hdrColorData.HdrDynamicRange} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings HDR Mode {hdrColorData.HdrMode} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings Mastering Display Data {hdrColorData.MasteringDisplayData} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want HDR settings Static Meradata Description ID {hdrColorData.StaticMetadataDescriptorId} for Mosaic display {displayId}"); - // Apply the HDR removal - hdrColorData.Cmd = NV_HDR_CMD.CMD_SET; - hdrColorData.HdrMode = NV_HDR_MODE.OFF; - NVStatus = NVImport.NvAPI_Disp_HdrColorControl(displayIdAsUInt32, ref hdrColorData); + NV_SET_ADAPTIVE_SYNC_DATA_V1 adaptiveSyncData = myDisplay.AdaptiveSyncConfig; + try + { + if (myDisplay.AdaptiveSyncConfig.DisableAdaptiveSync) + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to DISABLE Adaptive Sync for display {displayId}."); + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to ENABLE Adaptive Sync for display {displayId}."); + } + + if (myDisplay.AdaptiveSyncConfig.DisableFrameSplitting) + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to DISABLE Frame Splitting for display {displayId}."); + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to ENABLE Frame Splitting for display {displayId}."); + } + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want to set the Adaptice Sync Max Frame Interval to {myDisplay.AdaptiveSyncConfig.MaxFrameInterval}ms for display {displayId}."); + + // Apply the AdaptiveSync settings + NVStatus = NVImport.NvAPI_DISP_SetAdaptiveSyncData(displayId, ref adaptiveSyncData); if (NVStatus == NVAPI_STATUS.NVAPI_OK) { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: NvAPI_Disp_HdrColorControl returned OK. We just successfully turned off the HDR mode for Mosaic display {displayId}."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: NvAPI_DISP_SetAdaptiveSyncData returned OK. We just successfully set the Adaptive Sync settings for display {displayId}."); } else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The input buffer is not large enough to hold it's contents. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The input buffer is not large enough to hold it's contents. NvAPI_DISP_SetAdaptiveSyncData() returned error code {NVStatus}"); } else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_DISPLAY_ID) { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The input monitor is either not connected or is not a DP or HDMI panel. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The input monitor is either not connected or is not a DP or HDMI panel. NvAPI_DISP_SetAdaptiveSyncData() returned error code {NVStatus}"); } else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED) { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The NvAPI API needs to be initialized first. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The NvAPI API needs to be initialized first. NvAPI_DISP_SetAdaptiveSyncData() returned error code {NVStatus}"); } else if (NVStatus == NVAPI_STATUS.NVAPI_NO_IMPLEMENTATION) { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: This entry point not available in this NVIDIA Driver. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: This entry point not available in this NVIDIA Driver. NvAPI_DISP_SetAdaptiveSyncData() returned error code {NVStatus}"); } else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: A miscellaneous error occurred. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: A miscellaneous error occurred. NvAPI_DISP_SetAdaptiveSyncData() returned error code {NVStatus}"); } else { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Some non standard error occurred while getting Mosaic Topology! NvAPI_Disp_HdrColorControl() returned error code {NVStatus}. It's most likely that your monitor {displayId} doesn't support HDR."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Some non standard error occurred while getting Mosaic Topology! NvAPI_DISP_SetAdaptiveSyncData() returned error code {NVStatus}. It's most likely that your monitor {displayId} doesn't support HDR."); } - } - else - { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We want only want to turn off custom NVIDIA HDR settings if needed for display {displayId}, and that currently isn't required. Skipping changing NVIDIA HDR mode."); - } - } - catch (Exception ex) - { - SharedLogger.logger.Error(ex, $"NVIDIALibrary/SetActiveConfig: Exception caused while turning off prior NVIDIA HDR colour settings for display {displayId}."); + } + catch (Exception ex) + { + SharedLogger.logger.Error(ex, $"NVIDIALibrary/SetActiveConfig: Exception caused while trying to set NVIDIA Adaptive Sync settings for display {displayId}."); + } } } + // Now we've set the color the way we want it, lets do the thing // We want to check the NVIDIA Surround (Mosaic) config is valid SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Testing whether the display configuration is valid"); @@ -1977,7 +2244,70 @@ namespace DisplayMagicianShared.NVIDIA { // We are on a non-Mosaic profile now, and we are changing to a non-Mosaic profile // so there is nothing to do as far as NVIDIA is concerned! - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We are on a non-Mosaic profile now, and we are changing to a non-Mosaic profile so there is nothing to do as far as NVIDIA is concerned!"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: We are on a non-Mosaic profile now, and we are changing to a non-Mosaic profile so there is no need to modify Mosaic settings!"); + } + + // Now we set the NVIDIA Display Config (if we have one!) + // If the display profile is a cloned config then NVIDIA GetDisplayConfig doesn't work + // so we need to check for that. We just skip the SetDisplayConfig as it won't exist + if (displayConfig.DisplayConfigs.Count > 0) + { + NVStatus = NVImport.NvAPI_DISP_SetDisplayConfig((UInt32)displayConfig.DisplayConfigs.Count, displayConfig.DisplayConfigs.ToArray(), NV_DISPLAYCONFIG_FLAGS.SAVE_TO_PERSISTENCE); + if (NVStatus == NVAPI_STATUS.NVAPI_OK) + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: NvAPI_DISP_SetDisplayConfig returned OK."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Waiting 0.5 second to let the Display Config layout change take place before continuing"); + System.Threading.Thread.Sleep(500); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_NO_ACTIVE_SLI_TOPOLOGY) + { + SharedLogger.logger.Error($"NVIDIALibrary/SetActiveConfig: No matching GPU topologies could be found. NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}"); + return false; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_DISPLAY_ID) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfig: The Display ID of the first display is not currently possible to use. NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}. Trying again with the next display."); + return false; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_ARGUMENT) + { + SharedLogger.logger.Error($"NVIDIALibrary/SetActiveConfig: One or more arguments passed in are invalid. NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}"); + return false; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED) + { + SharedLogger.logger.Error($"NVIDIALibrary/SetActiveConfig: The NvAPI API needs to be initialized first. NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}"); + return false; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_NO_IMPLEMENTATION) + { + SharedLogger.logger.Error($"NVIDIALibrary/SetActiveConfig: This entry point not available in this NVIDIA Driver. NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}"); + return false; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INCOMPATIBLE_STRUCT_VERSION) + { + SharedLogger.logger.Error($"NVIDIALibrary/SetActiveConfig: The version of the structure passed in is not compatible with this entrypoint. NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}"); + return false; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_MODE_CHANGE_FAILED) + { + SharedLogger.logger.Error($"NVIDIALibrary/SetActiveConfig: There was an error changing the display mode. NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}"); + return false; + } + else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) + { + SharedLogger.logger.Error($"NVIDIALibrary/SetActiveConfig: A miscellaneous error occurred. NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}"); + return false; + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Some non standard error occurred while getting Setting the NVIDIA Display Config! NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}"); + return false; + } + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Skipping setting the NVIDIA Display Config as there isn't one provided in the configuration."); } } @@ -1994,171 +2324,171 @@ namespace DisplayMagicianShared.NVIDIA NVAPI_STATUS NVStatus = NVAPI_STATUS.NVAPI_ERROR; - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want to turn on colour if it's user set colour."); - // Now we try to set each display color - foreach (var colorDataDict in displayConfig.ColorConfig.ColorData) + // Go through the physical adapters + foreach (var physicalGPU in displayConfig.PhysicalAdapters) { - NV_COLOR_DATA_V5 colorData = colorDataDict.Value; - string displayId = colorDataDict.Key; - try - { - UInt32 displayIdAsUInt32 = UInt32.Parse(displayId); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Processing settings for Physical GPU #{physicalGPU.Key}"); + NVIDIA_PER_ADAPTER_CONFIG myAdapter = physicalGPU.Value; + UInt32 myAdapterIndex = physicalGPU.Key; - if (!ActiveDisplayConfig.ColorConfig.ColorData.ContainsKey(displayId)) + foreach (var displayDict in myAdapter.Displays) + { + NVIDIA_PER_DISPLAY_CONFIG myDisplay = displayDict.Value; + UInt32 displayId = displayDict.Key; + + // Now we try to set each display settings + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want to process settings for display {displayId}."); + + if (!ActiveDisplayConfig.PhysicalAdapters[myAdapterIndex].Displays.ContainsKey(displayId)) { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Display {displayId} doesn't exist in this setup, so skipping setting the NVIDIA Color Settings."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Display {displayId} doesn't exist in this setup, so skipping overriding any NVIDIA display Settings."); continue; } - // If this is a setting that says it uses user colour settings, then we turn it off - if (ActiveDisplayConfig.ColorConfig.ColorData[displayId].ColorSelectionPolicy != colorData.ColorSelectionPolicy) + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want to turn on colour if it's user set colour."); + // Now we try to set each display color + + NV_COLOR_DATA_V5 colorData = myDisplay.ColorData; + try { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want to use custom NVIDIA HDR Colour for display {displayId}."); - - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want the standard colour settings to be {displayConfig.ColorConfig.ColorData[displayId].ColorSelectionPolicy.ToString("G")} for Mosaic display {displayId}."); - colorData = displayConfig.ColorConfig.ColorData[displayId]; - - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want the standard colour settings to be {displayConfig.ColorConfig.ColorData[displayId].ColorSelectionPolicy.ToString("G")} and they are {ActiveDisplayConfig.ColorConfig.ColorData[displayId].ColorSelectionPolicy.ToString("G")} for Mosaic display {displayId}."); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want to turn off standard colour mode for Mosaic display {displayId}."); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings Color selection policy {colorData.ColorSelectionPolicy.ToString("G")} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings BPC {colorData.Bpc} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings colour format {colorData.ColorFormat} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings colourimetry {colorData.Colorimetry} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings colour depth {colorData.Depth} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings dynamic range {colorData.DynamicRange} for Mosaic display {displayId}"); - - // Set the command as a 'SET' - colorData.Cmd = NV_COLOR_CMD.NV_COLOR_CMD_SET; - NVStatus = NVImport.NvAPI_Disp_ColorControl(displayIdAsUInt32, ref colorData); - if (NVStatus == NVAPI_STATUS.NVAPI_OK) + // If this is a setting that says it uses user colour settings, then we turn it off + if (ActiveDisplayConfig.PhysicalAdapters[myAdapterIndex].Displays[displayId].ColorData.ColorSelectionPolicy != colorData.ColorSelectionPolicy) { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: NvAPI_Disp_ColorControl returned OK. BPC is set to {colorData.Bpc.ToString("G")}. Color Format is set to {colorData.ColorFormat.ToString("G")}. Colorimetry is set to {colorData.Colorimetry.ToString("G")}. Color Selection Policy is set to {colorData.ColorSelectionPolicy.ToString("G")}. Color Depth is set to {colorData.Depth.ToString("G")}. Dynamic Range is set to {colorData.DynamicRange.ToString("G")}"); - switch (colorData.ColorSelectionPolicy) + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want to use custom NVIDIA HDR Colour for display {displayId}."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want the standard colour settings to be {myDisplay.ColorData.ColorSelectionPolicy.ToString("G")} and they are {ActiveDisplayConfig.PhysicalAdapters[myAdapterIndex].Displays[displayId].ColorData.ColorSelectionPolicy.ToString("G")} for Mosaic display {displayId}."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want to turn off standard colour mode for Mosaic display {displayId}."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings Color selection policy {colorData.ColorSelectionPolicy.ToString("G")} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings BPC {colorData.Bpc} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings colour format {colorData.ColorFormat} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings colourimetry {colorData.Colorimetry} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings colour depth {colorData.Depth} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want standard colour settings dynamic range {colorData.DynamicRange} for Mosaic display {displayId}"); + + // Set the command as a 'SET' + colorData.Cmd = NV_COLOR_CMD.NV_COLOR_CMD_SET; + NVStatus = NVImport.NvAPI_Disp_ColorControl(displayId, ref colorData); + if (NVStatus == NVAPI_STATUS.NVAPI_OK) { - case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_USER: - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_USER so the color settings have been set by the user in the NVIDIA Control Panel."); - break; - case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_BEST_QUALITY: // Also matches NV_COLOR_SELECTION_POLICY_DEFAULT as it is 1 - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_BEST_QUALITY so the color settings are being handled by the Windows OS."); - break; - case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_UNKNOWN: - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_UNKNOWN so the color settings aren't being handled by either the Windows OS or the NVIDIA Setup!"); - break; + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: NvAPI_Disp_ColorControl returned OK. BPC is set to {colorData.Bpc.ToString("G")}. Color Format is set to {colorData.ColorFormat.ToString("G")}. Colorimetry is set to {colorData.Colorimetry.ToString("G")}. Color Selection Policy is set to {colorData.ColorSelectionPolicy.ToString("G")}. Color Depth is set to {colorData.Depth.ToString("G")}. Dynamic Range is set to {colorData.DynamicRange.ToString("G")}"); + switch (colorData.ColorSelectionPolicy) + { + case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_USER: + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_USER so the color settings have been set by the user in the NVIDIA Control Panel."); + break; + case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_BEST_QUALITY: // Also matches NV_COLOR_SELECTION_POLICY_DEFAULT as it is 1 + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_BEST_QUALITY so the color settings are being handled by the Windows OS."); + break; + case NV_COLOR_SELECTION_POLICY.NV_COLOR_SELECTION_POLICY_UNKNOWN: + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: Color Selection Policy is set to NV_COLOR_SELECTION_POLICY_UNKNOWN so the color settings aren't being handled by either the Windows OS or the NVIDIA Setup!"); + break; + } + } + else if (NVStatus == NVAPI_STATUS.NVAPI_NOT_SUPPORTED) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: Your monitor {displayId} doesn't support the requested color settings. BPC = {colorData.Bpc.ToString("G")}. Color Format = {colorData.ColorFormat.ToString("G")}. Colorimetry = {colorData.Colorimetry.ToString("G")}. Color Selection Policy = {colorData.ColorSelectionPolicy.ToString("G")}. Color Depth = {colorData.Depth.ToString("G")}. Dynamic Range = {colorData.DynamicRange.ToString("G")}. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The input buffer is not large enough to hold it's contents. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_DISPLAY_ID) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The input monitor is either not connected or is not a DP or HDMI panel. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The NvAPI API needs to be initialized first. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_NO_IMPLEMENTATION) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: This entry point not available in this NVIDIA Driver. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: A miscellaneous error occurred. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Some non standard error occurred while seting the color settings! NvAPI_Disp_ColorControl() returned error code {NVStatus}. It's most likely that your monitor {displayId} doesn't support this color mode."); } } - else if (NVStatus == NVAPI_STATUS.NVAPI_NOT_SUPPORTED) + else { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: Your monitor {displayId} doesn't support the requested color settings. BPC = {colorData.Bpc.ToString("G")}. Color Format = {colorData.ColorFormat.ToString("G")}. Colorimetry = {colorData.Colorimetry.ToString("G")}. Color Selection Policy = {colorData.ColorSelectionPolicy.ToString("G")}. Color Depth = {colorData.Depth.ToString("G")}. Dynamic Range = {colorData.DynamicRange.ToString("G")}. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want only want to turn on custom NVIDIA colour settings if needed for display {displayId}, and that currently isn't required. Skipping changing NVIDIA colour mode."); } - else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) + } + catch (Exception ex) + { + SharedLogger.logger.Error(ex, $"NVIDIALibrary/SetActiveConfigOverride: Exception caused while turning on NVIDIA custom colour settings for display {displayId}."); + } + + + + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want to turn on NVIDIA HDR colour if it's user wants to use NVIDIA HDR colour."); + // Now we try to set each display color + + NV_HDR_COLOR_DATA_V2 hdrColorData = myDisplay.HdrColorData; + try + { + + // if it's HDR and it's a different mode than what we are in now, then set HDR + if (ActiveDisplayConfig.PhysicalAdapters[myAdapterIndex].Displays[displayId].HdrColorData.HdrMode != hdrColorData.HdrMode) { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The input buffer is not large enough to hold it's contents. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_DISPLAY_ID) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The input monitor is either not connected or is not a DP or HDMI panel. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The NvAPI API needs to be initialized first. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_NO_IMPLEMENTATION) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: This entry point not available in this NVIDIA Driver. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: A miscellaneous error occurred. NvAPI_Disp_ColorControl() returned error code {NVStatus}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want to turn on user-set HDR mode for display {displayId} as it's supposed to be on."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: HDR mode is currently {ActiveDisplayConfig.PhysicalAdapters[myAdapterIndex].Displays[displayId].HdrColorData.HdrMode.ToString("G")} for Mosaic display {displayId}."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings BPC {hdrColorData.HdrBpc} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings HDR Colour Format {hdrColorData.HdrColorFormat} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings HDR dynamic range {hdrColorData.HdrDynamicRange} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings HDR Mode {hdrColorData.HdrMode} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings Mastering Display Data {hdrColorData.MasteringDisplayData} for Mosaic display {displayId}"); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings Static Meradata Description ID {hdrColorData.StaticMetadataDescriptorId} for Mosaic display {displayId}"); + // Apply the HDR removal + hdrColorData.Cmd = NV_HDR_CMD.CMD_SET; + NVStatus = NVImport.NvAPI_Disp_HdrColorControl(displayId, ref hdrColorData); + if (NVStatus == NVAPI_STATUS.NVAPI_OK) + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: NvAPI_Disp_HdrColorControl returned OK. We just successfully turned off the HDR mode for Mosaic display {displayId}."); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The input buffer is not large enough to hold it's contents. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_DISPLAY_ID) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The input monitor is either not connected or is not a DP or HDMI panel. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The NvAPI API needs to be initialized first. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_NO_IMPLEMENTATION) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: This entry point not available in this NVIDIA Driver. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + } + else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) + { + SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: A miscellaneous error occurred. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); + } + else + { + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Some non standard error occurred while getting Mosaic Topology! NvAPI_Disp_HdrColorControl() returned error code {NVStatus}. It's most likely that your monitor {displayId} doesn't support HDR."); + } } else { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Some non standard error occurred while seting the color settings! NvAPI_Disp_ColorControl() returned error code {NVStatus}. It's most likely that your monitor {displayId} doesn't support this color mode."); + SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want only want to turn on custom NVIDIA HDR if needed for display {displayId} and that currently isn't required. Skipping changing NVIDIA HDR mode."); } } - else + catch (Exception ex) { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want only want to turn on custom NVIDIA colour settings if needed for display {displayId}, and that currently isn't required. Skipping changing NVIDIA colour mode."); + SharedLogger.logger.Error(ex, $"NVIDIALibrary/SetActiveConfigOverride: Exception caused while turning on custom NVIDIA HDR colour settings for display {displayId}."); } + } - catch (Exception ex) - { - SharedLogger.logger.Error(ex, $"NVIDIALibrary/SetActiveConfigOverride: Exception caused while turning on NVIDIA custom colour settings for display {displayId}."); - } + + } - - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want to turn on NVIDIA HDR colour if it's user wants to use NVIDIA HDR colour."); - // Now we try to set each display color - foreach (var hdrColorDataDict in displayConfig.HdrConfig.HdrColorData) - { - NV_HDR_COLOR_DATA_V2 hdrColorData = hdrColorDataDict.Value; - string displayId = hdrColorDataDict.Key; - - try - { - UInt32 displayIdAsUInt32 = UInt32.Parse(displayId); - - if (!ActiveDisplayConfig.HdrConfig.HdrColorData.ContainsKey(displayId)) - { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Display {displayId} doesn't exist in this setup, so skipping setting the HdrColorData."); - continue; - } - - // if it's HDR and it's a different mode than what we are in now, then set HDR - if (ActiveDisplayConfig.HdrConfig.HdrColorData[displayId].HdrMode != hdrColorData.HdrMode) - { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want to turn on user-set HDR mode for display {displayId} as it's supposed to be on."); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: HDR mode is currently {ActiveDisplayConfig.HdrConfig.HdrColorData[displayId].HdrMode.ToString("G")} for Mosaic display {displayId}."); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings BPC {hdrColorData.HdrBpc} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings HDR Colour Format {hdrColorData.HdrColorFormat} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings HDR dynamic range {hdrColorData.HdrDynamicRange} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings HDR Mode {hdrColorData.HdrMode} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings Mastering Display Data {hdrColorData.MasteringDisplayData} for Mosaic display {displayId}"); - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want HDR settings Static Meradata Description ID {hdrColorData.StaticMetadataDescriptorId} for Mosaic display {displayId}"); - // Apply the HDR removal - hdrColorData.Cmd = NV_HDR_CMD.CMD_SET; - NVStatus = NVImport.NvAPI_Disp_HdrColorControl(displayIdAsUInt32, ref hdrColorData); - if (NVStatus == NVAPI_STATUS.NVAPI_OK) - { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: NvAPI_Disp_HdrColorControl returned OK. We just successfully turned off the HDR mode for Mosaic display {displayId}."); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_INSUFFICIENT_BUFFER) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The input buffer is not large enough to hold it's contents. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_DISPLAY_ID) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The input monitor is either not connected or is not a DP or HDMI panel. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: The NvAPI API needs to be initialized first. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_NO_IMPLEMENTATION) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: This entry point not available in this NVIDIA Driver. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); - } - else if (NVStatus == NVAPI_STATUS.NVAPI_ERROR) - { - SharedLogger.logger.Warn($"NVIDIALibrary/SetActiveConfigOverride: A miscellaneous error occurred. NvAPI_Disp_HdrColorControl() returned error code {NVStatus}"); - } - else - { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Some non standard error occurred while getting Mosaic Topology! NvAPI_Disp_HdrColorControl() returned error code {NVStatus}. It's most likely that your monitor {displayId} doesn't support HDR."); - } - } - else - { - SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: We want only want to turn on custom NVIDIA HDR if needed for display {displayId} and that currently isn't required. Skipping changing NVIDIA HDR mode."); - } - } - catch (Exception ex) - { - SharedLogger.logger.Error(ex, $"NVIDIALibrary/SetActiveConfigOverride: Exception caused while turning on custom NVIDIA HDR colour settings for display {displayId}."); - } - } - - } else { diff --git a/DisplayMagicianShared/ProfileRepository.cs b/DisplayMagicianShared/ProfileRepository.cs index 1289e1c..5d112b6 100644 --- a/DisplayMagicianShared/ProfileRepository.cs +++ b/DisplayMagicianShared/ProfileRepository.cs @@ -839,43 +839,7 @@ namespace DisplayMagicianShared // We do the actual change we were trying to do try { - - // Now we try to patch in a Windows Taskbar Stuck Rects list into the json if there isnt one - SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Looking for missing Windows Taskbar layout."); - // Create a default object (an empty list) - List taskBarStuckRectangles = new List(); - for (int i = 0; i < root.Count; i++) - { - JObject profile = (JObject)root[i]; - JArray WindowsTaskBarLayout = (JArray)profile.SelectToken("WindowsDisplayConfig.TaskBarLayout"); - if (WindowsTaskBarLayout == null) - { - JObject WindowsDisplayConfig = (JObject)profile.SelectToken("WindowsDisplayConfig"); - JArray newTaskBarLayout = JArray.FromObject(taskBarStuckRectangles); - WindowsDisplayConfig.Add("TaskBarLayout",newTaskBarLayout); - changedJson = true; - SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Patched missing Windows TaskBarLayout in profile {profile.SelectToken("Name")} (index {i})."); - } - } - - // Now we try to patch in a Windows Taskbar Settings list into the json if there isnt one - SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Looking for missing Windows Taskbar settings."); - // Create a default object using whatever the taskbar settings are right now - // (We're assuming the user keeps these settings standard) - TaskBarSettings taskBarSettings = TaskBarSettings.GetCurrent(); - for (int i = 0; i < root.Count; i++) - { - JObject profile = (JObject)root[i]; - JObject WindowsTaskBarSettings = (JObject)profile.SelectToken("WindowsDisplayConfig.TaskBarSettings"); - if (WindowsTaskBarSettings == null) - { - JObject WindowsDisplayConfig = (JObject)profile.SelectToken("WindowsDisplayConfig"); - JObject newTaskBarSettings = JObject.FromObject(taskBarSettings); - WindowsDisplayConfig.Add("TaskBarSettings", newTaskBarSettings); - changedJson = true; - SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Patched missing Windows TaskBarSettings in profile {profile.SelectToken("Name")} (index {i})."); - } - } + // Nothing to patch at the moment! } catch (JsonReaderException ex) { diff --git a/DisplayMagicianShared/Utils.cs b/DisplayMagicianShared/Utils.cs index d382088..57d2937 100644 --- a/DisplayMagicianShared/Utils.cs +++ b/DisplayMagicianShared/Utils.cs @@ -1,6 +1,7 @@ using Microsoft.Win32; using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -8,26 +9,259 @@ using System.Threading.Tasks; namespace DisplayMagicianShared { - class Utils + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)] + public struct WINDOWPOS { - [StructLayout(LayoutKind.Sequential)] - public struct RECT + public IntPtr hWnd; + public IntPtr hwndInsertAfter; + public int x; + public int y; + public int cx; + public int cy; + public SET_WINDOW_POSITION_FLAGS flags; + + // Returns the WINDOWPOS structure pointed to by the lParam parameter + // of a WM_WINDOWPOSCHANGING or WM_WINDOWPOSCHANGED message. + public static WINDOWPOS FromMessage(IntPtr lParam) { - public int left; - public int top; - public int right; - public int bottom; + // Marshal the lParam parameter to an WINDOWPOS structure, + // and return the new structure + return (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS)); } - [Flags] - public enum SendMessageTimeoutFlag : uint + // Replaces the original WINDOWPOS structure pointed to by the lParam + // parameter of a WM_WINDOWPOSCHANGING or WM_WINDOWPSCHANGING message + // with this one, so that the native window will be able to see any + // changes that we have made to its values. + public void UpdateMessage(IntPtr lParam) { - SMTO_NORMAL = 0x0, - SMTO_BLOCK = 0x1, - SMTO_ABORTIFHUNG = 0x2, - SMTO_NOTIMEOUTIFNOTHUNG = 0x8, - SMTO_ERRORONEXIT = 0x20 + // Marshal this updated structure back to lParam so the native + // window can respond to our changes. + // The old structure that it points to should be deleted, too. + Marshal.StructureToPtr(this, lParam, true); } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)] + public struct RECT + { + public Int32 left; + public Int32 top; + public Int32 right; + public Int32 bottom; + } + + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 8)] + public struct APPBARDATA + { + public int cbSize; + public IntPtr hWnd; + public uint uCallbackMessage; + public ABEDGE uEdge; + public RECT rc; + public int lParam; + } + + + + /// + /// The MONITORINFOEX structure contains information about a display monitor. + /// The GetMonitorInfo function stores information into a MONITORINFOEX structure or a MONITORINFO structure. + /// The MONITORINFOEX structure is a superset of the MONITORINFO structure. The MONITORINFOEX structure adds a string member to contain a name + /// for the display monitor. + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)] + public struct MONITORINFOEX + { + /// + /// The size, in bytes, of the structure. Set this member to sizeof(MONITORINFOEX) (72) before calling the GetMonitorInfo function. + /// Doing so lets the function determine the type of structure you are passing to it. + /// + public UInt32 cbSize; + + /// + /// A RECT structure that specifies the display monitor rectangle, expressed in virtual-screen coordinates. + /// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values. + /// + public RECT rcMonitor; + + /// + /// A RECT structure that specifies the work area rectangle of the display monitor that can be used by applications, + /// expressed in virtual-screen coordinates. Windows uses this rectangle to maximize an application on the monitor. + /// The rest of the area in rcMonitor contains system windows such as the task bar and side bars. + /// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values. + /// + public RECT rcWork; + + /// + /// The attributes of the display monitor. + /// + /// This member can be the following value: + /// 1 : MONITORINFOF_PRIMARY + /// + public UInt32 dwFlags; + + /// + /// A string that specifies the device name of the monitor being used. Most applications have no use for a display monitor name, + /// and so can save some bytes by using a MONITORINFO structure. + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = Utils.CCHDEVICENAME)] + public string szDevice; + + /*public void Init() + { + this.cbSize = 40 + 2 * Utils.CCHDEVICENAME; + this.DeviceName = string.Empty; + }*/ + } + + + /// + /// The MONITORINFO structure contains information about a display monitor. + /// The GetMonitorInfo function stores information into a MONITORINFOEX structure or a MONITORINFO structure. + /// The MONITORINFOEX structure is a superset of the MONITORINFO structure. The MONITORINFOEX structure adds a string member to contain a name + /// for the display monitor. + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)] + public struct MONITORINFO + { + /// + /// The size, in bytes, of the structure. Set this member to sizeof(MONITORINFOEX) (72) before calling the GetMonitorInfo function. + /// Doing so lets the function determine the type of structure you are passing to it. + /// + public UInt32 cbSize; + + /// + /// A RECT structure that specifies the display monitor rectangle, expressed in virtual-screen coordinates. + /// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values. + /// + public RECT rcMonitor; + + /// + /// A RECT structure that specifies the work area rectangle of the display monitor that can be used by applications, + /// expressed in virtual-screen coordinates. Windows uses this rectangle to maximize an application on the monitor. + /// The rest of the area in rcMonitor contains system windows such as the task bar and side bars. + /// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values. + /// + public RECT rcWork; + + /// + /// The attributes of the display monitor. + /// + /// This member can be the following value: + /// 1 : MONITORINFOF_PRIMARY + /// + public UInt32 dwFlags; + + /*public void Init() + { + this.cbSize = 40 + 2 * Utils.CCHDEVICENAME; + this.DeviceName = string.Empty; + }*/ + } + + + [Flags] + public enum SET_WINDOW_POSITION_FLAGS : UInt32 + { + SWP_ASYNCWINDOWPOS = 0x4000, + SWP_DEFERERASE = 0x2000, + SWP_DRAWFRAME = 0x0020, + SWP_FRAMECHANGED = 0x0020, + SWP_HIDEWINDOW = 0x0080, + SWP_NOACTIVATE = 0x0010, + SWP_NOCOPYBITS = 0x0100, + SWP_NOMOVE = 0x0002, + SWP_NOOWNERZORDER = 0x0200, + SWP_NOREDRAW = 0x0008, + SWP_NOREPOSITION = 0x0200, + SWP_NOSENDCHANGING = 0x0400, + SWP_NOSIZE = 0x0001, + SWP_NOZORDER = 0x0004, + SWP_SHOWWINDOW = 0x0040, + } + + public enum SET_WINDOW_POSITION_ZORDER : Int32 + { + HWND_TOP = 0, + HWND_BOTTOM = 1, + HWND_TOPMOST = -1, + HWND_NOTOPMOST = -2, + } + + + [Flags] + public enum SendMessageTimeoutFlag : uint + { + SMTO_NORMAL = 0x0, + SMTO_BLOCK = 0x1, + SMTO_ABORTIFHUNG = 0x2, + SMTO_NOTIMEOUTIFNOTHUNG = 0x8, + SMTO_ERRORONEXIT = 0x20 + } + + public enum ABEDGE : UInt32 + { + ABE_LEFT = 0x0, + ABE_TOP = 0x1, + ABE_RIGHT = 0x2, + ABE_BOTTOM = 0x3, + } + + [Flags] + public enum MOUSEKEYS : UInt32 + { + MK_LBUTTON = 0x1, + MK_RBUTTON = 0x2, + MK_SHIFT = 0x4, + MK_CONTROL = 0x8, + MK_MBUTTON = 0x10, + MK_XBUTTON1 = 0x20, + MK_XBUTTON2 = 0x40, + } + + + enum SYSCOMMAND : int + { + SC_SIZE = 0xF000, + SC_MOVE = 0xF010, + SC_MINIMIZE = 0xF020, + SC_MAXIMIZE = 0xF030, + SC_NEXTWINDOW = 0xF040, + SC_PREVWINDOW = 0xF050, + SC_CLOSE = 0xF060, + SC_VSCROLL = 0xF070, + SC_HSCROLL = 0xF080, + SC_MOUSEMENU = 0xF090, + SC_KEYMENU = 0xF100, + SC_ARRANGE = 0xF110, + SC_RESTORE = 0xF120, + SC_TASKLIST = 0xF130, + SC_SCREENSAVE = 0xF140, + SC_HOTKEY = 0xF150, + //#if(WINVER >= 0x0400) //Win95 + SC_DEFAULT = 0xF160, + SC_MONITORPOWER = 0xF170, + SC_CONTEXTHELP = 0xF180, + SC_SEPARATOR = 0xF00F, + //#endif /* WINVER >= 0x0400 */ + + //#if(WINVER >= 0x0600) //Vista + SCF_ISSECURE = 0x00000001, + //#endif /* WINVER >= 0x0600 */ + + /* + * Obsolete names + */ + SC_ICON = SC_MINIMIZE, + SC_ZOOM = SC_MAXIMIZE, + } + + + class Utils + { + #region enum HChangeNotifyEventID /// @@ -298,19 +532,43 @@ namespace DisplayMagicianShared [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern int SendMessage(IntPtr hWnd, uint wMsg, IntPtr wParam, IntPtr lParam); - + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam); + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, Int16 wParam, Int16 lParam); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumProc lpfnEnum, IntPtr dwData); + [DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern void SHChangeNotify(HChangeNotifyEventID wEventId, HChangeNotifyFlags uFlags, IntPtr dwItem1, IntPtr dwItem2); + [DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern int SHAppBarMessage(uint dwMessage, ref APPBARDATA pData); + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); @@ -318,6 +576,44 @@ namespace DisplayMagicianShared [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetWindowPos(IntPtr hWnd, SET_WINDOW_POSITION_ZORDER hWndInsertAfter, int x, int y, int cx, int cy, SET_WINDOW_POSITION_FLAGS uFlags); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern int ShowWindow(IntPtr hwnd, int command); + + [DllImport("user32.dll", SetLastError = true)] + static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong); + + + /// + /// The MoveWindow function changes the position and dimensions of the specified window. For a top-level window, the + /// position and dimensions are relative to the upper-left corner of the screen. For a child window, they are relative + /// to the upper-left corner of the parent window's client area. + /// + /// Go to https://msdn.microsoft.com/en-us/library/windows/desktop/ms633534%28v=vs.85%29.aspx for more + /// information + /// + /// + /// C++ ( hWnd [in]. Type: HWND )
Handle to the window. + /// C++ ( X [in]. Type: int )
Specifies the new position of the left side of the window. + /// C++ ( Y [in]. Type: int )
Specifies the new position of the top of the window. + /// C++ ( nWidth [in]. Type: int )
Specifies the new width of the window. + /// C++ ( nHeight [in]. Type: int )
Specifies the new height of the window. + /// + /// C++ ( bRepaint [in]. Type: bool )
Specifies whether the window is to be repainted. If this + /// parameter is TRUE, the window receives a message. If the parameter is FALSE, no repainting of any kind occurs. This + /// applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the + /// parent window uncovered as a result of moving a child window. + /// + /// + /// If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. + ///
To get extended error information, call GetLastError. + ///
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); + public static bool IsWindows11() { var reg = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); @@ -328,11 +624,41 @@ namespace DisplayMagicianShared return currentBuild >= 22000; } - public static int MakeLParam(int p, int p_2) + public static int MakeLParam(int p, int p_2) { return ((p_2 << 16) | (p & 0xFFFF)); } + internal delegate bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData); + + public static List EnumMonitors() + { + List monitors = new List(); + EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, + delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) + { + MONITORINFOEX mi = new MONITORINFOEX(); + mi.cbSize = (uint)Marshal.SizeOf(mi); + bool success = GetMonitorInfo(hMonitor, ref mi); + if (success) + { + monitors.Add(mi); + } + return true; + }, IntPtr.Zero); + return monitors; + } + + private static bool MonitorEnumCallBack(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) + { + MONITORINFOEX mon_info = new MONITORINFOEX(); + mon_info.cbSize = (UInt32)Marshal.SizeOf(typeof(MONITORINFOEX)); + //mon_info.szDevice = new char[Utils.CCHDEVICENAME]; + GetMonitorInfo(hMonitor, ref mon_info); + ///Monitor info is stored in 'mon_info' + return true; + } + /*public static bool RefreshNotificationTray() { Utils.SHChangeNotify(Utils.HChangeNotifyEventID.SHCNE_ASSOCCHANGED, Utils.HChangeNotifyFlags.SHCNF_IDLIST, IntPtr.Zero, IntPtr.Zero); @@ -344,13 +670,84 @@ namespace DisplayMagicianShared return true; }*/ + public static Point PointFromLParam(IntPtr lParam) + { + return new Point((int)(lParam) & 0xFFFF, ((int)(lParam) >> 16) & 0xFFFF); + } + + public static IntPtr LParamFromPoint(Point point) + { + return (IntPtr)((point.Y << 16) | (point.X & 0xFFFF)); + } + + public static IntPtr LParamFromPoint(int x, int y) + { + return (IntPtr)((y << 16) | (x & 0xFFFF)); + } + + public const int NULL = 0; public const int HWND_BROADCAST = 0xffff; + public const int WM_ENTERSIZEMOVE = 0x0231; + public const int WM_EXITSIZEMOVE = 0x0232; + public const int WM_WINDOWPOSCHANGING = 0x0046; + public const int WM_WINDOWPOSCHANGED = 0x0047; + public const int WM_SYSCOMMAND = 0x112; + public const int WM_NOTIFY = 0xA005; public const int WM_SETTINGCHANGE = 0x001a; + public const int WM_THEMECHANGED = 0x031a; public const int WM_MOUSEMOVE = 0x0200; - public const int SPI_SETWORKAREA = 0x002F; + public const int SPI_SETWORKAREA = 0x002F; + public const int SHELLHOOK = 0xC028; public const int WM_USER_REFRESHTASKBAR = 0x05CA; + public const int WM_USER_451 = 0x05C3; + public const int WM_USER_440 = 0x05B8; + public const int WM_USER_336 = 0x0550; + public const int WM_USER_92 = 0x045C; + public const int WM_USER_7 = 0x0407; + public const int WM_USER_1 = 0x0401; + public const int WM_USER_100 = 0x0464; + public const int WM_USER_13 = 0x040D; public const int wParam_SHELLTRAY = 0x00000006; + + public const int ABM_NEW = 0x00000000; + public const int ABM_REMOVE = 0x00000001; + public const int ABM_QUERYPOS = 0x00000002; + public const int ABM_SETPOS = 0x00000003; + public const int ABM_GETSTATE = 0x00000004; + public const int ABM_GETTASKBARPOS = 0x00000005; + public const int ABM_ACTIVATE = 0x00000006; // lParam == TRUE/FALSE means activate/deactivate + public const int ABM_GETAUTOHIDEBAR = 0x00000007; + public const int ABM_SETAUTOHIDEBAR = 0x00000008; // this can fail at any time. MUST check the result + // lParam = TRUE/FALSE Set/Unset + // uEdge = what edge + public const int ABM_WINDOWPOSCHANGED = 0x0000009; + public const int ABM_SETSTATE = 0x0000000a; + + // these are put in the wparam of callback messages + public const int ABN_STATECHANGE = 0x0000000; + public const int ABN_POSCHANGED = 0x0000001; + public const int ABN_FULLSCREENAPP = 0x0000002; + public const int ABN_WINDOWARRANGE = 0x0000003; // lParam == TRUE means hide + + // flags for get state + public const int ABS_AUTOHIDE = 0x0000001; + public const int ABS_ALWAYSONTOP = 0x0000002; + + public const int ABE_LEFT = 0; + public const int ABE_TOP = 1; + public const int ABE_RIGHT = 2; + public const int ABE_BOTTOM = 3; + + public const int MONITOR_DEFAULTTONULL = 0; + public const int MONITOR_DEFAULTTOPRIMARY = 1; + public const int MONITOR_DEFAULTTONEAREST = 2; + + // size of a device name string + public const int CCHDEVICENAME = 32; + public const uint MONITORINFOF_PRIMARY = 1; + + } diff --git a/DisplayMagicianShared/Windows/CCD.cs b/DisplayMagicianShared/Windows/CCD.cs index aa114d6..1320a22 100644 --- a/DisplayMagicianShared/Windows/CCD.cs +++ b/DisplayMagicianShared/Windows/CCD.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; namespace DisplayMagicianShared.Windows { - public enum WIN32STATUS : uint + public enum WIN32STATUS : UInt32 { ERROR_SUCCESS = 0, ERROR_ACCESS_DENIED = 5, @@ -19,24 +19,29 @@ namespace DisplayMagicianShared.Windows ERROR_BAD_CONFIGURATION = 1610, } - public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : uint + public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : UInt32 { Zero = 0, - 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, + DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1, // Specifies the source name of the display device. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns the source name in the DISPLAYCONFIG_SOURCE_DEVICE_NAME structure. + DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2, // Specifies information about the monitor. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns info about the monitor in the DISPLAYCONFIG_TARGET_DEVICE_NAME structure. + DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3, // Specifies information about the preferred mode of a monitor. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns info about the preferred mode of a monitor in the DISPLAYCONFIG_TARGET_PREFERRED_MODE structure. + DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4, // Specifies the graphics adapter name. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns the adapter name in the DISPLAYCONFIG_ADAPTER_NAME structure. + DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5, // Specifies how to set the monitor. If the DisplayConfigSetDeviceInfo function is successful, DisplayConfigSetDeviceInfo uses info in the DISPLAYCONFIG_SET_TARGET_PERSISTENCE structure to force the output in a boot-persistent manner. + DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6, // Specifies how to set the base output technology for a given target ID. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns base output technology info in the DISPLAYCONFIG_TARGET_BASE_TYPE structure. + // Supported by WDDM 1.3 and later user-mode display drivers running on Windows 8.1 and later. + DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7, // Specifies the state of virtual mode support. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns virtual mode support information in the DISPLAYCONFIG_SUPPORT_VIRTUAL_RESOLUTION structure. Supported starting in Windows 10. + DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8, // Specifies how to set the state of virtual mode support. If the DisplayConfigSetDeviceInfo function is successful, DisplayConfigSetDeviceInfo uses info in the DISPLAYCONFIG_SUPPORT_VIRTUAL_RESOLUTION structure to change the state of virtual mode support. Supported starting in Windows 10. + DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9, // Specifies information about the state of the HDR Color for a display + DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10, // Enables or disables the HDR Color for a display + DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11, // Specifies the current SDR white level for an HDR monitor. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo return SDR white level info in the DISPLAYCONFIG_SDR_WHITE_LEVEL structure. + // Supported starting in Windows�10 Fall Creators Update (Version 1709). + DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_SPECIALIZATION = 12, + DISPLAYCONFIG_DEVICE_INFO_SET_MONITOR_SPECIALIZATION = 13, + DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF // Only here to } [Flags] - public enum DISPLAYCONFIG_COLOR_ENCODING : uint + public enum DISPLAYCONFIG_COLOR_ENCODING : UInt32 { DISPLAYCONFIG_COLOR_ENCODING_RGB = 0, DISPLAYCONFIG_COLOR_ENCODING_YCBCR444 = 1, @@ -46,7 +51,7 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum DISPLAYCONFIG_SCALING : uint + public enum DISPLAYCONFIG_SCALING : UInt32 { Zero = 0, DISPLAYCONFIG_SCALING_IDENTITY = 1, @@ -59,7 +64,7 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum DISPLAYCONFIG_ROTATION : uint + public enum DISPLAYCONFIG_ROTATION : UInt32 { Zero = 0, DISPLAYCONFIG_ROTATION_IDENTITY = 1, @@ -70,7 +75,7 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY : uint + public enum DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY : UInt32 { DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER = 4294967295, // - 1 DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15 = 0, @@ -95,7 +100,7 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum DISPLAYCONFIG_TOPOLOGY_ID : uint + public enum DISPLAYCONFIG_TOPOLOGY_ID : UInt32 { Zero = 0x0, DISPLAYCONFIG_TOPOLOGY_INTERNAL = 0x00000001, @@ -106,7 +111,7 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum DISPLAYCONFIG_PATH_FLAGS : uint + public enum DISPLAYCONFIG_PATH_FLAGS : UInt32 { Zero = 0x0, DISPLAYCONFIG_PATH_ACTIVE = 0x00000001, @@ -115,14 +120,14 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum DISPLAYCONFIG_SOURCE_FLAGS : uint + public enum DISPLAYCONFIG_SOURCE_FLAGS : UInt32 { Zero = 0x0, DISPLAYCONFIG_SOURCE_IN_USE = 0x00000001, } [Flags] - public enum DISPLAYCONFIG_TARGET_FLAGS : uint + public enum DISPLAYCONFIG_TARGET_FLAGS : UInt32 { Zero = 0x0, DISPLAYCONFIG_TARGET_IN_USE = 0x00000001, @@ -134,7 +139,7 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum QDC : uint + public enum QDC : UInt32 { Zero = 0x0, // Get all paths @@ -154,7 +159,7 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum SDC : uint + public enum SDC : UInt32 { Zero = 0x0, SDC_TOPOLOGY_public = 0x00000001, @@ -194,7 +199,7 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum DISPLAYCONFIG_SCANLINE_ORDERING : uint + public enum DISPLAYCONFIG_SCANLINE_ORDERING : UInt32 { DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED = 0, DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE = 1, @@ -205,7 +210,7 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum DISPLAYCONFIG_PIXELFORMAT : uint + public enum DISPLAYCONFIG_PIXELFORMAT : UInt32 { Zero = 0x0, DISPLAYCONFIG_PIXELFORMAT_8BPP = 1, @@ -217,7 +222,7 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum DISPLAYCONFIG_MODE_INFO_TYPE : uint + public enum DISPLAYCONFIG_MODE_INFO_TYPE : UInt32 { Zero = 0x0, DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1, @@ -227,7 +232,7 @@ namespace DisplayMagicianShared.Windows } [Flags] - public enum D3D_VIDEO_SIGNAL_STANDARD : uint + public enum D3D_VIDEO_SIGNAL_STANDARD : UInt32 { Uninitialized = 0, VesaDmt = 1, diff --git a/DisplayMagicianShared/Windows/TaskBarLayout.cs b/DisplayMagicianShared/Windows/TaskBarLayout.cs new file mode 100644 index 0000000..25b278d --- /dev/null +++ b/DisplayMagicianShared/Windows/TaskBarLayout.cs @@ -0,0 +1,861 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using DisplayMagicianShared; +using Microsoft.Win32; +using Newtonsoft.Json; + +// This file is based on Soroush Falahati's amazing HeliosDisplayManagement software +// available at https://github.com/falahati/HeliosDisplayManagement + +// Substantial modifications made by Terry MacDonald 2022 onwards + +namespace DisplayMagicianShared.Windows +{ + public class TaskBarLayout + { + + public enum TaskBarEdge : UInt32 + { + Left = 0, + Top = 1, + Right = 2, + Bottom = 3 + } + + [Flags] + public enum TaskBarOptions : UInt32 + { + None = 0, + AutoHide = 1 << 0, + KeepOnTop = 1 << 1, + UseSmallIcons = 1 << 2, + HideClock = 1 << 3, + HideVolume = 1 << 4, + HideNetwork = 1 << 5, + HidePower = 1 << 6, + WindowPreview = 1 << 7, + Unknown1 = 1 << 8, + Unknown2 = 1 << 9, + HideActionCenter = 1 << 10, + Unknown3 = 1 << 11, + HideLocation = 1 << 12, + HideLanguageBar = 1 << 13 + } + + private const string MainDisplayAddress = + "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects{0:D}"; + + private const string MultiDisplayAddress = + "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MMStuckRects{0:D}"; + + /*private static readonly Dictionary Headers = new Dictionary + { + {2, new byte[] {0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}}, + {3, new byte[] {0x30, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF}} + }; +*/ + public bool ReadFromRegistry(string regKeyValue) + { + bool MMStuckRectVerFound = false; + // Check if key exists + int version = 3; + string address = string.Format(MultiDisplayAddress, version); + if (Registry.CurrentUser.OpenSubKey(address) != null) + { + MMStuckRectVerFound = true; + SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found MMStuckRect3 registry key! {address}"); + } + else + { + // If it's not version 3, then try version 2 + version = 2; + address = string.Format(MultiDisplayAddress, version); + if (Registry.CurrentUser.OpenSubKey(address) != null) + { + MMStuckRectVerFound = true; + SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found MMStuckRect2 registry key! {address}"); + } + else + { + // It's not v2 or v3, so it must be a single display + MMStuckRectVerFound = false; + SharedLogger.logger.Warn($"TaskBarStuckRectangle/TaskBarStuckRectangle: Couldn't find an MMStuckRect2 or MMStuckRect3 registry key! Going to test if it is a single display only."); + } + } + + if (MMStuckRectVerFound) + { + // Check if value exists + if (version >= 2 && version <= 3) + { + using (var key = Registry.CurrentUser.OpenSubKey( + address, + RegistryKeyPermissionCheck.ReadSubTree)) + { + var binary = key?.GetValue(regKeyValue) as byte[]; + if (binary?.Length > 0) + { + MainScreen = false; + RegKeyValue = regKeyValue; + Binary = binary; + Version = version; + + // Extract the values from the binary byte field + PopulateFieldsFromBinary(); + + SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size."); + + // If we get here then we're done and don't need to continue with the rest of the code. + return true; + } + else + { + SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen."); + } + } + } + else + { + SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: A MMStuckRect entry was found, but the version of the field is wrong."); + } + } + else + { + SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: A MMStuckRect entry was NOT found. We will try to find the object in the StuckRect registry key instead"); + } + + bool StuckRectVerFound = false; + // Check if string exists + version = 3; + address = string.Format(MainDisplayAddress, version); + if (Registry.CurrentUser.OpenSubKey(address) != null) + { + StuckRectVerFound = true; + SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found StuckRect3 single display registry key! {address}"); + } + else + { + // If it's not version 3, then try version 2 + version = 2; + address = string.Format(MainDisplayAddress, version); + if (Registry.CurrentUser.OpenSubKey(address) != null) + { + StuckRectVerFound = true; + SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found StuckRect2 single display registry key! {address}"); + } + else + { + SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: Couldn't find an single display StuckRect2 or StuckRect3 registry key! So we have to just return after doing nothing as there is nothing we can do."); + return false; + } + } + + if (StuckRectVerFound) + { + // Check if value exists + if (version >= 2 && version <= 3) + { + using (var key = Registry.CurrentUser.OpenSubKey( + address, + RegistryKeyPermissionCheck.ReadSubTree)) + { + var binary = key?.GetValue(regKeyValue) as byte[]; + if (binary?.Length > 0) + { + MainScreen = true; + RegKeyValue = regKeyValue; + Binary = binary; + Version = version; + + // Extract the values from the binary byte field + PopulateFieldsFromBinary(); + + SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size."); + return true; + } + else + { + SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen."); + return false; + } + } + } + else + { + SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: A StuckRect entry was found, but the version of the field is wrong."); + return false; + } + } + else + { + SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: A StuckRect entry was NOT found. This means we're unable to get the taskbar location, an unable to return a sensible TaskBarStuckRectangle object."); + return false; + } + + } + + public TaskBarLayout() + { + } + + public byte[] Binary { get; set; } + + public string RegKeyValue { get; set; } + + public bool MainScreen { get; set; } + + public UInt32 DPI { get; set; } + + public TaskBarEdge Edge { get; set; } + + public Rectangle TaskBarLocation { get; set; } + + public Rectangle StartMenuLocation { get; set; } + + public Rectangle MonitorLocation { get; set; } + + public Size MinSize { get; set; } + + public TaskBarOptions Options { get; set; } + + public uint Rows { get; set; } + + public int Version { get; set; } + + public override bool Equals(object obj) => obj is TaskBarLayout other && this.Equals(other); + public bool Equals(TaskBarLayout other) + { + return Version == other.Version && + RegKeyValue == other.RegKeyValue && + MainScreen == other.MainScreen && + DPI == other.DPI && + Edge == other.Edge && + TaskBarLocation == other.TaskBarLocation && + MinSize == other.MinSize && + Options == other.Options && + Rows == other.Rows; + } + + public override int GetHashCode() + { + return (Version, MainScreen, RegKeyValue, DPI, Edge, TaskBarLocation, MinSize, Options, Rows).GetHashCode(); + } + public static bool operator ==(TaskBarLayout lhs, TaskBarLayout rhs) => lhs.Equals(rhs); + + public static bool operator !=(TaskBarLayout lhs, TaskBarLayout rhs) => !(lhs == rhs); + + static bool Xor(byte[] a, byte[] b) + + { + + int x = a.Length ^ b.Length; + + for (int i = 0; i < a.Length && i < b.Length; ++i) + + { + + x |= a[i] ^ b[i]; + + } + + return x == 0; + + } + + private bool PopulateFieldsFromBinary() + { + // Now we decipher the binary properties features to populate the stuckrectangle + // DPI + if (Binary.Length < 44) + { + DPI = 0; + } + else + { + DPI = BitConverter.ToUInt32(Binary, 40); + } + // Edge + if (Binary.Length < 16) + { + Edge = TaskBarEdge.Bottom; + } + else + { + Edge = (TaskBarEdge)BitConverter.ToUInt32(Binary, 12); + } + // Location + if (Binary.Length < 40) + { + TaskBarLocation = Rectangle.Empty; + } + else + { + var left = BitConverter.ToInt32(Binary, 24); + var top = BitConverter.ToInt32(Binary, 28); + var right = BitConverter.ToInt32(Binary, 32); + var bottom = BitConverter.ToInt32(Binary, 36); + + TaskBarLocation = Rectangle.FromLTRB(left, top, right, bottom); + } + // MinSize + if (Binary.Length < 24) + { + MinSize = Size.Empty; + } + else + { + var width = BitConverter.ToInt32(Binary, 16); + var height = BitConverter.ToInt32(Binary, 20); + + MinSize = new Size(width, height); + } + // Options + if (Binary.Length < 12) + { + Options = 0; + } + else + { + Options = (TaskBarOptions)BitConverter.ToUInt32(Binary, 8); + } + // Rows + if (Binary.Length < 48) + { + Rows = 1; + } + else + { + Rows = BitConverter.ToUInt32(Binary, 44); + } + + SharedLogger.logger.Trace($"TaskBarStuckRectangle/PopulateFieldsFromBinary: Grabbed the following settings for {RegKeyValue} from the registry: DPI = {DPI}, Edge = {Edge}, Location = ({TaskBarLocation.X},{TaskBarLocation.Y}), MinSize = {TaskBarLocation.Width}x{TaskBarLocation.Height}, Options = {Options}, Rows = {Rows}."); + + return true; + } + + public bool PopulateBinaryFromFields() + { + // Set the DPI + if (Binary.Length < 44) + { + DPI = 0; + var bytes = BitConverter.GetBytes(DPI); + Array.Copy(bytes, 0, Binary, 40, 4); + } + else + { + var bytes = BitConverter.GetBytes(DPI); + Array.Copy(bytes, 0, Binary, 40, 4); + } + // Edge + if (Binary.Length < 16) + { + Edge = TaskBarEdge.Bottom; + var bytes = BitConverter.GetBytes((uint)Edge); + Array.Copy(bytes, 0, Binary, 12, 4); + } + else + { + var bytes = BitConverter.GetBytes((uint)Edge); + Array.Copy(bytes, 0, Binary, 12, 4); + } + // Location + if (Binary.Length < 40) + { + var bytes = BitConverter.GetBytes(0); + Array.Copy(bytes, 0, Binary, 24, 4); + + bytes = BitConverter.GetBytes(0); + Array.Copy(bytes, 0, Binary, 28, 4); + + bytes = BitConverter.GetBytes(0); + Array.Copy(bytes, 0, Binary, 32, 4); + + bytes = BitConverter.GetBytes(0); + Array.Copy(bytes, 0, Binary, 36, 4); + } + else + { + var bytes = BitConverter.GetBytes(TaskBarLocation.Left); + Array.Copy(bytes, 0, Binary, 24, 4); + + bytes = BitConverter.GetBytes(TaskBarLocation.Top); + Array.Copy(bytes, 0, Binary, 28, 4); + + bytes = BitConverter.GetBytes(TaskBarLocation.Right); + Array.Copy(bytes, 0, Binary, 32, 4); + + bytes = BitConverter.GetBytes(TaskBarLocation.Bottom); + Array.Copy(bytes, 0, Binary, 36, 4); + } + // MinSize + if (Binary.Length < 24) + { + var bytes = BitConverter.GetBytes(0); + Array.Copy(bytes, 0, Binary, 16, 4); + + bytes = BitConverter.GetBytes(0); + Array.Copy(bytes, 0, Binary, 20, 4); + } + else + { + var bytes = BitConverter.GetBytes(MinSize.Width); + Array.Copy(bytes, 0, Binary, 16, 4); + + bytes = BitConverter.GetBytes(MinSize.Height); + Array.Copy(bytes, 0, Binary, 20, 4); + } + // Options + if (Binary.Length < 12) + { + var bytes = BitConverter.GetBytes((uint)0); + Array.Copy(bytes, 0, Binary, 8, 4); + } + else + { + var bytes = BitConverter.GetBytes((uint)Options); + Array.Copy(bytes, 0, Binary, 8, 4); + } + // Rows + if (Binary.Length < 48) + { + var bytes = BitConverter.GetBytes(1); + Array.Copy(bytes, 0, Binary, 44, 4); + } + else + { + var bytes = BitConverter.GetBytes(Rows); + Array.Copy(bytes, 0, Binary, 44, 4); + } + + SharedLogger.logger.Trace($"TaskBarStuckRectangle/PopulateBinaryFromFields: Set the following settings for {RegKeyValue} into registry: DPI = {DPI}, Edge = {Edge}, Location = ({TaskBarLocation.X},{TaskBarLocation.Y}), MinSize = {TaskBarLocation.Width}x{TaskBarLocation.Height}, Options = {Options}, Rows = {Rows}."); + + return true; + } + + public bool WriteToRegistry() + { + // Update the binary with the current settings from the object + //PopulateBinaryFromFields(); + + // Write the binary field to registry + string address; + if (MainScreen && RegKeyValue.Equals("Settings")) + { + address = string.Format(MainDisplayAddress, Version); + // Set the Main Screen + try + { + using (var key = Registry.CurrentUser.OpenSubKey( + address, + RegistryKeyPermissionCheck.ReadWriteSubTree)) + { + key.SetValue(RegKeyValue, Binary); + SharedLogger.logger.Trace($"TaskBarStuckRectangle/Apply: Successfully applied TaskBarStuckRectangle registry settings for the {RegKeyValue} Screen in {address}!"); + } + } + catch (Exception ex) + { + SharedLogger.logger.Error(ex, $"TaskBarStuckRectangle/GetCurrent: Unable to set the {RegKeyValue} TaskBarStuckRectangle registry settings in {address} due to an exception!"); + } + } + else + { + address = string.Format(MultiDisplayAddress, Version); + // Grab the main screen taskbar placement + try + { + using (var key = Registry.CurrentUser.OpenSubKey( + address, + RegistryKeyPermissionCheck.ReadWriteSubTree)) + { + key.SetValue(RegKeyValue, Binary); + SharedLogger.logger.Trace($"TaskBarStuckRectangle/WriteToRegistry: Successfully applied TaskBarStuckRectangle registry settings for the {RegKeyValue} Screen in {address}!"); + } + } + catch (Exception ex) + { + SharedLogger.logger.Error(ex, $"TaskBarStuckRectangle/WriteToRegistry: Unable to set the {RegKeyValue} TaskBarStuckRectangle registry settings in {address} due to an exception!"); + } + } + + return true; + } + + public static Dictionary GetAllCurrentTaskBarLayouts(Dictionary> displaySources) + { + Dictionary taskBarStuckRectangles = new Dictionary(); + int state; + + APPBARDATA abd = new APPBARDATA(); + + // Sleep delay just for testing so I can get the position of the Start Menu + // Note for future me, the Start menu window is moved around the desktop to be next to the start button that is pressed by the user. + // e.g. if you have two screens, and you click the right most start button, the Start menu window is moved to be the same as the WorkRect of the + // monitor that the start button is on. + //System.Threading.Thread.Sleep(5000); + + // Firstly try to get the position of the main screen and main start menu + try + { + // Figure out which monitor this taskbar is on + IntPtr mainTaskbarHwnd = Utils.FindWindow("Shell_TrayWnd", ""); + IntPtr mainMonitorHwnd = Utils.MonitorFromWindow(mainTaskbarHwnd, Utils.MONITOR_DEFAULTTOPRIMARY); + //IntPtr startMenuHwnd = Utils.FindWindow("Windows.UI.Core.CoreWindow", "Start"); + + //Utils.GetWindowRect(startMenuHwnd, out RECT lpRect); + + // Figure out the monitor coordinates + MONITORINFOEX monitorInfo = new MONITORINFOEX(); + monitorInfo.cbSize = (UInt32)Marshal.SizeOf(typeof(MONITORINFOEX)); + //monitorInfo.szDevice = new char[Utils.CCHDEVICENAME]; + Utils.GetMonitorInfo(mainMonitorHwnd, ref monitorInfo); + + abd.hWnd = mainTaskbarHwnd; + abd.uEdge = ABEDGE.ABE_BOTTOM; + abd.lParam = 0x1; + abd.cbSize = Marshal.SizeOf(typeof(APPBARDATA)); + + state = Utils.SHAppBarMessage(Utils.ABM_GETTASKBARPOS, ref abd); + + if (state == 1) + { + int tbWidth = Math.Abs(abd.rc.left - abd.rc.right); + int tbHeight = Math.Abs(abd.rc.top - abd.rc.bottom); + int monWidth = Math.Abs(monitorInfo.rcMonitor.left - monitorInfo.rcMonitor.right); + int monHeight = Math.Abs(monitorInfo.rcMonitor.top - monitorInfo.rcMonitor.bottom); + + TaskBarLayout tbsr = new TaskBarLayout(); + // Now we're at the point that we should be able to update the binary that we grabbed earlier when the object was created + tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath)); + tbsr.Edge = (TaskBarEdge)abd.uEdge; + tbsr.MonitorLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monWidth, monHeight); + switch (tbsr.Edge) + { + case TaskBarEdge.Left: + tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight); + break; + case TaskBarEdge.Top: + tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight); + break; + case TaskBarEdge.Right: + tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.right, monitorInfo.rcWork.top, tbWidth, tbHeight); + break; + case TaskBarEdge.Bottom: + tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.left, monitorInfo.rcWork.bottom, tbWidth, tbHeight); + break; + default: + // Default is bottom taskbar + tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.left, monitorInfo.rcWork.bottom, tbWidth, tbHeight); + break; + } + tbsr.MainScreen = true; + + // Now as a LAST step we update the Binary field just before we apply it to make sure that the correct binary settings are stored + tbsr.PopulateBinaryFromFields(); + + taskBarStuckRectangles.Add(monitorInfo.szDevice, tbsr); + + // If it's a main screen, also add a duplicate so we track the main StuckRects settings separately too + TaskBarLayout tbsrMain = new TaskBarLayout(); + tbsrMain.ReadFromRegistry("Settings"); + tbsrMain.Edge = tbsr.Edge; + tbsrMain.MonitorLocation = tbsr.MonitorLocation; + tbsrMain.TaskBarLocation = tbsr.TaskBarLocation; + tbsrMain.MainScreen = tbsr.MainScreen; + + // Now as a LAST step we update the Binary field just before we apply it to make sure that the correct binary settings are stored + tbsrMain.PopulateBinaryFromFields(); + taskBarStuckRectangles.Add("Settings", tbsrMain); + } + } + catch (Exception ex) + { + SharedLogger.logger.Error(ex, $"WinLibrary/GetAllCurrentTaskBarPositions: Exception while trying to get the maintaskbar position"); + } + + // Then go through the secondary windows and get the position of them + // Tell Windows to refresh the Other Windows Taskbars if needed + IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL; + for (int i = 0; i < 100; i++) + { + // Find the next "Shell_SecondaryTrayWnd" window + IntPtr nextTaskBarWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, lastTaskBarWindowHwnd, "Shell_SecondaryTrayWnd", null); + if (nextTaskBarWindowHwnd == (IntPtr)Utils.NULL) + { + // No more windows taskbars to notify + break; + } + + IntPtr secMonitorHwnd = Utils.MonitorFromWindow(nextTaskBarWindowHwnd, Utils.MONITOR_DEFAULTTONEAREST); + + // Figure out the monitor coordinates + MONITORINFOEX monitorInfo = new MONITORINFOEX(); + monitorInfo.cbSize = (UInt32)Marshal.SizeOf(typeof(MONITORINFOEX)); + //monitorInfo.szDevice = new char[Utils.CCHDEVICENAME]; + Utils.GetMonitorInfo(secMonitorHwnd, ref monitorInfo); + + // Figure out the position of the taskbar ourselves + int monWidth = Math.Abs(monitorInfo.rcMonitor.left - monitorInfo.rcMonitor.right); + int monHeight = Math.Abs(monitorInfo.rcMonitor.top - monitorInfo.rcMonitor.bottom); + int wrkWidth = Math.Abs(monitorInfo.rcWork.left - monitorInfo.rcWork.right); + int wrkHeight = Math.Abs(monitorInfo.rcWork.top - monitorInfo.rcWork.bottom); + int tbWidth; + int tbHeight; + + TaskBarLayout tbsr = new TaskBarLayout(); + // Now we're at the point that we should be able to update the binary that we grabbed earlier when the object was created + tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath)); + if (monWidth == wrkWidth) + { + // Taskbar on top or bottom + if (monitorInfo.rcMonitor.left == monitorInfo.rcWork.left && monitorInfo.rcMonitor.top == monitorInfo.rcWork.top) + { + // Taskbar on bottom + tbWidth = monWidth; + tbHeight = monHeight - wrkHeight; + tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcWork.bottom, tbWidth, tbHeight); + tbsr.Edge = TaskBarEdge.Bottom; + } + else if (monitorInfo.rcMonitor.right == monitorInfo.rcWork.right && monitorInfo.rcMonitor.bottom == monitorInfo.rcWork.bottom) + { + // Taskbar on top + tbWidth = monWidth; + tbHeight = monHeight - wrkHeight; + tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight); + tbsr.Edge = TaskBarEdge.Top; + } + else + { + // Invalid state + SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Taskbar position was not on a horizontal edge of a monitor!"); + } + + } + else if (monHeight == wrkHeight) + { + // Taskbar on the sides + if (monitorInfo.rcMonitor.right == monitorInfo.rcWork.right && monitorInfo.rcMonitor.bottom == monitorInfo.rcWork.bottom) + { + // Taskbar on left + tbWidth = monWidth - wrkWidth; + tbHeight = monHeight; + tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight); + tbsr.Edge = TaskBarEdge.Left; + } + else if (monitorInfo.rcMonitor.left == monitorInfo.rcWork.left && monitorInfo.rcMonitor.top == monitorInfo.rcWork.top) + { + // Taskbar on right + tbWidth = monWidth - wrkWidth; + tbHeight = monHeight; + tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.right, monitorInfo.rcMonitor.top, tbWidth, tbHeight); + tbsr.Edge = TaskBarEdge.Right; + } + else + { + // Invalid state + SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Taskbar position was not on a vertical edge of a monitor!"); + } + } + else + { + // Invalid state + SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Taskbar position was not fully along one of the monitor edges!"); + } + + tbsr.MonitorLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monWidth, monHeight); + tbsr.MainScreen = false; + + // Now as a LAST step we update the Binary field just before we apply it to make sure that the correct binary settings are stored + tbsr.PopulateBinaryFromFields(); + + if (!taskBarStuckRectangles.ContainsKey(monitorInfo.szDevice)) + { + taskBarStuckRectangles.Add(monitorInfo.szDevice, tbsr); + } + else + { + SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Skipping grabbing Taskbar position from a cloned display {monitorInfo.szDevice}"); + } + + // Prep the next taskbar window so we continue through them + lastTaskBarWindowHwnd = nextTaskBarWindowHwnd; + } + + return taskBarStuckRectangles; + } + + public bool MoveTaskBar() + { + if (RegKeyValue.Equals("Settings") && MainScreen) + { + // We only want to set the position for the main screen if it has a "Settings" entry and is a main screen + // Find the window to move + IntPtr mainTaskbarHwnd = Utils.FindWindow("Shell_TrayWnd", ""); + //IntPtr startButtonHandle = Utils.FindWindowEx(mainTaskbarHwnd, IntPtr.Zero, "Start", null); + IntPtr systemTrayNotifyHandle = Utils.FindWindowEx(mainTaskbarHwnd, IntPtr.Zero, "TrayNotifyWnd", null); + //IntPtr rebarWindowHandle = Utils.FindWindowEx(mainTaskbarHwnd, IntPtr.Zero, "ReBarWindow32", null); + //IntPtr trayDesktopShowButtonHandle = Utils.FindWindowEx(systemTrayNotifyHandle, IntPtr.Zero, "TrayShowDesktopButtonWClass", null); + + IntPtr result; + + // ===== MOVE THE MAIN TASKBAR WINDOW ===== + // Prepare the taskbar for moving + Utils.SendMessageTimeout(mainTaskbarHwnd, Utils.WM_ENTERSIZEMOVE, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result); + // Move the taskbar window + Utils.MoveWindow(mainTaskbarHwnd, TaskBarLocation.X, TaskBarLocation.Y, TaskBarLocation.Width, TaskBarLocation.Height, false); + + // ===== LOCK THE MAIN TASKBAR WINDOW BACK DOWN ===== + // Tell the taskbar we've stopped moving it now + Utils.SendMessageTimeout(mainTaskbarHwnd, Utils.WM_EXITSIZEMOVE, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result); + // Tell the taskbar it needs to update it's theme + Utils.PostMessage(mainTaskbarHwnd, Utils.WM_THEMECHANGED, IntPtr.Zero, IntPtr.Zero); + // Tell the taskbar it needs to recalculate it's work area + Utils.SendMessageTimeout(systemTrayNotifyHandle, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result); + + // We also save the taskbar position for the monitor in registry, so that Windows will actually properly update the position + // after 5 seconds (and this one will stick between reboots too!). + WriteToRegistry(); + + } + else if (MainScreen && !RegKeyValue.Equals("Settings")) + { + // If it's a main screen, but not "settings", then its the registry key only taskbar setting we need to change + // This is because hte only screen settings that matter are the StuckRect3 registry key (for the main screen) and + // all of the secondary windows + WriteToRegistry(); + } + else + { + // This is a secondary screen, so we need to set it's position + // Then go through the secondary windows and get the position of them + // Tell Windows to refresh the Other Windows Taskbars if needed + //WriteToRegistry(); + + IntPtr mainTaskbarHwnd = Utils.FindWindow("Shell_TrayWnd", ""); + + IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL; + for (int i = 0; i < 100; i++) + { + // Find the next "Shell_SecondaryTrayWnd" window + IntPtr nextTaskBarWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, lastTaskBarWindowHwnd, "Shell_SecondaryTrayWnd", null); + if (nextTaskBarWindowHwnd == (IntPtr)Utils.NULL) + { + // No more windows taskbars to notify + break; + } + + IntPtr secMonitorHwnd = Utils.MonitorFromWindow(nextTaskBarWindowHwnd, Utils.MONITOR_DEFAULTTONEAREST); + + // Figure out this monitor coordinates + MONITORINFOEX monitorInfo = new MONITORINFOEX(); + monitorInfo.cbSize = (UInt32)Marshal.SizeOf(typeof(MONITORINFOEX)); + //monitorInfo.szDevice = new char[Utils.CCHDEVICENAME]; + Utils.GetMonitorInfo(secMonitorHwnd, ref monitorInfo); + + // Figure out the position of the taskbar ourselves + int monWidth = Math.Abs(monitorInfo.rcMonitor.left - monitorInfo.rcMonitor.right); + int monHeight = Math.Abs(monitorInfo.rcMonitor.top - monitorInfo.rcMonitor.bottom); + Rectangle thisMonitorLocation = new Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monWidth, monHeight); + if (MonitorLocation.Equals(thisMonitorLocation)) + { + // This is the right monitor, so we should move the taskbar on it. + + IntPtr result; + + // ===== MOVE THE MAIN TASKBAR WINDOW ===== + // Prepare the taskbar for moving + Utils.SendMessageTimeout(nextTaskBarWindowHwnd, Utils.WM_ENTERSIZEMOVE, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result); + // Move the taskbar window + Utils.MoveWindow(nextTaskBarWindowHwnd, TaskBarLocation.X, TaskBarLocation.Y, TaskBarLocation.Width, TaskBarLocation.Height, true); + + // ===== LOCK THE MAIN TASKBAR WINDOW BACK DOWN ===== + // Tell the taskbar we've stopped moving it now + //Utils.SendMessageTimeout(nextTaskBarWindowHwnd, Utils.WM_EXITSIZEMOVE, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result); + // Tell the taskbar it needs to update it's theme + //Utils.PostMessage(nextTaskBarWindowHwnd, Utils.WM_THEMECHANGED, IntPtr.Zero, IntPtr.Zero); + // Tell the taskbar it needs to recalculate it's work area + //Utils.SendMessageTimeout(nextTaskBarWindowHwnd, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result); + + // We also save the taskbar position for the monitor in registry, so that Windows will actually properly update the position + // after 5 seconds (and this one will stick between reboots too!). + WriteToRegistry(); + + + // We then want to stop as we've found the correct taskbar to move! + break; + + } + + // Prep the next taskbar window so we continue through them + lastTaskBarWindowHwnd = nextTaskBarWindowHwnd; + } + } + + return true; + } + + + public static string GetRegKeyValueFromDevicePath(string devicePath) + { + string regKeyValue = ""; + // e.g. "\\\\?\\DISPLAY#NVS10DE#5&2b46c695&0&UID185344#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}" + string pattern = @"DISPLAY\#(.*)\#\{"; + Match match = Regex.Match(devicePath, pattern); + if (match.Success) + { + regKeyValue = match.Groups[1].Value; + SharedLogger.logger.Trace($"TaskBarLayout/GetRegKeyValueFromDevicePath: Found regKeyValue {regKeyValue } in the devicePath {devicePath }."); + } + else + { + SharedLogger.logger.Warn($"TaskBarLayout/GetRegKeyValueFromDevicePath: We were unable to figure out the regKeyValue {regKeyValue } in the devicePath {devicePath }.."); + } + return regKeyValue; + } + + public static bool ForceTaskBarRedraw(IntPtr mainToolBarHWnd) + { + // Tell Windows to refresh the Main Screen Windows Taskbar registry settings by telling Explorer to update. + Utils.SendMessage(mainToolBarHWnd, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL); + // Tell Windows to refresh the child Windows in the taskbar + IntPtr lastChildWindowHwnd = (IntPtr)Utils.NULL; + for (int i = 0; i < 100; i++) + { + // Find the next "Shell_SecondaryTrayWnd" window + IntPtr nextChildWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, mainToolBarHWnd, "", null); + if (nextChildWindowHwnd == (IntPtr)Utils.NULL) + { + // No more windows taskbars to notify + break; + } + // Send the "Shell_TrayWnd" window a WM_SETTINGCHANGE with a wParameter of SPI_SETWORKAREA + Utils.SendMessage(lastChildWindowHwnd, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL); + lastChildWindowHwnd = nextChildWindowHwnd; + } + + //IntPtr explorerToolBarHWnd = Utils.FindWindow("Shell_TrayWnd", null); + //Utils.PostMessage((IntPtr)Utils.HWND_BROADCAST, Utils.SHELLHOOK, 0x13, (int) mainToolBarHWnd); + //Utils.PostMessage((IntPtr)Utils.HWND_BROADCAST, Utils.WM_SETTINGCHANGE, (int)Utils.SPI_SETWORKAREA, (int)Utils.NULL); + /*IntPtr result; + Utils.SendMessageTimeout((IntPtr)Utils.HWND_BROADCAST, Utils.WM_USER_1, (IntPtr)Utils.NULL, (IntPtr)Utils.NULL, Utils.SendMessageTimeoutFlag.SMTO_ABORTIFHUNG, 15, out result);*/ + return true; + } + + } + + [global::System.Serializable] + public class TaskBarStuckRectangleException : Exception + { + public TaskBarStuckRectangleException() { } + public TaskBarStuckRectangleException(string message) : base(message) { } + public TaskBarStuckRectangleException(string message, Exception inner) : base(message, inner) { } + protected TaskBarStuckRectangleException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/DisplayMagicianShared/Windows/TaskBarSettings.cs b/DisplayMagicianShared/Windows/TaskBarSettings.cs deleted file mode 100644 index 9b2965b..0000000 --- a/DisplayMagicianShared/Windows/TaskBarSettings.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Win32; - -// This file is taken from Soroush Falahati's amazing HeliosDisplayManagement software -// available at https://github.com/falahati/HeliosDisplayManagement - -// Modifications made by Terry MacDonald - -namespace DisplayMagicianShared.Windows -{ - public class TaskBarSettings - { - private const string AdvancedSettingsAddress = - "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"; - - private static List WantedAdvancedSettingValues = new List - { - // Win10/11 registry keys (not all will be populated, only those that the user modified from default at least once) - "MMTaskbarEnabled", // Multiple Taskbars: 0 for show taskbar on main screen only, 1 for show taskbar on all screens - "MMTaskbarMode", // Show taskbar buttons on: 0 = all taskbars, 1 = main taskbar and where windows is open, 2 = taskbar where window is open - "MMTaskbarGlomLevel", // Buttons on other taskbars: 0 = always combine, combine when the taskbar is full, 2 = never combine - "NoTaskGrouping", // Disable all Task Grouping (overrides "TaskbarGroupSize"): 0 = enable task grouping, 1 = disable task grouping - "SearchboxTaskbarMode", // Show Search Button in Taskbar: 0 = remove search button, 1 = show search button - "ShowTaskViewButton", // Show Taskview Button in Taskbar: 0 = remove taskview button, 1 = show taskview button - "TaskbarAl", // Start Button Alignment: 0 for left, 1 for center, - "TaskbarDa", // Show Widgets button in Taskbar: 0 = remove widgets button, 1 = Show widgets button - "TaskbarGlomLevel", // Buttons on main screen: 0 = always combine, combine when the taskbar is full, 2 = never combine - "TaskbarGroupSize", // TaskBar left/right grouping by age: 0 = oldest first (default), 1 = roup by size largest first, 2 = group all with 2 or more, 3 = group all with 3 or more (see NoTaskGrouping to prevent Grouping ) - "TaskbarMn", // Show Chat Button in Taskbar: 0 = remove chat button, 1 = show chat button - "TaskbarSi", // Taskbar Size: 0 = small, 1 = medium, 2 = Large - "TaskbarSizeMove", // Lock the Taskbar (prevent resizing): 0 = taskbar size is locked, 1 = taskbar size is unlocked - "TaskbarSmallIcons", // Small Taskbar Icons: 0 = normal sized icons, 1 = small icons - "TaskbarSd", // Show Desktop Button in Taskbar: 0 for hid the show desktop button, 1 for show the Show desktop button - }; - - - public Tuple[] Options { get; set; } - - public override bool Equals(object obj) => obj is TaskBarSettings other && this.Equals(other); - public bool Equals(TaskBarSettings other) - => Options.All(a => other.Options.Any(x => x.Item1 == a.Item1 && x.Item2 == a.Item2)); - - public override int GetHashCode() - { - return (Options).GetHashCode(); - } - public static bool operator ==(TaskBarSettings lhs, TaskBarSettings rhs) => lhs.Equals(rhs); - - public static bool operator !=(TaskBarSettings lhs, TaskBarSettings rhs) => !(lhs == rhs); - - public static TaskBarSettings GetCurrent() - { - var taskBarOptions = new List>(); - - // Get modified and stored Taskbar options from the User Registry - // Note: Only the taskbar options changed from default at least once in the past will be listed in Registry - try - { - using (var key = Registry.CurrentUser.OpenSubKey( - AdvancedSettingsAddress, - RegistryKeyPermissionCheck.ReadSubTree)) - { - if (key != null) - { - foreach (var valueName in WantedAdvancedSettingValues) - { - try - { - - var value = key.GetValue(valueName, null, - RegistryValueOptions.DoNotExpandEnvironmentNames); - - if (value != null && value is int intValue) - { - taskBarOptions.Add(new Tuple(valueName, intValue)); - } - } - catch (Exception) - { - // ignored, as this will happen - } - } - } - } - } - catch (Exception) - { - // ignored - } - - if (taskBarOptions.Count == 0) - { - return null; - } - - return new TaskBarSettings - { - Options = taskBarOptions.ToArray() - }; - } - - public bool Apply() - { - if (Options.Length == 0) - { - throw new InvalidOperationException(); - } - - using (var optionsKey = Registry.CurrentUser.OpenSubKey( - AdvancedSettingsAddress, - RegistryKeyPermissionCheck.ReadWriteSubTree)) - { - if (optionsKey == null) - { - return false; - } - - // Write - foreach (var option in Options) - { - try - { - optionsKey.SetValue(option.Item1, option.Item2); - } - catch (Exception) - { - // ignored - } - } - } - - return true; - } - } -} \ No newline at end of file diff --git a/DisplayMagicianShared/Windows/TaskBarStuckRectangle.cs b/DisplayMagicianShared/Windows/TaskBarStuckRectangle.cs index a166816..9b2965b 100644 --- a/DisplayMagicianShared/Windows/TaskBarStuckRectangle.cs +++ b/DisplayMagicianShared/Windows/TaskBarStuckRectangle.cs @@ -1,561 +1,137 @@ using System; using System.Collections.Generic; -using System.Drawing; -using System.Runtime.InteropServices; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using DisplayMagicianShared; +using System.Linq; using Microsoft.Win32; -using Newtonsoft.Json; // This file is taken from Soroush Falahati's amazing HeliosDisplayManagement software // available at https://github.com/falahati/HeliosDisplayManagement -// Substantial modifications made by Terry MacDonald 2022 onwards +// Modifications made by Terry MacDonald namespace DisplayMagicianShared.Windows { - public class TaskBarStuckRectangle - { + public class TaskBarSettings + { + private const string AdvancedSettingsAddress = + "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"; - public enum TaskBarEdge : UInt32 + private static List WantedAdvancedSettingValues = new List { - Left = 0, - Top = 1, - Right = 2, - Bottom = 3 - } - - [Flags] - public enum TaskBarOptions : UInt32 - { - None = 0, - AutoHide = 1 << 0, - KeepOnTop = 1 << 1, - UseSmallIcons = 1 << 2, - HideClock = 1 << 3, - HideVolume = 1 << 4, - HideNetwork = 1 << 5, - HidePower = 1 << 6, - WindowPreview = 1 << 7, - Unknown1 = 1 << 8, - Unknown2 = 1 << 9, - HideActionCenter = 1 << 10, - Unknown3 = 1 << 11, - HideLocation = 1 << 12, - HideLanguageBar = 1 << 13 - } - - private const string MainDisplayAddress = - "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects{0:D}"; - - private const string MultiDisplayAddress = - "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MMStuckRects{0:D}"; - - /*private static readonly Dictionary Headers = new Dictionary - { - {2, new byte[] {0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}}, - {3, new byte[] {0x30, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF}} + // Win10/11 registry keys (not all will be populated, only those that the user modified from default at least once) + "MMTaskbarEnabled", // Multiple Taskbars: 0 for show taskbar on main screen only, 1 for show taskbar on all screens + "MMTaskbarMode", // Show taskbar buttons on: 0 = all taskbars, 1 = main taskbar and where windows is open, 2 = taskbar where window is open + "MMTaskbarGlomLevel", // Buttons on other taskbars: 0 = always combine, combine when the taskbar is full, 2 = never combine + "NoTaskGrouping", // Disable all Task Grouping (overrides "TaskbarGroupSize"): 0 = enable task grouping, 1 = disable task grouping + "SearchboxTaskbarMode", // Show Search Button in Taskbar: 0 = remove search button, 1 = show search button + "ShowTaskViewButton", // Show Taskview Button in Taskbar: 0 = remove taskview button, 1 = show taskview button + "TaskbarAl", // Start Button Alignment: 0 for left, 1 for center, + "TaskbarDa", // Show Widgets button in Taskbar: 0 = remove widgets button, 1 = Show widgets button + "TaskbarGlomLevel", // Buttons on main screen: 0 = always combine, combine when the taskbar is full, 2 = never combine + "TaskbarGroupSize", // TaskBar left/right grouping by age: 0 = oldest first (default), 1 = roup by size largest first, 2 = group all with 2 or more, 3 = group all with 3 or more (see NoTaskGrouping to prevent Grouping ) + "TaskbarMn", // Show Chat Button in Taskbar: 0 = remove chat button, 1 = show chat button + "TaskbarSi", // Taskbar Size: 0 = small, 1 = medium, 2 = Large + "TaskbarSizeMove", // Lock the Taskbar (prevent resizing): 0 = taskbar size is locked, 1 = taskbar size is unlocked + "TaskbarSmallIcons", // Small Taskbar Icons: 0 = normal sized icons, 1 = small icons + "TaskbarSd", // Show Desktop Button in Taskbar: 0 for hid the show desktop button, 1 for show the Show desktop button }; -*/ - public TaskBarStuckRectangle(string devicePath) - { - bool MMStuckRectVerFound = false; - // Check if key exists - int version = 3; - string address = string.Format(MultiDisplayAddress, version); - if (Registry.CurrentUser.OpenSubKey(address) != null) - { - MMStuckRectVerFound = true; - SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found MMStuckRect3 registry key! {address}"); - } - else - { - // If it's not version 3, then try version 2 - version = 2; - address = string.Format(MultiDisplayAddress, version); - if (Registry.CurrentUser.OpenSubKey(address) != null) - { - MMStuckRectVerFound = true; - SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found MMStuckRect2 registry key! {address}"); - } - else - { - // It's not v2 or v3, so it must be a single display - MMStuckRectVerFound = false; - SharedLogger.logger.Warn($"TaskBarStuckRectangle/TaskBarStuckRectangle: Couldn't find an MMStuckRect2 or MMStuckRect3 registry key! Going to test if it is a single display only."); - } - } - bool foundDevicePath = false; - if (MMStuckRectVerFound) - { - // Check if value exists - if (version >= 2 && version <= 3) - { - using (var key = Registry.CurrentUser.OpenSubKey( - address, - RegistryKeyPermissionCheck.ReadSubTree)) - { - var binary = key?.GetValue(devicePath) as byte[]; - if (binary?.Length > 0) - { - foundDevicePath = true; - MainScreen = false; - DevicePath = devicePath; - Binary = binary; - OriginalBinary = new byte[binary.Length]; - binary.CopyTo(OriginalBinary, 0); - Version = version; - // Extract the values from the binary byte field - PopulateFieldsFromBinary(); + public Tuple[] Options { get; set; } - SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: The taskbar for {DevicePath} is against the {Edge} edge, is positioned at ({Location.X},{Location.Y}) and is {Location.Width}x{Location.Height} in size."); - } - else - { - SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Unable to get the TaskBarStuckRectangle binary settings from {devicePath} screen."); - } - } - } - } - - if (!foundDevicePath) - { - bool StuckRectVerFound = false; - // Check if string exists - version = 3; - address = string.Format(MainDisplayAddress, version); - if (Registry.CurrentUser.OpenSubKey(address) != null) - { - StuckRectVerFound = true; - SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found StuckRect3 single display registry key! {address}"); - } - else - { - // If it's not version 3, then try version 2 - version = 2; - address = string.Format(MainDisplayAddress, version); - if (Registry.CurrentUser.OpenSubKey(address) != null) - { - StuckRectVerFound = true; - SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found StuckRect2 single display registry key! {address}"); - } - else - { - SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: Couldn't find an single display StuckRect2 or StuckRect3 registry key! So we have to just return after doing nothing as there is nothing we can do."); - return; - } - } - - if (StuckRectVerFound) - { - // Check if value exists - if (version >= 2 && version <= 3) - { - using (var key = Registry.CurrentUser.OpenSubKey( - address, - RegistryKeyPermissionCheck.ReadSubTree)) - { - var binary = key?.GetValue(devicePath) as byte[]; - if (binary?.Length > 0) - { - foundDevicePath = true; - MainScreen = true; - DevicePath = devicePath; - Binary = binary; - OriginalBinary = new byte[binary.Length]; - binary.CopyTo(OriginalBinary, 0); - Version = version; - - // Extract the values from the binary byte field - PopulateFieldsFromBinary(); - - SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: The taskbar for {DevicePath} is against the {Edge} edge, is positioned at ({Location.X},{Location.Y}) and is {Location.Width}x{Location.Height} in size."); - } - else - { - SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Unable to get the TaskBarStuckRectangle binary settings from {devicePath} screen."); - } - } - } - } - } - } - - public TaskBarStuckRectangle() - { - } - - public byte[] Binary { get; set; } - - public byte[] OriginalBinary { get; set; } - - public string DevicePath { get; set; } - - public bool MainScreen { get; set; } - - public UInt32 DPI { get; set; } - - public TaskBarEdge Edge { get; set; } - - public Rectangle Location { get; set; } - - public Size MinSize { get; set; } - - public TaskBarOptions Options { get; set; } - - public uint Rows { get; set; } - - public int Version { get; set; } - - public override bool Equals(object obj) => obj is TaskBarStuckRectangle other && this.Equals(other); - public bool Equals(TaskBarStuckRectangle other) - { - return Version == other.Version && - DevicePath == other.DevicePath && - MainScreen == other.MainScreen && - DPI == other.DPI && - Edge == other.Edge && - Location == other.Location && - MinSize == other.MinSize && - Options == other.Options && - Rows == other.Rows; - } + public override bool Equals(object obj) => obj is TaskBarSettings other && this.Equals(other); + public bool Equals(TaskBarSettings other) + => Options.All(a => other.Options.Any(x => x.Item1 == a.Item1 && x.Item2 == a.Item2)); public override int GetHashCode() { - return (Version, MainScreen, DevicePath, DPI, Edge, Location, MinSize, Options, Rows).GetHashCode(); + return (Options).GetHashCode(); } - public static bool operator ==(TaskBarStuckRectangle lhs, TaskBarStuckRectangle rhs) => lhs.Equals(rhs); + public static bool operator ==(TaskBarSettings lhs, TaskBarSettings rhs) => lhs.Equals(rhs); - public static bool operator !=(TaskBarStuckRectangle lhs, TaskBarStuckRectangle rhs) => !(lhs == rhs); - - static bool Xor(byte[] a, byte[] b) + public static bool operator !=(TaskBarSettings lhs, TaskBarSettings rhs) => !(lhs == rhs); + public static TaskBarSettings GetCurrent() { + var taskBarOptions = new List>(); - int x = a.Length ^ b.Length; - - for (int i = 0; i < a.Length && i < b.Length; ++i) - + // Get modified and stored Taskbar options from the User Registry + // Note: Only the taskbar options changed from default at least once in the past will be listed in Registry + try { - - x |= a[i] ^ b[i]; - - } - - return x == 0; - - } - - private bool PopulateFieldsFromBinary() - { - // Now we decipher the binary properties features to populate the stuckrectangle - // DPI - if (Binary.Length < 44) - { - DPI = 0; - } - else - { - DPI = BitConverter.ToUInt32(Binary, 40); - } - // Edge - if (Binary.Length < 16) - { - Edge = TaskBarEdge.Bottom; - } - else - { - Edge = (TaskBarEdge)BitConverter.ToUInt32(Binary, 12); - } - // Location - if (Binary.Length < 40) - { - Location = Rectangle.Empty; - } - else - { - var left = BitConverter.ToInt32(Binary, 24); - var top = BitConverter.ToInt32(Binary, 28); - var right = BitConverter.ToInt32(Binary, 32); - var bottom = BitConverter.ToInt32(Binary, 36); - - Location = Rectangle.FromLTRB(left, top, right, bottom); - } - // MinSize - if (Binary.Length < 24) - { - MinSize = Size.Empty; - } - else - { - var width = BitConverter.ToInt32(Binary, 16); - var height = BitConverter.ToInt32(Binary, 20); - - MinSize = new Size(width, height); - } - // Options - if (Binary.Length < 12) - { - Options = 0; - } - else - { - Options = (TaskBarOptions)BitConverter.ToUInt32(Binary, 8); - } - // Rows - if (Binary.Length < 48) - { - Rows = 1; - } - else - { - Rows = BitConverter.ToUInt32(Binary, 44); - } - - SharedLogger.logger.Trace($"TaskBarStuckRectangle/PopulateFieldsFromBinary: Grabbed the following settings for {DevicePath} from the registry: DPI = {DPI}, Edge = {Edge}, Location = ({Location.X},{Location.Y}), MinSize = {Location.Width}x{Location.Height}, Options = {Options}, Rows = {Rows}."); - - return true; - } - - public bool PopulateBinaryFromFields() - { - // Set the DPI - if (Binary.Length < 44) - { - DPI = 0; - var bytes = BitConverter.GetBytes(DPI); - Array.Copy(bytes, 0, Binary, 40, 4); - } - else - { - var bytes = BitConverter.GetBytes(DPI); - Array.Copy(bytes, 0, Binary, 40, 4); - } - // Edge - if (Binary.Length < 16) - { - Edge = TaskBarEdge.Bottom; - var bytes = BitConverter.GetBytes((uint)Edge); - Array.Copy(bytes, 0, Binary, 12, 1); - } - else - { - var bytes = BitConverter.GetBytes((uint)Edge); - Array.Copy(bytes, 0, Binary, 12, 1); - } - // Location - if (Binary.Length < 40) - { - var bytes = BitConverter.GetBytes(0); - Array.Copy(bytes, 0, Binary, 24, 4); - - bytes = BitConverter.GetBytes(0); - Array.Copy(bytes, 0, Binary, 28, 4); - - bytes = BitConverter.GetBytes(0); - Array.Copy(bytes, 0, Binary, 32, 4); - - bytes = BitConverter.GetBytes(0); - Array.Copy(bytes, 0, Binary, 36, 4); - } - else - { - var bytes = BitConverter.GetBytes(Location.Left); - Array.Copy(bytes, 0, Binary, 24, 4); - - bytes = BitConverter.GetBytes(Location.Top); - Array.Copy(bytes, 0, Binary, 28, 4); - - bytes = BitConverter.GetBytes(Location.Right); - Array.Copy(bytes, 0, Binary, 32, 4); - - bytes = BitConverter.GetBytes(Location.Bottom); - Array.Copy(bytes, 0, Binary, 36, 4); - } - // MinSize - if (Binary.Length < 24) - { - var bytes = BitConverter.GetBytes(0); - Array.Copy(bytes, 0, Binary, 16, 4); - - bytes = BitConverter.GetBytes(0); - Array.Copy(bytes, 0, Binary, 20, 4); - } - else - { - var bytes = BitConverter.GetBytes(MinSize.Width); - Array.Copy(bytes, 0, Binary, 16, 4); - - bytes = BitConverter.GetBytes(MinSize.Height); - Array.Copy(bytes, 0, Binary, 20, 4); - } - // Options - if (Binary.Length < 12) - { - var bytes = BitConverter.GetBytes((uint)0); - Array.Copy(bytes, 0, Binary, 8, 4); - } - else - { - var bytes = BitConverter.GetBytes((uint)Options); - Array.Copy(bytes, 0, Binary, 8, 4); - } - // Rows - if (Binary.Length < 48) - { - var bytes = BitConverter.GetBytes(1); - Array.Copy(bytes, 0, Binary, 44, 4); - } - else - { - var bytes = BitConverter.GetBytes(Rows); - Array.Copy(bytes, 0, Binary, 44, 4); - } - - SharedLogger.logger.Trace($"TaskBarStuckRectangle/PopulateBinaryFromFields: Set the following settings for {DevicePath} into registry: DPI = {DPI}, Edge = {Edge}, Location = ({Location.X},{Location.Y}), MinSize = {Location.Width}x{Location.Height}, Options = {Options}, Rows = {Rows}."); - - return true; - } - - public bool WriteToRegistry() - { - // Update the binary with the current settings from the object - PopulateBinaryFromFields(); - - // Write the binary field to registry - string address; - if (MainScreen) - { - address = string.Format(MainDisplayAddress, Version); - // Set the Main Screen - try + using (var key = Registry.CurrentUser.OpenSubKey( + AdvancedSettingsAddress, + RegistryKeyPermissionCheck.ReadSubTree)) { - using (var key = Registry.CurrentUser.OpenSubKey( - address, - RegistryKeyPermissionCheck.ReadWriteSubTree)) + if (key != null) { - key.SetValue(DevicePath, Binary); - SharedLogger.logger.Trace($"TaskBarStuckRectangle/Apply: Successfully applied TaskBarStuckRectangle registry settings for the {DevicePath} Screen in {address}!"); + foreach (var valueName in WantedAdvancedSettingValues) + { + try + { + + var value = key.GetValue(valueName, null, + RegistryValueOptions.DoNotExpandEnvironmentNames); + + if (value != null && value is int intValue) + { + taskBarOptions.Add(new Tuple(valueName, intValue)); + } + } + catch (Exception) + { + // ignored, as this will happen + } + } } } - catch (Exception ex) - { - SharedLogger.logger.Error(ex, $"TaskBarStuckRectangle/GetCurrent: Unable to set the {DevicePath} TaskBarStuckRectangle registry settings in {address} due to an exception!"); - } } - else + catch (Exception) { - address = string.Format(MultiDisplayAddress, Version); - // Grab the main screen taskbar placement - try + // ignored + } + + if (taskBarOptions.Count == 0) + { + return null; + } + + return new TaskBarSettings + { + Options = taskBarOptions.ToArray() + }; + } + + public bool Apply() + { + if (Options.Length == 0) + { + throw new InvalidOperationException(); + } + + using (var optionsKey = Registry.CurrentUser.OpenSubKey( + AdvancedSettingsAddress, + RegistryKeyPermissionCheck.ReadWriteSubTree)) + { + if (optionsKey == null) { - using (var key = Registry.CurrentUser.OpenSubKey( - address, - RegistryKeyPermissionCheck.ReadWriteSubTree)) + return false; + } + + // Write + foreach (var option in Options) + { + try { - key.SetValue(DevicePath, Binary); - SharedLogger.logger.Trace($"TaskBarStuckRectangle/WriteToRegistry: Successfully applied TaskBarStuckRectangle registry settings for the {DevicePath} Screen in {address}!"); + optionsKey.SetValue(option.Item1, option.Item2); + } + catch (Exception) + { + // ignored } } - catch (Exception ex) - { - SharedLogger.logger.Error(ex, $"TaskBarStuckRectangle/WriteToRegistry: Unable to set the {DevicePath} TaskBarStuckRectangle registry settings in {address} due to an exception!"); - } } return true; } - - public static bool RepositionMainTaskBar(TaskBarEdge edge) - { - // Tell Windows to refresh the Main Screen Windows Taskbar - // Find the "Shell_TrayWnd" window - IntPtr mainToolBarHWnd = Utils.FindWindow("Shell_TrayWnd", null); - // Send the "Shell_TrayWnd" window a WM_USER_REFRESHTASKBAR with a wParameter of 0006 and a lParamater of the position (e.g. 0000 for left, 0001 for top, 0002 for right and 0003 for bottom) - IntPtr taskBarPositionBuffer = new IntPtr((Int32)edge); - Utils.SendMessage(mainToolBarHWnd, Utils.WM_USER_REFRESHTASKBAR, (IntPtr)Utils.wParam_SHELLTRAY, taskBarPositionBuffer); - return true; - } - - public static bool RepositionSecondaryTaskBars() - { - // Tell Windows to refresh the Other Windows Taskbars if needed - IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL; - for (int i = 0; i < 100; i++) - { - // Find the next "Shell_SecondaryTrayWnd" window - IntPtr nextTaskBarWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, lastTaskBarWindowHwnd, "Shell_SecondaryTrayWnd", null); - if (nextTaskBarWindowHwnd == (IntPtr)Utils.NULL) - { - // No more windows taskbars to notify - break; - } - // Send the "Shell_TrayWnd" window a WM_SETTINGCHANGE with a wParameter of SPI_SETWORKAREA - Utils.SendMessage(lastTaskBarWindowHwnd, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL); - lastTaskBarWindowHwnd = nextTaskBarWindowHwnd; - } - return true; - } - - public static void RefreshTrayArea() - { - // Finds the Shell_TrayWnd -> TrayNotifyWnd -> SysPager -> "Notification Area" containing the visible notification area icons (windows 7 version) - IntPtr systemTrayContainerHandle = Utils.FindWindow("Shell_TrayWnd", null); - IntPtr systemTrayHandle = Utils.FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null); - IntPtr sysPagerHandle = Utils.FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null); - IntPtr notificationAreaHandle = Utils.FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area"); - // If the visible notification area icons (Windows 7 aren't found, then we're on a later version of windows, and we need to look for different window names - if (notificationAreaHandle == IntPtr.Zero) - { - // Finds the Shell_TrayWnd -> TrayNotifyWnd -> SysPager -> "User Promoted Notification Area" containing the visible notification area icons (windows 10+ version) - notificationAreaHandle = Utils.FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area"); - // Also attempt to find the NotifyIconOverflowWindow -> "Overflow Notification Area' window which is the hidden windoww that notification icons live when they are - // too numberous or are hidden by the user. - IntPtr notifyIconOverflowWindowHandle = Utils.FindWindow("NotifyIconOverflowWindow", null); - IntPtr overflowNotificationAreaHandle = Utils.FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area"); - // Fool the "Overflow Notification Area' window into thinking the mouse is moving over it - // which will force windows to refresh the "Overflow Notification Area' window and remove old icons. - RefreshTrayArea(overflowNotificationAreaHandle); - notifyIconOverflowWindowHandle = IntPtr.Zero; - overflowNotificationAreaHandle = IntPtr.Zero; - } - // Fool the "Notification Area" or "User Promoted Notification Area" window (depends on the version of windows) into thinking the mouse is moving over it - // which will force windows to refresh the "Notification Area" or "User Promoted Notification Area" window and remove old icons. - RefreshTrayArea(notificationAreaHandle); - systemTrayContainerHandle = IntPtr.Zero; - systemTrayHandle = IntPtr.Zero; - sysPagerHandle = IntPtr.Zero; - notificationAreaHandle = IntPtr.Zero; - - } - - - private static void RefreshTrayArea(IntPtr windowHandle) - { - // Moves the mouse around within the window area of the supplied window - Utils.RECT rect; - Utils.GetClientRect(windowHandle, out rect); - for (var x = 0; x < rect.right; x += 5) - for (var y = 0; y < rect.bottom; y += 5) - Utils.SendMessage(windowHandle, Utils.WM_MOUSEMOVE, 0, (y << 16) + x); - } - - /*public void DoMouseLeftClick(IntPtr handle, Point x) - { - Utils.SendMessage(handle, (int)Utils.WM_MOUSEMOVE, 0, Utils.MakeLParam(x.X, x.Y)); - //SendMessage(handle, (int)Utils.WM_LBUTTONUP, 0, Utils.MakeLParam(x.X, x.Y)); - - return; - - //I have tried PostMessage, and SendMessage, and both of them at the same time, and neither works. - - Utils.PostMessage(handle, Utils.WM_MOUSEMOVE, 0, Utils.MakeLParam(x.X, x.Y)); - //PostMessage(handle, (uint)Utils.WM_LBUTTONUP, 0, Utils.MakeLParam(x.X, x.Y)); - } -*/ } } \ No newline at end of file diff --git a/DisplayMagicianShared/Windows/WinLibrary.cs b/DisplayMagicianShared/Windows/WinLibrary.cs index b1a1dec..bb1a800 100644 --- a/DisplayMagicianShared/Windows/WinLibrary.cs +++ b/DisplayMagicianShared/Windows/WinLibrary.cs @@ -9,6 +9,8 @@ using System.IO; using System.ComponentModel; using Microsoft.Win32; using System.Threading.Tasks; +using static DisplayMagicianShared.Windows.TaskBarLayout; +using System.Diagnostics; namespace DisplayMagicianShared.Windows { @@ -37,6 +39,27 @@ namespace DisplayMagicianShared.Windows public static bool operator !=(ADVANCED_HDR_INFO_PER_PATH lhs, ADVANCED_HDR_INFO_PER_PATH rhs) => !(lhs == rhs); } + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAY_SOURCE : IEquatable + { + public LUID AdapterId; + public UInt32 SourceId; + public UInt32 TargetId; + public string DevicePath; + + public override bool Equals(object obj) => obj is DISPLAY_SOURCE other && this.Equals(other); + public bool Equals(DISPLAY_SOURCE other) + => true; + public override int GetHashCode() + { + return 300; + } + + public static bool operator ==(DISPLAY_SOURCE lhs, DISPLAY_SOURCE rhs) => lhs.Equals(rhs); + + public static bool operator !=(DISPLAY_SOURCE lhs, DISPLAY_SOURCE rhs) => !(lhs == rhs); + } + [StructLayout(LayoutKind.Sequential)] public struct WINDOWS_DISPLAY_CONFIG : IEquatable { @@ -45,13 +68,13 @@ namespace DisplayMagicianShared.Windows public DISPLAYCONFIG_MODE_INFO[] DisplayConfigModes; public List DisplayHDRStates; public Dictionary GdiDisplaySettings; - public List TaskBarLayout; + public Dictionary TaskBarLayout; public TaskBarSettings TaskBarSettings; public bool IsCloned; // 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. - public Dictionary> DisplaySources; + public Dictionary> DisplaySources; public List DisplayIdentifiers; public override bool Equals(object obj) => obj is WINDOWS_DISPLAY_CONFIG other && this.Equals(other); @@ -64,9 +87,12 @@ namespace DisplayMagicianShared.Windows // Additionally, we had to disable the DEviceKey from the equality testing within the GDI library itself as that waould also change after changing back from NVIDIA surround // This still allows us to detect when refresh rates change, which will allow DisplayMagician to detect profile differences. GdiDisplaySettings.Values.SequenceEqual(other.GdiDisplaySettings.Values) && - DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers) && - TaskBarLayout.SequenceEqual(other.TaskBarLayout) && - TaskBarSettings.Equals(other.TaskBarSettings); + DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers); + // NOTE: I have disabled the TaskBar specific matching for now due to errors I cannot fix + // WinLibrary will still track the location of the taskbars, but won't actually set them as the setting of the taskbars doesnt work at the moment. + /*&& + TaskBarLayout.SequenceEqual(other.TaskBarLayout) && + TaskBarSettings.Equals(other.TaskBarSettings);*/ public override int GetHashCode() { @@ -180,9 +206,9 @@ namespace DisplayMagicianShared.Windows myDefaultConfig.DisplayConfigPaths = new DISPLAYCONFIG_PATH_INFO[0]; myDefaultConfig.DisplayHDRStates = new List(); myDefaultConfig.DisplayIdentifiers = new List(); - myDefaultConfig.DisplaySources = new Dictionary>(); + myDefaultConfig.DisplaySources = new Dictionary>(); myDefaultConfig.GdiDisplaySettings = new Dictionary(); - myDefaultConfig.TaskBarLayout = new List(); + myDefaultConfig.TaskBarLayout = new Dictionary(); myDefaultConfig.TaskBarSettings = new TaskBarSettings(); myDefaultConfig.IsCloned = false; @@ -292,6 +318,34 @@ namespace DisplayMagicianShared.Windows } } + SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Going through the display sources list info to update the adapter id"); + // Update the DisplaySources with the current adapter id + for (int i = 0; i < savedDisplayConfig.DisplaySources.Count; i++) + { + List dsList = savedDisplayConfig.DisplaySources.ElementAt(i).Value; + if (dsList.Count > 0) + { + for (int j = 0; j < dsList.Count; j++) + { + DISPLAY_SOURCE ds = dsList[j]; + // Change the Display Source AdapterID + if (adapterOldToNewMap.ContainsKey(ds.AdapterId.Value)) + { + // We get here if there is a matching adapter + newAdapterValue = adapterOldToNewMap[ds.AdapterId.Value]; + ds.AdapterId = AdapterValueToLUID(newAdapterValue); + } + else + { + // if there isn't a matching adapter, then we just pick the first current one and hope that works! + // (it is highly likely to... its only if the user has multiple graphics cards with some weird config it may break) + newAdapterValue = currentAdapterMap.First().Key; + SharedLogger.logger.Warn($"WinLibrary/PatchAdapterIDs: Uh Oh. Adapter {savedDisplayConfig.DisplayHDRStates[i].AdapterId.Value} didn't have a current match in Display Sources! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead."); + ds.AdapterId = AdapterValueToLUID(newAdapterValue); + } + } + } + } } public bool UpdateActiveConfig() @@ -320,6 +374,10 @@ namespace DisplayMagicianShared.Windows private WINDOWS_DISPLAY_CONFIG GetWindowsDisplayConfig(QDC selector = QDC.QDC_ONLY_ACTIVE_PATHS | QDC.QDC_INCLUDE_HMD) { + + // Prepare the empty windows display config + WINDOWS_DISPLAY_CONFIG windowsDisplayConfig = CreateDefaultConfig(); + // Get the size of the largest Active Paths and Modes arrays SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the size of the largest Active Paths and Modes arrays"); int pathCount = 0; @@ -368,12 +426,6 @@ namespace DisplayMagicianShared.Windows throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays."); } - // Prepare the empty windows display config - WINDOWS_DISPLAY_CONFIG windowsDisplayConfig = new WINDOWS_DISPLAY_CONFIG(); - windowsDisplayConfig.DisplayAdapters = new Dictionary(); - windowsDisplayConfig.DisplayHDRStates = new List(); - windowsDisplayConfig.DisplaySources = new Dictionary>(); - windowsDisplayConfig.IsCloned = false; // First of all generate the current displayIdentifiers windowsDisplayConfig.DisplayIdentifiers = GetCurrentDisplayIdentifiers(); @@ -393,6 +445,7 @@ namespace DisplayMagicianShared.Windows // Now cycle through the paths and grab the HDR state information // and map the adapter name to adapter id + // and populate the display source information List targetPathIdsToChange = new List(); List targetModeIdsToChange = new List(); List targetIdsFound = new List(); @@ -432,7 +485,11 @@ namespace DisplayMagicianShared.Windows if (windowsDisplayConfig.DisplaySources.ContainsKey(sourceInfo.ViewGdiDeviceName)) { // We already have at least one display using this source, so we need to add the other cloned display to the existing list - windowsDisplayConfig.DisplaySources[sourceInfo.ViewGdiDeviceName].Add(paths[i].SourceInfo.Id); + DISPLAY_SOURCE ds = new DISPLAY_SOURCE(); + ds.AdapterId = paths[i].SourceInfo.AdapterId; + ds.SourceId = paths[i].SourceInfo.Id; + ds.TargetId = paths[i].TargetInfo.Id; + windowsDisplayConfig.DisplaySources[sourceInfo.ViewGdiDeviceName].Add(ds); isClonedPath = true; isClonedProfile = true; windowsDisplayConfig.IsCloned = true; @@ -440,9 +497,13 @@ namespace DisplayMagicianShared.Windows else { // This is the first display to use this source - List sourceIds = new List(); - sourceIds.Add(paths[i].SourceInfo.Id); - windowsDisplayConfig.DisplaySources.Add(sourceInfo.ViewGdiDeviceName, sourceIds); + List sources = new List(); + DISPLAY_SOURCE ds = new DISPLAY_SOURCE(); + ds.AdapterId = paths[i].SourceInfo.AdapterId; + ds.SourceId = paths[i].SourceInfo.Id; + ds.TargetId = paths[i].TargetInfo.Id; + sources.Add(ds); + windowsDisplayConfig.DisplaySources.Add(sourceInfo.ViewGdiDeviceName, sources); } SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Display Source {sourceInfo.ViewGdiDeviceName} for source {paths[i].SourceInfo.Id}."); @@ -618,33 +679,58 @@ namespace DisplayMagicianShared.Windows modes[i].Id = targetIdMap[modes[i].Id]; } } + + // And then we need to go through the list of display sources and patch the 'cloned' displays with a real display ID so the display layout is right in cloned displays + for (int i = 0; i < windowsDisplayConfig.DisplaySources.Count; i++) + { + string key = windowsDisplayConfig.DisplaySources.ElementAt(i).Key; + DISPLAY_SOURCE[] dsList = windowsDisplayConfig.DisplaySources.ElementAt(i).Value.ToArray(); + for (int j = 0; j < dsList.Length; j++) + { + // We only change the ids that match in InfoType for target displays + if (targetIdMap.ContainsKey(dsList[j].TargetId)) + { + // Patch the cloned ids with a real working one! + dsList[j].TargetId = targetIdMap[dsList[j].TargetId]; + + } + } + windowsDisplayConfig.DisplaySources[key] = dsList.ToList(); + } } + // Now we need to find the DevicePaths for the DisplaySources (as at this point the cloned display sources have been corrected) + for (int i = 0; i < windowsDisplayConfig.DisplaySources.Count; i++) + { + string key = windowsDisplayConfig.DisplaySources.ElementAt(i).Key; + DISPLAY_SOURCE[] dsList = windowsDisplayConfig.DisplaySources.ElementAt(i).Value.ToArray(); + for (int j = 0; j < dsList.Length; j++) + { + // get display target name + var targetInfo = new DISPLAYCONFIG_TARGET_DEVICE_NAME(); + targetInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; + targetInfo.Header.Size = (uint)Marshal.SizeOf(); + targetInfo.Header.AdapterId = dsList[j].AdapterId; + targetInfo.Header.Id = dsList[j].TargetId; + err = CCDImport.DisplayConfigGetDeviceInfo(ref targetInfo); + if (err == WIN32STATUS.ERROR_SUCCESS) + { + SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Successfully got the target info from {dsList[j].TargetId}."); + dsList[j].DevicePath = targetInfo.MonitorDevicePath; + } + else + { + SharedLogger.logger.Warn($"WinLibrary/GetSomeDisplayIdentifiers: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the target info for display #{dsList[j].TargetId}"); + } + } + windowsDisplayConfig.DisplaySources[key] = dsList.ToList(); + } + + + Dictionary taskBarStuckRectangles = new Dictionary(); + // Now attempt to get the windows taskbar location for each display - // We use the information we already got from the display identifiers - SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get the Windows Taskbar layout."); - List taskBarStuckRectangles = new List(); - foreach (var displayId in windowsDisplayConfig.DisplayIdentifiers) - { - // e.g. "WINAPI|\\\\?\\PCI#VEN_10DE&DEV_2482&SUBSYS_408E1458&REV_A1#4&2283f625&0&0019#{5b45201d-f2f2-4f3b-85bb-30ff1f953599}|DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI|54074|4318|\\\\?\\DISPLAY#NVS10DE#5&2b46c695&0&UID185344#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}|NV Surround" - string[] winapiLine = displayId.Split('|'); - string pattern = @"DISPLAY\#(.*)\#\{"; - Match match = Regex.Match(winapiLine[5], pattern); - if (match.Success) - { - string devicePath = match.Groups[1].Value; - TaskBarStuckRectangle taskBarStuckRectangle = new TaskBarStuckRectangle(devicePath); - taskBarStuckRectangles.Add(taskBarStuckRectangle); - } - else - { - SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: We were unable to figure out the DevicePath for the '{displayId}' display identifier."); - } - - } - // And we get the Main Screen taskbar too - TaskBarStuckRectangle mainTaskBarStuckRectangle = new TaskBarStuckRectangle("Settings"); - taskBarStuckRectangles.Add(mainTaskBarStuckRectangle); + taskBarStuckRectangles = TaskBarLayout.GetAllCurrentTaskBarLayouts(windowsDisplayConfig.DisplaySources); // Now we try to get the taskbar settings too SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get the Windows Taskbar settings."); @@ -655,7 +741,6 @@ namespace DisplayMagicianShared.Windows windowsDisplayConfig.DisplayConfigModes = modes; windowsDisplayConfig.GdiDisplaySettings = GetGdiDisplaySettings(); windowsDisplayConfig.TaskBarLayout = taskBarStuckRectangles; - //windowsDisplayConfig.OriginalTaskBarLayout = new List(taskBarStuckRectangles); windowsDisplayConfig.TaskBarSettings = taskBarSettings; return windowsDisplayConfig; @@ -1380,36 +1465,65 @@ namespace DisplayMagicianShared.Windows } + // NOTE: I have disabled the TaskBar setting logic for now due to errors I cannot fix in this code. + // WinLibrary will still track the location of the taskbars, but won't actually set them as the setting of the taskbars doesnt work at the moment. + // I hope to eventually fix this code, but I don't want to hold up a new DisplayMagician release while troubleshooting this. + /* // Now set the taskbar position for each screen - if (displayConfig.TaskBarLayout.Count > 0) + if (displayConfig.TaskBarLayout.Count > 0 && allWindowsDisplayConfig.TaskBarLayout.Count > 0) { - SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Setting the taskbar layout."); - foreach (TaskBarStuckRectangle tbsr in displayConfig.TaskBarLayout) + foreach (var tbrDictEntry in displayConfig.TaskBarLayout) { - if (tbsr.Version >= 2 && tbsr.Version <= 3) + // Look up the monitor location of the current monitor and find the matching taskbar location in the taskbar settings + if (allWindowsDisplayConfig.TaskBarLayout.ContainsKey(tbrDictEntry.Key)) { - // Write the settings to registry - tbsr.WriteToRegistry(); - - if (tbsr.MainScreen) + // check the current monitor taskbar location + // if the current monitor location is the same as the monitor we want to set then + TaskBarLayout currentLayout = displayConfig.TaskBarLayout[tbrDictEntry.Key]; + TaskBarLayout wantedLayout = allWindowsDisplayConfig.TaskBarLayout[tbrDictEntry.Key]; + if (currentLayout.Equals(wantedLayout)) { - TaskBarStuckRectangle.RepositionMainTaskBar(tbsr.Edge); + SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Display {tbrDictEntry.Key} ({tbrDictEntry.Value.RegKeyValue}) has the taskbar with the correct position, size and settings, so no need to move it"); + } + else + { + // if the current monitor taskbar location is not where we want it then + // move the taskbar manually + TaskBarLayout tbr = tbrDictEntry.Value; + tbr.MoveTaskBar(); } - } else { - SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Unable to set the {tbsr.DevicePath} TaskBarStuckRectangle registry settings as the version isn't v2 or v3!"); + SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Display {tbrDictEntry.Key} ({tbrDictEntry.Value.RegKeyValue}) is not currently in use, so cannot set any taskbars on it!"); } } - - // Tell Windows to refresh the Other Windows Taskbars if needed - IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL; - if (displayConfig.TaskBarLayout.Count > 1) + // This will actually move the taskbars by forcing Explorer to read from registry key + /*RestartManagerSession.RestartExplorer(); + Process[] explorers = Process.GetProcessesByName("Explorer"); + for (int i = 0; i < explorers.Length; i++) { - TaskBarStuckRectangle.RepositionSecondaryTaskBars(); + kill } + // Enum all the monitors + //List currentMonitors = Utils.EnumMonitors(); + // Go through each monitor + //foreach (MONITORINFOEX mi in currentMonitors) + //{ + // Look up the monitor location of the current monitor and find the matching taskbar location in the taskbar settings + //if (current) + // check the current monitor taskbar location + // if the current monitor location is the same as the monitor we want to set then + // if the current monitor taskbar location where we want it then + // move the taskbar manually + // Find the registry key for the monitor we are modifying + // save the taskbar position for the monitor in registry + // else + // log the fact that the monitor is in the right place so skipping moving it + // if we didn't find a taskbar location for this monitor + // log the fact that the taskbar location wasnt foound for this monitor + //} } else @@ -1437,7 +1551,7 @@ namespace DisplayMagicianShared.Windows { // The settings are the same, so we should skip applying them SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: The current taskbar settings are the same as the one's we want, so skipping setting them!"); - } + }*/ return true; } @@ -1958,7 +2072,118 @@ namespace DisplayMagicianShared.Windows { return false; } - } + } + + + + public static bool RepositionMainTaskBar(TaskBarEdge edge) + { + // Tell Windows to refresh the Main Screen Windows Taskbar + // Find the "Shell_TrayWnd" window + IntPtr systemTrayContainerHandle = Utils.FindWindow("Shell_TrayWnd", null); + IntPtr startButtonHandle = Utils.FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "Start", null); + IntPtr systemTrayHandle = Utils.FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null); + IntPtr rebarWindowHandle = Utils.FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "ReBarWindow32", null); + IntPtr taskBarPositionBuffer = new IntPtr((Int32)edge); + IntPtr trayDesktopShowButtonHandle = Utils.FindWindowEx(systemTrayHandle, IntPtr.Zero, "TrayShowDesktopButtonWClass", null); + IntPtr trayInputIndicatorHandle = Utils.FindWindowEx(systemTrayHandle, IntPtr.Zero, "TrayInputIndicatorWClass", null); + + // Send messages + // Send the "TrayNotifyWnd" window a WM_USER+13 (0x040D) message with a wParameter of 0x0 and a lParameter of the position (e.g. 0x0000 for left, 0x0001 for top, 0x0002 for right and 0x0003 for bottom) + Utils.SendMessage(systemTrayHandle, Utils.WM_USER_13, IntPtr.Zero, taskBarPositionBuffer); + Utils.SendMessage(systemTrayHandle, Utils.WM_USER_100, (IntPtr)0x3e, (IntPtr)0x21c); + Utils.SendMessage(systemTrayHandle, Utils.WM_THEMECHANGED, (IntPtr)0xffffffffffffffff, (IntPtr)0x000000008000001); + // Next, send the "TrayShowDesktopButtonWClass" window a WM_USER+13 (0x040D) message with a wParameter of 0x0 and a lParameter of the position (e.g. 0x0000 for left, 0x0001 for top, 0x0002 for right and 0x0003 for bottom) + Utils.SendMessage(trayDesktopShowButtonHandle, Utils.WM_USER_13, IntPtr.Zero, taskBarPositionBuffer); + Utils.SendMessage(startButtonHandle, Utils.WM_USER_440, (IntPtr)0x0, (IntPtr)0x0); + Utils.SendMessage(systemTrayHandle, Utils.WM_USER_1, (IntPtr)0x0, (IntPtr)0x0); + Utils.SendMessage(systemTrayContainerHandle, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL); + Utils.PostMessage(systemTrayHandle, Utils.WM_SETTINGCHANGE, Utils.SPI_SETWORKAREA, Utils.NULL); + Utils.SendMessage(systemTrayContainerHandle, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL); + Utils.SendMessage(rebarWindowHandle, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL); + //Utils.SendMessage(trayInputIndicatorHandle, Utils.WM_USER_100, (IntPtr)0x3e, (IntPtr)0x21c); + //Utils.SendMessage(systemTrayHandle, Utils.WM_USER_1, (IntPtr)0x0, (IntPtr)0x0); + // Move all the taskbars to this location + //Utils.SendMessage(systemTrayContainerHandle, Utils.WM_USER_REFRESHTASKBAR, (IntPtr)Utils.wParam_SHELLTRAY, taskBarPositionBuffer); + //Utils.SendMessage(systemTrayContainerHandle, Utils.WM_USER_REFRESHTASKBAR, (IntPtr)Utils.wParam_SHELLTRAY, taskBarPositionBuffer); + return true; + } + + public static bool RepositionAllTaskBars(TaskBarEdge edge) + { + // Tell Windows to refresh the Main Screen Windows Taskbar + // Find the "Shell_TrayWnd" window + IntPtr mainToolBarHWnd = Utils.FindWindow("Shell_TrayWnd", null); + // Send the "Shell_TrayWnd" window a WM_USER_REFRESHTASKBAR with a wParameter of 0006 and a lParameter of the position (e.g. 0000 for left, 0001 for top, 0002 for right and 0003 for bottom) + IntPtr taskBarPositionBuffer = new IntPtr((Int32)edge); + Utils.SendMessage(mainToolBarHWnd, Utils.WM_USER_REFRESHTASKBAR, (IntPtr)Utils.wParam_SHELLTRAY, taskBarPositionBuffer); + return true; + } + + public static bool RepositionSecondaryTaskBars() + { + // Tell Windows to refresh the Other Windows Taskbars if needed + IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL; + for (int i = 0; i < 100; i++) + { + // Find the next "Shell_SecondaryTrayWnd" window + IntPtr nextTaskBarWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, lastTaskBarWindowHwnd, "Shell_SecondaryTrayWnd", null); + if (nextTaskBarWindowHwnd == (IntPtr)Utils.NULL) + { + // No more windows taskbars to notify + break; + } + // Send the "Shell_TrayWnd" window a WM_SETTINGCHANGE with a wParameter of SPI_SETWORKAREA + Utils.SendMessage(lastTaskBarWindowHwnd, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL); + lastTaskBarWindowHwnd = nextTaskBarWindowHwnd; + } + return true; + } + + + + public static void RefreshTrayArea() + { + // Finds the Shell_TrayWnd -> TrayNotifyWnd -> SysPager -> "Notification Area" containing the visible notification area icons (windows 7 version) + IntPtr systemTrayContainerHandle = Utils.FindWindow("Shell_TrayWnd", null); + IntPtr systemTrayHandle = Utils.FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null); + IntPtr sysPagerHandle = Utils.FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null); + IntPtr notificationAreaHandle = Utils.FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area"); + // If the visible notification area icons (Windows 7 aren't found, then we're on a later version of windows, and we need to look for different window names + if (notificationAreaHandle == IntPtr.Zero) + { + // Finds the Shell_TrayWnd -> TrayNotifyWnd -> SysPager -> "User Promoted Notification Area" containing the visible notification area icons (windows 10+ version) + notificationAreaHandle = Utils.FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area"); + // Also attempt to find the NotifyIconOverflowWindow -> "Overflow Notification Area' window which is the hidden windoww that notification icons live when they are + // too numberous or are hidden by the user. + IntPtr notifyIconOverflowWindowHandle = Utils.FindWindow("NotifyIconOverflowWindow", null); + IntPtr overflowNotificationAreaHandle = Utils.FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area"); + // Fool the "Overflow Notification Area' window into thinking the mouse is moving over it + // which will force windows to refresh the "Overflow Notification Area' window and remove old icons. + RefreshTrayArea(overflowNotificationAreaHandle); + notifyIconOverflowWindowHandle = IntPtr.Zero; + overflowNotificationAreaHandle = IntPtr.Zero; + } + // Fool the "Notification Area" or "User Promoted Notification Area" window (depends on the version of windows) into thinking the mouse is moving over it + // which will force windows to refresh the "Notification Area" or "User Promoted Notification Area" window and remove old icons. + RefreshTrayArea(notificationAreaHandle); + systemTrayContainerHandle = IntPtr.Zero; + systemTrayHandle = IntPtr.Zero; + sysPagerHandle = IntPtr.Zero; + notificationAreaHandle = IntPtr.Zero; + + } + + + private static void RefreshTrayArea(IntPtr windowHandle) + { + // Moves the mouse around within the window area of the supplied window + RECT rect; + Utils.GetClientRect(windowHandle, out rect); + for (var x = 0; x < rect.right; x += 5) + for (var y = 0; y < rect.bottom; y += 5) + Utils.SendMessage(windowHandle, Utils.WM_MOUSEMOVE, 0, (y << 16) + x); + } }