From f666a6117b341ca36941aa538383590503127425 Mon Sep 17 00:00:00 2001 From: Vincent Maas Date: Sat, 5 Sep 2020 14:46:31 +0200 Subject: [PATCH] add minimize on close option, fix reconnection issues, send WOL packet to all network interfaces, add current display settings line --- ColorControl/ColorControl.csproj | 2 +- ColorControl/Config.cs | 2 + ColorControl/LgService.cs | 91 ++++++++++------ ColorControl/MainForm.Designer.cs | 23 ++++- ColorControl/MainForm.cs | 131 +++++++++++++++++++++--- ColorControl/NvPreset.cs | 2 +- ColorControl/NvService.cs | 48 ++++++++- ColorControl/Properties/AssemblyInfo.cs | 4 +- ColorControl/WOL.cs | 14 +-- ColorControl/lgtv/LgTvApi.cs | 5 +- ColorControl/lgtv/LgTvConnection.cs | 24 ++++- 11 files changed, 276 insertions(+), 70 deletions(-) diff --git a/ColorControl/ColorControl.csproj b/ColorControl/ColorControl.csproj index af8ce9f..df4d882 100644 --- a/ColorControl/ColorControl.csproj +++ b/ColorControl/ColorControl.csproj @@ -27,7 +27,7 @@ ColorControl Maassoft 0 - 1.1.0.%2a + 1.2.0.0 false true true diff --git a/ColorControl/Config.cs b/ColorControl/Config.cs index a33af3d..828037b 100644 --- a/ColorControl/Config.cs +++ b/ColorControl/Config.cs @@ -4,6 +4,8 @@ { public bool StartMinimized { get; set; } + public bool MinimizeOnClose { get; set; } + public int DisplaySettingsDelay { get; set; } public string ScreenSaverShortcut { get; set; } diff --git a/ColorControl/LgService.cs b/ColorControl/LgService.cs index 71bea36..f5e2afc 100644 --- a/ColorControl/LgService.cs +++ b/ColorControl/LgService.cs @@ -132,7 +132,7 @@ namespace ColorControl return false; } - if (reconnect || _lgTvApi == null || !string.Equals(_lgTvApi.GetIpAddress(), SelectedDevice.IpAddress)) + if (reconnect || _lgTvApi == null || _lgTvApi.ConnectionClosed || !string.Equals(_lgTvApi.GetIpAddress(), SelectedDevice.IpAddress)) { if (!await ConnectToSelectedDevice()) { @@ -146,40 +146,64 @@ namespace ColorControl public async Task ApplyPreset(LgPreset preset, bool reconnect = false) { - if (!await Connected(reconnect)) - { - return false; - } - var hasApp = !string.IsNullOrEmpty(preset.appId); - if (hasApp) + for (var tries = 0; tries <= 1; tries++) { - await _lgTvApi.LaunchApp(preset.appId); - - if (_justWokeUp) + if (!await Connected(reconnect || tries == 1)) { - _justWokeUp = false; - await Task.Delay(500); - } - } - - if (preset.steps.Any()) - { - if (hasApp) - { - await Task.Delay(1500); - } - try - { - await ExecuteSteps(_lgTvApi, preset); - } - catch (Exception ex) - { - string logMessage = ex.ToLogString(Environment.StackTrace); - Logger.Error("Error while executing steps: " + logMessage); return false; } + + if (hasApp) + { + try + { + await _lgTvApi.LaunchApp(preset.appId); + } + catch (Exception ex) + { + string logMessage = ex.ToLogString(Environment.StackTrace); + Logger.Error("Error while launching app: " + logMessage); + + if (tries == 0) + { + continue; + } + return false; + } + + if (_justWokeUp) + { + _justWokeUp = false; + await Task.Delay(1000); + } + } + + if (preset.steps.Any()) + { + if (hasApp) + { + await Task.Delay(1500); + } + try + { + await ExecuteSteps(_lgTvApi, preset); + } + catch (Exception ex) + { + string logMessage = ex.ToLogString(Environment.StackTrace); + Logger.Error("Error while executing steps: " + logMessage); + + if (tries == 0) + { + continue; + } + return false; + } + } + + return true; } return true; @@ -227,8 +251,13 @@ namespace ColorControl return null; } - var api = await LgTvApi.CreateLgTvApi(SelectedDevice.IpAddress); - var list = await api.GetApps(force); + if (!await Connected(force)) + { + Logger.Debug("Cannot refresh apps: no connection could be made"); + return null; + } + + var list = await _lgTvApi.GetApps(force); return list.ToList(); } diff --git a/ColorControl/MainForm.Designer.cs b/ColorControl/MainForm.Designer.cs index 54d2c33..c0c7df1 100644 --- a/ColorControl/MainForm.Designer.cs +++ b/ColorControl/MainForm.Designer.cs @@ -98,6 +98,7 @@ this.edtDelayDisplaySettings = new System.Windows.Forms.NumericUpDown(); this.label6 = new System.Windows.Forms.Label(); this.grpGeneralOptions = new System.Windows.Forms.GroupBox(); + this.chkMinimizeOnClose = new System.Windows.Forms.CheckBox(); this.chkStartMinimized = new System.Windows.Forms.CheckBox(); this.chkStartAfterLogin = new System.Windows.Forms.CheckBox(); this.tabLog = new System.Windows.Forms.TabPage(); @@ -722,7 +723,7 @@ this.grpMiscellaneousOptions.Controls.Add(this.edtBlankScreenSaverShortcut); this.grpMiscellaneousOptions.Controls.Add(this.label10); this.grpMiscellaneousOptions.Controls.Add(this.chkFixChromeFonts); - this.grpMiscellaneousOptions.Location = new System.Drawing.Point(6, 275); + this.grpMiscellaneousOptions.Location = new System.Drawing.Point(6, 309); this.grpMiscellaneousOptions.Name = "grpMiscellaneousOptions"; this.grpMiscellaneousOptions.Size = new System.Drawing.Size(400, 130); this.grpMiscellaneousOptions.TabIndex = 5; @@ -784,7 +785,7 @@ this.grpLGOptions.Controls.Add(this.label9); this.grpLGOptions.Controls.Add(this.edtLgPowerOnAfterResumeDelay); this.grpLGOptions.Controls.Add(this.label8); - this.grpLGOptions.Location = new System.Drawing.Point(6, 162); + this.grpLGOptions.Location = new System.Drawing.Point(6, 196); this.grpLGOptions.Name = "grpLGOptions"; this.grpLGOptions.Size = new System.Drawing.Size(403, 107); this.grpLGOptions.TabIndex = 4; @@ -843,7 +844,7 @@ // this.grpHDROptions.Controls.Add(this.edtDelayDisplaySettings); this.grpHDROptions.Controls.Add(this.label6); - this.grpHDROptions.Location = new System.Drawing.Point(6, 84); + this.grpHDROptions.Location = new System.Drawing.Point(6, 118); this.grpHDROptions.Name = "grpHDROptions"; this.grpHDROptions.Size = new System.Drawing.Size(400, 72); this.grpHDROptions.TabIndex = 3; @@ -881,15 +882,27 @@ // // grpGeneralOptions // + this.grpGeneralOptions.Controls.Add(this.chkMinimizeOnClose); this.grpGeneralOptions.Controls.Add(this.chkStartMinimized); this.grpGeneralOptions.Controls.Add(this.chkStartAfterLogin); this.grpGeneralOptions.Location = new System.Drawing.Point(6, 6); this.grpGeneralOptions.Name = "grpGeneralOptions"; - this.grpGeneralOptions.Size = new System.Drawing.Size(400, 72); + this.grpGeneralOptions.Size = new System.Drawing.Size(400, 106); this.grpGeneralOptions.TabIndex = 2; this.grpGeneralOptions.TabStop = false; this.grpGeneralOptions.Text = "General"; // + // chkMinimizeOnClose + // + this.chkMinimizeOnClose.AutoSize = true; + this.chkMinimizeOnClose.Location = new System.Drawing.Point(6, 65); + this.chkMinimizeOnClose.Name = "chkMinimizeOnClose"; + this.chkMinimizeOnClose.Size = new System.Drawing.Size(109, 17); + this.chkMinimizeOnClose.TabIndex = 4; + this.chkMinimizeOnClose.Text = "Minimize on close"; + this.chkMinimizeOnClose.UseVisualStyleBackColor = true; + this.chkMinimizeOnClose.CheckedChanged += new System.EventHandler(this.chkMinimizeOnClose_CheckedChanged); + // // chkStartMinimized // this.chkStartMinimized.AutoSize = true; @@ -1032,6 +1045,7 @@ this.MinimumSize = new System.Drawing.Size(800, 398); this.Name = "MainForm"; this.Text = "ColorControl"; + this.Activated += new System.EventHandler(this.MainForm_Activated); this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.MainForm_FormClosed); this.Load += new System.EventHandler(this.MainForm_Load); @@ -1146,6 +1160,7 @@ private System.Windows.Forms.Button btnSetShortcutScreenSaver; private System.Windows.Forms.Label label11; private System.Windows.Forms.TextBox edtBlankScreenSaverShortcut; + private System.Windows.Forms.CheckBox chkMinimizeOnClose; } } diff --git a/ColorControl/MainForm.cs b/ColorControl/MainForm.cs index 19cd48f..c922e3e 100644 --- a/ColorControl/MainForm.cs +++ b/ColorControl/MainForm.cs @@ -45,6 +45,8 @@ namespace ColorControl private static int WM_HOTKEY = 0x0312; private static string TS_TASKNAME = "ColorControl"; private static bool SystemShutdown = false; + private static bool EndSession = false; + private static bool UserExit = false; private static int SHORTCUTID_SCREENSAVER = -100; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); @@ -119,6 +121,14 @@ namespace ColorControl var converter = new ColorDataConverter(); _JsonDeserializer.RegisterConverters(new[] { converter }); + try + { + _nvService = new NvService(); + } + catch (Exception) + { + } + _nvPresetsFilename = Path.Combine(_dataDir, "NvPresets.json"); FillNvPresets(); @@ -140,13 +150,6 @@ namespace ColorControl _configFilename = Path.Combine(_dataDir, "Settings.json"); LoadConfig(); - try - { - _nvService = new NvService(); - } - catch (Exception) - { - } try { _lgService = new LgService(_dataDir, StartUpParams.RunningFromScheduledTask); @@ -268,6 +271,7 @@ namespace ColorControl void Exit(object sender, EventArgs e) { + UserExit = true; Close(); } @@ -329,6 +333,8 @@ namespace ColorControl } + UpdateDisplayInfoItems(); + foreach (var preset in _presets) { AddOrUpdateItem(preset); @@ -338,6 +344,79 @@ namespace ColorControl //UpdateTrayMenuNv(); } + private void UpdateDisplayInfoItems() + { + if (_nvService == null) + { + return; + } + + var displays = _nvService.GetDisplays(); + foreach (var display in displays) + { + var id = Math.Abs((int)display.Handle.MemoryAddress.ToInt64()); + + ListViewItem item = null; + for (var i = 0; i < lvNvPresets.Items.Count; i++) + { + item = lvNvPresets.Items[i]; + + if (item.Tag == null && item.ImageIndex == id) + { + break; + } + item = null; + } + + if (item == null) + { + item = lvNvPresets.Items.Add(display.Name); + item.ImageIndex = id; + item.Font = new Font(item.Font, item.Font.Style | FontStyle.Bold); + item.BackColor = Color.LightGray; + } + + var values = new List(); + + var name = display.Name; + + var screen = Screen.AllScreens.FirstOrDefault(x => x.DeviceName.Equals(name)); + if (screen != null) + { + name += " (" + screen.DeviceFriendlyName() + ")"; + } + + values.Add(name); + + var colorData = display.DisplayDevice.CurrentColorData; + var colorSettings = string.Format("{0}, {1}, {2}, {3}", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange, colorData.Colorimetry); + + values.Add(colorSettings); + + var refreshRate = display.DisplayDevice.CurrentTiming.Extra.RefreshRate; + + values.Add($"{refreshRate}Hz"); + + //values.Add(_nvService.GetDithering() ? "Yes" : "No"); + values.Add(string.Empty); + + values.Add(_nvService.IsHDREnabled() ? "Yes" : "No"); + + item.Text = values[0]; + for (var i = 1; i < values.Count; i++) + { + if (item.SubItems.Count - 1 >= i) + { + item.SubItems[i].Text = values[i]; + } + else + { + item.SubItems.Add(values[i]); + } + } + } + } + private void RegisterShortcut(int id, string shortcut, bool clear = false) { if (clear) @@ -392,6 +471,14 @@ namespace ColorControl private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { + if (!(SystemShutdown || EndSession || UserExit) && _config.MinimizeOnClose) + { + e.Cancel = true; + WindowState = FormWindowState.Minimized; + UserExit = false; + return; + } + SaveNvPresets(); SaveLgPresets(); @@ -430,14 +517,11 @@ namespace ColorControl File.WriteAllText(_lgPresetsFilename, json); } - private void button1_Click_1(object sender, EventArgs e) - { - - } - private void lvNvPresets_SelectedIndexChanged(object sender, EventArgs e) { - var enabled = lvNvPresets.SelectedItems.Count > 0; + var preset = GetSelectedNvPreset(); + var enabled = preset != null; + btnApply.Enabled = enabled; btnChange.Enabled = enabled; edtShortcut.Enabled = enabled; @@ -445,7 +529,6 @@ namespace ColorControl btnClone.Enabled = enabled; btnNvPresetDelete.Enabled = enabled; - var preset = GetSelectedNvPreset(); if (preset != null) { edtShortcut.Text = preset.shortcut; @@ -496,6 +579,10 @@ namespace ColorControl { SystemShutdown = true; } + else if (m.Msg == NativeConstants.WM_ENDSESSION) + { + EndSession = true; + } base.WndProc(ref m); } @@ -652,6 +739,7 @@ namespace ColorControl } chkStartMinimized.Checked = _config.StartMinimized; + chkMinimizeOnClose.Checked = _config.MinimizeOnClose; edtDelayDisplaySettings.Value = _config.DisplaySettingsDelay; edtBlankScreenSaverShortcut.Text = _config.ScreenSaverShortcut; @@ -820,6 +908,7 @@ namespace ColorControl miNvApply.Enabled = preset != null; mnuNvDisplay.Enabled = preset != null; miNvPresetColorSettings.Enabled = preset != null; + mnuNvPresetsColorSettings.Enabled = preset != null; mnuRefreshRate.Enabled = preset != null; miNvPresetDithering.Enabled = preset != null; miNvHDR.Enabled = preset != null; @@ -1511,6 +1600,8 @@ namespace ColorControl { throw new Exception("Error while applying NVIDIA preset"); } + + UpdateDisplayInfoItems(); } catch (Exception e) { @@ -1674,7 +1765,7 @@ See Options to test this functionality." ); } - this.BeginInvoke(new Action(() => + BeginInvoke(new Action(() => { _lgService.Config.PowerOnAfterStartup = clbLgPower.GetItemChecked(0); _lgService.Config.PowerOnAfterResume = clbLgPower.GetItemChecked(1); @@ -1742,5 +1833,15 @@ Do you want to continue?"; RegisterShortcut(SHORTCUTID_SCREENSAVER, shortcut, clear); } + + private void chkMinimizeOnClose_CheckedChanged(object sender, EventArgs e) + { + _config.MinimizeOnClose = chkMinimizeOnClose.Checked; + } + + private void MainForm_Activated(object sender, EventArgs e) + { + UpdateDisplayInfoItems(); + } } } \ No newline at end of file diff --git a/ColorControl/NvPreset.cs b/ColorControl/NvPreset.cs index aa64bbe..9fc09ce 100644 --- a/ColorControl/NvPreset.cs +++ b/ColorControl/NvPreset.cs @@ -69,7 +69,7 @@ namespace ColorControl public static string[] GetColumnNames() { //return new[] { "BPC", "Format", "Dynamic range", "Toggle HDR", "Shortcut" }; - return new[] { "Display|80", "Color settings (BPC, format, dyn. range, color space)|260", "Refresh rate|100", "Dithering", "HDR", "Shortcut" }; + return new[] { "Display|140", "Color settings (BPC, format, dyn. range, color space)|260", "Refresh rate|100", "Dithering", "HDR", "Shortcut" }; } public List GetDisplayValues() diff --git a/ColorControl/NvService.cs b/ColorControl/NvService.cs index e9b682d..ad9551e 100644 --- a/ColorControl/NvService.cs +++ b/ColorControl/NvService.cs @@ -1,9 +1,6 @@ using NvAPIWrapper; using NvAPIWrapper.Display; -using NvAPIWrapper.Native; using NvAPIWrapper.Native.Display; -using NvAPIWrapper.Native.Display.Structures; -using NvAPIWrapper.Native.Exceptions; using NvAPIWrapper.Native.GPU.Structures; using NWin32; using NWin32.NativeTypes; @@ -39,6 +36,20 @@ namespace ColorControl [In] uint mode ); + public delegate long NvAPI_Disp_GetDitherControl( + [In] PhysicalGPUHandle physicalGpu, + [Out] uint OutputId, + [In] [Out] IntPtr ditherControl); + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct NV_GPU_DITHER_CONTROL_V1 + { + public int version; + public uint state; + public uint bits; + public uint mode; + }; + private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); private Display _currentDisplay; @@ -149,6 +160,37 @@ namespace ColorControl } } + public bool GetDithering() + { + var ptr = NvAPI64_QueryInterface(0x932AC8FB); + if (ptr != IntPtr.Zero) + { + var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_GetDitherControl)) as NvAPI_Disp_GetDitherControl; + + var displayDevice = GetCurrentDisplay().DisplayDevice; + + var gpuHandle = displayDevice.PhysicalGPU.Handle; + var displayId = displayDevice.DisplayId; + + NV_GPU_DITHER_CONTROL_V1 info = new NV_GPU_DITHER_CONTROL_V1(); + info.version = 1; + IntPtr bla = Marshal.AllocHGlobal(Marshal.SizeOf(info)); + Marshal.StructureToPtr(info, bla, false); + + // Does not work yet...What is the exact interface of NvAPI_Disp_GetDitherControl? + + //var result = delegateValue(gpuHandle, displayId, bla); + //if (result != 0) + //{ + // Logger.Error($"Could not get dithering because NvAPI_Disp_GetDitherControl returned a non-zero return code: {result}"); + //} + + return info.state == 1; + } + + return false; + } + public bool SetRefreshRate(uint refreshRate) { var display = GetCurrentDisplay(); diff --git a/ColorControl/Properties/AssemblyInfo.cs b/ColorControl/Properties/AssemblyInfo.cs index 316cf9e..7f2619d 100644 --- a/ColorControl/Properties/AssemblyInfo.cs +++ b/ColorControl/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.1.0.0")] -[assembly: AssemblyFileVersion("1.1.0.0")] +[assembly: AssemblyVersion("1.2.0.0")] +[assembly: AssemblyFileVersion("1.2.0.0")] diff --git a/ColorControl/WOL.cs b/ColorControl/WOL.cs index e981f7e..97ae727 100644 --- a/ColorControl/WOL.cs +++ b/ColorControl/WOL.cs @@ -32,13 +32,13 @@ namespace ColorControl foreach (var device in devices) { - if (device is NpcapDevice npcapDevice) - { - if (!npcapDevice.Interface.GatewayAddresses.Any()) - { - continue; - } - } + //if (device is NpcapDevice npcapDevice) + //{ + // if (!npcapDevice.Interface.GatewayAddresses.Any()) + // { + // continue; + // } + //} Logger.Debug("Opening network device: " + device.Name); diff --git a/ColorControl/lgtv/LgTvApi.cs b/ColorControl/lgtv/LgTvApi.cs index cf17c4e..8b3aefc 100644 --- a/ColorControl/lgtv/LgTvApi.cs +++ b/ColorControl/lgtv/LgTvApi.cs @@ -21,6 +21,8 @@ namespace LgTv } public class LgTvApi:IDisposable { + public bool ConnectionClosed { get => _connection == null || _connection.ConnectionClosed; } + private readonly LgTvApiCore _connection; private LgWebOsMouseService _mouseConnection; @@ -61,7 +63,7 @@ namespace LgTv return null; } - public LgTvApi(string ip, LgTvApiCore connection, ClientKeyStore keyStore) + private LgTvApi(string ip, LgTvApiCore connection, ClientKeyStore keyStore) { webSocketUri = "ws://" + ip + ":3000"; _ip = ip; @@ -92,7 +94,6 @@ namespace LgTv public async Task Connect() { - var ctx = _connection.Connect(new Uri(webSocketUri)); return await ctx; } diff --git a/ColorControl/lgtv/LgTvConnection.cs b/ColorControl/lgtv/LgTvConnection.cs index c4485e3..ecfa61b 100644 --- a/ColorControl/lgtv/LgTvConnection.cs +++ b/ColorControl/lgtv/LgTvConnection.cs @@ -23,6 +23,8 @@ namespace LgTv public class LgTvApiCore : IDisposable { + public bool ConnectionClosed { get; private set; } + private MessageWebSocket _connection; private DataWriter _messageWriter; private int _commandCount; @@ -37,6 +39,7 @@ namespace LgTv { try { + ConnectionClosed = false; _commandCount = 0; _connection = new MessageWebSocket(); _connection.Control.MessageType = SocketMessageType.Utf8; @@ -66,11 +69,21 @@ namespace LgTv } } + public async void SendMessageAsync(string message) { - _messageWriter.WriteString(message); - await _messageWriter.StoreAsync(); + try + { + _messageWriter.WriteString(message); + await _messageWriter.StoreAsync(); + } + catch (Exception e) + { + ConnectionClosed = true; + _messageWriter?.Dispose(); + } } + public Task SendCommandAsync(string message) { var obj = JsonConvert.DeserializeObject(message); @@ -84,16 +97,18 @@ namespace LgTv var taskSource = new TaskCompletionSource(); _tokens.TryAdd(id, taskSource); SendMessageAsync(message); + if (ConnectionClosed) + { + throw new Exception("Connection closed"); + } return taskSource.Task; } catch (Exception e) { throw new SendMessageException("Can't send message", e); } - } - public Task SendCommandAsync(RequestMessage message) { var rawMessage = new RawRequestMessage(message, ++_commandCount); @@ -109,6 +124,7 @@ namespace LgTv { MessageWebSocket webSocket = Interlocked.Exchange(ref _connection, null); webSocket?.Dispose(); + ConnectionClosed = true; } private void Connection_MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args)