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