From 0c877623c08b0935cf2a95b63a4545773bff6a8c Mon Sep 17 00:00:00 2001 From: Vincent Maas Date: Sat, 3 Apr 2021 16:27:59 +0200 Subject: [PATCH] add screensaver trigger, focus main window when already started, fix some bugs --- ColorControl/ADLWrapper.cs | 5 +- ColorControl/App.config | 6 +- ColorControl/ColorControl.csproj | 36 +++--- ColorControl/Config.cs | 3 + ColorControl/LgService.cs | 191 +++++++++++++++++++++++++++- ColorControl/LgServiceConfig.cs | 2 + ColorControl/MainForm.Designer.cs | 23 +++- ColorControl/MainForm.cs | 146 ++++++++++----------- ColorControl/NvService.cs | 18 ++- ColorControl/Program.cs | 28 +++- ColorControl/UserSessionInfo.cs | 41 ++++++ ColorControl/Utils.cs | 49 ++++++- ColorControl/lgtv/LgTvApi.cs | 3 - ColorControl/lgtv/LgTvConnection.cs | 14 +- ColorControl/packages.config | 18 +-- 15 files changed, 449 insertions(+), 134 deletions(-) create mode 100644 ColorControl/UserSessionInfo.cs diff --git a/ColorControl/ADLWrapper.cs b/ColorControl/ADLWrapper.cs index f61cd6d..1853e6e 100644 --- a/ColorControl/ADLWrapper.cs +++ b/ColorControl/ADLWrapper.cs @@ -66,7 +66,7 @@ public static bool Initialize() { int ADLRet = -1; - Logger.Debug("Initialize"); + //Logger.Debug("Initialize"); var del = ADL.GetDelegate(); @@ -77,15 +77,12 @@ public static bool Initialize() CheckError(ADLRet, nameof(ADL.ADL_Main_Control_Create)); } - Logger.Debug("ADLRet1: " + ADLRet); - var del2 = ADL.GetDelegate(); if (del2 != null) { // Second parameter is 1: Get only the present adapters ADLRet = del2(ADL.ADL_Main_Memory_Alloc_Func, 1, ref context); - Logger.Debug("ADLRet2: " + ADLRet); CheckError(ADLRet, nameof(ADL.ADL2_Main_Control_Create)); } diff --git a/ColorControl/App.config b/ColorControl/App.config index f79ebd0..6dfbd73 100644 --- a/ColorControl/App.config +++ b/ColorControl/App.config @@ -15,7 +15,11 @@ - + + + + + diff --git a/ColorControl/ColorControl.csproj b/ColorControl/ColorControl.csproj index 06b6214..f5ac2c2 100644 --- a/ColorControl/ColorControl.csproj +++ b/ColorControl/ColorControl.csproj @@ -101,33 +101,33 @@ pngbarn.ico - - ..\packages\TaskScheduler.2.8.20\lib\net452\Microsoft.Win32.TaskScheduler.dll + + ..\packages\TaskScheduler.2.9.1\lib\net452\Microsoft.Win32.TaskScheduler.dll ..\packages\Native.0.1.0\lib\net35\Native.dll - - ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll - ..\packages\NLog.4.7.2\lib\net45\NLog.dll + ..\packages\NLog.4.7.9\lib\net45\NLog.dll - - ..\packages\NStandard.0.4.3\lib\net46\NStandard.dll + + ..\packages\NStandard.0.6.12.5\lib\net46\NStandard.dll - - ..\packages\NvAPIWrapper.Net.0.8.0.98\lib\net45\NvAPIWrapper.dll + + ..\packages\NvAPIWrapper.Net.0.8.1.101\lib\net45\NvAPIWrapper.dll - - ..\packages\NWin32.1.0.6\lib\net46\NWin32.dll + + ..\packages\NWin32.1.0.7.6\lib\net46\NWin32.dll - - ..\packages\PacketDotNet.1.0.5\lib\net45\PacketDotNet.dll + + ..\packages\PacketDotNet.1.2.0\lib\net45\PacketDotNet.dll - - ..\packages\SharpPcap.5.2.0\lib\netstandard2.0\SharpPcap.dll + + ..\packages\SharpPcap.5.4.0\lib\netstandard2.0\SharpPcap.dll @@ -137,6 +137,7 @@ + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll @@ -144,8 +145,8 @@ ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll @@ -175,6 +176,7 @@ + diff --git a/ColorControl/Config.cs b/ColorControl/Config.cs index dd66afe..3e60a10 100644 --- a/ColorControl/Config.cs +++ b/ColorControl/Config.cs @@ -6,6 +6,8 @@ class Config public bool MinimizeOnClose { get; set; } + public bool MinimizeToTray { get; set; } + public int DisplaySettingsDelay { get; set; } public string ScreenSaverShortcut { get; set; } @@ -23,6 +25,7 @@ public Config() ScreenSaverShortcut = string.Empty; FormWidth = 900; FormHeight = 600; + MinimizeToTray = true; } } } diff --git a/ColorControl/LgService.cs b/ColorControl/LgService.cs index 3709ad5..811dbdb 100644 --- a/ColorControl/LgService.cs +++ b/ColorControl/LgService.cs @@ -1,7 +1,10 @@ using LgTv; +using Microsoft.Win32; using Newtonsoft.Json; +using NWin32; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -50,6 +53,11 @@ public PnpDev SelectedDevice private Dictionary> _invokableActions = new Dictionary>(); + private bool _poweredOffByScreenSaver; + private int _poweredOffByScreenSaverProcessId; + private Task _monitorTask; + private int _monitorTaskCounter; + public LgService(string dataDir, bool allowPowerOn) { _dataDir = dataDir; @@ -318,7 +326,7 @@ private async Task Connected(bool reconnect = false) return false; } - if (reconnect || _lgTvApi == null || _lgTvApi.ConnectionClosed || !string.Equals(_lgTvApi.GetIpAddress(), SelectedDevice.IpAddress)) + if (reconnect || !IsConnected() || !string.Equals(_lgTvApi.GetIpAddress(), SelectedDevice.IpAddress)) { if (!await ConnectToSelectedDevice()) { @@ -328,7 +336,11 @@ private async Task Connected(bool reconnect = false) } return true; } - + + private bool IsConnected() + { + return !_lgTvApi?.ConnectionClosed ?? false; + } public async Task ApplyPreset(LgPreset preset, bool reconnect = false) { @@ -523,8 +535,14 @@ internal void WakeAfterResume() } } - private async Task WakeAndConnectToSelectedDeviceWithRetries() + private async Task WakeAndConnectToSelectedDeviceWithRetries(bool checkUserSession = true) { + if (checkUserSession && !UserSessionInfo.UserLocalSession) + { + Logger.Debug($"WakeAndConnectToSelectedDeviceWithRetries: not waking because session info indicates no local session"); + return false; + } + var wakeDelay = 0; var maxRetries = Config.PowerOnRetries <= 1 ? 5 : Config.PowerOnRetries; @@ -581,5 +599,172 @@ private async Task WakeAndConnectToSelectedDevice(int wakeDelay = 5000, in return false; } } + + public void InstallEventHandlers() + { + SystemEvents.PowerModeChanged -= PowerModeChanged; + SystemEvents.PowerModeChanged += PowerModeChanged; + //SystemEvents.SessionEnded -= SessionEnded; + //SystemEvents.SessionEnded += SessionEnded; + + UserSessionInfo.UserSessionSwitch -= SessionSwitched; + UserSessionInfo.UserSessionSwitch += SessionSwitched; + + MonitorProcesses(); + } + + private void SessionSwitched(bool toLocal) + { + if (toLocal) + { + if (_poweredOffByScreenSaver) + { + Logger.Debug("User switched to local and screen was powered off due to screensaver: waking up"); + _poweredOffByScreenSaver = false; + _poweredOffByScreenSaverProcessId = 0; + var _ = WakeAndConnectToSelectedDeviceWithRetries(); + } + else + { + Logger.Debug("User switched to local but screen was not powered off by screensaver: NOT waking up"); + } + } + } + + private void PowerModeChanged(object sender, PowerModeChangedEventArgs e) + { + var powerOn = e.Mode == PowerModes.Resume; + + Logger.Debug($"PowerModeChanged: {e.Mode}"); + + if (powerOn) + { + DisposeConnection(); + WakeAfterResume(); + return; + } + + if (Config.PowerOffOnStandby) + { + NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS | NativeConstants.ES_SYSTEM_REQUIRED | NativeConstants.ES_AWAYMODE_REQUIRED); + try + { + Logger.Debug("Powering off tv..."); + var task = PowerOff(); + Utils.WaitForTask(task); + Logger.Debug("Done powering off tv"); + } + finally + { + NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS); + } + } + } + + private void SessionEnded(object sender, SessionEndedEventArgs e) + { + //if (e.Reason == SessionEndReasons.SystemShutdown) + //{ + // Logger.Debug($"SessionEnded: {e.Reason}"); + + // if (Config.PowerOffOnShutdown) + // { + // PowerOff(); + // } + //} + } + + private void MonitorProcesses() + { + if (Config.PowerSwitchOnScreenSaver && _monitorTask == null) + { + _monitorTask = CheckProcesses(); + } + else if (!Config.PowerSwitchOnScreenSaver && _monitorTask != null) + { + _monitorTask = null; + } + } + + public async Task CheckProcesses() + { + var wasConnected = IsConnected(); + _monitorTaskCounter++; + var validCounter = _monitorTaskCounter; + while (Config.PowerSwitchOnScreenSaver && validCounter == _monitorTaskCounter) + { + await Task.Delay(500); + + if (_poweredOffByScreenSaver && !UserSessionInfo.UserLocalSession) + { + continue; + } + + var processes = Process.GetProcesses(); + + if (_poweredOffByScreenSaver) + { + if (processes.Any(p => p.Id == _poweredOffByScreenSaverProcessId)) + { + continue; + } + + Logger.Debug("Screensaver stopped, waking"); + _poweredOffByScreenSaver = false; + _poweredOffByScreenSaverProcessId = 0; + var _ = WakeAndConnectToSelectedDeviceWithRetries(); + + continue; + } + + if (!IsConnected()) + { + if (wasConnected) + { + Logger.Debug("TV was connected, but not any longer"); + wasConnected = false; + } + continue; + } + + if (!wasConnected) + { + Logger.Debug("TV was not connected, but connection has now been established"); + wasConnected = true; + } + + foreach (var process in processes) + { + var name = process.ProcessName.ToLowerInvariant(); + if (name.EndsWith(".scr")) + { + var parent = process.Parent(); + if (parent?.ProcessName.Contains("winlogon") ?? false) + { + Logger.Debug($"Screensaver started: {process.ProcessName}, parent: {parent.ProcessName}"); + try + { + Logger.Debug("Powering off tv because of screensaver"); + _poweredOffByScreenSaver = await PowerOff(); + _poweredOffByScreenSaverProcessId = process.Id; + } + catch (Exception e) + { + Logger.Error("CheckProcesses: can't power off: " + e.ToLogString()); + } + if (!IsConnected()) + { + _poweredOffByScreenSaver = false; + } + break; + } + else + { + Logger.Debug($"Screensaver started: {process.ProcessName}, but invalid parent: {parent?.ProcessName ?? "no parent"}"); + } + } + } + } + } } } diff --git a/ColorControl/LgServiceConfig.cs b/ColorControl/LgServiceConfig.cs index 3a00d18..1a7ad36 100644 --- a/ColorControl/LgServiceConfig.cs +++ b/ColorControl/LgServiceConfig.cs @@ -10,6 +10,8 @@ class LgServiceConfig public bool PowerOffOnStandby { get; set; } + public bool PowerSwitchOnScreenSaver { get; set; } + public int PowerOnDelayAfterResume { get; set; } public int PowerOnRetries { get; set; } diff --git a/ColorControl/MainForm.Designer.cs b/ColorControl/MainForm.Designer.cs index b200ca7..c900294 100644 --- a/ColorControl/MainForm.Designer.cs +++ b/ColorControl/MainForm.Designer.cs @@ -148,6 +148,7 @@ private void InitializeComponent() this.edtDelayDisplaySettings = new System.Windows.Forms.NumericUpDown(); this.label6 = new System.Windows.Forms.Label(); this.grpGeneralOptions = new System.Windows.Forms.GroupBox(); + this.chkMinimizeToSystemTray = new System.Windows.Forms.CheckBox(); this.chkMinimizeOnClose = new System.Windows.Forms.CheckBox(); this.chkStartMinimized = new System.Windows.Forms.CheckBox(); this.chkStartAfterLogin = new System.Windows.Forms.CheckBox(); @@ -906,10 +907,11 @@ private void InitializeComponent() "Automatically power off on shutdown. Because this app cannot detect a restart, re" + "starting could also trigger this. Hold down Ctrl on restart to prevent power off" + ".", - "Automatically power off on standby"}); + "Automatically power off on standby", + "Automatically power off on screensaver and power on when screensaver deactivates"}); this.clbLgPower.Location = new System.Drawing.Point(6, 33); this.clbLgPower.Name = "clbLgPower"; - this.clbLgPower.Size = new System.Drawing.Size(829, 64); + this.clbLgPower.Size = new System.Drawing.Size(829, 79); this.clbLgPower.TabIndex = 34; this.clbLgPower.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.clbLgPower_ItemCheck); // @@ -1154,10 +1156,10 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Right))); this.lvLgPresets.FullRowSelect = true; this.lvLgPresets.HideSelection = false; - this.lvLgPresets.Location = new System.Drawing.Point(6, 103); + this.lvLgPresets.Location = new System.Drawing.Point(6, 116); this.lvLgPresets.MultiSelect = false; this.lvLgPresets.Name = "lvLgPresets"; - this.lvLgPresets.Size = new System.Drawing.Size(829, 185); + this.lvLgPresets.Size = new System.Drawing.Size(829, 172); this.lvLgPresets.TabIndex = 8; this.lvLgPresets.UseCompatibleStateImageBehavior = false; this.lvLgPresets.View = System.Windows.Forms.View.Details; @@ -1435,6 +1437,7 @@ private void InitializeComponent() // // grpGeneralOptions // + this.grpGeneralOptions.Controls.Add(this.chkMinimizeToSystemTray); this.grpGeneralOptions.Controls.Add(this.chkMinimizeOnClose); this.grpGeneralOptions.Controls.Add(this.chkStartMinimized); this.grpGeneralOptions.Controls.Add(this.chkStartAfterLogin); @@ -1445,6 +1448,17 @@ private void InitializeComponent() this.grpGeneralOptions.TabStop = false; this.grpGeneralOptions.Text = "General"; // + // chkMinimizeToSystemTray + // + this.chkMinimizeToSystemTray.AutoSize = true; + this.chkMinimizeToSystemTray.Location = new System.Drawing.Point(204, 19); + this.chkMinimizeToSystemTray.Name = "chkMinimizeToSystemTray"; + this.chkMinimizeToSystemTray.Size = new System.Drawing.Size(133, 17); + this.chkMinimizeToSystemTray.TabIndex = 5; + this.chkMinimizeToSystemTray.Text = "Minimize to system tray"; + this.chkMinimizeToSystemTray.UseVisualStyleBackColor = true; + this.chkMinimizeToSystemTray.CheckedChanged += new System.EventHandler(this.chkMinimizeToSystemTray_CheckedChanged); + // // chkMinimizeOnClose // this.chkMinimizeOnClose.AutoSize = true; @@ -1783,6 +1797,7 @@ private void InitializeComponent() private System.Windows.Forms.Button btnApplyAmd; private System.Windows.Forms.ListView lvAmdPresets; private System.Windows.Forms.Button btnAddAmd; + private System.Windows.Forms.CheckBox chkMinimizeToSystemTray; } } diff --git a/ColorControl/MainForm.cs b/ColorControl/MainForm.cs index a2a3263..d9bcb0f 100644 --- a/ColorControl/MainForm.cs +++ b/ColorControl/MainForm.cs @@ -1,6 +1,5 @@ using ATI.ADL; using LgTv; -using Microsoft.Win32; using NLog; using NStandard; using NvAPIWrapper.Display; @@ -16,8 +15,6 @@ using System.IO; using System.Linq; using System.Reflection; -// 1. Import the InteropServices type -using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Web.Script.Serialization; using System.Windows.Forms; @@ -26,13 +23,6 @@ namespace ColorControl { public partial class MainForm : Form { - [DllImport("user32.dll")] - public extern static bool ShutdownBlockReasonCreate(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string pwszReason); - - [DllImport("user32.dll")] - public extern static bool ShutdownBlockReasonDestroy(IntPtr hWnd); - - private static int WM_HOTKEY = 0x0312; private static string TS_TASKNAME = "ColorControl"; private static bool SystemShutdown = false; private static bool EndSession = false; @@ -48,7 +38,7 @@ public partial class MainForm : Form private JavaScriptSerializer _JsonDeserializer = new JavaScriptSerializer(); private string _lastDisplayRefreshRates = string.Empty; - private NotifyIcon trayIcon; + private NotifyIcon _trayIcon; private bool _initialized = false; private string _configFilename; private Config _config; @@ -74,26 +64,29 @@ public MainForm(StartUpParams startUpParams) InitLogger(); + _configFilename = Path.Combine(_dataDir, "Settings.json"); + LoadConfig(); + MessageForms.MainForm = this; _nvTrayMenu = new MenuItem("NVIDIA presets"); _lgTrayMenu = new MenuItem("LG presets"); - trayIcon = new NotifyIcon() + _trayIcon = new NotifyIcon() { Icon = Icon, ContextMenu = new ContextMenu(new MenuItem[] { _nvTrayMenu, _lgTrayMenu, - new MenuItem("-", OpenForm), + new MenuItem("-"), new MenuItem("Open", OpenForm), new MenuItem("-"), new MenuItem("Exit", Exit) }), - Visible = true, + Visible = _config.MinimizeToTray, Text = Text }; - trayIcon.MouseDoubleClick += trayIcon_MouseDoubleClick; - trayIcon.ContextMenu.Popup += trayIconContextMenu_Popup; + _trayIcon.MouseDoubleClick += trayIcon_MouseDoubleClick; + _trayIcon.ContextMenu.Popup += trayIconContextMenu_Popup; chkStartAfterLogin.Checked = Utils.TaskExists(TS_TASKNAME, true); @@ -103,15 +96,14 @@ public MainForm(StartUpParams startUpParams) chkFixChromeFonts.Checked = Utils.IsChromeFixInstalled(); } - _configFilename = Path.Combine(_dataDir, "Settings.json"); - LoadConfig(); - InitNvService(); InitAmdService(); InitLgService(); InitInfo(); + UserSessionInfo.Install(); + _restartDetector = new RestartDetector(); //Scale(new SizeF(1.25F, 1.25F)); @@ -130,7 +122,8 @@ private void InitNvService() } catch (Exception e) { - Logger.Error("Error initializing NvService: " + e.ToLogString()); + //Logger.Error("Error initializing NvService: " + e.ToLogString()); + Logger.Debug("No NVIDIA device detected"); tcMain.TabPages.Remove(tabNVIDIA); } } @@ -140,12 +133,12 @@ private void InitAmdService() try { _amdService = new AmdService(_dataDir); - //lblErrorAMD.Text = "If you see this message, it means you have AMD graphics drivers installed. Unfortunately, this feature is not completed yet. If I get an AMD card I might finish it."; FillAmdPresets(); } catch (Exception e) { - Logger.Error("Error initializing AmdService: " + e.ToLogString()); + //Logger.Error("Error initializing AmdService: " + e.ToLogString()); + Logger.Debug("No AMD device detected"); tcMain.TabPages.Remove(tabAMD); } } @@ -162,6 +155,7 @@ private void InitLgService() clbLgPower.SetItemChecked(1, _lgService.Config.PowerOnAfterResume); clbLgPower.SetItemChecked(2, _lgService.Config.PowerOffOnShutdown); clbLgPower.SetItemChecked(3, _lgService.Config.PowerOffOnStandby); + clbLgPower.SetItemChecked(4, _lgService.Config.PowerSwitchOnScreenSaver); edtLgMaxPowerOnRetries.Value = _lgService.Config.PowerOnRetries; edtLgDeviceFilter.Text = _lgService.Config.DeviceSearchKey; chkLgOldWolMechanism.Checked = _lgService.Config.UseOldNpcapWol; @@ -188,9 +182,7 @@ private void InitLgService() item.Click += miLgAddAction_Click; } - - SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(PowerModeChanged); - //SystemEvents.SessionEnded += new SessionEndedEventHandler(SessionEnded); + _lgService.InstallEventHandlers(); } catch (Exception e) { @@ -198,49 +190,6 @@ private void InitLgService() } } - private void PowerModeChanged(object sender, PowerModeChangedEventArgs e) - { - var powerOn = e.Mode == PowerModes.Resume; - - Logger.Debug($"PowerModeChanged: {e.Mode}"); - - if (powerOn) - { - _lgService.DisposeConnection(); - _lgService.WakeAfterResume(); - return; - } - - if (_lgService.Config.PowerOffOnStandby) - { - NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS | NativeConstants.ES_SYSTEM_REQUIRED | NativeConstants.ES_AWAYMODE_REQUIRED); - try - { - Logger.Debug("Powering off tv..."); - var task = _lgService.PowerOff(); - Utils.WaitForTask(task); - Logger.Debug("Done powering off tv"); - } - finally - { - NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS); - } - } - } - - private void SessionEnded(object sender, SessionEndedEventArgs e) - { - //if (e.Reason == SessionEndReasons.SystemShutdown) - //{ - // Logger.Debug($"SessionEnded: {e.Reason}"); - - // if (_lgService.Config.PowerOffOnShutdown) - // { - // _lgService.PowerOff(); - // } - //} - } - private void InitLogger() { var config = new NLog.Config.LoggingConfiguration(); @@ -284,6 +233,7 @@ private void OpenForm(object sender, EventArgs e) { Show(); WindowState = FormWindowState.Normal; + Activate(); } void Exit(object sender, EventArgs e) @@ -377,7 +327,7 @@ private void UpdateDisplayInfoItems() text += "\n" + displayInfo.InfoLine; } - Utils.SetNotifyIconText(trayIcon, text); + Utils.SetNotifyIconText(_trayIcon, text); } private void UpdateDisplayInfoItemsAmd() @@ -433,7 +383,7 @@ private void UpdateDisplayInfoItemsAmd() text += "\n" + displayInfo.InfoLine; } - Utils.SetNotifyIconText(trayIcon, text); + Utils.SetNotifyIconText(_trayIcon, text); } private void AddOrUpdateItem(NvPreset preset = null) @@ -520,7 +470,7 @@ private void lvNvPresets_SelectedIndexChanged(object sender, EventArgs e) protected override void WndProc(ref Message m) { // 5. Catch when a HotKey is pressed ! - if (m.Msg == WM_HOTKEY && !edtShortcut.Focused && !edtShortcutLg.Focused && !edtAmdShortcut.Focused && !edtBlankScreenSaverShortcut.Focused) + if (m.Msg == NativeConstants.WM_HOTKEY && !edtShortcut.Focused && !edtShortcutLg.Focused && !edtAmdShortcut.Focused && !edtBlankScreenSaverShortcut.Focused) { int id = m.WParam.ToInt32(); @@ -559,6 +509,11 @@ protected override void WndProc(ref Message m) { EndSession = true; } + else if (m.Msg == Utils.WM_BRINGTOFRONT) + { + Logger.Debug("WM_BRINGTOFRONT message received, opening form"); + OpenForm(this, EventArgs.Empty); + } base.WndProc(ref m); } @@ -592,14 +547,17 @@ private void btnSetShortcut_Click(object sender, EventArgs e) private void MainForm_FormClosed(object sender, FormClosedEventArgs e) { // Hide tray icon, otherwise it will remain shown until user mouses over it - trayIcon.Visible = false; + _trayIcon.Visible = false; } private void MainForm_Resize(object sender, EventArgs e) { if (WindowState == FormWindowState.Minimized) { - Hide(); + if (_config.MinimizeToTray) + { + Hide(); + } } else if (WindowState == FormWindowState.Normal && _config != null) { @@ -632,6 +590,7 @@ private void LoadConfig() chkStartMinimized.Checked = _config.StartMinimized; chkMinimizeOnClose.Checked = _config.MinimizeOnClose; + chkMinimizeToSystemTray.Checked = _config.MinimizeToTray; edtDelayDisplaySettings.Value = _config.DisplaySettingsDelay; edtBlankScreenSaverShortcut.Text = _config.ScreenSaverShortcut; @@ -707,7 +666,14 @@ protected override void SetVisibleCore(bool value) if (!_setVisibleCalled && _config.StartMinimized) { _setVisibleCalled = true; - value = false; + if (_config.MinimizeToTray) + { + value = false; + } + else + { + WindowState = FormWindowState.Minimized; + } } if (!IsDisposed) { base.SetVisibleCore(value); @@ -1256,18 +1222,22 @@ private void tabControl1_SelectedIndexChanged(object sender, EventArgs e) grpNvidiaOptions.Visible = _nvService != null; if (grpNvidiaOptions.Visible) { - if (cbxDitheringBitDepth.Items.Count == 0) + var firstTime = cbxDitheringBitDepth.Items.Count == 0; + + if (firstTime) { cbxDitheringBitDepth.Items.AddRange(Utils.GetDescriptions().ToArray()); cbxDitheringMode.Items.AddRange(Utils.GetDescriptions().ToArray()); } - var preset = _nvService.GetLastAppliedPreset() ?? GetSelectedNvPreset(); - chkDitheringEnabled.Checked = preset?.ditheringEnabled ?? true; - cbxDitheringBitDepth.SelectedIndex = (int)(preset?.ditheringBits ?? (int)NvDitherBits.Bits8); - cbxDitheringMode.SelectedIndex = (int)(preset?.ditheringMode ?? (int)NvDitherMode.Temporal); - FillGradient(); + if (firstTime || preset != null) + { + chkDitheringEnabled.Checked = preset?.ditheringEnabled ?? true; + cbxDitheringBitDepth.SelectedIndex = (int)(preset?.ditheringBits ?? (int)NvDitherBits.Bits8); + cbxDitheringMode.SelectedIndex = (int)(preset?.ditheringMode ?? (int)NvDitherMode.Temporal); + FillGradient(); + } } } } @@ -1630,12 +1600,12 @@ private void clbLgPower_ItemCheck(object sender, ItemCheckEventArgs e) return; } - if (!(_lgService.Config.PowerOnAfterResume || _lgService.Config.PowerOnAfterStartup)) + if (!(_lgService.Config.PowerOnAfterResume || _lgService.Config.PowerOnAfterStartup || _lgService.Config.PowerSwitchOnScreenSaver)) { MessageForms.InfoOk( @"Be sure to activate the following setting on the TV, or the app will not be able to wake the TV: -Connection > Mobile TV On > Turn on via Wi-Fi +Connection > Mobile TV On > Turn on via Wi-Fi (Networked Standby Mode) See Options to test this functionality." ); @@ -1647,6 +1617,9 @@ See Options to test this functionality." _lgService.Config.PowerOnAfterResume = clbLgPower.GetItemChecked(1); _lgService.Config.PowerOffOnShutdown = clbLgPower.GetItemChecked(2); _lgService.Config.PowerOffOnStandby = clbLgPower.GetItemChecked(3); + _lgService.Config.PowerSwitchOnScreenSaver = clbLgPower.GetItemChecked(4); + + _lgService.InstallEventHandlers(); })); } @@ -2218,5 +2191,14 @@ private void btnAddAmd_Click(object sender, EventArgs e) MessageForms.InfoOk("All presets for every color setting already exist."); } } + + private void chkMinimizeToSystemTray_CheckedChanged(object sender, EventArgs e) + { + if (_initialized) + { + _config.MinimizeToTray = chkMinimizeToSystemTray.Checked; + _trayIcon.Visible = _config.MinimizeToTray; + } + } } } \ No newline at end of file diff --git a/ColorControl/NvService.cs b/ColorControl/NvService.cs index 7468eca..a1482f1 100644 --- a/ColorControl/NvService.cs +++ b/ColorControl/NvService.cs @@ -227,11 +227,24 @@ public bool ApplyPreset(NvPreset preset, Config config) return result; } + private ColorData GetCurrentColorData(Display display) + { + try + { + return display.DisplayDevice.CurrentColorData; + } + catch (Exception e) + { + Logger.Error("Error while reading current color data: " + e.Message); + return new ColorData(); + } + } + private bool ColorDataDiffers(ColorData colorData) { var display = GetCurrentDisplay(); - var currentColorData = display.DisplayDevice.CurrentColorData; + var currentColorData = GetCurrentColorData(display); var settingRange = colorData.DynamicRange == ColorDataDynamicRange.Auto ? colorData.ColorFormat == ColorDataFormat.RGB ? ColorDataDynamicRange.VESA : ColorDataDynamicRange.CEA : colorData.DynamicRange; @@ -401,7 +414,8 @@ public List GetDisplayInfos() values.Add(name); - var colorData = display.DisplayDevice.CurrentColorData; + var colorData = GetCurrentColorData(display); + var colorSettings = string.Format("{0}, {1}, {2}, {3}", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange, colorData.Colorimetry); values.Add(colorSettings); diff --git a/ColorControl/Program.cs b/ColorControl/Program.cs index 3add68a..820f5dd 100644 --- a/ColorControl/Program.cs +++ b/ColorControl/Program.cs @@ -1,4 +1,7 @@ -using System; +using NWin32; +using System; +using System.Diagnostics; +using System.Text; using System.Threading; using System.Windows.Forms; @@ -33,6 +36,22 @@ static void Main(string[] args) { if (!mutexCreated) { + var currentProcessId = Process.GetCurrentProcess().Id; + foreach (var process in Process.GetProcesses()) + { + if (process.ProcessName.Equals("ColorControl") && process.Id != currentProcessId) + { + if (process.Threads.Count > 0) + { + var thread = process.Threads[0]; + + NativeMethods.EnumThreadWindows((uint)thread.Id, EnumThreadWindows, IntPtr.Zero); + } + + return; + } + } + MessageBox.Show("Only one instance of this program can be active.", "ColorControl"); } else @@ -58,6 +77,13 @@ static void Main(string[] args) } } + public static int EnumThreadWindows(IntPtr handle, IntPtr param) + { + NativeMethods.SendMessageW(handle, Utils.WM_BRINGTOFRONT, UIntPtr.Zero, IntPtr.Zero); + + return 1; + } + private static void GlobalUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) { var ex = (Exception)e.ExceptionObject; diff --git a/ColorControl/UserSessionInfo.cs b/ColorControl/UserSessionInfo.cs new file mode 100644 index 0000000..e756f8b --- /dev/null +++ b/ColorControl/UserSessionInfo.cs @@ -0,0 +1,41 @@ +using Microsoft.Win32; + +namespace ColorControl +{ + static class UserSessionInfo + { + private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); + + public static bool UserLocalSession { get; private set; } = true; + public static SessionSwitchReason LastSessionSwitchReason { get; private set; } = 0; + + public delegate void UserSessionSwitchHandler(bool toLocal); + public static event UserSessionSwitchHandler UserSessionSwitch; + + public static void Install() + { + SystemEvents.SessionSwitch += SessionSwitchHandler; + } + + public static void SessionSwitchHandler(object sender, SessionSwitchEventArgs evt) + { + if (evt.Reason == SessionSwitchReason.ConsoleDisconnect) + { + Logger.Debug("Detected a disconnect from the console"); + UserLocalSession = false; + UserSessionSwitch(false); + } + else if (evt.Reason == SessionSwitchReason.ConsoleConnect) + { + Logger.Debug("Detected a connect to the console"); + if (!UserLocalSession) + { + Logger.Debug("Session state switched to local"); + UserLocalSession = true; + UserSessionSwitch(true); + } + } + LastSessionSwitchReason = evt.Reason; + } + } +} diff --git a/ColorControl/Utils.cs b/ColorControl/Utils.cs index 06a8486..b345bb3 100644 --- a/ColorControl/Utils.cs +++ b/ColorControl/Utils.cs @@ -1,7 +1,7 @@ using Microsoft.Win32; using Microsoft.Win32.TaskScheduler; using NStandard; -using NvAPIWrapper.Display; +using NWin32; using System; using System.Collections.Generic; using System.ComponentModel; @@ -33,14 +33,30 @@ public enum ModKeys : int Win = 8 } + public const int WM_BRINGTOFRONT = NativeConstants.WM_USER + 1; + public static string PKEY_PNPX_IpAddress = "{656a3bb3-ecc0-43fd-8477-4ae0404a96cd} 12297"; public static string PKEY_PNPX_PhysicalAddress = "{656a3bb3-ecc0-43fd-8477-4ae0404a96cd} 12294"; + public delegate void PCREATE_PROCESS_NOTIFY_ROUTINE(IntPtr ParentId, IntPtr ProcessId, bool Create); + [DllImport("user32.dll")] public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc); [DllImport("user32.dll")] public static extern bool UnregisterHotKey(IntPtr hWnd, int id); + [DllImport("user32.dll")] + public extern static bool ShutdownBlockReasonCreate(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string pwszReason); + + [DllImport("user32.dll")] + public extern static bool ShutdownBlockReasonDestroy(IntPtr hWnd); + + //[DllImport("Wtsapi32.dll")] + //public extern static bool WTSRegisterSessionNotification(IntPtr hWnd, uint dwFlags); + + //[DllImport("Wtsapi32.dll")] + //public extern static bool WTSUnRegisterSessionNotification(IntPtr hWnd); + private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); private static bool WinKeyDown = false; @@ -602,7 +618,38 @@ public static void AddOrUpdateListItem(ListView listView, List presets, Co } } } + } + public static class ProcessExtensions + { + private static string FindIndexedProcessName(int pid) + { + var processName = Process.GetProcessById(pid).ProcessName; + var processesByName = Process.GetProcessesByName(processName); + string processIndexdName = null; + for (var index = 0; index < processesByName.Length; index++) + { + processIndexdName = index == 0 ? processName : processName + "#" + index; + var processId = new PerformanceCounter("Process", "ID Process", processIndexdName); + if ((int)processId.NextValue() == pid) + { + return processIndexdName; + } + } + + return processIndexdName; + } + + private static Process FindPidFromIndexedProcessName(string indexedProcessName) + { + var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName); + return Process.GetProcessById((int)parentId.NextValue()); + } + + public static Process Parent(this Process process) + { + return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id)); + } } } diff --git a/ColorControl/lgtv/LgTvApi.cs b/ColorControl/lgtv/LgTvApi.cs index f59515a..00453e4 100644 --- a/ColorControl/lgtv/LgTvApi.cs +++ b/ColorControl/lgtv/LgTvApi.cs @@ -1,13 +1,10 @@ using ColorControl; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; -using Windows.Foundation; -using Windows.Storage; namespace LgTv { diff --git a/ColorControl/lgtv/LgTvConnection.cs b/ColorControl/lgtv/LgTvConnection.cs index 46f8ff2..63b9d01 100644 --- a/ColorControl/lgtv/LgTvConnection.cs +++ b/ColorControl/lgtv/LgTvConnection.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; @@ -9,7 +8,6 @@ using Windows.Web; using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Linq; -using Windows.Foundation; namespace LgTv { @@ -32,8 +30,7 @@ public class LgTvApiCore : IDisposable public event IsConnectedDelegate IsConnected; private readonly ConcurrentDictionary> _tokens = new ConcurrentDictionary>(); - private static void Log(RawRequestMessage message) { } - + private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); public async Task Connect(Uri uri, bool ignoreReceiver = false) { @@ -67,7 +64,6 @@ public async Task Connect(Uri uri, bool ignoreReceiver = false) } return false; } - } public async void SendMessageAsync(string message) @@ -77,8 +73,9 @@ public async void SendMessageAsync(string message) _messageWriter.WriteString(message); await _messageWriter.StoreAsync(); } - catch (Exception) + catch (Exception e) { + Logger.Error("SendMessageAsync: " + e.Message); ConnectionClosed = true; _messageWriter?.Dispose(); } @@ -167,7 +164,10 @@ private void Connection_MessageReceived(MessageWebSocket sender, MessageWebSocke } catch (Exception ex) { - WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult); + var status = WebSocketError.GetStatus(ex.GetBaseException().HResult); + Logger.Error($"Connection_MessageReceived: status: {status}, exception: {ex.Message}"); + ConnectionClosed = true; + _messageWriter?.Dispose(); } } public void Close() diff --git a/ColorControl/packages.config b/ColorControl/packages.config index 3f23c22..22dd691 100644 --- a/ColorControl/packages.config +++ b/ColorControl/packages.config @@ -1,16 +1,16 @@  - - - - - - - + + + + + + + - - + + \ No newline at end of file