diff --git a/Artemis/Artemis/DAL/ProfileProvider.cs b/Artemis/Artemis/DAL/ProfileProvider.cs index 2b770e74e..5d89fd42c 100644 --- a/Artemis/Artemis/DAL/ProfileProvider.cs +++ b/Artemis/Artemis/DAL/ProfileProvider.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Xml.Serialization; +using Artemis.KeyboardProviders; using Artemis.Models; using Artemis.Models.Profiles; @@ -27,10 +28,16 @@ namespace Artemis.DAL /// Get all profiles matching the provided game /// /// The game to match + /// The keyboard to match /// All profiles matching the provided game - public static List GetAll(GameModel game) + public static List GetAll(GameModel game, KeyboardProvider keyboard) { - return GetAll().Where(g => g.GameName.Equals(game.Name)).ToList(); + if (game == null) + throw new ArgumentNullException(nameof(game)); + if (keyboard == null) + throw new ArgumentNullException(nameof(keyboard)); + + return GetAll().Where(g => g.GameName.Equals(game.Name) && g.KeyboardName.Equals(keyboard.Name)).ToList(); } /// @@ -40,6 +47,8 @@ namespace Artemis.DAL /// The profile to add or update public static void AddOrUpdate(ProfileModel prof) { + if (prof == null) + throw new ArgumentNullException(nameof(prof)); if (!(prof.GameName?.Length > 1) || !(prof.KeyboardName?.Length > 1) || !(prof.Name?.Length > 1)) throw new ArgumentException("Profile is invalid. Name, GameName and KeyboardName are required"); diff --git a/Artemis/Artemis/Events/ActiveKeyboardChanged.cs b/Artemis/Artemis/Events/ActiveKeyboardChanged.cs index fb123dd86..a6656802d 100644 --- a/Artemis/Artemis/Events/ActiveKeyboardChanged.cs +++ b/Artemis/Artemis/Events/ActiveKeyboardChanged.cs @@ -1,12 +1,16 @@ -namespace Artemis.Events +using Artemis.KeyboardProviders; + +namespace Artemis.Events { public class ActiveKeyboardChanged { - public ActiveKeyboardChanged(string activeKeyboard) + public ActiveKeyboardChanged(KeyboardProvider oldKeyboard, KeyboardProvider newKeyboard) { - ActiveKeyboard = activeKeyboard; + OldKeyboard = oldKeyboard; + NewKeyboard = newKeyboard; } - public string ActiveKeyboard { get; set; } + public KeyboardProvider OldKeyboard { get; set; } + public KeyboardProvider NewKeyboard { get; set; } } } \ No newline at end of file diff --git a/Artemis/Artemis/InjectionModules/BaseModules.cs b/Artemis/Artemis/InjectionModules/BaseModules.cs index 8e895bfb1..46c2fcd2a 100644 --- a/Artemis/Artemis/InjectionModules/BaseModules.cs +++ b/Artemis/Artemis/InjectionModules/BaseModules.cs @@ -17,6 +17,7 @@ namespace Artemis.InjectionModules Bind().To().InSingletonScope(); Bind().ToFactory(); + Bind().To().InSingletonScope(); Bind().To().InSingletonScope(); Bind().To().InSingletonScope(); Bind().To().InSingletonScope(); diff --git a/Artemis/Artemis/KeyboardProviders/Corsair/CorsairRGB.cs b/Artemis/Artemis/KeyboardProviders/Corsair/CorsairRGB.cs index 93069f2c2..5e0bf0fb8 100644 --- a/Artemis/Artemis/KeyboardProviders/Corsair/CorsairRGB.cs +++ b/Artemis/Artemis/KeyboardProviders/Corsair/CorsairRGB.cs @@ -123,17 +123,10 @@ namespace Artemis.KeyboardProviders.Corsair /// public override void DrawBitmap(Bitmap bitmap) { - var fixedBmp = new Bitmap(bitmap.Width, bitmap.Height); - using (var g = Graphics.FromImage(fixedBmp)) - { - g.Clear(Color.Black); - g.DrawImage(bitmap, 0, 0); - } - - var fixedImage = ImageUtilities.ResizeImage(fixedBmp, Width, Height); + var image = ImageUtilities.ResizeImage(bitmap, Width, Height); var brush = new ImageBrush { - Image = fixedImage + Image = image }; _keyboard.Brush = brush; diff --git a/Artemis/Artemis/Managers/EffectManager.cs b/Artemis/Artemis/Managers/EffectManager.cs index 43329a0c6..654d11d92 100644 --- a/Artemis/Artemis/Managers/EffectManager.cs +++ b/Artemis/Artemis/Managers/EffectManager.cs @@ -79,6 +79,9 @@ namespace Artemis.Managers /// private void SetupProfilePreview(object sender, ElapsedEventArgs e) { + if (_keyboardManager.ChangingKeyboard) + return; + // Make sure the preview model should still be active if (ActiveEffect is ProfilePreviewModel) { @@ -133,41 +136,39 @@ namespace Artemis.Managers if (effectModel is OverlayModel) throw new ArgumentException("Can't set an Overlay effect as the active effect"); - lock (_keyboardManager) + if (_keyboardManager.ActiveKeyboard == null) + _keyboardManager.EnableLastKeyboard(); + // If still null, no last keyboard, so stop. + if (_keyboardManager.ActiveKeyboard == null) + return; + + // Game models are only used if they are enabled + var gameModel = effectModel as GameModel; + if (gameModel != null) + if (!gameModel.Enabled) + return; + + var wasNull = false; + if (ActiveEffect == null) { - lock (this) - { - if (_keyboardManager.ActiveKeyboard == null) - _keyboardManager.EnableLastKeyboard(); - // If still null, no last keyboard, so stop. - if (_keyboardManager.ActiveKeyboard == null) - return; + wasNull = true; + ActiveEffect = effectModel; + } - // Game models are only used if they are enabled - var gameModel = effectModel as GameModel; - if (gameModel != null) - if (!gameModel.Enabled) - return; + lock (ActiveEffect) + { + if (!wasNull) + ActiveEffect.Dispose(); - var wasNull = false; - if (ActiveEffect == null) - { - wasNull = true; - ActiveEffect = effectModel; - } - if (!wasNull) - ActiveEffect.Dispose(); + ActiveEffect = effectModel; + ActiveEffect.Enable(); - ActiveEffect = effectModel; - ActiveEffect.Enable(); + if (ActiveEffect is GameModel || ActiveEffect is ProfilePreviewModel) + return; - if (ActiveEffect is GameModel || ActiveEffect is ProfilePreviewModel) - return; - - // Non-game effects are stored as the new LastEffect. - General.Default.LastEffect = ActiveEffect?.Name; - General.Default.Save(); - } + // Non-game effects are stored as the new LastEffect. + General.Default.LastEffect = ActiveEffect?.Name; + General.Default.Save(); } if (loopManager != null && !loopManager.Running) @@ -185,18 +186,19 @@ namespace Artemis.Managers /// public void ClearEffect() { - lock (_keyboardManager) - { - lock (this) - { - ActiveEffect.Dispose(); - ActiveEffect = null; + if (ActiveEffect == null) + return; - General.Default.LastEffect = null; - General.Default.Save(); - } + lock (ActiveEffect) + { + ActiveEffect.Dispose(); + ActiveEffect = null; + + General.Default.LastEffect = null; + General.Default.Save(); } + _logger.Debug("Cleared active effect"); } diff --git a/Artemis/Artemis/Managers/KeyboardManager.cs b/Artemis/Artemis/Managers/KeyboardManager.cs index 5d0e72ad1..bceff508b 100644 --- a/Artemis/Artemis/Managers/KeyboardManager.cs +++ b/Artemis/Artemis/Managers/KeyboardManager.cs @@ -18,7 +18,6 @@ namespace Artemis.Managers { private readonly IEventAggregator _events; private readonly ILogger _logger; - private KeyboardProvider _activeKeyboard; public KeyboardManager(IEventAggregator events, ILogger logger, List keyboardProviders) { @@ -36,16 +35,9 @@ namespace Artemis.Managers public List KeyboardProviders { get; set; } - public KeyboardProvider ActiveKeyboard - { - get { return _activeKeyboard; } - set - { - _activeKeyboard = value; - // Let the ViewModels know - _events.PublishOnUIThread(new ActiveKeyboardChanged(value?.Name)); - } - } + public KeyboardProvider ActiveKeyboard { get; set; } + + public bool ChangingKeyboard { get; private set; } /// /// Enables the last keyboard according to the settings file @@ -68,11 +60,19 @@ namespace Artemis.Managers { lock (this) { + ChangingKeyboard = true; + if (keyboardProvider == null) throw new ArgumentNullException(nameof(keyboardProvider)); if (ActiveKeyboard?.Name == keyboardProvider.Name) + { + ChangingKeyboard = false; 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) @@ -94,6 +94,7 @@ namespace Artemis.Managers General.Default.LastKeyboard = null; General.Default.Save(); _logger.Warn("Failed enabling keyboard: {0}", keyboardProvider.Name); + ChangingKeyboard = false; return; } @@ -102,6 +103,9 @@ namespace Artemis.Managers General.Default.LastKeyboard = ActiveKeyboard.Name; General.Default.Save(); + + ChangingKeyboard = false; + _events.PublishOnUIThread(new ActiveKeyboardChanged(oldKeyboard, ActiveKeyboard)); _logger.Debug("Enabled keyboard: {0}", keyboardProvider.Name); } } @@ -116,9 +120,17 @@ namespace Artemis.Managers if (ActiveKeyboard == null) return; + // Store the old keyboard so it can be used in the event we're raising later + var oldKeyboard = ActiveKeyboard; + var releaseName = ActiveKeyboard.Name; ActiveKeyboard.Disable(); ActiveKeyboard = null; + + General.Default.LastKeyboard = null; + General.Default.Save(); + + _events.PublishOnUIThread(new ActiveKeyboardChanged(oldKeyboard, null)); _logger.Debug("Released keyboard: {0}", releaseName); } } diff --git a/Artemis/Artemis/Managers/LoopManager.cs b/Artemis/Artemis/Managers/LoopManager.cs index 7d855ecd7..1e8d21312 100644 --- a/Artemis/Artemis/Managers/LoopManager.cs +++ b/Artemis/Artemis/Managers/LoopManager.cs @@ -1,4 +1,5 @@ using System; +using System.Drawing; using System.Timers; using Artemis.Events; using Caliburn.Micro; @@ -57,7 +58,6 @@ namespace Artemis.Managers return; } - // TODO: Deadlock maybe? I don't know what Resharper is on about if (_effectManager.ActiveEffect == null) { var lastEffect = _effectManager.GetLastEffect(); @@ -88,6 +88,18 @@ namespace Artemis.Managers if (!Running) return; + // Stop if no active effect + if (_effectManager.ActiveEffect == null) + { + _logger.Debug("No active effect, stopping"); + Stop(); + return; + } + var renderEffect = _effectManager.ActiveEffect; + + if (_keyboardManager.ChangingKeyboard) + return; + // Stop if no active keyboard if (_keyboardManager.ActiveKeyboard == null) { @@ -95,50 +107,47 @@ namespace Artemis.Managers Stop(); return; } - // Lock both the active keyboard and active effect so they will not change while rendering. - lock (_keyboardManager) + + lock (_keyboardManager.ActiveKeyboard) { - lock (_effectManager) + // Skip frame if effect is still initializing + if (renderEffect.Initialized == false) + return; + + // Update the current effect + if (renderEffect.Initialized) + renderEffect.Update(); + + // Get ActiveEffect's bitmap + var bitmap = renderEffect.Initialized + ? renderEffect.GenerateBitmap() + : null; + + // Draw enabled overlays on top + foreach (var overlayModel in _effectManager.EnabledOverlays) { - // Stop if no active effect - if (_effectManager.ActiveEffect == null) - { - _logger.Debug("No active effect, stopping"); - Stop(); - return; - } - - // Skip frame if effect is still initializing - if (_effectManager.ActiveEffect.Initialized == false) - return; - - // Update the current effect - if (_effectManager.ActiveEffect.Initialized) - _effectManager.ActiveEffect.Update(); - - // Get ActiveEffect's bitmap - var bitmap = _effectManager.ActiveEffect.Initialized - ? _effectManager.ActiveEffect.GenerateBitmap() - : null; - - // Draw enabled overlays on top - foreach (var overlayModel in _effectManager.EnabledOverlays) - { - overlayModel.Update(); - bitmap = bitmap != null - ? overlayModel.GenerateBitmap(bitmap) - : overlayModel.GenerateBitmap(); - } - - if (bitmap == null) - return; - - // If it exists, send bitmap to the device - _keyboardManager.ActiveKeyboard.DrawBitmap(bitmap); - - // debugging TODO: Disable when window isn't shown (in Debug VM, or get rid of it, w/e) - _events.PublishOnUIThread(new ChangeBitmap(bitmap)); + overlayModel.Update(); + bitmap = bitmap != null + ? overlayModel.GenerateBitmap(bitmap) + : overlayModel.GenerateBitmap(); } + + if (bitmap == null) + return; + + // Fill the bitmap's background with blackness to avoid trailing colors on some keyboards + using (var g = Graphics.FromImage(bitmap)) + { + var preFix = (Bitmap)bitmap.Clone(); + g.Clear(Color.Black); + g.DrawImage(preFix, 0, 0); + } + + // If it exists, send bitmap to the device + _keyboardManager.ActiveKeyboard?.DrawBitmap(bitmap); + + // debugging TODO: Disable when window isn't shown (in Debug VM, or get rid of it, w/e) + _events.PublishOnUIThread(new ChangeBitmap(bitmap)); } } } diff --git a/Artemis/Artemis/Modules/Effects/Debug/DebugEffectModel.cs b/Artemis/Artemis/Modules/Effects/Debug/DebugEffectModel.cs index d8e6fc79f..317b99b53 100644 --- a/Artemis/Artemis/Modules/Effects/Debug/DebugEffectModel.cs +++ b/Artemis/Artemis/Modules/Effects/Debug/DebugEffectModel.cs @@ -35,13 +35,8 @@ namespace Artemis.Modules.Effects.Debug KeyboardRectangle = new KeyboardRectangle(MainManager.KeyboardManager.ActiveKeyboard, 0, 0, new List { - Color.Red, - Color.OrangeRed, - Color.Yellow, - Color.Green, - Color.Blue, - Color.Purple, - Color.DeepPink + Color.FromArgb(0, 226, 190), + Color.FromArgb(0, 208, 255) }, LinearGradientMode.Horizontal); Initialized = true; diff --git a/Artemis/Artemis/ViewModels/Abstract/GameViewModel.cs b/Artemis/Artemis/ViewModels/Abstract/GameViewModel.cs index dec3ef5ec..25e8c3516 100644 --- a/Artemis/Artemis/ViewModels/Abstract/GameViewModel.cs +++ b/Artemis/Artemis/ViewModels/Abstract/GameViewModel.cs @@ -1,5 +1,4 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using Artemis.InjectionFactories; @@ -16,7 +15,6 @@ namespace Artemis.ViewModels.Abstract public abstract class GameViewModel : Screen { private GameSettings _gameSettings; - private bool _startLoopManager; protected GameViewModel(MainManager mainManager, GameModel gameModel, IEventAggregator events, IProfileEditorViewModelFactory pFactory) @@ -34,6 +32,7 @@ namespace Artemis.ViewModels.Abstract [Inject] public ILogger Logger { get; set; } + [Inject] public ProfilePreviewModel ProfilePreviewModel { get; set; } @@ -94,39 +93,38 @@ namespace Artemis.ViewModels.Abstract protected override void OnActivate() { base.OnActivate(); - SetEditorShown(true); - - // OnActivate gets called at odd times, only start the LoopManager if it's been active - // for 600ms. - _startLoopManager = true; - Task.Factory.StartNew(() => - { - Thread.Sleep(600); - if (MainManager.LoopManager.Running || !_startLoopManager) - return; - - Logger.Debug("Starting LoopManager for profile preview"); - MainManager.LoopManager.Start(); - }); + Task.Factory.StartNew(HandleActivationSwitch); } protected override void OnDeactivate(bool close) { base.OnDeactivate(close); - SetEditorShown(false); - - _startLoopManager = false; + Task.Factory.StartNew(HandleActivationSwitch); } - public void SetEditorShown(bool enable) + private void HandleActivationSwitch() { - MainManager.EffectManager.ProfilePreviewModel = ProfilePreviewModel; - MainManager.EffectManager.ProfilePreviewModel.SelectedProfile = enable ? ProfileEditor.SelectedProfile : null; + Thread.Sleep(600); + if (IsActive) + { + if (!MainManager.LoopManager.Running) + { + Logger.Debug("Starting LoopManager for profile preview"); + MainManager.LoopManager.Start(); + } + MainManager.EffectManager.ProfilePreviewModel = ProfilePreviewModel; + ProfilePreviewModel.SelectedProfile = ProfileEditor.SelectedProfile; + } + else + { + MainManager.EffectManager.ProfilePreviewModel = ProfilePreviewModel; + ProfilePreviewModel.SelectedProfile = null; + } } private void ProfileUpdater(object sender, PropertyChangedEventArgs e) { - if (e.PropertyName != "SelectedProfile") + if (e.PropertyName != "SelectedProfile" && IsActive) return; GameModel.Profile = ProfileEditor.SelectedProfile; diff --git a/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs b/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs index a34568039..ee4379427 100644 --- a/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs +++ b/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs @@ -48,7 +48,8 @@ namespace Artemis.ViewModels.Flyouts { get { - var collection = new BindableCollection(MainManager.KeyboardManager.KeyboardProviders.Select(k => k.Name)); + var collection = + new BindableCollection(MainManager.KeyboardManager.KeyboardProviders.Select(k => k.Name)); collection.Insert(0, "None"); return collection; } @@ -70,7 +71,6 @@ namespace Artemis.ViewModels.Flyouts get { return MainManager.ProgramEnabled; } set { - if (value) MainManager.EnableProgram(); else @@ -111,7 +111,8 @@ namespace Artemis.ViewModels.Flyouts return; _logger.Debug("Handling SelectedKeyboard change in UI"); - var keyboard = MainManager.KeyboardManager.KeyboardProviders.FirstOrDefault(k => k.Name == SelectedKeyboardProvider); + var keyboard = MainManager.KeyboardManager.KeyboardProviders + .FirstOrDefault(k => k.Name == SelectedKeyboardProvider); if (keyboard != null) { MainManager.KeyboardManager.EnableKeyboard(keyboard); @@ -119,7 +120,6 @@ namespace Artemis.ViewModels.Flyouts } else MainManager.KeyboardManager.ReleaseActiveKeyboard(); - } public void ToggleEnabled() diff --git a/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs b/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs index ca1a6dccf..01e510ce8 100644 --- a/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs +++ b/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs @@ -212,7 +212,10 @@ namespace Artemis.ViewModels private void LoadProfiles() { Profiles.Clear(); - Profiles.AddRange(ProfileProvider.GetAll(_gameModel)); + if (_gameModel == null || ActiveKeyboard == null) + return; + + Profiles.AddRange(ProfileProvider.GetAll(_gameModel, ActiveKeyboard)); SelectedProfile = Profiles.FirstOrDefault(); } diff --git a/Artemis/Artemis/ViewModels/ShellViewModel.cs b/Artemis/Artemis/ViewModels/ShellViewModel.cs index 67e0b79b9..b04887ff6 100644 --- a/Artemis/Artemis/ViewModels/ShellViewModel.cs +++ b/Artemis/Artemis/ViewModels/ShellViewModel.cs @@ -10,10 +10,12 @@ namespace Artemis.ViewModels { private readonly BaseViewModel[] _viewModels; - public ShellViewModel(BaseViewModel[] viewModels, IKernel kernel) + public ShellViewModel(IKernel kernel, IEventAggregator events, BaseViewModel[] viewModels) { _viewModels = viewModels; + events.Subscribe(this); + // Setup UI DisplayName = "Artemis"; Flyouts = new BindableCollection diff --git a/Artemis/Artemis/ViewModels/WelcomeViewModel.cs b/Artemis/Artemis/ViewModels/WelcomeViewModel.cs index 9b91abe16..4d8f23524 100644 --- a/Artemis/Artemis/ViewModels/WelcomeViewModel.cs +++ b/Artemis/Artemis/ViewModels/WelcomeViewModel.cs @@ -1,9 +1,9 @@ using System.Diagnostics; -using Caliburn.Micro; +using Artemis.ViewModels.Abstract; namespace Artemis.ViewModels { - public sealed class WelcomeViewModel : Screen + public sealed class WelcomeViewModel : BaseViewModel { public WelcomeViewModel() {