From 26a91dc7e9a6cfc92bfa3f5e5b37c56b001e884b Mon Sep 17 00:00:00 2001 From: Vincent Maas Date: Sun, 2 May 2021 17:38:38 +0200 Subject: [PATCH] manually add device, add device setting per preset, add remote control image --- ColorControl/ColorControl.csproj | 25 +- ColorControl/LgDevice.cs | 358 ++++++++ ColorControl/LgPreset.cs | 36 +- ColorControl/LgService.cs | 530 ++++------- ColorControl/LgServiceConfig.cs | 17 +- ColorControl/MainForm.Designer.cs | 837 ++++++++++-------- ColorControl/MainForm.cs | 248 +++++- ColorControl/MainForm.resx | 3 + ColorControl/MessageForms.cs | 91 +- ColorControl/PnpDev.cs | 22 +- ColorControl/PresetBase.cs | 21 + ColorControl/Program.cs | 2 +- ColorControl/Properties/AssemblyInfo.cs | 4 +- ColorControl/Properties/Resources.Designer.cs | 81 +- ColorControl/Properties/Resources.resx | 20 +- ColorControl/RemoteControlForm.Designer.cs | 64 +- ColorControl/RemoteControlForm.cs | 35 +- ColorControl/RemoteControlForm.resx | 6 + ColorControl/RemoteControlPanel.Designer.cs | 81 ++ ColorControl/RemoteControlPanel.cs | 174 ++++ ColorControl/RemoteControlPanel.resx | 120 +++ ColorControl/Resources/LG_remote.jpg | Bin 0 -> 94771 bytes ColorControl/Resources/RC_coords.txt | 37 + ColorControl/Utils.cs | 33 +- ColorControl/lgtv/LgTvApi.cs | 8 +- ColorControl/lgtv/LgTvConnection.cs | 26 +- ColorControl/lgtv/LgWebOsMouseService.cs | 10 +- 27 files changed, 2027 insertions(+), 862 deletions(-) create mode 100644 ColorControl/LgDevice.cs create mode 100644 ColorControl/RemoteControlPanel.Designer.cs create mode 100644 ColorControl/RemoteControlPanel.cs create mode 100644 ColorControl/RemoteControlPanel.resx create mode 100644 ColorControl/Resources/LG_remote.jpg create mode 100644 ColorControl/Resources/RC_coords.txt diff --git a/ColorControl/ColorControl.csproj b/ColorControl/ColorControl.csproj index 2331356..fa24a7a 100644 --- a/ColorControl/ColorControl.csproj +++ b/ColorControl/ColorControl.csproj @@ -27,7 +27,7 @@ ColorControl Maassoft 0 - 2.2.0.0 + 3.0.0.0 false true true @@ -177,6 +177,18 @@ + + + True + True + Resources.resx + + + UserControl + + + RemoteControlPanel.cs + @@ -226,21 +238,22 @@ + + MainForm.cs ResXFileCodeGenerator - Resources.Designer.cs Designer + Resources.Designer.cs - - True - Resources.resx - RemoteControlForm.cs + + RemoteControlPanel.cs + PreserveNewest diff --git a/ColorControl/LgDevice.cs b/ColorControl/LgDevice.cs new file mode 100644 index 0000000..1dbbda0 --- /dev/null +++ b/ColorControl/LgDevice.cs @@ -0,0 +1,358 @@ +using LgTv; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ColorControl +{ + class LgDevice + { + protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); + + public string Name { get; private set; } + public string IpAddress { get; private set; } + public string MacAddress { get; private set; } + public bool IsCustom { get; private set; } + [JsonIgnore] + public bool IsDummy { get; private set; } + + public bool PowerOnAfterStartup { get; set; } + public bool PowerOnAfterResume { get; set; } + public bool PowerOffOnShutdown { get; set; } + public bool PowerOffOnStandby { get; set; } + public bool PowerSwitchOnScreenSaver { get; set; } + + [JsonIgnore] + private LgTvApi _lgTvApi; + [JsonIgnore] + private bool _justWokeUp; + + [JsonIgnore] + private Dictionary> _invokableActions = new Dictionary>(); + + [JsonConstructor] + public LgDevice(string name, string ipAddress, string macAddress, bool isCustom = true, bool isDummy = false) + { + Name = name; + IpAddress = ipAddress; + MacAddress = macAddress; + IsCustom = isCustom; + IsDummy = isDummy; + + _invokableActions.Add("WOL", new Func(Wake)); + } + + public override string ToString() + { + return (IsDummy ? string.Empty : (IsCustom ? "Custom: " : "Auto detect: ")) + $"{Name}" + (!string.IsNullOrEmpty(IpAddress) ? $" ({IpAddress})" : string.Empty); + } + + public async Task Connect(int retries = 3) + { + try + { + DisposeConnection(); + _lgTvApi = await LgTvApi.CreateLgTvApi(IpAddress, retries); + + //Test(); + return _lgTvApi != null; + } + catch (Exception ex) + { + string logMessage = ex.ToLogString(Environment.StackTrace); + Logger.Error($"Error while connecting to {IpAddress}: {logMessage}"); + return false; + } + } + + public bool IsConnected() + { + return !_lgTvApi?.ConnectionClosed ?? false; + } + + internal void DisposeConnection() + { + if (_lgTvApi != null) + { + _lgTvApi.Dispose(); + _lgTvApi = null; + } + } + public async Task ExecutePreset(LgPreset preset, bool reconnect = false) + { + var hasApp = !string.IsNullOrEmpty(preset.appId); + + var hasWOL = preset.steps.Any(s => s.Equals("WOL", StringComparison.OrdinalIgnoreCase)); + + if (hasWOL) + { + var connected = await WakeAndConnect(0); + if (!connected) + { + return false; + } + } + + for (var tries = 0; tries <= 1; tries++) + { + if (!await Connected(reconnect || tries == 1)) + { + 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; + } + + private async Task ExecuteSteps(LgTvApi api, LgPreset preset) + { + var mouse = await api.GetMouse(); + foreach (var step in preset.steps) + { + var keySpec = step.Split(':'); + var key = keySpec[0].ToUpper(); + if (_invokableActions.ContainsKey(key)) + { + continue; + } + + if (keySpec.Length == 2) + { + SendKey(mouse, key, int.Parse(keySpec[1])); + } + else + { + SendKey(mouse, key); + } + } + } + + private void SendKey(LgWebOsMouseService mouse, string key, int delay = 180) + { + if (key.Length == 1 && int.TryParse(key, out _)) + { + key = "_" + key; + } + var button = (ButtonType)Enum.Parse(typeof(ButtonType), key); + mouse.SendButton(button); + Thread.Sleep(delay); + } + + public async Task GetMouseAsync() + { + return await _lgTvApi.GetMouse(); + } + + public async Task> GetApps(bool force = false) + { + if (!await Connected(force)) + { + Logger.Debug("Cannot refresh apps: no connection could be made"); + return new List(); + } + + return await _lgTvApi.GetApps(force); + } + + internal async Task PowerOff() + { + if (!await Connected(true)) + { + return false; + } + + await _lgTvApi.TurnOff(); + return true; + } + + internal async Task TestConnection(int retries = 1) + { + if (!await Connected(true, retries)) + { + return false; + } + + try + { + await _lgTvApi.IsMuted(); + return true; + } + catch (Exception ex) + { + Logger.Error("TestConnection: " + ex.ToLogString()); + return false; + } + } + + internal async Task WakeAndConnect(int wakeDelay = 5000, int connectDelay = 500) + { + try + { + //if (wakeDelay == 0) + //{ + // if (await ConnectToSelectedDevice()) + // { + // Logger.Debug("Already connected, no wake needed?"); + // return; + // }; + //} + + await Task.Delay(wakeDelay); + var result = Wake(); + if (!result) + { + Logger.Debug("WOL failed"); + return false; + } + Logger.Debug("WOL succeeded"); + await Task.Delay(connectDelay); + result = await Connect(); + Logger.Debug("Connect succeeded: " + result); + return result; + } + catch (Exception e) + { + Logger.Error("WakeAndConnectToSelectedDevice: " + e.ToLogString()); + return false; + } + } + + internal bool Wake() + { + var result = false; + + if (MacAddress != null) + { + result = WOL.WakeFunction(MacAddress); + _justWokeUp = true; + } + else + { + Logger.Debug("Cannot wake device: the device has no MAC-address"); + } + + return result; + } + + internal async Task WakeAndConnectWithRetries(int retries = 5) + { + var wakeDelay = 0; + var maxRetries = retries <= 1 ? 5 : retries; + + var result = false; + for (var retry = 0; retry < maxRetries && !result; retry++) + { + Logger.Debug($"WakeAndConnectWithRetries: attempt {retry + 1} of {maxRetries}..."); + + var ms = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + result = await WakeAndConnect(retry == 0 ? wakeDelay : 0); + ms = DateTimeOffset.Now.ToUnixTimeMilliseconds() - ms; + if (!result) + { + var delay = 2000 - ms; + if (delay > 0) + { + await Task.Delay((int)delay); + } + } + } + + return result; + } + + public Dictionary> GetInvokableActions() + { + return _invokableActions; + } + + public async Task PowerOn() + { + var mouse = await _lgTvApi.GetMouse(); + mouse.SendButton(ButtonType.POWER); + } + + public void Test() + { + try + { + //_lgTvApi.GetServiceList(); + //_lgTvApi?.Test(); + } + catch (Exception ex) + { + Logger.Error("TEST: " + ex.ToLogString()); + } + } + + private async Task Connected(bool reconnect = false, int retries = 3) + { + if (reconnect || !IsConnected() || !string.Equals(_lgTvApi.GetIpAddress(), IpAddress)) + { + if (!await Connect(retries)) + { + Logger.Debug("Cannot apply LG-preset: no connection could be made"); + return false; + } + } + return true; + } + + internal void ConvertToCustom() + { + IsCustom = true; + } + } +} diff --git a/ColorControl/LgPreset.cs b/ColorControl/LgPreset.cs index 7721999..fc113c7 100644 --- a/ColorControl/LgPreset.cs +++ b/ColorControl/LgPreset.cs @@ -1,4 +1,5 @@ using LgTv; +using Newtonsoft.Json; using System.Collections.Generic; using System.Linq; @@ -6,10 +7,18 @@ namespace ColorControl { class LgPreset : PresetBase { + public static List LgApps { get; set; } + public static List LgDevices { get; set; } + + public string appId { get; set; } public List steps { get; set; } + public string DeviceMacAddress { get; set; } - public static List LgApps { get; set; } + [JsonIgnore] + public int X { get; set; } + [JsonIgnore] + public int Y { get; set; } public LgPreset() { @@ -37,7 +46,7 @@ namespace ColorControl public static List GetColumnNames() { - return new List() { "Name", "App|200", "Steps|400", "Shortcut" }; + return new List() { "Name", "Device|120", "App|200", "Steps|400", "Shortcut" }; } public override List GetDisplayValues(Config config = null) @@ -45,6 +54,29 @@ namespace ColorControl var values = new List(); values.Add(name); + + var deviceString = "Global"; + if (!string.IsNullOrEmpty(DeviceMacAddress)) + { + if (LgDevices != null) + { + var device = LgDevices.FirstOrDefault(d => !string.IsNullOrEmpty(d.MacAddress) && d.MacAddress.Equals(DeviceMacAddress)); + if (device != null) + { + deviceString = device.Name; + } + else + { + deviceString = "Unknown: device not found"; + } + } + else + { + deviceString = DeviceMacAddress; + } + } + values.Add(deviceString); + var app = appId; if (LgApps != null) { diff --git a/ColorControl/LgService.cs b/ColorControl/LgService.cs index fb1c452..dfb67a7 100644 --- a/ColorControl/LgService.cs +++ b/ColorControl/LgService.cs @@ -15,16 +15,21 @@ namespace ColorControl { class LgService : ServiceBase { + internal enum PowerOnOffState + { + StartUp = 1, + Resume = 2, + ScreenSaver = 3, + ShutDown = 4, + StandBy = 5 + } + protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); public static string LgListAppsJson = "listApps.json"; - //private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); - - //public string FriendlyScreenName { get; private set; } - - public List Devices { get; private set; } - public PnpDev SelectedDevice + public List Devices { get; private set; } + public LgDevice SelectedDevice { get { return _selectedDevice; } set @@ -36,17 +41,13 @@ namespace ColorControl public LgServiceConfig Config { get; private set; } - private LgTvApi _lgTvApi; private string _configFilename; private bool _allowPowerOn; - private bool _justWokeUp; - private PnpDev _selectedDevice; + private LgDevice _selectedDevice; private string _rcButtonsFilename; private List _remoteControlButtons; private List _lgApps = new List(); - private Dictionary> _invokableActions = new Dictionary>(); - private bool _poweredOffByScreenSaver; private int _poweredOffByScreenSaverProcessId; private Task _monitorTask; @@ -56,22 +57,11 @@ namespace ColorControl { _allowPowerOn = allowPowerOn; - _invokableActions.Add("WOL", new Func(WakeSelectedDevice)); - LgPreset.LgApps = _lgApps; LoadConfig(); LoadPresets(); LoadRemoteControlButtons(); - - //foreach (var screen in Screen.AllScreens) - //{ - // var name = screen.DeviceFriendlyName(); - // if (name.Contains("LG")) - // { - // FriendlyScreenName = name; - // } - //} } ~LgService() @@ -165,6 +155,8 @@ namespace ColorControl { Config = new LgServiceConfig(); } + + LgPreset.LgDevices = Config.Devices; } private void LoadRemoteControlButtons() @@ -192,11 +184,6 @@ namespace ColorControl return _remoteControlButtons; } - public Dictionary> GetInvokableActions() - { - return _invokableActions; - } - private List GenerateDefaultRemoteControlButtons() { var list = new List(); @@ -207,11 +194,18 @@ namespace ColorControl list.Add(GeneratePreset(button.ToString())); } + list.Add(GeneratePreset("Power", step: "POWER")); + list.Add(GeneratePreset("Home", appId: "com.webos.app.home", key: Keys.H)); list.Add(GeneratePreset("Settings", appId: "com.palm.app.settings", key: Keys.S)); + list.Add(GeneratePreset("TV Guide", appId: "com.webos.app.livemenu")); + list.Add(GeneratePreset("List", step: "LIST")); + list.Add(GeneratePreset("SAP", step: "SAP")); list.Add(GeneratePreset("Vol +", step: "VOLUMEUP", key: Keys.VolumeUp)); list.Add(GeneratePreset("Vol -", step: "VOLUMEDOWN", key: Keys.VolumeDown)); list.Add(GeneratePreset("Mute", step: "MUTE", key: Keys.VolumeMute)); + list.Add(GeneratePreset("Channel +", step: "CHANNELUP", key: Keys.MediaNextTrack)); + list.Add(GeneratePreset("Channel -", step: "CHANNELDOWN", key: Keys.MediaPreviousTrack)); list.Add(GeneratePreset("Up", step: "UP", key: Keys.Up)); list.Add(GeneratePreset("Down", step: "DOWN", key: Keys.Down)); @@ -222,6 +216,19 @@ namespace ColorControl list.Add(GeneratePreset("Back", step: "BACK", key: Keys.Back)); list.Add(GeneratePreset("Exit", step: "EXIT", key: Keys.Escape)); + list.Add(GeneratePreset("Netflix", appId: "netflix")); + list.Add(GeneratePreset("Inputs", appId: "com.webos.app.homeconnect")); + list.Add(GeneratePreset("Amazon Prime", appId: "amazon")); + + list.Add(GeneratePreset("Red", step: "RED")); + list.Add(GeneratePreset("Green", step: "GREEN")); + list.Add(GeneratePreset("Yellow", step: "YELLOW")); + list.Add(GeneratePreset("Blue", step: "BLUE")); + + list.Add(GeneratePreset("Rakuten TV", appId: "ui30")); + list.Add(GeneratePreset("Play", step: "PLAY")); + list.Add(GeneratePreset("Pause", step: "PAUSE")); + return list; } @@ -258,6 +265,8 @@ namespace ColorControl private void SaveConfig() { + Config.PowerOnAfterStartup = Devices.Any(d => d.PowerOnAfterStartup); + var json = JsonConvert.SerializeObject(Config); File.WriteAllText(_configFilename, json); } @@ -270,14 +279,36 @@ namespace ColorControl public async Task RefreshDevices(bool connect = true, bool afterStartUp = false) { - var devices = await Utils.GetPnpDevices(Config.DeviceSearchKey); - SetDevices(devices); + Devices = Config.Devices; + var customIpAddresses = Devices.Where(d => d.IsCustom).Select(d => d.IpAddress); + + var pnpDevices = await Utils.GetPnpDevices(Config.DeviceSearchKey); + + var autoDevices = pnpDevices.Where(p => !customIpAddresses.Contains(p.IpAddress)).Select(d => new LgDevice(d.Name, d.IpAddress, d.MacAddress, false)).ToList(); + var autoIpAddresses = pnpDevices.Select(d => d.IpAddress); + + Devices.RemoveAll(d => !d.IsCustom && !autoIpAddresses.Contains(d.IpAddress)); + + var newAutoDevices = autoDevices.Where(ad => !Devices.Any(d => d.IpAddress.Equals(ad.IpAddress))); + Devices.AddRange(newAutoDevices); + + if (Devices.Any()) + { + var preferredDevice = Devices.FirstOrDefault(x => x.MacAddress != null && x.MacAddress.Equals(Config.PreferredMacAddress)) ?? Devices[0]; + + SelectedDevice = preferredDevice; + } + else + { + SelectedDevice = null; + } if (afterStartUp && SelectedDevice == null && Config.PowerOnAfterStartup && !string.IsNullOrEmpty(Config.PreferredMacAddress)) { Logger.Debug("No device has been found, trying to wake it first..."); - WakeSelectedDevice(Config.PreferredMacAddress); + var tempDevice = new LgDevice("Test", string.Empty, Config.PreferredMacAddress); + tempDevice.Wake(); await Task.Delay(4000); await RefreshDevices(); @@ -287,70 +318,17 @@ namespace ColorControl if (connect && SelectedDevice != null) { - if (Config.PowerOnAfterStartup && _allowPowerOn) + if (_allowPowerOn) { - var _ = WakeAndConnectToSelectedDeviceWithRetries(); + WakeAfterStartupOrResume(); } else { - var _ = ConnectToSelectedDevice(); + var _ = SelectedDevice.Connect(); } } } - private void SetDevices(List devices) - { - Devices = devices; - if (Devices?.Count > 0) - { - var preferredDevice = Devices.FirstOrDefault(x => x.MacAddress != null && x.MacAddress.Equals(Config.PreferredMacAddress)) ?? Devices[0]; - - SelectedDevice = preferredDevice; - } - } - - public async Task ConnectToSelectedDevice(int retries = 3) - { - try - { - DisposeConnection(); - _lgTvApi = await LgTvApi.CreateLgTvApi(SelectedDevice.IpAddress, retries); - - //_lgTvApi.GetServiceList(); - return _lgTvApi != null; - } - catch (Exception ex) - { - string logMessage = ex.ToLogString(Environment.StackTrace); - Logger.Error($"Error while connecting to {SelectedDevice.IpAddress}: {logMessage}"); - return false; - } - } - - private async Task Connected(bool reconnect = false) - { - if (SelectedDevice == null) - { - Logger.Debug("Cannot apply LG-preset: no device has been selected"); - return false; - } - - if (reconnect || !IsConnected() || !string.Equals(_lgTvApi.GetIpAddress(), SelectedDevice.IpAddress)) - { - if (!await ConnectToSelectedDevice()) - { - Logger.Debug("Cannot apply LG-preset: no connection could be made"); - return false; - } - } - return true; - } - - private bool IsConnected() - { - return !_lgTvApi?.ConnectionClosed ?? false; - } - public async Task ApplyPreset(string presetName) { var preset = _presets.FirstOrDefault(p => p.name.Equals(presetName, StringComparison.OrdinalIgnoreCase)); @@ -366,123 +344,28 @@ namespace ColorControl public async Task ApplyPreset(LgPreset preset, bool reconnect = false) { - var hasApp = !string.IsNullOrEmpty(preset.appId); + var device = GetPresetDevice(preset); - var hasWOL = preset.steps.Any(s => s.Equals("WOL", StringComparison.OrdinalIgnoreCase)); - - if (hasWOL) + if (device == null) { - var connected = await WakeAndConnectToSelectedDevice(0); - if (!connected) - { - return false; - } + Logger.Debug("Cannot apply preset: no device has been selected"); + return false; } - for (var tries = 0; tries <= 1; tries++) - { - if (!await Connected(reconnect || tries == 1)) - { - 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; + return await device.ExecutePreset(preset, reconnect); } - public async Task PowerOn() + public LgDevice GetPresetDevice(LgPreset preset) { - var mouse = await _lgTvApi.GetMouse(); - mouse.SendButton(ButtonType.POWER); - } - - private async Task ExecuteSteps(LgTvApi api, LgPreset preset) - { - var mouse = await api.GetMouse(); - foreach (var step in preset.steps) - { - var keySpec = step.Split(':'); - var key = keySpec[0].ToUpper(); - if (_invokableActions.ContainsKey(key)) - { - continue; - } - - if (keySpec.Length == 2) - { - SendKey(mouse, key, int.Parse(keySpec[1])); - } - else - { - SendKey(mouse, key); - } - } - } - - private void SendKey(LgWebOsMouseService mouse, string key, int delay = 180) - { - if (key.Length == 1 && int.TryParse(key, out _)) - { - key = "_" + key; - } - var button = (ButtonType)Enum.Parse(typeof(ButtonType), key); - mouse.SendButton(button); - Thread.Sleep(delay); + var device = string.IsNullOrEmpty(preset.DeviceMacAddress) + ? SelectedDevice + : Devices.FirstOrDefault(d => d.MacAddress?.Equals(preset.DeviceMacAddress, StringComparison.OrdinalIgnoreCase) ?? false); + return device; } public async Task GetMouseAsync() { - return await _lgTvApi.GetMouse(); + return await SelectedDevice?.GetMouseAsync(); } public async Task RefreshApps(bool force = false) @@ -490,15 +373,16 @@ namespace ColorControl if (SelectedDevice == null) { Logger.Debug("Cannot refresh apps: no device has been selected"); + return; } - if (!await Connected(force)) + var apps = await SelectedDevice.GetApps(); + + if (apps.Any()) { - Logger.Debug("Cannot refresh apps: no connection could be made"); + _lgApps.Clear(); + _lgApps.AddRange(apps); } - - _lgApps.Clear(); - _lgApps.AddRange(await _lgTvApi.GetApps(force)); } public List GetApps() @@ -506,138 +390,47 @@ namespace ColorControl return _lgApps; } - internal void DisposeConnection() - { - if (_lgTvApi != null) - { - _lgTvApi.Dispose(); - _lgTvApi = null; - } - } - internal async Task PowerOff() { - if (!await Connected(true)) - { - return false; - } - - await _lgTvApi.TurnOff(); - return true; + return await SelectedDevice?.PowerOff(); } - internal async Task TestConnection() + internal Task PowerOffOnShutdownOrResume(PowerOnOffState state = PowerOnOffState.ShutDown) { - if (!await Connected(true)) + var devices = Devices.Where(d => state == PowerOnOffState.ShutDown && d.PowerOffOnShutdown || + state == PowerOnOffState.StandBy && d.PowerOffOnStandby); + + var tasks = new List(); + foreach (var device in devices) { - return false; + var task = device.PowerOff(); + + tasks.Add(task); } - try - { - await _lgTvApi.IsMuted(); - return true; - } - catch (Exception ex) - { - Logger.Error("TestConnection: " + ex.ToLogString()); - return false; - } + return Task.WhenAll(tasks.ToArray()); } internal bool WakeSelectedDevice() { - return WakeSelectedDevice(SelectedDevice?.MacAddress ?? Config.PreferredMacAddress); + return SelectedDevice?.Wake() ?? false; } - internal bool WakeSelectedDevice(string macAddress) - { - var result = false; - - macAddress = string.IsNullOrEmpty(macAddress) ? SelectedDevice?.MacAddress : macAddress; - if (macAddress != null) - { - result = WOL.WakeFunction(macAddress, Config.UseOldNpcapWol); - _justWokeUp = true; - } - else - { - Logger.Debug("Cannot wake device: no device has been selected or there is no preferred MAC-address stored in the configuration"); - } - - return result; - } - - internal void WakeAfterResume() - { - if (Config.PowerOnAfterResume) - { - var _ = WakeAndConnectToSelectedDeviceWithRetries(); - } - } - - private async Task WakeAndConnectToSelectedDeviceWithRetries(bool checkUserSession = true) + internal void WakeAfterStartupOrResume(PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true) { if (checkUserSession && !UserSessionInfo.UserLocalSession) { - Logger.Debug($"WakeAndConnectToSelectedDeviceWithRetries: not waking because session info indicates no local session"); - return false; + Logger.Debug($"WakeAfterStartupOrResume: not waking because session info indicates no local session"); + return; } - var wakeDelay = 0; - var maxRetries = Config.PowerOnRetries <= 1 ? 5 : Config.PowerOnRetries; - - var result = false; - for (var retry = 0; retry < maxRetries && !result; retry++) + var wakeDevices = Devices.Where(d => state == PowerOnOffState.StartUp && d.PowerOnAfterStartup || + state == PowerOnOffState.Resume && d.PowerOnAfterResume || + state == PowerOnOffState.ScreenSaver && d.PowerSwitchOnScreenSaver); + foreach (var device in wakeDevices) { - Logger.Debug($"WakeAndConnectToSelectedDeviceWithRetries: attempt {retry + 1} of {maxRetries}..."); - - var ms = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - result = await WakeAndConnectToSelectedDevice(retry == 0 ? wakeDelay : 0); - ms = DateTimeOffset.Now.ToUnixTimeMilliseconds() - ms; - if (!result) - { - var delay = 2000 - ms; - if (delay > 0) - { - await Task.Delay((int)delay); - } - } - } - - return result; - } - - private async Task WakeAndConnectToSelectedDevice(int wakeDelay = 5000, int connectDelay = 500) - { - try - { - //if (wakeDelay == 0) - //{ - // if (await ConnectToSelectedDevice()) - // { - // Logger.Debug("Already connected, no wake needed?"); - // return; - // }; - //} - - await Task.Delay(wakeDelay); - var result = WakeSelectedDevice(); - if (!result) - { - Logger.Debug("WOL failed"); - return false; - } - Logger.Debug("WOL succeeded"); - await Task.Delay(connectDelay); - result = await ConnectToSelectedDevice(); - Logger.Debug("Connect succeeded: " + result); - return result; - } - catch (Exception e) - { - Logger.Error("WakeAndConnectToSelectedDevice: " + e.ToLogString()); - return false; + device.DisposeConnection(); + var _ = device.WakeAndConnectWithRetries(Config.PowerOnRetries); } } @@ -663,7 +456,7 @@ namespace ColorControl Logger.Debug("User switched to local and screen was powered off due to screensaver: waking up"); _poweredOffByScreenSaver = false; _poweredOffByScreenSaverProcessId = 0; - var _ = WakeAndConnectToSelectedDeviceWithRetries(); + WakeAfterStartupOrResume(); } else { @@ -680,18 +473,17 @@ namespace ColorControl if (powerOn) { - DisposeConnection(); - WakeAfterResume(); + WakeAfterStartupOrResume(PowerOnOffState.Resume); return; } - if (Config.PowerOffOnStandby) + if (Devices.Any(d => d.PowerOffOnStandby)) { NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS | NativeConstants.ES_SYSTEM_REQUIRED | NativeConstants.ES_AWAYMODE_REQUIRED); try { Logger.Debug("Powering off tv..."); - var task = PowerOff(); + var task = PowerOffOnShutdownOrResume(PowerOnOffState.StandBy); Utils.WaitForTask(task); Logger.Debug("Done powering off tv"); } @@ -717,11 +509,12 @@ namespace ColorControl private void MonitorProcesses() { - if (Config.PowerSwitchOnScreenSaver && _monitorTask == null) + var enableMonitoring = Devices.Any(d => d.PowerSwitchOnScreenSaver); + if (enableMonitoring && _monitorTask == null) { _monitorTask = CheckProcesses(); } - else if (!Config.PowerSwitchOnScreenSaver && _monitorTask != null) + else if (!enableMonitoring && _monitorTask != null) { _monitorTask = null; } @@ -729,12 +522,12 @@ namespace ColorControl public async Task CheckProcesses() { - var wasConnected = IsConnected(); + var wasConnected = Devices.Any(d => d.PowerSwitchOnScreenSaver && d.IsConnected()); _monitorTaskCounter++; var validCounter = _monitorTaskCounter; var lastProcessId = 0; - while (Config.PowerSwitchOnScreenSaver && validCounter == _monitorTaskCounter) + while (Devices.Any(d => d.PowerSwitchOnScreenSaver) && validCounter == _monitorTaskCounter) { await Task.Delay(500); @@ -758,16 +551,18 @@ namespace ColorControl _poweredOffByScreenSaver = false; _poweredOffByScreenSaverProcessId = 0; lastProcessId = 0; - var _ = WakeAndConnectToSelectedDeviceWithRetries(); + WakeAfterStartupOrResume(PowerOnOffState.ScreenSaver); continue; } - if (!IsConnected()) + var devices = Devices.Where(d => d.PowerSwitchOnScreenSaver && d.IsConnected()).ToList(); + + if (!devices.Any()) { if (wasConnected) { - Logger.Debug("TV was connected, but not any longer"); + Logger.Debug("Screensaver check: TV(s) where connected, but not any longer"); wasConnected = false; } continue; @@ -775,49 +570,50 @@ namespace ColorControl if (!wasConnected) { - Logger.Debug("TV was not connected, but connection has now been established"); + Logger.Debug("Screensaver check: TV(s) where 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("Test connection..."); - var test = await TestConnection(); - Logger.Debug("Test connection result: " + test); + var process = processes.FirstOrDefault(p => p.ProcessName.ToLowerInvariant().EndsWith(".scr")); - Logger.Debug("Powering off tv because of screensaver"); - _poweredOffByScreenSaver = await PowerOff(); - _poweredOffByScreenSaverProcessId = process.Id; - lastProcessId = 0; - } - catch (Exception e) - { - Logger.Error("CheckProcesses: can't power off: " + e.ToLogString()); - } - if (!IsConnected()) - { - _poweredOffByScreenSaver = false; - } - break; - } - else + if (process == null) + { + continue; + } + + var parent = process.Parent(); + + if (parent?.ProcessName.Contains("winlogon") ?? false) + { + Logger.Debug($"Screensaver started: {process.ProcessName}, parent: {parent.ProcessName}"); + try + { + foreach (var device in devices) { - if (process.Id != lastProcessId) + Logger.Debug($"Screensaver check: test connection with {device.Name}..."); + var test = await device.TestConnection(); + Logger.Debug("Screensaver check: test connection result: " + test); + if (!test) { - Logger.Debug($"Screensaver started: {process.ProcessName}, but invalid parent: {parent?.ProcessName ?? "no parent"}"); - lastProcessId = process.Id; + continue; } + + Logger.Debug($"Screensaver check: powering off tv {device.Name} because of screensaver"); + _poweredOffByScreenSaver = await device.PowerOff(); } + + _poweredOffByScreenSaverProcessId = process.Id; + lastProcessId = 0; } + catch (Exception e) + { + Logger.Error($"Screensaver check: can't power off: " + e.ToLogString()); + } + } + else if (process.Id != lastProcessId) + { + Logger.Debug($"Screensaver started: {process.ProcessName}, but invalid parent: {parent?.ProcessName ?? "no parent"}"); + lastProcessId = process.Id; } } catch (Exception ex) @@ -826,5 +622,29 @@ namespace ColorControl } } } + + internal void AddCustomDevice(LgDevice device) + { + Devices.Add(device); + } + + internal void RemoveCustomDevice(LgDevice device) + { + Devices.Remove(device); + + if (!string.IsNullOrEmpty(device.MacAddress)) + { + var presets = _presets.Where(p => !string.IsNullOrEmpty(p.DeviceMacAddress) && p.DeviceMacAddress.Equals(device.MacAddress)).ToList(); + presets.ForEach(preset => + { + preset.DeviceMacAddress = null; + }); + } + + if (SelectedDevice == device) + { + SelectedDevice = Devices.Any() ? Devices.First() : null; + } + } } } diff --git a/ColorControl/LgServiceConfig.cs b/ColorControl/LgServiceConfig.cs index 1a7ad36..9773eec 100644 --- a/ColorControl/LgServiceConfig.cs +++ b/ColorControl/LgServiceConfig.cs @@ -1,17 +1,11 @@ -namespace ColorControl +using System.Collections.Generic; + +namespace ColorControl { class LgServiceConfig { public bool PowerOnAfterStartup { get; set; } - public bool PowerOnAfterResume { get; set; } - - public bool PowerOffOnShutdown { get; set; } - - public bool PowerOffOnStandby { get; set; } - - public bool PowerSwitchOnScreenSaver { get; set; } - public int PowerOnDelayAfterResume { get; set; } public int PowerOnRetries { get; set; } @@ -22,11 +16,16 @@ public bool UseOldNpcapWol { get; set; } + public List Devices { get; set; } + + public bool ShowRemoteControl { get; set; } + public LgServiceConfig() { PowerOnDelayAfterResume = 5000; DeviceSearchKey = "[LG]"; PowerOnRetries = 10; + Devices = new List(); } } } diff --git a/ColorControl/MainForm.Designer.cs b/ColorControl/MainForm.Designer.cs index 21e165d..fa20d76 100644 --- a/ColorControl/MainForm.Designer.cs +++ b/ColorControl/MainForm.Designer.cs @@ -99,36 +99,43 @@ this.miAmdHDRIncluded = new System.Windows.Forms.ToolStripMenuItem(); this.miAmdHDRToggle = new System.Windows.Forms.ToolStripMenuItem(); this.miAmdHDREnabled = new System.Windows.Forms.ToolStripMenuItem(); + this.miAmdCopyId = new System.Windows.Forms.ToolStripMenuItem(); this.btnApplyAmd = new System.Windows.Forms.Button(); this.lvAmdPresets = new System.Windows.Forms.ListView(); this.tabLG = new System.Windows.Forms.TabPage(); - this.btnLGRemoteControl = new System.Windows.Forms.Button(); - this.btnLgDeviceFilterRefresh = new System.Windows.Forms.Button(); - this.clbLgPower = new System.Windows.Forms.CheckedListBox(); + this.scLgController = new System.Windows.Forms.SplitContainer(); + this.chkLgRemoteControlShow = new System.Windows.Forms.CheckBox(); + this.label3 = new System.Windows.Forms.Label(); + this.cbxLgDevices = new System.Windows.Forms.ComboBox(); + this.btnLgDeviceConvertToCustom = new System.Windows.Forms.Button(); + this.cbxLgApps = new System.Windows.Forms.ComboBox(); + this.lvLgPresets = new System.Windows.Forms.ListView(); + this.label5 = new System.Windows.Forms.Label(); + this.cbxLgPresetDevice = new System.Windows.Forms.ComboBox(); + this.btnDeleteLg = new System.Windows.Forms.Button(); + this.btnApplyLg = new System.Windows.Forms.Button(); + this.lblDeviceFilter = new System.Windows.Forms.Label(); + this.lblLgPresetDevice = new System.Windows.Forms.Label(); + this.btnAddLg = new System.Windows.Forms.Button(); + this.edtShortcutLg = new System.Windows.Forms.TextBox(); + this.edtLgDeviceFilter = new System.Windows.Forms.TextBox(); + this.btnLgRemoveDevice = new System.Windows.Forms.Button(); + this.btnLgRefreshApps = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.btnLgAddDevice = new System.Windows.Forms.Button(); + this.edtNameLg = new System.Windows.Forms.TextBox(); + this.btnSetShortcutLg = new System.Windows.Forms.Button(); + this.edtStepsLg = new System.Windows.Forms.TextBox(); this.btnLgAddButton = new System.Windows.Forms.Button(); this.mnuLgButtons = new System.Windows.Forms.ContextMenuStrip(this.components); this.mnuLgRcButtons = new System.Windows.Forms.ToolStripMenuItem(); this.mnuLgActions = new System.Windows.Forms.ToolStripMenuItem(); - this.label3 = new System.Windows.Forms.Label(); - this.cbxLgDevices = new System.Windows.Forms.ComboBox(); - this.btnLgRefreshApps = new System.Windows.Forms.Button(); - this.btnAddLg = new System.Windows.Forms.Button(); - this.btnDeleteLg = new System.Windows.Forms.Button(); - this.cbxLgApps = new System.Windows.Forms.ComboBox(); - this.label5 = new System.Windows.Forms.Label(); - this.lblDeviceFilter = new System.Windows.Forms.Label(); - this.edtLgDeviceFilter = new System.Windows.Forms.TextBox(); - this.label1 = new System.Windows.Forms.Label(); - this.edtNameLg = new System.Windows.Forms.TextBox(); - this.edtStepsLg = new System.Windows.Forms.TextBox(); - this.lblStepsLg = new System.Windows.Forms.Label(); - this.lblLgError = new System.Windows.Forms.Label(); this.btnCloneLg = new System.Windows.Forms.Button(); - this.btnSetShortcutLg = new System.Windows.Forms.Button(); - this.label2 = new System.Windows.Forms.Label(); - this.edtShortcutLg = new System.Windows.Forms.TextBox(); - this.btnApplyLg = new System.Windows.Forms.Button(); - this.lvLgPresets = new System.Windows.Forms.ListView(); + this.lblStepsLg = new System.Windows.Forms.Label(); + this.btnLgDeviceFilterRefresh = new System.Windows.Forms.Button(); + this.clbLgPower = new System.Windows.Forms.CheckedListBox(); + this.lblLgError = new System.Windows.Forms.Label(); this.tabOptions = new System.Windows.Forms.TabPage(); this.grpNvidiaOptions = new System.Windows.Forms.GroupBox(); this.lblDitheringMode = new System.Windows.Forms.Label(); @@ -168,13 +175,16 @@ this.lbPlugins = new System.Windows.Forms.ListBox(); this.label7 = new System.Windows.Forms.Label(); this.lblInfo = new System.Windows.Forms.Label(); - this.miAmdCopyId = new System.Windows.Forms.ToolStripMenuItem(); + this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker(); this.tcMain.SuspendLayout(); this.tabNVIDIA.SuspendLayout(); this.mnuNvPresets.SuspendLayout(); this.tabAMD.SuspendLayout(); this.mnuAmdPresets.SuspendLayout(); this.tabLG.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.scLgController)).BeginInit(); + this.scLgController.Panel1.SuspendLayout(); + this.scLgController.SuspendLayout(); this.mnuLgButtons.SuspendLayout(); this.tabOptions.SuspendLayout(); this.grpNvidiaOptions.SuspendLayout(); @@ -205,7 +215,7 @@ this.tcMain.Location = new System.Drawing.Point(12, 12); this.tcMain.Name = "tcMain"; this.tcMain.SelectedIndex = 0; - this.tcMain.Size = new System.Drawing.Size(849, 472); + this.tcMain.Size = new System.Drawing.Size(962, 479); this.tcMain.TabIndex = 1; this.tcMain.SelectedIndexChanged += new System.EventHandler(this.tabControl1_SelectedIndexChanged); // @@ -226,7 +236,7 @@ this.tabNVIDIA.Location = new System.Drawing.Point(4, 22); this.tabNVIDIA.Name = "tabNVIDIA"; this.tabNVIDIA.Padding = new System.Windows.Forms.Padding(3); - this.tabNVIDIA.Size = new System.Drawing.Size(841, 446); + this.tabNVIDIA.Size = new System.Drawing.Size(954, 453); this.tabNVIDIA.TabIndex = 0; this.tabNVIDIA.Text = "NVIDIA controller"; this.tabNVIDIA.UseVisualStyleBackColor = true; @@ -235,7 +245,7 @@ // this.lblNvPresetName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.lblNvPresetName.AutoSize = true; - this.lblNvPresetName.Location = new System.Drawing.Point(6, 397); + this.lblNvPresetName.Location = new System.Drawing.Point(6, 404); this.lblNvPresetName.Name = "lblNvPresetName"; this.lblNvPresetName.Size = new System.Drawing.Size(38, 13); this.lblNvPresetName.TabIndex = 22; @@ -245,7 +255,7 @@ // this.edtNvPresetName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.edtNvPresetName.Enabled = false; - this.edtNvPresetName.Location = new System.Drawing.Point(87, 394); + this.edtNvPresetName.Location = new System.Drawing.Point(87, 401); this.edtNvPresetName.Name = "edtNvPresetName"; this.edtNvPresetName.Size = new System.Drawing.Size(200, 20); this.edtNvPresetName.TabIndex = 21; @@ -253,7 +263,7 @@ // btnAddModesNv // this.btnAddModesNv.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.btnAddModesNv.Location = new System.Drawing.Point(330, 365); + this.btnAddModesNv.Location = new System.Drawing.Point(330, 372); this.btnAddModesNv.Name = "btnAddModesNv"; this.btnAddModesNv.Size = new System.Drawing.Size(75, 23); this.btnAddModesNv.TabIndex = 9; @@ -265,7 +275,7 @@ // this.btnNvPresetDelete.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnNvPresetDelete.Enabled = false; - this.btnNvPresetDelete.Location = new System.Drawing.Point(249, 365); + this.btnNvPresetDelete.Location = new System.Drawing.Point(249, 372); this.btnNvPresetDelete.Name = "btnNvPresetDelete"; this.btnNvPresetDelete.Size = new System.Drawing.Size(75, 23); this.btnNvPresetDelete.TabIndex = 8; @@ -287,7 +297,7 @@ // this.btnClone.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnClone.Enabled = false; - this.btnClone.Location = new System.Drawing.Point(168, 365); + this.btnClone.Location = new System.Drawing.Point(168, 372); this.btnClone.Name = "btnClone"; this.btnClone.Size = new System.Drawing.Size(75, 23); this.btnClone.TabIndex = 6; @@ -299,7 +309,7 @@ // this.btnNvPresetSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnNvPresetSave.Enabled = false; - this.btnNvPresetSave.Location = new System.Drawing.Point(411, 365); + this.btnNvPresetSave.Location = new System.Drawing.Point(411, 372); this.btnNvPresetSave.Name = "btnNvPresetSave"; this.btnNvPresetSave.Size = new System.Drawing.Size(75, 23); this.btnNvPresetSave.TabIndex = 5; @@ -311,7 +321,7 @@ // this.lblShortcut.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.lblShortcut.AutoSize = true; - this.lblShortcut.Location = new System.Drawing.Point(6, 423); + this.lblShortcut.Location = new System.Drawing.Point(6, 430); this.lblShortcut.Name = "lblShortcut"; this.lblShortcut.Size = new System.Drawing.Size(50, 13); this.lblShortcut.TabIndex = 4; @@ -321,7 +331,7 @@ // this.edtShortcut.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.edtShortcut.Enabled = false; - this.edtShortcut.Location = new System.Drawing.Point(87, 420); + this.edtShortcut.Location = new System.Drawing.Point(87, 427); this.edtShortcut.Name = "edtShortcut"; this.edtShortcut.ReadOnly = true; this.edtShortcut.Size = new System.Drawing.Size(200, 20); @@ -335,7 +345,7 @@ this.btnChange.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnChange.ContextMenuStrip = this.mnuNvPresets; this.btnChange.Enabled = false; - this.btnChange.Location = new System.Drawing.Point(87, 365); + this.btnChange.Location = new System.Drawing.Point(87, 372); this.btnChange.Name = "btnChange"; this.btnChange.Size = new System.Drawing.Size(75, 23); this.btnChange.TabIndex = 2; @@ -578,7 +588,7 @@ // this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnApply.Enabled = false; - this.btnApply.Location = new System.Drawing.Point(6, 365); + this.btnApply.Location = new System.Drawing.Point(6, 372); this.btnApply.Name = "btnApply"; this.btnApply.Size = new System.Drawing.Size(75, 23); this.btnApply.TabIndex = 1; @@ -597,7 +607,7 @@ this.lvNvPresets.Location = new System.Drawing.Point(6, 6); this.lvNvPresets.MultiSelect = false; this.lvNvPresets.Name = "lvNvPresets"; - this.lvNvPresets.Size = new System.Drawing.Size(829, 353); + this.lvNvPresets.Size = new System.Drawing.Size(942, 360); this.lvNvPresets.TabIndex = 0; this.lvNvPresets.UseCompatibleStateImageBehavior = false; this.lvNvPresets.View = System.Windows.Forms.View.Details; @@ -621,7 +631,7 @@ this.tabAMD.Location = new System.Drawing.Point(4, 22); this.tabAMD.Name = "tabAMD"; this.tabAMD.Padding = new System.Windows.Forms.Padding(3); - this.tabAMD.Size = new System.Drawing.Size(841, 446); + this.tabAMD.Size = new System.Drawing.Size(954, 453); this.tabAMD.TabIndex = 5; this.tabAMD.Text = "AMD controller"; this.tabAMD.UseVisualStyleBackColor = true; @@ -630,7 +640,7 @@ // this.lblAmdPresetName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.lblAmdPresetName.AutoSize = true; - this.lblAmdPresetName.Location = new System.Drawing.Point(6, 397); + this.lblAmdPresetName.Location = new System.Drawing.Point(6, 404); this.lblAmdPresetName.Name = "lblAmdPresetName"; this.lblAmdPresetName.Size = new System.Drawing.Size(38, 13); this.lblAmdPresetName.TabIndex = 24; @@ -640,7 +650,7 @@ // this.edtAmdPresetName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.edtAmdPresetName.Enabled = false; - this.edtAmdPresetName.Location = new System.Drawing.Point(87, 394); + this.edtAmdPresetName.Location = new System.Drawing.Point(87, 401); this.edtAmdPresetName.Name = "edtAmdPresetName"; this.edtAmdPresetName.Size = new System.Drawing.Size(200, 20); this.edtAmdPresetName.TabIndex = 23; @@ -648,7 +658,7 @@ // btnAddAmd // this.btnAddAmd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.btnAddAmd.Location = new System.Drawing.Point(330, 365); + this.btnAddAmd.Location = new System.Drawing.Point(330, 372); this.btnAddAmd.Name = "btnAddAmd"; this.btnAddAmd.Size = new System.Drawing.Size(75, 23); this.btnAddAmd.TabIndex = 18; @@ -670,7 +680,7 @@ // this.btnDeleteAmd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnDeleteAmd.Enabled = false; - this.btnDeleteAmd.Location = new System.Drawing.Point(249, 365); + this.btnDeleteAmd.Location = new System.Drawing.Point(249, 372); this.btnDeleteAmd.Name = "btnDeleteAmd"; this.btnDeleteAmd.Size = new System.Drawing.Size(75, 23); this.btnDeleteAmd.TabIndex = 17; @@ -682,7 +692,7 @@ // this.btnCloneAmd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnCloneAmd.Enabled = false; - this.btnCloneAmd.Location = new System.Drawing.Point(168, 365); + this.btnCloneAmd.Location = new System.Drawing.Point(168, 372); this.btnCloneAmd.Name = "btnCloneAmd"; this.btnCloneAmd.Size = new System.Drawing.Size(75, 23); this.btnCloneAmd.TabIndex = 16; @@ -694,7 +704,7 @@ // this.btnAmdPresetSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnAmdPresetSave.Enabled = false; - this.btnAmdPresetSave.Location = new System.Drawing.Point(411, 365); + this.btnAmdPresetSave.Location = new System.Drawing.Point(411, 372); this.btnAmdPresetSave.Name = "btnAmdPresetSave"; this.btnAmdPresetSave.Size = new System.Drawing.Size(75, 23); this.btnAmdPresetSave.TabIndex = 15; @@ -706,7 +716,7 @@ // this.lblAmdShortcut.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.lblAmdShortcut.AutoSize = true; - this.lblAmdShortcut.Location = new System.Drawing.Point(6, 423); + this.lblAmdShortcut.Location = new System.Drawing.Point(6, 430); this.lblAmdShortcut.Name = "lblAmdShortcut"; this.lblAmdShortcut.Size = new System.Drawing.Size(50, 13); this.lblAmdShortcut.TabIndex = 14; @@ -716,7 +726,7 @@ // this.edtAmdShortcut.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.edtAmdShortcut.Enabled = false; - this.edtAmdShortcut.Location = new System.Drawing.Point(87, 420); + this.edtAmdShortcut.Location = new System.Drawing.Point(87, 427); this.edtAmdShortcut.Name = "edtAmdShortcut"; this.edtAmdShortcut.ReadOnly = true; this.edtAmdShortcut.Size = new System.Drawing.Size(200, 20); @@ -730,7 +740,7 @@ this.btnChangeAmd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnChangeAmd.ContextMenuStrip = this.mnuAmdPresets; this.btnChangeAmd.Enabled = false; - this.btnChangeAmd.Location = new System.Drawing.Point(87, 365); + this.btnChangeAmd.Location = new System.Drawing.Point(87, 372); this.btnChangeAmd.Name = "btnChangeAmd"; this.btnChangeAmd.Size = new System.Drawing.Size(75, 23); this.btnChangeAmd.TabIndex = 12; @@ -752,7 +762,7 @@ this.mnuAmdHDR, this.miAmdCopyId}); this.mnuAmdPresets.Name = "mnuNvPresets"; - this.mnuAmdPresets.Size = new System.Drawing.Size(185, 208); + this.mnuAmdPresets.Size = new System.Drawing.Size(185, 186); this.mnuAmdPresets.Opening += new System.ComponentModel.CancelEventHandler(this.mnuAmdPresets_Opening); // // miAmdApply @@ -866,11 +876,18 @@ this.miAmdHDREnabled.Text = "Enabled"; this.miAmdHDREnabled.Click += new System.EventHandler(this.miAmdHDREnabled_Click); // + // miAmdCopyId + // + this.miAmdCopyId.Name = "miAmdCopyId"; + this.miAmdCopyId.Size = new System.Drawing.Size(184, 22); + this.miAmdCopyId.Text = "Copy Id to Clipboard"; + this.miAmdCopyId.Click += new System.EventHandler(this.miAmdCopyId_Click); + // // btnApplyAmd // this.btnApplyAmd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnApplyAmd.Enabled = false; - this.btnApplyAmd.Location = new System.Drawing.Point(6, 365); + this.btnApplyAmd.Location = new System.Drawing.Point(6, 372); this.btnApplyAmd.Name = "btnApplyAmd"; this.btnApplyAmd.Size = new System.Drawing.Size(75, 23); this.btnApplyAmd.TabIndex = 11; @@ -889,7 +906,7 @@ this.lvAmdPresets.Location = new System.Drawing.Point(6, 6); this.lvAmdPresets.MultiSelect = false; this.lvAmdPresets.Name = "lvAmdPresets"; - this.lvAmdPresets.Size = new System.Drawing.Size(829, 353); + this.lvAmdPresets.Size = new System.Drawing.Size(942, 360); this.lvAmdPresets.TabIndex = 10; this.lvAmdPresets.UseCompatibleStateImageBehavior = false; this.lvAmdPresets.View = System.Windows.Forms.View.Details; @@ -898,51 +915,367 @@ // // tabLG // - this.tabLG.Controls.Add(this.btnLGRemoteControl); - this.tabLG.Controls.Add(this.btnLgDeviceFilterRefresh); - this.tabLG.Controls.Add(this.clbLgPower); - this.tabLG.Controls.Add(this.btnLgAddButton); - this.tabLG.Controls.Add(this.label3); - this.tabLG.Controls.Add(this.cbxLgDevices); - this.tabLG.Controls.Add(this.btnLgRefreshApps); - this.tabLG.Controls.Add(this.btnAddLg); - this.tabLG.Controls.Add(this.btnDeleteLg); - this.tabLG.Controls.Add(this.cbxLgApps); - this.tabLG.Controls.Add(this.label5); - this.tabLG.Controls.Add(this.lblDeviceFilter); - this.tabLG.Controls.Add(this.edtLgDeviceFilter); - this.tabLG.Controls.Add(this.label1); - this.tabLG.Controls.Add(this.edtNameLg); - this.tabLG.Controls.Add(this.edtStepsLg); - this.tabLG.Controls.Add(this.lblStepsLg); - this.tabLG.Controls.Add(this.lblLgError); - this.tabLG.Controls.Add(this.btnCloneLg); - this.tabLG.Controls.Add(this.btnSetShortcutLg); - this.tabLG.Controls.Add(this.label2); - this.tabLG.Controls.Add(this.edtShortcutLg); - this.tabLG.Controls.Add(this.btnApplyLg); - this.tabLG.Controls.Add(this.lvLgPresets); + this.tabLG.Controls.Add(this.scLgController); this.tabLG.Location = new System.Drawing.Point(4, 22); this.tabLG.Name = "tabLG"; this.tabLG.Padding = new System.Windows.Forms.Padding(3); - this.tabLG.Size = new System.Drawing.Size(841, 446); + this.tabLG.Size = new System.Drawing.Size(954, 453); this.tabLG.TabIndex = 1; this.tabLG.Text = "LG controller"; this.tabLG.UseVisualStyleBackColor = true; // - // btnLGRemoteControl + // scLgController // - this.btnLGRemoteControl.Location = new System.Drawing.Point(675, 5); - this.btnLGRemoteControl.Name = "btnLGRemoteControl"; - this.btnLGRemoteControl.Size = new System.Drawing.Size(95, 23); - this.btnLGRemoteControl.TabIndex = 36; - this.btnLGRemoteControl.Text = "Remote Control"; - this.btnLGRemoteControl.UseVisualStyleBackColor = true; - this.btnLGRemoteControl.Click += new System.EventHandler(this.btnLGRemoteControl_Click); + this.scLgController.Dock = System.Windows.Forms.DockStyle.Fill; + this.scLgController.Location = new System.Drawing.Point(3, 3); + this.scLgController.Name = "scLgController"; + // + // scLgController.Panel1 + // + this.scLgController.Panel1.Controls.Add(this.chkLgRemoteControlShow); + this.scLgController.Panel1.Controls.Add(this.label3); + this.scLgController.Panel1.Controls.Add(this.cbxLgDevices); + this.scLgController.Panel1.Controls.Add(this.btnLgDeviceConvertToCustom); + this.scLgController.Panel1.Controls.Add(this.cbxLgApps); + this.scLgController.Panel1.Controls.Add(this.lvLgPresets); + this.scLgController.Panel1.Controls.Add(this.label5); + this.scLgController.Panel1.Controls.Add(this.cbxLgPresetDevice); + this.scLgController.Panel1.Controls.Add(this.btnDeleteLg); + this.scLgController.Panel1.Controls.Add(this.btnApplyLg); + this.scLgController.Panel1.Controls.Add(this.lblDeviceFilter); + this.scLgController.Panel1.Controls.Add(this.lblLgPresetDevice); + this.scLgController.Panel1.Controls.Add(this.btnAddLg); + this.scLgController.Panel1.Controls.Add(this.edtShortcutLg); + this.scLgController.Panel1.Controls.Add(this.edtLgDeviceFilter); + this.scLgController.Panel1.Controls.Add(this.btnLgRemoveDevice); + this.scLgController.Panel1.Controls.Add(this.btnLgRefreshApps); + this.scLgController.Panel1.Controls.Add(this.label2); + this.scLgController.Panel1.Controls.Add(this.label1); + this.scLgController.Panel1.Controls.Add(this.btnLgAddDevice); + this.scLgController.Panel1.Controls.Add(this.edtNameLg); + this.scLgController.Panel1.Controls.Add(this.btnSetShortcutLg); + this.scLgController.Panel1.Controls.Add(this.edtStepsLg); + this.scLgController.Panel1.Controls.Add(this.btnLgAddButton); + this.scLgController.Panel1.Controls.Add(this.btnCloneLg); + this.scLgController.Panel1.Controls.Add(this.lblStepsLg); + this.scLgController.Panel1.Controls.Add(this.btnLgDeviceFilterRefresh); + this.scLgController.Panel1.Controls.Add(this.clbLgPower); + this.scLgController.Panel1.Controls.Add(this.lblLgError); + this.scLgController.Panel1MinSize = 750; + this.scLgController.Panel2MinSize = 150; + this.scLgController.Size = new System.Drawing.Size(948, 447); + this.scLgController.SplitterDistance = 767; + this.scLgController.TabIndex = 43; + // + // chkLgRemoteControlShow + // + this.chkLgRemoteControlShow.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.chkLgRemoteControlShow.Appearance = System.Windows.Forms.Appearance.Button; + this.chkLgRemoteControlShow.AutoSize = true; + this.chkLgRemoteControlShow.Location = new System.Drawing.Point(673, 7); + this.chkLgRemoteControlShow.Name = "chkLgRemoteControlShow"; + this.chkLgRemoteControlShow.Size = new System.Drawing.Size(90, 23); + this.chkLgRemoteControlShow.TabIndex = 43; + this.chkLgRemoteControlShow.Text = "Remote Control"; + this.chkLgRemoteControlShow.UseVisualStyleBackColor = true; + this.chkLgRemoteControlShow.CheckedChanged += new System.EventHandler(this.chkLgRemoteControlShow_CheckedChanged); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(5, 12); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(44, 13); + this.label3.TabIndex = 31; + this.label3.Text = "Device:"; + // + // cbxLgDevices + // + this.cbxLgDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbxLgDevices.FormattingEnabled = true; + this.cbxLgDevices.Location = new System.Drawing.Point(86, 8); + this.cbxLgDevices.Name = "cbxLgDevices"; + this.cbxLgDevices.Size = new System.Drawing.Size(318, 21); + this.cbxLgDevices.TabIndex = 30; + this.cbxLgDevices.SelectedIndexChanged += new System.EventHandler(this.cbxLgDevices_SelectedIndexChanged); + // + // btnLgDeviceConvertToCustom + // + this.btnLgDeviceConvertToCustom.Location = new System.Drawing.Point(572, 7); + this.btnLgDeviceConvertToCustom.Name = "btnLgDeviceConvertToCustom"; + this.btnLgDeviceConvertToCustom.Size = new System.Drawing.Size(75, 23); + this.btnLgDeviceConvertToCustom.TabIndex = 41; + this.btnLgDeviceConvertToCustom.Text = "To custom..."; + this.btnLgDeviceConvertToCustom.UseVisualStyleBackColor = true; + this.btnLgDeviceConvertToCustom.Click += new System.EventHandler(this.btnLgDeviceConvertToCustom_Click); + // + // cbxLgApps + // + this.cbxLgApps.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.cbxLgApps.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbxLgApps.Enabled = false; + this.cbxLgApps.FormattingEnabled = true; + this.cbxLgApps.Location = new System.Drawing.Point(86, 346); + this.cbxLgApps.Name = "cbxLgApps"; + this.cbxLgApps.Size = new System.Drawing.Size(200, 21); + this.cbxLgApps.TabIndex = 26; + // + // lvLgPresets + // + this.lvLgPresets.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.lvLgPresets.FullRowSelect = true; + this.lvLgPresets.HideSelection = false; + this.lvLgPresets.Location = new System.Drawing.Point(5, 146); + this.lvLgPresets.MultiSelect = false; + this.lvLgPresets.Name = "lvLgPresets"; + this.lvLgPresets.Size = new System.Drawing.Size(758, 111); + this.lvLgPresets.TabIndex = 8; + this.lvLgPresets.UseCompatibleStateImageBehavior = false; + this.lvLgPresets.View = System.Windows.Forms.View.Details; + this.lvLgPresets.SelectedIndexChanged += new System.EventHandler(this.lvLgPresets_SelectedIndexChanged); + this.lvLgPresets.DoubleClick += new System.EventHandler(this.lvLgPresets_DoubleClick); + // + // label5 + // + this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(5, 349); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(29, 13); + this.label5.TabIndex = 25; + this.label5.Text = "App:"; + // + // cbxLgPresetDevice + // + this.cbxLgPresetDevice.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.cbxLgPresetDevice.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbxLgPresetDevice.Enabled = false; + this.cbxLgPresetDevice.FormattingEnabled = true; + this.cbxLgPresetDevice.Location = new System.Drawing.Point(86, 318); + this.cbxLgPresetDevice.Name = "cbxLgPresetDevice"; + this.cbxLgPresetDevice.Size = new System.Drawing.Size(318, 21); + this.cbxLgPresetDevice.TabIndex = 40; + // + // btnDeleteLg + // + this.btnDeleteLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnDeleteLg.Enabled = false; + this.btnDeleteLg.Location = new System.Drawing.Point(248, 263); + this.btnDeleteLg.Name = "btnDeleteLg"; + this.btnDeleteLg.Size = new System.Drawing.Size(75, 23); + this.btnDeleteLg.TabIndex = 27; + this.btnDeleteLg.Text = "Delete"; + this.btnDeleteLg.UseVisualStyleBackColor = true; + this.btnDeleteLg.Click += new System.EventHandler(this.btnDeleteLg_Click); + // + // btnApplyLg + // + this.btnApplyLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnApplyLg.Enabled = false; + this.btnApplyLg.Location = new System.Drawing.Point(5, 263); + this.btnApplyLg.Name = "btnApplyLg"; + this.btnApplyLg.Size = new System.Drawing.Size(75, 23); + this.btnApplyLg.TabIndex = 9; + this.btnApplyLg.Text = "Apply"; + this.btnApplyLg.UseVisualStyleBackColor = true; + this.btnApplyLg.Click += new System.EventHandler(this.btnApplyLg_Click); + // + // lblDeviceFilter + // + this.lblDeviceFilter.AutoSize = true; + this.lblDeviceFilter.Location = new System.Drawing.Point(5, 39); + this.lblDeviceFilter.Name = "lblDeviceFilter"; + this.lblDeviceFilter.Size = new System.Drawing.Size(66, 13); + this.lblDeviceFilter.TabIndex = 24; + this.lblDeviceFilter.Text = "Device filter:"; + // + // lblLgPresetDevice + // + this.lblLgPresetDevice.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.lblLgPresetDevice.AutoSize = true; + this.lblLgPresetDevice.Location = new System.Drawing.Point(5, 321); + this.lblLgPresetDevice.Name = "lblLgPresetDevice"; + this.lblLgPresetDevice.Size = new System.Drawing.Size(44, 13); + this.lblLgPresetDevice.TabIndex = 39; + this.lblLgPresetDevice.Text = "Device:"; + // + // btnAddLg + // + this.btnAddLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnAddLg.Location = new System.Drawing.Point(167, 263); + this.btnAddLg.Name = "btnAddLg"; + this.btnAddLg.Size = new System.Drawing.Size(75, 23); + this.btnAddLg.TabIndex = 28; + this.btnAddLg.Text = "Add"; + this.btnAddLg.UseVisualStyleBackColor = true; + this.btnAddLg.Click += new System.EventHandler(this.btnAddLg_Click); + // + // edtShortcutLg + // + this.edtShortcutLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.edtShortcutLg.Enabled = false; + this.edtShortcutLg.Location = new System.Drawing.Point(86, 373); + this.edtShortcutLg.Name = "edtShortcutLg"; + this.edtShortcutLg.ReadOnly = true; + this.edtShortcutLg.Size = new System.Drawing.Size(200, 20); + this.edtShortcutLg.TabIndex = 11; + this.edtShortcutLg.TextChanged += new System.EventHandler(this.edtShortcutLg_TextChanged); + this.edtShortcutLg.KeyDown += new System.Windows.Forms.KeyEventHandler(this.edtShortcut_KeyDown); + this.edtShortcutLg.KeyUp += new System.Windows.Forms.KeyEventHandler(this.edtShortcut_KeyUp); + // + // edtLgDeviceFilter + // + this.edtLgDeviceFilter.Location = new System.Drawing.Point(86, 35); + this.edtLgDeviceFilter.Name = "edtLgDeviceFilter"; + this.edtLgDeviceFilter.Size = new System.Drawing.Size(142, 20); + this.edtLgDeviceFilter.TabIndex = 23; + this.edtLgDeviceFilter.TextChanged += new System.EventHandler(this.edtLgDeviceFilter_TextChanged); + // + // btnLgRemoveDevice + // + this.btnLgRemoveDevice.Location = new System.Drawing.Point(491, 7); + this.btnLgRemoveDevice.Name = "btnLgRemoveDevice"; + this.btnLgRemoveDevice.Size = new System.Drawing.Size(75, 23); + this.btnLgRemoveDevice.TabIndex = 38; + this.btnLgRemoveDevice.Text = "Remove"; + this.btnLgRemoveDevice.UseVisualStyleBackColor = true; + this.btnLgRemoveDevice.Click += new System.EventHandler(this.btnLgRemoveDevice_Click); + // + // btnLgRefreshApps + // + this.btnLgRefreshApps.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnLgRefreshApps.Location = new System.Drawing.Point(292, 344); + this.btnLgRefreshApps.Name = "btnLgRefreshApps"; + this.btnLgRefreshApps.Size = new System.Drawing.Size(75, 23); + this.btnLgRefreshApps.TabIndex = 29; + this.btnLgRefreshApps.Text = "Refresh"; + this.btnLgRefreshApps.UseVisualStyleBackColor = true; + this.btnLgRefreshApps.Click += new System.EventHandler(this.btnLgRefreshApps_Click); + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(5, 376); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(50, 13); + this.label2.TabIndex = 12; + this.label2.Text = "Shortcut:"; + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(5, 295); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(38, 13); + this.label1.TabIndex = 20; + this.label1.Text = "Name:"; + // + // btnLgAddDevice + // + this.btnLgAddDevice.Location = new System.Drawing.Point(410, 7); + this.btnLgAddDevice.Name = "btnLgAddDevice"; + this.btnLgAddDevice.Size = new System.Drawing.Size(75, 23); + this.btnLgAddDevice.TabIndex = 37; + this.btnLgAddDevice.Text = "Add"; + this.btnLgAddDevice.UseVisualStyleBackColor = true; + this.btnLgAddDevice.Click += new System.EventHandler(this.btnLgAddDevice_Click); + // + // edtNameLg + // + this.edtNameLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.edtNameLg.Enabled = false; + this.edtNameLg.Location = new System.Drawing.Point(86, 292); + this.edtNameLg.Name = "edtNameLg"; + this.edtNameLg.Size = new System.Drawing.Size(200, 20); + this.edtNameLg.TabIndex = 19; + // + // btnSetShortcutLg + // + this.btnSetShortcutLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnSetShortcutLg.Enabled = false; + this.btnSetShortcutLg.Location = new System.Drawing.Point(329, 263); + this.btnSetShortcutLg.Name = "btnSetShortcutLg"; + this.btnSetShortcutLg.Size = new System.Drawing.Size(75, 23); + this.btnSetShortcutLg.TabIndex = 13; + this.btnSetShortcutLg.Text = "Save"; + this.btnSetShortcutLg.UseVisualStyleBackColor = true; + this.btnSetShortcutLg.Click += new System.EventHandler(this.btnSetShortcutLg_Click); + // + // edtStepsLg + // + this.edtStepsLg.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.edtStepsLg.Enabled = false; + this.edtStepsLg.Location = new System.Drawing.Point(86, 398); + this.edtStepsLg.Multiline = true; + this.edtStepsLg.Name = "edtStepsLg"; + this.edtStepsLg.Size = new System.Drawing.Size(596, 38); + this.edtStepsLg.TabIndex = 17; + // + // btnLgAddButton + // + this.btnLgAddButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnLgAddButton.ContextMenuStrip = this.mnuLgButtons; + this.btnLgAddButton.Enabled = false; + this.btnLgAddButton.Location = new System.Drawing.Point(688, 397); + this.btnLgAddButton.Name = "btnLgAddButton"; + this.btnLgAddButton.Size = new System.Drawing.Size(75, 23); + this.btnLgAddButton.TabIndex = 32; + this.btnLgAddButton.Text = "Add step"; + this.btnLgAddButton.UseVisualStyleBackColor = true; + this.btnLgAddButton.Click += new System.EventHandler(this.btnLgAddButton_Click); + // + // mnuLgButtons + // + this.mnuLgButtons.ImageScalingSize = new System.Drawing.Size(20, 20); + this.mnuLgButtons.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuLgRcButtons, + this.mnuLgActions}); + this.mnuLgButtons.Name = "mnuLgButtons"; + this.mnuLgButtons.Size = new System.Drawing.Size(116, 48); + this.mnuLgButtons.Opening += new System.ComponentModel.CancelEventHandler(this.mnuLgButtons_Opening); + // + // mnuLgRcButtons + // + this.mnuLgRcButtons.Name = "mnuLgRcButtons"; + this.mnuLgRcButtons.Size = new System.Drawing.Size(115, 22); + this.mnuLgRcButtons.Text = "Buttons"; + // + // mnuLgActions + // + this.mnuLgActions.Name = "mnuLgActions"; + this.mnuLgActions.Size = new System.Drawing.Size(115, 22); + this.mnuLgActions.Text = "Actions"; + // + // btnCloneLg + // + this.btnCloneLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnCloneLg.Enabled = false; + this.btnCloneLg.Location = new System.Drawing.Point(86, 263); + this.btnCloneLg.Name = "btnCloneLg"; + this.btnCloneLg.Size = new System.Drawing.Size(75, 23); + this.btnCloneLg.TabIndex = 14; + this.btnCloneLg.Text = "Clone"; + this.btnCloneLg.UseVisualStyleBackColor = true; + this.btnCloneLg.Click += new System.EventHandler(this.btnCloneLg_Click); + // + // lblStepsLg + // + this.lblStepsLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.lblStepsLg.AutoSize = true; + this.lblStepsLg.Location = new System.Drawing.Point(5, 402); + this.lblStepsLg.Name = "lblStepsLg"; + this.lblStepsLg.Size = new System.Drawing.Size(37, 13); + this.lblStepsLg.TabIndex = 16; + this.lblStepsLg.Text = "Steps:"; // // btnLgDeviceFilterRefresh // - this.btnLgDeviceFilterRefresh.Location = new System.Drawing.Point(594, 5); + this.btnLgDeviceFilterRefresh.Location = new System.Drawing.Point(234, 34); this.btnLgDeviceFilterRefresh.Name = "btnLgDeviceFilterRefresh"; this.btnLgDeviceFilterRefresh.Size = new System.Drawing.Size(75, 23); this.btnLgDeviceFilterRefresh.TabIndex = 35; @@ -959,270 +1292,29 @@ this.clbLgPower.Items.AddRange(new object[] { "Automatically power on after startup. Requires \"Automatically start after login\" " + "- see Options.", - "Automatically power on after resume from standby. Needs a system dependent delay " + - "before waking TV, see Options.", + "Automatically power on after resume from standby. May need some retries for wakin" + + "g TV, see Options.", "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 screensaver and power on when screensaver deactivates"}); - this.clbLgPower.Location = new System.Drawing.Point(6, 33); + this.clbLgPower.Location = new System.Drawing.Point(5, 61); this.clbLgPower.Name = "clbLgPower"; - this.clbLgPower.Size = new System.Drawing.Size(829, 79); + this.clbLgPower.Size = new System.Drawing.Size(758, 79); this.clbLgPower.TabIndex = 34; this.clbLgPower.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.clbLgPower_ItemCheck); // - // btnLgAddButton - // - this.btnLgAddButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.btnLgAddButton.ContextMenuStrip = this.mnuLgButtons; - this.btnLgAddButton.Enabled = false; - this.btnLgAddButton.Location = new System.Drawing.Point(760, 400); - this.btnLgAddButton.Name = "btnLgAddButton"; - this.btnLgAddButton.Size = new System.Drawing.Size(75, 23); - this.btnLgAddButton.TabIndex = 32; - this.btnLgAddButton.Text = "Add step"; - this.btnLgAddButton.UseVisualStyleBackColor = true; - this.btnLgAddButton.Click += new System.EventHandler(this.btnLgAddButton_Click); - // - // mnuLgButtons - // - this.mnuLgButtons.ImageScalingSize = new System.Drawing.Size(20, 20); - this.mnuLgButtons.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.mnuLgRcButtons, - this.mnuLgActions}); - this.mnuLgButtons.Name = "mnuLgButtons"; - this.mnuLgButtons.Size = new System.Drawing.Size(116, 48); - // - // mnuLgRcButtons - // - this.mnuLgRcButtons.Name = "mnuLgRcButtons"; - this.mnuLgRcButtons.Size = new System.Drawing.Size(115, 22); - this.mnuLgRcButtons.Text = "Buttons"; - // - // mnuLgActions - // - this.mnuLgActions.Name = "mnuLgActions"; - this.mnuLgActions.Size = new System.Drawing.Size(115, 22); - this.mnuLgActions.Text = "Actions"; - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(6, 10); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(44, 13); - this.label3.TabIndex = 31; - this.label3.Text = "Device:"; - // - // cbxLgDevices - // - this.cbxLgDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cbxLgDevices.FormattingEnabled = true; - this.cbxLgDevices.Location = new System.Drawing.Point(87, 6); - this.cbxLgDevices.Name = "cbxLgDevices"; - this.cbxLgDevices.Size = new System.Drawing.Size(281, 21); - this.cbxLgDevices.TabIndex = 30; - this.cbxLgDevices.SelectedIndexChanged += new System.EventHandler(this.cbxLgDevices_SelectedIndexChanged); - // - // btnLgRefreshApps - // - this.btnLgRefreshApps.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.btnLgRefreshApps.Location = new System.Drawing.Point(293, 347); - this.btnLgRefreshApps.Name = "btnLgRefreshApps"; - this.btnLgRefreshApps.Size = new System.Drawing.Size(75, 23); - this.btnLgRefreshApps.TabIndex = 29; - this.btnLgRefreshApps.Text = "Refresh"; - this.btnLgRefreshApps.UseVisualStyleBackColor = true; - this.btnLgRefreshApps.Click += new System.EventHandler(this.btnLgRefreshApps_Click); - // - // btnAddLg - // - this.btnAddLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.btnAddLg.Location = new System.Drawing.Point(168, 294); - this.btnAddLg.Name = "btnAddLg"; - this.btnAddLg.Size = new System.Drawing.Size(75, 23); - this.btnAddLg.TabIndex = 28; - this.btnAddLg.Text = "Add"; - this.btnAddLg.UseVisualStyleBackColor = true; - this.btnAddLg.Click += new System.EventHandler(this.btnAddLg_Click); - // - // btnDeleteLg - // - this.btnDeleteLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.btnDeleteLg.Enabled = false; - this.btnDeleteLg.Location = new System.Drawing.Point(249, 294); - this.btnDeleteLg.Name = "btnDeleteLg"; - this.btnDeleteLg.Size = new System.Drawing.Size(75, 23); - this.btnDeleteLg.TabIndex = 27; - this.btnDeleteLg.Text = "Delete"; - this.btnDeleteLg.UseVisualStyleBackColor = true; - this.btnDeleteLg.Click += new System.EventHandler(this.btnDeleteLg_Click); - // - // cbxLgApps - // - this.cbxLgApps.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.cbxLgApps.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cbxLgApps.Enabled = false; - this.cbxLgApps.FormattingEnabled = true; - this.cbxLgApps.Location = new System.Drawing.Point(87, 349); - this.cbxLgApps.Name = "cbxLgApps"; - this.cbxLgApps.Size = new System.Drawing.Size(200, 21); - this.cbxLgApps.TabIndex = 26; - // - // label5 - // - this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(6, 352); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(29, 13); - this.label5.TabIndex = 25; - this.label5.Text = "App:"; - // - // lblDeviceFilter - // - this.lblDeviceFilter.AutoSize = true; - this.lblDeviceFilter.Location = new System.Drawing.Point(374, 10); - this.lblDeviceFilter.Name = "lblDeviceFilter"; - this.lblDeviceFilter.Size = new System.Drawing.Size(66, 13); - this.lblDeviceFilter.TabIndex = 24; - this.lblDeviceFilter.Text = "Device filter:"; - // - // edtLgDeviceFilter - // - this.edtLgDeviceFilter.Location = new System.Drawing.Point(446, 6); - this.edtLgDeviceFilter.Name = "edtLgDeviceFilter"; - this.edtLgDeviceFilter.Size = new System.Drawing.Size(142, 20); - this.edtLgDeviceFilter.TabIndex = 23; - this.edtLgDeviceFilter.TextChanged += new System.EventHandler(this.edtLgDeviceFilter_TextChanged); - // - // label1 - // - this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(6, 326); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(38, 13); - this.label1.TabIndex = 20; - this.label1.Text = "Name:"; - // - // edtNameLg - // - this.edtNameLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.edtNameLg.Enabled = false; - this.edtNameLg.Location = new System.Drawing.Point(87, 323); - this.edtNameLg.Name = "edtNameLg"; - this.edtNameLg.Size = new System.Drawing.Size(200, 20); - this.edtNameLg.TabIndex = 19; - // - // edtStepsLg - // - this.edtStepsLg.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.edtStepsLg.Enabled = false; - this.edtStepsLg.Location = new System.Drawing.Point(87, 402); - this.edtStepsLg.Multiline = true; - this.edtStepsLg.Name = "edtStepsLg"; - this.edtStepsLg.Size = new System.Drawing.Size(667, 37); - this.edtStepsLg.TabIndex = 17; - // - // lblStepsLg - // - this.lblStepsLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.lblStepsLg.AutoSize = true; - this.lblStepsLg.Location = new System.Drawing.Point(6, 405); - this.lblStepsLg.Name = "lblStepsLg"; - this.lblStepsLg.Size = new System.Drawing.Size(37, 13); - this.lblStepsLg.TabIndex = 16; - this.lblStepsLg.Text = "Steps:"; - // // lblLgError // this.lblLgError.AutoSize = true; - this.lblLgError.Location = new System.Drawing.Point(6, 6); + this.lblLgError.Location = new System.Drawing.Point(5, 8); this.lblLgError.Name = "lblLgError"; this.lblLgError.Size = new System.Drawing.Size(50, 13); this.lblLgError.TabIndex = 15; this.lblLgError.Text = "ErrorText"; this.lblLgError.Visible = false; // - // btnCloneLg - // - this.btnCloneLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.btnCloneLg.Enabled = false; - this.btnCloneLg.Location = new System.Drawing.Point(87, 294); - this.btnCloneLg.Name = "btnCloneLg"; - this.btnCloneLg.Size = new System.Drawing.Size(75, 23); - this.btnCloneLg.TabIndex = 14; - this.btnCloneLg.Text = "Clone"; - this.btnCloneLg.UseVisualStyleBackColor = true; - this.btnCloneLg.Click += new System.EventHandler(this.btnCloneLg_Click); - // - // btnSetShortcutLg - // - this.btnSetShortcutLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.btnSetShortcutLg.Enabled = false; - this.btnSetShortcutLg.Location = new System.Drawing.Point(330, 294); - this.btnSetShortcutLg.Name = "btnSetShortcutLg"; - this.btnSetShortcutLg.Size = new System.Drawing.Size(75, 23); - this.btnSetShortcutLg.TabIndex = 13; - this.btnSetShortcutLg.Text = "Save"; - this.btnSetShortcutLg.UseVisualStyleBackColor = true; - this.btnSetShortcutLg.Click += new System.EventHandler(this.btnSetShortcutLg_Click); - // - // label2 - // - this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(6, 379); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(50, 13); - this.label2.TabIndex = 12; - this.label2.Text = "Shortcut:"; - // - // edtShortcutLg - // - this.edtShortcutLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.edtShortcutLg.Enabled = false; - this.edtShortcutLg.Location = new System.Drawing.Point(87, 376); - this.edtShortcutLg.Name = "edtShortcutLg"; - this.edtShortcutLg.ReadOnly = true; - this.edtShortcutLg.Size = new System.Drawing.Size(200, 20); - this.edtShortcutLg.TabIndex = 11; - this.edtShortcutLg.TextChanged += new System.EventHandler(this.edtShortcutLg_TextChanged); - this.edtShortcutLg.KeyDown += new System.Windows.Forms.KeyEventHandler(this.edtShortcut_KeyDown); - this.edtShortcutLg.KeyUp += new System.Windows.Forms.KeyEventHandler(this.edtShortcut_KeyUp); - // - // btnApplyLg - // - this.btnApplyLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.btnApplyLg.Enabled = false; - this.btnApplyLg.Location = new System.Drawing.Point(6, 294); - this.btnApplyLg.Name = "btnApplyLg"; - this.btnApplyLg.Size = new System.Drawing.Size(75, 23); - this.btnApplyLg.TabIndex = 9; - this.btnApplyLg.Text = "Apply"; - this.btnApplyLg.UseVisualStyleBackColor = true; - this.btnApplyLg.Click += new System.EventHandler(this.btnApplyLg_Click); - // - // lvLgPresets - // - this.lvLgPresets.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.lvLgPresets.FullRowSelect = true; - this.lvLgPresets.HideSelection = false; - 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, 172); - this.lvLgPresets.TabIndex = 8; - this.lvLgPresets.UseCompatibleStateImageBehavior = false; - this.lvLgPresets.View = System.Windows.Forms.View.Details; - this.lvLgPresets.SelectedIndexChanged += new System.EventHandler(this.lvLgPresets_SelectedIndexChanged); - this.lvLgPresets.DoubleClick += new System.EventHandler(this.lvLgPresets_DoubleClick); - // // tabOptions // this.tabOptions.Controls.Add(this.grpNvidiaOptions); @@ -1233,7 +1325,7 @@ this.tabOptions.Location = new System.Drawing.Point(4, 22); this.tabOptions.Name = "tabOptions"; this.tabOptions.Padding = new System.Windows.Forms.Padding(3); - this.tabOptions.Size = new System.Drawing.Size(841, 446); + this.tabOptions.Size = new System.Drawing.Size(954, 453); this.tabOptions.TabIndex = 2; this.tabOptions.Text = "Options"; this.tabOptions.UseVisualStyleBackColor = true; @@ -1251,7 +1343,7 @@ this.grpNvidiaOptions.Controls.Add(this.pbGradient); this.grpNvidiaOptions.Location = new System.Drawing.Point(412, 6); this.grpNvidiaOptions.Name = "grpNvidiaOptions"; - this.grpNvidiaOptions.Size = new System.Drawing.Size(400, 297); + this.grpNvidiaOptions.Size = new System.Drawing.Size(514, 304); this.grpNvidiaOptions.TabIndex = 6; this.grpNvidiaOptions.TabStop = false; this.grpNvidiaOptions.Text = "NVIDIA options - test dithering"; @@ -1315,7 +1407,7 @@ this.pbGradient.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.pbGradient.Location = new System.Drawing.Point(6, 92); this.pbGradient.Name = "pbGradient"; - this.pbGradient.Size = new System.Drawing.Size(388, 198); + this.pbGradient.Size = new System.Drawing.Size(502, 205); this.pbGradient.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.pbGradient.TabIndex = 0; this.pbGradient.TabStop = false; @@ -1555,7 +1647,7 @@ this.tabLog.Location = new System.Drawing.Point(4, 22); this.tabLog.Name = "tabLog"; this.tabLog.Padding = new System.Windows.Forms.Padding(3); - this.tabLog.Size = new System.Drawing.Size(841, 446); + this.tabLog.Size = new System.Drawing.Size(954, 453); this.tabLog.TabIndex = 3; this.tabLog.Text = "Log"; this.tabLog.UseVisualStyleBackColor = true; @@ -1580,7 +1672,7 @@ this.edtLog.Name = "edtLog"; this.edtLog.ReadOnly = true; this.edtLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.edtLog.Size = new System.Drawing.Size(832, 408); + this.edtLog.Size = new System.Drawing.Size(920, 415); this.edtLog.TabIndex = 0; // // tabInfo @@ -1590,7 +1682,7 @@ this.tabInfo.Location = new System.Drawing.Point(4, 22); this.tabInfo.Name = "tabInfo"; this.tabInfo.Padding = new System.Windows.Forms.Padding(3); - this.tabInfo.Size = new System.Drawing.Size(841, 446); + this.tabInfo.Size = new System.Drawing.Size(954, 453); this.tabInfo.TabIndex = 4; this.tabInfo.Text = "Info"; this.tabInfo.UseVisualStyleBackColor = true; @@ -1604,7 +1696,7 @@ this.grpNVIDIAInfo.Controls.Add(this.tvNVIDIAInfo); this.grpNVIDIAInfo.Location = new System.Drawing.Point(397, 6); this.grpNVIDIAInfo.Name = "grpNVIDIAInfo"; - this.grpNVIDIAInfo.Size = new System.Drawing.Size(440, 436); + this.grpNVIDIAInfo.Size = new System.Drawing.Size(477, 443); this.grpNVIDIAInfo.TabIndex = 4; this.grpNVIDIAInfo.TabStop = false; this.grpNVIDIAInfo.Text = "NVIDIA info"; @@ -1612,7 +1704,7 @@ // btnRefreshNVIDIAInfo // this.btnRefreshNVIDIAInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.btnRefreshNVIDIAInfo.Location = new System.Drawing.Point(6, 407); + this.btnRefreshNVIDIAInfo.Location = new System.Drawing.Point(6, 414); this.btnRefreshNVIDIAInfo.Name = "btnRefreshNVIDIAInfo"; this.btnRefreshNVIDIAInfo.Size = new System.Drawing.Size(75, 23); this.btnRefreshNVIDIAInfo.TabIndex = 1; @@ -1627,7 +1719,7 @@ | System.Windows.Forms.AnchorStyles.Right))); this.tvNVIDIAInfo.Location = new System.Drawing.Point(6, 19); this.tvNVIDIAInfo.Name = "tvNVIDIAInfo"; - this.tvNVIDIAInfo.Size = new System.Drawing.Size(428, 382); + this.tvNVIDIAInfo.Size = new System.Drawing.Size(465, 389); this.tvNVIDIAInfo.TabIndex = 0; // // groupBox3 @@ -1669,19 +1761,12 @@ this.lblInfo.TabIndex = 1; this.lblInfo.Text = "Info"; // - // miAmdCopyId - // - this.miAmdCopyId.Name = "miAmdCopyId"; - this.miAmdCopyId.Size = new System.Drawing.Size(184, 22); - this.miAmdCopyId.Text = "Copy Id to Clipboard"; - this.miAmdCopyId.Click += new System.EventHandler(this.miAmdCopyId_Click); - // // MainForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.SystemColors.Control; - this.ClientSize = new System.Drawing.Size(873, 496); + this.ClientSize = new System.Drawing.Size(986, 503); this.Controls.Add(this.tcMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MinimumSize = new System.Drawing.Size(800, 535); @@ -1701,7 +1786,10 @@ this.tabAMD.PerformLayout(); this.mnuAmdPresets.ResumeLayout(false); this.tabLG.ResumeLayout(false); - this.tabLG.PerformLayout(); + this.scLgController.Panel1.ResumeLayout(false); + this.scLgController.Panel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.scLgController)).EndInit(); + this.scLgController.ResumeLayout(false); this.mnuLgButtons.ResumeLayout(false); this.tabOptions.ResumeLayout(false); this.grpNvidiaOptions.ResumeLayout(false); @@ -1750,27 +1838,10 @@ private System.Windows.Forms.ToolStripMenuItem miToggleHDR; private System.Windows.Forms.ToolStripMenuItem miHDREnabled; private System.Windows.Forms.Label lblError; - private System.Windows.Forms.Label lblLgError; - private System.Windows.Forms.Button btnCloneLg; - private System.Windows.Forms.Button btnSetShortcutLg; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.TextBox edtShortcutLg; - private System.Windows.Forms.Button btnApplyLg; - private System.Windows.Forms.ListView lvLgPresets; - private System.Windows.Forms.TextBox edtStepsLg; - private System.Windows.Forms.Label lblStepsLg; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.TextBox edtNameLg; private System.Windows.Forms.Button btnNvPresetDelete; private System.Windows.Forms.Button btnAddModesNv; - private System.Windows.Forms.Label lblDeviceFilter; - private System.Windows.Forms.TextBox edtLgDeviceFilter; - private System.Windows.Forms.ComboBox cbxLgApps; - private System.Windows.Forms.Label label5; private System.Windows.Forms.ToolStripMenuItem miNvApply; private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; - private System.Windows.Forms.Button btnDeleteLg; - private System.Windows.Forms.Button btnAddLg; private System.Windows.Forms.GroupBox grpGeneralOptions; private System.Windows.Forms.CheckBox chkStartMinimized; private System.Windows.Forms.CheckBox chkStartAfterLogin; @@ -1788,15 +1859,10 @@ private System.Windows.Forms.Label lblInfo; private System.Windows.Forms.GroupBox grpNVIDIAInfo; private System.Windows.Forms.TreeView tvNVIDIAInfo; - private System.Windows.Forms.Button btnLgRefreshApps; - private System.Windows.Forms.Label label3; - private System.Windows.Forms.ComboBox cbxLgDevices; private System.Windows.Forms.Button btnRefreshNVIDIAInfo; - private System.Windows.Forms.Button btnLgAddButton; private System.Windows.Forms.ContextMenuStrip mnuLgButtons; private System.Windows.Forms.ToolStripMenuItem mnuNvPresetsColorSettings; private System.Windows.Forms.ToolStripMenuItem miNvPresetColorSettings; - private System.Windows.Forms.CheckedListBox clbLgPower; private System.Windows.Forms.GroupBox grpLGOptions; private System.Windows.Forms.NumericUpDown edtLgMaxPowerOnRetries; private System.Windows.Forms.Label lblLgMaxPowerOnRetries; @@ -1813,7 +1879,6 @@ private System.Windows.Forms.TabPage tabAMD; private System.Windows.Forms.Label lblErrorAMD; private System.Windows.Forms.Button btnClearLog; - private System.Windows.Forms.Button btnLgDeviceFilterRefresh; private System.Windows.Forms.GroupBox grpNvidiaOptions; private System.Windows.Forms.PictureBox pbGradient; private System.Windows.Forms.Label lblDitheringMode; @@ -1831,7 +1896,6 @@ private System.Windows.Forms.ToolStripMenuItem spatialDynamic2x2ToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem spatialStatic2x2ToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem temporalToolStripMenuItem; - private System.Windows.Forms.Button btnLGRemoteControl; private System.Windows.Forms.ToolStripMenuItem miNvPresetApplyOnStartup; private System.Windows.Forms.CheckBox chkLgOldWolMechanism; private System.Windows.Forms.ToolStripMenuItem mnuLgRcButtons; @@ -1868,6 +1932,37 @@ private System.Windows.Forms.Label lblAmdPresetName; private System.Windows.Forms.TextBox edtAmdPresetName; private System.Windows.Forms.ToolStripMenuItem miAmdCopyId; + private System.Windows.Forms.SplitContainer scLgController; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.ComboBox cbxLgDevices; + private System.Windows.Forms.Button btnLgDeviceConvertToCustom; + private System.Windows.Forms.ComboBox cbxLgApps; + private System.Windows.Forms.ListView lvLgPresets; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.ComboBox cbxLgPresetDevice; + private System.Windows.Forms.Button btnDeleteLg; + private System.Windows.Forms.Button btnApplyLg; + private System.Windows.Forms.Label lblDeviceFilter; + private System.Windows.Forms.Label lblLgPresetDevice; + private System.Windows.Forms.Button btnAddLg; + private System.Windows.Forms.TextBox edtShortcutLg; + private System.Windows.Forms.TextBox edtLgDeviceFilter; + private System.Windows.Forms.Button btnLgRemoveDevice; + private System.Windows.Forms.Button btnLgRefreshApps; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button btnLgAddDevice; + private System.Windows.Forms.TextBox edtNameLg; + private System.Windows.Forms.Button btnSetShortcutLg; + private System.Windows.Forms.TextBox edtStepsLg; + private System.Windows.Forms.Button btnLgAddButton; + private System.Windows.Forms.Button btnCloneLg; + private System.Windows.Forms.Label lblStepsLg; + private System.Windows.Forms.Button btnLgDeviceFilterRefresh; + private System.Windows.Forms.CheckedListBox clbLgPower; + private System.Windows.Forms.Label lblLgError; + private System.Windows.Forms.CheckBox chkLgRemoteControlShow; + private System.ComponentModel.BackgroundWorker backgroundWorker1; } } diff --git a/ColorControl/MainForm.cs b/ColorControl/MainForm.cs index ae9a98e..4d2cfeb 100644 --- a/ColorControl/MainForm.cs +++ b/ColorControl/MainForm.cs @@ -37,12 +37,12 @@ namespace ColorControl private NotifyIcon _trayIcon; private bool _initialized = false; + private bool _disableEvents = false; private Config _config; private bool _setVisibleCalled = false; private RestartDetector _restartDetector; private LgService _lgService; - private RemoteControlForm _remoteControlForm; private MenuItem _nvTrayMenu; private MenuItem _amdTrayMenu; @@ -149,11 +149,6 @@ namespace ColorControl FillLgPresets(); - clbLgPower.SetItemChecked(0, _lgService.Config.PowerOnAfterStartup); - 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; @@ -171,15 +166,6 @@ namespace ColorControl item.Click += miLgAddButton_Click; } - var actions = _lgService.GetInvokableActions(); - foreach (var keyValuePair in actions) - { - var text = keyValuePair.Key; - - var item = mnuLgActions.DropDownItems.Add(text); - item.Click += miLgAddAction_Click; - } - _lgService.InstallEventHandlers(); } catch (Exception e) @@ -403,7 +389,7 @@ namespace ColorControl GlobalSave(); - if (SystemShutdown && _lgService.Config.PowerOffOnShutdown) + if (SystemShutdown && _lgService.Devices.Any(d => d.PowerOffOnShutdown)) { Logger.Debug($"MainForm_FormClosing: SystemShutdown"); @@ -418,7 +404,7 @@ namespace ColorControl else { Logger.Debug("Powering off tv..."); - var task = _lgService.PowerOff(); + var task = _lgService.PowerOffOnShutdownOrResume(); Utils.WaitForTask(task); Logger.Debug("Done powering off tv"); //ExecPowerOffPreset(true); @@ -1049,6 +1035,7 @@ namespace ColorControl btnApplyLg.Enabled = enabled; btnCloneLg.Enabled = enabled; edtNameLg.Enabled = enabled; + cbxLgPresetDevice.Enabled = enabled; edtShortcutLg.Enabled = enabled; btnSetShortcutLg.Enabled = enabled; edtStepsLg.Enabled = enabled; @@ -1065,6 +1052,20 @@ namespace ColorControl cbxLgApps.SelectedIndex = lgApps == null ? -1 : lgApps.FindIndex(x => x.appId.Equals(preset.appId)); edtShortcutLg.Text = preset.shortcut; edtStepsLg.Text = preset.steps.Aggregate("", (a, b) => (string.IsNullOrEmpty(a) ? "" : a + ", ") + b); + + var index = -1; + + for (var i = 0; i < cbxLgPresetDevice.Items.Count; i++) + { + var pnpDev = (LgDevice)cbxLgPresetDevice.Items[i]; + if ((string.IsNullOrEmpty(preset.DeviceMacAddress) && string.IsNullOrEmpty(pnpDev.MacAddress)) || pnpDev.MacAddress.Equals(preset.DeviceMacAddress, StringComparison.OrdinalIgnoreCase)) + { + index = i; + break; + } + } + + cbxLgPresetDevice.SelectedIndex = index; } else { @@ -1072,6 +1073,7 @@ namespace ColorControl cbxLgApps.SelectedIndex = -1; edtShortcutLg.Text = string.Empty; edtStepsLg.Text = string.Empty; + cbxLgPresetDevice.SelectedIndex = -1; } } @@ -1105,6 +1107,16 @@ namespace ColorControl preset.name = name; + if (cbxLgPresetDevice.SelectedIndex == -1) + { + preset.DeviceMacAddress = string.Empty; + } + else + { + var device = (LgDevice)cbxLgPresetDevice.SelectedItem; + preset.DeviceMacAddress = device.MacAddress; + } + if (cbxLgApps.SelectedIndex == -1) { preset.appId = string.Empty; @@ -1199,6 +1211,15 @@ namespace ColorControl { FillLgDevices(); } + + if (scLgController.Panel2.Controls.Count == 0) + { + var rcPanel = new RemoteControlPanel(_lgService, _lgService.GetRemoteControlButtons()); + rcPanel.Parent = scLgController.Panel2; + rcPanel.Dock = DockStyle.Fill; + } + chkLgRemoteControlShow.Checked = _lgService.Config.ShowRemoteControl; + scLgController.Panel2Collapsed = !_lgService.Config.ShowRemoteControl; } } else if (tcMain.SelectedTab == tabLog) @@ -1236,15 +1257,21 @@ namespace ColorControl private void FillLgDevices() { - cbxLgDevices.Items.Clear(); var devices = _lgService.Devices; - foreach (var device in devices) + + cbxLgDevices.Items.Clear(); + cbxLgDevices.Items.AddRange(devices.ToArray()); + + cbxLgPresetDevice.Items.Clear(); + cbxLgPresetDevice.Items.AddRange(devices.ToArray()); + var globalDevice = new LgDevice("Globally selected device", string.Empty, string.Empty, true, true); + cbxLgPresetDevice.Items.Insert(0, globalDevice); + + var device = _lgService.SelectedDevice; + + if (device != null) { - cbxLgDevices.Items.Add(device); - } - if (_lgService.SelectedDevice != null) - { - cbxLgDevices.SelectedIndex = cbxLgDevices.Items.IndexOf(_lgService.SelectedDevice); + cbxLgDevices.SelectedIndex = cbxLgDevices.Items.IndexOf(device); } if (!devices.Any()) @@ -1252,10 +1279,32 @@ namespace ColorControl MessageForms.WarningOk("It seems there's no LG TV available! Please make sure it's connected to the same network as this PC."); } - if (cbxLgApps.Items.Count == 0 && _lgService.SelectedDevice != null) + if (cbxLgApps.Items.Count == 0 && device != null) { _lgService.RefreshApps().ContinueWith((task) => BeginInvoke(new Action(FillLgApps))); } + + SetLgDevicePowerOptions(); + } + + private void SetLgDevicePowerOptions() + { + var device = _lgService.SelectedDevice; + clbLgPower.Enabled = device != null; + + _disableEvents = true; + try + { + clbLgPower.SetItemChecked(0, device?.PowerOnAfterStartup ?? false); + clbLgPower.SetItemChecked(1, device?.PowerOnAfterResume ?? false); + clbLgPower.SetItemChecked(2, device?.PowerOffOnShutdown ?? false); + clbLgPower.SetItemChecked(3, device?.PowerOffOnStandby ?? false); + clbLgPower.SetItemChecked(4, device?.PowerSwitchOnScreenSaver ?? false); + } + finally + { + _disableEvents = false; + } } private void FillLgApps() @@ -1542,9 +1591,13 @@ namespace ColorControl } else { - _lgService.SelectedDevice = (PnpDev)cbxLgDevices.SelectedItem; + _lgService.SelectedDevice = (LgDevice)cbxLgDevices.SelectedItem; } - btnLGRemoteControl.Enabled = _lgService.SelectedDevice != null; + chkLgRemoteControlShow.Enabled = _lgService.SelectedDevice != null; + btnLgRemoveDevice.Enabled = _lgService.SelectedDevice != null && _lgService.SelectedDevice.IsCustom; + btnLgDeviceConvertToCustom.Enabled = _lgService.SelectedDevice != null && !_lgService.SelectedDevice.IsCustom; + + SetLgDevicePowerOptions(); } private void lvLgPresets_DoubleClick(object sender, EventArgs e) @@ -1620,12 +1673,18 @@ namespace ColorControl private void clbLgPower_ItemCheck(object sender, ItemCheckEventArgs e) { - if (!_initialized) + if (!_initialized || _disableEvents) { return; } - if (!(_lgService.Config.PowerOnAfterResume || _lgService.Config.PowerOnAfterStartup || _lgService.Config.PowerSwitchOnScreenSaver)) + var device = _lgService?.SelectedDevice; + if (device == null) + { + return; + } + + if (!(device.PowerOnAfterResume || device.PowerOnAfterStartup || device.PowerSwitchOnScreenSaver)) { MessageForms.InfoOk( @"Be sure to activate the following setting on the TV, or the app will not be able to wake the TV: @@ -1638,11 +1697,11 @@ See Options to test this functionality." BeginInvoke(new Action(() => { - _lgService.Config.PowerOnAfterStartup = clbLgPower.GetItemChecked(0); - _lgService.Config.PowerOnAfterResume = clbLgPower.GetItemChecked(1); - _lgService.Config.PowerOffOnShutdown = clbLgPower.GetItemChecked(2); - _lgService.Config.PowerOffOnStandby = clbLgPower.GetItemChecked(3); - _lgService.Config.PowerSwitchOnScreenSaver = clbLgPower.GetItemChecked(4); + device.PowerOnAfterStartup = clbLgPower.GetItemChecked(0); + device.PowerOnAfterResume = clbLgPower.GetItemChecked(1); + device.PowerOffOnShutdown = clbLgPower.GetItemChecked(2); + device.PowerOffOnStandby = clbLgPower.GetItemChecked(3); + device.PowerSwitchOnScreenSaver = clbLgPower.GetItemChecked(4); _lgService.InstallEventHandlers(); })); @@ -1810,16 +1869,6 @@ Do you want to continue?"; private void btnLGRemoteControl_Click(object sender, EventArgs e) { - if (_remoteControlForm == null || _remoteControlForm.IsDisposed) - { - var buttons = _lgService.GetRemoteControlButtons(); - _remoteControlForm = new RemoteControlForm(_lgService, buttons); - _remoteControlForm.Show(); - } - else - { - _remoteControlForm.Show(); - } } private void AfterInitialized() @@ -2273,5 +2322,116 @@ Do you want to continue?"; Clipboard.SetText(preset.id.ToString()); } } + + private void btnLgAddDevice_Click(object sender, EventArgs e) + { + var values = MessageForms.ShowDialog("Add tv", new[] { "Name", "Ip-address", "MAC-address" }, ValidateAddDevice); + if (values.Any()) + { + var device = new LgDevice(values[0], values[1], values[2]); + + var form = MessageForms.ShowProgress("Connecting to device..."); + + var result = false; + Enabled = false; + try + { + var task = device.TestConnection(); + + result = Utils.WaitForTask(task); + } + finally + { + form.Close(); + Enabled = true; + } + + if (!result && MessageForms.QuestionYesNo("Unable to connect to the device. Are you sure you want to add it?") != DialogResult.Yes) + { + return; + } + + _lgService.AddCustomDevice(device); + + FillLgDevices(); + } + } + + private string ValidateAddDevice(List values) + { + if (values.Any(v => string.IsNullOrEmpty(v))) + { + return "Please fill in all the fields"; + } + + return null; + } + + private void btnLgRemoveDevice_Click(object sender, EventArgs e) + { + if (MessageForms.QuestionYesNo("Are you sure you want to remove this device?") != DialogResult.Yes) + { + return; + } + + var device = _lgService.SelectedDevice; + + if (device != null) + { + _lgService.RemoveCustomDevice(device); + + FillLgDevices(); + } + } + + private void mnuLgButtons_Opening(object sender, CancelEventArgs e) + { + mnuLgActions.DropDownItems.Clear(); + + var preset = GetSelectedLgPreset(); + + if (preset == null) + { + return; + } + + var device = _lgService.GetPresetDevice(preset); + + var actions = device?.GetInvokableActions(); + foreach (var keyValuePair in actions) + { + var text = keyValuePair.Key; + + var item = mnuLgActions.DropDownItems.Add(text); + item.Click += miLgAddAction_Click; + } + + } + + private void btnLgDeviceConvertToCustom_Click(object sender, EventArgs e) + { + if (MessageForms.QuestionYesNo( +@"This will convert the automatically detected device to a custom variant. +This means that the device will remain here even if it is not detected anymore. +Do you want to continue?" + ) != DialogResult.Yes) + { + return; + } + + _lgService.SelectedDevice.ConvertToCustom(); + + FillLgDevices(); + } + + private void btnLgRemoteControlShow_Click(object sender, EventArgs e) + { + } + + private void chkLgRemoteControlShow_CheckedChanged(object sender, EventArgs e) + { + scLgController.Panel2Collapsed = !chkLgRemoteControlShow.Checked; + _lgService.Config.ShowRemoteControl = chkLgRemoteControlShow.Checked; + } } } \ No newline at end of file diff --git a/ColorControl/MainForm.resx b/ColorControl/MainForm.resx index 3c2e711..e2818cd 100644 --- a/ColorControl/MainForm.resx +++ b/ColorControl/MainForm.resx @@ -126,6 +126,9 @@ 147, 17 + + 419, 17 + diff --git a/ColorControl/MessageForms.cs b/ColorControl/MessageForms.cs index d784622..7f176fc 100644 --- a/ColorControl/MessageForms.cs +++ b/ColorControl/MessageForms.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace ColorControl @@ -30,5 +29,91 @@ namespace ColorControl { return MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Information); } + + public static List ShowDialog(string caption, IEnumerable labels, Func, string> validateFunc = null) + { + var values = new List(); + + var prompt = new Form() + { + Width = 500, + Height = 90 + (labels.Count() * 55), + FormBorderStyle = FormBorderStyle.FixedDialog, + Text = caption, + StartPosition = FormStartPosition.CenterScreen + }; + + var top = 10; + var boxes = new List(); + + foreach (var label in labels) + { + var textLabel = new Label() { Left = 40, Top = top, Text = label }; + top += 25; + prompt.Controls.Add(textLabel); + + var textBox = new MaskedTextBox() { Left = 40, Top = top, Width = 400 }; + + if (label.Contains("Ip-address")) + { + textBox.Mask = "###.###.###.###"; + textBox.ValidatingType = typeof(System.Net.IPAddress); + textBox.Culture = CultureInfo.InvariantCulture; + } + else if (label.Contains("MAC-address")) + { + textBox.Mask = "AA-AA-AA-AA-AA-AA"; + textBox.Culture = CultureInfo.InvariantCulture; + } + + top += 30; + prompt.Controls.Add(textBox); + boxes.Add(textBox); + } + + var confirmation = new Button() { Text = "Add", Left = 365, Width = 75, Top = prompt.ClientRectangle.Height - 30 }; + confirmation.Click += (sender, e) => + { + values.AddRange(boxes.Select(b => b.Text.Trim())); + + if (validateFunc != null) + { + var message = validateFunc(values); + + if (!string.IsNullOrEmpty(message)) + { + WarningOk(message); + values.Clear(); + return; + } + } + prompt.Close(); + }; + prompt.Controls.Add(confirmation); + prompt.AcceptButton = confirmation; + + prompt.ShowDialog(MainForm); + + return values; + } + + public static Form ShowProgress(string caption) + { + var prompt = new Form() + { + Width = 300, + Height = 100, + FormBorderStyle = FormBorderStyle.FixedToolWindow, + Text = caption, + StartPosition = FormStartPosition.CenterScreen, + ControlBox = false + }; + var textLabel = new Label() { Left = 20, Top = 25, Text = caption, AutoSize = true }; + prompt.Controls.Add(textLabel); + + prompt.Show(MainForm); + + return prompt; + } } -} +} \ No newline at end of file diff --git a/ColorControl/PnpDev.cs b/ColorControl/PnpDev.cs index 7133ed4..759f142 100644 --- a/ColorControl/PnpDev.cs +++ b/ColorControl/PnpDev.cs @@ -1,11 +1,14 @@ -using Windows.Devices.Enumeration; +using Newtonsoft.Json; +using Windows.Devices.Enumeration; using Windows.Devices.Enumeration.Pnp; namespace ColorControl { class PnpDev { + [JsonIgnore] public DeviceInformation DevInfo { get; private set; } + [JsonIgnore] public PnpObject PnpObject { get; private set; } public string Name { get; private set; } @@ -13,18 +16,27 @@ namespace ColorControl public string MacAddress { get; private set; } - public PnpDev(DeviceInformation devInfo, PnpObject pnpObject, string name, string ipAddress, string macAddress) + [JsonConstructor] + public PnpDev(string name, string ipAddress, string macAddress) { - DevInfo = devInfo; - PnpObject = pnpObject; Name = name; IpAddress = ipAddress; MacAddress = macAddress; } + public PnpDev(DeviceInformation devInfo, PnpObject pnpObject, string name, string ipAddress, string macAddress) : this(name, ipAddress, macAddress) + { + DevInfo = devInfo; + PnpObject = pnpObject; + } public override string ToString() { - return Name + " (" + IpAddress + ")"; + return (IsCustom() ? "Custom: " : "Auto detect: ") + $"{Name}" + (!string.IsNullOrEmpty(IpAddress) ? $"({IpAddress})" : string.Empty); + } + + public bool IsCustom() + { + return DevInfo == null; } } } diff --git a/ColorControl/PresetBase.cs b/ColorControl/PresetBase.cs index 6376ac3..cd9fc4f 100644 --- a/ColorControl/PresetBase.cs +++ b/ColorControl/PresetBase.cs @@ -1,12 +1,33 @@ using System; using System.Collections.Generic; +using System.ComponentModel; namespace ColorControl { + [Flags] + enum PresetTrigger + { + [Description("Startup")] + Startup = 1, + [Description("Resume")] + Resume = 2, + [Description("Shutdown")] + Shutdown = 4, + [Description("Standby")] + Standby = 8, + [Description("Screensaver")] + Screensaver = 16, + [Description("Process start")] + ProcessStart = 32, + [Description("Process exit")] + ProcessExit = 64 + } + internal abstract class PresetBase { public string name { get; set; } public string shortcut { get; set; } + public PresetTrigger Triggers { get; set; } private int _id; diff --git a/ColorControl/Program.cs b/ColorControl/Program.cs index 887f5fd..a490f08 100644 --- a/ColorControl/Program.cs +++ b/ColorControl/Program.cs @@ -150,7 +150,7 @@ namespace ColorControl Console.WriteLine("--amdpreset : execute AMD-preset"); Console.WriteLine("--lgpreset : execute LG-preset"); Console.WriteLine("--help : displays this help info"); - Console.WriteLine("Options :"); + Console.WriteLine("Options:"); Console.WriteLine("--nogui: starts command from the command line and will not open GUI (is forced when GUI is already running)"); result = true; diff --git a/ColorControl/Properties/AssemblyInfo.cs b/ColorControl/Properties/AssemblyInfo.cs index a217925..c969fec 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("2.2.0.0")] -[assembly: AssemblyFileVersion("2.2.0.0")] +[assembly: AssemblyVersion("3.0.0.0")] +[assembly: AssemblyFileVersion("3.0.0.0")] diff --git a/ColorControl/Properties/Resources.Designer.cs b/ColorControl/Properties/Resources.Designer.cs index e406eea..378073f 100644 --- a/ColorControl/Properties/Resources.Designer.cs +++ b/ColorControl/Properties/Resources.Designer.cs @@ -8,10 +8,10 @@ // //------------------------------------------------------------------------------ -namespace ColorControl.Properties -{ - - +namespace ColorControl.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,53 +19,82 @@ namespace ColorControl.Properties // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ColorControl.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap LG_remote { + get { + object obj = ResourceManager.GetObject("LG_remote", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized string similar to Power;120;132 + ///1;106;228 + ///2;196;228 + ///3;286;228 + ///4;106;296 + ///5;196;296 + ///6;286;296 + ///7;106;362 + ///8;196;362 + ///9;286;362 + ///0;196;425 + ///Vol +;98;513 + ///Mute;198;513 + ///Channel +;298;513 + ///Vol -;98;622 + ///Speech;198;633 + ///Channel -;298;622 + ///Home;78;695 + ///Settings;318;695. + /// + internal static string RC_coords { + get { + return ResourceManager.GetString("RC_coords", resourceCulture); + } + } } } diff --git a/ColorControl/Properties/Resources.resx b/ColorControl/Properties/Resources.resx index af7dbeb..7bd5ff2 100644 --- a/ColorControl/Properties/Resources.resx +++ b/ColorControl/Properties/Resources.resx @@ -46,7 +46,7 @@ mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with - : System.Serialization.Formatters.Binary.BinaryFormatter + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 @@ -60,6 +60,7 @@ : and then encoded with base64 encoding. --> + @@ -68,9 +69,10 @@ - + + @@ -85,9 +87,10 @@ - + + @@ -109,9 +112,16 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\LG_remote.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\RC_coords.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + \ No newline at end of file diff --git a/ColorControl/RemoteControlForm.Designer.cs b/ColorControl/RemoteControlForm.Designer.cs index df69698..6718f69 100644 --- a/ColorControl/RemoteControlForm.Designer.cs +++ b/ColorControl/RemoteControlForm.Designer.cs @@ -29,21 +29,25 @@ namespace ColorControl /// private void InitializeComponent() { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RemoteControlForm)); this.flowPanel = new System.Windows.Forms.FlowLayoutPanel(); this.btnClose = new System.Windows.Forms.Button(); this.lblUC = new System.Windows.Forms.Label(); this.chkDirectMode = new System.Windows.Forms.CheckBox(); this.pnlMouse = new System.Windows.Forms.Panel(); this.lblPos = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); this.SuspendLayout(); // // flowPanel // this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.flowPanel.Location = new System.Drawing.Point(12, 16); + this.flowPanel.Location = new System.Drawing.Point(12, 185); this.flowPanel.Name = "flowPanel"; - this.flowPanel.Size = new System.Drawing.Size(787, 164); + this.flowPanel.Size = new System.Drawing.Size(817, 120); this.flowPanel.TabIndex = 0; this.flowPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RemoteControlForm_MouseDown); this.flowPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.flowPanel_MouseMove); @@ -56,7 +60,7 @@ namespace ColorControl this.btnClose.FlatAppearance.BorderSize = 0; this.btnClose.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btnClose.ForeColor = System.Drawing.Color.White; - this.btnClose.Location = new System.Drawing.Point(796, 0); + this.btnClose.Location = new System.Drawing.Point(826, 0); this.btnClose.Name = "btnClose"; this.btnClose.Size = new System.Drawing.Size(14, 23); this.btnClose.TabIndex = 1; @@ -68,7 +72,7 @@ namespace ColorControl // this.lblUC.AutoSize = true; this.lblUC.ForeColor = System.Drawing.Color.Maroon; - this.lblUC.Location = new System.Drawing.Point(130, 0); + this.lblUC.Location = new System.Drawing.Point(12, 0); this.lblUC.Name = "lblUC"; this.lblUC.Size = new System.Drawing.Size(100, 13); this.lblUC.TabIndex = 0; @@ -78,8 +82,8 @@ namespace ColorControl // this.chkDirectMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.chkDirectMode.AutoSize = true; - this.chkDirectMode.ForeColor = System.Drawing.Color.DarkGray; - this.chkDirectMode.Location = new System.Drawing.Point(12, 643); + this.chkDirectMode.ForeColor = System.Drawing.Color.White; + this.chkDirectMode.Location = new System.Drawing.Point(12, 718); this.chkDirectMode.Name = "chkDirectMode"; this.chkDirectMode.Size = new System.Drawing.Size(105, 17); this.chkDirectMode.TabIndex = 2; @@ -93,9 +97,9 @@ namespace ColorControl | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.pnlMouse.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.pnlMouse.Location = new System.Drawing.Point(12, 191); + this.pnlMouse.Location = new System.Drawing.Point(12, 311); this.pnlMouse.Name = "pnlMouse"; - this.pnlMouse.Size = new System.Drawing.Size(787, 446); + this.pnlMouse.Size = new System.Drawing.Size(817, 401); this.pnlMouse.TabIndex = 3; this.pnlMouse.MouseEnter += new System.EventHandler(this.pnlMouse_MouseEnter); this.pnlMouse.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pnlMouse_MouseMove); @@ -106,17 +110,53 @@ namespace ColorControl this.lblPos.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.lblPos.AutoSize = true; this.lblPos.ForeColor = System.Drawing.Color.Maroon; - this.lblPos.Location = new System.Drawing.Point(719, 643); + this.lblPos.Location = new System.Drawing.Point(749, 718); this.lblPos.Name = "lblPos"; this.lblPos.Size = new System.Drawing.Size(0, 13); this.lblPos.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.ForeColor = System.Drawing.Color.White; + this.label1.Location = new System.Drawing.Point(12, 26); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(538, 52); + this.label1.TabIndex = 4; + this.label1.Text = resources.GetString("label1.Text"); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.ForeColor = System.Drawing.Color.White; + this.label2.Location = new System.Drawing.Point(231, 91); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(216, 52); + this.label2.TabIndex = 5; + this.label2.Text = "Mouse:\r\n- left button: activate/open item\r\n- right button: go back\r\n- wheel: scro" + + "ll through menus (only vertically)"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.ForeColor = System.Drawing.Color.White; + this.label3.Location = new System.Drawing.Point(12, 91); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(180, 91); + this.label3.TabIndex = 6; + this.label3.Text = "Keyboard keys:\r\n- arrow keys: navigate the menu\r\n- backspace: go back\r\n- escape: " + + "exit\r\n- S: open picture settings\r\n- H: open Home\r\n- media keys: volume up/down/m" + + "ute"; // // RemoteControlForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.Black; - this.ClientSize = new System.Drawing.Size(811, 660); + this.ClientSize = new System.Drawing.Size(841, 735); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); this.Controls.Add(this.lblPos); this.Controls.Add(this.pnlMouse); this.Controls.Add(this.chkDirectMode); @@ -132,6 +172,7 @@ namespace ColorControl this.Text = "RemoteControlForm"; this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.RemoteControlForm_FormClosed); this.Load += new System.EventHandler(this.RemoteControlForm_Load); + this.Shown += new System.EventHandler(this.RemoteControlForm_Shown); this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.RemoteControlForm_KeyDown); this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.RemoteControlForm_KeyUp); this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RemoteControlForm_MouseDown); @@ -149,5 +190,8 @@ namespace ColorControl private System.Windows.Forms.CheckBox chkDirectMode; private System.Windows.Forms.Panel pnlMouse; private System.Windows.Forms.Label lblPos; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; } } \ No newline at end of file diff --git a/ColorControl/RemoteControlForm.cs b/ColorControl/RemoteControlForm.cs index 5c659bb..ef7dcfc 100644 --- a/ColorControl/RemoteControlForm.cs +++ b/ColorControl/RemoteControlForm.cs @@ -116,24 +116,24 @@ namespace ColorControl { if (chkDirectMode.Checked) { - MessageForms.InfoOk( -@"If you enable Direct Mode you will be able to control the TV directly via your keyboard and mouse. -Note that your mouse will be captured in a box and that due to pointer accelaration the control will not be perfect. +// MessageForms.InfoOk( +//@"If you enable Direct Mode you will be able to control the TV directly via your keyboard and mouse. +//Note that your mouse will be captured in a box and that due to pointer accelaration the control will not be perfect. -Keyboard keys: -- arrow keys: navigate the menu -- backspace: go back -- escape: exit -- S: open picture settings -- media keys: volume up/down/mute +//Keyboard keys: +//- arrow keys: navigate the menu +//- backspace: go back +//- escape: exit +//- S: open picture settings +//- media keys: volume up/down/mute -Mouse: -- left button: activate/open item -- right button: go back -- wheel: scroll through menus (only vertically) +//Mouse: +//- left button: activate/open item +//- right button: go back +//- wheel: scroll through menus (only vertically) -Press F1 to stop Direct Mode." - ); +//Press F1 to stop Direct Mode." +// ); if (_mouseService == null) { @@ -216,6 +216,11 @@ Press F1 to stop Direct Mode." } } + private void RemoteControlForm_Shown(object sender, EventArgs e) + { + chkDirectMode.Checked = true; + } + private void pnlMouse_MouseMove(object sender, MouseEventArgs e) { HandleMouse(e); diff --git a/ColorControl/RemoteControlForm.resx b/ColorControl/RemoteControlForm.resx index 1af7de1..dfefb6a 100644 --- a/ColorControl/RemoteControlForm.resx +++ b/ColorControl/RemoteControlForm.resx @@ -117,4 +117,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + This form allows you to control the TV directly via your keyboard and mouse. +Note that your mouse will be captured in a box and that due to pointer acceleration the control will not be perfect. + +Press F1 to stop Direct Mode. + \ No newline at end of file diff --git a/ColorControl/RemoteControlPanel.Designer.cs b/ColorControl/RemoteControlPanel.Designer.cs new file mode 100644 index 0000000..3c4cf30 --- /dev/null +++ b/ColorControl/RemoteControlPanel.Designer.cs @@ -0,0 +1,81 @@ + +namespace ColorControl +{ + partial class RemoteControlPanel + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.pbRemote = new System.Windows.Forms.PictureBox(); + this.btnRcDirect = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.pbRemote)).BeginInit(); + this.SuspendLayout(); + // + // pbRemote + // + this.pbRemote.Dock = System.Windows.Forms.DockStyle.Fill; + this.pbRemote.Image = global::ColorControl.Properties.Resources.LG_remote; + this.pbRemote.Location = new System.Drawing.Point(0, 0); + this.pbRemote.Name = "pbRemote"; + this.pbRemote.Size = new System.Drawing.Size(129, 468); + this.pbRemote.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.pbRemote.TabIndex = 0; + this.pbRemote.TabStop = false; + this.pbRemote.Click += new System.EventHandler(this.pbRemote_Click); + this.pbRemote.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pbRemote_MouseMove); + // + // btnRcDirect + // + this.btnRcDirect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnRcDirect.Location = new System.Drawing.Point(71, 7); + this.btnRcDirect.Name = "btnRcDirect"; + this.btnRcDirect.Size = new System.Drawing.Size(55, 23); + this.btnRcDirect.TabIndex = 3; + this.btnRcDirect.Text = "Direct"; + this.btnRcDirect.UseVisualStyleBackColor = true; + this.btnRcDirect.Click += new System.EventHandler(this.button1_Click); + // + // RemoteControlPanel + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.White; + this.Controls.Add(this.btnRcDirect); + this.Controls.Add(this.pbRemote); + this.Name = "RemoteControlPanel"; + this.Size = new System.Drawing.Size(129, 468); + this.Load += new System.EventHandler(this.RemoteControlPanel_Load); + ((System.ComponentModel.ISupportInitialize)(this.pbRemote)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.PictureBox pbRemote; + private System.Windows.Forms.Button btnRcDirect; + } +} diff --git a/ColorControl/RemoteControlPanel.cs b/ColorControl/RemoteControlPanel.cs new file mode 100644 index 0000000..5ef437d --- /dev/null +++ b/ColorControl/RemoteControlPanel.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; + +namespace ColorControl +{ + partial class RemoteControlPanel : UserControl + { + private static int Range = 35; + + private LgService _lgService; + private List _buttons; + private RemoteControlForm _remoteControlForm; + private Bitmap _backupImage; + private LgPreset _lastHoveredPreset; + + public RemoteControlPanel(LgService lgService, List buttons) + { + _lgService = lgService; + _buttons = buttons; + + InitializeComponent(); + + _backupImage = new Bitmap(pbRemote.Image); + + var txtCoords = Utils.GetResourceFile("RC_coords.txt"); + + foreach (var line in txtCoords.Split('\n')) + { + var columns = line.Split(';'); + + var name = columns[0]; + var preset = _buttons.FirstOrDefault(p => p.name.Equals(name)); + if (preset != null) + { + preset.X = int.Parse(columns[1]); + preset.Y = int.Parse(columns[2]); + } + } + } + + private void RemoteControlPanel_Load(object sender, EventArgs e) + { + } + + private void ButtonClick(object sender, EventArgs e) + { + if (e is MouseEventArgs) + { + var preset = (LgPreset)(sender as Button).Tag; + + var _ = _lgService.ApplyPreset(preset); + } + } + + private void pbRemote_Click(object sender, EventArgs e) + { + if (e is MouseEventArgs mouseArgs) + { + var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y); + + var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range)); + + if (preset != null) + { + var _ = _lgService.ApplyPreset(preset); + } + } + } + + private (int, int) GetCoordsInImage(int curX, int curY) + { + var width = pbRemote.Width; + var aspect = 0.27D; + var currentAspect = (double)pbRemote.Width / pbRemote.Height; + if (currentAspect > aspect) + { + width = (int)(aspect * pbRemote.Height); + curX = curX - ((pbRemote.Width - width) / 2); + } + else + { + var height = (int)(pbRemote.Width / aspect); + curY = curY - ((pbRemote.Height - height) / 2); + } + + var factor = 405D / width; + + var posX = (int)(curX * factor); + var posY = (int)(curY * factor); + + return (posX, posY); + } + + private void btnRcDirect_Click(object sender, EventArgs e) + { + } + + private void pbRemote_MouseMove(object sender, MouseEventArgs mouseArgs) + { + var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y); + + var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range)); + + var refresh = false; + + if (_lastHoveredPreset != null && (preset == null || preset != _lastHoveredPreset)) + { + var image = (Bitmap)pbRemote.Image; + + for (var y = Math.Max(0, _lastHoveredPreset.Y - Range); y < _lastHoveredPreset.Y + Range; y++) + { + for (var x = Math.Max(0, _lastHoveredPreset.X - Range); x < _lastHoveredPreset.X + Range; x++) + { + image.SetPixel(x, y, _backupImage.GetPixel(x, y)); + + } + } + + refresh = true; + } + + if (preset != null && preset != _lastHoveredPreset) + { + var image = (Bitmap)pbRemote.Image; + var rec = new Rectangle(Math.Max(0, preset.X - Range), Math.Max(0, preset.Y - Range), Range * 2, Range * 2); + + using (var g = Graphics.FromImage(image)) + { + using (var cloud_brush = new SolidBrush(Color.FromArgb(128, Color.White))) + { + g.FillEllipse(cloud_brush, rec); + } + } + + //for (var y = preset.Y - 30; y < preset.Y + 30; y++) + //{ + // for (var x = preset.X - 30; x < preset.X + 30; x++) + // { + // var pixel = image.GetPixel(x, y); + + // image.SetPixel(x, y, Color.FromArgb(150, 0, 0)); + + // } + //} + + refresh = true; + } + + if (refresh) + { + pbRemote.Refresh(); + } + + _lastHoveredPreset = preset; + } + + private void button1_Click(object sender, EventArgs e) + { + if (_remoteControlForm == null || _remoteControlForm.IsDisposed) + { + var buttons = _lgService.GetRemoteControlButtons(); + _remoteControlForm = new RemoteControlForm(_lgService, buttons); + _remoteControlForm.Show(); + } + else + { + _remoteControlForm.Show(); + } + } + } +} diff --git a/ColorControl/RemoteControlPanel.resx b/ColorControl/RemoteControlPanel.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/ColorControl/RemoteControlPanel.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ColorControl/Resources/LG_remote.jpg b/ColorControl/Resources/LG_remote.jpg new file mode 100644 index 0000000000000000000000000000000000000000..58b9ddaea861b006026c3781c06470f1ff1c741d GIT binary patch literal 94771 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.Threading.Tasks.Task task) + public static T WaitForTask(Task task) { while (task != null && (task.Status < TaskStatus.WaitingForChildrenToComplete)) { - Thread.Sleep(100); + Thread.Sleep(10); Application.DoEvents(); } return task.Result; } + public static void WaitForTask(System.Threading.Tasks.Task task) + { + while (task != null && (task.Status < TaskStatus.WaitingForChildrenToComplete)) + { + Thread.Sleep(10); + Application.DoEvents(); + } + } + public static void SetNotifyIconText(NotifyIcon ni, string text) { text = text.Substring(0, Math.Min(text.Length, 127)); @@ -686,6 +695,26 @@ namespace ColorControl return null; } + + public static void StartProcess(string fileName, string arguments) + { + var process = Process.Start(fileName, arguments); + } + + public static string GetResourceFile(string resourceName) + { + resourceName = "ColorControl.Resources." + resourceName; + + var assembly = Assembly.GetExecutingAssembly(); + + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) + using (StreamReader reader = new StreamReader(stream)) + { + var result = reader.ReadToEnd(); + + return result; + } + } } public static class ProcessExtensions diff --git a/ColorControl/lgtv/LgTvApi.cs b/ColorControl/lgtv/LgTvApi.cs index 31d60b8..63515fc 100644 --- a/ColorControl/lgtv/LgTvApi.cs +++ b/ColorControl/lgtv/LgTvApi.cs @@ -91,7 +91,7 @@ namespace LgTv public async Task Connect() { - var ctx = _connection.Connect(new Uri(webSocketUri)); + var ctx = _connection.Connect(new Uri(webSocketUri)); return await ctx; } @@ -365,6 +365,12 @@ namespace LgTv return (string)response.sessionId; } + public async Task Test() + { + var msg = JObject.Parse(@"{ ""configs"": { ""tv.model.motionProMode"": ""OLED Motion Pro"" } }"); + var requestMessage = new RequestMessage("luna://com.webos.service.config/setConfigs", msg); + var response = await _connection.SendCommandAsync(requestMessage); + } public void Close() { diff --git a/ColorControl/lgtv/LgTvConnection.cs b/ColorControl/lgtv/LgTvConnection.cs index 63b9d01..5f7c192 100644 --- a/ColorControl/lgtv/LgTvConnection.cs +++ b/ColorControl/lgtv/LgTvConnection.cs @@ -44,12 +44,30 @@ namespace LgTv _connection.MessageReceived += Connection_MessageReceived; _connection.Closed += Connection_Closed; - await _connection.ConnectAsync(uri); + var cancellationTokenSource = new CancellationTokenSource(); + var connectTask = _connection.ConnectAsync(uri).AsTask(cancellationTokenSource.Token); - IsConnected?.Invoke(true); + cancellationTokenSource.CancelAfter(5000); - _messageWriter = new DataWriter(_connection.OutputStream); - return true; + var result = await connectTask.ContinueWith((antecedent) => + { + if (antecedent.Status == TaskStatus.RanToCompletion) + { + // connectTask ran to completion, so we know that the MessageWebSocket is connected. + // Add additional code here to use the MessageWebSocket. + IsConnected?.Invoke(true); + + _messageWriter = new DataWriter(_connection.OutputStream); + return true; + } + else + { + // connectTask timed out, or faulted. + return false; + } + }); + + return result; } catch (Exception e) { diff --git a/ColorControl/lgtv/LgWebOsMouseService.cs b/ColorControl/lgtv/LgWebOsMouseService.cs index 5d3d692..d64c8d2 100644 --- a/ColorControl/lgtv/LgWebOsMouseService.cs +++ b/ColorControl/lgtv/LgWebOsMouseService.cs @@ -33,7 +33,15 @@ namespace LgTv MUTE, MENU, CC, - DASH + DASH, + CHANNELUP, + CHANNELDOWN, + LIST, + AD, + SAP, + PROGRAM, + PLAY, + PAUSE } public class LgWebOsMouseService : IDisposable