diff --git a/ColorControl/ColorControl.csproj b/ColorControl/ColorControl.csproj
index 2c33512..a0d7a3b 100644
--- a/ColorControl/ColorControl.csproj
+++ b/ColorControl/ColorControl.csproj
@@ -18,8 +18,8 @@
Maassoft
Maassoft
0
- 9.8.0.1
- 9.8.0.1
+ 9.8.1.0
+ 9.8.1.0
false
true
false
diff --git a/ColorControl/MainForm.Designer.cs b/ColorControl/MainForm.Designer.cs
index 3ce3d9a..68fa5aa 100644
--- a/ColorControl/MainForm.Designer.cs
+++ b/ColorControl/MainForm.Designer.cs
@@ -64,6 +64,7 @@ private void InitializeComponent()
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 @@ private void InitializeComponent()
// 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 @@ private void InitializeComponent()
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 @@ private void InitializeComponent()
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 void InitializeComponent()
private System.Windows.Forms.Button btnOptionsColorProfiles;
private System.Windows.Forms.ContextMenuStrip mnuColorProfiles;
private System.Windows.Forms.ToolStripMenuItem miCreateHDRColorProfile;
+ private System.Windows.Forms.ToolStripMenuItem miCreateSDRColorProfile;
}
}
diff --git a/ColorControl/MainForm.cs b/ColorControl/MainForm.cs
index 6d97155..55eeb5c 100644
--- a/ColorControl/MainForm.cs
+++ b/ColorControl/MainForm.cs
@@ -168,7 +168,7 @@ public MainForm(AppContextProvider appContextProvider, PowerEventDispatcher powe
_initialized = true;
- AfterInitialized();
+ Task.Run(AfterInitialized);
if (_config.UseDarkMode)
{
@@ -176,10 +176,6 @@ public MainForm(AppContextProvider appContextProvider, PowerEventDispatcher powe
}
}
- private void MainForm_Load(object sender, EventArgs e)
- {
- }
-
private void LoadModules()
{
_modules.Add("NVIDIA controller", InitNvService);
@@ -236,6 +232,8 @@ private void InitModules()
}
tcMain.SelectedIndex = 0;
+
+ _serviceManager.NvService?.InstallEventHandlers();
}
private void UpdateServiceInfo()
@@ -352,7 +350,7 @@ private UserControl InitLgService()
{
_serviceManager.LgService = _serviceProvider.GetRequiredService();
- _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 @@ private UserControl InitSamsungService()
//_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 @@ private void MainForm_Activated(object sender, EventArgs e)
}
}
- 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 @@ private async void MainForm_Click(object sender, EventArgs e)
//InstallUpdate("");
//await Test();
- //_serviceManager.NvService.TestResolution();
+ _serviceManager.NvService?.TestResolution();
}
private async Task Test()
@@ -1395,8 +1401,15 @@ private void btnOptionsAdvanced_Click(object sender, EventArgs e)
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 @@ private void btnOptionsAdvanced_Click(object sender, EventArgs e)
_config.ProcessMonitorPollingInterval = processPollingIntervalField.ValueAsInt;
_config.UseRawInput = useRawInputField.ValueAsBool;
+ _config.SetMinTmlAndMaxTml = setMinTmlAndMaxTmlField.ValueAsBool;
KeyboardShortcutManager.SetUseRawInput(_config.UseRawInput);
}
@@ -1441,5 +1455,10 @@ private void btnOptionsColorProfiles_Click(object sender, EventArgs e)
{
mnuColorProfiles.ShowCustom(btnOptionsColorProfiles);
}
+
+ private void miCreateSDRColorProfile_Click(object sender, EventArgs e)
+ {
+ ColorProfileWindow.CreateAndShow(isHDR: false);
+ }
}
}
\ No newline at end of file
diff --git a/ColorControl/Program.cs b/ColorControl/Program.cs
index 6646ffa..07b10af 100644
--- a/ColorControl/Program.cs
+++ b/ColorControl/Program.cs
@@ -273,6 +273,7 @@ private static async Task RunService(string[] args)
.ConfigureServices(services =>
{
services.RegisterSharedServices();
+ services.AddSingleton();
services.AddHostedService();
})
.Build();
diff --git a/ColorControl/Services/AMD/AmdPanel.cs b/ColorControl/Services/AMD/AmdPanel.cs
index cee18e4..36819b0 100644
--- a/ColorControl/Services/AMD/AmdPanel.cs
+++ b/ColorControl/Services/AMD/AmdPanel.cs
@@ -120,9 +120,9 @@ private void AddOrUpdateItemAmd(AmdPreset preset = null)
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)
diff --git a/ColorControl/Services/Common/PresetBase.cs b/ColorControl/Services/Common/PresetBase.cs
index 4b86706..fdd0bb2 100644
--- a/ColorControl/Services/Common/PresetBase.cs
+++ b/ColorControl/Services/Common/PresetBase.cs
@@ -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 @@ public bool TriggerActive(PresetTriggerContext context)
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 @@ public bool TriggerActive(PresetTriggerContext context)
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 @@ public override string ToString()
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((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((int)Conditions)) : string.Empty)}";
}
public static string DisplayProcesses(IEnumerable processes)
@@ -110,12 +126,14 @@ public static string DisplayProcesses(IEnumerable processes)
class PresetTriggerContext
{
+ public IEnumerable 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 ChangedProcesses { get; set; }
public bool IsNotificationDisabled { get; set; }
+ public ScreenSaverTransitionState ScreenSaverTransitionState { get; set; }
}
internal abstract class PresetBase
@@ -177,7 +195,7 @@ public virtual string GetTextForMenuItem()
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())
{
diff --git a/ColorControl/Services/Common/ServiceBase.cs b/ColorControl/Services/Common/ServiceBase.cs
index 5c96f33..d790320 100644
--- a/ColorControl/Services/Common/ServiceBase.cs
+++ b/ColorControl/Services/Common/ServiceBase.cs
@@ -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 @@ public abstract string ServiceName
protected List _presets;
protected T _lastAppliedPreset;
protected string _loadPresetsError;
+ protected T _lastTriggeredPreset;
protected AppContextProvider _appContextProvider;
@@ -176,5 +180,58 @@ public void ToggleQuickAccessForm()
QuickAccessForm.ToggleQuickAccessForm(this);
}
+ protected async Task CreateTriggerContext(ServiceManager serviceManager, ProcessChangedEventArgs context = null, bool? isHDRActive = null, IList triggerTypes = null)
+ {
+ triggerTypes ??= new[] { PresetTriggerType.ProcessSwitch };
+
+ var changedProcesses = new List();
+ 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 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);
+ }
+ }
+
}
}
diff --git a/ColorControl/Services/Common/ServiceFormUtils.cs b/ColorControl/Services/Common/ServiceFormUtils.cs
index a48025c..398d7cf 100644
--- a/ColorControl/Services/Common/ServiceFormUtils.cs
+++ b/ColorControl/Services/Common/ServiceFormUtils.cs
@@ -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 @@ public static void ListViewItemChecked(ListView listView, ItemCheckedEventArg
{
menu.DropDownItems.Clear();
- foreach (var nvPreset in service?.GetPresets() ?? new List())
+ foreach (var nvPreset in service?.GetPresets()?.OrderBy(p => p.name).ToList() ?? new List())
{
var text = nvPreset.name;
diff --git a/ColorControl/Services/EventDispatcher/EventDispatcher.cs b/ColorControl/Services/EventDispatcher/EventDispatcher.cs
index 38b1a07..08fae03 100644
--- a/ColorControl/Services/EventDispatcher/EventDispatcher.cs
+++ b/ColorControl/Services/EventDispatcher/EventDispatcher.cs
@@ -53,6 +53,8 @@ public async Task DispatchEventAsync(string eventName, T eventArgs)
{
await asyncEventHandlers[eventName]?.InvokeAsync(this, eventArgs);
}
+
+ DispatchEvent(eventName, eventArgs);
}
protected bool HasHandlers(string eventName)
diff --git a/ColorControl/Services/EventDispatcher/PowerEventDispatcher.cs b/ColorControl/Services/EventDispatcher/PowerEventDispatcher.cs
index 7823315..11ca589 100644
--- a/ColorControl/Services/EventDispatcher/PowerEventDispatcher.cs
+++ b/ColorControl/Services/EventDispatcher/PowerEventDispatcher.cs
@@ -1,5 +1,7 @@
using Microsoft.Win32;
+using NWin32;
using System;
+using System.Threading.Tasks;
namespace ColorControl.Services.EventDispatcher
{
@@ -36,10 +38,13 @@ public class PowerEventDispatcher : 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 @@ public void SendEvent(string eventName)
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 @@ public void SendEvent(string eventName)
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}");
+ }
+ }
}
}
diff --git a/ColorControl/Services/EventDispatcher/ProcessEventDispatcher.cs b/ColorControl/Services/EventDispatcher/ProcessEventDispatcher.cs
index 8069665..4ffc2b5 100644
--- a/ColorControl/Services/EventDispatcher/ProcessEventDispatcher.cs
+++ b/ColorControl/Services/EventDispatcher/ProcessEventDispatcher.cs
@@ -10,6 +10,14 @@
namespace ColorControl.Services.EventDispatcher
{
+ public enum ScreenSaverTransitionState
+ {
+ None = 0,
+ Started = 1,
+ Running = 2,
+ Stopped = 3
+ }
+
public class ProcessChangedEventArgs : EventArgs
{
public IList StartedProcesses { get; set; }
@@ -21,6 +29,7 @@ public class ProcessChangedEventArgs : EventArgs
public string LastFullScreenProcessName = string.Empty;
public bool StoppedFullScreen { get; set; }
public bool IsScreenSaverActive { get; set; }
+ public ScreenSaverTransitionState ScreenSaverTransitionState { get; set; }
}
@@ -100,7 +109,12 @@ private void FillContext(ProcessChangedEventArgs context)
{
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;
diff --git a/ColorControl/Services/LG/LgDevice.cs b/ColorControl/Services/LG/LgDevice.cs
index e7a846a..594c8bf 100644
--- a/ColorControl/Services/LG/LgDevice.cs
+++ b/ColorControl/Services/LG/LgDevice.cs
@@ -272,6 +272,8 @@ public LgDevice(string name, string ipAddress, string macAddress, bool isCustom
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 @@ private void AddSetConfigAction(string name, Type type, string title)
_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 @@ private async Task GenericSetConfigAction(Dictionary param
return true;
}
+ private async Task GenericLunaAction(Dictionary 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")
diff --git a/ColorControl/Services/LG/LgPanel.cs b/ColorControl/Services/LG/LgPanel.cs
index 1e58a11..77b9c5c 100644
--- a/ColorControl/Services/LG/LgPanel.cs
+++ b/ColorControl/Services/LG/LgPanel.cs
@@ -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.Collections.Generic;
using System.ComponentModel;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -28,6 +30,7 @@ public partial class LgPanel : UserControl, IModulePanel
private LgService _lgService;
private NvService _nvService;
private AmdService _amdService;
+ private readonly PowerEventDispatcher _powerEventDispatcher;
private IntPtr _mainHandle;
private NotifyIcon _trayIcon;
@@ -35,13 +38,14 @@ public partial class LgPanel : UserControl, IModulePanel
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 @@ internal LgPanel(LgService lgService, NvService nvService, AmdService amdService
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 @@ private void SetLgDevicesSelectedIndex(object sender)
cbxLgDevices.SelectedIndex = cbxLgDevices.Items.IndexOf(sender);
}
- private void AfterLgServiceRefreshDevices()
+ private async Task AfterLgServiceRefreshDevices()
{
FillLgDevices();
@@ -100,7 +111,14 @@ private void AfterLgServiceRefreshDevices()
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 @@ public void UpdateInfo()
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 @@ private void clbLgPower_ItemCheck(object sender, ItemCheckEventArgs e)
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:
diff --git a/ColorControl/Services/LG/LgService.cs b/ColorControl/Services/LG/LgService.cs
index 04a9368..a79214e 100644
--- a/ColorControl/Services/LG/LgService.cs
+++ b/ColorControl/Services/LG/LgService.cs
@@ -59,7 +59,6 @@ public LgDevice SelectedDevice
private bool _poweredOffByScreenSaver;
private int _poweredOffByScreenSaverProcessId;
- private LgPreset _lastTriggeredPreset;
private RestartDetector _restartDetector;
private readonly WinApiService _winApiService;
private readonly WinApiAdminService _winApiAdminService;
@@ -321,7 +320,7 @@ public async Task RefreshDevices(bool connect = true, bool afterStartUp = false)
{
if (_allowPowerOn)
{
- WakeAfterStartupOrResume();
+ await WakeAfterStartupOrResume();
}
else
{
@@ -415,7 +414,7 @@ internal bool WakeSelectedDevice()
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 @@ internal void WakeAfterStartupOrResume(PowerOnOffState state = PowerOnOffState.S
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 @@ private void SessionSwitched(object sender, SessionSwitchEventArgs e)
}
}
+ 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 @@ private void PowerModeChanged(object sender, PowerStateChangedEventArgs e)
}
}
+ 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 @@ public async Task ProcessChanged(object sender, ProcessChangedEventArgs args, Ca
{
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 @@ public async Task ProcessChanged(object sender, ProcessChangedEventArgs args, Ca
_poweredOffByScreenSaver = false;
_poweredOffByScreenSaverProcessId = 0;
- WakeAfterStartupOrResume(PowerOnOffState.ScreenSaver);
+ await WakeAfterStartupOrResume(PowerOnOffState.ScreenSaver);
+
+ await ExecuteScreenSaverPresetsCustom(args, applicableDevices);
return;
}
@@ -582,7 +622,7 @@ public async Task ProcessChanged(object sender, ProcessChangedEventArgs args, Ca
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 @@ public async Task ProcessChanged(object sender, ProcessChangedEventArgs args, Ca
}
}
- 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 @@ public async Task ProcessChanged(object sender, ProcessChangedEventArgs args, Ca
await ExecuteProcessPresets(args, connectedDevices);
+ await ExecuteScreenSaverPresetsCustom(args, connectedDevices);
}
catch (Exception ex)
{
@@ -629,6 +670,18 @@ public async Task ProcessChanged(object sender, ProcessChangedEventArgs args, Ca
}
}
+ private async Task ExecuteScreenSaverPresetsCustom(ProcessChangedEventArgs context, IList 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 connectedDevices)
{
var triggerDevices = connectedDevices.Where(d => d.TriggersEnabled).ToList();
@@ -648,12 +701,6 @@ private async Task ExecuteProcessPresets(ProcessChangedEventArgs context, IList<
return;
}
- var changedProcesses = new List();
- 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 @@ private async Task ExecuteProcessPresets(ProcessChangedEventArgs context, IList<
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 @@ public void PowerOffDevices(IEnumerable devices, PowerOnOffState state
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)
- {
- Thread.Sleep(100);
+ var sleep = Config.ShutdownDelay;
- if (_restartDetector != null && (_restartDetector.RestartDetected || _restartDetector.IsRebootInProgress()))
- {
- Logger.Debug("Not powering off because of a restart");
- return;
- }
+ Logger.Debug($"PowerOffDevices: Waiting for a maximum of {sleep} milliseconds...");
- sleep -= 100;
- }
- }
-
- Logger.Debug("Powering off tv(s)...");
- var tasks = new List();
- foreach (var device in devices)
+ while (sleep > 0 && _restartDetector?.PowerOffDetected == false)
{
- var task = device.PowerOff(true, false);
-
- tasks.Add(task);
- }
+ Thread.Sleep(100);
- if (state == PowerOnOffState.StandBy)
- {
- var standByScript = Path.Combine(Program.DataDir, "StandByScript.bat");
- if (File.Exists(standByScript))
+ if (_restartDetector != null && (_restartDetector.RestartDetected || _restartDetector.IsRebootInProgress()))
{
- _winApiAdminService.StartProcess(standByScript, hidden: true, wait: true);
+ Logger.Debug("Not powering off because of a restart");
+ return;
}
+
+ sleep -= 100;
}
+ }
- // We can't use async here because we need to stay on the main thread...
- var _ = Task.WhenAll(tasks.ToArray()).ConfigureAwait(true);
+ Logger.Debug("Powering off tv(s)...");
+ var tasks = new List();
+ foreach (var device in devices)
+ {
+ var task = device.PowerOff(true, false);
- Logger.Debug("Done powering off tv(s)");
+ tasks.Add(task);
}
- finally
+
+ if (state == PowerOnOffState.StandBy)
{
- var error2 = NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS);
- Logger.Debug($"SetThreadExecutionState (reset): {error2}, Thread#: {Environment.CurrentManagedThreadId}");
+ 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 devices, PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
+ public Task PowerOnDevicesTask(IEnumerable 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 devices, PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
@@ -924,5 +949,10 @@ private void SendConfigToService()
PipeUtils.SendMessage(message);
}
+
+ public async Task ExecuteEventPresets(PresetTriggerType triggerType)
+ {
+ await ExecuteEventPresets(_serviceManager, new[] { triggerType });
+ }
}
}
\ No newline at end of file
diff --git a/ColorControl/Services/NVIDIA/NvPanel.Designer.cs b/ColorControl/Services/NVIDIA/NvPanel.Designer.cs
index 3fbd11d..21e2cc5 100644
--- a/ColorControl/Services/NVIDIA/NvPanel.Designer.cs
+++ b/ColorControl/Services/NVIDIA/NvPanel.Designer.cs
@@ -80,6 +80,10 @@ private void InitializeComponent()
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 @@ private void InitializeComponent()
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 @@ private void InitializeComponent()
// 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 @@ private void InitializeComponent()
// 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 @@ private void InitializeComponent()
// 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 @@ private void InitializeComponent()
//
// 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 @@ private void InitializeComponent()
// 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 @@ private void InitializeComponent()
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 @@ private void InitializeComponent()
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;
}
}
diff --git a/ColorControl/Services/NVIDIA/NvPanel.cs b/ColorControl/Services/NVIDIA/NvPanel.cs
index b38472c..670bf7a 100644
--- a/ColorControl/Services/NVIDIA/NvPanel.cs
+++ b/ColorControl/Services/NVIDIA/NvPanel.cs
@@ -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 @@ private void mnuNvPresets_Opening(object sender, CancelEventArgs e)
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 @@ private void mnuNvPresets_Opening(object sender, CancelEventArgs e)
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 @@ private void lvNvPresets_MouseMove(object sender, MouseEventArgs e)
var text = subItem.Text;
- if (index == 7)
+ if (index == 8)
{
text = text.Replace(", ", "\r\n");
}
@@ -1944,9 +1950,9 @@ private void spatial1ToolStripMenuItem_Click(object sender, EventArgs e)
AddOrUpdateItem();
}
- public void AfterInitialized()
+ public async Task AfterInitialized()
{
- var _ = ApplyNvPresetOnStartup();
+ await ApplyNvPresetOnStartup();
}
internal void Save()
@@ -2004,5 +2010,74 @@ private void miNvPresetColorEnhancementsIncluded_Click(object sender, EventArgs
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(new[] { PresetTriggerType.ProcessSwitch, PresetTriggerType.ScreensaverStart, PresetTriggerType.ScreensaverStop, PresetTriggerType.Reserved5 }).Select(d => d.Value);
+ var conditions = Utils.GetDescriptions(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();
+ trigger.Conditions = conditionsField.ValueAsEnum();
+
+ preset.UpdateTrigger(trigger.Trigger, trigger.Conditions);
+
+ AddOrUpdateItem();
+
+ }
}
}
diff --git a/ColorControl/Services/NVIDIA/NvPreset.cs b/ColorControl/Services/NVIDIA/NvPreset.cs
index 4bb50c7..e34827b 100644
--- a/ColorControl/Services/NVIDIA/NvPreset.cs
+++ b/ColorControl/Services/NVIDIA/NvPreset.cs
@@ -47,6 +47,7 @@ class NvPreset : PresetBase
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 @@ public NvPreset() : base()
HdmiInfoFrameSettings = new NvHdmiInfoFrameSettings();
ColorProfileSettings = new NvColorProfileSettings();
ColorEnhancementSettings = new NvColorEnhancementSettings();
+ HdrSettings = new NvHdrSettings();
}
public NvPreset(ColorData colorData) : this()
@@ -102,6 +104,8 @@ public NvPreset(NvPreset preset) : this()
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 @@ public override List GetDisplayValues(Config config = null)
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));
diff --git a/ColorControl/Services/NVIDIA/NvService.cs b/ColorControl/Services/NVIDIA/NvService.cs
index ca17db5..9fc7759 100644
--- a/ColorControl/Services/NVIDIA/NvService.cs
+++ b/ColorControl/Services/NVIDIA/NvService.cs
@@ -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 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.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 @@ class NvService : GraphicsService
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 @@ public delegate int NvAPI_Disp_SetDitherControl(
[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 @@ public struct NV_GPU_DITHER_CONTROL_V1
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 @@ public struct NV_GPU_DITHER_CONTROL_V1
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 @@ public struct NV_GPU_DITHER_CONTROL_V1
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 _driverSettingIds = new()
{
DRS_GSYNC_APPLICATION_MODE,
@@ -148,12 +199,16 @@ public struct NV_GPU_DITHER_CONTROL_V1
};
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 @@ public NvService(AppContextProvider appContextProvider, WinApiService winApiServ
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 ExecutePresetAsync(string idOrName)
{
try
@@ -354,14 +417,22 @@ public override async Task ApplyPreset(NvPreset preset)
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 @@ public override async Task ApplyPreset(NvPreset preset)
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 @@ private bool ColorDataDiffers(ColorData colorData)
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 @@ public int GetHueAngle(Display display)
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(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();
+
+ 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();
+
+ 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 result = true;
+ var delegateValue = DelegateFactory.GetDelegate();
- var ptr = NvAPI64_QueryInterface(0xDF0DFCDD);
- if (ptr != IntPtr.Zero)
+ if (delegateValue == null)
{
- var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_SetDitherControl)) as NvAPI_Disp_SetDitherControl;
+ return false;
+ }
- var display = currentDisplay ?? GetCurrentDisplay();
- if (display == null)
- {
- return false;
- }
+ var result = true;
- var displayDevice = display.DisplayDevice;
+ var display = currentDisplay ?? GetCurrentDisplay();
+ if (display == null)
+ {
+ return false;
+ }
- var gpuHandle = displayDevice.PhysicalGPU.Handle;
- var displayId = displayDevice.DisplayId;
+ var displayDevice = display.DisplayDevice;
- 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 gpuHandle = displayDevice.PhysicalGPU.Handle;
+ var displayId = displayDevice.DisplayId;
- 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;
- }
+ if (state == NvDitherState.Auto)
+ {
+ // These seem to be ignored in Auto-state
+ bits = 0;
+ mode = 0;
}
- else
+ 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 the function NvAPI_Disp_SetDitherControl could not be found");
+ Logger.Error($"Could not set dithering because NvAPI_Disp_SetDitherControl returned a non-zero return code: {resultValue}");
result = false;
}
@@ -687,33 +844,32 @@ public bool SetDithering(NvDitherState state, uint bits = 1, uint mode = 4, NvPr
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(1);
+ var dither = new NV_GPU_DITHER_CONTROL_V1 { version = version };
- var ptr = NvAPI64_QueryInterface(0x932AC8FB);
- if (ptr != IntPtr.Zero)
+ var delegateValue = DelegateFactory.GetDelegate();
+
+ if (delegateValue == null)
{
- var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_GetDitherControl)) as NvAPI_Disp_GetDitherControl;
+ dither.state = -2;
+ return dither;
+ }
- var display = currentDisplay ?? GetCurrentDisplay();
- if (display == null)
- {
- dither.state = -1;
- return dither;
- }
+ var display = currentDisplay ?? GetCurrentDisplay();
+ if (display == null)
+ {
+ dither.state = -1;
+ return dither;
+ }
- var displayDevice = display.DisplayDevice;
- var displayId = displayDevice.DisplayId;
+ 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
+ var result = delegateValue(displayId, ref dither);
+ if (result != 0)
{
- dither.state = -2;
+ 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 @@ public void SetHDMIContentType(Display display, InfoFrameVideoContentType conten
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 @@ public NvHdmiInfoFrameSettings GetHdmiSettings(Display display)
};
}
+ 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 @@ public void SetScaling(Display display, Scaling scaling)
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 @@ public NvPreset GetPresetForDisplay(Display display, bool driverSettings = false
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 @@ protected override void Initialize()
NvAPIWrapper.NVIDIA.Initialize();
_initialized = true;
+ var version = default(string);
+ DriverVersion = GeneralApi.GetDriverAndBranchVersion(out version);
+
_drs = DrsServiceLocator.SettingService;
_meta = DrsServiceLocator.MetaService;
@@ -1415,5 +1582,53 @@ internal Display ResolveDisplay(NvPreset preset)
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(int version)
+ {
+ return (uint)((Marshal.SizeOf(typeof(T))) | version << 16);
+ }
}
}
diff --git a/ColorControl/Services/Samsung/SamsungPanel.cs b/ColorControl/Services/Samsung/SamsungPanel.cs
index b33ccb5..cdf90f2 100644
--- a/ColorControl/Services/Samsung/SamsungPanel.cs
+++ b/ColorControl/Services/Samsung/SamsungPanel.cs
@@ -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.Collections.Generic;
using System.ComponentModel;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -27,17 +29,17 @@ public partial class SamsungPanel : UserControl, IModulePanel
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 @@ internal SamsungPanel(SamsungService samsungService, NvService nvService, AmdSer
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 @@ private void SetSamsungDevicesSelectedIndex(object sender)
cbxSamsungDevices.SelectedIndex = cbxSamsungDevices.Items.IndexOf(sender);
}
- private void AfterSamsungServiceRefreshDevices()
+ private async Task AfterSamsungServiceRefreshDevices()
{
FillSamsungDevices();
@@ -98,7 +106,13 @@ private void AfterSamsungServiceRefreshDevices()
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 @@ public void UpdateInfo()
//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 @@ private void clbLgPower_ItemCheck(object sender, ItemCheckEventArgs e)
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 @@ private void btnSamsungPresetEditTriggerConditions_Click(object sender, EventArg
}
var value = (PresetConditionType)values.First().Value;
- edtSamsungPresetTriggerConditions.Tag = (PresetConditionType)values.First().Value;
+ edtSamsungPresetTriggerConditions.Tag = value;
edtSamsungPresetTriggerConditions.Text = Utils.GetDescriptions((int)value).Join(", ");
}
diff --git a/ColorControl/Services/Samsung/SamsungService.cs b/ColorControl/Services/Samsung/SamsungService.cs
index 8973b7f..a048360 100644
--- a/ColorControl/Services/Samsung/SamsungService.cs
+++ b/ColorControl/Services/Samsung/SamsungService.cs
@@ -37,7 +37,6 @@ class SamsungService : ServiceBase, ISamsungService
private string _configFilename;
private bool _poweredOffByScreenSaver;
private int _poweredOffByScreenSaverProcessId;
- private object _lastTriggeredPreset;
private List _samsungApps = new List();
private Shared.Common.GlobalContext _appContext;
@@ -88,7 +87,7 @@ public void InstallEventHandlers()
_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 @@ private void LoadConfig()
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 @@ private void PowerModeChanged(object sender, PowerStateChangedEventArgs e)
}
}
+ 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 @@ public async Task RefreshDevices(bool connect = true, bool afterStartUp = false)
{
if (_allowPowerOn)
{
- WakeAfterStartupOrResume();
+ await WakeAfterStartupOrResume();
}
else
{
@@ -308,7 +343,7 @@ internal void RemoveCustomDevice(SamsungDevice device)
}
}
- 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 @@ private void WakeAfterStartupOrResume(PowerOnOffState state = PowerOnOffState.St
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 devices, PowerOnOffState state = PowerOnOffState.ShutDown)
@@ -332,63 +367,53 @@ private void PowerOffDevices(IEnumerable devices, PowerOnOffState
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)
- {
- Thread.Sleep(100);
-
- if (_restartDetector != null && (_restartDetector.RestartDetected || _restartDetector.IsRebootInProgress()))
- {
- Logger.Debug("Not powering off because of a restart");
- return;
- }
+ var sleep = Config.ShutdownDelay;
- sleep -= 100;
- }
- }
+ Logger.Debug($"PowerOffDevices: Waiting for a maximum of {sleep} milliseconds...");
- Logger.Debug("Powering off tv(s)...");
- var tasks = new List();
- foreach (var device in devices)
+ while (sleep > 0 && _restartDetector?.PowerOffDetected == false)
{
- var task = device.PowerOffAsync();
-
- tasks.Add(task);
- }
+ Thread.Sleep(100);
- if (state == PowerOnOffState.StandBy)
- {
- var standByScript = Path.Combine(Program.DataDir, "StandByScript.bat");
- if (File.Exists(standByScript))
+ if (_restartDetector != null && (_restartDetector.RestartDetected || _restartDetector.IsRebootInProgress()))
{
- _winApiAdminService.StartProcess(standByScript, hidden: true, wait: true);
+ Logger.Debug("Not powering off because of a restart");
+ return;
}
+
+ sleep -= 100;
}
+ }
- // We can't use async here because we need to stay on the main thread...
- var _ = Task.WhenAll(tasks.ToArray()).ConfigureAwait(true);
+ Logger.Debug("Powering off tv(s)...");
+ var tasks = new List();
+ foreach (var device in devices)
+ {
+ var task = device.PowerOffAsync();
- Logger.Debug("Done powering off tv(s)");
+ tasks.Add(task);
}
- finally
+
+ if (state == PowerOnOffState.StandBy)
{
- var error2 = NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS);
- Logger.Debug($"SetThreadExecutionState (reset): {error2}, Thread#: {Environment.CurrentManagedThreadId}");
+ 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 devices, PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
+ private Task PowerOnDevicesTask(IEnumerable 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 devices, PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
@@ -460,10 +485,15 @@ private async Task ProcessChanged(object sender, ProcessChangedEventArgs args, C
{
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 @@ private async Task ProcessChanged(object sender, ProcessChangedEventArgs args, C
_poweredOffByScreenSaver = false;
_poweredOffByScreenSaverProcessId = 0;
- WakeAfterStartupOrResume(PowerOnOffState.ScreenSaver);
+ await WakeAfterStartupOrResume(PowerOnOffState.ScreenSaver);
+
+ await ExecuteScreenSaverPresetsCustom(args, applicableDevices);
return;
}
@@ -507,7 +539,7 @@ private async Task ProcessChanged(object sender, ProcessChangedEventArgs args, C
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 @@ private async Task ProcessChanged(object sender, ProcessChangedEventArgs args, C
}
}
- 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 @@ private async Task ProcessChanged(object sender, ProcessChangedEventArgs args, C
await ExecuteProcessPresets(args, connectedDevices);
+ await ExecuteScreenSaverPresetsCustom(args, connectedDevices);
}
catch (Exception ex)
{
@@ -554,6 +587,18 @@ private async Task ProcessChanged(object sender, ProcessChangedEventArgs args, C
}
}
+ private async Task ExecuteScreenSaverPresetsCustom(ProcessChangedEventArgs context, IList 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 connectedDevices)
{
var triggerDevices = connectedDevices.Where(d => d.Options.TriggersEnabled).ToList();
@@ -588,19 +633,7 @@ private async Task ExecuteProcessPresets(ProcessChangedEventArgs context, IList<
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 @@ private async Task HandleScreenSaverProcessAsync(IList processes, List<
Logger.Error($"Screensaver check: can't power off: " + e.ToLogString());
}
}
+
+ public async Task ExecuteEventPresets(PresetTriggerType triggerType)
+ {
+ await ExecuteEventPresets(_serviceManager, new[] { triggerType });
+ }
}
-}
+}
\ No newline at end of file
diff --git a/ColorControl/XForms/ColorProfileViewModel.cs b/ColorControl/XForms/ColorProfileViewModel.cs
index 223ba10..976ce03 100644
--- a/ColorControl/XForms/ColorProfileViewModel.cs
+++ b/ColorControl/XForms/ColorProfileViewModel.cs
@@ -7,9 +7,11 @@
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 ExistingProfiles { get; set; }
+ public ObservableCollection 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 SaveOptions { get; } = Utils.EnumToDictionary();
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 @@ public override string this[string columnName]
{
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 @@ public ColorProfileViewModel(bool isHDR = true)
RefreshDisplays();
- ExistingProfiles = new List { CreateANewProfile };
+ ExistingProfiles = new ObservableCollection { CreateANewProfile };
NewProfileName = $"CC_{(isHDR ? "HDR" : "SDR")}_profile_{DateTime.Now:yyyyMMddHHmmss}.icm";
Description = $"CC_{(isHDR ? "HDR" : "SDR")}";
@@ -163,11 +186,16 @@ public ColorProfileViewModel(bool isHDR = true)
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 @@ private void UpdateDisplay()
}
}
- ExistingProfiles = new List { CreateANewProfile };
- ExistingProfiles.AddRange(CCD.GetDisplayColorProfiles(SelectedDisplay.DisplayName));
+ ExistingProfiles = new ObservableCollection { CreateANewProfile };
+
+ var profiles = CCD.GetDisplayColorProfiles(SelectedDisplay.DisplayName);
+
+ foreach (var profile in profiles)
+ {
+ ExistingProfiles.Add(profile);
+ }
}
private void GetEDID()
diff --git a/ColorControl/XForms/ColorProfileWindow.xaml b/ColorControl/XForms/ColorProfileWindow.xaml
index 7030df6..46066d0 100644
--- a/ColorControl/XForms/ColorProfileWindow.xaml
+++ b/ColorControl/XForms/ColorProfileWindow.xaml
@@ -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 @@
-
-
-
-
-
-
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
-
+
-
-
+
+
-
+
-
+
-
+
-
-
+
+
@@ -81,21 +81,24 @@
-
-
+
+
-
+
+
-
+
+
+
-
-
+
+
diff --git a/ColorControl/XForms/ColorProfileWindow.xaml.cs b/ColorControl/XForms/ColorProfileWindow.xaml.cs
index e133edc..9bf73cc 100644
--- a/ColorControl/XForms/ColorProfileWindow.xaml.cs
+++ b/ColorControl/XForms/ColorProfileWindow.xaml.cs
@@ -18,14 +18,18 @@ public partial class ColorProfileWindow : BaseWindow
{
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 @@ private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)
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();
+ var winApiAdminService = Program.ServiceProvider.GetRequiredService();
+ var appContextProvider = Program.ServiceProvider.GetRequiredService();
+ var window = new ColorProfileWindow(winApiAdminService, appContextProvider, isHDR);
if (show)
{
@@ -87,7 +93,11 @@ private void Window_Loaded(object sender, RoutedEventArgs e)
{
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 @@ private void Button_Click(object sender, RoutedEventArgs e)
var command = new GenerateProfileCommand
{
Description = _viewModel.Description,
+ IsHDRProfile = _viewModel.IsHDR,
MinCLL = _viewModel.MinCLL,
MaxCLL = _viewModel.MaxCLL,
BlackLuminance = _viewModel.BlackLuminance,
@@ -146,7 +157,81 @@ private void Button_Click(object sender, RoutedEventArgs e)
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();
+ }
}
}
}
\ No newline at end of file
diff --git a/ColorControl/lgtv/LgTvApi.cs b/ColorControl/lgtv/LgTvApi.cs
index b2d518c..b8869dc 100644
--- a/ColorControl/lgtv/LgTvApi.cs
+++ b/ColorControl/lgtv/LgTvApi.cs
@@ -538,12 +538,12 @@ public async Task TurnOff3D()
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 IsTurnedOn3D()
@@ -847,6 +847,18 @@ public async Task SetDeviceConfig(string id, string icon, string label)
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[]
diff --git a/MHC2Gen/IccContext.cs b/MHC2Gen/IccContext.cs
index 068abc4..eaac37f 100644
--- a/MHC2Gen/IccContext.cs
+++ b/MHC2Gen/IccContext.cs
@@ -5,6 +5,7 @@
using System;
using System.Buffers.Binary;
using System.IO;
+using System.Text.Json;
namespace MHC2Gen
{
@@ -206,6 +207,16 @@ public byte[] ToBytes()
}
}
+ 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 @@ public static Matrix GetChromaticAdaptationMatrix(CIEXYZ sourceIlluminan
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 @@ internal class DeviceIccContext : IccContext
ToneCurve profileGreenReverseToneCurve;
ToneCurve profileBlueReverseToneCurve;
+ public ExtraInfoTag? ExtraInfoTag { get; }
+
public bool UseChromaticAdaptation { get; set; }
public DeviceIccContext(IccProfile profile) : base(profile)
@@ -401,6 +414,16 @@ public DeviceIccContext(IccProfile profile) : base(profile)
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(json);
+ }
+ }
}
public static Matrix RgbToXYZ(RgbPrimaries primaries)
@@ -450,7 +473,7 @@ public string GetDeviceDescription()
{
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 @@ public IccProfile CreateIcc(GenerateProfileCommand command)
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 @@ public IccProfile CreateIcc(GenerateProfileCommand command)
MaxCLL = command.MaxCLL
};
- if (command.SDRTransferFunction == SDRTransferFunction.Piecewise)
+ if (!command.IsHDRProfile || command.SDRTransferFunction == SDRTransferFunction.Piecewise)
{
MHC2.ApplyPiecewise();
}
@@ -966,6 +989,20 @@ public IccProfile CreateIcc(GenerateProfileCommand command)
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 @@ public IccProfile CreateCscIcc(RgbPrimaries? sourcePrimaries = null, string sour
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);
diff --git a/MHC2Gen/MHC2Wrapper.cs b/MHC2Gen/MHC2Wrapper.cs
index 27c05a6..7542032 100644
--- a/MHC2Gen/MHC2Wrapper.cs
+++ b/MHC2Gen/MHC2Wrapper.cs
@@ -1,4 +1,6 @@
using LittleCms;
+using System;
+using System.IO;
namespace MHC2Gen;
@@ -23,4 +25,47 @@ public static byte[] GenerateSdrAcmProfile(GenerateProfileCommand command)
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
+ };
+ }
}
diff --git a/MHC2Gen/ST2084.cs b/MHC2Gen/ST2084.cs
index e86d1a6..6410356 100644
--- a/MHC2Gen/ST2084.cs
+++ b/MHC2Gen/ST2084.cs
@@ -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; }
diff --git a/NvAPIWrapper/Native/Attributes/FunctionIdAttribute.cs b/NvAPIWrapper/Native/Attributes/FunctionIdAttribute.cs
index 260da7d..420bbb3 100644
--- a/NvAPIWrapper/Native/Attributes/FunctionIdAttribute.cs
+++ b/NvAPIWrapper/Native/Attributes/FunctionIdAttribute.cs
@@ -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)
{
diff --git a/NvAPIWrapper/Native/GeneralApi.cs b/NvAPIWrapper/Native/GeneralApi.cs
index 4d31533..a015c70 100644
--- a/NvAPIWrapper/Native/GeneralApi.cs
+++ b/NvAPIWrapper/Native/GeneralApi.cs
@@ -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
{
diff --git a/NvAPIWrapper/Native/Helpers/DelegateFactory.cs b/NvAPIWrapper/Native/Helpers/DelegateFactory.cs
index 662692e..6c7a423 100644
--- a/NvAPIWrapper/Native/Helpers/DelegateFactory.cs
+++ b/NvAPIWrapper/Native/Helpers/DelegateFactory.cs
@@ -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, object> Delegates =
new Dictionary, object>();
@@ -37,7 +37,7 @@ public static T GetDelegate() where T : class
return Delegates[delegateKey] as T;
}
- var ptr = NvAPI_QueryInterface((uint) functionId.FunctionId);
+ var ptr = NvAPI_QueryInterface((uint)functionId.FunctionId);
if (ptr != IntPtr.Zero)
{
diff --git a/NvAPIWrapper/Native/Helpers/FunctionId.cs b/NvAPIWrapper/Native/Helpers/FunctionId.cs
index 8dc6885..0a243a4 100644
--- a/NvAPIWrapper/Native/Helpers/FunctionId.cs
+++ b/NvAPIWrapper/Native/Helpers/FunctionId.cs
@@ -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 @@ internal enum FunctionId : uint
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,
diff --git a/Shared/Common/Utils.cs b/Shared/Common/Utils.cs
index 3f62336..40430db 100644
--- a/Shared/Common/Utils.cs
+++ b/Shared/Common/Utils.cs
@@ -206,9 +206,9 @@ public static string FirstCharUpperCase(string name)
return name.Substring(0, 1).ToUpper() + name.Substring(1);
}
- public static Dictionary EnumToDictionary() where T : struct, Enum
+ public static Dictionary EnumToDictionary(IEnumerable skipValues = null) where T : struct, Enum
{
- return Enum.GetValues().ToDictionary(k => k, e => e.GetDescription());
+ return Enum.GetValues().Where(v => skipValues == null || !skipValues.Contains(v)).ToDictionary(k => k, e => e.GetDescription());
}
public static string GetDescriptionByEnumName(string value) where T : IConvertible
diff --git a/Shared/Contracts/Config.cs b/Shared/Contracts/Config.cs
index c7c2671..3fae172 100644
--- a/Shared/Contracts/Config.cs
+++ b/Shared/Contracts/Config.cs
@@ -40,6 +40,7 @@ public class Config
public string LogLevel { get; set; }
public List Modules { get; set; }
public bool UseRawInput { get; set; }
+ public bool SetMinTmlAndMaxTml { get; set; }
public Config()
{
diff --git a/Shared/Contracts/NVIDIA/NvHdrSettings.cs b/Shared/Contracts/NVIDIA/NvHdrSettings.cs
new file mode 100644
index 0000000..ff7e290
--- /dev/null
+++ b/Shared/Contracts/NVIDIA/NvHdrSettings.cs
@@ -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;
+ }
+ }
+}
diff --git a/Shared/Forms/MessageForms.cs b/Shared/Forms/MessageForms.cs
index 5c39059..489d8d7 100644
--- a/Shared/Forms/MessageForms.cs
+++ b/Shared/Forms/MessageForms.cs
@@ -316,6 +316,11 @@ public static List ShowDialog(string caption, IEnumerable
+ {
+ 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();
+ 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(DisplayConfigModeInfoDelegate infoDelegate, string displayName = null)
{
uint pathCount, modeCount;
@@ -583,6 +638,7 @@ private enum DISPLAYCONFIG_DEVICE_INFO_TYPE
}
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 @@ private struct DISPLAYCONFIG_GET_DISPLAY_INFO
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 @@ static class NativeMethods
[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);
diff --git a/Shared/Shared.csproj b/Shared/Shared.csproj
index bc10159..f64cc3e 100644
--- a/Shared/Shared.csproj
+++ b/Shared/Shared.csproj
@@ -41,6 +41,7 @@
+
diff --git a/novideo_srgb/MainWindow.xaml.cs b/novideo_srgb/MainWindow.xaml.cs
index 3d7b21f..7f5a85a 100644
--- a/novideo_srgb/MainWindow.xaml.cs
+++ b/novideo_srgb/MainWindow.xaml.cs
@@ -73,7 +73,7 @@ public static void UpdateContextMenu(ToolStripMenuItem parent)
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;
diff --git a/novideo_srgb/MonitorData.cs b/novideo_srgb/MonitorData.cs
index 6b5e846..9074c8c 100644
--- a/novideo_srgb/MonitorData.cs
+++ b/novideo_srgb/MonitorData.cs
@@ -179,6 +179,11 @@ public void ReapplyClamp(bool? forceClamp = null)
{
try
{
+ if (!ClampSdr)
+ {
+ return;
+ }
+
var clamped = CanClamp && ClampSdr && (!forceClamp.HasValue || forceClamp.Value);
UpdateClamp(clamped);
_clamped = clamped;