From 82b2607911be20ce0854f033bed4de41dabd72db Mon Sep 17 00:00:00 2001 From: Ted Date: Sun, 5 Jun 2016 14:59:50 -0700 Subject: [PATCH 01/10] First attempt at fix for isasue 93. 10 attempts to reconnect, 500 MS between each. --- Artemis/Artemis/Managers/DeviceManager.cs | 69 ++++++++++++++++------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/Artemis/Artemis/Managers/DeviceManager.cs b/Artemis/Artemis/Managers/DeviceManager.cs index 88f9637b6..3a9cfe5a2 100644 --- a/Artemis/Artemis/Managers/DeviceManager.cs +++ b/Artemis/Artemis/Managers/DeviceManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using Artemis.DeviceProviders; using Artemis.Events; using Artemis.Services; @@ -93,33 +94,59 @@ namespace Artemis.Managers if (!wasNull) ReleaseActiveKeyboard(); - - // Disable everything if there's no active keyboard found - if (!keyboardProvider.CanEnable()) + bool asynchEnable = false; + if (keyboardProvider.CanEnable()) { - DialogService.ShowErrorMessageBox(keyboardProvider.CantEnableText); - ActiveKeyboard = null; - General.Default.LastKeyboard = null; - General.Default.Save(); - _logger.Warn("Failed enabling keyboard: {0}", keyboardProvider.Name); - ChangingKeyboard = false; - return; + FinishEnableKeyboard(keyboardProvider, oldKeyboard); } + else + { + for (int i = 1; i <= 10; i++) + { + var thread = new Thread( + () => + { + Thread.Sleep(500); + asynchEnable = keyboardProvider.CanEnable(); + }); + _logger.Warn("Failed enabling keyboard: {0}, re-attempt {1} of 10", keyboardProvider.Name, i); + thread.Start(); + if (asynchEnable) + break; + } - ActiveKeyboard = keyboardProvider; - ActiveKeyboard.Enable(); - - General.Default.LastKeyboard = ActiveKeyboard.Name; - General.Default.Save(); - - EnableUsableDevices(); - - ChangingKeyboard = false; - _events.PublishOnUIThread(new ActiveKeyboardChanged(oldKeyboard, ActiveKeyboard)); - _logger.Debug("Enabled keyboard: {0}", keyboardProvider.Name); + if (!asynchEnable) + { + // Disable everything if there's no active keyboard found + DialogService.ShowErrorMessageBox(keyboardProvider.CantEnableText); + ActiveKeyboard = null; + General.Default.LastKeyboard = null; + General.Default.Save(); + _logger.Warn("Failed enabling keyboard: {0}", keyboardProvider.Name); + ChangingKeyboard = false; + return; + } + FinishEnableKeyboard(keyboardProvider, oldKeyboard); + } + } } + private void FinishEnableKeyboard(KeyboardProvider keyboardProvider, KeyboardProvider oldKeyboard) + { + ActiveKeyboard = keyboardProvider; + ActiveKeyboard.Enable(); + + General.Default.LastKeyboard = ActiveKeyboard.Name; + General.Default.Save(); + + EnableUsableDevices(); + + ChangingKeyboard = false; + _events.PublishOnUIThread(new ActiveKeyboardChanged(oldKeyboard, ActiveKeyboard)); + _logger.Debug("Enabled keyboard: {0}", keyboardProvider.Name); + } + private void EnableUsableDevices() { foreach (var mouseProvider in MiceProviders) From 5f281480e1b125c200b44ee62b23f676f41b178c Mon Sep 17 00:00:00 2001 From: Ted Date: Sat, 11 Jun 2016 15:02:45 -0700 Subject: [PATCH 02/10] Added Overall CPU and RAM resource monitors for Windows Profile. --- .../WindowsProfile/WindowsProfileDataModel.cs | 8 ++ .../WindowsProfile/WindowsProfileModel.cs | 77 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs index de9bc9206..c44c5a36a 100644 --- a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs +++ b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs @@ -8,9 +8,11 @@ namespace Artemis.Modules.Effects.WindowsProfile { Spotify = new Spotify(); Cpu = new CpuDataModel(); + Performance = new PerformanceDataModel(); } public CpuDataModel Cpu { get; set; } + public PerformanceDataModel Performance { get; set; } public Spotify Spotify { get; set; } } @@ -26,6 +28,12 @@ namespace Artemis.Modules.Effects.WindowsProfile public int Core8Usage { get; set; } } + public class PerformanceDataModel + { + public int CPUUsage { get; set; } + public int RAMUsage { get; set; } + } + public class Spotify { public bool Running { get; set; } diff --git a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs index b709dcde3..906ed42e1 100644 --- a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs +++ b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs @@ -8,13 +8,69 @@ using Artemis.Models; using Artemis.Models.Profiles; using Ninject.Extensions.Logging; using SpotifyAPI.Local; +using System.Runtime.InteropServices; namespace Artemis.Modules.Effects.WindowsProfile { + static class PerformanceInfo + { + [DllImport("psapi.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetPerformanceInfo([Out] out PerformanceInformation PerformanceInformation, [In] int Size); + + [StructLayout(LayoutKind.Sequential)] + public struct PerformanceInformation + { + public int Size; + public IntPtr CommitTotal; + public IntPtr CommitLimit; + public IntPtr CommitPeak; + public IntPtr PhysicalTotal; + public IntPtr PhysicalAvailable; + public IntPtr SystemCache; + public IntPtr KernelTotal; + public IntPtr KernelPaged; + public IntPtr KernelNonPaged; + public IntPtr PageSize; + public int HandlesCount; + public int ProcessCount; + public int ThreadCount; + } + + public static Int64 GetPhysicalAvailableMemoryInMiB() + { + PerformanceInformation pi = new PerformanceInformation(); + if (GetPerformanceInfo(out pi, Marshal.SizeOf(pi))) + { + return Convert.ToInt64((pi.PhysicalAvailable.ToInt64() * pi.PageSize.ToInt64() / 1048576)); + } + else + { + return -1; + } + + } + + public static Int64 GetTotalMemoryInMiB() + { + PerformanceInformation pi = new PerformanceInformation(); + if (GetPerformanceInfo(out pi, Marshal.SizeOf(pi))) + { + return Convert.ToInt64((pi.PhysicalTotal.ToInt64() * pi.PageSize.ToInt64() / 1048576)); + } + else + { + return -1; + } + + } + } + public class WindowsProfileModel : EffectModel { private readonly ILogger _logger; private List _cores; + private PerformanceCounter _overallCPU; private int _cpuFrames; private SpotifyLocalAPI _spotify; private bool _spotifySetupBusy; @@ -100,6 +156,16 @@ namespace Artemis.Modules.Effects.WindowsProfile dataModel.Cpu.Core7Usage = (int) _cores[6].NextValue(); if (_cores[7] != null) dataModel.Cpu.Core8Usage = (int) _cores[7].NextValue(); + + //From Ted - Let's get overall RAM and CPU usage here + dataModel.Performance.CPUUsage = (int)_overallCPU.NextValue(); + + Int64 phav = PerformanceInfo.GetPhysicalAvailableMemoryInMiB(); + Int64 tot = PerformanceInfo.GetTotalMemoryInMiB(); + decimal percentFree = ((decimal)phav / (decimal)tot) * 100; + decimal percentOccupied = 100 - percentFree; + + dataModel.Performance.RAMUsage = (int)percentOccupied; } public override List GetRenderLayers(bool renderMice, bool renderHeadsets) @@ -107,6 +173,17 @@ namespace Artemis.Modules.Effects.WindowsProfile return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets, false); } + public static PerformanceCounter GetOverallPerformanceCounter() + { + PerformanceCounter cpuCounter = new PerformanceCounter(); + + cpuCounter.CategoryName = "Processor"; + cpuCounter.CounterName = "% Processor Time"; + cpuCounter.InstanceName = "_Total"; + + return cpuCounter; + } + public static List GetPerformanceCounters() { var performanceCounters = new List(); From 13e298a9e8969a16e67bca7730f892cd372d438a Mon Sep 17 00:00:00 2001 From: Ted Date: Sat, 11 Jun 2016 15:02:45 -0700 Subject: [PATCH 03/10] Added Overall CPU and RAM resource monitors for Windows Profile. --- .../Modules/Effects/WindowsProfile/WindowsProfileModel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs index 906ed42e1..630f5bd01 100644 --- a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs +++ b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs @@ -119,6 +119,7 @@ namespace Artemis.Modules.Effects.WindowsProfile _cores.Add(null); coreCount++; } + _overallCPU = GetOverallPerformanceCounter(); } catch (InvalidOperationException) { @@ -129,7 +130,7 @@ namespace Artemis.Modules.Effects.WindowsProfile private void UpdateCpu(WindowsProfileDataModel dataModel) { - if (_cores == null) + if (_cores == null || _overallCPU == null) return; // CPU is only updated every 15 frames, the performance counter gives 0 if updated too often From df35b8e5b82238c3e2102fab07afb501840cd089 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Sun, 12 Jun 2016 10:51:35 +0200 Subject: [PATCH 04/10] Disabled Bubbles for now, moved CPUUsage to Cpu class in Windows Profile --- .../InjectionModules/ArtemisModules.cs | 2 +- .../WindowsProfile/WindowsProfileDataModel.cs | 2 +- .../WindowsProfile/WindowsProfileModel.cs | 87 +++++++++---------- 3 files changed, 41 insertions(+), 50 deletions(-) diff --git a/Artemis/Artemis/InjectionModules/ArtemisModules.cs b/Artemis/Artemis/InjectionModules/ArtemisModules.cs index 2ec4602f1..531797855 100644 --- a/Artemis/Artemis/InjectionModules/ArtemisModules.cs +++ b/Artemis/Artemis/InjectionModules/ArtemisModules.cs @@ -27,7 +27,7 @@ namespace Artemis.InjectionModules // Effects Bind().To().InSingletonScope(); Bind().To().InSingletonScope(); - Bind().To().InSingletonScope(); + //Bind().To().InSingletonScope(); TODO: Performance Bind().To().InSingletonScope(); // Games diff --git a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs index c44c5a36a..be27cad22 100644 --- a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs +++ b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs @@ -18,6 +18,7 @@ namespace Artemis.Modules.Effects.WindowsProfile public class CpuDataModel { + public int TotalUsage { get; set; } public int Core1Usage { get; set; } public int Core2Usage { get; set; } public int Core3Usage { get; set; } @@ -30,7 +31,6 @@ namespace Artemis.Modules.Effects.WindowsProfile public class PerformanceDataModel { - public int CPUUsage { get; set; } public int RAMUsage { get; set; } } diff --git a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs index 630f5bd01..03d1843ee 100644 --- a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs +++ b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Artemis.Managers; @@ -8,15 +9,34 @@ using Artemis.Models; using Artemis.Models.Profiles; using Ninject.Extensions.Logging; using SpotifyAPI.Local; -using System.Runtime.InteropServices; namespace Artemis.Modules.Effects.WindowsProfile { - static class PerformanceInfo + internal static class PerformanceInfo { [DllImport("psapi.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetPerformanceInfo([Out] out PerformanceInformation PerformanceInformation, [In] int Size); + public static extern bool GetPerformanceInfo([Out] out PerformanceInformation performanceInformation, [In] int size); + + public static long GetPhysicalAvailableMemoryInMiB() + { + var pi = new PerformanceInformation(); + if (GetPerformanceInfo(out pi, Marshal.SizeOf(pi))) + { + return Convert.ToInt64(pi.PhysicalAvailable.ToInt64()*pi.PageSize.ToInt64()/1048576); + } + return -1; + } + + public static long GetTotalMemoryInMiB() + { + var pi = new PerformanceInformation(); + if (GetPerformanceInfo(out pi, Marshal.SizeOf(pi))) + { + return Convert.ToInt64(pi.PhysicalTotal.ToInt64()*pi.PageSize.ToInt64()/1048576); + } + return -1; + } [StructLayout(LayoutKind.Sequential)] public struct PerformanceInformation @@ -36,45 +56,16 @@ namespace Artemis.Modules.Effects.WindowsProfile public int ProcessCount; public int ThreadCount; } - - public static Int64 GetPhysicalAvailableMemoryInMiB() - { - PerformanceInformation pi = new PerformanceInformation(); - if (GetPerformanceInfo(out pi, Marshal.SizeOf(pi))) - { - return Convert.ToInt64((pi.PhysicalAvailable.ToInt64() * pi.PageSize.ToInt64() / 1048576)); - } - else - { - return -1; - } - - } - - public static Int64 GetTotalMemoryInMiB() - { - PerformanceInformation pi = new PerformanceInformation(); - if (GetPerformanceInfo(out pi, Marshal.SizeOf(pi))) - { - return Convert.ToInt64((pi.PhysicalTotal.ToInt64() * pi.PageSize.ToInt64() / 1048576)); - } - else - { - return -1; - } - - } } public class WindowsProfileModel : EffectModel { private readonly ILogger _logger; private List _cores; - private PerformanceCounter _overallCPU; private int _cpuFrames; + private PerformanceCounter _overallCpu; private SpotifyLocalAPI _spotify; private bool _spotifySetupBusy; - private bool _triedCpuFix; public WindowsProfileModel(ILogger logger, MainManager mainManager, WindowsProfileSettings settings) : base(mainManager, new WindowsProfileDataModel()) @@ -119,20 +110,19 @@ namespace Artemis.Modules.Effects.WindowsProfile _cores.Add(null); coreCount++; } - _overallCPU = GetOverallPerformanceCounter(); + _overallCpu = GetOverallPerformanceCounter(); } catch (InvalidOperationException) { _logger.Warn("Failed to setup CPU information, try running \"lodctr /R\" as administrator."); } - } private void UpdateCpu(WindowsProfileDataModel dataModel) { - if (_cores == null || _overallCPU == null) + if (_cores == null || _overallCpu == null) return; - + // CPU is only updated every 15 frames, the performance counter gives 0 if updated too often _cpuFrames++; if (_cpuFrames < 16) @@ -159,14 +149,14 @@ namespace Artemis.Modules.Effects.WindowsProfile dataModel.Cpu.Core8Usage = (int) _cores[7].NextValue(); //From Ted - Let's get overall RAM and CPU usage here - dataModel.Performance.CPUUsage = (int)_overallCPU.NextValue(); + dataModel.Cpu.TotalUsage = (int) _overallCpu.NextValue(); - Int64 phav = PerformanceInfo.GetPhysicalAvailableMemoryInMiB(); - Int64 tot = PerformanceInfo.GetTotalMemoryInMiB(); - decimal percentFree = ((decimal)phav / (decimal)tot) * 100; - decimal percentOccupied = 100 - percentFree; + var phav = PerformanceInfo.GetPhysicalAvailableMemoryInMiB(); + var tot = PerformanceInfo.GetTotalMemoryInMiB(); + var percentFree = phav/(decimal) tot*100; + var percentOccupied = 100 - percentFree; - dataModel.Performance.RAMUsage = (int)percentOccupied; + dataModel.Performance.RAMUsage = (int) percentOccupied; } public override List GetRenderLayers(bool renderMice, bool renderHeadsets) @@ -176,11 +166,12 @@ namespace Artemis.Modules.Effects.WindowsProfile public static PerformanceCounter GetOverallPerformanceCounter() { - PerformanceCounter cpuCounter = new PerformanceCounter(); - - cpuCounter.CategoryName = "Processor"; - cpuCounter.CounterName = "% Processor Time"; - cpuCounter.InstanceName = "_Total"; + var cpuCounter = new PerformanceCounter + { + CategoryName = "Processor", + CounterName = "% Processor Time", + InstanceName = "_Total" + }; return cpuCounter; } From 12ddcc3600d564bd64902108560733b2e7e98024 Mon Sep 17 00:00:00 2001 From: Thoth2020 Date: Sun, 12 Jun 2016 15:10:02 -0700 Subject: [PATCH 05/10] Update DeviceManager.cs Attempt #2 at Issue 93 fix --- Artemis/Artemis/Managers/DeviceManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Artemis/Artemis/Managers/DeviceManager.cs b/Artemis/Artemis/Managers/DeviceManager.cs index 3a9cfe5a2..0ccf65536 100644 --- a/Artemis/Artemis/Managers/DeviceManager.cs +++ b/Artemis/Artemis/Managers/DeviceManager.cs @@ -106,7 +106,7 @@ namespace Artemis.Managers var thread = new Thread( () => { - Thread.Sleep(500); + Thread.Sleep(500 * i); asynchEnable = keyboardProvider.CanEnable(); }); _logger.Warn("Failed enabling keyboard: {0}, re-attempt {1} of 10", keyboardProvider.Name, i); @@ -184,4 +184,4 @@ namespace Artemis.Managers } } } -} \ No newline at end of file +} From caf2e3b2ae3f0617feb01257b41b16237e0204f4 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Mon, 13 Jun 2016 11:38:46 +0200 Subject: [PATCH 06/10] Replaced DynamicLinq with DynamicExpresso --- Artemis/Artemis/Artemis.csproj | 8 ++-- .../Models/Profiles/LayerConditionModel.cs | 41 ++++++++++++++----- .../Modules/Effects/Bubbles/BubblesModel.cs | 1 + Artemis/Artemis/packages.config | 2 +- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj index a97a823b3..13907072e 100644 --- a/Artemis/Artemis/Artemis.csproj +++ b/Artemis/Artemis/Artemis.csproj @@ -148,6 +148,10 @@ ..\packages\CUE.NET.1.0.3\lib\net45\CUE.NET.dll True + + ..\packages\DynamicExpresso.Core.1.3.1.0\lib\net40\DynamicExpresso.Core.dll + True + ..\packages\gong-wpf-dragdrop.0.1.4.3\lib\net40\GongSolutions.Wpf.DragDrop.dll True @@ -205,10 +209,6 @@ - - ..\packages\System.Linq.Dynamic.1.0.6\lib\net40\System.Linq.Dynamic.dll - True - diff --git a/Artemis/Artemis/Models/Profiles/LayerConditionModel.cs b/Artemis/Artemis/Models/Profiles/LayerConditionModel.cs index 7b6fc0fb1..ee659913e 100644 --- a/Artemis/Artemis/Models/Profiles/LayerConditionModel.cs +++ b/Artemis/Artemis/Models/Profiles/LayerConditionModel.cs @@ -1,12 +1,19 @@ -using System.Collections.Generic; -using System.Linq.Dynamic; +using System; using Artemis.Models.Interfaces; using Artemis.Utilities; +using DynamicExpresso; namespace Artemis.Models.Profiles { public class LayerConditionModel { + private readonly Interpreter _interpreter; + + public LayerConditionModel() + { + _interpreter = new Interpreter(); + } + public string Field { get; set; } public string Value { get; set; } public string Operator { get; set; } @@ -22,15 +29,29 @@ namespace Artemis.Models.Profiles return false; // Put the subject in a list, allowing Dynamic Linq to be used. - var subjectList = new List {(T) subject}; - bool res; if (Type == "String") - res = subjectList.Where($"{Field}.ToLower() {Operator} @0", Value.ToLower()).Any(); - else if (Type == "Enum") - res = subjectList.Where($"{Field} {Operator} \"{Value}\"").Any(); - else - res = subjectList.Where($"{Field} {Operator} {Value}").Any(); - return res; + { + return _interpreter.Eval($"subject.{Field}.ToLower() {Operator} value", + new Parameter("subject", typeof(T), subject), + new Parameter("value", Value.ToLower())); + } + + Parameter rightParam = null; + switch (Type) + { + case "Enum": + var enumType = _interpreter.Eval($"subject.{Field}.GetType()", new Parameter("subject", typeof(T), subject)); + rightParam = new Parameter("value", Enum.Parse(enumType, Value)); + break; + case "Boolean": + rightParam = new Parameter("value", bool.Parse(Value)); + break; + case "Int32": + rightParam = new Parameter("value", int.Parse(Value)); + break; + } + + return _interpreter.Eval($"subject.{Field} {Operator} value", new Parameter("subject", typeof(T), subject), rightParam); } } } \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesModel.cs b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesModel.cs index 7addca167..d97fd8d0e 100644 --- a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesModel.cs +++ b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; +using System.Linq; using System.Windows; using Artemis.Managers; using Artemis.Models; diff --git a/Artemis/Artemis/packages.config b/Artemis/Artemis/packages.config index 48f0b98da..9c566c400 100644 --- a/Artemis/Artemis/packages.config +++ b/Artemis/Artemis/packages.config @@ -5,6 +5,7 @@ + @@ -20,7 +21,6 @@ - \ No newline at end of file From 47b9b78634c76dc2ac29ce91e6adff724c0ab286 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Mon, 13 Jun 2016 11:50:46 +0200 Subject: [PATCH 07/10] Formatting --- Artemis/Artemis/Managers/DeviceManager.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Artemis/Artemis/Managers/DeviceManager.cs b/Artemis/Artemis/Managers/DeviceManager.cs index 0ccf65536..64c922764 100644 --- a/Artemis/Artemis/Managers/DeviceManager.cs +++ b/Artemis/Artemis/Managers/DeviceManager.cs @@ -94,22 +94,23 @@ namespace Artemis.Managers if (!wasNull) ReleaseActiveKeyboard(); - bool asynchEnable = false; + var asynchEnable = false; if (keyboardProvider.CanEnable()) { FinishEnableKeyboard(keyboardProvider, oldKeyboard); } else { - for (int i = 1; i <= 10; i++) + for (var i = 1; i <= 10; i++) { - var thread = new Thread( - () => + var thread = new Thread(() => { - Thread.Sleep(500 * i); + Thread.Sleep(500*i); asynchEnable = keyboardProvider.CanEnable(); }); + _logger.Warn("Failed enabling keyboard: {0}, re-attempt {1} of 10", keyboardProvider.Name, i); + thread.Start(); if (asynchEnable) break; @@ -125,10 +126,9 @@ namespace Artemis.Managers _logger.Warn("Failed enabling keyboard: {0}", keyboardProvider.Name); ChangingKeyboard = false; return; - } + } FinishEnableKeyboard(keyboardProvider, oldKeyboard); } - } } @@ -184,4 +184,4 @@ namespace Artemis.Managers } } } -} +} \ No newline at end of file From b546d98db0ebaeba75d6fb935b1181d86ccd3178 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Mon, 13 Jun 2016 20:17:31 +0200 Subject: [PATCH 08/10] All device enable methods are now async. This fixes #104 --- .../Corsair/CorsairHeadsets.cs | 20 ++-- .../DeviceProviders/Corsair/CorsairMice.cs | 19 ++-- .../DeviceProviders/Corsair/CorsairRGB.cs | 34 ++++++- .../Artemis/DeviceProviders/DeviceProvider.cs | 12 ++- .../DeviceProviders/KeyboardProvider.cs | 16 ++- .../InjectionModules/ArtemisModules.cs | 2 +- Artemis/Artemis/Managers/DeviceManager.cs | 97 +++++++++---------- Artemis/Artemis/Managers/EffectManager.cs | 2 +- Artemis/Artemis/Managers/LoopManager.cs | 9 +- Artemis/Artemis/Managers/MainManager.cs | 3 +- Artemis/Artemis/Managers/ProfileManager.cs | 2 +- .../VolumeDisplay/VolumeDisplayModel.cs | 3 + .../Artemis/Services/MetroDialogService.cs | 11 ++- .../Flyouts/FlyoutSettingsViewModel.cs | 2 +- 14 files changed, 153 insertions(+), 79 deletions(-) diff --git a/Artemis/Artemis/DeviceProviders/Corsair/CorsairHeadsets.cs b/Artemis/Artemis/DeviceProviders/Corsair/CorsairHeadsets.cs index 8090d1dcb..fdedd3c49 100644 --- a/Artemis/Artemis/DeviceProviders/Corsair/CorsairHeadsets.cs +++ b/Artemis/Artemis/DeviceProviders/Corsair/CorsairHeadsets.cs @@ -46,18 +46,20 @@ namespace Artemis.DeviceProviders.Corsair var visual = new DrawingVisual(); using (var c = visual.RenderOpen()) c.DrawRectangle(brush, null, rect); - var img = ImageUtilities.DrawinVisualToBitmap(visual, rect); - var ledIndex = 0; - // Color each LED according to one of the pixels - foreach (var corsairLed in CueSDK.HeadsetSDK.Leds) + using (var img = ImageUtilities.DrawinVisualToBitmap(visual, rect)) { - corsairLed.Color = ledIndex == 0 - ? img.GetPixel(0, 0) - : img.GetPixel((ledIndex + 1)*20 - 1, (ledIndex + 1)*20 - 1); - ledIndex++; - } + var ledIndex = 0; + // Color each LED according to one of the pixels + foreach (var corsairLed in CueSDK.HeadsetSDK.Leds) + { + corsairLed.Color = ledIndex == 0 + ? img.GetPixel(0, 0) + : img.GetPixel((ledIndex + 1)*20 - 1, (ledIndex + 1)*20 - 1); + ledIndex++; + } + } // Flush is required for headset to work reliably on CUE2 for some reason CueSDK.HeadsetSDK.Update(true); } diff --git a/Artemis/Artemis/DeviceProviders/Corsair/CorsairMice.cs b/Artemis/Artemis/DeviceProviders/Corsair/CorsairMice.cs index 9d4763851..3e08f941c 100644 --- a/Artemis/Artemis/DeviceProviders/Corsair/CorsairMice.cs +++ b/Artemis/Artemis/DeviceProviders/Corsair/CorsairMice.cs @@ -46,18 +46,19 @@ namespace Artemis.DeviceProviders.Corsair var visual = new DrawingVisual(); using (var c = visual.RenderOpen()) c.DrawRectangle(brush, null, rect); - var img = ImageUtilities.DrawinVisualToBitmap(visual, rect); - var ledIndex = 0; - // Color each LED according to one of the pixels - foreach (var corsairLed in CueSDK.MouseSDK.Leds) + using (var img = ImageUtilities.DrawinVisualToBitmap(visual, rect)) { - corsairLed.Color = ledIndex == 0 - ? img.GetPixel(0, 0) - : img.GetPixel((ledIndex + 1)*20 - 1, (ledIndex + 1)*20 - 1); - ledIndex++; + var ledIndex = 0; + // Color each LED according to one of the pixels + foreach (var corsairLed in CueSDK.MouseSDK.Leds) + { + corsairLed.Color = ledIndex == 0 + ? img.GetPixel(0, 0) + : img.GetPixel((ledIndex + 1)*20 - 1, (ledIndex + 1)*20 - 1); + ledIndex++; + } } - CueSDK.MouseSDK.Update(); } diff --git a/Artemis/Artemis/DeviceProviders/Corsair/CorsairRGB.cs b/Artemis/Artemis/DeviceProviders/Corsair/CorsairRGB.cs index ddc637a53..ba375a6d7 100644 --- a/Artemis/Artemis/DeviceProviders/Corsair/CorsairRGB.cs +++ b/Artemis/Artemis/DeviceProviders/Corsair/CorsairRGB.cs @@ -1,5 +1,6 @@ using System.Drawing; using System.Threading; +using System.Threading.Tasks; using System.Windows; using Artemis.Properties; using Artemis.Utilities; @@ -7,6 +8,7 @@ using CUE.NET; using CUE.NET.Brushes; using CUE.NET.Devices.Generic.Enums; using CUE.NET.Devices.Keyboard; +using MahApps.Metro.Controls.Dialogs; using Ninject.Extensions.Logging; using Point = System.Drawing.Point; @@ -14,7 +16,6 @@ namespace Artemis.DeviceProviders.Corsair { public class CorsairRGB : KeyboardProvider { - public ILogger Logger { get; set; } private CorsairKeyboard _keyboard; private ImageBrush _keyboardBrush; @@ -28,6 +29,37 @@ namespace Artemis.DeviceProviders.Corsair "If needed, you can select a different keyboard in Artemis under settings."; } + public ILogger Logger { get; set; } + + public sealed override Task CanEnableAsync(ProgressDialogController dialog) + { + return Task.Run(() => + { + // This will skip the check-loop if the SDK is initialized + if (CueSDK.IsInitialized) + return CueSDK.IsSDKAvailable(CorsairDeviceType.Keyboard); + for (var tries = 0; tries < 9; tries++) + { + // Stop trying if cancelled by user + if (dialog != null && dialog.IsCanceled) + { + dialog.SetIndeterminate(); + return false; + } + dialog?.SetProgress(0.1*(tries + 1)); + + if (CueSDK.IsSDKAvailable(CorsairDeviceType.Keyboard)) + { + dialog?.SetIndeterminate(); + return true; + } + Thread.Sleep(2000); + } + dialog?.SetIndeterminate(); + return false; + }); + } + public override bool CanEnable() { // This will skip the check-loop if the SDK is initialized diff --git a/Artemis/Artemis/DeviceProviders/DeviceProvider.cs b/Artemis/Artemis/DeviceProviders/DeviceProvider.cs index f86ee2e64..c244eb73a 100644 --- a/Artemis/Artemis/DeviceProviders/DeviceProvider.cs +++ b/Artemis/Artemis/DeviceProviders/DeviceProvider.cs @@ -1,4 +1,5 @@ -using System.Windows.Media; +using System.Threading.Tasks; +using System.Windows.Media; namespace Artemis.DeviceProviders { @@ -29,6 +30,15 @@ namespace Artemis.DeviceProviders /// Disables the device /// public abstract void Disable(); + + /// + /// Tries to enable the device and updates CanUse accordingly asynchronously + /// + /// + public Task TryEnableAsync() + { + return Task.Run(() => TryEnable()); + } } public enum DeviceType diff --git a/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs b/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs index 54a157b60..7498c6279 100644 --- a/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs +++ b/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs @@ -1,6 +1,8 @@ using System; using System.Drawing; +using System.Threading.Tasks; using System.Windows; +using MahApps.Metro.Controls.Dialogs; using Brush = System.Windows.Media.Brush; using Size = System.Windows.Size; @@ -18,7 +20,6 @@ namespace Artemis.DeviceProviders public int Height { get; set; } public int Width { get; set; } public string CantEnableText { get; set; } - public PreviewSettings PreviewSettings { get; set; } public abstract bool CanEnable(); @@ -39,6 +40,16 @@ namespace Artemis.DeviceProviders public Rect KeyboardRectangle(int scale) => new Rect(new Size(Width*scale, Height*scale)); + public virtual Task CanEnableAsync(ProgressDialogController dialog) + { + return Task.Run(() => CanEnable()); + } + + public virtual Task EnableAsync(ProgressDialogController dialog) + { + return Task.Run(() => Enable()); + } + public override void UpdateDevice(Brush brush) { throw new NotImplementedException("KeyboardProvider doesn't implement UpdateDevice, use DrawBitmap instead."); @@ -46,7 +57,8 @@ namespace Artemis.DeviceProviders public override bool TryEnable() { - throw new NotImplementedException("KeyboardProvider doesn't implement TryEnable, use CanEnable instead."); + throw new NotImplementedException( + "KeyboardProvider doesn't implement TryEnable, use CanEnableAsync instead."); } } diff --git a/Artemis/Artemis/InjectionModules/ArtemisModules.cs b/Artemis/Artemis/InjectionModules/ArtemisModules.cs index 531797855..2ec4602f1 100644 --- a/Artemis/Artemis/InjectionModules/ArtemisModules.cs +++ b/Artemis/Artemis/InjectionModules/ArtemisModules.cs @@ -27,7 +27,7 @@ namespace Artemis.InjectionModules // Effects Bind().To().InSingletonScope(); Bind().To().InSingletonScope(); - //Bind().To().InSingletonScope(); TODO: Performance + Bind().To().InSingletonScope(); Bind().To().InSingletonScope(); // Games diff --git a/Artemis/Artemis/Managers/DeviceManager.cs b/Artemis/Artemis/Managers/DeviceManager.cs index 64c922764..a805481a9 100644 --- a/Artemis/Artemis/Managers/DeviceManager.cs +++ b/Artemis/Artemis/Managers/DeviceManager.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Threading.Tasks; using Artemis.DeviceProviders; using Artemis.Events; using Artemis.Services; using Artemis.Settings; using Caliburn.Micro; +using MahApps.Metro.Controls.Dialogs; using Ninject; using Ninject.Extensions.Logging; @@ -65,8 +67,14 @@ namespace Artemis.Managers /// Enables the given keyboard /// /// - public void EnableKeyboard(KeyboardProvider keyboardProvider) + public async void EnableKeyboard(KeyboardProvider keyboardProvider) { + if (ChangingKeyboard) + return; + + // Store the old keyboard so it can be used in the event we're raising later + var oldKeyboard = ActiveKeyboard; + lock (this) { ChangingKeyboard = true; @@ -80,9 +88,6 @@ namespace Artemis.Managers return; } - // Store the old keyboard so it can be used in the event we're raising later - var oldKeyboard = ActiveKeyboard; - var wasNull = false; if (ActiveKeyboard == null) { @@ -90,69 +95,63 @@ namespace Artemis.Managers ActiveKeyboard = keyboardProvider; } - _logger.Debug("Enabling keyboard: {0}", keyboardProvider.Name); - if (!wasNull) ReleaseActiveKeyboard(); - var asynchEnable = false; - if (keyboardProvider.CanEnable()) - { - FinishEnableKeyboard(keyboardProvider, oldKeyboard); - } - else - { - for (var i = 1; i <= 10; i++) - { - var thread = new Thread(() => - { - Thread.Sleep(500*i); - asynchEnable = keyboardProvider.CanEnable(); - }); - - _logger.Warn("Failed enabling keyboard: {0}, re-attempt {1} of 10", keyboardProvider.Name, i); - - thread.Start(); - if (asynchEnable) - break; - } - - if (!asynchEnable) - { - // Disable everything if there's no active keyboard found - DialogService.ShowErrorMessageBox(keyboardProvider.CantEnableText); - ActiveKeyboard = null; - General.Default.LastKeyboard = null; - General.Default.Save(); - _logger.Warn("Failed enabling keyboard: {0}", keyboardProvider.Name); - ChangingKeyboard = false; - return; - } - FinishEnableKeyboard(keyboardProvider, oldKeyboard); - } } - } - private void FinishEnableKeyboard(KeyboardProvider keyboardProvider, KeyboardProvider oldKeyboard) - { + // TODO: LoopManager shouldn't be running at this point + _logger.Debug("Enabling keyboard: {0}", keyboardProvider.Name); + + // Create a dialog to let the user know Artemis hasn't frozen + ProgressDialogController dialog = null; + if (DialogService.GetActiveWindow() != null) + { + dialog = await DialogService.ShowProgressDialog("Enabling keyboard", + $"Checking if keyboard '{keyboardProvider.Name}' can be enabled...", true); + + // May seem a bit cheesy, but it's tidier to have the animation finish + await Task.Delay(500); + } + dialog?.SetIndeterminate(); + + var canEnable = await keyboardProvider.CanEnableAsync(dialog); + if (!canEnable) + { + if (dialog != null) + await dialog.CloseAsync(); + + DialogService.ShowErrorMessageBox(keyboardProvider.CantEnableText); + ActiveKeyboard = null; + General.Default.LastKeyboard = null; + General.Default.Save(); + _logger.Warn("Failed enabling keyboard: {0}", keyboardProvider.Name); + ChangingKeyboard = false; + return; + } + + dialog?.SetMessage($"Enabling keyboard: {keyboardProvider.Name}..."); ActiveKeyboard = keyboardProvider; - ActiveKeyboard.Enable(); + await ActiveKeyboard.EnableAsync(dialog); General.Default.LastKeyboard = ActiveKeyboard.Name; General.Default.Save(); EnableUsableDevices(); - - ChangingKeyboard = false; _events.PublishOnUIThread(new ActiveKeyboardChanged(oldKeyboard, ActiveKeyboard)); _logger.Debug("Enabled keyboard: {0}", keyboardProvider.Name); + + if (dialog != null) + await dialog.CloseAsync(); + + ChangingKeyboard = false; } private void EnableUsableDevices() { foreach (var mouseProvider in MiceProviders) - mouseProvider.TryEnable(); + mouseProvider.TryEnableAsync(); foreach (var headsetProvider in HeadsetProviders) - headsetProvider.TryEnable(); + headsetProvider.TryEnableAsync(); } /// diff --git a/Artemis/Artemis/Managers/EffectManager.cs b/Artemis/Artemis/Managers/EffectManager.cs index a7143e039..0605ac372 100644 --- a/Artemis/Artemis/Managers/EffectManager.cs +++ b/Artemis/Artemis/Managers/EffectManager.cs @@ -134,7 +134,7 @@ namespace Artemis.Managers if (loopManager != null && !loopManager.Running) { _logger.Debug("Starting LoopManager for effect change"); - loopManager.Start(); + loopManager.StartAsync(); } _logger.Debug("Changed active effect to: {0}", effectModel.Name); diff --git a/Artemis/Artemis/Managers/LoopManager.cs b/Artemis/Artemis/Managers/LoopManager.cs index 6cb6d12c3..ef358b3aa 100644 --- a/Artemis/Artemis/Managers/LoopManager.cs +++ b/Artemis/Artemis/Managers/LoopManager.cs @@ -1,6 +1,7 @@ using System; using System.Drawing; using System.Linq; +using System.Threading.Tasks; using System.Timers; using Ninject.Extensions.Logging; using Brush = System.Windows.Media.Brush; @@ -40,7 +41,12 @@ namespace Artemis.Managers _loopTimer.Dispose(); } - public void Start() + public Task StartAsync() + { + return Task.Run(() => Start()); + } + + private void Start() { if (Running) return; @@ -157,6 +163,7 @@ namespace Artemis.Managers // Update the keyboard _deviceManager.ActiveKeyboard?.DrawBitmap(bitmap); + bitmap.Dispose(); } } } diff --git a/Artemis/Artemis/Managers/MainManager.cs b/Artemis/Artemis/Managers/MainManager.cs index c2c8ca63d..4a25ea4af 100644 --- a/Artemis/Artemis/Managers/MainManager.cs +++ b/Artemis/Artemis/Managers/MainManager.cs @@ -46,7 +46,6 @@ namespace Artemis.Managers ProgramEnabled = false; Running = false; - // TODO: Dependency inject utilities? KeyboardHook = new KeyboardHook(); @@ -94,7 +93,7 @@ namespace Artemis.Managers { _logger.Debug("Enabling program"); ProgramEnabled = true; - LoopManager.Start(); + LoopManager.StartAsync(); _events.PublishOnUIThread(new ToggleEnabled(ProgramEnabled)); } diff --git a/Artemis/Artemis/Managers/ProfileManager.cs b/Artemis/Artemis/Managers/ProfileManager.cs index ed30fc18e..637743b75 100644 --- a/Artemis/Artemis/Managers/ProfileManager.cs +++ b/Artemis/Artemis/Managers/ProfileManager.cs @@ -68,7 +68,7 @@ namespace Artemis.Managers } // LoopManager might be running, this method won't do any harm in that case. - _loopManager.Start(); + _loopManager.StartAsync(); if (!ReferenceEquals(ProfilePreviewModel.Profile, activePreview.ProfileEditor.SelectedProfile)) ProfilePreviewModel.Profile = activePreview.ProfileEditor.SelectedProfile; diff --git a/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs b/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs index e6dda0fd2..9bad9ac89 100644 --- a/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs +++ b/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs @@ -94,6 +94,9 @@ namespace Artemis.Modules.Overlays.VolumeDisplay public override void RenderOverlay(ref Bitmap keyboard, ref Brush mouse, ref Brush headset, bool renderMice, bool renderHeadsets) { + if (MainManager.DeviceManager.ActiveKeyboard == null) + return; + keyboard = GenerateBitmap(keyboard ?? MainManager.DeviceManager.ActiveKeyboard.KeyboardBitmap(4)); } } diff --git a/Artemis/Artemis/Services/MetroDialogService.cs b/Artemis/Artemis/Services/MetroDialogService.cs index 10954f6bc..7bdb4ab62 100644 --- a/Artemis/Artemis/Services/MetroDialogService.cs +++ b/Artemis/Artemis/Services/MetroDialogService.cs @@ -32,7 +32,7 @@ namespace Artemis.Services { public class MetroDialogService : DialogService { - private MetroWindow GetActiveWindow() + public MetroWindow GetActiveWindow() { MetroWindow window = null; @@ -128,5 +128,14 @@ namespace Artemis.Services return res.Value; } + + public Task ShowProgressDialog(string title, string message, bool isCancelable = false, + MetroDialogSettings settings = null) + { + return GetActiveWindow() == null + ? null + : GetActiveWindow().Dispatcher.Invoke(() => + GetActiveWindow()?.ShowProgressAsync(title, message, isCancelable, settings)); + } } } \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs b/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs index 30178eb03..e4a5f8a85 100644 --- a/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs +++ b/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs @@ -167,7 +167,7 @@ namespace Artemis.ViewModels.Flyouts if (keyboard != null) { MainManager.DeviceManager.EnableKeyboard(keyboard); - MainManager.LoopManager.Start(); + MainManager.LoopManager.StartAsync(); } else MainManager.DeviceManager.ReleaseActiveKeyboard(true); From 6627a48d932c355a71ada2748e387b3d9e15dd13 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Tue, 14 Jun 2016 11:56:27 +0200 Subject: [PATCH 09/10] Cleaned up CorsairRGB keyboard provider, added dialog on window open if a keyboard is still being enabled. --- .../DeviceProviders/Corsair/CorsairRGB.cs | 44 +------------------ .../DeviceProviders/KeyboardProvider.cs | 43 ++++++++++++++++-- Artemis/Artemis/Managers/DeviceManager.cs | 1 - Artemis/Artemis/Managers/MainManager.cs | 12 ++--- .../Artemis/Services/MetroDialogService.cs | 25 +++-------- Artemis/Artemis/ViewModels/ShellViewModel.cs | 12 ++++- .../Artemis/ViewModels/SystemTrayViewModel.cs | 34 +++++++++++++- 7 files changed, 96 insertions(+), 75 deletions(-) diff --git a/Artemis/Artemis/DeviceProviders/Corsair/CorsairRGB.cs b/Artemis/Artemis/DeviceProviders/Corsair/CorsairRGB.cs index ba375a6d7..6cef35822 100644 --- a/Artemis/Artemis/DeviceProviders/Corsair/CorsairRGB.cs +++ b/Artemis/Artemis/DeviceProviders/Corsair/CorsairRGB.cs @@ -1,6 +1,4 @@ using System.Drawing; -using System.Threading; -using System.Threading.Tasks; using System.Windows; using Artemis.Properties; using Artemis.Utilities; @@ -8,7 +6,6 @@ using CUE.NET; using CUE.NET.Brushes; using CUE.NET.Devices.Generic.Enums; using CUE.NET.Devices.Keyboard; -using MahApps.Metro.Controls.Dialogs; using Ninject.Extensions.Logging; using Point = System.Drawing.Point; @@ -31,48 +28,9 @@ namespace Artemis.DeviceProviders.Corsair public ILogger Logger { get; set; } - public sealed override Task CanEnableAsync(ProgressDialogController dialog) - { - return Task.Run(() => - { - // This will skip the check-loop if the SDK is initialized - if (CueSDK.IsInitialized) - return CueSDK.IsSDKAvailable(CorsairDeviceType.Keyboard); - for (var tries = 0; tries < 9; tries++) - { - // Stop trying if cancelled by user - if (dialog != null && dialog.IsCanceled) - { - dialog.SetIndeterminate(); - return false; - } - dialog?.SetProgress(0.1*(tries + 1)); - - if (CueSDK.IsSDKAvailable(CorsairDeviceType.Keyboard)) - { - dialog?.SetIndeterminate(); - return true; - } - Thread.Sleep(2000); - } - dialog?.SetIndeterminate(); - return false; - }); - } - public override bool CanEnable() { - // This will skip the check-loop if the SDK is initialized - if (CueSDK.IsInitialized) - return CueSDK.IsSDKAvailable(CorsairDeviceType.Keyboard); - - for (var tries = 0; tries < 9; tries++) - { - if (CueSDK.IsSDKAvailable(CorsairDeviceType.Keyboard)) - return true; - Thread.Sleep(2000); - } - return false; + return CueSDK.IsSDKAvailable(CorsairDeviceType.Keyboard); } /// diff --git a/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs b/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs index 7498c6279..ab0984904 100644 --- a/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs +++ b/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using System.Threading; using System.Threading.Tasks; using System.Windows; using MahApps.Metro.Controls.Dialogs; @@ -40,12 +41,48 @@ namespace Artemis.DeviceProviders public Rect KeyboardRectangle(int scale) => new Rect(new Size(Width*scale, Height*scale)); - public virtual Task CanEnableAsync(ProgressDialogController dialog) + /// + /// Runs CanEnable asynchronously multiple times until successful, cancelled or max tries reached + /// + /// + /// + public Task CanEnableAsync(ProgressDialogController dialog) { - return Task.Run(() => CanEnable()); + return Task.Run(() => + { + for (var tries = 1; tries <= 10; tries++) + { + // Dialog interaction + if (dialog != null) + { + // Stop if cancelled by user + if (dialog.IsCanceled) + { + dialog.SetIndeterminate(); + return false; + } + // Updated progress to indicate how much tries are left + dialog.SetProgress(0.1*tries); + } + + if (CanEnable()) + { + dialog?.SetIndeterminate(); + return true; + } + Thread.Sleep(2000); + } + dialog?.SetIndeterminate(); + return false; + }); } - public virtual Task EnableAsync(ProgressDialogController dialog) + /// + /// Runs CanEnable asynchronously + /// + /// + /// + public Task EnableAsync(ProgressDialogController dialog) { return Task.Run(() => Enable()); } diff --git a/Artemis/Artemis/Managers/DeviceManager.cs b/Artemis/Artemis/Managers/DeviceManager.cs index a805481a9..b3eba9ba9 100644 --- a/Artemis/Artemis/Managers/DeviceManager.cs +++ b/Artemis/Artemis/Managers/DeviceManager.cs @@ -99,7 +99,6 @@ namespace Artemis.Managers ReleaseActiveKeyboard(); } - // TODO: LoopManager shouldn't be running at this point _logger.Debug("Enabling keyboard: {0}", keyboardProvider.Name); // Create a dialog to let the user know Artemis hasn't frozen diff --git a/Artemis/Artemis/Managers/MainManager.cs b/Artemis/Artemis/Managers/MainManager.cs index 4a25ea4af..3d612aa7b 100644 --- a/Artemis/Artemis/Managers/MainManager.cs +++ b/Artemis/Artemis/Managers/MainManager.cs @@ -78,12 +78,12 @@ namespace Artemis.Managers { _logger.Debug("Shutting down MainManager"); - _processTimer.Stop(); - _processTimer.Dispose(); - LoopManager.Stop(); - EffectManager.ActiveEffect.Dispose(); - GameStateWebServer.Stop(); - PipeServer.Stop(); + _processTimer?.Stop(); + _processTimer?.Dispose(); + LoopManager?.Stop(); + EffectManager?.ActiveEffect?.Dispose(); + GameStateWebServer?.Stop(); + PipeServer?.Stop(); } /// diff --git a/Artemis/Artemis/Services/MetroDialogService.cs b/Artemis/Artemis/Services/MetroDialogService.cs index 7bdb4ab62..04465596e 100644 --- a/Artemis/Artemis/Services/MetroDialogService.cs +++ b/Artemis/Artemis/Services/MetroDialogService.cs @@ -102,26 +102,16 @@ namespace Artemis.Services }; if (initialDir != null) - { ofd.InitialDirectory = initialDir; - } - if (Application.Current.MainWindow != null) - { - res = ofd.ShowDialog(Application.Current.MainWindow); - } - else - { - res = ofd.ShowDialog(); - } + res = Application.Current.MainWindow != null + ? ofd.ShowDialog(Application.Current.MainWindow) + : ofd.ShowDialog(); + if (res == true) - { lPath = ofd.FileName; - } else - { res = false; - } }); path = lPath; @@ -132,10 +122,9 @@ namespace Artemis.Services public Task ShowProgressDialog(string title, string message, bool isCancelable = false, MetroDialogSettings settings = null) { - return GetActiveWindow() == null - ? null - : GetActiveWindow().Dispatcher.Invoke(() => - GetActiveWindow()?.ShowProgressAsync(title, message, isCancelable, settings)); + var activeWindow = GetActiveWindow(); + return activeWindow?.Dispatcher.Invoke( + () => activeWindow.ShowProgressAsync(title, message, isCancelable, settings)); } } } \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/ShellViewModel.cs b/Artemis/Artemis/ViewModels/ShellViewModel.cs index b04887ff6..e7a6f4fba 100644 --- a/Artemis/Artemis/ViewModels/ShellViewModel.cs +++ b/Artemis/Artemis/ViewModels/ShellViewModel.cs @@ -1,4 +1,7 @@ using System.Linq; +using System.Threading.Tasks; +using Artemis.Managers; +using Artemis.Services; using Artemis.ViewModels.Abstract; using Artemis.ViewModels.Flyouts; using Caliburn.Micro; @@ -8,11 +11,16 @@ namespace Artemis.ViewModels { public sealed class ShellViewModel : Conductor.Collection.OneActive { + private readonly DeviceManager _deviceManager; + private readonly MetroDialogService _dialogService; private readonly BaseViewModel[] _viewModels; - public ShellViewModel(IKernel kernel, IEventAggregator events, BaseViewModel[] viewModels) + public ShellViewModel(IKernel kernel, IEventAggregator events, BaseViewModel[] viewModels, + DeviceManager deviceManager, MetroDialogService dialogService) { _viewModels = viewModels; + _deviceManager = deviceManager; + _dialogService = dialogService; events.Subscribe(this); @@ -34,7 +42,7 @@ namespace Artemis.ViewModels ActiveItem = _viewModels.FirstOrDefault(); } - + public void Settings() { Flyouts.First().IsOpen = !Flyouts.First().IsOpen; diff --git a/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs b/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs index dd5cc716d..1d0c43bdb 100644 --- a/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs +++ b/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs @@ -1,4 +1,6 @@ -using System.Windows; +using System; +using System.Threading.Tasks; +using System.Windows; using Artemis.Events; using Artemis.Managers; using Artemis.Services; @@ -41,7 +43,8 @@ namespace Artemis.ViewModels public bool CanShowWindow => !_shellViewModel.IsActive; - public bool CanHideWindow => _shellViewModel.IsActive; + public bool CanHideWindow => _shellViewModel.IsActive && !MainManager.DeviceManager.ChangingKeyboard; + public bool CanToggleEnabled => !MainManager.DeviceManager.ChangingKeyboard; public bool Enabled { @@ -114,9 +117,36 @@ namespace Artemis.ViewModels return; _checkedForUpdate = true; + + ShowKeyboardDialog(); Updater.CheckForUpdate(DialogService); } + private async void ShowKeyboardDialog() + { + NotifyOfPropertyChange(() => CanHideWindow); + NotifyOfPropertyChange(() => CanToggleEnabled); + + var dialog = await DialogService.ShowProgressDialog("Enabling keyboard", + "Artemis is still busy trying to enable your last used keyboard. " + + "Please what while the progress completes"); + dialog.SetIndeterminate(); + + while (MainManager.DeviceManager.ChangingKeyboard) + await Task.Delay(200); + + NotifyOfPropertyChange(() => CanHideWindow); + NotifyOfPropertyChange(() => CanToggleEnabled); + + try + { + await dialog.CloseAsync(); + } + catch (InvalidOperationException) + { + // Occurs when window is closed again, can't find a proper check for this + } + } public void HideWindow() { From aa7746e70a3a709107012727d3ae00bc6191a0d5 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Tue, 14 Jun 2016 12:28:38 +0200 Subject: [PATCH 10/10] Fixed keyboard change dialog causing crash when show on startup is enabled --- Artemis/Artemis/ViewModels/SystemTrayViewModel.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs b/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs index 1d0c43bdb..33cdc0fca 100644 --- a/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs +++ b/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using System.Windows; using Artemis.Events; @@ -20,13 +21,14 @@ namespace Artemis.ViewModels private bool _enabled; private string _toggleText; - public SystemTrayViewModel(IWindowManager windowManager, IEventAggregator events, ShellViewModel shellViewModel, + public SystemTrayViewModel(IWindowManager windowManager, IEventAggregator events, MetroDialogService dialogService, ShellViewModel shellViewModel, MainManager mainManager) { _windowManager = windowManager; _shellViewModel = shellViewModel; _checkedForUpdate = false; + DialogService = dialogService; MainManager = mainManager; events.Subscribe(this); @@ -36,7 +38,6 @@ namespace Artemis.ViewModels ShowWindow(); } - [Inject] public MetroDialogService DialogService { get; set; } public MainManager MainManager { get; set; } @@ -124,16 +125,19 @@ namespace Artemis.ViewModels private async void ShowKeyboardDialog() { + while(!_shellViewModel.IsActive) + await Task.Delay(200); + NotifyOfPropertyChange(() => CanHideWindow); NotifyOfPropertyChange(() => CanToggleEnabled); var dialog = await DialogService.ShowProgressDialog("Enabling keyboard", "Artemis is still busy trying to enable your last used keyboard. " + - "Please what while the progress completes"); + "Please wait while the progress completes"); dialog.SetIndeterminate(); while (MainManager.DeviceManager.ChangingKeyboard) - await Task.Delay(200); + await Task.Delay(10); NotifyOfPropertyChange(() => CanHideWindow); NotifyOfPropertyChange(() => CanToggleEnabled);