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/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 9308d6d86..87ead0241 100644 --- a/Artemis/Artemis/DeviceProviders/Corsair/CorsairRGB.cs +++ b/Artemis/Artemis/DeviceProviders/Corsair/CorsairRGB.cs @@ -1,5 +1,4 @@ using System.Drawing; -using System.Threading; using System.Windows; using Artemis.Properties; using Artemis.Utilities; @@ -14,7 +13,6 @@ namespace Artemis.DeviceProviders.Corsair { public class CorsairRGB : KeyboardProvider { - public ILogger Logger { get; set; } private CorsairKeyboard _keyboard; private ImageBrush _keyboardBrush; @@ -28,19 +26,11 @@ namespace Artemis.DeviceProviders.Corsair "If needed, you can select a different keyboard in Artemis under settings."; } + public ILogger Logger { get; set; } + 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/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..ab0984904 100644 --- a/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs +++ b/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs @@ -1,6 +1,9 @@ using System; using System.Drawing; +using System.Threading; +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 +21,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 +41,52 @@ namespace Artemis.DeviceProviders public Rect KeyboardRectangle(int scale) => new Rect(new Size(Width*scale, Height*scale)); + /// + /// Runs CanEnable asynchronously multiple times until successful, cancelled or max tries reached + /// + /// + /// + public Task CanEnableAsync(ProgressDialogController dialog) + { + 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; + }); + } + + /// + /// Runs CanEnable asynchronously + /// + /// + /// + public 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 +94,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/Managers/DeviceManager.cs b/Artemis/Artemis/Managers/DeviceManager.cs index 88f9637b6..b3eba9ba9 100644 --- a/Artemis/Artemis/Managers/DeviceManager.cs +++ b/Artemis/Artemis/Managers/DeviceManager.cs @@ -1,11 +1,14 @@ using System; 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; @@ -64,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; @@ -79,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) { @@ -89,43 +95,62 @@ namespace Artemis.Managers ActiveKeyboard = keyboardProvider; } - _logger.Debug("Enabling keyboard: {0}", keyboardProvider.Name); - if (!wasNull) ReleaseActiveKeyboard(); - - // Disable everything if there's no active keyboard found - 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; - } - - 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); } + + _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; + await ActiveKeyboard.EnableAsync(dialog); + + General.Default.LastKeyboard = ActiveKeyboard.Name; + General.Default.Save(); + + EnableUsableDevices(); + _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 5056e6f94..0ba6f8f61 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; @@ -42,7 +43,12 @@ namespace Artemis.Managers _keyboardBitmap?.Dispose(); } - public void Start() + public Task StartAsync() + { + return Task.Run(() => Start()); + } + + private void Start() { if (Running) return; diff --git a/Artemis/Artemis/Managers/MainManager.cs b/Artemis/Artemis/Managers/MainManager.cs index c2c8ca63d..3d612aa7b 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(); @@ -79,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(); } /// @@ -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/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/WindowsProfile/WindowsProfileDataModel.cs b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs index de9bc9206..be27cad22 100644 --- a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs +++ b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs @@ -8,14 +8,17 @@ 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; } } public class CpuDataModel { + public int TotalUsage { get; set; } public int Core1Usage { get; set; } public int Core2Usage { get; set; } public int Core3Usage { get; set; } @@ -26,6 +29,11 @@ namespace Artemis.Modules.Effects.WindowsProfile public int Core8Usage { get; set; } } + public class PerformanceDataModel + { + 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..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; @@ -11,14 +12,60 @@ using SpotifyAPI.Local; namespace Artemis.Modules.Effects.WindowsProfile { + 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 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 + { + 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 class WindowsProfileModel : EffectModel { private readonly ILogger _logger; private List _cores; 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()) @@ -63,19 +110,19 @@ namespace Artemis.Modules.Effects.WindowsProfile _cores.Add(null); coreCount++; } + _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) + 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) @@ -100,6 +147,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.Cpu.TotalUsage = (int) _overallCpu.NextValue(); + + var phav = PerformanceInfo.GetPhysicalAvailableMemoryInMiB(); + var tot = PerformanceInfo.GetTotalMemoryInMiB(); + var percentFree = phav/(decimal) tot*100; + var percentOccupied = 100 - percentFree; + + dataModel.Performance.RAMUsage = (int) percentOccupied; } public override List GetRenderLayers(bool renderMice, bool renderHeadsets) @@ -107,6 +164,18 @@ namespace Artemis.Modules.Effects.WindowsProfile return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets, false); } + public static PerformanceCounter GetOverallPerformanceCounter() + { + var cpuCounter = new PerformanceCounter + { + CategoryName = "Processor", + CounterName = "% Processor Time", + InstanceName = "_Total" + }; + + return cpuCounter; + } + public static List GetPerformanceCounters() { var performanceCounters = new List(); diff --git a/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs b/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs index 5ee938957..7a2af183c 100644 --- a/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs +++ b/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs @@ -81,7 +81,7 @@ namespace Artemis.Modules.Overlays.VolumeDisplay public override void RenderOverlay(Graphics keyboard, ref Brush mouse, ref Brush headset, bool renderMice, bool renderHeadsets) { - if (VolumeDisplay != null && VolumeDisplay.Ttl >= 1) + if (MainManager.DeviceManager.ActiveKeyboard != null && VolumeDisplay != null && VolumeDisplay.Ttl >= 1) VolumeDisplay.Draw(keyboard); } } diff --git a/Artemis/Artemis/Services/MetroDialogService.cs b/Artemis/Artemis/Services/MetroDialogService.cs index 10954f6bc..04465596e 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; @@ -102,31 +102,29 @@ 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; return res.Value; } + + public Task ShowProgressDialog(string title, string message, bool isCancelable = false, + MetroDialogSettings settings = null) + { + 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/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); 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..33cdc0fca 100644 --- a/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs +++ b/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs @@ -1,4 +1,7 @@ -using System.Windows; +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; using Artemis.Events; using Artemis.Managers; using Artemis.Services; @@ -18,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); @@ -34,14 +38,14 @@ namespace Artemis.ViewModels ShowWindow(); } - [Inject] public MetroDialogService DialogService { get; set; } public MainManager MainManager { get; set; } 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 +118,39 @@ namespace Artemis.ViewModels return; _checkedForUpdate = true; + + ShowKeyboardDialog(); Updater.CheckForUpdate(DialogService); } + 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 wait while the progress completes"); + dialog.SetIndeterminate(); + + while (MainManager.DeviceManager.ChangingKeyboard) + await Task.Delay(10); + + 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() { 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