add creation of SDR color profile, add NVIDIA HDR10+ support, fix MinTML/MaxTML, add LG TPC/GSR actions, add LG/Samsung trigger events

This commit is contained in:
Vincent Maas committed 2024-02-11 14:38:53 +01:00
1 parent c958e6cb04
commit b1120c8d49
39 files changed
+1323 -349

No files matched your search

+2 -2
View File
@@ -18,8 +18,8 @@
<PublisherName>Maassoft</PublisherName>
<Company>Maassoft</Company>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>9.8.0.1</ApplicationVersion>
<Version>9.8.0.1</Version>
<ApplicationVersion>9.8.1.0</ApplicationVersion>
<Version>9.8.1.0</Version>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>false</BootstrapperEnabled>
+11 -3
View File
@@ -64,6 +64,7 @@
btnUpdate = new System.Windows.Forms.Button();
mnuColorProfiles = new System.Windows.Forms.ContextMenuStrip(components);
miCreateHDRColorProfile = new System.Windows.Forms.ToolStripMenuItem();
miCreateSDRColorProfile = new System.Windows.Forms.ToolStripMenuItem();
tcMain.SuspendLayout();
tabOptions.SuspendLayout();
grpOptionsModules.SuspendLayout();
@@ -475,9 +476,9 @@
// mnuColorProfiles
//
mnuColorProfiles.ImageScalingSize = new System.Drawing.Size(20, 20);
mnuColorProfiles.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { miCreateHDRColorProfile });
mnuColorProfiles.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { miCreateHDRColorProfile, miCreateSDRColorProfile });
mnuColorProfiles.Name = "mnuLgButtons";
mnuColorProfiles.Size = new System.Drawing.Size(214, 26);
mnuColorProfiles.Size = new System.Drawing.Size(214, 70);
//
// miCreateHDRColorProfile
//
@@ -486,6 +487,13 @@
miCreateHDRColorProfile.Text = "Create HDR Color Profile...";
miCreateHDRColorProfile.Click += miCreateHDRColorProfile_Click;
//
// miCreateSDRColorProfile
//
miCreateSDRColorProfile.Name = "miCreateSDRColorProfile";
miCreateSDRColorProfile.Size = new System.Drawing.Size(213, 22);
miCreateSDRColorProfile.Text = "Create SDR Color Profile...";
miCreateSDRColorProfile.Click += miCreateSDRColorProfile_Click;
//
// MainForm
//
AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
@@ -503,7 +511,6 @@
Deactivate += MainForm_Deactivate;
FormClosing += MainForm_FormClosing;
FormClosed += MainForm_FormClosed;
Load += MainForm_Load;
Shown += MainForm_Shown;
ResizeBegin += MainForm_ResizeBegin;
ResizeEnd += MainForm_ResizeEnd;
@@ -557,6 +564,7 @@
private System.Windows.Forms.Button btnOptionsColorProfiles;
private System.Windows.Forms.ContextMenuStrip mnuColorProfiles;
private System.Windows.Forms.ToolStripMenuItem miCreateHDRColorProfile;
private System.Windows.Forms.ToolStripMenuItem miCreateSDRColorProfile;
}
}
+32 -13
View File
@@ -168,7 +168,7 @@ namespace ColorControl
_initialized = true;
AfterInitialized();
Task.Run(AfterInitialized);
if (_config.UseDarkMode)
{
@@ -176,10 +176,6 @@ namespace ColorControl
}
}
private void MainForm_Load(object sender, EventArgs e)
{
}
private void LoadModules()
{
_modules.Add("NVIDIA controller", InitNvService);
@@ -236,6 +232,8 @@ namespace ColorControl
}
tcMain.SelectedIndex = 0;
_serviceManager.NvService?.InstallEventHandlers();
}
private void UpdateServiceInfo()
@@ -352,7 +350,7 @@ namespace ColorControl
{
_serviceManager.LgService = _serviceProvider.GetRequiredService<LgService>();
_lgPanel = new LgPanel(_serviceManager.LgService, _serviceManager.NvService, _serviceManager.AmdService, _trayIcon, Handle);
_lgPanel = new LgPanel(_serviceManager.LgService, _serviceManager.NvService, _serviceManager.AmdService, _trayIcon, Handle, _powerEventDispatcher);
_lgPanel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
_lgPanel.Init();
@@ -378,7 +376,7 @@ namespace ColorControl
//_serviceManager.SamsungService.Init();
_samsungPanel = new SamsungPanel(_serviceManager.SamsungService, _serviceManager.NvService, _serviceManager.AmdService, Handle);
_samsungPanel = new SamsungPanel(_serviceManager.SamsungService, _serviceManager.NvService, _serviceManager.AmdService, Handle, _powerEventDispatcher);
_samsungPanel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
_samsungPanel.Init();
@@ -958,13 +956,21 @@ NOTE: installing the service may cause a User Account Control popup.");
}
}
private void AfterInitialized()
private async Task AfterInitialized()
{
_nvPanel?.AfterInitialized();
_amdPanel?.AfterInitialized();
await _powerEventDispatcher.SendEventAsync(PowerEventDispatcher.Event_Startup);
if (_nvPanel != null)
{
await _nvPanel.AfterInitialized();
}
if (_amdPanel != null)
{
await _amdPanel.AfterInitialized();
}
if (_trayIcon.Visible)
{
var _ = CheckForUpdates();
await CheckForUpdates();
}
}
@@ -1289,7 +1295,7 @@ Currently ColorControl is {(_winApiService.IsAdministrator() ? "" : "not ")}runn
//InstallUpdate("");
//await Test();
//_serviceManager.NvService.TestResolution();
_serviceManager.NvService?.TestResolution();
}
private async Task Test()
@@ -1395,8 +1401,15 @@ Currently ColorControl is {(_winApiService.IsAdministrator() ? "" : "not ")}runn
SubLabel = "This enables shortcuts to work during applications/games that block certain keys (like WinKey or Control). NOTE: if the application in the foreground runs with higher privileges than ColorControl, Raw Input does not work and normal hot keys are used",
Value = _config.UseRawInput
};
var setMinTmlAndMaxTmlField = new FieldDefinition
{
FieldType = FieldType.CheckBox,
Label = "Set MinTML and MaxTML when applying color profiles",
SubLabel = "When this is enabled MinTML and MaxTML will be automatically be set to respectively the minimum luminance and the maximum luminance of the color profile",
Value = _config.SetMinTmlAndMaxTml
};
var values = MessageForms.ShowDialog("Advanced settings", new[] { processPollingIntervalField, useRawInputField });
var values = MessageForms.ShowDialog("Advanced settings", new[] { processPollingIntervalField, useRawInputField, setMinTmlAndMaxTmlField });
if (values?.Any() != true)
{
@@ -1405,6 +1418,7 @@ Currently ColorControl is {(_winApiService.IsAdministrator() ? "" : "not ")}runn
_config.ProcessMonitorPollingInterval = processPollingIntervalField.ValueAsInt;
_config.UseRawInput = useRawInputField.ValueAsBool;
_config.SetMinTmlAndMaxTml = setMinTmlAndMaxTmlField.ValueAsBool;
KeyboardShortcutManager.SetUseRawInput(_config.UseRawInput);
}
@@ -1441,5 +1455,10 @@ Currently ColorControl is {(_winApiService.IsAdministrator() ? "" : "not ")}runn
{
mnuColorProfiles.ShowCustom(btnOptionsColorProfiles);
}
private void miCreateSDRColorProfile_Click(object sender, EventArgs e)
{
ColorProfileWindow.CreateAndShow(isHDR: false);
}
}
}
+1
View File
@@ -273,6 +273,7 @@ namespace ColorControl
.ConfigureServices(services =>
{
services.RegisterSharedServices();
services.AddSingleton<PowerEventDispatcher>();
services.AddHostedService<ColorControlBackgroundService>();
})
.Build();
+2 -2
View File
@@ -120,9 +120,9 @@ namespace ColorControl.Services.AMD
ServiceFormUtils.AddOrUpdateListItem(lvAmdPresets, _amdService.GetPresets(), _config, preset);
}
public void AfterInitialized()
public async Task AfterInitialized()
{
var _ = ApplyAmdPresetOnStartup();
await ApplyAmdPresetOnStartup();
}
private async Task ApplyAmdPresetOnStartup(int attempts = 5)
+23 -5
View File
@@ -1,4 +1,5 @@
using ColorControl.Shared.Common;
using ColorControl.Services.EventDispatcher;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using NStandard;
using System;
@@ -68,10 +69,10 @@ namespace ColorControl.Services.Common
var active = Conditions == PresetConditionType.None ||
(Conditions.HasFlag(PresetConditionType.SDR) ? !context.IsHDRActive : true) && (Conditions.HasFlag(PresetConditionType.HDR) ? context.IsHDRActive : true) &&
(Conditions.HasFlag(PresetConditionType.GsyncDisabled) ? !context.IsGsyncActive : true) && (Conditions.HasFlag(PresetConditionType.GsyncEnabled) ? context.IsGsyncActive : true);
var allProcesses = IncludedProcesses.Contains("*");
if (Trigger == PresetTriggerType.ProcessSwitch)
if (Trigger == PresetTriggerType.ProcessSwitch && context.Triggers.Contains(Trigger))
{
var allProcesses = IncludedProcesses.Contains("*");
active = active && (allProcesses || (context.ChangedProcesses?.Any() ?? false));
if (active)
@@ -88,6 +89,15 @@ namespace ColorControl.Services.Common
active = included && !excluded && screenSizeCheck && notificationsDisabledCheck;
}
}
else if ((Trigger == PresetTriggerType.ScreensaverStart || Trigger == PresetTriggerType.ScreensaverStop) && context.Triggers.Contains(Trigger))
{
active = active && (Trigger == PresetTriggerType.ScreensaverStart && context.ScreenSaverTransitionState == ScreenSaverTransitionState.Started ||
Trigger == PresetTriggerType.ScreensaverStop && context.ScreenSaverTransitionState == ScreenSaverTransitionState.Stopped);
}
else
{
active = context.Triggers.Contains(Trigger);
}
return active;
}
@@ -99,7 +109,13 @@ namespace ColorControl.Services.Common
return string.Empty;
}
return $"On {Trigger.GetDescription()} of {DisplayProcesses(IncludedProcesses)}{(ExcludedProcesses.Any() ? ", excluding " + DisplayProcesses(ExcludedProcesses) : string.Empty)}{(Conditions > 0 ? ", only in " + string.Join(", ", Utils.GetDescriptions<PresetConditionType>((int)Conditions)) : string.Empty)}";
var text = $"On {Trigger.GetDescription()}";
if (Trigger == PresetTriggerType.ProcessSwitch)
{
text += $"of {DisplayProcesses(IncludedProcesses)}{(ExcludedProcesses.Any() ? ", excluding " + DisplayProcesses(ExcludedProcesses) : string.Empty)}";
}
return text += $"{(Conditions > 0 ? ", only in " + string.Join(", ", Utils.GetDescriptions<PresetConditionType>((int)Conditions)) : string.Empty)}";
}
public static string DisplayProcesses(IEnumerable<string> processes)
@@ -110,12 +126,14 @@ namespace ColorControl.Services.Common
class PresetTriggerContext
{
public IEnumerable<PresetTriggerType> Triggers { get; set; } = new[] { PresetTriggerType.ProcessSwitch };
public bool IsHDRActive { get; set; }
public bool IsGsyncActive { get; set; }
public Process ForegroundProcess { get; set; }
public bool ForegroundProcessIsFullScreen { get; set; }
public List<Process> ChangedProcesses { get; set; }
public bool IsNotificationDisabled { get; set; }
public ScreenSaverTransitionState ScreenSaverTransitionState { get; set; }
}
internal abstract class PresetBase
@@ -177,7 +195,7 @@ namespace ColorControl.Services.Common
return name;
}
public void UpdateTrigger(PresetTriggerType triggerType, PresetConditionType conditions, string includedProcesses, string excludedProcesses)
public void UpdateTrigger(PresetTriggerType triggerType, PresetConditionType conditions, string includedProcesses = null, string excludedProcesses = null)
{
if (!Triggers.Any())
{
+58 -1
View File
@@ -1,7 +1,10 @@
using ColorControl.Shared.Services;
using ColorControl.Services.EventDispatcher;
using ColorControl.Shared.Native;
using ColorControl.Shared.Services;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -27,6 +30,7 @@ namespace ColorControl.Services.Common
protected List<T> _presets;
protected T _lastAppliedPreset;
protected string _loadPresetsError;
protected T _lastTriggeredPreset;
protected AppContextProvider _appContextProvider;
@@ -176,5 +180,58 @@ namespace ColorControl.Services.Common
QuickAccessForm<T>.ToggleQuickAccessForm(this);
}
protected async Task<PresetTriggerContext> CreateTriggerContext(ServiceManager serviceManager, ProcessChangedEventArgs context = null, bool? isHDRActive = null, IList<PresetTriggerType> triggerTypes = null)
{
triggerTypes ??= new[] { PresetTriggerType.ProcessSwitch };
var changedProcesses = new List<Process>();
if (context?.ForegroundProcess != null)
{
changedProcesses.Add(context.ForegroundProcess);
}
var isGsyncActive = await serviceManager.HandleExternalServiceAsync("GsyncEnabled", new[] { "" });
var triggerContext = new PresetTriggerContext
{
Triggers = triggerTypes,
IsHDRActive = isHDRActive ?? CCD.IsHDREnabled(),
IsGsyncActive = isGsyncActive,
ForegroundProcess = context?.ForegroundProcess,
ForegroundProcessIsFullScreen = context?.ForegroundProcessIsFullScreen ?? false,
IsNotificationDisabled = context?.IsNotificationDisabled ?? false,
ChangedProcesses = changedProcesses,
ScreenSaverTransitionState = context?.ScreenSaverTransitionState ?? ScreenSaverTransitionState.None
};
return triggerContext;
}
protected async Task ExecuteScreenSaverPresets(ServiceManager serviceManager, ProcessChangedEventArgs context, bool? isHDRActive = null)
{
await ExecuteEventPresets(serviceManager, new[] { PresetTriggerType.ScreensaverStart, PresetTriggerType.ScreensaverStop }, context, isHDRActive);
}
public async Task ExecuteEventPresets(ServiceManager serviceManager, IList<PresetTriggerType> triggerTypes, ProcessChangedEventArgs context = null, bool? isHDRActive = null)
{
var triggerContext = await CreateTriggerContext(serviceManager, context, isHDRActive, triggerTypes);
var triggerPresets = _presets.Where(p => p.Triggers.Any(t => t.TriggerActive(triggerContext))).ToList();
if (!triggerPresets.Any())
{
return;
}
Logger.Debug($"Executing event presets count: {triggerPresets.Count}");
foreach (var preset in triggerPresets)
{
Logger.Debug($"Executing event preset: {preset.name}");
await ApplyPreset(preset);
}
}
}
}
@@ -1,6 +1,5 @@
using ColorControl.Services.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Forms;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -101,7 +100,7 @@ namespace ColorControl.Shared.Forms
{
menu.DropDownItems.Clear();
foreach (var nvPreset in service?.GetPresets() ?? new List<T>())
foreach (var nvPreset in service?.GetPresets()?.OrderBy(p => p.name).ToList() ?? new List<T>())
{
var text = nvPreset.name;
@@ -53,6 +53,8 @@ public class EventDispatcher<T> where T : EventArgs
{
await asyncEventHandlers[eventName]?.InvokeAsync(this, eventArgs);
}
DispatchEvent(eventName, eventArgs);
}
protected bool HasHandlers(string eventName)
@@ -1,5 +1,7 @@
using Microsoft.Win32;
using NWin32;
using System;
using System.Threading.Tasks;
namespace ColorControl.Services.EventDispatcher
{
@@ -36,10 +38,13 @@ namespace ColorControl.Services.EventDispatcher
{
public const string Event_Suspend = "Suspend";
public const string Event_Resume = "Resume";
public const string Event_Startup = "Startup";
public const string Event_Shutdown = "Shutdown";
public const string Event_MonitorOff = "MonitorOff";
public const string Event_MonitorOn = "MonitorOn";
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public PowerEventDispatcher()
{
SystemEvents.PowerModeChanged += OnPowerModeChanged;
@@ -50,6 +55,7 @@ namespace ColorControl.Services.EventDispatcher
var state = eventName switch
{
Event_Shutdown => PowerOnOffState.ShutDown,
Event_Startup => PowerOnOffState.StartUp,
Event_MonitorOff => PowerOnOffState.MonitorOff,
Event_MonitorOn => PowerOnOffState.MonitorOn,
_ => PowerOnOffState.None
@@ -60,19 +66,52 @@ namespace ColorControl.Services.EventDispatcher
return;
}
if (state == PowerOnOffState.ShutDown)
{
DispatchEventWithExecutionState(Event_Shutdown, PowerOnOffState.ShutDown);
return;
}
DispatchEvent(eventName, new PowerStateChangedEventArgs(state));
}
private void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)
public async Task SendEventAsync(string eventName)
{
var state = eventName switch
{
Event_Startup => PowerOnOffState.StartUp,
_ => PowerOnOffState.None
};
await DispatchEventAsync(eventName, new PowerStateChangedEventArgs(state));
}
private async void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
if (e.Mode == PowerModes.Suspend)
{
DispatchEvent(Event_Suspend, new PowerStateChangedEventArgs(PowerOnOffState.StandBy));
DispatchEventWithExecutionState(Event_Suspend, PowerOnOffState.StandBy);
}
else if (e.Mode == PowerModes.Resume)
{
await DispatchEventAsync(Event_Resume, new PowerStateChangedEventArgs(PowerOnOffState.Resume));
DispatchEvent(Event_Resume, new PowerStateChangedEventArgs(PowerOnOffState.Resume));
}
}
private void DispatchEventWithExecutionState(string eventName, PowerOnOffState powerState)
{
var error = NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS | NativeConstants.ES_SYSTEM_REQUIRED | NativeConstants.ES_AWAYMODE_REQUIRED);
try
{
Logger.Debug($"SetThreadExecutionState: {error}, Thread#: {Environment.CurrentManagedThreadId}");
DispatchEvent(eventName, new PowerStateChangedEventArgs(powerState));
}
finally
{
var error2 = NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS);
Logger.Debug($"SetThreadExecutionState (reset): {error2}, Thread#: {Environment.CurrentManagedThreadId}");
}
}
}
}
@@ -10,6 +10,14 @@ using System.Threading.Tasks;
namespace ColorControl.Services.EventDispatcher
{
public enum ScreenSaverTransitionState
{
None = 0,
Started = 1,
Running = 2,
Stopped = 3
}
public class ProcessChangedEventArgs : EventArgs
{
public IList<Process> StartedProcesses { get; set; }
@@ -21,6 +29,7 @@ namespace ColorControl.Services.EventDispatcher
public string LastFullScreenProcessName = string.Empty;
public bool StoppedFullScreen { get; set; }
public bool IsScreenSaverActive { get; set; }
public ScreenSaverTransitionState ScreenSaverTransitionState { get; set; }
}
@@ -100,7 +109,12 @@ namespace ColorControl.Services.EventDispatcher
{
var process = context.RunningProcesses.FirstOrDefault(p => p.Id == processId);
context.IsScreenSaverActive = process?.ProcessName?.Contains(".scr") == true;
var screenSaverActive = process?.ProcessName?.Contains(".scr") == true;
context.IsScreenSaverActive = screenSaverActive;
context.ScreenSaverTransitionState = screenSaverActive ?
context.ScreenSaverTransitionState == ScreenSaverTransitionState.None ? ScreenSaverTransitionState.Started : ScreenSaverTransitionState.Running :
context.ScreenSaverTransitionState == ScreenSaverTransitionState.Running ? ScreenSaverTransitionState.Stopped : ScreenSaverTransitionState.None;
context.ForegroundProcess = process;
context.ForegroundProcessIsFullScreen = isFullScreen;
+31
View File
@@ -272,6 +272,8 @@ namespace ColorControl.Services.LG
AddSetConfigAction("tv.conti.supportUsedTime", typeof(BoolFalseToTrue), title: "Total Power On Time");
AddGenericPictureAction("wolwowlOnOff", typeof(FalseToTrue), category: "network", title: "Wake-On-LAN");
AddLunaAction("TPC", typeof(BoolFalseToTrue), title: "Temporal Peak Luminance Control (TPC)", ModelYear.Series2020);
AddLunaAction("GSR", typeof(BoolFalseToTrue), title: "Global Sticky/Stress Reduction (GSR)", ModelYear.Series2020);
}
~LgDevice()
@@ -360,6 +362,22 @@ namespace ColorControl.Services.LG
_invokableActions.Add(action);
}
private void AddLunaAction(string name, Type type, string title, ModelYear fromModelYear = ModelYear.None)
{
var action = new InvokableAction
{
Name = name,
AsyncFunction = GenericLunaAction,
EnumType = type,
Title = title == null ? Utils.FirstCharUpperCase(name) : title,
Category = "Config",
FromModelYear = fromModelYear,
Advanced = true
};
_invokableActions.Add(action);
}
public void AddGameBarAction(string name)
{
if (!ActionsOnGameBar.Contains(name))
@@ -1145,6 +1163,19 @@ namespace ColorControl.Services.LG
return true;
}
private async Task<bool> GenericLunaAction(Dictionary<string, object> parameters)
{
await CheckConnectionAsync();
var key = parameters["name"].ToString();
var values = parameters["value"] as object[];
var value = values[0];
await _lgTvApi.SetTpcOrGsr(key, value?.ToString() == "bool_true");
return true;
}
private void UpdateCurrentValueOfAction(string settingName, string value)
{
if (settingName == "backlight" || settingName == "contrast" || settingName == "brightness" || settingName == "color")
+24 -6
View File
@@ -1,5 +1,6 @@
using ColorControl.Services.AMD;
using ColorControl.Services.Common;
using ColorControl.Services.EventDispatcher;
using ColorControl.Services.NVIDIA;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
@@ -12,6 +13,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -28,6 +30,7 @@ namespace ColorControl.Services.LG
private LgService _lgService;
private NvService _nvService;
private AmdService _amdService;
private readonly PowerEventDispatcher _powerEventDispatcher;
private IntPtr _mainHandle;
private NotifyIcon _trayIcon;
@@ -35,13 +38,14 @@ namespace ColorControl.Services.LG
private bool _disableEvents = false;
private LgGameBar _gameBarForm;
internal LgPanel(LgService lgService, NvService nvService, AmdService amdService, NotifyIcon trayIcon, IntPtr handle)
internal LgPanel(LgService lgService, NvService nvService, AmdService amdService, NotifyIcon trayIcon, IntPtr handle, PowerEventDispatcher powerEventDispatcher)
{
_lgService = lgService;
_nvService = nvService;
_amdService = amdService;
_trayIcon = trayIcon;
_mainHandle = handle;
_powerEventDispatcher = powerEventDispatcher;
_config = Shared.Common.GlobalContext.CurrentContext.Config;
@@ -72,10 +76,17 @@ namespace ColorControl.Services.LG
public void Init()
{
_lgService.RefreshDevices(afterStartUp: true).ContinueWith((_) => FormUtils.BeginInvokeCheck(this, () => AfterLgServiceRefreshDevices()));
var _ = Handle;
_powerEventDispatcher.RegisterAsyncEventHandler(PowerEventDispatcher.Event_Startup, PowerStateChanged);
_lgService.InstallEventHandlers();
}
private async Task PowerStateChanged(object sender, PowerStateChangedEventArgs e, CancellationToken token)
{
await _lgService.RefreshDevices(afterStartUp: true).ContinueWith((_) => FormUtils.BeginInvokeCheck(this, AfterLgServiceRefreshDevices));
}
private void _lgService_SelectedDeviceChangedEvent(object sender, EventArgs e)
{
FormUtils.BeginInvokeCheck(this, () => SetLgDevicesSelectedIndex(sender));
@@ -92,7 +103,7 @@ namespace ColorControl.Services.LG
cbxLgDevices.SelectedIndex = cbxLgDevices.Items.IndexOf(sender);
}
private void AfterLgServiceRefreshDevices()
private async Task AfterLgServiceRefreshDevices()
{
FillLgDevices();
@@ -100,7 +111,14 @@ namespace ColorControl.Services.LG
if (startUpParams.ExecuteLgPreset)
{
var _ = _lgService.ApplyPreset(startUpParams.LgPresetName);
await _lgService.ApplyPreset(startUpParams.LgPresetName);
return;
}
if (startUpParams.RunningFromScheduledTask)
{
await _lgService.ExecuteEventPresets(PresetTriggerType.Startup);
}
}
@@ -320,7 +338,7 @@ namespace ColorControl.Services.LG
chkLgRemoteControlShow.Checked = _lgService.Config.ShowRemoteControl;
scLgController.Panel2Collapsed = !_lgService.Config.ShowRemoteControl;
FormUtils.BuildComboBox(cbxLgPresetTrigger, PresetTriggerType.Resume, PresetTriggerType.Shutdown, PresetTriggerType.Standby, PresetTriggerType.Startup, PresetTriggerType.Reserved5, PresetTriggerType.ScreensaverStart, PresetTriggerType.ScreensaverStop);
FormUtils.BuildComboBox(cbxLgPresetTrigger, PresetTriggerType.Reserved5);
if (!string.IsNullOrEmpty(_lgTabMessage))
{
@@ -660,7 +678,7 @@ namespace ColorControl.Services.LG
return;
}
if (e.Index is 0 or 1 or 4 or 7 && !(device.PowerOnAfterResume || device.PowerOnAfterStartup || device.PowerOnAfterScreenSaver || device.PowerOnByWindows))
if (e.Index is 0 or 1 or 5 or 8 && !(device.PowerOnAfterResume || device.PowerOnAfterStartup || device.PowerOnAfterScreenSaver || device.PowerOnByWindows))
{
MessageForms.InfoOk(
@"Be sure to activate the following setting on the TV, or the app will not be able to wake the TV:
+109 -79
View File
@@ -59,7 +59,6 @@ namespace ColorControl.Services.LG
private bool _poweredOffByScreenSaver;
private int _poweredOffByScreenSaverProcessId;
private LgPreset _lastTriggeredPreset;
private RestartDetector _restartDetector;
private readonly WinApiService _winApiService;
private readonly WinApiAdminService _winApiAdminService;
@@ -321,7 +320,7 @@ namespace ColorControl.Services.LG
{
if (_allowPowerOn)
{
WakeAfterStartupOrResume();
await WakeAfterStartupOrResume();
}
else
{
@@ -415,7 +414,7 @@ namespace ColorControl.Services.LG
return SelectedDevice?.Wake() ?? false;
}
internal void WakeAfterStartupOrResume(PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
internal Task WakeAfterStartupOrResume(PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
{
Devices.ForEach(d => d.ClearPowerOffTask());
@@ -423,13 +422,13 @@ namespace ColorControl.Services.LG
state == PowerOnOffState.Resume && d.PowerOnAfterResume ||
state == PowerOnOffState.ScreenSaver && d.PowerOnAfterScreenSaver);
PowerOnDevicesTask(wakeDevices, state, checkUserSession);
return PowerOnDevicesTask(wakeDevices, state, checkUserSession);
}
public void InstallEventHandlers()
{
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Suspend, PowerModeChanged);
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Resume, PowerModeChanged);
_powerEventDispatcher.RegisterAsyncEventHandler(PowerEventDispatcher.Event_Resume, PowerModeResume);
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Shutdown, PowerModeChanged);
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_MonitorOff, PowerModeChanged);
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_MonitorOn, PowerModeChanged);
@@ -462,25 +461,39 @@ namespace ColorControl.Services.LG
}
}
private async Task PowerModeResume(object sender, PowerStateChangedEventArgs e, CancellationToken _)
{
Logger.Debug($"PowerModeChanged: {e.State}");
await WakeAfterStartupOrResume(PowerOnOffState.Resume);
ExecutePresetsForEvent(PresetTriggerType.Resume);
}
private void PowerModeChanged(object sender, PowerStateChangedEventArgs e)
{
Logger.Debug($"PowerModeChanged: {e.State}");
if (Devices?.Any() != true)
{
Logger.Debug("Devices have not been loaded, ignoring event");
return;
}
switch (e.State)
{
case PowerOnOffState.Resume:
{
WakeAfterStartupOrResume(PowerOnOffState.Resume);
break;
}
case PowerOnOffState.StandBy:
{
ExecutePresetsForEvent(PresetTriggerType.Standby);
var devices = Devices.Where(d => d.PowerOffOnStandby);
PowerOffDevices(devices, PowerOnOffState.StandBy);
break;
}
case PowerOnOffState.ShutDown:
{
ExecutePresetsForEvent(PresetTriggerType.Shutdown);
var devices = Devices.Where(d => d.PowerOffOnShutdown);
PowerOffDevices(devices);
@@ -502,6 +515,26 @@ namespace ColorControl.Services.LG
}
}
private void ExecutePresetsForEvent(PresetTriggerType triggerType)
{
var presets = _presets.Where(p => p.Triggers.Any(t => t.Trigger == triggerType)).ToList();
if (!presets.Any())
{
return;
}
var applicableDevices = Devices.Where(d => d.TriggersEnabled &&
presets.Any(p => (string.IsNullOrEmpty(p.DeviceMacAddress) && d == SelectedDevice) || p.DeviceMacAddress.Equals(d.MacAddress, StringComparison.OrdinalIgnoreCase))).ToList();
if (!applicableDevices.Any())
{
return;
}
var _ = Task.WhenAll(ExecuteEventPresets(_serviceManager, new[] { triggerType }, isHDRActive: applicableDevices.Any(d => d.IsUsingHDRPictureMode()))).ConfigureAwait(true);
}
private void SessionEnded(object sender, SessionEndedEventArgs e)
{
//if (e.Reason == SessionEndReasons.SystemShutdown)
@@ -535,10 +568,15 @@ namespace ColorControl.Services.LG
{
try
{
var wasConnected = Devices.Any(d => (d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver) && d.IsConnected());
var wasConnected = Devices?.Any(d => (d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver) && d.IsConnected());
if (!wasConnected.HasValue)
{
return;
}
var applicableDevices = Devices.Where(d => d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver || d.TriggersEnabled && _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None) &&
((string.IsNullOrEmpty(p.DeviceMacAddress) && d == SelectedDevice) || p.DeviceMacAddress.Equals(d.MacAddress, StringComparison.OrdinalIgnoreCase))));
((string.IsNullOrEmpty(p.DeviceMacAddress) && d == SelectedDevice) || p.DeviceMacAddress.Equals(d.MacAddress, StringComparison.OrdinalIgnoreCase)))).ToList();
if (!applicableDevices.Any())
{
@@ -573,7 +611,9 @@ namespace ColorControl.Services.LG
_poweredOffByScreenSaver = false;
_poweredOffByScreenSaverProcessId = 0;
WakeAfterStartupOrResume(PowerOnOffState.ScreenSaver);
await WakeAfterStartupOrResume(PowerOnOffState.ScreenSaver);
await ExecuteScreenSaverPresetsCustom(args, applicableDevices);
return;
}
@@ -582,7 +622,7 @@ namespace ColorControl.Services.LG
if (!connectedDevices.Any())
{
if (wasConnected)
if (wasConnected == true)
{
Logger.Debug("Process monitor: TV(s) where connected, but not any longer");
wasConnected = false;
@@ -609,7 +649,7 @@ namespace ColorControl.Services.LG
}
}
if (!wasConnected)
if (wasConnected == false)
{
Logger.Debug("Process monitor: TV(s) where not connected, but connection has now been established");
wasConnected = true;
@@ -622,6 +662,7 @@ namespace ColorControl.Services.LG
await ExecuteProcessPresets(args, connectedDevices);
await ExecuteScreenSaverPresetsCustom(args, connectedDevices);
}
catch (Exception ex)
{
@@ -629,6 +670,18 @@ namespace ColorControl.Services.LG
}
}
private async Task ExecuteScreenSaverPresetsCustom(ProcessChangedEventArgs context, IList<LgDevice> connectedDevices)
{
var triggerDevices = connectedDevices.Where(d => d.TriggersEnabled).ToList();
if (!triggerDevices.Any())
{
return;
}
await ExecuteScreenSaverPresets(_serviceManager, context, triggerDevices.Any(d => d.IsUsingHDRPictureMode()));
}
private async Task ExecuteProcessPresets(ProcessChangedEventArgs context, IList<LgDevice> connectedDevices)
{
var triggerDevices = connectedDevices.Where(d => d.TriggersEnabled).ToList();
@@ -648,12 +701,6 @@ namespace ColorControl.Services.LG
return;
}
var changedProcesses = new List<Process>();
if (context.ForegroundProcess != null)
{
changedProcesses.Add(context.ForegroundProcess);
}
if (context.ForegroundProcess != null && context.ForegroundProcessIsFullScreen)
{
presets = presets.Where(p => p.Triggers.Any(t => t.Conditions.HasFlag(PresetConditionType.FullScreen)));
@@ -663,19 +710,7 @@ namespace ColorControl.Services.LG
presets = presets.Where(p => p.Triggers.Any(t => !t.Conditions.HasFlag(PresetConditionType.FullScreen)));
}
var isHDRActive = triggerDevices.Any(d => d.IsUsingHDRPictureMode());
var isGsyncActive = await _serviceManager.HandleExternalServiceAsync("GsyncEnabled", new[] { "" });
var triggerContext = new PresetTriggerContext
{
IsHDRActive = isHDRActive,
IsGsyncActive = isGsyncActive,
ForegroundProcess = context.ForegroundProcess,
ForegroundProcessIsFullScreen = context.ForegroundProcessIsFullScreen,
ChangedProcesses = changedProcesses,
IsNotificationDisabled = context.IsNotificationDisabled
};
var triggerContext = await CreateTriggerContext(_serviceManager, context, triggerDevices.Any(d => d.IsUsingHDRPictureMode()));
var toApplyPresets = presets.Where(p => p.Triggers.Any(t => t.TriggerActive(triggerContext))).ToList();
@@ -805,63 +840,53 @@ namespace ColorControl.Services.LG
return;
}
var error = NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS | NativeConstants.ES_SYSTEM_REQUIRED | NativeConstants.ES_AWAYMODE_REQUIRED);
try
if (state == PowerOnOffState.ShutDown)
{
Logger.Debug($"SetThreadExecutionState: {error}, Thread#: {Environment.CurrentManagedThreadId}");
if (state == PowerOnOffState.ShutDown)
var sleep = Config.ShutdownDelay;
Logger.Debug($"PowerOffDevices: Waiting for a maximum of {sleep} milliseconds...");
while (sleep > 0 && _restartDetector?.PowerOffDetected == false)
{
var sleep = Config.ShutdownDelay;
Thread.Sleep(100);
Logger.Debug($"PowerOffDevices: Waiting for a maximum of {sleep} milliseconds...");
while (sleep > 0 && _restartDetector?.PowerOffDetected == false)
if (_restartDetector != null && (_restartDetector.RestartDetected || _restartDetector.IsRebootInProgress()))
{
Thread.Sleep(100);
if (_restartDetector != null && (_restartDetector.RestartDetected || _restartDetector.IsRebootInProgress()))
{
Logger.Debug("Not powering off because of a restart");
return;
}
sleep -= 100;
Logger.Debug("Not powering off because of a restart");
return;
}
sleep -= 100;
}
Logger.Debug("Powering off tv(s)...");
var tasks = new List<Task>();
foreach (var device in devices)
{
var task = device.PowerOff(true, false);
tasks.Add(task);
}
if (state == PowerOnOffState.StandBy)
{
var standByScript = Path.Combine(Program.DataDir, "StandByScript.bat");
if (File.Exists(standByScript))
{
_winApiAdminService.StartProcess(standByScript, hidden: true, wait: true);
}
}
// We can't use async here because we need to stay on the main thread...
var _ = Task.WhenAll(tasks.ToArray()).ConfigureAwait(true);
Logger.Debug("Done powering off tv(s)");
}
finally
Logger.Debug("Powering off tv(s)...");
var tasks = new List<Task>();
foreach (var device in devices)
{
var error2 = NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS);
Logger.Debug($"SetThreadExecutionState (reset): {error2}, Thread#: {Environment.CurrentManagedThreadId}");
var task = device.PowerOff(true, false);
tasks.Add(task);
}
if (state == PowerOnOffState.StandBy)
{
var standByScript = Path.Combine(Program.DataDir, "StandByScript.bat");
if (File.Exists(standByScript))
{
_winApiAdminService.StartProcess(standByScript, hidden: true, wait: true);
}
}
// We can't use async here because we need to stay on the main thread...
var _ = Task.WhenAll(tasks.ToArray()).ConfigureAwait(true);
Logger.Debug("Done powering off tv(s)");
}
public void PowerOnDevicesTask(IEnumerable<LgDevice> devices, PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
public Task PowerOnDevicesTask(IEnumerable<LgDevice> devices, PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
{
Task.Run(async () => await PowerOnDevices(devices, state, checkUserSession));
return Task.Run(async () => await PowerOnDevices(devices, state, checkUserSession));
}
public async Task PowerOnDevices(IEnumerable<LgDevice> devices, PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
@@ -924,5 +949,10 @@ namespace ColorControl.Services.LG
PipeUtils.SendMessage(message);
}
public async Task ExecuteEventPresets(PresetTriggerType triggerType)
{
await ExecuteEventPresets(_serviceManager, new[] { triggerType });
}
}
}
+64 -8
View File
@@ -80,6 +80,10 @@ namespace ColorControl.Services.NVIDIA
miHDRIncluded = new System.Windows.Forms.ToolStripMenuItem();
miToggleHDR = new System.Windows.Forms.ToolStripMenuItem();
miHDREnabled = new System.Windows.Forms.ToolStripMenuItem();
miHDROuputMode = new System.Windows.Forms.ToolStripMenuItem();
miHDROutputModeUnchanged = new System.Windows.Forms.ToolStripMenuItem();
miHDROutputModeHDR10 = new System.Windows.Forms.ToolStripMenuItem();
miHDROutputModeHDR10Plus = new System.Windows.Forms.ToolStripMenuItem();
mnuNvDriverSettings = new System.Windows.Forms.ToolStripMenuItem();
miNvDriverSettingsIncluded = new System.Windows.Forms.ToolStripMenuItem();
mnuNvOtherSettings = new System.Windows.Forms.ToolStripMenuItem();
@@ -98,6 +102,8 @@ namespace ColorControl.Services.NVIDIA
miNvTestDithering = new System.Windows.Forms.ToolStripMenuItem();
miNVIDIAInfo = new System.Windows.Forms.ToolStripMenuItem();
lvNvPresetsToolTip = new System.Windows.Forms.ToolTip(components);
mnuNvTriggers = new System.Windows.Forms.ToolStripMenuItem();
miNvTrigger1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
mnuNvPresets.SuspendLayout();
mnuNvSettings.SuspendLayout();
SuspendLayout();
@@ -328,9 +334,9 @@ namespace ColorControl.Services.NVIDIA
// mnuNvPresets
//
mnuNvPresets.ImageScalingSize = new System.Drawing.Size(20, 20);
mnuNvPresets.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { miNvApply, miNvPresetApplyOnStartup, tssNvPresetMenu, mnuNvDisplay, mnuNvPresetsColorSettings, mnuNvPresetsColorEnhancements, mnuRefreshRate, mnuNvResolution, miNvPresetDithering, miNvHDR, mnuNvDriverSettings, mnuNvOtherSettings, mnuNvHdmiSettings, mnuNvOverclocking, miNvCopyId });
mnuNvPresets.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { miNvApply, miNvPresetApplyOnStartup, tssNvPresetMenu, mnuNvDisplay, mnuNvPresetsColorSettings, mnuNvPresetsColorEnhancements, mnuRefreshRate, mnuNvResolution, miNvPresetDithering, miNvHDR, mnuNvDriverSettings, mnuNvOtherSettings, mnuNvHdmiSettings, mnuNvOverclocking, mnuNvTriggers, miNvCopyId });
mnuNvPresets.Name = "mnuNvPresets";
mnuNvPresets.Size = new System.Drawing.Size(185, 340);
mnuNvPresets.Size = new System.Drawing.Size(185, 362);
mnuNvPresets.Opening += mnuNvPresets_Opening;
//
// miNvApply
@@ -377,7 +383,7 @@ namespace ColorControl.Services.NVIDIA
// miNvPresetColorSettings
//
miNvPresetColorSettings.Name = "miNvPresetColorSettings";
miNvPresetColorSettings.Size = new System.Drawing.Size(180, 22);
miNvPresetColorSettings.Size = new System.Drawing.Size(120, 22);
miNvPresetColorSettings.Text = "Included";
miNvPresetColorSettings.Click += miNvPresetColorSettings_Click;
//
@@ -391,7 +397,7 @@ namespace ColorControl.Services.NVIDIA
// miNvPresetColorEnhancementsIncluded
//
miNvPresetColorEnhancementsIncluded.Name = "miNvPresetColorEnhancementsIncluded";
miNvPresetColorEnhancementsIncluded.Size = new System.Drawing.Size(180, 22);
miNvPresetColorEnhancementsIncluded.Size = new System.Drawing.Size(120, 22);
miNvPresetColorEnhancementsIncluded.Text = "Included";
miNvPresetColorEnhancementsIncluded.Click += miNvPresetColorEnhancementsIncluded_Click;
//
@@ -525,7 +531,7 @@ namespace ColorControl.Services.NVIDIA
//
// miNvHDR
//
miNvHDR.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { miHDRIncluded, miToggleHDR, miHDREnabled });
miNvHDR.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { miHDRIncluded, miToggleHDR, miHDREnabled, miHDROuputMode });
miNvHDR.Name = "miNvHDR";
miNvHDR.Size = new System.Drawing.Size(184, 22);
miNvHDR.Text = "HDR";
@@ -533,24 +539,54 @@ namespace ColorControl.Services.NVIDIA
// miHDRIncluded
//
miHDRIncluded.Name = "miHDRIncluded";
miHDRIncluded.Size = new System.Drawing.Size(136, 22);
miHDRIncluded.Size = new System.Drawing.Size(142, 22);
miHDRIncluded.Text = "Included";
miHDRIncluded.Click += miHDRIncluded_Click;
//
// miToggleHDR
//
miToggleHDR.Name = "miToggleHDR";
miToggleHDR.Size = new System.Drawing.Size(136, 22);
miToggleHDR.Size = new System.Drawing.Size(142, 22);
miToggleHDR.Text = "Toggle HDR";
miToggleHDR.Click += miToggleHDR_Click;
//
// miHDREnabled
//
miHDREnabled.Name = "miHDREnabled";
miHDREnabled.Size = new System.Drawing.Size(136, 22);
miHDREnabled.Size = new System.Drawing.Size(142, 22);
miHDREnabled.Text = "Enabled";
miHDREnabled.Click += miHDREnabled_Click;
//
// miHDROuputMode
//
miHDROuputMode.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { miHDROutputModeUnchanged, miHDROutputModeHDR10, miHDROutputModeHDR10Plus });
miHDROuputMode.Name = "miHDROuputMode";
miHDROuputMode.Size = new System.Drawing.Size(142, 22);
miHDROuputMode.Text = "Ouput Mode";
//
// miHDROutputModeUnchanged
//
miHDROutputModeUnchanged.Name = "miHDROutputModeUnchanged";
miHDROutputModeUnchanged.Size = new System.Drawing.Size(135, 22);
miHDROutputModeUnchanged.Text = "Unchanged";
miHDROutputModeUnchanged.Click += miHDROutputModeUnchanged_Click;
//
// miHDROutputModeHDR10
//
miHDROutputModeHDR10.Name = "miHDROutputModeHDR10";
miHDROutputModeHDR10.Size = new System.Drawing.Size(135, 22);
miHDROutputModeHDR10.Tag = "1";
miHDROutputModeHDR10.Text = "HDR10";
miHDROutputModeHDR10.Click += miHDROutputModeUnchanged_Click;
//
// miHDROutputModeHDR10Plus
//
miHDROutputModeHDR10Plus.Name = "miHDROutputModeHDR10Plus";
miHDROutputModeHDR10Plus.Size = new System.Drawing.Size(135, 22);
miHDROutputModeHDR10Plus.Tag = "2";
miHDROutputModeHDR10Plus.Text = "HDR10+";
miHDROutputModeHDR10Plus.Click += miHDROutputModeUnchanged_Click;
//
// mnuNvDriverSettings
//
mnuNvDriverSettings.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { miNvDriverSettingsIncluded });
@@ -691,6 +727,20 @@ namespace ColorControl.Services.NVIDIA
miNVIDIAInfo.Text = "NVIDIA Info";
miNVIDIAInfo.Click += miNVIDIAInfo_Click;
//
// mnuNvTriggers
//
mnuNvTriggers.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { miNvTrigger1ToolStripMenuItem });
mnuNvTriggers.Name = "mnuNvTriggers";
mnuNvTriggers.Size = new System.Drawing.Size(184, 22);
mnuNvTriggers.Text = "Triggers";
//
// miNvTrigger1ToolStripMenuItem
//
miNvTrigger1ToolStripMenuItem.Name = "miNvTrigger1ToolStripMenuItem";
miNvTrigger1ToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
miNvTrigger1ToolStripMenuItem.Text = "Trigger 1";
miNvTrigger1ToolStripMenuItem.Click += miNvTrigger1ToolStripMenuItem_Click;
//
// NvPanel
//
AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
@@ -796,5 +846,11 @@ namespace ColorControl.Services.NVIDIA
private System.Windows.Forms.ToolStripMenuItem miNVIDIAInfo;
private System.Windows.Forms.ToolStripMenuItem mnuNvPresetsColorEnhancements;
private System.Windows.Forms.ToolStripMenuItem miNvPresetColorEnhancementsIncluded;
private System.Windows.Forms.ToolStripMenuItem miHDROuputMode;
private System.Windows.Forms.ToolStripMenuItem miHDROutputModeUnchanged;
private System.Windows.Forms.ToolStripMenuItem miHDROutputModeHDR10;
private System.Windows.Forms.ToolStripMenuItem miHDROutputModeHDR10Plus;
private System.Windows.Forms.ToolStripMenuItem mnuNvTriggers;
private System.Windows.Forms.ToolStripMenuItem miNvTrigger1ToolStripMenuItem;
}
}
+79 -4
View File
@@ -1,4 +1,5 @@
using ColorControl.Shared.Common;
using ColorControl.Services.Common;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Contracts.NVIDIA;
using ColorControl.Shared.Forms;
@@ -351,6 +352,8 @@ namespace ColorControl.Services.NVIDIA
miNvOtherIncluded.Visible = presetItemsVisible;
mnuNvHdmiSettings.Enabled = preset != null;
miNvHdmiIncluded.Visible = presetItemsVisible;
miHDROuputMode.Visible = _nvService.OutputModeAvailable;
mnuNvTriggers.Visible = false; // TODO: implement later
if (preset == null)
{
@@ -369,6 +372,9 @@ namespace ColorControl.Services.NVIDIA
miNvResolutionIncluded.Visible = !isCurrentDisplay;
miRefreshRateIncluded.Visible = !isCurrentDisplay;
miHDRIncluded.Visible = !isCurrentDisplay;
miHDROutputModeUnchanged.Visible = !isCurrentDisplay;
miHDROutputModeHDR10.Checked = preset.HdrSettings.OutputMode == NvHdrSettings.NV_DISPLAY_OUTPUT_MODE.NV_DISPLAY_OUTPUT_MODE_HDR10;
miHDROutputModeHDR10Plus.Checked = preset.HdrSettings.OutputMode == NvHdrSettings.NV_DISPLAY_OUTPUT_MODE.NV_DISPLAY_OUTPUT_MODE_HDR10PLUS_GAMING;
if (mnuNvDisplay.DropDownItems.Count == 1)
{
@@ -1616,7 +1622,7 @@ namespace ColorControl.Services.NVIDIA
var text = subItem.Text;
if (index == 7)
if (index == 8)
{
text = text.Replace(", ", "\r\n");
}
@@ -1944,9 +1950,9 @@ namespace ColorControl.Services.NVIDIA
AddOrUpdateItem();
}
public void AfterInitialized()
public async Task AfterInitialized()
{
var _ = ApplyNvPresetOnStartup();
await ApplyNvPresetOnStartup();
}
internal void Save()
@@ -2004,5 +2010,74 @@ namespace ColorControl.Services.NVIDIA
AddOrUpdateItem();
}
private void miHDROutputModeUnchanged_Click(object sender, EventArgs e)
{
var preset = GetSelectedNvPreset(true);
var menuItem = (ToolStripMenuItem)sender;
var outputMode = menuItem.Tag == null ? default : (string)menuItem.Tag == "1" ? NvHdrSettings.NV_DISPLAY_OUTPUT_MODE.NV_DISPLAY_OUTPUT_MODE_HDR10 : NvHdrSettings.NV_DISPLAY_OUTPUT_MODE.NV_DISPLAY_OUTPUT_MODE_HDR10PLUS_GAMING;
if (preset.IsDisplayPreset)
{
if (outputMode == default)
{
return;
}
_nvService.SetOutputMode(outputMode, preset.Display);
UpdateDisplayInfoItems();
return;
}
preset.HdrSettings.OutputMode = outputMode == default ? null : outputMode;
AddOrUpdateItem();
}
private void miNvTrigger1ToolStripMenuItem_Click(object sender, EventArgs e)
{
var preset = GetSelectedNvPreset(true);
var menuItem = (ToolStripMenuItem)sender;
var trigger = preset.Triggers.FirstOrDefault() ?? new PresetTrigger();
var triggerTypes = Utils.EnumToDictionary<PresetTriggerType>(new[] { PresetTriggerType.ProcessSwitch, PresetTriggerType.ScreensaverStart, PresetTriggerType.ScreensaverStop, PresetTriggerType.Reserved5 }).Select(d => d.Value);
var conditions = Utils.GetDescriptions<PresetConditionType>(fromValue: 1);
var eventField = new FieldDefinition
{
Label = "Event",
FieldType = FieldType.DropDown,
Values = triggerTypes,
Value = trigger.Trigger.GetDescription()
};
var conditionsField = new FieldDefinition
{
Label = "Conditions",
FieldType = FieldType.Flags,
Values = conditions,
Value = trigger.Conditions
};
var values = MessageForms.ShowDialog("Edit trigger 1", new[] {
eventField,
conditionsField
});
if (!values.Any())
{
return;
}
trigger.Trigger = eventField.ValueAsEnum<PresetTriggerType>();
trigger.Conditions = conditionsField.ValueAsEnum<PresetConditionType>();
preset.UpdateTrigger(trigger.Trigger, trigger.Conditions);
AddOrUpdateItem();
}
}
}
+7 -1
View File
@@ -47,6 +47,7 @@ namespace ColorControl.Services.NVIDIA
public bool applyHdmiSettings { get; set; }
public NvHdmiInfoFrameSettings HdmiInfoFrameSettings { get; set; }
public NvColorProfileSettings ColorProfileSettings { get; set; }
public NvHdrSettings HdrSettings { get; set; }
[JsonIgnore]
public bool IsDisplayPreset { get; set; }
@@ -80,6 +81,7 @@ namespace ColorControl.Services.NVIDIA
HdmiInfoFrameSettings = new NvHdmiInfoFrameSettings();
ColorProfileSettings = new NvColorProfileSettings();
ColorEnhancementSettings = new NvColorEnhancementSettings();
HdrSettings = new NvHdrSettings();
}
public NvPreset(ColorData colorData) : this()
@@ -102,6 +104,8 @@ namespace ColorControl.Services.NVIDIA
applyHDR = preset.applyHDR;
HDREnabled = preset.HDREnabled;
toggleHDR = preset.toggleHDR;
HdrSettings = new NvHdrSettings(preset.HdrSettings);
applyDithering = preset.applyDithering;
ditheringEnabled = preset.ditheringEnabled;
applyRefreshRate = preset.applyRefreshRate;
@@ -168,7 +172,9 @@ namespace ColorControl.Services.NVIDIA
var dithering = GetDitheringDescription();
values.Add(FormatDisplaySetting(dithering, isCurrent, applyDithering));
values.Add(FormatDisplaySetting(toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled", isCurrent, applyHDR));
var hdrEnabledDesc = HDREnabled ? $"Enabled{(HdrSettings.OutputMode.HasValue ? (HdrSettings.OutputMode == NvHdrSettings.NV_DISPLAY_OUTPUT_MODE.NV_DISPLAY_OUTPUT_MODE_HDR10PLUS_GAMING ? " (HDR10+)" : " (HDR10)") : "")}" : "";
values.Add(FormatDisplaySetting(toggleHDR ? "Toggle" : HDREnabled ? hdrEnabledDesc : "Disabled", isCurrent, applyHDR));
values.Add(FormatDisplaySetting(GetDriverSettingsDescription(), isCurrent, applyDriverSettings));
+286 -71
View File
@@ -1,4 +1,5 @@
using ColorControl.Services.Common;
using ColorControl.Services.EventDispatcher;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Contracts.NVIDIA;
@@ -13,10 +14,12 @@ using nspector.Common.Meta;
using NvAPIWrapper.Display;
using NvAPIWrapper.GPU;
using NvAPIWrapper.Native;
using NvAPIWrapper.Native.Attributes;
using NvAPIWrapper.Native.Display;
using NvAPIWrapper.Native.Display.Structures;
using NvAPIWrapper.Native.General.Structures;
using NvAPIWrapper.Native.GPU.Structures;
using NvAPIWrapper.Native.Helpers;
using NvAPIWrapper.Native.Interfaces.Display;
using NWin32;
using NWin32.NativeTypes;
@@ -28,6 +31,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using static ColorControl.Shared.Contracts.NVIDIA.NvHdrSettings;
using static ColorControl.Shared.Native.CCD;
namespace ColorControl.Services.NVIDIA
@@ -74,6 +78,7 @@ namespace ColorControl.Services.NVIDIA
PreserveSig = true)]
private static extern IntPtr NvAPI64_QueryInterface(uint interfaceId);
[FunctionId(FunctionId.NvAPI_Disp_SetDitherControl)]
public delegate int NvAPI_Disp_SetDitherControl(
[In] PhysicalGPUHandle physicalGpu,
[In] uint OutputId,
@@ -82,6 +87,7 @@ namespace ColorControl.Services.NVIDIA
[In] uint mode
);
[FunctionId(FunctionId.NvAPI_Disp_GetDitherControl)]
public delegate int NvAPI_Disp_GetDitherControl(
[In] uint DisplayId,
[MarshalAs(UnmanagedType.Struct)] ref NV_GPU_DITHER_CONTROL_V1 ditherControl);
@@ -97,6 +103,47 @@ namespace ColorControl.Services.NVIDIA
public uint modeCaps;
};
[FunctionId(FunctionId.NvAPI_Disp_SetOutputMode)]
public delegate int NvAPI_Disp_SetOutputMode(
[In] uint DisplayId,
ref NV_DISPLAY_OUTPUT_MODE pDisplayMode
);
[FunctionId(FunctionId.NvAPI_Disp_GetOutputMode)]
public delegate int NvAPI_Disp_GetOutputMode(
[In] uint DisplayId,
ref NV_DISPLAY_OUTPUT_MODE pDisplayMode
);
//public struct NV_HDR_METADATA_V1
//{
// public uint version; //!< Version of this structure
// public short displayPrimary_x0; //!< x coordinate of color primary 0 (e.g. Red) of mastering display ([0x0000-0xC350] = [0.0 - 1.0])
// public short displayPrimary_y0; //!< y coordinate of color primary 0 (e.g. Red) of mastering display ([0x0000-0xC350] = [0.0 - 1.0])
// public short displayPrimary_x1; //!< x coordinate of color primary 1 (e.g. Green) of mastering display ([0x0000-0xC350] = [0.0 - 1.0])
// public short displayPrimary_y1; //!< y coordinate of color primary 1 (e.g. Green) of mastering display ([0x0000-0xC350] = [0.0 - 1.0])
// public short displayPrimary_x2; //!< x coordinate of color primary 2 (e.g. Blue) of mastering display ([0x0000-0xC350] = [0.0 - 1.0])
// public short displayPrimary_y2; //!< y coordinate of color primary 2 (e.g. Blue) of mastering display ([0x0000-0xC350] = [0.0 - 1.0])
// public short displayWhitePoint_x; //!< x coordinate of white point of mastering display ([0x0000-0xC350] = [0.0 - 1.0])
// public short displayWhitePoint_y; //!< y coordinate of white point of mastering display ([0x0000-0xC350] = [0.0 - 1.0])
// public short max_display_mastering_luminance; //!< Maximum display mastering luminance ([0x0000-0xFFFF] = [0.0 - 65535.0] cd/m^2, in units of 1 cd/m^2)
// public short min_display_mastering_luminance; //!< Minimum display mastering luminance ([0x0000-0xFFFF] = [0.0 - 6.55350] cd/m^2, in units of 0.0001 cd/m^2)
// public short max_content_light_level; //!< Maximum Content Light level (MaxCLL) ([0x0000-0xFFFF] = [0.0 - 65535.0] cd/m^2, in units of 1 cd/m^2)
// public short max_frame_average_light_level; //!< Maximum Frame-Average Light Level (MaxFALL) ([0x0000-0xFFFF] = [0.0 - 65535.0] cd/m^2, in units of 1 cd/m^2)
//}
//public delegate int NvAPI_Disp_GetSourceHdrMetadata(
// [In] uint DisplayId,
// [MarshalAs(UnmanagedType.Struct)] ref NV_HDR_METADATA_V1 pMetadata,
// [In] long sourcePID
//);
public override string ServiceName => "NVIDIA";
protected override string PresetsBaseFilename => "NvPresets.json";
@@ -105,6 +152,7 @@ namespace ColorControl.Services.NVIDIA
private string _baseProfileName = "";
private string _configFilename;
public NvServiceConfig Config { get; private set; }
private NV_DISPLAY_OUTPUT_MODE? Hdr10OutputModeForced { get; set; }
private DrsSettingsService _drs;
private DrsSettingsMetaService _meta;
@@ -131,6 +179,9 @@ namespace ColorControl.Services.NVIDIA
public const uint DRS_RBAR_OPTIONS = 0X000F00BB;
public const uint DRS_RBAR_SIZE_LIMIT = 0X000F00FF;
public uint DriverVersion { get; private set; }
public bool OutputModeAvailable => DriverVersion >= 52500;
private static readonly List<uint> _driverSettingIds = new()
{
DRS_GSYNC_APPLICATION_MODE,
@@ -148,12 +199,16 @@ namespace ColorControl.Services.NVIDIA
};
private readonly WinApiService _winApiService;
private readonly RpcClientService _rpcClientService;
private readonly PowerEventDispatcher _powerEventDispatcher;
private readonly ServiceManager _serviceManager;
private static NvService ServiceInstance;
public NvService(AppContextProvider appContextProvider, WinApiService winApiService, RpcClientService rpcClientService) : base(appContextProvider)
public NvService(AppContextProvider appContextProvider, WinApiService winApiService, RpcClientService rpcClientService, PowerEventDispatcher powerEventDispatcher, ServiceManager serviceManager) : base(appContextProvider)
{
_winApiService = winApiService;
_rpcClientService = rpcClientService;
_powerEventDispatcher = powerEventDispatcher;
_serviceManager = serviceManager;
_rpcClientService.Name = nameof(NvService);
NvPreset.NvService = this;
@@ -162,6 +217,14 @@ namespace ColorControl.Services.NVIDIA
LoadPresets();
}
public void InstallEventHandlers()
{
// TODO: implement later
//_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Suspend, PowerModeChanged);
//_powerEventDispatcher.RegisterAsyncEventHandler(PowerEventDispatcher.Event_Resume, PowerModeResume);
//_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Shutdown, PowerModeChanged);
}
public static async Task<bool> ExecutePresetAsync(string idOrName)
{
try
@@ -354,14 +417,22 @@ namespace ColorControl.Services.NVIDIA
SetColorData(display, preset.colorData);
}
if (applyHdr)
if (preset.applyHDR)
{
if (newHdrEnabled && !hdrEnabled)
if (applyHdr)
{
MainWindow.BeforeDisplaySettingsChange();
if (newHdrEnabled && !hdrEnabled)
{
MainWindow.BeforeDisplaySettingsChange();
}
SetHDRState(display, newHdrEnabled);
}
SetHDRState(display, newHdrEnabled);
if (newHdrEnabled && preset.HdrSettings.OutputMode.HasValue)
{
SetOutputMode(preset.HdrSettings.OutputMode.Value, display);
}
}
if (preset.applyRefreshRate || preset.applyResolution)
@@ -381,7 +452,7 @@ namespace ColorControl.Services.NVIDIA
if (preset.ColorProfileSettings.ProfileName != null)
{
CCD.SetDisplayDefaultColorProfile(display.Name, preset.ColorProfileSettings.ProfileName);
CCD.SetDisplayDefaultColorProfile(display.Name, preset.ColorProfileSettings.ProfileName, _appContextProvider.GetAppContext().Config.SetMinTmlAndMaxTml);
}
}
@@ -575,6 +646,12 @@ namespace ColorControl.Services.NVIDIA
public void SetHDRState(Display display, bool enabled)
{
if (!enabled && Hdr10OutputModeForced.HasValue)
{
SetOutputMode(NV_DISPLAY_OUTPUT_MODE.NV_DISPLAY_OUTPUT_MODE_SDR, display);
Hdr10OutputModeForced = null;
}
CCD.SetHDRState(enabled, display.Name);
}
@@ -637,48 +714,128 @@ namespace ColorControl.Services.NVIDIA
return display.HUEControl.CurrentAngle;
}
//public NV_HDR_METADATA_V1? GetHdrMetaData(Display display = null)
//{
// var ptr = NvAPI64_QueryInterface(0x0D3F52DA);
// if (ptr != IntPtr.Zero)
// {
// var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_GetSourceHdrMetadata)) as NvAPI_Disp_GetSourceHdrMetadata;
// display ??= GetCurrentDisplay();
// var displayDevice = display.DisplayDevice;
// var displayId = displayDevice.DisplayId;
// var metadata = new NV_HDR_METADATA_V1();
// metadata.version = MAKE_NVAPI_VERSION<NV_HDR_METADATA_V1>(1);
// var processId = Process.GetCurrentProcess().Id;
// var resultValue = delegateValue(displayId, ref metadata, processId);
// return resultValue == 0 ? metadata : null;
// }
// return null;
//}
public bool SetOutputMode(NV_DISPLAY_OUTPUT_MODE outputMode, Display display = null)
{
var delegateValue = DelegateFactory.GetDelegate<NvAPI_Disp_SetOutputMode>();
if (delegateValue == null)
{
return false;
}
display ??= GetCurrentDisplay();
var displayDevice = display.DisplayDevice;
var displayId = displayDevice.DisplayId;
var newOutputMode = outputMode;
var previousOutputMode = Hdr10OutputModeForced;
var resultValue = delegateValue(displayId, ref outputMode);
if (resultValue == 0)
{
Hdr10OutputModeForced = newOutputMode > NV_DISPLAY_OUTPUT_MODE.NV_DISPLAY_OUTPUT_MODE_SDR ? newOutputMode : null;
}
if (previousOutputMode == NV_DISPLAY_OUTPUT_MODE.NV_DISPLAY_OUTPUT_MODE_HDR10PLUS_GAMING && newOutputMode == NV_DISPLAY_OUTPUT_MODE.NV_DISPLAY_OUTPUT_MODE_HDR10)
{
// HDR10+ will not be disabled properly when setting output mode to HDR10, just disable and enable HDR again
SetHDRState(display, false);
SetHDRState(display, true);
}
return resultValue == 0;
}
public NV_DISPLAY_OUTPUT_MODE? GetOutputMode(Display display = null)
{
var delegateValue = DelegateFactory.GetDelegate<NvAPI_Disp_GetOutputMode>();
if (delegateValue == null)
{
return null;
}
display ??= GetCurrentDisplay();
var displayDevice = display.DisplayDevice;
var displayId = displayDevice.DisplayId;
var outputMode = NV_DISPLAY_OUTPUT_MODE.NV_DISPLAY_OUTPUT_MODE_SDR;
var resultValue = delegateValue(displayId, ref outputMode);
return resultValue == 0 ? outputMode : null;
}
public bool SetDithering(NvDitherState state, uint bits = 1, uint mode = 4, NvPreset preset = null, Display currentDisplay = null)
{
var delegateValue = DelegateFactory.GetDelegate<NvAPI_Disp_SetDitherControl>();
if (delegateValue == null)
{
return false;
}
var result = true;
var ptr = NvAPI64_QueryInterface(0xDF0DFCDD);
if (ptr != IntPtr.Zero)
var display = currentDisplay ?? GetCurrentDisplay();
if (display == null)
{
var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_SetDitherControl)) as NvAPI_Disp_SetDitherControl;
var display = currentDisplay ?? GetCurrentDisplay();
if (display == null)
{
return false;
}
var displayDevice = display.DisplayDevice;
var gpuHandle = displayDevice.PhysicalGPU.Handle;
var displayId = displayDevice.DisplayId;
if (state == NvDitherState.Auto)
{
// These seem to be ignored in Auto-state
bits = 0;
mode = 0;
}
else if (preset != null)
{
bits = preset.ditheringBits;
mode = preset.ditheringMode;
}
var resultValue = delegateValue(gpuHandle, displayId, (uint)state, bits, mode);
if (resultValue != 0)
{
Logger.Error($"Could not set dithering because NvAPI_Disp_SetDitherControl returned a non-zero return code: {resultValue}");
result = false;
}
return false;
}
else
var displayDevice = display.DisplayDevice;
var gpuHandle = displayDevice.PhysicalGPU.Handle;
var displayId = displayDevice.DisplayId;
if (state == NvDitherState.Auto)
{
Logger.Error($"Could not set dithering because the function NvAPI_Disp_SetDitherControl could not be found");
// These seem to be ignored in Auto-state
bits = 0;
mode = 0;
}
else if (preset != null)
{
bits = preset.ditheringBits;
mode = preset.ditheringMode;
}
var resultValue = delegateValue(gpuHandle, displayId, (uint)state, bits, mode);
if (resultValue != 0)
{
Logger.Error($"Could not set dithering because NvAPI_Disp_SetDitherControl returned a non-zero return code: {resultValue}");
result = false;
}
@@ -687,33 +844,32 @@ namespace ColorControl.Services.NVIDIA
public NV_GPU_DITHER_CONTROL_V1 GetDithering(Display currentDisplay = null)
{
var dither = new NV_GPU_DITHER_CONTROL_V1 { version = 0x10018 };
var version = MAKE_NVAPI_VERSION<NV_GPU_DITHER_CONTROL_V1>(1);
var dither = new NV_GPU_DITHER_CONTROL_V1 { version = version };
var ptr = NvAPI64_QueryInterface(0x932AC8FB);
if (ptr != IntPtr.Zero)
{
var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_GetDitherControl)) as NvAPI_Disp_GetDitherControl;
var delegateValue = DelegateFactory.GetDelegate<NvAPI_Disp_GetDitherControl>();
var display = currentDisplay ?? GetCurrentDisplay();
if (display == null)
{
dither.state = -1;
return dither;
}
var displayDevice = display.DisplayDevice;
var displayId = displayDevice.DisplayId;
var result = delegateValue(displayId, ref dither);
if (result != 0)
{
Logger.Error($"Could not get dithering because NvAPI_Disp_GetDitherControl returned a non-zero return code: {result}");
dither.state = -1;
}
}
else
if (delegateValue == null)
{
dither.state = -2;
return dither;
}
var display = currentDisplay ?? GetCurrentDisplay();
if (display == null)
{
dither.state = -1;
return dither;
}
var displayDevice = display.DisplayDevice;
var displayId = displayDevice.DisplayId;
var result = delegateValue(displayId, ref dither);
if (result != 0)
{
Logger.Error($"Could not get dithering because NvAPI_Disp_GetDitherControl returned a non-zero return code: {result}");
dither.state = -1;
}
return dither;
@@ -879,18 +1035,14 @@ namespace ColorControl.Services.NVIDIA
public InfoFrameVideoContentType GetHDMIContentType(Display display)
{
var displayDevice = display.DisplayDevice;
var info = displayDevice.HDMIVideoFrameOverrideInformation ?? displayDevice.HDMIVideoFrameCurrentInformation;
var info = GetInfoFrameVideo(display);
return info.HasValue ? info.Value.ContentType : InfoFrameVideoContentType.Auto;
}
public NvHdmiInfoFrameSettings GetHdmiSettings(Display display)
{
var displayDevice = display.DisplayDevice;
var info = displayDevice.HDMIVideoFrameOverrideInformation ?? displayDevice.HDMIVideoFrameCurrentInformation;
var info = GetInfoFrameVideo(display);
if (!info.HasValue)
{
@@ -911,6 +1063,13 @@ namespace ColorControl.Services.NVIDIA
};
}
private static InfoFrameVideo? GetInfoFrameVideo(Display display)
{
var displayDevice = display.DisplayDevice;
return Logger.Swallow(() => displayDevice.HDMIVideoFrameOverrideInformation) ?? Logger.Swallow(() => displayDevice.HDMIVideoFrameCurrentInformation);
}
public Scaling GetScaling(Display display)
{
var pathTargetInfo = GetTargetInfoForDisplay(display);
@@ -972,11 +1131,15 @@ namespace ColorControl.Services.NVIDIA
public void SetColorProfile(Display display, string name)
{
CCD.SetDisplayDefaultColorProfile(display.Name, name);
CCD.SetDisplayDefaultColorProfile(display.Name, name, _appContextProvider.GetAppContext().Config.SetMinTmlAndMaxTml);
}
public void TestResolution()
{
//GetHdrMetaData();
//SetOutputMode(NV_DISPLAY_OUTPUT_MODE.NV_DISPLAY_OUTPUT_MODE_HDR10PLUS_GAMING);
//var display = GetCurrentDisplay();
//CCD.GetUsePerUserDisplayProfiles(display.Name);
@@ -1157,6 +1320,7 @@ namespace ColorControl.Services.NVIDIA
var preset = new NvPreset { Display = display, IsDisplayPreset = true, applyColorData = false, primaryDisplay = isPrimaryDisplay };
preset.HDREnabled = IsHDREnabled(display);
preset.HdrSettings.OutputMode = preset.HDREnabled && OutputModeAvailable ? GetOutputMode(display) : null;
preset.displayName = FormUtils.ExtendedDisplayName(display.Name);
preset.colorData = GetCurrentColorData(display);
@@ -1284,6 +1448,9 @@ namespace ColorControl.Services.NVIDIA
NvAPIWrapper.NVIDIA.Initialize();
_initialized = true;
var version = default(string);
DriverVersion = GeneralApi.GetDriverAndBranchVersion(out version);
_drs = DrsServiceLocator.SettingService;
_meta = DrsServiceLocator.MetaService;
@@ -1415,5 +1582,53 @@ namespace ColorControl.Services.NVIDIA
return preset.Display;
}
private async Task PowerModeResume(object sender, PowerStateChangedEventArgs e, CancellationToken _)
{
Logger.Debug($"PowerModeChanged: {e.State}");
// Wait
await Task.Delay(30000);
ExecutePresetsForEvent(PresetTriggerType.Resume);
}
private void PowerModeChanged(object sender, PowerStateChangedEventArgs e)
{
Logger.Debug($"PowerModeChanged: {e.State}");
switch (e.State)
{
case PowerOnOffState.StandBy:
{
ExecutePresetsForEvent(PresetTriggerType.Standby);
break;
}
case PowerOnOffState.ShutDown:
{
ExecutePresetsForEvent(PresetTriggerType.Shutdown);
break;
}
}
}
private void ExecutePresetsForEvent(PresetTriggerType triggerType)
{
Logger.Debug($"Executing presets for event {triggerType}");
var presets = _presets.Where(p => p.Triggers.Any(t => t.Trigger == triggerType)).ToList();
if (!presets.Any())
{
return;
}
ExecuteEventPresets(_serviceManager, new[] { triggerType }).Wait();
}
private static uint MAKE_NVAPI_VERSION<T>(int version)
{
return (uint)((Marshal.SizeOf(typeof(T))) | version << 16);
}
}
}
+23 -9
View File
@@ -1,5 +1,6 @@
using ColorControl.Services.AMD;
using ColorControl.Services.Common;
using ColorControl.Services.EventDispatcher;
using ColorControl.Services.NVIDIA;
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
@@ -11,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -27,17 +29,17 @@ namespace ColorControl.Services.Samsung
private NvService _nvService;
private AmdService _amdService;
private IntPtr _mainHandle;
private readonly PowerEventDispatcher _powerEventDispatcher;
private string _tabMessage;
private bool _disableEvents = false;
internal SamsungPanel(SamsungService samsungService, NvService nvService, AmdService amdService, IntPtr handle)
internal SamsungPanel(SamsungService samsungService, NvService nvService, AmdService amdService, IntPtr handle, PowerEventDispatcher powerEventDispatcher)
{
_samsungService = samsungService;
_nvService = nvService;
_amdService = amdService;
_mainHandle = handle;
_powerEventDispatcher = powerEventDispatcher;
_config = Shared.Common.GlobalContext.CurrentContext.Config;
InitializeComponent();
@@ -70,10 +72,16 @@ namespace ColorControl.Services.Samsung
public void Init()
{
var _ = Handle;
_samsungService.RefreshDevices(afterStartUp: true).ContinueWith((_) => FormUtils.BeginInvokeCheck(this, AfterSamsungServiceRefreshDevices));
_powerEventDispatcher.RegisterAsyncEventHandler(PowerEventDispatcher.Event_Startup, PowerStateChanged);
_samsungService.InstallEventHandlers();
}
private async Task PowerStateChanged(object sender, PowerStateChangedEventArgs e, CancellationToken token)
{
await _samsungService.RefreshDevices(afterStartUp: true).ContinueWith((_) => FormUtils.BeginInvokeCheck(this, AfterSamsungServiceRefreshDevices));
}
private void _samsungService_SelectedDeviceChangedEvent(object sender, EventArgs e)
{
FormUtils.BeginInvokeCheck(this, () => SetSamsungDevicesSelectedIndex(sender));
@@ -90,7 +98,7 @@ namespace ColorControl.Services.Samsung
cbxSamsungDevices.SelectedIndex = cbxSamsungDevices.Items.IndexOf(sender);
}
private void AfterSamsungServiceRefreshDevices()
private async Task AfterSamsungServiceRefreshDevices()
{
FillSamsungDevices();
@@ -98,7 +106,13 @@ namespace ColorControl.Services.Samsung
if (startUpParams.ExecuteSamsungPreset)
{
var _ = _samsungService.ApplyPreset(startUpParams.SamsungPresetName);
await _samsungService.ApplyPreset(startUpParams.SamsungPresetName);
return;
}
if (startUpParams.RunningFromScheduledTask)
{
await _samsungService.ExecuteEventPresets(PresetTriggerType.Startup);
}
}
@@ -318,7 +332,7 @@ namespace ColorControl.Services.Samsung
//chkSamsungRemoteControlShow.Checked = _samsungService.Config.ShowRemoteControl;
//scSamsungController.Panel2Collapsed = !_samsungService.Config.ShowRemoteControl;
FormUtils.BuildComboBox(cbxSamsungPresetTrigger, PresetTriggerType.Resume, PresetTriggerType.Shutdown, PresetTriggerType.Standby, PresetTriggerType.Startup, PresetTriggerType.Reserved5, PresetTriggerType.ScreensaverStart, PresetTriggerType.ScreensaverStop);
FormUtils.BuildComboBox(cbxSamsungPresetTrigger, PresetTriggerType.Reserved5);
if (!string.IsNullOrEmpty(_tabMessage))
{
@@ -649,7 +663,7 @@ namespace ColorControl.Services.Samsung
return;
}
if (e.Index is 0 or 1 or 4 or 7 && !(device.Options.PowerOnAfterResume || device.Options.PowerOnAfterStartup || device.Options.PowerOnAfterScreenSaver || device.Options.PowerOnByWindows))
if (e.Index is 0 or 1 or 5 or 8 && !(device.Options.PowerOnAfterResume || device.Options.PowerOnAfterStartup || device.Options.PowerOnAfterScreenSaver || device.Options.PowerOnByWindows))
{
MessageForms.InfoOk(
@"Be sure to activate the following setting on the TV, or the app will not be able to wake the TV:
@@ -863,7 +877,7 @@ Do you want to continue?"
}
var value = (PresetConditionType)values.First().Value;
edtSamsungPresetTriggerConditions.Tag = (PresetConditionType)values.First().Value;
edtSamsungPresetTriggerConditions.Tag = value;
edtSamsungPresetTriggerConditions.Text = Utils.GetDescriptions<PresetConditionType>((int)value).Join(", ");
}
+112 -74
View File
@@ -37,7 +37,6 @@ namespace ColorControl.Services.Samsung
private string _configFilename;
private bool _poweredOffByScreenSaver;
private int _poweredOffByScreenSaverProcessId;
private object _lastTriggeredPreset;
private List<SamsungApp> _samsungApps = new List<SamsungApp>();
private Shared.Common.GlobalContext _appContext;
@@ -88,7 +87,7 @@ namespace ColorControl.Services.Samsung
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_MonitorOn, PowerModeChanged);
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_MonitorOff, PowerModeChanged);
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Suspend, PowerModeChanged);
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Resume, PowerModeChanged);
_powerEventDispatcher.RegisterAsyncEventHandler(PowerEventDispatcher.Event_Resume, PowerModeResume);
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Shutdown, PowerModeChanged);
_processEventDispatcher.RegisterAsyncEventHandler(ProcessEventDispatcher.Event_ProcessChanged, ProcessChanged);
}
@@ -112,25 +111,39 @@ namespace ColorControl.Services.Samsung
SamsungPreset.SamsungDevices = Config.Devices;
}
private async Task PowerModeResume(object sender, PowerStateChangedEventArgs e, CancellationToken _)
{
Logger.Debug($"PowerModeChanged: {e.State}");
await WakeAfterStartupOrResume(PowerOnOffState.Resume);
ExecutePresetsForEvent(PresetTriggerType.Resume);
}
private void PowerModeChanged(object sender, PowerStateChangedEventArgs e)
{
Logger.Debug($"PowerModeChanged: {e.State}");
if (Devices?.Any() != true)
{
Logger.Debug("Devices have not been loaded, ignoring event");
return;
}
switch (e.State)
{
case PowerOnOffState.Resume:
{
WakeAfterStartupOrResume(PowerOnOffState.Resume);
break;
}
case PowerOnOffState.StandBy:
{
ExecutePresetsForEvent(PresetTriggerType.Standby);
var devices = Devices.Where(d => d.Options.PowerOffOnStandby);
PowerOffDevices(devices, PowerOnOffState.StandBy);
break;
}
case PowerOnOffState.ShutDown:
{
ExecutePresetsForEvent(PresetTriggerType.Shutdown);
var devices = Devices.Where(d => d.Options.PowerOffOnShutdown);
PowerOffDevices(devices);
@@ -152,6 +165,28 @@ namespace ColorControl.Services.Samsung
}
}
private void ExecutePresetsForEvent(PresetTriggerType triggerType)
{
Logger.Debug($"Executing presets for event {triggerType}");
var presets = _presets.Where(p => p.Triggers.Any(t => t.Trigger == triggerType)).ToList();
if (!presets.Any())
{
return;
}
var applicableDevices = Devices.Where(d => d.Options.TriggersEnabled &&
presets.Any(p => (string.IsNullOrEmpty(p.DeviceMacAddress) && d == SelectedDevice) || p.DeviceMacAddress.Equals(d.MacAddress, StringComparison.OrdinalIgnoreCase))).ToList();
if (!applicableDevices.Any())
{
return;
}
ExecuteEventPresets(_serviceManager, new[] { triggerType }).Wait();
}
public async Task RefreshDevices(bool connect = true, bool afterStartUp = false)
{
Devices = Config.Devices;
@@ -201,7 +236,7 @@ namespace ColorControl.Services.Samsung
{
if (_allowPowerOn)
{
WakeAfterStartupOrResume();
await WakeAfterStartupOrResume();
}
else
{
@@ -308,7 +343,7 @@ namespace ColorControl.Services.Samsung
}
}
private void WakeAfterStartupOrResume(PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
private Task WakeAfterStartupOrResume(PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
{
Devices.ForEach(d => d.ClearPowerOffTask());
@@ -316,7 +351,7 @@ namespace ColorControl.Services.Samsung
state == PowerOnOffState.Resume && d.Options.PowerOnAfterResume ||
state == PowerOnOffState.ScreenSaver && d.Options.PowerOnAfterScreenSaver);
PowerOnDevicesTask(wakeDevices, state, checkUserSession);
return PowerOnDevicesTask(wakeDevices, state, checkUserSession);
}
private void PowerOffDevices(IEnumerable<SamsungDevice> devices, PowerOnOffState state = PowerOnOffState.ShutDown)
@@ -332,63 +367,53 @@ namespace ColorControl.Services.Samsung
return;
}
var error = NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS | NativeConstants.ES_SYSTEM_REQUIRED | NativeConstants.ES_AWAYMODE_REQUIRED);
try
if (state == PowerOnOffState.ShutDown)
{
Logger.Debug($"SetThreadExecutionState: {error}, Thread#: {Environment.CurrentManagedThreadId}");
if (state == PowerOnOffState.ShutDown)
var sleep = Config.ShutdownDelay;
Logger.Debug($"PowerOffDevices: Waiting for a maximum of {sleep} milliseconds...");
while (sleep > 0 && _restartDetector?.PowerOffDetected == false)
{
var sleep = Config.ShutdownDelay;
Thread.Sleep(100);
Logger.Debug($"PowerOffDevices: Waiting for a maximum of {sleep} milliseconds...");
while (sleep > 0 && _restartDetector?.PowerOffDetected == false)
if (_restartDetector != null && (_restartDetector.RestartDetected || _restartDetector.IsRebootInProgress()))
{
Thread.Sleep(100);
if (_restartDetector != null && (_restartDetector.RestartDetected || _restartDetector.IsRebootInProgress()))
{
Logger.Debug("Not powering off because of a restart");
return;
}
sleep -= 100;
Logger.Debug("Not powering off because of a restart");
return;
}
sleep -= 100;
}
Logger.Debug("Powering off tv(s)...");
var tasks = new List<Task>();
foreach (var device in devices)
{
var task = device.PowerOffAsync();
tasks.Add(task);
}
if (state == PowerOnOffState.StandBy)
{
var standByScript = Path.Combine(Program.DataDir, "StandByScript.bat");
if (File.Exists(standByScript))
{
_winApiAdminService.StartProcess(standByScript, hidden: true, wait: true);
}
}
// We can't use async here because we need to stay on the main thread...
var _ = Task.WhenAll(tasks.ToArray()).ConfigureAwait(true);
Logger.Debug("Done powering off tv(s)");
}
finally
Logger.Debug("Powering off tv(s)...");
var tasks = new List<Task>();
foreach (var device in devices)
{
var error2 = NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS);
Logger.Debug($"SetThreadExecutionState (reset): {error2}, Thread#: {Environment.CurrentManagedThreadId}");
var task = device.PowerOffAsync();
tasks.Add(task);
}
if (state == PowerOnOffState.StandBy)
{
var standByScript = Path.Combine(Program.DataDir, "StandByScript.bat");
if (File.Exists(standByScript))
{
_winApiAdminService.StartProcess(standByScript, hidden: true, wait: true);
}
}
// We can't use async here because we need to stay on the main thread...
var _ = Task.WhenAll(tasks.ToArray()).ConfigureAwait(true);
Logger.Debug("Done powering off tv(s)");
}
private void PowerOnDevicesTask(IEnumerable<SamsungDevice> devices, PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
private Task PowerOnDevicesTask(IEnumerable<SamsungDevice> devices, PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
{
Task.Run(async () => await PowerOnDevices(devices, state, checkUserSession));
return Task.Run(async () => await PowerOnDevices(devices, state, checkUserSession));
}
private async Task PowerOnDevices(IEnumerable<SamsungDevice> devices, PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
@@ -460,10 +485,15 @@ namespace ColorControl.Services.Samsung
{
try
{
var wasConnected = Devices.Any(d => d.IsConnected());
var wasConnected = Devices?.Any(d => d.IsConnected());
if (!wasConnected.HasValue)
{
return;
}
var applicableDevices = Devices.Where(d => d.Options.PowerOffOnScreenSaver || d.Options.PowerOnAfterScreenSaver || d.Options.TriggersEnabled && _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None) &&
((string.IsNullOrEmpty(p.DeviceMacAddress) && d == SelectedDevice) || p.DeviceMacAddress.Equals(d.MacAddress, StringComparison.OrdinalIgnoreCase))));
((string.IsNullOrEmpty(p.DeviceMacAddress) && d == SelectedDevice) || p.DeviceMacAddress.Equals(d.MacAddress, StringComparison.OrdinalIgnoreCase)))).ToList();
if (!applicableDevices.Any())
{
@@ -498,7 +528,9 @@ namespace ColorControl.Services.Samsung
_poweredOffByScreenSaver = false;
_poweredOffByScreenSaverProcessId = 0;
WakeAfterStartupOrResume(PowerOnOffState.ScreenSaver);
await WakeAfterStartupOrResume(PowerOnOffState.ScreenSaver);
await ExecuteScreenSaverPresetsCustom(args, applicableDevices);
return;
}
@@ -507,7 +539,7 @@ namespace ColorControl.Services.Samsung
if (!connectedDevices.Any())
{
if (wasConnected)
if (wasConnected == true)
{
Logger.Debug("Process monitor: TV(s) where connected, but not any longer");
wasConnected = false;
@@ -534,7 +566,7 @@ namespace ColorControl.Services.Samsung
}
}
if (!wasConnected)
if (wasConnected == false)
{
Logger.Debug("Process monitor: TV(s) where not connected, but connection has now been established");
wasConnected = true;
@@ -547,6 +579,7 @@ namespace ColorControl.Services.Samsung
await ExecuteProcessPresets(args, connectedDevices);
await ExecuteScreenSaverPresetsCustom(args, connectedDevices);
}
catch (Exception ex)
{
@@ -554,6 +587,18 @@ namespace ColorControl.Services.Samsung
}
}
private async Task ExecuteScreenSaverPresetsCustom(ProcessChangedEventArgs context, IList<SamsungDevice> connectedDevices)
{
var triggerDevices = connectedDevices.Where(d => d.Options.TriggersEnabled).ToList();
if (!triggerDevices.Any())
{
return;
}
await ExecuteScreenSaverPresets(_serviceManager, context);
}
private async Task ExecuteProcessPresets(ProcessChangedEventArgs context, IList<SamsungDevice> connectedDevices)
{
var triggerDevices = connectedDevices.Where(d => d.Options.TriggersEnabled).ToList();
@@ -588,19 +633,7 @@ namespace ColorControl.Services.Samsung
presets = presets.Where(p => p.Triggers.Any(t => !t.Conditions.HasFlag(PresetConditionType.FullScreen)));
}
var isHDRActive = CCD.IsHDREnabled();
var isGsyncActive = await _serviceManager.HandleExternalServiceAsync("GsyncEnabled", new[] { "" });
var triggerContext = new PresetTriggerContext
{
IsHDRActive = isHDRActive,
IsGsyncActive = isGsyncActive,
ForegroundProcess = context.ForegroundProcess,
ForegroundProcessIsFullScreen = context.ForegroundProcessIsFullScreen,
ChangedProcesses = changedProcesses,
IsNotificationDisabled = context.IsNotificationDisabled
};
var triggerContext = await CreateTriggerContext(_serviceManager, context);
var toApplyPresets = presets.Where(p => p.Triggers.Any(t => t.TriggerActive(triggerContext))).ToList();
@@ -692,5 +725,10 @@ namespace ColorControl.Services.Samsung
Logger.Error($"Screensaver check: can't power off: " + e.ToLogString());
}
}
public async Task ExecuteEventPresets(PresetTriggerType triggerType)
{
await ExecuteEventPresets(_serviceManager, new[] { triggerType });
}
}
}
}
+42 -8
View File
@@ -7,9 +7,11 @@ using MHC2Gen;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using WindowsDisplayAPI;
@@ -112,7 +114,7 @@ internal class ColorProfileViewModel : BaseViewModel
public string SelectedDisplayName => SelectedDisplay?.DisplayName;
public List<string> ExistingProfiles { get; set; }
public ObservableCollection<string> ExistingProfiles { get; set; }
public string SelectedExistingProfile { get; set; } = CreateANewProfile;
public string NewProfileName { get; set; }
public string ProfileName => SelectedExistingProfile == CreateANewProfile ? NewProfileName : SelectedExistingProfile;
@@ -120,6 +122,11 @@ internal class ColorProfileViewModel : BaseViewModel
public Dictionary<SaveOption, string> SaveOptions { get; } = Utils.EnumToDictionary<SaveOption>();
public SaveOption SaveOption { get; set; } = SaveOption.InstallAndSetAsDefault;
public bool SDRSettingsEnabled { get; set; }
public Visibility VisibilityHDRSettings => IsHDR ? Visibility.Visible : Visibility.Collapsed;
public bool IsLoadEnabled { get; set; }
public bool SetMinMaxTml { get; set; } = true;
public bool PrimariesEnabled { get; set; } = true;
public override string this[string columnName]
{
@@ -127,10 +134,26 @@ internal class ColorProfileViewModel : BaseViewModel
{
var baseResult = base[columnName];
if (columnName == "SelectedDisplay" || columnName == "PrimariesSource")
if (columnName == nameof(SelectedDisplay))
{
UpdateDisplay();
}
else if (columnName == nameof(SDRTransferFunction))
{
SDRSettingsEnabled = SDRTransferFunction == SDRTransferFunction.PurePower;
OnPropertyChanged(nameof(SDRSettingsEnabled));
}
else if (columnName == nameof(SelectedExistingProfile))
{
IsLoadEnabled = SelectedExistingProfile != CreateANewProfile;
OnPropertyChanged(nameof(IsLoadEnabled));
}
else if (columnName == nameof(PrimariesSource))
{
PrimariesEnabled = PrimariesSource == DisplayPrimariesSource.Custom;
OnPropertyChanged(nameof(PrimariesEnabled));
UpdateDisplay();
}
return baseResult;
}
@@ -149,7 +172,7 @@ internal class ColorProfileViewModel : BaseViewModel
RefreshDisplays();
ExistingProfiles = new List<string> { CreateANewProfile };
ExistingProfiles = new ObservableCollection<string> { CreateANewProfile };
NewProfileName = $"CC_{(isHDR ? "HDR" : "SDR")}_profile_{DateTime.Now:yyyyMMddHHmmss}.icm";
Description = $"CC_{(isHDR ? "HDR" : "SDR")}";
@@ -163,11 +186,16 @@ internal class ColorProfileViewModel : BaseViewModel
public void RefreshDisplays()
{
Displays = Display.GetDisplays().Select(d => new CustomDisplay(d)).ToList();
Displays = Display.GetDisplays().Select(d => new CustomDisplay(d))
.Where(d => IsHDR == d.IsHDRSupportedAndEnabled())
.ToList();
}
if (IsHDR)
public void Update()
{
foreach (var property in GetType().GetProperties().Where(p => !new[] { "Display", "PrimariesSource" }.Any(s => p.Name.Contains(s))))
{
Displays = Displays.Where(d => d.IsHDRSupportedAndEnabled()).ToList();
OnPropertyChanged(property.Name);
}
}
@@ -219,8 +247,14 @@ internal class ColorProfileViewModel : BaseViewModel
}
}
ExistingProfiles = new List<string> { CreateANewProfile };
ExistingProfiles.AddRange(CCD.GetDisplayColorProfiles(SelectedDisplay.DisplayName));
ExistingProfiles = new ObservableCollection<string> { CreateANewProfile };
var profiles = CCD.GetDisplayColorProfiles(SelectedDisplay.DisplayName);
foreach (var profile in profiles)
{
ExistingProfiles.Add(profile);
}
}
private void GetEDID()
+33 -30
View File
@@ -7,7 +7,7 @@
Title="Create Color Profile"
ResizeMode="CanResize"
Width="773"
Height="775"
Height="791"
WindowStartupLocation="CenterOwner"
SnapsToDevicePixels="True"
Style="{DynamicResource CustomWindowStyle}"
@@ -19,58 +19,58 @@
<ColumnDefinition Width="132*"/>
<ColumnDefinition Width="577*"/>
</Grid.ColumnDefinitions>
<Label Content="Maximum Luminance (nits):" HorizontalAlignment="Left" Margin="206,232,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="412,236,0,0" TextWrapping="Wrap" Text="{Binding WhiteLuminance, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" Name="tbMaxLuminance" Height="18" InputScope="Number"/>
<Label Content="Minimum Luminance (nits):" HorizontalAlignment="Left" Margin="-5,232,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="76,236,0,0" TextWrapping="Wrap" Text="{Binding BlackLuminance, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbMinLuminance" Height="18" InputScope="Number"/>
<Label Content="Display:" HorizontalAlignment="Left" Margin="-6,8,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="76,10,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" SelectedValue="{Binding SelectedDisplay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" ItemsSource="{Binding Displays}"/>
<Label Content="Color Gamut:" HorizontalAlignment="Left" Margin="-5,201,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="76,203,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" ItemsSource="{Binding ColorGamuts}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding ColorGamut}" />
<Label Content="Display Primaries Source:" HorizontalAlignment="Left" Margin="-5,39,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="76,41,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="24" ItemsSource="{Binding PrimariesSources}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding PrimariesSource, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" />
<Label Content="Custom Primaries:" HorizontalAlignment="Left" Margin="-5,74,0,0" VerticalAlignment="Top" Width="118" Height="26"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="76,78,0,0" TextWrapping="Wrap" Text="{Binding RedPoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy" Height="18" InputScope="Number"/>
<TextBox IsEnabled="{Binding PrimariesEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="76,78,0,0" TextWrapping="Wrap" Text="{Binding RedPoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy" Height="18" InputScope="Number"/>
<Label HorizontalContentAlignment="Right" Content="Red:" HorizontalAlignment="Left" Margin="20,74,0,0" VerticalAlignment="Top" Width="51" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="195,78,0,0" TextWrapping="Wrap" Text="{Binding RedPoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy1" Height="18" InputScope="Number"/>
<TextBox IsEnabled="{Binding PrimariesEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="195,78,0,0" TextWrapping="Wrap" Text="{Binding RedPoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy1" Height="18" InputScope="Number"/>
<Label Content="x" HorizontalAlignment="Left" Margin="164,74,0,0" VerticalAlignment="Top" Width="26" Height="26" Grid.Column="1"/>
<Label Content="y" HorizontalAlignment="Center" Margin="0,74,0,0" VerticalAlignment="Top" Width="24" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="76,109,0,0" TextWrapping="Wrap" Text="{Binding GreenPoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy2" Height="18" InputScope="Number"/>
<TextBox IsEnabled="{Binding PrimariesEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="76,109,0,0" TextWrapping="Wrap" Text="{Binding GreenPoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy2" Height="18" InputScope="Number"/>
<Label HorizontalContentAlignment="Right" Content="Green:" HorizontalAlignment="Left" Margin="20,105,0,0" VerticalAlignment="Top" Width="52" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="195,109,0,0" TextWrapping="Wrap" Text="{Binding GreenPoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy3" Height="18" InputScope="Number"/>
<TextBox IsEnabled="{Binding PrimariesEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="195,109,0,0" TextWrapping="Wrap" Text="{Binding GreenPoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy3" Height="18" InputScope="Number"/>
<Label Content="x" HorizontalAlignment="Left" Margin="164,105,0,0" VerticalAlignment="Top" Width="26" Height="26" Grid.Column="1"/>
<Label Content="y" HorizontalAlignment="Center" Margin="0,105,0,0" VerticalAlignment="Top" Width="24" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="76,140,0,0" TextWrapping="Wrap" Text="{Binding BluePoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy4" Height="18" InputScope="Number"/>
<TextBox IsEnabled="{Binding PrimariesEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="76,140,0,0" TextWrapping="Wrap" Text="{Binding BluePoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy4" Height="18" InputScope="Number"/>
<Label HorizontalContentAlignment="Right" Content="Blue:" HorizontalAlignment="Left" Margin="19,136,0,0" VerticalAlignment="Top" Width="52" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="195,140,0,0" TextWrapping="Wrap" Text="{Binding BluePoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy5" Height="18" InputScope="Number"/>
<TextBox IsEnabled="{Binding PrimariesEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="195,140,0,0" TextWrapping="Wrap" Text="{Binding BluePoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy5" Height="18" InputScope="Number"/>
<Label Content="x" HorizontalAlignment="Left" Margin="164,136,0,0" VerticalAlignment="Top" Width="26" Height="26" Grid.Column="1"/>
<Label Content="y" HorizontalAlignment="Center" Margin="0,136,0,0" VerticalAlignment="Top" Width="24" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="76,171,0,0" TextWrapping="Wrap" Text="{Binding WhitePoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy7" Height="18" InputScope="Number"/>
<TextBox IsEnabled="{Binding PrimariesEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="76,171,0,0" TextWrapping="Wrap" Text="{Binding WhitePoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy7" Height="18" InputScope="Number"/>
<Label HorizontalContentAlignment="Right" Content="White:" HorizontalAlignment="Left" Margin="19,167,0,0" VerticalAlignment="Top" Width="52" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="195,171,0,0" TextWrapping="Wrap" Text="{Binding WhitePoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy8" Height="18" InputScope="Number"/>
<TextBox IsEnabled="{Binding PrimariesEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="195,171,0,0" TextWrapping="Wrap" Text="{Binding WhitePoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy8" Height="18" InputScope="Number"/>
<Label Content="x" HorizontalAlignment="Left" Margin="164,167,0,0" VerticalAlignment="Top" Width="26" Height="26" Grid.Column="1"/>
<Label Content="y" HorizontalAlignment="Center" Margin="0,167,0,0" VerticalAlignment="Top" Width="24" Height="26" Grid.Column="1"/>
<Label Content="Color Gamut:" HorizontalAlignment="Left" Margin="-5,201,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="76,203,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" ItemsSource="{Binding ColorGamuts}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding ColorGamut}" />
<Label Content="Minimum Luminance (nits):" HorizontalAlignment="Left" Margin="-5,232,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="76,236,0,0" TextWrapping="Wrap" Text="{Binding BlackLuminance, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbMinLuminance" Height="18" InputScope="Number"/>
<Label Content="Maximum Luminance (nits):" HorizontalAlignment="Left" Margin="206,232,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="412,236,0,0" TextWrapping="Wrap" Text="{Binding WhiteLuminance, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" Name="tbMaxLuminance" Height="18" InputScope="Number"/>
</Grid>
</GroupBox>
<GroupBox Header="Advanced HDR Settings" Padding="6" Height="133" Width="734" HorizontalAlignment="Left">
<GroupBox Header="Advanced HDR Settings" Padding="6" Height="133" Width="734" HorizontalAlignment="Left" Visibility="{Binding VisibilityHDRSettings}">
<Grid Height="103">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="179*"/>
<ColumnDefinition Width="735*"/>
</Grid.ColumnDefinitions>
<Label Content="SDR Transfer Function &amp; Gamma:" HorizontalAlignment="Left" Margin="-5,6,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="69,8,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" ItemsSource="{Binding SDRTransferFunctions}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding SDRTransferFunction}"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="405,10,0,0" TextWrapping="Wrap" Text="{Binding CustomGamma, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbCustomGamma" Height="18" InputScope="Number"/>
<ComboBox Margin="69,8,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" ItemsSource="{Binding SDRTransferFunctions}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding SDRTransferFunction, NotifyOnValidationError=True, ValidatesOnDataErrors=True}"/>
<TextBox IsEnabled="{Binding SDRSettingsEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="405,10,0,0" TextWrapping="Wrap" Text="{Binding CustomGamma, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbCustomGamma" Height="18" InputScope="Number"/>
<Label Content="SDR minimum brightness (nits):" HorizontalAlignment="Left" Margin="-5,37,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="69,41,0,0" TextWrapping="Wrap" Text="{Binding SDRMinBrightness, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbSDRMinBrightness" Height="18" InputScope="Number"/>
<TextBox IsEnabled="{Binding SDRSettingsEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="69,41,0,0" TextWrapping="Wrap" Text="{Binding SDRMinBrightness, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbSDRMinBrightness" Height="18" InputScope="Number"/>
<Label Content="SDR maximum brightness (nits):" HorizontalAlignment="Left" Margin="197,37,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="405,41,0,0" TextWrapping="Wrap" Text="{Binding SDRMaxBrightness, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbSDRMaxBrightness" Height="18" InputScope="Number"/>
<TextBox IsEnabled="{Binding SDRSettingsEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="405,41,0,0" TextWrapping="Wrap" Text="{Binding SDRMaxBrightness, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbSDRMaxBrightness" Height="18" InputScope="Number"/>
<Label Content="SDR brightness boost (%):" HorizontalAlignment="Left" Margin="-5,64,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="69,68,0,0" TextWrapping="Wrap" Text="{Binding SDRBrightnessBoost, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbSDRMinBrightness_Copy6" Height="18" InputScope="Number"/>
<TextBox IsEnabled="{Binding SDRSettingsEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="69,68,0,0" TextWrapping="Wrap" Text="{Binding SDRBrightnessBoost, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbSDRMinBrightness_Copy6" Height="18" InputScope="Number"/>
</Grid>
</GroupBox>
<GroupBox Header="Advanced Profile Settings" Padding="6" Height="78" Width="734" HorizontalAlignment="Left">
<Grid Height="47">
<GroupBox Header="Advanced Profile Settings" Padding="6" Height="71" Width="734" HorizontalAlignment="Left">
<Grid Height="34">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="4*"/>
@@ -81,21 +81,24 @@
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="400,10,0,0" TextWrapping="Wrap" Text="{Binding MaxCLL, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbMinLuminance_Copy1" Height="18" InputScope="Number"/>
</Grid>
</GroupBox>
<GroupBox DockPanel.Dock="Right" Margin="0,0,0,0" Header="Generate Options" Padding="6" Height="197" Width="733" HorizontalAlignment="Left">
<Grid Height="163" Margin="-2,0,2,0">
<GroupBox DockPanel.Dock="Right" Margin="0,0,0,0" Header="Generate Options" Padding="6" Height="225" Width="733" HorizontalAlignment="Left">
<Grid Height="194" Margin="-2,0,2,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*"/>
<ColumnDefinition Width="27*"/>
</Grid.ColumnDefinitions>
<Button IsEnabled="{Binding IsValid}" Click="Button_Click" Content="Generate" Margin="46,132,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20" Width="75" Grid.Column="1"/>
<Label Content="Overwrite existing profile:" HorizontalAlignment="Left" Margin="0,40,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="46,42,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" SelectedValue="{Binding SelectedExistingProfile}" ItemsSource="{Binding ExistingProfiles}"/>
<Label Content="New profile name:" HorizontalAlignment="Left" Margin="0,73,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="46,77,0,0" TextWrapping="Wrap" Text="{Binding NewProfileName}" VerticalAlignment="Top" Width="318" x:Name="tbName" Height="18"/>
<Label Content="Save option:" HorizontalAlignment="Left" Margin="0,8,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="46,10,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" ItemsSource="{Binding SaveOptions}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding SaveOption}" />
<Label Content="Overwrite existing profile:" HorizontalAlignment="Left" Margin="0,40,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="46,42,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" SelectedValue="{Binding SelectedExistingProfile, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" ItemsSource="{Binding ExistingProfiles}"/>
<Button Content="Load" Margin="385,42,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20" Width="75" Grid.Column="1" Click="Button_Click_1"/>
<Button Content="Delete" Grid.Column="1" Margin="472,42,0,132" Click="Button_Click_2"/>
<Label Content="New profile name:" HorizontalAlignment="Left" Margin="0,73,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="46,77,0,0" TextWrapping="Wrap" Text="{Binding NewProfileName}" VerticalAlignment="Top" Width="318" x:Name="tbName" Height="18"/>
<Label Content="Description:" HorizontalAlignment="Left" Margin="0,101,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="46,105,0,0" TextWrapping="Wrap" Text="{Binding Description, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="318" x:Name="tbName_Copy" Height="18"/>
<CheckBox Content="When activating, set MinTML and MaxTML to minimum/maximum luminance" IsChecked="{Binding SetMinMaxTml}" Margin="46,135,10,35" Grid.Column="1"/>
<Button IsEnabled="{Binding IsValid}" Click="Button_Click" Content="Generate" Margin="46,164,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20" Width="75" Grid.Column="1"/>
</Grid>
</GroupBox>
<StackPanel HorizontalAlignment="Right" Margin="0,6,0,0" VerticalAlignment="Bottom"/>
+92 -7
View File
@@ -18,14 +18,18 @@ namespace ColorControl.XForms
{
private ColorProfileViewModel _viewModel;
private readonly WinApiAdminService _winApiAdminService;
private readonly AppContextProvider _appContextProvider;
public ColorProfileWindow(WinApiAdminService winApiAdminService)
public ColorProfileWindow(WinApiAdminService winApiAdminService, AppContextProvider appContextProvider, bool isHDR)
{
_viewModel = new ColorProfileViewModel();
_winApiAdminService = winApiAdminService;
_appContextProvider = appContextProvider;
_viewModel = new ColorProfileViewModel(isHDR);
_viewModel.SetMinMaxTml = appContextProvider.GetAppContext().Config.SetMinTmlAndMaxTml;
DataContext = _viewModel;
InitializeComponent();
_winApiAdminService = winApiAdminService;
}
private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)
@@ -37,14 +41,16 @@ namespace ColorControl.XForms
System.Diagnostics.Process.Start(processStartInfo);
}
public static void CreateAndShow(bool show = true)
public static void CreateAndShow(bool show = true, bool isHDR = true)
{
if (Application.Current == null)
{
new Application { ShutdownMode = ShutdownMode.OnExplicitShutdown };
}
var window = Program.ServiceProvider.GetRequiredService<ColorProfileWindow>();
var winApiAdminService = Program.ServiceProvider.GetRequiredService<WinApiAdminService>();
var appContextProvider = Program.ServiceProvider.GetRequiredService<AppContextProvider>();
var window = new ColorProfileWindow(winApiAdminService, appContextProvider, isHDR);
if (show)
{
@@ -87,7 +93,11 @@ namespace ColorControl.XForms
{
if (!_viewModel.Displays.Any())
{
MessageForms.WarningOk("There are no displays available that support HDR and have it enabled. Please activate HDR first.");
var message = _viewModel.IsHDR ?
"There are no displays available that support HDR and have it enabled. Please activate HDR first." :
"There are no displays available are in SDR mode. Please deactivate HDR first.";
MessageForms.WarningOk(message);
}
}
@@ -96,6 +106,7 @@ namespace ColorControl.XForms
var command = new GenerateProfileCommand
{
Description = _viewModel.Description,
IsHDRProfile = _viewModel.IsHDR,
MinCLL = _viewModel.MinCLL,
MaxCLL = _viewModel.MaxCLL,
BlackLuminance = _viewModel.BlackLuminance,
@@ -146,7 +157,81 @@ namespace ColorControl.XForms
var profileName = Path.GetFileName(tempFilename);
CCD.SetDisplayDefaultColorProfile(displayName, profileName);
if (CCD.SetDisplayDefaultColorProfile(displayName, profileName, _viewModel.SetMinMaxTml))
{
if (!_viewModel.ExistingProfiles.Contains(profileName))
{
_viewModel.ExistingProfiles.Add(profileName);
}
_viewModel.SelectedExistingProfile = profileName;
_viewModel.Update();
}
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var existingProfile = _viewModel.SelectedExistingProfile;
if (existingProfile == ColorProfileViewModel.CreateANewProfile)
{
var openFileDialog = new System.Windows.Forms.OpenFileDialog
{
Filter = "Color profiles (*.icc;*.icm)|*.icc;*.icm"
};
if (openFileDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
{
return;
}
existingProfile = openFileDialog.FileName;
}
if (MessageForms.QuestionYesNo("This will discard all current changes. Do you want to continue?") != System.Windows.Forms.DialogResult.Yes)
{
return;
}
// Load profile
var profileProperties = MHC2Wrapper.LoadProfile(existingProfile, _viewModel.IsHDR);
_viewModel.RedPoint = new(profileProperties.DevicePrimaries.Red);
_viewModel.GreenPoint = new(profileProperties.DevicePrimaries.Green);
_viewModel.BluePoint = new(profileProperties.DevicePrimaries.Blue);
_viewModel.WhitePoint = new(profileProperties.DevicePrimaries.White);
_viewModel.ColorGamut = profileProperties.ColorGamut;
_viewModel.BlackLuminance = profileProperties.BlackLuminance;
_viewModel.WhiteLuminance = profileProperties.WhiteLuminance;
_viewModel.SDRTransferFunction = profileProperties.SDRTransferFunction;
_viewModel.CustomGamma = profileProperties.Gamma;
_viewModel.SDRMinBrightness = profileProperties.SDRMinBrightness;
_viewModel.SDRMaxBrightness = profileProperties.SDRMaxBrightness;
_viewModel.SDRBrightnessBoost = profileProperties.SDRBrightnessBoost;
_viewModel.MinCLL = profileProperties.MinCLL;
_viewModel.MaxCLL = profileProperties.MaxCLL;
_viewModel.Update();
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
var existingProfile = _viewModel.SelectedExistingProfile;
if (existingProfile == ColorProfileViewModel.CreateANewProfile)
{
return;
}
if (MessageForms.QuestionYesNo($"Are you sure you want to remove the color profile '{existingProfile}'?") != System.Windows.Forms.DialogResult.Yes)
{
return;
}
if (_winApiAdminService.UninstallColorProfile(existingProfile))
{
_viewModel.SelectedExistingProfile = ColorProfileViewModel.CreateANewProfile;
_viewModel.ExistingProfiles.Remove(existingProfile);
_viewModel.Update();
}
}
}
}
+14 -2
View File
@@ -538,12 +538,12 @@ namespace LgTv
public async Task TurnScreenOff()
{
await _connection.SendCommandAsync(new RequestMessage("", "ssap://com.webos.service.tvpower/power/turnOffScreen"));
await Logger.SwallowAsync(() => _connection.SendCommandAsync(new RequestMessage("", "ssap://com.webos.service.tvpower/power/turnOffScreen")));
}
public async Task TurnScreenOn()
{
await _connection.SendCommandAsync(new RequestMessage("", "ssap://com.webos.service.tvpower/power/turnOnScreen"));
await Logger.SwallowAsync(() => _connection.SendCommandAsync(new RequestMessage("", "ssap://com.webos.service.tvpower/power/turnOnScreen")));
}
public async Task<bool> IsTurnedOn3D()
@@ -847,6 +847,18 @@ namespace LgTv
await ExecuteRequest(lunauri, @params);
}
public async Task SetTpcOrGsr(string name, bool enable)
{
var lunauri = $"luna://com.webos.service.oledepl/{(name == "TPC" ? "setTemporalPeakControl" : "setGlobalStressReduction")}";
var @params = new
{
enable
};
await ExecuteRequest(lunauri, @params);
}
private async Task ExecuteRequest(string lunauri, object @params)
{
var buttons = new[]
+43 -6
View File
@@ -5,6 +5,7 @@ using MathNet.Numerics.LinearAlgebra.Double;
using System;
using System.Buffers.Binary;
using System.IO;
using System.Text.Json;
namespace MHC2Gen
{
@@ -206,6 +207,16 @@ namespace MHC2Gen
}
}
internal class ExtraInfoTag
{
public SDRTransferFunction SDRTransferFunction { get; set; }
public double Gamma { get; set; }
public double SDRMinBrightness { get; set; }
public double SDRMaxBrightness { get; set; }
public double SDRBrightnessBoost { get; set; }
public ColorGamut TargetGamut { get; set; }
}
internal class IccContext
{
protected IccProfile profile;
@@ -380,8 +391,8 @@ namespace MHC2Gen
internal class DeviceIccContext : IccContext
{
CIEXYZ illuminantRelativeBlackPoint;
double min_nits;
double max_nits;
public double min_nits;
public double max_nits;
ToneCurve profileRedToneCurve;
ToneCurve profileGreenToneCurve;
ToneCurve profileBlueToneCurve;
@@ -389,6 +400,8 @@ namespace MHC2Gen
ToneCurve profileGreenReverseToneCurve;
ToneCurve profileBlueReverseToneCurve;
public ExtraInfoTag? ExtraInfoTag { get; }
public bool UseChromaticAdaptation { get; set; }
public DeviceIccContext(IccProfile profile) : base(profile)
@@ -401,6 +414,16 @@ namespace MHC2Gen
profileRedReverseToneCurve = profileRedToneCurve.Reverse();
profileGreenReverseToneCurve = profileGreenToneCurve.Reverse();
profileBlueReverseToneCurve = profileBlueToneCurve.Reverse();
if (profile.ContainsTag(TagSignature.ScreeningDescTag))
{
var ccDesc = profile.ReadTag(SafeTagSignature.ScreeningDescTag);
var json = ccDesc?.Get("en", "US");
if (json != null)
{
ExtraInfoTag = JsonSerializer.Deserialize<ExtraInfoTag>(json);
}
}
}
public static Matrix<double> RgbToXYZ(RgbPrimaries primaries)
@@ -450,7 +473,7 @@ namespace MHC2Gen
{
max_nits = lumi.Y;
}
var min_nits = 0.005;
var min_nits = 0.0;
var bkpt = illuminantRelativeBlackPoint;
if (bkpt.Y != 0)
{
@@ -847,7 +870,7 @@ namespace MHC2Gen
var devicePrimaries = command.DevicePrimaries; // new RgbPrimaries(new() { x = 0.698, y = 0.292 }, new() { x = 0.255, y = 0.699 }, new() { x = 0.148, y = 0.056 }, new() { x = 0.3127, y = 0.3290 });
var srgbTrc = IccProfile.Create_sRGB().ReadTag(SafeTagSignature.RedTRCTag)!;
var srgbTrc = profile.ReadTag(SafeTagSignature.RedTRCTag)!;
var sourcePrimaries = RgbPrimaries.sRGB;
@@ -914,7 +937,7 @@ namespace MHC2Gen
MaxCLL = command.MaxCLL
};
if (command.SDRTransferFunction == SDRTransferFunction.Piecewise)
if (!command.IsHDRProfile || command.SDRTransferFunction == SDRTransferFunction.Piecewise)
{
MHC2.ApplyPiecewise();
}
@@ -966,6 +989,20 @@ namespace MHC2Gen
var new_desc_mlu = new MLU(new_desc);
outputProfile.WriteTag(SafeTagSignature.ProfileDescriptionTag, new_desc_mlu);
var extraInfoTag = new ExtraInfoTag
{
SDRTransferFunction = command.SDRTransferFunction,
Gamma = command.Gamma,
SDRMinBrightness = command.SDRMinBrightness,
SDRMaxBrightness = command.SDRMaxBrightness,
SDRBrightnessBoost = command.SDRBrightnessBoost,
TargetGamut = command.ColorGamut
};
var ccDesc = JsonSerializer.Serialize(extraInfoTag);
outputProfile.WriteTag(SafeTagSignature.ScreeningDescTag, new MLU(ccDesc));
outputProfile.WriteRawTag(MHC2Tag.Signature, MHC2.ToBytes());
outputProfile.ComputeProfileId();
@@ -1117,7 +1154,7 @@ namespace MHC2Gen
outputProfile.HeaderManufacturer = profile.HeaderManufacturer;
outputProfile.HeaderModel = profile.HeaderModel;
outputProfile.HeaderAttributes = profile.HeaderAttributes;
outputProfile.HeaderRenderingIntent = RenderingIntent.ABSOLUTE_COLORIMETRIC;
outputProfile.HeaderRenderingIntent = RenderingIntent.PERCEPTUAL;
var new_desc = $"XCSC: {sourceDescription} ({GetDeviceDescription()})";
var new_desc_mlu = new MLU(new_desc);
+45
View File
@@ -1,4 +1,6 @@
using LittleCms;
using System;
using System.IO;
namespace MHC2Gen;
@@ -23,4 +25,47 @@ public class MHC2Wrapper
return newProfile.GetBytes();
}
public static (double MinNits, double MaxNits) GetMinMaxLuminance(string profileName)
{
var fileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), $@"System32\spool\drivers\color\{profileName}");
var bytes = File.ReadAllBytes(fileName);
var profile = IccProfile.Open(bytes.AsSpan());
var deviceContext = new DeviceIccContext(profile);
return (deviceContext.min_nits, deviceContext.max_nits);
}
public static GenerateProfileCommand LoadProfile(string fileName, bool isHDRProfile)
{
if (fileName.IndexOf("\\") == -1 || !File.Exists(fileName))
{
fileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), $@"System32\spool\drivers\color\{fileName}");
}
var bytes = File.ReadAllBytes(fileName);
var profile = IccProfile.Open(bytes.AsSpan());
var deviceContext = new DeviceIccContext(profile);
return new GenerateProfileCommand
{
IsHDRProfile = isHDRProfile,
BlackLuminance = deviceContext.min_nits,
WhiteLuminance = deviceContext.max_nits,
ColorGamut = deviceContext.ExtraInfoTag?.TargetGamut ?? ColorGamut.Native,
DevicePrimaries = new RgbPrimaries(deviceContext.ProfilePrimaries.Red, deviceContext.ProfilePrimaries.Green, deviceContext.ProfilePrimaries.Blue, deviceContext.ProfilePrimaries.White),
MinCLL = deviceContext.MHC2?.MinCLL ?? deviceContext.min_nits,
MaxCLL = deviceContext.MHC2?.MaxCLL ?? deviceContext.max_nits,
SDRMinBrightness = deviceContext.ExtraInfoTag?.SDRMinBrightness ?? 0,
SDRMaxBrightness = deviceContext.ExtraInfoTag?.SDRMaxBrightness ?? 100,
SDRTransferFunction = deviceContext.ExtraInfoTag?.SDRTransferFunction ?? SDRTransferFunction.PurePower,
SDRBrightnessBoost = deviceContext.ExtraInfoTag?.SDRBrightnessBoost ?? 0,
Gamma = deviceContext.ExtraInfoTag?.Gamma ?? 2.2
};
}
}
+1
View File
@@ -24,6 +24,7 @@ public enum ColorGamut
public class GenerateProfileCommand
{
public string Description { get; set; }
public bool IsHDRProfile { get; set; }
public RgbPrimaries DevicePrimaries { get; set; } = new RgbPrimaries(RgbPrimaries.sRGB);
public SDRTransferFunction SDRTransferFunction { get; set; }
public ColorGamut ColorGamut { get; set; }
@@ -1,10 +1,10 @@
using System;
using NvAPIWrapper.Native.Helpers;
using NvAPIWrapper.Native.Helpers;
using System;
namespace NvAPIWrapper.Native.Attributes
{
[AttributeUsage(AttributeTargets.Delegate)]
internal class FunctionIdAttribute : Attribute
public class FunctionIdAttribute : Attribute
{
public FunctionIdAttribute(FunctionId functionId)
{
+1 -1
View File
@@ -1,10 +1,10 @@
using System;
using NvAPIWrapper.Native.Exceptions;
using NvAPIWrapper.Native.General;
using NvAPIWrapper.Native.General.Structures;
using NvAPIWrapper.Native.Helpers;
using NvAPIWrapper.Native.Helpers.Structures;
using NvAPIWrapper.Native.Interfaces.General;
using System;
namespace NvAPIWrapper.Native
{
@@ -1,13 +1,13 @@
using NvAPIWrapper.Native.Attributes;
using NvAPIWrapper.Native.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using NvAPIWrapper.Native.Attributes;
using NvAPIWrapper.Native.Exceptions;
namespace NvAPIWrapper.Native.Helpers
{
internal static class DelegateFactory
public static class DelegateFactory
{
private static readonly Dictionary<KeyValuePair<FunctionId, Type>, object> Delegates =
new Dictionary<KeyValuePair<FunctionId, Type>, object>();
@@ -37,7 +37,7 @@ namespace NvAPIWrapper.Native.Helpers
return Delegates[delegateKey] as T;
}
var ptr = NvAPI_QueryInterface((uint) functionId.FunctionId);
var ptr = NvAPI_QueryInterface((uint)functionId.FunctionId);
if (ptr != IntPtr.Zero)
{
+7 -1
View File
@@ -3,7 +3,7 @@
namespace NvAPIWrapper.Native.Helpers
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal enum FunctionId : uint
public enum FunctionId : uint
{
// -------------------------------------------------
// Public NvAPI Functions
@@ -702,6 +702,12 @@ namespace NvAPIWrapper.Native.Helpers
Unknown_F1D2777B = 0xf1d2777b, // `Unknown(hDisplayHandle, *mut hGpu)` maybe?
NvAPI_GPU_QueryThermalSensors = 0x65FE3AAD,
// CC: added functions
NvAPI_Disp_SetOutputMode = 0x98E7661A,
NvAPI_Disp_GetOutputMode = 0x81FED88D,
NvAPI_Disp_SetDitherControl = 0xDF0DFCDD,
NvAPI_Disp_GetDitherControl = 0x932AC8FB,
#endregion
NvAPI_Unload = 0xD22BDD7E,
+2 -2
View File
@@ -206,9 +206,9 @@ The best and suggested method to provide this is via a Windows Service. Only whe
return name.Substring(0, 1).ToUpper() + name.Substring(1);
}
public static Dictionary<T, string> EnumToDictionary<T>() where T : struct, Enum
public static Dictionary<T, string> EnumToDictionary<T>(IEnumerable<T> skipValues = null) where T : struct, Enum
{
return Enum.GetValues<T>().ToDictionary(k => k, e => e.GetDescription());
return Enum.GetValues<T>().Where(v => skipValues == null || !skipValues.Contains(v)).ToDictionary(k => k, e => e.GetDescription());
}
public static string GetDescriptionByEnumName<T>(string value) where T : IConvertible
+1
View File
@@ -40,6 +40,7 @@
public string LogLevel { get; set; }
public List<Module> Modules { get; set; }
public bool UseRawInput { get; set; }
public bool SetMinTmlAndMaxTml { get; set; }
public Config()
{
+32
View File
@@ -0,0 +1,32 @@
namespace ColorControl.Shared.Contracts.NVIDIA
{
public class NvHdrSettings
{
public enum NV_DISPLAY_OUTPUT_MODE
{
NV_DISPLAY_OUTPUT_MODE_SDR = 0,
NV_DISPLAY_OUTPUT_MODE_HDR10 = 1,
NV_DISPLAY_OUTPUT_MODE_HDR10PLUS_GAMING = 2
}
public NV_DISPLAY_OUTPUT_MODE? OutputMode { get; set; }
public NvHdrSettings()
{
}
public NvHdrSettings(NvHdrSettings settings)
{
settings ??= new NvHdrSettings();
OutputMode = settings.OutputMode;
}
public override string ToString()
{
var value = string.Empty;
return value;
}
}
}
+5
View File
@@ -316,6 +316,11 @@ namespace ColorControl.Shared.Forms
Text = label
};
if (textSubLabel != null)
{
top += textSubLabel.Height + 4;
}
checkBox.Checked = field.Value is bool boolValue && boolValue;
control = checkBox;
+68 -1
View File
@@ -1,4 +1,5 @@
using ColorControl.Shared.Forms;
using MHC2Gen;
using NWin32;
using System.ComponentModel;
using System.IO;
@@ -143,7 +144,7 @@ namespace ColorControl.Shared.Native
return err == NativeConstants.ERROR_SUCCESS;
}
public static bool SetDisplayDefaultColorProfile(string displayName, string profileName)
public static bool SetDisplayDefaultColorProfile(string displayName, string profileName, bool fixMaxTML = true)
{
var adapterId = GetAdapterId(displayName);
var sourceId = GetSourceId(displayName);
@@ -169,6 +170,16 @@ namespace ColorControl.Shared.Native
throw new Win32Exception(err);
}
if (fixMaxTML)
{
var (minNits, maxNits) = MHC2Wrapper.GetMinMaxLuminance(profileName);
if (minNits < maxNits)
{
SetMinMaxLuminance(minNits, maxNits, displayName: displayName);
}
}
return err == NativeConstants.ERROR_SUCCESS;
}
@@ -347,6 +358,50 @@ namespace ColorControl.Shared.Native
);
}
public static void SetMinMaxLuminance(double minLuminance, double maxLuminance, double? maxFFL = null, string displayName = null)
{
ExecuteForModeConfig((modeInfo) =>
{
if (modeInfo.infoType == DisplayConfigModeInfoType.Target)
{
var colorParams = GetColorParams(displayName);
var divider = colorParams.RedPointX <= 1 << 10 ? 1 << 10 : 1 << 20;
colorParams.MinLuminance = (uint)(minLuminance * 10000);
colorParams.MaxLuminance = (uint)(maxLuminance * 10000);
colorParams.MaxFullFrameLuminance = maxFFL.HasValue ? (uint)(maxFFL * 10000) : colorParams.MaxLuminance;
colorParams.WhitePointX = (uint)(0.3127f * divider);
colorParams.WhitePointY = (uint)(0.3290f * divider);
var requestpacket = new DISPLAYCONFIG_SET_ADVANCED_COLOR_PARAM();
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
unchecked
{
requestpacket.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_PARAM;
}
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_SET_ADVANCED_COLOR_PARAM>();
requestpacket.header.adapterId = modeInfo.adapterId;
requestpacket.header.id = modeInfo.id;
requestpacket.colorParams = colorParams;
var error = NativeMethods.DisplayConfigSetDeviceInfo(ref requestpacket);
if (error == NativeConstants.ERROR_SUCCESS)
{
return (true, false);
}
//throw new Win32Exception(error);
return (false, false);
}
return (false, true);
}, displayName
);
}
private static T ExecuteForModeConfig<T>(DisplayConfigModeInfoDelegate<T> infoDelegate, string displayName = null)
{
uint pathCount, modeCount;
@@ -583,6 +638,7 @@ namespace ColorControl.Shared.Native
}
private const uint DISPLAYCONFIG_DEVICE_INFO_GET_DISPLAY_INFO = 0xFFFFFFFE;
private const uint DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_PARAM = 0xFFFFFFF0;
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_DEVICE_INFO_HEADER
@@ -935,6 +991,15 @@ namespace ColorControl.Shared.Native
public byte[] Stuff2;
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_SET_ADVANCED_COLOR_PARAM
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
public ColorParams colorParams;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] Stuff;
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO
{
@@ -1103,6 +1168,8 @@ namespace ColorControl.Shared.Native
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_GET_DISPLAY_INFO requestPacket);
[DllImport("user32")]
public static extern int DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SET_ADVANCED_COLOR_PARAM setPacket);
[DllImport("mscms", CharSet = CharSet.Unicode)]
public static extern bool InstallColorProfileW(string machineName, string profilename);
+1
View File
@@ -41,6 +41,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MHC2Gen\MHC2Gen.csproj" />
<ProjectReference Include="..\NvAPIWrapper\NvAPIWrapper.csproj" />
</ItemGroup>
+1 -1
View File
@@ -73,7 +73,7 @@ namespace novideo_srgb
var item = new ToolStripMenuItem();
parent.DropDownItems.Add(item);
item.Text = monitor.Name;
item.Checked = monitor.Clamped;
item.CheckState = monitor.Clamped == true ? CheckState.Checked : monitor.Clamped == false ? CheckState.Unchecked : CheckState.Indeterminate;
item.Enabled = monitor.CanClamp;
item.ForeColor = parent.ForeColor;
item.Click += (sender, args) => monitor.Clamped = !monitor.Clamped;
+5
View File
@@ -179,6 +179,11 @@ namespace novideo_srgb
{
try
{
if (!ClampSdr)
{
return;
}
var clamped = CanClamp && ClampSdr && (!forceClamp.HasValue || forceClamp.Value);
UpdateClamp(clamped);
_clamped = clamped;