fix auto-start not working, add support for color profiles, add B3 as supported models for service menu unlock

This commit is contained in:
Vincent Maas committed 2023-11-25 11:47:52 +01:00
1 parent db19a9ad68
commit bc565abd38
17 files changed
+687 -92

No files matched your search

+3 -2
View File
@@ -18,8 +18,8 @@
<PublisherName>Maassoft</PublisherName>
<Company>Maassoft</Company>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>9.7.0.0</ApplicationVersion>
<Version>9.7.0.0</Version>
<ApplicationVersion>9.7.1.0</ApplicationVersion>
<Version>9.7.1.0</Version>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>false</BootstrapperEnabled>
@@ -38,6 +38,7 @@
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
<SignManifests>true</SignManifests>
<AssemblyOriginatorKeyFile>ColorControl_TemporaryKey.pfx</AssemblyOriginatorKeyFile>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<OutputPath>bin\x64\Debug\</OutputPath>
+2 -1
View File
@@ -266,8 +266,9 @@ namespace ColorControl
Logger.Debug("Initializing NVIDIA...Done.");
var rpcService = _serviceProvider.GetRequiredService<RpcClientService>();
var winAdminService = _serviceProvider.GetRequiredService<WinApiAdminService>();
_nvPanel = new NvPanel(_serviceManager.NvService, _trayIcon, Handle, appContextProvider, rpcService);
_nvPanel = new NvPanel(_serviceManager.NvService, _trayIcon, Handle, appContextProvider, rpcService, winAdminService);
_nvPanel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
InitShortcut(NvPanel.SHORTCUTID_NVQA, _config.NvQuickAccessShortcut, _serviceManager.NvService.ToggleQuickAccessForm);
+3 -3
View File
@@ -418,19 +418,19 @@
mnuLgSetSvcMenuFlag.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { miLgFullServiceMenuEnable, miLgFullServiceMenuDisable });
mnuLgSetSvcMenuFlag.Name = "mnuLgSetSvcMenuFlag";
mnuLgSetSvcMenuFlag.Size = new System.Drawing.Size(320, 22);
mnuLgSetSvcMenuFlag.Text = "Enable Full Service Menu (C3/G3 only)";
mnuLgSetSvcMenuFlag.Text = "Enable Full Service Menu (B3/C3/G3 only)";
//
// miLgFullServiceMenuEnable
//
miLgFullServiceMenuEnable.Name = "miLgFullServiceMenuEnable";
miLgFullServiceMenuEnable.Size = new System.Drawing.Size(112, 22);
miLgFullServiceMenuEnable.Size = new System.Drawing.Size(180, 22);
miLgFullServiceMenuEnable.Text = "Enable";
miLgFullServiceMenuEnable.Click += miLgFullServiceMenuEnable_Click;
//
// miLgFullServiceMenuDisable
//
miLgFullServiceMenuDisable.Name = "miLgFullServiceMenuDisable";
miLgFullServiceMenuDisable.Size = new System.Drawing.Size(112, 22);
miLgFullServiceMenuDisable.Size = new System.Drawing.Size(180, 22);
miLgFullServiceMenuDisable.Text = "Disable";
miLgFullServiceMenuDisable.Click += miLgFullServiceMenuDisable_Click;
//
+1 -1
View File
@@ -829,7 +829,7 @@ Do you want to continue?"
mnuLgOLEDMotionPro.Visible = visible;
miLgEnableMotionPro.Visible = enableVisible;
var svcMenuFlagVisible = new[] { "C3", "G3" }.Any(m => device?.ModelName?.Contains(m) == true);
var svcMenuFlagVisible = new[] { "B3", "C3", "G3" }.Any(m => device?.ModelName?.Contains(m) == true);
mnuLgSetSvcMenuFlag.Visible = svcMenuFlagVisible;
+61 -1
View File
@@ -1,4 +1,64 @@
<root>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
+101 -2
View File
@@ -38,8 +38,9 @@ namespace ColorControl.Services.NVIDIA
private string _lastDisplayRefreshRates = string.Empty;
private NotifyIcon _trayIcon;
private RpcClientService _rpcService;
private readonly WinApiAdminService _winApiAdminService;
public NvPanel(NvService nvService, NotifyIcon trayIcon, IntPtr handle, AppContextProvider appContextProvider, RpcClientService rpcService)
public NvPanel(NvService nvService, NotifyIcon trayIcon, IntPtr handle, AppContextProvider appContextProvider, RpcClientService rpcService, WinApiAdminService winApiAdminService)
{
_nvService = nvService;
_trayIcon = trayIcon;
@@ -47,7 +48,7 @@ namespace ColorControl.Services.NVIDIA
_appContextProvider = appContextProvider;
_config = appContextProvider.GetAppContext().Config;
_rpcService = rpcService;
_winApiAdminService = winApiAdminService;
_rpcService.Name = "NvService";
InitializeComponent();
@@ -441,6 +442,13 @@ namespace ColorControl.Services.NVIDIA
FormUtils.BuildDropDownMenu(mnuNvOtherSettings, "Scaling", typeof(Scaling), preset, "scaling", nvPresetScalingMenuItem_Click, unchanged: true, skipValues: new object[] { Scaling.Customized })
.DropDownItems[0].Visible = !isCurrentDisplay;
var itemNameProfile = $"{mnuNvOtherSettings.Name}_Profile";
var profileMenu = FormUtils.BuildMenuItem(mnuNvOtherSettings.DropDownItems, itemNameProfile, "Color Profile");
var subItemNameProfile = $"{itemNameProfile}_SubItem";
var subItemProfile = FormUtils.BuildMenuItem(profileMenu.DropDownItems, subItemNameProfile, "", onClick: nvPresetColorProfileMenuItem_Click);
subItemProfile.Text = (!preset.ColorProfileSettings.ProfileName.IsNullOrWhiteSpace() ? $"{preset.ColorProfileSettings.ProfileName}" : "Unset") + " (click to change)";
FormUtils.BuildDropDownMenu(mnuNvHdmiSettings, "Content type", typeof(InfoFrameVideoContentType), preset.HdmiInfoFrameSettings, "ContentType", nvPresetHdmiContentTypeMenuItem_Click, unchanged: true)
.DropDownItems[0].Visible = !isCurrentDisplay;
FormUtils.BuildDropDownMenu(mnuNvHdmiSettings, "Colorimetry", typeof(InfoFrameVideoColorimetry), preset.HdmiInfoFrameSettings, "Colorimetry", nvPresetHdmiColorimetryMenuItem_Click, unchanged: true)
@@ -847,6 +855,70 @@ namespace ColorControl.Services.NVIDIA
AddOrUpdateItem();
}
private void nvPresetColorProfileMenuItem_Click(object sender, EventArgs e)
{
var preset = GetSelectedNvPreset(true);
_nvService.ResolveDisplay(preset);
if (preset.Display == null)
{
MessageForms.WarningOk($"Display {preset.displayName} not found");
return;
}
var display = preset.Display.Name;
if (!CCD.GetUsePerUserDisplayProfiles(display))
{
if (MessageForms.QuestionYesNo("To be able to select a color profile, user specific settings have to be enabled. Do you want to enable this?") != DialogResult.Yes)
{
return;
}
CCD.SetUsePerUserDisplayProfiles(display, true);
}
var profiles = CCD.GetDisplayColorProfiles(display);
if (!profiles.Any())
{
if (MessageForms.QuestionYesNo("There are no profiles associated with this display. You have to manually add these through the Color Management app. Do you want to open this?") != DialogResult.Yes)
{
return;
}
_winApiAdminService.StartProcess("colorcpl.exe");
return;
}
profiles.Insert(0, "None");
var value = SelectStringValue(preset.ColorProfileSettings.ProfileName, "Color Profile", profiles);
if (value == null)
{
return;
}
if (value == "None")
{
value = null;
}
if (preset.IsDisplayPreset)
{
_nvService.SetColorProfile(preset.Display, value);
UpdateDisplayInfoItems();
return;
}
preset.ColorProfileSettings.ProfileName = value;
AddOrUpdateItem();
}
private void refreshRateMenuItem_Click(object sender, EventArgs e)
{
var refreshRate = (uint)((ToolStripItem)sender).Tag;
@@ -1357,6 +1429,33 @@ namespace ColorControl.Services.NVIDIA
return settingValue;
}
private string SelectStringValue(string currentValue, string label, IEnumerable<string> values)
{
var field = new FieldDefinition
{
Label = label,
FieldType = FieldType.DropDown,
Values = values,
Value = currentValue
};
var resultFields = MessageForms.ShowDialog(field.Label, new[] { field });
if (!resultFields.Any())
{
return null;
}
var strValue = (string)resultFields.First().Value;
if (string.IsNullOrWhiteSpace(strValue))
{
return null;
}
return strValue;
}
private void miNvProfileInspector_Click(object sender, EventArgs e)
{
StartNvProfileInspector();
+9 -1
View File
@@ -4,6 +4,7 @@ using ColorControl.Shared.Contracts;
using ColorControl.Shared.Contracts.NVIDIA;
using Newtonsoft.Json;
using nspector.Common;
using NStandard;
using NvAPIWrapper.Display;
using NvAPIWrapper.Native.Display;
using System;
@@ -43,9 +44,10 @@ namespace ColorControl.Services.NVIDIA
public bool applyHdmiSettings { get; set; }
public NvHdmiInfoFrameSettings HdmiInfoFrameSettings { get; set; }
public NvColorProfileSettings ColorProfileSettings { get; set; }
[JsonIgnore]
public bool IsDisplayPreset => Display != null;
public bool IsDisplayPreset { get; set; }
[JsonIgnore]
public Display Display { get; set; }
[JsonIgnore]
@@ -72,6 +74,7 @@ namespace ColorControl.Services.NVIDIA
driverSettings = new Dictionary<uint, uint>();
ocSettings = new List<NvGpuOcSettings>();
HdmiInfoFrameSettings = new NvHdmiInfoFrameSettings();
ColorProfileSettings = new NvColorProfileSettings();
}
public NvPreset(ColorData colorData) : this()
@@ -106,6 +109,7 @@ namespace ColorControl.Services.NVIDIA
SDRBrightness = preset.SDRBrightness;
scaling = preset.scaling;
ColorProfileSettings = new NvColorProfileSettings(preset.ColorProfileSettings);
applyOther = preset.applyOther;
HdmiInfoFrameSettings = new NvHdmiInfoFrameSettings(preset.HdmiInfoFrameSettings);
@@ -279,6 +283,10 @@ namespace ColorControl.Services.NVIDIA
{
values.Add($"Scaling: {scaling.Value.GetDescription()}");
}
if (!ColorProfileSettings.ProfileName.IsNullOrWhiteSpace())
{
values.Add($"Color profile: {ColorProfileSettings.ProfileName}");
}
if (!values.Any())
{
+44 -8
View File
@@ -28,6 +28,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using static ColorControl.Shared.Native.CCD;
namespace ColorControl.Services.NVIDIA
{
@@ -241,15 +242,18 @@ namespace ColorControl.Services.NVIDIA
}
private void SetCurrentDisplay(NvPreset preset)
{
_currentDisplay = GetPresetDisplay(preset);
}
private Display GetPresetDisplay(NvPreset preset)
{
if (preset.primaryDisplay)
{
_currentDisplay = GetPrimaryDisplay();
}
else
{
_currentDisplay = Display.GetDisplays().FirstOrDefault(x => x.Name.Equals(preset.displayName));
return GetPrimaryDisplay();
}
return Display.GetDisplays().FirstOrDefault(x => x.Name.Equals(preset.displayName));
}
public Display GetPrimaryDisplay()
@@ -346,6 +350,11 @@ namespace ColorControl.Services.NVIDIA
{
SetSDRBrightness(display, preset.SDRBrightness.Value);
}
if (preset.ColorProfileSettings.ProfileName != null)
{
CCD.SetDisplayDefaultColorProfile(display.Name, preset.ColorProfileSettings.ProfileName);
}
}
if (preset.applyHdmiSettings)
@@ -903,9 +912,25 @@ namespace ColorControl.Services.NVIDIA
return pathInfos.FirstOrDefault(c => c.TargetsInfo.Any(ti => ti.DisplayId == display.DisplayDevice.DisplayId));
}
public void SetColorProfile(Display display, string name)
{
CCD.SetDisplayDefaultColorProfile(display.Name, name);
}
public void TestResolution()
{
//var display = GetCurrentDisplay();
//CCD.GetUsePerUserDisplayProfiles(display.Name);
//CCD.GetDisplayColorProfiles(display.Name);
//CCD.InstallColorProfile(@"H:\srgb2000.icm");
//CCD.AddDisplayColorProfile(display.Name, "srgb2000.icm");
//CCD.SetDisplayDefaultColorProfile(display.Name, "srgb2000.icm");
//var displayDevice = display.DisplayDevice;
//var config = DisplayApi.GetDisplayConfig();
@@ -1071,13 +1096,16 @@ namespace ColorControl.Services.NVIDIA
}
var isPrimaryDisplay = displayCount == 1 || display.DisplayDevice.DisplayId == DisplayDevice.GetGDIPrimaryDisplayDevice()?.DisplayId;
var preset = new NvPreset { Display = display, applyColorData = false, primaryDisplay = isPrimaryDisplay };
var preset = new NvPreset { Display = display, IsDisplayPreset = true, applyColorData = false, primaryDisplay = isPrimaryDisplay };
preset.HDREnabled = IsHDREnabled(display);
preset.displayName = FormUtils.ExtendedDisplayName(display.Name);
preset.colorData = GetCurrentColorData(display);
preset.HdmiInfoFrameSettings = GetHdmiSettings(display);
preset.SDRBrightness = GetSDRBrightness(display);
preset.scaling = GetScaling(display);
preset.ColorProfileSettings.ProfileName = CCD.GetDisplayDefaultColorProfile(display.Name, preset.HDREnabled ? COLORPROFILESUBTYPE.CPST_EXTENDED_DISPLAY_COLOR_MODE : COLORPROFILESUBTYPE.CPST_STANDARD_DISPLAY_COLOR_MODE);
var mode = GetCurrentMode(display.Name);
@@ -1100,8 +1128,6 @@ namespace ColorControl.Services.NVIDIA
preset.ditheringMode = (uint)ditherInfo.mode;
}
preset.HDREnabled = IsHDREnabled(display);
if (Config.ShowOverclocking)
{
var gpus = GetGPUs();
@@ -1311,5 +1337,15 @@ namespace ColorControl.Services.NVIDIA
{
Utils.WriteObject(_configFilename, Config);
}
internal void ResolveDisplay(NvPreset preset)
{
if (preset.Display != null)
{
return;
}
preset.Display = GetPresetDisplay(preset);
}
}
}
@@ -25,7 +25,7 @@ namespace ColorControl.Services.Samsung
{
public static SynchronizationContext SyncContext { get; set; }
public bool ConnectionClosed { get; private set; }
public bool? ConnectionClosed { get; private set; }
public bool ClosedByDispose { get; private set; }
private MessageWebSocket _connection;
@@ -133,7 +133,7 @@ namespace ColorControl.Services.Samsung
_tokens.TryAdd(id, taskSource);
await SendMessageAsync(message);
if (ConnectionClosed)
if (ConnectionClosed == true)
{
throw new Exception("Connection closed");
}
@@ -143,7 +143,7 @@ namespace ColorControl.Services.Samsung
else
{
await SendMessageAsync(message);
if (ConnectionClosed)
if (ConnectionClosed == true)
{
throw new Exception("Connection closed");
}
@@ -205,7 +205,7 @@ namespace ColorControl.Services.Samsung
}
else
{
var _ = SelectedDevice.ConnectAsync();
//var _ = SelectedDevice.ConnectAsync();
}
}
}
@@ -0,0 +1,25 @@
namespace ColorControl.Shared.Contracts.NVIDIA
{
public class NvColorProfileSettings
{
public string ProfileName { get; set; }
public NvColorProfileSettings()
{
}
public NvColorProfileSettings(NvColorProfileSettings settings)
{
settings ??= new NvColorProfileSettings();
ProfileName = settings.ProfileName;
}
public override string ToString()
{
var value = string.Empty;
return value;
}
}
}
+378 -21
View File
@@ -1,4 +1,5 @@
using NWin32;
using ColorControl.Shared.Forms;
using NWin32;
using System.ComponentModel;
using System.Runtime.InteropServices;
@@ -8,6 +9,7 @@ namespace ColorControl.Shared.Native
public static class CCD
{
private static bool? IsHDRActive = null;
delegate (T, bool) DisplayConfigModeInfoDelegate<T>(DisplayConfigModeInfo modeInfo);
public enum DisplayTopology
{
@@ -60,7 +62,7 @@ namespace ColorControl.Shared.Native
}
}
public static void SetHDRState(bool enabled, string displayName = null, float? SDRWhiteLevelInNits = null)
public static void SetHDRState(bool enabled, string displayName = null)
{
uint pathCount, modeCount;
@@ -120,7 +122,271 @@ namespace ColorControl.Shared.Native
}
}
public static LUID GetAdapterId(string displayName = null)
{
return ExecuteForModeConfig((modeInfo) =>
{
if (modeInfo.infoType == DisplayConfigModeInfoType.Target)
{
return (modeInfo.adapterId, false);
}
return (default(LUID), true);
}, displayName
);
}
public static uint GetSourceId(string displayName = null)
{
return ExecuteForModeConfig((modeInfo) =>
{
if (modeInfo.infoType == DisplayConfigModeInfoType.Source)
{
return (modeInfo.id, false);
}
return (0u, true);
}, displayName
);
}
public static bool InstallColorProfile(string profileName)
{
var result = NativeMethods.InstallColorProfileW(null, profileName);
if (!result)
{
var err = NWin32.NativeMethods.GetLastError();
throw new Win32Exception((int)err);
}
return true;
}
private static bool MinRequiredVersion = true;
public static bool AddDisplayColorProfile(string displayName, string profileName, bool setAsDefault = true)
{
var adapterId = GetAdapterId(displayName);
var sourceId = GetSourceId(displayName);
var err = NativeMethods.ColorProfileAddDisplayAssociation(WCS_PROFILE_MANAGEMENT_SCOPE.WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER, profileName, adapterId, sourceId, setAsDefault, true);
if (err != NativeConstants.ERROR_SUCCESS)
{
throw new Win32Exception(err);
}
return err == NativeConstants.ERROR_SUCCESS;
}
public static bool RemoveDisplayColorProfile(string displayName, string profileName)
{
var adapterId = GetAdapterId(displayName);
var sourceId = GetSourceId(displayName);
var err = NativeMethods.ColorProfileRemoveDisplayAssociation(WCS_PROFILE_MANAGEMENT_SCOPE.WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER, profileName, adapterId, sourceId, true);
if (err != NativeConstants.ERROR_SUCCESS)
{
throw new Win32Exception(err);
}
return err == NativeConstants.ERROR_SUCCESS;
}
public static bool SetDisplayDefaultColorProfile(string displayName, string profileName)
{
var adapterId = GetAdapterId(displayName);
var sourceId = GetSourceId(displayName);
var subType = COLORPROFILESUBTYPE.CPST_PERCEPTUAL;
if (profileName == null)
{
profileName = GetDisplayDefaultColorProfile(displayName, COLORPROFILESUBTYPE.CPST_EXTENDED_DISPLAY_COLOR_MODE);
if (profileName == null)
{
return true;
}
return RemoveDisplayColorProfile(displayName, profileName) && AddDisplayColorProfile(displayName, profileName, false);
}
var err = NativeMethods.ColorProfileSetDisplayDefaultAssociation(WCS_PROFILE_MANAGEMENT_SCOPE.WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER, profileName, COLORPROFILETYPE.CPT_ICC, subType, adapterId, sourceId);
if (err != NativeConstants.ERROR_SUCCESS)
{
throw new Win32Exception(err);
}
return err == NativeConstants.ERROR_SUCCESS;
}
public static string GetDisplayDefaultColorProfile(string displayName, COLORPROFILESUBTYPE profileSubType = COLORPROFILESUBTYPE.CPST_STANDARD_DISPLAY_COLOR_MODE)
{
if (!MinRequiredVersion)
{
return null;
}
var adapterId = GetAdapterId(displayName);
var sourceId = GetSourceId(displayName);
var scope = GetUsePerUserDisplayProfiles(displayName) ? WCS_PROFILE_MANAGEMENT_SCOPE.WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER : WCS_PROFILE_MANAGEMENT_SCOPE.WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;
try
{
var pointer = new IntPtr();
var err = NativeMethods.ColorProfileGetDisplayDefault(scope, adapterId, sourceId, COLORPROFILETYPE.CPT_ICC, profileSubType, pointer);
if (err != NativeConstants.ERROR_SUCCESS)
{
// No default profile set
if (err == -2147024894)
{
return null;
}
throw new Win32Exception(err);
}
var profileName = Marshal.PtrToStringUni(pointer);
return profileName;
}
catch (Exception)
{
MinRequiredVersion = false;
}
return null;
}
[StructLayout(LayoutKind.Sequential)]
public struct UnmanagedStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public IntPtr[] listOfStrings;
}
public static List<string> GetDisplayColorProfiles(string displayName)
{
var adapterId = GetAdapterId(displayName);
var sourceId = GetSourceId(displayName);
var pointer = IntPtr.Zero;
var scope = GetUsePerUserDisplayProfiles(displayName) ? WCS_PROFILE_MANAGEMENT_SCOPE.WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER : WCS_PROFILE_MANAGEMENT_SCOPE.WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;
var err = NativeMethods.ColorProfileGetDisplayList(scope, adapterId, sourceId, pointer, out var profileCount);
if (err != NativeConstants.ERROR_SUCCESS)
{
throw new Win32Exception(err);
}
// No items
if (pointer == IntPtr.Zero)
{
return new List<string>();
}
var data = Marshal.PtrToStructure<UnmanagedStruct>(pointer);
var list = data.listOfStrings.Take((int)profileCount).Select(p => Marshal.PtrToStringUni(p)).ToList();
return list;
}
public static bool GetUsePerUserDisplayProfiles(string displayName)
{
var deviceKey = GetDisplayDeviceKey(displayName);
if (deviceKey == null)
{
return false;
}
var result = NativeMethods.WcsGetUsePerUserProfiles(deviceKey, DeviceClassFlags.CLASS_MONITOR, out var usePerUserProfiles);
if (!result)
{
//if (err != NativeConstants.ERROR_SUCCESS)
//{
// throw new Win32Exception(err);
//}
}
return usePerUserProfiles;
}
public static bool SetUsePerUserDisplayProfiles(string displayName, bool usePerUserProfiles)
{
var deviceKey = GetDisplayDeviceKey(displayName);
if (deviceKey == null)
{
return false;
}
var result = NativeMethods.WcsSetUsePerUserProfiles(deviceKey, DeviceClassFlags.CLASS_MONITOR, usePerUserProfiles);
return result;
}
private static string GetDisplayDeviceKey(string displayName)
{
var hMonitor = FormUtils.GetMonitorForDisplayName(displayName);
if (hMonitor == IntPtr.Zero)
{
return null;
}
var mInfo = new MONITORINFOEX();
mInfo.cbSize = (uint)Marshal.SizeOf(mInfo);
if (!NativeMethods.GetMonitorInfo(hMonitor, ref mInfo))
{
return null;
}
var dd = new DISPLAYDEVICE();
dd.cb = (uint)Marshal.SizeOf(dd);
if (!NativeMethods.EnumDisplayDevices(mInfo.szDevice, 0, ref dd, 0))
{
return null;
}
return dd.DeviceKey;
}
public static uint GetSDRWhiteLevel(string displayName = null)
{
return ExecuteForModeConfig<uint>((modeInfo) =>
{
if (modeInfo.infoType == DisplayConfigModeInfoType.Target)
{
var requestpacket = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
requestpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>();
requestpacket.header.adapterId = modeInfo.adapterId;
requestpacket.header.id = modeInfo.id;
var err = NativeMethods.DisplayConfigGetDeviceInfo(ref requestpacket);
if (err == NativeConstants.ERROR_SUCCESS && requestpacket.advancedColorSupported)
{
return (GetSDRWhiteLevelForDisplayConfig(modeInfo), false);
}
}
return (0, true);
}, displayName
);
}
private static T ExecuteForModeConfig<T>(DisplayConfigModeInfoDelegate<T> infoDelegate, string displayName = null)
{
uint pathCount, modeCount;
@@ -142,24 +408,13 @@ namespace ColorControl.Shared.Native
continue;
}
var requestpacket = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
requestpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>();
for (int i = 0; i < modeCount; i++)
{
if (modesArray[i].infoType == DisplayConfigModeInfoType.Target)
var result = infoDelegate(modesArray[i]);
if (!result.Item2)
{
requestpacket.header.adapterId = modesArray[i].adapterId;
requestpacket.header.id = modesArray[i].id;
err = NativeMethods.DisplayConfigGetDeviceInfo(ref requestpacket);
if (err == NativeConstants.ERROR_SUCCESS && requestpacket.advancedColorSupported)
{
return GetSDRWhiteLevelForDisplayConfig(modesArray[i]);
}
return result.Item1;
}
}
}
@@ -171,7 +426,7 @@ namespace ColorControl.Shared.Native
throw new Win32Exception(err);
}
return 0;
return default;
}
public static bool IsHDREnabled(string displayName = null)
@@ -314,7 +569,7 @@ namespace ColorControl.Shared.Native
}
[StructLayout(LayoutKind.Sequential)]
struct LUID
public struct LUID
{
uint LowPart;
uint HighPart;
@@ -682,6 +937,87 @@ namespace ColorControl.Shared.Native
public string viewGdiDeviceName;
}
enum WCS_PROFILE_MANAGEMENT_SCOPE
{
WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE,
WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER
};
enum COLORPROFILETYPE
{
CPT_ICC,
CPT_DMP,
CPT_CAMP,
CPT_GMMP
};
public enum COLORPROFILESUBTYPE
{
CPST_PERCEPTUAL,
CPST_RELATIVE_COLORIMETRIC,
CPST_SATURATION,
CPST_ABSOLUTE_COLORIMETRIC,
CPST_NONE,
CPST_RGB_WORKING_SPACE,
CPST_CUSTOM_WORKING_SPACE,
CPST_STANDARD_DISPLAY_COLOR_MODE,
CPST_EXTENDED_DISPLAY_COLOR_MODE
};
enum DeviceClassFlags : UInt32
{
// from: c:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\um\Icm.h
/// <summary>
///#define CLASS_MONITOR 'mntr'
/// </summary>
CLASS_MONITOR = 0x6d6e7472,
/// <summary>
/// #define CLASS_PRINTER 'prtr'
/// </summary>
CLASS_PRINTER = 0x70727472,
/// <summary>
/// #define CLASS_SCANNER 'scnr'
/// </summary>
CLASS_SCANNER = 0x73636e72
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct MONITORINFOEX
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string szDevice;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DISPLAYDEVICE
{
public uint cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
public uint StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;
}
#endregion
static class NativeMethods
@@ -710,8 +1046,29 @@ namespace ColorControl.Shared.Native
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SDR_WHITE_LEVEL requestPacket);
[DllImport("user32")]
public static extern int DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SDR_WHITE_LEVEL setPacket);
[DllImport("mscms", CharSet = CharSet.Unicode)]
public static extern bool InstallColorProfileW(string machineName, string profilename);
[DllImport("mscms", CharSet = CharSet.Unicode)]
public static extern int ColorProfileSetDisplayDefaultAssociation(WCS_PROFILE_MANAGEMENT_SCOPE scope, string profilename, COLORPROFILETYPE profiletype, COLORPROFILESUBTYPE profilesubtype, LUID targetadapterid, uint sourceid);
[DllImport("mscms", CharSet = CharSet.Unicode)]
public static extern int ColorProfileAddDisplayAssociation(WCS_PROFILE_MANAGEMENT_SCOPE scope, string profilename, LUID targetadapterid, uint sourceid, bool setAsDefault, bool associateAsAdvancedColor);
[DllImport("mscms", CharSet = CharSet.Unicode)]
public static extern int ColorProfileRemoveDisplayAssociation(WCS_PROFILE_MANAGEMENT_SCOPE scope, string profilename, LUID targetadapterid, uint sourceid, bool associateAsAdvancedColor);
[DllImport("mscms", CharSet = CharSet.Unicode)]
public static extern int ColorProfileGetDisplayList(WCS_PROFILE_MANAGEMENT_SCOPE scope, LUID targetadapterid, uint sourceid, in IntPtr pointer, out uint profileCount);
[DllImport("mscms", CharSet = CharSet.Unicode)]
public static extern int ColorProfileGetDisplayDefault(WCS_PROFILE_MANAGEMENT_SCOPE scope, LUID targetadapterid, uint sourceid, COLORPROFILETYPE profiletype, COLORPROFILESUBTYPE profilesubtype, in IntPtr pointer);
[DllImport("mscms", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool WcsGetUsePerUserProfiles(string deviceName, DeviceClassFlags deviceClass, out bool usePerUserProfiles);
[DllImport("mscms", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool WcsSetUsePerUserProfiles(string deviceName, DeviceClassFlags deviceClass, bool usePerUserProfiles);
[DllImport("user32.dll")]
public extern static bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAYDEVICE lpDisplayDevice, uint dwFlags);
}
}
}
+9 -10
View File
@@ -34,8 +34,7 @@ public class RpcServerService
var arg = message.Arguments[i];
var methodParam = methodParams[i];
var convertedArg = ConvertArgument(arg, methodParam.ParameterType);
convertedArgs.Add(arg);
AddArgument(arg, methodParam.ParameterType, convertedArgs);
}
Logger.Debug($"Executing RPC: {message.ServiceName}.{message.MethodName}({string.Join(", ", convertedArgs)})");
@@ -54,21 +53,22 @@ public class RpcServerService
}
}
private object ConvertArgument(object arg, Type type)
private void AddArgument(object arg, Type type, List<object> list)
{
if (arg.GetType() != type)
{
if (type.IsEnum)
{
return Enum.ToObject(type, arg);
// Add enum directly to list and not return as 'object'
list.Add(Enum.ToObject(type, arg));
return;
}
var convertedArg = Convert.ChangeType(arg, type);
return convertedArg;
list.Add(Convert.ChangeType(arg, type));
return;
}
return arg;
list.Add(arg);
}
public async Task<SvcResultMessage> ExecuteRpcTypedAsync(SvcRpcMessageTyped message)
@@ -92,8 +92,7 @@ public class RpcServerService
var arg = prop.GetValue(message);
var methodParam = methodParams[paramIndex];
var convertedArg = ConvertArgument(arg, methodParam.ParameterType);
convertedArgs.Add(arg);
AddArgument(arg, methodParam.ParameterType, convertedArgs);
paramIndex++;
}
+2 -5
View File
@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace novideo_srgb
@@ -15,7 +12,7 @@ namespace novideo_srgb
private static extern int QueryDisplayConfig(QDC flags, ref int numPathArrayElements, [In, Out] DISPLAYCONFIG_PATH_INFO[] pathArray, ref int numModeInfoArrayElements, [In, Out] DISPLAYCONFIG_MODE_INFO[] modeInfoArray, out DISPLAYCONFIG_TOPOLOGY_ID currentTopologyId);
[DllImport("user32")]
private static extern int QueryDisplayConfig(QDC flags, ref int numPathArrayElements, [In, Out] DISPLAYCONFIG_PATH_INFO[] pathArray, ref int numModeInfoArrayElements, [In, Out] DISPLAYCONFIG_MODE_INFO[] modeInfoArray, IntPtr currentTopologyId);
private static extern int QueryDisplayConfig(QDC flags, ref int numPathArrayElements, [In, Out] DISPLAYCONFIG_PATH_INFO[] path4Array, ref int numModeInfoArrayElements, [In, Out] DISPLAYCONFIG_MODE_INFO[] modeInfoArray, IntPtr currentTopologyId);
[DllImport("user32")]
private static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO requestPacket);
+1 -2
View File
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.IO;
namespace novideo_srgb
{
+6 -2
View File
@@ -37,11 +37,15 @@ namespace novideo_srgb
foreach (var display in Display.GetDisplays())
{
var displays = WindowsDisplayAPI.Display.GetDisplays();
var path = displays.First(x => x.DisplayName == display.Name).DevicePath;
var path = displays.FirstOrDefault(x => x.DisplayName == display.Name)?.DevicePath;
if (path == null)
{
continue;
}
var hdrActive = hdrPaths.Contains(path);
var settings = config?.FirstOrDefault(x => (string)x.Attribute("path") == path);
var settings = config?.FirstOrDefault(x => (string?)x.Attribute("path") == path);
MonitorData monitor;
if (settings != null)
{
+38 -29
View File
@@ -6,6 +6,7 @@ using NvAPIWrapper.Display;
using NvAPIWrapper.GPU;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using static novideo_srgb.Colorimetry;
namespace novideo_srgb
{
@@ -87,49 +88,57 @@ namespace novideo_srgb
_clamped = Novideo.IsColorSpaceConversionActive(_output);
}
if (!doClamp) return;
if (!doClamp)
{
return;
}
if (_clamped) Thread.Sleep(100);
if (UseEdid)
Novideo.SetColorSpaceConversion(_output, Colorimetry.RGBToRGB(TargetColorSpace, EdidColorSpace));
else if (UseIcc)
{
var profile = ICCMatrixProfile.FromFile(ProfilePath);
if (CalibrateGamma)
SetICCProfile(ProfilePath, SelectedGamma, TargetColorSpace);
}
}
private void SetICCProfile(string fileName, int selectedGamma, ColorSpace colorSpace)
{
var profile = ICCMatrixProfile.FromFile(fileName);
if (CalibrateGamma)
{
var trcBlack = Matrix.FromValues(new[,]
{
var trcBlack = Matrix.FromValues(new[,]
{
{ profile.trcs[0].SampleAt(0) },
{ profile.trcs[1].SampleAt(0) },
{ profile.trcs[2].SampleAt(0) }
});
var black = (profile.matrix * trcBlack)[1];
var black = (profile.matrix * trcBlack)[1];
ToneCurve gamma;
switch (SelectedGamma)
{
case 0:
gamma = new SrgbEOTF(black);
break;
case 1:
gamma = new GammaToneCurve(2.4, black, 0);
break;
case 2:
gamma = new GammaToneCurve(CustomGamma, black, CustomPercentage / 100);
break;
case 3:
gamma = new GammaToneCurve(CustomGamma, black, CustomPercentage / 100, true);
break;
default:
throw new NotSupportedException("Unsupported gamma type " + SelectedGamma);
}
Novideo.SetColorSpaceConversion(_output, profile, TargetColorSpace, gamma, DisableOptimization);
}
else
ToneCurve gamma;
switch (selectedGamma)
{
Novideo.SetColorSpaceConversion(_output, profile, TargetColorSpace);
case 0:
gamma = new SrgbEOTF(black);
break;
case 1:
gamma = new GammaToneCurve(2.4, black, 0);
break;
case 2:
gamma = new GammaToneCurve(CustomGamma, black, CustomPercentage / 100);
break;
case 3:
gamma = new GammaToneCurve(CustomGamma, black, CustomPercentage / 100, true);
break;
default:
throw new NotSupportedException("Unsupported gamma type " + selectedGamma);
}
Novideo.SetColorSpaceConversion(_output, profile, colorSpace, gamma, DisableOptimization);
}
else
{
Novideo.SetColorSpaceConversion(_output, profile, colorSpace);
}
}