diff --git a/ColorControl/ColorControl.csproj b/ColorControl/ColorControl.csproj index 332c52e..4c2ad31 100644 --- a/ColorControl/ColorControl.csproj +++ b/ColorControl/ColorControl.csproj @@ -19,8 +19,8 @@ Maassoft Maassoft 0 - 8.0.0.0 - 8.0.0.0 + 8.1.0.0 + 8.1.0.0 false true false diff --git a/ColorControl/Common/Utils.cs b/ColorControl/Common/Utils.cs index 481a9e7..61de190 100644 --- a/ColorControl/Common/Utils.cs +++ b/ColorControl/Common/Utils.cs @@ -426,6 +426,12 @@ public static async Task> GetPnpDevices(string deviceName) public static T WaitForTask(Task task) { + if (ConsoleOpened) + { + task.Wait(); + return task.Result; + } + while (task != null && (task.Status < TaskStatus.WaitingForChildrenToComplete)) { Thread.Sleep(10); @@ -436,6 +442,12 @@ public static T WaitForTask(Task task) public static void WaitForTask(System.Threading.Tasks.Task task) { + if (ConsoleOpened) + { + task.Wait(); + return; + } + while (task != null && (task.Status < TaskStatus.WaitingForChildrenToComplete)) { Thread.Sleep(10); @@ -733,7 +745,7 @@ public static Process GetProcessByName(string name, bool skipCurrent = true) return null; } - public static void StartProcess(string fileName, string arguments = null, bool hidden = false, bool wait = false, bool setWorkingDir = false, bool elevate = false, uint affinityMask = 0, uint priorityClass = 0) + public static Process StartProcess(string fileName, string arguments = null, bool hidden = false, bool wait = false, bool setWorkingDir = false, bool elevate = false, uint affinityMask = 0, uint priorityClass = 0) { var process = Process.Start(new ProcessStartInfo(fileName, arguments) { @@ -757,6 +769,8 @@ public static void StartProcess(string fileName, string arguments = null, bool h { process.WaitForExit(); } + + return process; } internal static void SetProcessAffinity(int processId, uint affinityMask) diff --git a/ColorControl/Forms/ListViewColumnSorter.cs b/ColorControl/Forms/ListViewColumnSorter.cs index 854c4f2..93887b8 100644 --- a/ColorControl/Forms/ListViewColumnSorter.cs +++ b/ColorControl/Forms/ListViewColumnSorter.cs @@ -64,7 +64,9 @@ public int Compare(object x, object y) else { // Compare the two items - compareResult = ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text, listviewY.SubItems[ColumnToSort].Text); + var subItemTextX = listviewX.SubItems.Count > ColumnToSort ? listviewX.SubItems[ColumnToSort].Text : string.Empty; + var subItemTextY = listviewY.SubItems.Count > ColumnToSort ? listviewY.SubItems[ColumnToSort].Text : string.Empty; + compareResult = ObjectCompare.Compare(subItemTextX, subItemTextY); } // Calculate correct return value based on object comparison diff --git a/ColorControl/Forms/MessageForms.cs b/ColorControl/Forms/MessageForms.cs index f4dc00a..cfbdca5 100644 --- a/ColorControl/Forms/MessageForms.cs +++ b/ColorControl/Forms/MessageForms.cs @@ -198,7 +198,7 @@ public static List ShowDialog(string caption, IEnumerable().ToArray()); + cbxGameStepType.SelectedIndex = 0; + } } private void InitLgTab() @@ -1769,7 +1782,8 @@ private void InitOptionsTab() ElevationMethod.None => rbElevationNone.Checked = true, ElevationMethod.RunAsAdmin => rbElevationAdmin.Checked = true, ElevationMethod.UseService => rbElevationService.Checked = true, - ElevationMethod.UseElevatedProcess => rbElevationProcess.Checked = true + ElevationMethod.UseElevatedProcess => rbElevationProcess.Checked = true, + _ => false }; } finally @@ -1865,10 +1879,12 @@ private void SetLgDevicePowerOptions() clbLgPower.SetItemChecked(1, device?.PowerOnAfterResume ?? false); clbLgPower.SetItemChecked(2, device?.PowerOffOnShutdown ?? false); clbLgPower.SetItemChecked(3, device?.PowerOffOnStandby ?? false); - clbLgPower.SetItemChecked(4, device?.PowerSwitchOnScreenSaver ?? false); - clbLgPower.SetItemChecked(5, device?.PowerOnAfterManualPowerOff ?? false); - clbLgPower.SetItemChecked(6, device?.TriggersEnabled ?? true); - clbLgPower.SetItemChecked(7, device?.PowerByWindows ?? false); + clbLgPower.SetItemChecked(4, device?.PowerOffOnScreenSaver ?? false); + clbLgPower.SetItemChecked(5, device?.PowerOnAfterScreenSaver ?? false); + clbLgPower.SetItemChecked(6, device?.PowerOnAfterManualPowerOff ?? false); + clbLgPower.SetItemChecked(7, device?.TriggersEnabled ?? true); + clbLgPower.SetItemChecked(8, device?.PowerOffByWindows ?? false); + clbLgPower.SetItemChecked(8, device?.PowerOnByWindows ?? false); } finally { @@ -2388,7 +2404,7 @@ private void clbLgPower_ItemCheck(object sender, ItemCheckEventArgs e) return; } - if (e.Index is 0 or 1 or 4 or 7 && !(device.PowerOnAfterResume || device.PowerOnAfterStartup || device.PowerSwitchOnScreenSaver || device.PowerByWindows)) + if (e.Index is 0 or 1 or 4 or 7 && !(device.PowerOnAfterResume || device.PowerOnAfterStartup || device.PowerOnAfterScreenSaver || device.PowerOnByWindows)) { MessageForms.InfoOk( @"Be sure to activate the following setting on the TV, or the app will not be able to wake the TV: @@ -2406,10 +2422,12 @@ See Options to test this functionality. device.PowerOnAfterResume = clbLgPower.GetItemChecked(1); device.PowerOffOnShutdown = clbLgPower.GetItemChecked(2); device.PowerOffOnStandby = clbLgPower.GetItemChecked(3); - device.PowerSwitchOnScreenSaver = clbLgPower.GetItemChecked(4); - device.PowerOnAfterManualPowerOff = clbLgPower.GetItemChecked(5); - device.TriggersEnabled = clbLgPower.GetItemChecked(6); - device.PowerByWindows = clbLgPower.GetItemChecked(7); + device.PowerOffOnScreenSaver = clbLgPower.GetItemChecked(4); + device.PowerOnAfterScreenSaver = clbLgPower.GetItemChecked(5); + device.PowerOnAfterManualPowerOff = clbLgPower.GetItemChecked(6); + device.TriggersEnabled = clbLgPower.GetItemChecked(7); + device.PowerOffByWindows = clbLgPower.GetItemChecked(8); + device.PowerOnByWindows = clbLgPower.GetItemChecked(9); _lgService.InstallEventHandlers(); }); @@ -3166,16 +3184,7 @@ private void mnuLgButtons_Opening(object sender, CancelEventArgs e) var device = _lgService.GetPresetDevice(preset); - var actions = device?.GetInvokableActions(); - foreach (var action in actions.Where(a => a.Function != null)) - { - var text = action.Title ?? action.Name; - - var item = mnuLgActions.DropDownItems.Add(text); - item.Tag = action; - item.Click += miLgAddAction_Click; - } - + BuildLgActionMenu(device, mnuLgActions.DropDownItems, mnuLgActions.Name, miLgAddAction_Click); BuildServicePresetsMenu(mnuLgNvPresets, _nvService, "NVIDIA", miLgAddNvPreset_Click); BuildServicePresetsMenu(mnuLgAmdPresets, _amdService, "AMD", miLgAddAmdPreset_Click); } @@ -3232,40 +3241,51 @@ private void mnuLgExpert_Opening(object sender, CancelEventArgs e) var device = _lgService.SelectedDevice; var eligibleModels = new[] { "B9", "C9", "E9", "W9", "C2", "G2" }; + var eligibleModelsEnable = new[] { "B9", "C9", "E9", "W9" }; - var visible = device?.ModelName != null ? eligibleModels.Any(m => device.ModelName.Contains(m)) : false; + var visible = eligibleModels.Any(m => device?.ModelName?.Contains(m) == true); + var enableVisible = eligibleModelsEnable.Any(m => device?.ModelName?.Contains(m) == true); mnuLgOLEDMotionPro.Visible = visible; + miLgEnableMotionPro.Visible = enableVisible; miLgExpertSeparator1.Visible = visible; - // Does not work yet, getting a "401 no permissions" error - //var task = device?.GetPictureSettings(); - //var settings = Utils.WaitForTask(task); + BuildLgActionMenu(device, mnuLgExpert.Items, mnuLgExpert.Name, btnLgExpertColorGamut_Click, _lgService.Config.ShowAdvancedActions, true); + } + private void BuildLgActionMenu(LgDevice device, ToolStripItemCollection parent, string parentName, EventHandler clickEvent, bool showAdvanced = false, bool showGameBar = false) + { if (device == null) { return; } - var actions = device.GetInvokableActions(_lgService.Config.ShowAdvancedActions); + var actions = device.GetInvokableActions(showAdvanced); var gameBarActions = device.GetInvokableActionsForGameBar(); var activatedGameBarActions = device.GetActionsForGameBar(); const string gameBarName = "miGameBar"; - var expertActions = actions.Where(a => !a.Name.Contains("uhd", StringComparison.OrdinalIgnoreCase) && - !a.Name.Contains("gameOpt", StringComparison.OrdinalIgnoreCase) && - !a.Name.Contains("hdmiPc", StringComparison.OrdinalIgnoreCase) && - (a.EnumType != null || a.MaxValue > a.MinValue)).ToList(); + var expertActions = actions.Where(a => a.EnumType != null || a.MaxValue > a.MinValue).ToList(); var categories = expertActions.Select(a => a.Category ?? "misc").Where(c => !string.IsNullOrEmpty(c)).Distinct(); foreach (var category in categories) { - var catMenuItem = FormUtils.BuildDropDownMenuEx(mnuLgExpert.Items, mnuLgExpert.Name, Utils.FirstCharUpperCase(category), null, null, category); + var catMenuItem = FormUtils.BuildDropDownMenuEx(parent, parentName, Utils.FirstCharUpperCase(category), null, null, category); foreach (var action in expertActions.Where(a => (a.Category ?? "misc") == category)) { - var menu = FormUtils.BuildDropDownMenuEx(catMenuItem.DropDownItems, catMenuItem.Name, action.Title, action.EnumType, btnLgExpertColorGamut_Click, action, (int)action.MinValue, (int)action.MaxValue, action.NumberOfValues > 1); + if (!showGameBar) + { + var text = action.Title ?? action.Name; + + var item = catMenuItem.DropDownItems.Add(text); + item.Tag = action; + item.Click += clickEvent; + continue; + } + + var menu = FormUtils.BuildDropDownMenuEx(catMenuItem.DropDownItems, catMenuItem.Name, action.Title, action.EnumType, clickEvent, action, (int)action.MinValue, (int)action.MaxValue, action.NumberOfValues > 1); if (!gameBarActions.Contains(action)) { @@ -3289,16 +3309,16 @@ private void mnuLgExpert_Opening(object sender, CancelEventArgs e) } } - if (!_lgService.Config.ShowAdvancedActions) + if (!showAdvanced) { return; } var presetActions = actions.Where(a => a.Preset != null); - if (presetActions.Any() && mnuLgExpert.Items.Find("miLgExpertActionsSeparator", false).Length == 0) + if (presetActions.Any() && parent.Find("miLgExpertActionsSeparator", false).Length == 0) { - mnuLgExpert.Items.Add(new ToolStripSeparator + parent.Add(new ToolStripSeparator { Name = "miLgExpertActionsSeparator" }); @@ -3306,7 +3326,7 @@ private void mnuLgExpert_Opening(object sender, CancelEventArgs e) foreach (var presetAction in presetActions) { - var menu = FormUtils.BuildDropDownMenuEx(mnuLgExpert.Items, mnuLgExpert.Name, presetAction.Name, null, btnLgExpertPresetAction_Click, presetAction.Preset); + var menu = FormUtils.BuildDropDownMenuEx(parent, parentName, presetAction.Name, null, btnLgExpertPresetAction_Click, presetAction.Preset); } } @@ -3709,7 +3729,7 @@ private void lvGamePresets_SelectedIndexChanged(object sender, EventArgs _) edtGameParameters.Text = preset.Parameters; chkGameRunAsAdmin.Checked = preset.RunAsAdministrator; chkGameQuickAccess.Checked = preset.ShowInQuickAccess; - edtGamePrelaunchSteps.Text = string.Join(", ", preset.PreLaunchSteps); + ShowGameSteps(); } else { @@ -3768,6 +3788,11 @@ private void AddGamePreset(FileInfo file) } private void btnGameSave_Click(object sender, EventArgs e) + { + SaveGamePreset(); + } + + private void SaveGamePreset() { var shortcut = string.Empty; if (!Utils.ValidateShortcut(shortcut)) @@ -3801,7 +3826,15 @@ private void btnGameSave_Click(object sender, EventArgs e) var text = edtGamePrelaunchSteps.Text; - Utils.ParseWords(preset.PreLaunchSteps, text); + var stepsList = (GameStepType)cbxGameStepType.SelectedIndex switch + { + GameStepType.PreLaunch => preset.PreLaunchSteps, + GameStepType.PostLaunch => preset.PostLaunchSteps, + GameStepType.Finalize => preset.FinalizeSteps, + _ => throw new InvalidOperationException("Invalid game step type") + }; + + Utils.ParseWords(stepsList, text); AddOrUpdateItemGame(); @@ -4320,5 +4353,38 @@ private void cbxLogType_SelectedIndexChanged(object sender, EventArgs e) { LoadLog(); } + + private void cbxGameStepType_SelectedIndexChanged(object sender, EventArgs e) + { + ShowGameSteps(); + } + + private void ShowGameSteps() + { + var preset = GetSelectedGamePreset(); + + if (preset == null) + { + return; + } + + switch ((GameStepType)cbxGameStepType.SelectedIndex) + { + case GameStepType.PreLaunch: + edtGamePrelaunchSteps.Text = string.Join(", ", preset.PreLaunchSteps); + break; + case GameStepType.PostLaunch: + edtGamePrelaunchSteps.Text = string.Join(", ", preset.PostLaunchSteps); + break; + case GameStepType.Finalize: + edtGamePrelaunchSteps.Text = string.Join(", ", preset.FinalizeSteps); + break; + } + } + + private void edtGamePrelaunchSteps_Leave(object sender, EventArgs e) + { + SaveGamePreset(); + } } } \ No newline at end of file diff --git a/ColorControl/Services/GameLauncher/GamePreset.cs b/ColorControl/Services/GameLauncher/GamePreset.cs index bf9e66f..6a95b2b 100644 --- a/ColorControl/Services/GameLauncher/GamePreset.cs +++ b/ColorControl/Services/GameLauncher/GamePreset.cs @@ -1,9 +1,20 @@ using ColorControl.Services.Common; using NWin32; using System.Collections.Generic; +using System.ComponentModel; namespace ColorControl.Services.GameLauncher { + public enum GameStepType + { + [Description("Pre-launch steps")] + PreLaunch = 0, + [Description("Post-launch steps")] + PostLaunch = 1, + [Description("Finalize steps")] + Finalize = 2, + } + public enum GamePriorityClass { Idle = NativeConstants.IDLE_PRIORITY_CLASS, @@ -21,6 +32,7 @@ class GamePreset : PresetBase public bool RunAsAdministrator { get; set; } public List PreLaunchSteps { get; set; } public List PostLaunchSteps { get; set; } + public List FinalizeSteps { get; set; } public uint ProcessAffinityMask { get; set; } public uint ProcessPriorityClass { get; set; } @@ -28,6 +40,7 @@ public GamePreset() : base() { PreLaunchSteps = new List(); PostLaunchSteps = new List(); + FinalizeSteps = new List(); } public GamePreset(GamePreset preset) : this() @@ -40,6 +53,7 @@ public GamePreset(GamePreset preset) : this() RunAsAdministrator = preset.RunAsAdministrator; PreLaunchSteps.AddRange(preset.PreLaunchSteps); PostLaunchSteps.AddRange(preset.PostLaunchSteps); + FinalizeSteps.AddRange(preset.FinalizeSteps); } public GamePreset Clone() @@ -53,7 +67,7 @@ public GamePreset Clone() public static string[] GetColumnNames() { - return new[] { "Name|160", "File/URI|400", "Parameters|200", "Pre-launch steps|300" }; + return new[] { "Name|160", "File/URI|400", "Parameters|200", "Pre-launch steps|300", "Post-launch steps|300", "Finalize steps|300" }; } public override List GetDisplayValues(Config config = null) @@ -66,6 +80,7 @@ public override List GetDisplayValues(Config config = null) values.Add(string.Join(", ", PreLaunchSteps)); values.Add(string.Join(", ", PostLaunchSteps)); + values.Add(string.Join(", ", FinalizeSteps)); return values; } diff --git a/ColorControl/Services/GameLauncher/GameService.cs b/ColorControl/Services/GameLauncher/GameService.cs index 3551b8a..57ed3e2 100644 --- a/ColorControl/Services/GameLauncher/GameService.cs +++ b/ColorControl/Services/GameLauncher/GameService.cs @@ -2,6 +2,8 @@ using ColorControl.Services.Common; using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; namespace ColorControl.Services.GameLauncher @@ -79,7 +81,44 @@ public bool ApplyPreset(GamePreset preset, AppContext appContext) { var result = true; - foreach (var step in preset.PreLaunchSteps) + ExecuteSteps(preset.PreLaunchSteps); + + Process process = null; + + if (!string.IsNullOrEmpty(preset.Path)) + { + process = Utils.StartProcess(preset.Path, preset.Parameters, setWorkingDir: true, elevate: preset.RunAsAdministrator, affinityMask: preset.ProcessAffinityMask, priorityClass: preset.ProcessPriorityClass); + } + + ExecuteSteps(preset.PostLaunchSteps); + + if (process != null && preset.FinalizeSteps?.Any() == true) + { + var _ = ExecuteFinalizationStepsAsync(process, preset.FinalizeSteps); + } + + _lastAppliedPreset = preset; + + PresetApplied(); + + return result; + } + + private async Task ExecuteFinalizationStepsAsync(Process process, List finalizeSteps) + { + await process.WaitForExitAsync(); + + ExecuteSteps(finalizeSteps); + } + + private void ExecuteSteps(List steps) + { + if (steps?.Any() != true) + { + return; + } + + foreach (var step in steps) { var keySpec = step.Split(':'); @@ -120,17 +159,6 @@ public bool ApplyPreset(GamePreset preset, AppContext appContext) } } - - if (!string.IsNullOrEmpty(preset.Path)) - { - Utils.StartProcess(preset.Path, preset.Parameters, setWorkingDir: true, elevate: preset.RunAsAdministrator, affinityMask: preset.ProcessAffinityMask, priorityClass: preset.ProcessPriorityClass); - } - - _lastAppliedPreset = preset; - - PresetApplied(); - - return result; } } } diff --git a/ColorControl/Services/LG/LgDevice.cs b/ColorControl/Services/LG/LgDevice.cs index 79e0130..65b5566 100644 --- a/ColorControl/Services/LG/LgDevice.cs +++ b/ColorControl/Services/LG/LgDevice.cs @@ -24,6 +24,8 @@ public class InvokableAction public int NumberOfValues { get; set; } public LgPreset Preset { get; set; } public bool Advanced { get; set; } + public ModelYear FromModelYear { get; set; } = ModelYear.None; + public ModelYear ToModelYear { get; set; } = ModelYear.None; } public class LgDevicePictureSettings @@ -51,6 +53,17 @@ public enum PowerOffSource Manually } + public enum ModelYear + { + None = 0, + SeriesPre2018, + Series2018, + Series2019, + Series2020, + Series2021, + Series2022 + } + protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); public static Func ExternalServiceHandler; public static List DefaultActionsOnGameBar = new() { "backlight", "contrast", "brightness", "color" }; @@ -66,9 +79,11 @@ public enum PowerOffSource public bool PowerOnAfterResume { get; set; } public bool PowerOffOnShutdown { get; set; } public bool PowerOffOnStandby { get; set; } - public bool PowerSwitchOnScreenSaver { get; set; } + public bool PowerOffOnScreenSaver { get; set; } + public bool PowerOnAfterScreenSaver { get; set; } public bool PowerOnAfterManualPowerOff { get; set; } - public bool PowerByWindows { get; set; } + public bool PowerOnByWindows { get; set; } + public bool PowerOffByWindows { get; set; } public bool TriggersEnabled { get; set; } public int HDMIPortNumber { get; set; } @@ -114,6 +129,8 @@ public List ActionsOnGameBar [JsonIgnore] public string ModelName { get; private set; } + [JsonIgnore] + public ModelYear Year { get; private set; } [JsonIgnore] public LgDevicePictureSettings PictureSettings { get; private set; } @@ -153,8 +170,8 @@ public LgDevice(string name, string ipAddress, string macAddress, bool isCustom AddGenericPictureAction("eyeComfortMode", typeof(OffToOn), title: "Eye Comfort Mode"); //AddGenericPictureAction("dynamicColor", typeof(OffToAuto)); //AddGenericPictureAction("superResolution", typeof(OffToAuto)); - AddGenericPictureAction("peakBrightness", typeof(OffToHigh), title: "Peak Brightness"); - AddGenericPictureAction("smoothGradation", typeof(OffToAuto), title: "Smooth Gradation"); + AddGenericPictureAction("peakBrightness", typeof(OffToHigh), title: "Peak Brightness", fromModelYear: ModelYear.Series2019); + AddGenericPictureAction("smoothGradation", typeof(OffToAuto), title: "Smooth Gradation", fromModelYear: ModelYear.Series2019); AddGenericPictureAction("energySaving", typeof(EnergySaving), title: "Energy Saving"); AddGenericPictureAction("hdrDynamicToneMapping", typeof(DynamicTonemapping), title: "HDR Dynamic Tone Mapping"); AddGenericPictureAction("blackLevel", typeof(BlackLevel), title: "HDMI Black Level"); @@ -170,7 +187,7 @@ public LgDevice(string name, string ipAddress, string macAddress, bool isCustom AddGenericPictureAction("truMotionMode", typeof(TruMotionMode), title: "TruMotion"); AddGenericPictureAction("truMotionJudder", minValue: 0, maxValue: 10, title: "TruMotion Judder"); AddGenericPictureAction("truMotionBlur", minValue: 0, maxValue: 10, title: "TruMotion Blur"); - AddGenericPictureAction("motionProOLED", typeof(OffToHigh), title: "OLED Motion Pro"); + AddGenericPictureAction("motionProOLED", typeof(OffToHigh), title: "OLED Motion Pro", fromModelYear: ModelYear.Series2019); AddGenericPictureAction("motionPro", typeof(OffToOn), title: "Motion Pro"); AddGenericPictureAction("uhdDeepColorHDMI1", typeof(OffToOn), category: "other"); AddGenericPictureAction("uhdDeepColorHDMI2", typeof(OffToOn), category: "other"); @@ -180,7 +197,10 @@ public LgDevice(string name, string ipAddress, string macAddress, bool isCustom AddGenericPictureAction("gameOptimizationHDMI2", typeof(OffToOn), category: "other"); AddGenericPictureAction("gameOptimizationHDMI3", typeof(OffToOn), category: "other"); AddGenericPictureAction("gameOptimizationHDMI4", typeof(OffToOn), category: "other"); - //AddGenericPictureAction("freesyncOLEDHDMI4", typeof(OffToOn), category: "other"); + AddGenericPictureAction("freesyncOLEDHDMI1", typeof(OffToOn), category: "other", fromModelYear: ModelYear.Series2020); + AddGenericPictureAction("freesyncOLEDHDMI2", typeof(OffToOn), category: "other", fromModelYear: ModelYear.Series2020); + AddGenericPictureAction("freesyncOLEDHDMI3", typeof(OffToOn), category: "other", fromModelYear: ModelYear.Series2020); + AddGenericPictureAction("freesyncOLEDHDMI4", typeof(OffToOn), category: "other", fromModelYear: ModelYear.Series2020); //AddGenericPictureAction("freesyncSupport", typeof(OffToOn), category: "other"); AddGenericPictureAction("hdmiPcMode_hdmi1", typeof(FalseToTrue), category: "other"); AddGenericPictureAction("hdmiPcMode_hdmi2", typeof(FalseToTrue), category: "other"); @@ -236,7 +256,7 @@ private void AddInternalPresetAction(LgPreset preset) _invokableActions.Add(action); } - private void AddGenericPictureAction(string name, Type type = null, decimal minValue = 0, decimal maxValue = 0, string category = "picture", string title = null, int numberOfValues = 1) + private void AddGenericPictureAction(string name, Type type = null, decimal minValue = 0, decimal maxValue = 0, string category = "picture", string title = null, int numberOfValues = 1, ModelYear fromModelYear = ModelYear.None) { var action = new InvokableAction { @@ -247,7 +267,8 @@ private void AddGenericPictureAction(string name, Type type = null, decimal minV MaxValue = maxValue, NumberOfValues = numberOfValues, Category = category, - Title = title == null ? Utils.FirstCharUpperCase(name) : title + Title = title == null ? Utils.FirstCharUpperCase(name) : title, + FromModelYear = fromModelYear }; _invokableActions.Add(action); @@ -318,12 +339,13 @@ public async Task Connect(int retries = 3) //Test(); //_lgTvApi.Test3(); - if (_lgTvApi != null) + if (_lgTvApi != null && !Utils.ConsoleOpened) { var info = await _lgTvApi.GetSystemInfo("modelName"); if (info != null) { ModelName = info.modelName; + SetModelYear(); } //await _lgTvApi.SubscribeVolume(VolumeChanged); @@ -360,6 +382,38 @@ public async Task Connect(int retries = 3) } } + private void SetModelYear() + { + if (ModelName == null || ModelName.Contains("OLED") != true) + { + Year = ModelYear.None; + return; + } + + var normalized = ModelName.Replace("OLED", "").Replace(" ", ""); + + if (normalized.Length > 3) + { + normalized = normalized.Substring(3); + } + + var suffix = normalized.First(); + + Year = suffix switch + { + '6' => ModelYear.SeriesPre2018, + '7' => ModelYear.SeriesPre2018, + '8' => ModelYear.Series2018, + '9' => ModelYear.Series2019, + 'X' => ModelYear.Series2020, + '1' => ModelYear.Series2021, + '2' => ModelYear.Series2022, + _ => ModelYear.None + }; + + _invokableActions = _invokableActions.Where(a => Year == ModelYear.None || a.FromModelYear == ModelYear.None || Year >= a.FromModelYear).ToList(); + } + public bool VolumeChanged(dynamic payload) { return true; @@ -798,7 +852,7 @@ internal async Task WakeAndConnectWithRetries(int retries = 5) public List GetInvokableActions(bool includedAdvanced = false) { - return includedAdvanced ? _invokableActions : _invokableActions.Where(a => !a.Advanced).ToList(); + return _invokableActions.Where(a => includedAdvanced || !a.Advanced).ToList(); } public List GetInvokableActionsForGameBar() diff --git a/ColorControl/Services/LG/LgService.cs b/ColorControl/Services/LG/LgService.cs index f977831..1da42a6 100644 --- a/ColorControl/Services/LG/LgService.cs +++ b/ColorControl/Services/LG/LgService.cs @@ -420,7 +420,7 @@ internal void WakeAfterStartupOrResume(PowerOnOffState state = PowerOnOffState.S { var wakeDevices = Devices.Where(d => state == PowerOnOffState.StartUp && d.PowerOnAfterStartup || state == PowerOnOffState.Resume && d.PowerOnAfterResume || - state == PowerOnOffState.ScreenSaver && d.PowerSwitchOnScreenSaver); + state == PowerOnOffState.ScreenSaver && d.PowerOnAfterScreenSaver); PowerOnDevices(wakeDevices, state, checkUserSession); } @@ -514,12 +514,12 @@ private void MonitorProcesses() public bool ShouldMonitorProcesses() { - return Devices.Any(d => d.PowerSwitchOnScreenSaver) || _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None)); + return Devices.Any(d => d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver) || _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None)); } public async Task CheckProcesses() { - var wasConnected = Devices.Any(d => d.PowerSwitchOnScreenSaver && d.IsConnected()); + var wasConnected = Devices.Any(d => (d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver) && d.IsConnected()); _monitorTaskCounter++; var validCounter = _monitorTaskCounter; var lastProcessId = 0; @@ -535,7 +535,7 @@ public async Task CheckProcesses() try { - var applicableDevices = Devices.Where(d => d.PowerSwitchOnScreenSaver || d.TriggersEnabled && _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None) && + var applicableDevices = Devices.Where(d => d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver || d.TriggersEnabled && _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None) && ((string.IsNullOrEmpty(p.DeviceMacAddress) && d == SelectedDevice) || p.DeviceMacAddress.Equals(d.MacAddress, StringComparison.OrdinalIgnoreCase)))); if (!applicableDevices.Any()) @@ -601,7 +601,7 @@ public async Task CheckProcesses() wasConnected = true; } - if (connectedDevices.Any(d => d.PowerSwitchOnScreenSaver)) + if (connectedDevices.Any(d => d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver)) { lastProcessId = await HandleScreenSaverProcessAsync(lastProcessId, processes, connectedDevices); } @@ -634,7 +634,7 @@ private async Task HandleScreenSaverProcessAsync(int lastProcessId, Process Logger.Debug($"Screensaver started: {process.ProcessName}, parent: {parent.ProcessName}"); try { - foreach (var device in connectedDevices) + foreach (var device in connectedDevices.Where(d => d.PowerOffOnScreenSaver)) { Logger.Debug($"Screensaver check: test connection with {device.Name}..."); var test = await device.TestConnection(); @@ -815,14 +815,15 @@ internal void RemoveCustomDevice(LgDevice device) public void PowerSettingChanged(WindowsPowerSetting setting) { - var devices = Devices.Where(d => d.PowerByWindows).ToList(); if (setting == WindowsPowerSetting.Off) { + var devices = Devices.Where(d => d.PowerOffByWindows).ToList(); PowerOffDevices(devices, PowerOnOffState.StandBy); } else if (setting == WindowsPowerSetting.On) { + var devices = Devices.Where(d => d.PowerOnByWindows).ToList(); PowerOnDevices(devices); } } diff --git a/ColorControl/Svc/ColorControlBackgroundService.cs b/ColorControl/Svc/ColorControlBackgroundService.cs index d3607d4..a8ac8d0 100644 --- a/ColorControl/Svc/ColorControlBackgroundService.cs +++ b/ColorControl/Svc/ColorControlBackgroundService.cs @@ -63,7 +63,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } catch (Exception ex) { - Logger.Error($"While handling message: {message}"); + Logger.Error(ex, $"Error while handling message: {message}"); var result = SvcResultMessage.FromResult(false, ex.Message); var resultJson = JsonConvert.SerializeObject(result); @@ -104,7 +104,8 @@ private SvcResultMessage HandleMessage(string json) SvcMessageType.SetLgConfig => HandleSetLgConfigMessage(message), SvcMessageType.GetLog => HandleGetLogMessage(message), SvcMessageType.ClearLog => HandleClearLogMessage(message), - SvcMessageType.ExecuteRpc => HandleExecuteRpcCommand(message) + SvcMessageType.ExecuteRpc => HandleExecuteRpcCommand(message), + _ => SvcResultMessage.FromResult(false, "Unexpected message type") }; return result; diff --git a/ColorControl/lgtv/LgTvConnection.cs b/ColorControl/lgtv/LgTvConnection.cs index 052637e..5c83b19 100644 --- a/ColorControl/lgtv/LgTvConnection.cs +++ b/ColorControl/lgtv/LgTvConnection.cs @@ -1,14 +1,13 @@ -using System; +using ColorControl.Common; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; -using Newtonsoft.Json; using Windows.Networking.Sockets; using Windows.Storage.Streams; -using Newtonsoft.Json.Serialization; -using Newtonsoft.Json.Linq; -using ColorControl; -using ColorControl.Common; namespace LgTv { @@ -19,7 +18,7 @@ public SendMessageException(string message, Exception e) : base(message, e) } } - + public class LgTvApiCore : IDisposable { public bool ConnectionClosed { get; private set; } @@ -58,30 +57,6 @@ public async Task Connect(Uri uri, bool ignoreReceiver = false) { return false; } - - //using (var cancellationTokenSource = new CancellationTokenSource(5000)) - //{ - // var connectTask = _connection.ConnectAsync(uri).AsTask(cancellationTokenSource.Token); - // 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) { @@ -145,9 +120,8 @@ private async Task SendCommandAsync(string id, string message) { throw new Exception("Connection closed"); } - var result = await taskSource.Task.ConfigureAwait(false); - return result; + return await taskSource.Task; } catch (Exception e) {