From b185b2864584c12b5977240435ee4c9ea3185bf1 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 17 Nov 2020 20:54:49 +0100 Subject: [PATCH] Core - Corrected disposable pattern usage Core - Further nullable refactoring.. --- src/Artemis.Core/Artemis.Core.csproj | 4 +- .../Profile/DataBindings/DataBinding.cs | 70 ++++++++++++------- .../Conditional/ConditionalDataBinding.cs | 24 +++++-- .../Modes/Conditional/DataBindingCondition.cs | 25 ++++++- .../Modes/Direct/DataBindingModifier.cs | 32 +++++++-- .../DataBindings/Modes/DirectDataBinding.cs | 28 ++++++-- .../Models/Profile/DataModel/DataModelPath.cs | 28 ++++++-- .../Profile/DataModel/DataModelPathSegment.cs | 36 +++++++--- .../Profile/LayerProperties/LayerProperty.cs | 30 ++++++-- .../Models/Profile/LayerPropertyGroup.cs | 30 ++++++-- src/Artemis.Core/Models/Profile/Profile.cs | 7 +- .../Models/Profile/RenderProfileElement.cs | 4 +- src/Artemis.Core/Models/Profile/Timeline.cs | 17 +++-- .../LayerBrushes/Internal/BaseLayerBrush.cs | 21 ++++-- .../Internal/PropertiesLayerBrush.cs | 2 +- .../LayerBrushes/LayerBrushProvider.cs | 2 +- .../Plugins/LayerBrushes/RgbNetLayerBrush.cs | 9 ++- .../LayerEffects/Internal/BaseLayerEffect.cs | 24 ++++++- .../Plugins/LayerEffects/LayerEffect.cs | 2 +- .../LayerEffects/LayerEffectProvider.cs | 2 +- src/Artemis.Core/Plugins/Modules/Module.cs | 12 ++-- .../Plugins/Modules/ProfileModule.cs | 7 +- src/Artemis.Core/Plugins/Plugin.cs | 36 +++++++--- src/Artemis.Core/Plugins/PluginFeature.cs | 22 +++++- .../Plugins/TimedUpdateRegistration.cs | 39 ++++++++--- src/Artemis.Core/RGB.NET/BitmapBrush.cs | 6 +- src/Artemis.Core/Services/ModuleService.cs | 12 ++-- src/Artemis.Core/Services/RgbService.cs | 10 +-- .../Services/Storage/ProfileService.cs | 8 +-- .../Services/Storage/SurfaceService.cs | 4 +- .../ConditionOperatorRegistration.cs | 2 +- .../DataBindingModifierTypeRegistration.cs | 2 +- .../Registrations/DataModelRegistration.cs | 2 +- .../Registrations/LayerBrushRegistration.cs | 2 +- .../Registrations/LayerEffectRegistration.cs | 2 +- .../DataModelListPropertiesViewModel.cs | 2 +- .../Utilities/PluginUtilities.cs | 2 - .../Screens/Sidebar/SidebarViewModel.cs | 24 ++++--- 38 files changed, 429 insertions(+), 162 deletions(-) diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index e597d75d3..dbfa0a2d6 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -13,7 +13,8 @@ x64 bin\x64\Debug\Artemis.Core.xml - 1701;1702 + + 5 @@ -26,6 +27,7 @@ git true enable + latest diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs index 86160eb35..7a31710a4 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs @@ -7,9 +7,9 @@ namespace Artemis.Core public class DataBinding : IDataBinding { private TProperty _currentValue = default!; - private TProperty _previousValue = default!; private bool _disposed; private TimeSpan _easingProgress; + private TProperty _previousValue = default!; internal DataBinding(DataBindingRegistration dataBindingRegistration) { @@ -98,6 +98,24 @@ namespace Artemis.Core return Registration?.PropertyExpression.ReturnType; } + /// + /// Updates the smoothing progress of the data binding + /// + /// The delta to apply during update + public void UpdateWithDelta(TimeSpan delta) + { + if (_disposed) + throw new ObjectDisposedException("DataBinding"); + + // Data bindings cannot go back in time like brushes + if (delta < TimeSpan.Zero) + delta = TimeSpan.Zero; + + _easingProgress = _easingProgress.Add(delta); + if (_easingProgress > EasingTime) + _easingProgress = EasingTime; + } + private void ResetEasing(TProperty value) { _previousValue = GetInterpolatedValue(); @@ -143,24 +161,6 @@ namespace Artemis.Core UpdateWithDelta(timeline.Delta); } - /// - /// Updates the smoothing progress of the data binding - /// - /// The delta to apply during update - public void UpdateWithDelta(TimeSpan delta) - { - if (_disposed) - throw new ObjectDisposedException("DataBinding"); - - // Data bindings cannot go back in time like brushes - if (delta < TimeSpan.Zero) - delta = TimeSpan.Zero; - - _easingProgress = _easingProgress.Add(delta); - if (_easingProgress > EasingTime) - _easingProgress = EasingTime; - } - /// public void Apply() { @@ -175,16 +175,36 @@ namespace Artemis.Core Converter.ApplyValue(value); } + #region IDisposable + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _disposed = true; + + if (Registration != null) + Registration.DataBinding = null; + DataBindingMode?.Dispose(); + } + } + /// public void Dispose() { - _disposed = true; - - if (Registration != null) - Registration.DataBinding = null; - DataBindingMode?.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } + #endregion + #region Mode management /// @@ -255,7 +275,7 @@ namespace Artemis.Core LayerProperty.Entity.DataBindingEntities.Add(Entity); // Don't save an invalid state - if (Registration != null) + if (Registration != null) Entity.TargetExpression = Registration.PropertyExpression.ToString(); Entity.EasingTime = EasingTime; diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/ConditionalDataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/ConditionalDataBinding.cs index 666e31587..db98a5b86 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/ConditionalDataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/ConditionalDataBinding.cs @@ -43,13 +43,29 @@ namespace Artemis.Core #region IDisposable + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _disposed = true; + + foreach (DataBindingCondition dataBindingCondition in Conditions) + dataBindingCondition.Dispose(); + } + } + /// public void Dispose() { - _disposed = true; - - foreach (DataBindingCondition dataBindingCondition in Conditions) - dataBindingCondition.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } #endregion diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/DataBindingCondition.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/DataBindingCondition.cs index 9be9256e4..c82d8111b 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/DataBindingCondition.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/DataBindingCondition.cs @@ -92,12 +92,31 @@ namespace Artemis.Core Order = Entity.Order; } + #region IDisposable + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _disposed = true; + ConditionalDataBinding.Dispose(); + } + } + /// public void Dispose() { - _disposed = true; - - Condition.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modes/Direct/DataBindingModifier.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modes/Direct/DataBindingModifier.cs index b0b10c0e5..49a921fea 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modes/Direct/DataBindingModifier.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modes/Direct/DataBindingModifier.cs @@ -270,17 +270,37 @@ namespace Artemis.Core // Parameter is done during initialize } + #region IDisposable + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _disposed = true; + + DataBindingModifierTypeStore.DataBindingModifierAdded -= DataBindingModifierTypeStoreOnDataBindingModifierAdded; + DataBindingModifierTypeStore.DataBindingModifierRemoved -= DataBindingModifierTypeStoreOnDataBindingModifierRemoved; + + ParameterPath?.Dispose(); + } + } + /// public void Dispose() { - _disposed = true; - - DataBindingModifierTypeStore.DataBindingModifierAdded -= DataBindingModifierTypeStoreOnDataBindingModifierAdded; - DataBindingModifierTypeStore.DataBindingModifierRemoved -= DataBindingModifierTypeStoreOnDataBindingModifierRemoved; - - ParameterPath?.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } + #endregion + #region Event handlers private void DataBindingModifierTypeStoreOnDataBindingModifierAdded(object? sender, DataBindingModifierTypeStoreEvent e) diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs index 03dada505..6dab244f4 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs @@ -54,15 +54,31 @@ namespace Artemis.Core #region IDisposable + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _disposed = true; + + foreach (DataBindingModifier dataBindingModifier in Modifiers) + dataBindingModifier.Dispose(); + + SourcePath?.Dispose(); + } + } + /// public void Dispose() { - _disposed = true; - - foreach (DataBindingModifier dataBindingModifier in Modifiers) - dataBindingModifier.Dispose(); - - SourcePath?.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } #endregion diff --git a/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs b/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs index a784716ec..357da89a2 100644 --- a/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs +++ b/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs @@ -247,15 +247,31 @@ namespace Artemis.Core #region IDisposable + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _disposed = true; + + DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded; + DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved; + + Invalidate(); + } + } + /// public void Dispose() { - _disposed = true; - - DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded; - DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved; - - Invalidate(); + Dispose(true); + GC.SuppressFinalize(this); } #endregion diff --git a/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs b/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs index 063a18386..c8fd98d79 100644 --- a/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs +++ b/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs @@ -250,19 +250,35 @@ namespace Artemis.Core #region IDisposable + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (_dynamicDataModel != null) + { + _dynamicDataModel.DynamicDataModelAdded -= DynamicDataModelOnDynamicDataModelAdded; + _dynamicDataModel.DynamicDataModelRemoved -= DynamicDataModelOnDynamicDataModelRemoved; + } + + Type = DataModelPathSegmentType.Invalid; + + _accessorLambda = null; + Accessor = null; + } + } + /// public void Dispose() { - if (_dynamicDataModel != null) - { - _dynamicDataModel.DynamicDataModelAdded -= DynamicDataModelOnDynamicDataModelAdded; - _dynamicDataModel.DynamicDataModelRemoved -= DynamicDataModelOnDynamicDataModelRemoved; - } - - Type = DataModelPathSegmentType.Invalid; - - _accessorLambda = null; - Accessor = null; + Dispose(true); + GC.SuppressFinalize(this); } #endregion diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index 16b34b8c2..12edfed9c 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -28,7 +28,7 @@ namespace Artemis.Core // Cant define generic types as nullable ¯\_(ツ)_/¯ CurrentValue = default!; DefaultValue = default!; - + _baseValue = default!; _keyframes = new List>(); } @@ -61,15 +61,35 @@ namespace Artemis.Core OnUpdated(); } + #region IDisposable + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _disposed = true; + + foreach (IDataBinding dataBinding in _dataBindings) + dataBinding.Dispose(); + } + } + /// public void Dispose() { - _disposed = true; - - foreach (IDataBinding dataBinding in _dataBindings) - dataBinding.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } + #endregion + #region Hierarchy private bool _isHidden; diff --git a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs index 5369ebaf6..2e39f756c 100644 --- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs +++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs @@ -263,16 +263,32 @@ namespace Artemis.Core #region IDisposable + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _disposed = true; + DisableProperties(); + + foreach (ILayerProperty layerProperty in _layerProperties) + layerProperty.Dispose(); + foreach (LayerPropertyGroup layerPropertyGroup in _layerPropertyGroups) + layerPropertyGroup.Dispose(); + } + } + /// public void Dispose() { - _disposed = true; - DisableProperties(); - - foreach (ILayerProperty layerProperty in _layerProperties) - layerProperty.Dispose(); - foreach (LayerPropertyGroup layerPropertyGroup in _layerPropertyGroups) - layerPropertyGroup.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } #endregion diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs index 177d77a50..abadbc421 100644 --- a/src/Artemis.Core/Models/Profile/Profile.cs +++ b/src/Artemis.Core/Models/Profile/Profile.cs @@ -13,6 +13,7 @@ namespace Artemis.Core public sealed class Profile : ProfileElement { private bool _isActivated; + private readonly object _lock = new object(); internal Profile(ProfileModule module, string name) { @@ -64,7 +65,7 @@ namespace Artemis.Core /// public override void Update(double deltaTime) { - lock (this) + lock (_lock) { if (Disposed) throw new ObjectDisposedException("Profile"); @@ -79,7 +80,7 @@ namespace Artemis.Core /// public override void Render(SKCanvas canvas) { - lock (this) + lock (_lock) { if (Disposed) throw new ObjectDisposedException("Profile"); @@ -182,7 +183,7 @@ namespace Artemis.Core internal void Activate(ArtemisSurface surface) { - lock (this) + lock (_lock) { if (Disposed) throw new ObjectDisposedException("Profile"); diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs index 2365c9d9a..53b4cc9b6 100644 --- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs +++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs @@ -287,7 +287,7 @@ namespace Artemis.Core OnLayerEffectsUpdated(); } - private void LayerEffectStoreOnLayerEffectRemoved(object sender, LayerEffectStoreEvent e) + private void LayerEffectStoreOnLayerEffectRemoved(object? sender, LayerEffectStoreEvent e) { // If effects provided by the plugin are on the element, replace them with placeholders List pluginEffects = LayerEffectsList.Where(ef => ef.Descriptor.Provider != null && @@ -303,7 +303,7 @@ namespace Artemis.Core } } - private void LayerEffectStoreOnLayerEffectAdded(object sender, LayerEffectStoreEvent e) + private void LayerEffectStoreOnLayerEffectAdded(object? sender, LayerEffectStoreEvent e) { if (RenderElementEntity.LayerEffects.Any(ef => ef.ProviderId == e.Registration.PluginFeature.Id)) ActivateEffects(); diff --git a/src/Artemis.Core/Models/Profile/Timeline.cs b/src/Artemis.Core/Models/Profile/Timeline.cs index f4bfc52bd..c4e49cc56 100644 --- a/src/Artemis.Core/Models/Profile/Timeline.cs +++ b/src/Artemis.Core/Models/Profile/Timeline.cs @@ -12,6 +12,7 @@ namespace Artemis.Core public class Timeline : CorePropertyChanged, IStorageModel { private const int MaxExtraTimelines = 15; + private readonly object _lock = new object(); /// /// Creates a new instance of the class @@ -19,9 +20,10 @@ namespace Artemis.Core public Timeline() { Entity = new TimelineEntity(); - _extraTimelines = new List(); MainSegmentLength = TimeSpan.FromSeconds(5); + _extraTimelines = new List(); + Save(); } @@ -35,6 +37,7 @@ namespace Artemis.Core private Timeline(Timeline parent) { + Entity = new TimelineEntity(); Parent = parent; StartSegmentLength = Parent.StartSegmentLength; MainSegmentLength = Parent.MainSegmentLength; @@ -303,7 +306,7 @@ namespace Artemis.Core /// Whether to stick to the main segment, wrapping around if needed public void Update(TimeSpan delta, bool stickToMainSegment) { - lock (this) + lock (_lock) { Delta += delta; Position += delta; @@ -330,7 +333,7 @@ namespace Artemis.Core /// public void JumpToStart() { - lock (this) + lock (_lock) { if (Position == TimeSpan.Zero) return; @@ -345,7 +348,7 @@ namespace Artemis.Core /// public void JumpToEndSegment() { - lock (this) + lock (_lock) { if (Position >= EndSegmentStartPosition) return; @@ -360,7 +363,7 @@ namespace Artemis.Core /// public void JumpToEnd() { - lock (this) + lock (_lock) { if (Position >= EndSegmentEndPosition) return; @@ -377,7 +380,7 @@ namespace Artemis.Core /// Whether to stick to the main segment, wrapping around if needed public void Override(TimeSpan position, bool stickToMainSegment) { - lock (this) + lock (_lock) { Delta += position - Position; Position = position; @@ -395,7 +398,7 @@ namespace Artemis.Core /// public void ClearDelta() { - lock (this) + lock (_lock) { Delta = TimeSpan.Zero; } diff --git a/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs index fd0661a94..837f32142 100644 --- a/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs +++ b/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs @@ -97,19 +97,32 @@ namespace Artemis.Core.LayerBrushes internal abstract void InternalRender(SKCanvas canvas, SKRect path, SKPaint paint); - internal virtual void Dispose(bool disposing) + #region IDisposable + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) { + if (disposing) + { + DisableLayerBrush(); + BaseProperties?.Dispose(); + } } /// public void Dispose() { - DisableLayerBrush(); - BaseProperties?.Dispose(); - Dispose(true); GC.SuppressFinalize(this); } + + #endregion } /// diff --git a/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs index 86cac67a7..d8ede59ee 100644 --- a/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs +++ b/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs @@ -26,7 +26,7 @@ namespace Artemis.Core.LayerBrushes { // I imagine a null reference here can be confusing, so lets throw an exception explaining what to do if (_properties == null) - throw new ArtemisPluginException("Cannot access brush properties until OnPropertiesInitialized has been called"); + throw new InvalidOperationException("Cannot access brush properties until OnPropertiesInitialized has been called"); return _properties; } internal set => _properties = value; diff --git a/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushProvider.cs b/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushProvider.cs index a828c8d25..9b83b0b6b 100644 --- a/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushProvider.cs +++ b/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushProvider.cs @@ -46,7 +46,7 @@ namespace Artemis.Core.LayerBrushes LayerBrushStore.Add(descriptor); } - private void OnDisabled(object sender, EventArgs e) + private void OnDisabled(object? sender, EventArgs e) { // The store will clean up the registrations by itself, the plugin just needs to clear its own list _layerBrushDescriptors.Clear(); diff --git a/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs index ee90e8cfa..c106a1c15 100644 --- a/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs +++ b/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs @@ -56,7 +56,10 @@ namespace Artemis.Core.LayerBrushes UpdateLedGroup(); } - internal override void Dispose(bool disposing) + #region IDisposable + + /// + protected override void Dispose(bool disposing) { if (disposing) { @@ -67,13 +70,15 @@ namespace Artemis.Core.LayerBrushes base.Dispose(disposing); } + #endregion + // Not used in this effect type internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint) { throw new NotImplementedException("RGB.NET layer effectes do not implement InternalRender"); } - private void LayerOnRenderPropertiesUpdated(object sender, EventArgs e) + private void LayerOnRenderPropertiesUpdated(object? sender, EventArgs e) { UpdateLedGroup(); } diff --git a/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs index bff86bdf9..f76014daf 100644 --- a/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs +++ b/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs @@ -103,13 +103,33 @@ namespace Artemis.Core.LayerEffects internal string PropertyRootPath => $"LayerEffect.{EntityId}.{GetType().Name}."; + #region IDisposable + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + DisableLayerEffect(); + BaseProperties?.Dispose(); + } + } + /// public void Dispose() { - DisableLayerEffect(); - BaseProperties?.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } + #endregion + /// /// Called when the layer effect is activated /// diff --git a/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs index 75bcce09e..585fb8463 100644 --- a/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs +++ b/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs @@ -27,7 +27,7 @@ namespace Artemis.Core.LayerEffects { // I imagine a null reference here can be confusing, so lets throw an exception explaining what to do if (_properties == null) - throw new ArtemisPluginException("Cannot access effect properties until OnPropertiesInitialized has been called"); + throw new InvalidOperationException("Cannot access effect properties until OnPropertiesInitialized has been called"); return _properties; } internal set => _properties = value; diff --git a/src/Artemis.Core/Plugins/LayerEffects/LayerEffectProvider.cs b/src/Artemis.Core/Plugins/LayerEffects/LayerEffectProvider.cs index b6b68df7a..7de03159e 100644 --- a/src/Artemis.Core/Plugins/LayerEffects/LayerEffectProvider.cs +++ b/src/Artemis.Core/Plugins/LayerEffects/LayerEffectProvider.cs @@ -46,7 +46,7 @@ namespace Artemis.Core.LayerEffects LayerEffectStore.Add(descriptor); } - private void OnDisabled(object sender, EventArgs e) + private void OnDisabled(object? sender, EventArgs e) { // The store will clean up the registrations by itself, the plugin just needs to clear its own list _layerEffectDescriptors.Clear(); diff --git a/src/Artemis.Core/Plugins/Modules/Module.cs b/src/Artemis.Core/Plugins/Modules/Module.cs index 8c3c3ee45..89698c176 100644 --- a/src/Artemis.Core/Plugins/Modules/Module.cs +++ b/src/Artemis.Core/Plugins/Modules/Module.cs @@ -140,7 +140,7 @@ namespace Artemis.Core.Modules internal DataModel? InternalDataModel { get; set; } internal bool InternalExpandsMainDataModel { get; set; } - internal ModuleSettingsEntity? Entity { get; set; } + internal ModuleSettingsEntity? SettingsEntity { get; set; } /// /// Called each frame when the module should update @@ -233,12 +233,12 @@ namespace Artemis.Core.Modules internal void ApplyToEntity() { - if (Entity == null) - Entity = new ModuleSettingsEntity(); + if (SettingsEntity == null) + SettingsEntity = new ModuleSettingsEntity(); - Entity.ModuleId = Id; - Entity.PriorityCategory = (int) PriorityCategory; - Entity.Priority = Priority; + SettingsEntity.ModuleId = Id; + SettingsEntity.PriorityCategory = (int) PriorityCategory; + SettingsEntity.Priority = Priority; } } diff --git a/src/Artemis.Core/Plugins/Modules/ProfileModule.cs b/src/Artemis.Core/Plugins/Modules/ProfileModule.cs index 90e04a005..a5b45926c 100644 --- a/src/Artemis.Core/Plugins/Modules/ProfileModule.cs +++ b/src/Artemis.Core/Plugins/Modules/ProfileModule.cs @@ -96,6 +96,7 @@ namespace Artemis.Core.Modules /// Gets a list of all properties ignored at runtime using IgnoreProperty(x => x.y) /// protected internal readonly List HiddenPropertiesList = new List(); + private readonly object _lock = new object(); /// /// Creates a new instance of the class @@ -154,7 +155,7 @@ namespace Artemis.Core.Modules if (IsUpdateAllowed) Update(deltaTime); - lock (this) + lock (_lock) { OpacityOverride = AnimatingProfileChange ? Math.Max(0, OpacityOverride - 0.1) @@ -172,7 +173,7 @@ namespace Artemis.Core.Modules { Render(deltaTime, surface, canvas, canvasInfo); - lock (this) + lock (_lock) { // Render the profile ActiveProfile?.Render(canvas); @@ -210,7 +211,7 @@ namespace Artemis.Core.Modules if (!IsActivated) throw new ArtemisCoreException("Cannot activate a profile on a deactivated module"); - lock (this) + lock (_lock) { if (profile == ActiveProfile) return; diff --git a/src/Artemis.Core/Plugins/Plugin.cs b/src/Artemis.Core/Plugins/Plugin.cs index c881ef5fb..2eb977b9d 100644 --- a/src/Artemis.Core/Plugins/Plugin.cs +++ b/src/Artemis.Core/Plugins/Plugin.cs @@ -160,19 +160,39 @@ namespace Artemis.Core } } + #region IDisposable + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + foreach (PluginFeature feature in Features) + feature.Dispose(); + + Kernel?.Dispose(); + PluginLoader?.Dispose(); + + _features.Clear(); + SetEnabled(false); + } + } + /// public void Dispose() { - foreach (PluginFeature feature in Features) - feature.Dispose(); - - Kernel?.Dispose(); - PluginLoader?.Dispose(); - - _features.Clear(); - SetEnabled(false); + Dispose(true); + GC.SuppressFinalize(this); } + #endregion + #region Events /// diff --git a/src/Artemis.Core/Plugins/PluginFeature.cs b/src/Artemis.Core/Plugins/PluginFeature.cs index 06f09b90b..3cc20c092 100644 --- a/src/Artemis.Core/Plugins/PluginFeature.cs +++ b/src/Artemis.Core/Plugins/PluginFeature.cs @@ -127,10 +127,30 @@ namespace Artemis.Core Disable(); } + #region IDisposable + + /// + /// Releases the unmanaged resources used by the plugin feature and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Disable(); + } + } + + #endregion + /// public void Dispose() { - Disable(); + Dispose(true); + GC.SuppressFinalize(this); } #region Loading diff --git a/src/Artemis.Core/Plugins/TimedUpdateRegistration.cs b/src/Artemis.Core/Plugins/TimedUpdateRegistration.cs index 9f322c566..cc77111b7 100644 --- a/src/Artemis.Core/Plugins/TimedUpdateRegistration.cs +++ b/src/Artemis.Core/Plugins/TimedUpdateRegistration.cs @@ -13,6 +13,7 @@ namespace Artemis.Core private DateTime _lastEvent; private Timer _timer; private bool _disposed; + private readonly object _lock = new object(); internal TimedUpdateRegistration(PluginFeature feature, TimeSpan interval, Action action) { @@ -68,7 +69,7 @@ namespace Artemis.Core if (_disposed) throw new ObjectDisposedException("TimedUpdateRegistration"); - lock (this) + lock (_lock) { if (!Feature.IsEnabled) throw new ArtemisPluginException("Cannot start a timed update for a disabled plugin feature"); @@ -92,7 +93,7 @@ namespace Artemis.Core if (_disposed) throw new ObjectDisposedException("TimedUpdateRegistration"); - lock (this) + lock (_lock) { if (_timer == null) return; @@ -109,7 +110,7 @@ namespace Artemis.Core if (!Feature.IsEnabled) return; - lock (this) + lock (_lock) { TimeSpan interval = DateTime.Now - _lastEvent; _lastEvent = DateTime.Now; @@ -138,15 +139,35 @@ namespace Artemis.Core Stop(); } + #region IDisposable + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Stop(); + + Feature.Enabled -= FeatureOnEnabled; + Feature.Disabled -= FeatureOnDisabled; + + _disposed = true; + } + } + /// public void Dispose() { - Stop(); - - Feature.Enabled -= FeatureOnEnabled; - Feature.Disabled -= FeatureOnDisabled; - - _disposed = true; + Dispose(true); + GC.SuppressFinalize(this); } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/RGB.NET/BitmapBrush.cs b/src/Artemis.Core/RGB.NET/BitmapBrush.cs index 1bfa5a615..1ac02169d 100644 --- a/src/Artemis.Core/RGB.NET/BitmapBrush.cs +++ b/src/Artemis.Core/RGB.NET/BitmapBrush.cs @@ -8,7 +8,7 @@ namespace Artemis.Core /// /// The RGB.NET brush Artemis uses to map the SkiaSharp bitmap to LEDs /// - public class BitmapBrush : AbstractDecoratable, IBrush, IDisposable + public sealed class BitmapBrush : AbstractDecoratable, IBrush, IDisposable { private readonly object _disposeLock; private readonly PluginSetting _sampleSizeSetting; @@ -67,7 +67,7 @@ namespace Artemis.Core #region Methods /// - public virtual void PerformRender(Rectangle rectangle, IEnumerable renderTargets) + public void PerformRender(Rectangle rectangle, IEnumerable renderTargets) { lock (_disposeLock) { @@ -155,7 +155,7 @@ namespace Artemis.Core } /// - public virtual void PerformFinalize() + public void PerformFinalize() { } diff --git a/src/Artemis.Core/Services/ModuleService.cs b/src/Artemis.Core/Services/ModuleService.cs index 97ccf53ce..d5c543d82 100644 --- a/src/Artemis.Core/Services/ModuleService.cs +++ b/src/Artemis.Core/Services/ModuleService.cs @@ -139,11 +139,11 @@ namespace Artemis.Core.Services ModulePriorityCategory category = module.DefaultPriorityCategory; int priority = 1; - module.Entity = _moduleRepository.GetByModuleId(module.Id); - if (module.Entity != null) + module.SettingsEntity = _moduleRepository.GetByModuleId(module.Id); + if (module.SettingsEntity != null) { - category = (ModulePriorityCategory) module.Entity.PriorityCategory; - priority = module.Entity.Priority; + category = (ModulePriorityCategory) module.SettingsEntity.PriorityCategory; + priority = module.SettingsEntity.Priority; } UpdateModulePriority(module, category, priority); @@ -255,10 +255,10 @@ namespace Artemis.Core.Services categoryModule.Priority = index; // Don't save modules whose priority hasn't been initialized yet - if (categoryModule == module || categoryModule.Entity != null) + if (categoryModule == module || categoryModule.SettingsEntity != null) { categoryModule.ApplyToEntity(); - _moduleRepository.Save(categoryModule.Entity); + _moduleRepository.Save(categoryModule.SettingsEntity); } } } diff --git a/src/Artemis.Core/Services/RgbService.cs b/src/Artemis.Core/Services/RgbService.cs index 5d096e91f..9091dbdd2 100644 --- a/src/Artemis.Core/Services/RgbService.cs +++ b/src/Artemis.Core/Services/RgbService.cs @@ -55,7 +55,7 @@ namespace Artemis.Core.Services catch (Exception e) { _logger.Error(e, "Exception during device loading for device provider {deviceProvider}", deviceProvider.GetType().Name); - throw e; + throw; } if (deviceProvider.Devices == null || !deviceProvider.Devices.Any()) @@ -86,12 +86,12 @@ namespace Artemis.Core.Services Surface.Dispose(); } - private void RenderScaleSettingOnSettingChanged(object sender, EventArgs e) + private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e) { UpdateSurfaceLedGroup(); } - private void TargetFrameRateSettingOnSettingChanged(object sender, EventArgs e) + private void TargetFrameRateSettingOnSettingChanged(object? sender, EventArgs e) { UpdateTrigger.UpdateFrequency = 1.0 / _targetFrameRateSetting.Value; } @@ -104,8 +104,8 @@ namespace Artemis.Core.Services #region Events - public event EventHandler DeviceLoaded; - public event EventHandler DeviceReloaded; + public event EventHandler? DeviceLoaded; + public event EventHandler? DeviceReloaded; public void UpdateSurfaceLedGroup() { diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index 6ff5a49fd..c1c556618 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -107,12 +107,12 @@ namespace Artemis.Core.Services Profile profile = new Profile(profileDescriptor.ProfileModule, profileEntity); InstantiateProfile(profile); - void ActivatingProfileSurfaceUpdate(object sender, SurfaceConfigurationEventArgs e) + void ActivatingProfileSurfaceUpdate(object? sender, SurfaceConfigurationEventArgs e) { profile.PopulateLeds(e.Surface); } - void ActivatingProfilePluginToggle(object sender, PluginEventArgs e) + void ActivatingProfilePluginToggle(object? sender, PluginEventArgs e) { InstantiateProfile(profile); } @@ -292,12 +292,12 @@ namespace Artemis.Core.Services #region Event handlers - private void OnActiveSurfaceConfigurationSelected(object sender, SurfaceConfigurationEventArgs e) + private void OnActiveSurfaceConfigurationSelected(object? sender, SurfaceConfigurationEventArgs e) { ActiveProfilesPopulateLeds(e.Surface); } - private void OnSurfaceConfigurationUpdated(object sender, SurfaceConfigurationEventArgs e) + private void OnSurfaceConfigurationUpdated(object? sender, SurfaceConfigurationEventArgs e) { if (e.Surface.IsActive) ActiveProfilesPopulateLeds(e.Surface); diff --git a/src/Artemis.Core/Services/Storage/SurfaceService.cs b/src/Artemis.Core/Services/Storage/SurfaceService.cs index fe0d251eb..af1b2a349 100644 --- a/src/Artemis.Core/Services/Storage/SurfaceService.cs +++ b/src/Artemis.Core/Services/Storage/SurfaceService.cs @@ -201,7 +201,7 @@ namespace Artemis.Core.Services #region Event handlers - private void RgbServiceOnDeviceLoaded(object sender, DeviceEventArgs e) + private void RgbServiceOnDeviceLoaded(object? sender, DeviceEventArgs e) { lock (_surfaceConfigurations) { @@ -212,7 +212,7 @@ namespace Artemis.Core.Services UpdateSurfaceConfiguration(ActiveSurface, true); } - private void RenderScaleSettingOnSettingChanged(object sender, EventArgs e) + private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e) { foreach (ArtemisSurface surfaceConfiguration in SurfaceConfigurations) { diff --git a/src/Artemis.Core/Stores/Registrations/ConditionOperatorRegistration.cs b/src/Artemis.Core/Stores/Registrations/ConditionOperatorRegistration.cs index 46cb22068..2691ee1ec 100644 --- a/src/Artemis.Core/Stores/Registrations/ConditionOperatorRegistration.cs +++ b/src/Artemis.Core/Stores/Registrations/ConditionOperatorRegistration.cs @@ -30,7 +30,7 @@ namespace Artemis.Core /// public bool IsInStore { get; internal set; } - private void OnDisabled(object sender, EventArgs e) + private void OnDisabled(object? sender, EventArgs e) { Plugin.Disabled -= OnDisabled; if (IsInStore) diff --git a/src/Artemis.Core/Stores/Registrations/DataBindingModifierTypeRegistration.cs b/src/Artemis.Core/Stores/Registrations/DataBindingModifierTypeRegistration.cs index b0ff66088..ddab462bd 100644 --- a/src/Artemis.Core/Stores/Registrations/DataBindingModifierTypeRegistration.cs +++ b/src/Artemis.Core/Stores/Registrations/DataBindingModifierTypeRegistration.cs @@ -30,7 +30,7 @@ namespace Artemis.Core /// public bool IsInStore { get; internal set; } - private void OnDisabled(object sender, EventArgs e) + private void OnDisabled(object? sender, EventArgs e) { Plugin.Disabled -= OnDisabled; if (IsInStore) diff --git a/src/Artemis.Core/Stores/Registrations/DataModelRegistration.cs b/src/Artemis.Core/Stores/Registrations/DataModelRegistration.cs index 7a1812e02..09c162758 100644 --- a/src/Artemis.Core/Stores/Registrations/DataModelRegistration.cs +++ b/src/Artemis.Core/Stores/Registrations/DataModelRegistration.cs @@ -31,7 +31,7 @@ namespace Artemis.Core /// public bool IsInStore { get; internal set; } - private void OnDisabled(object sender, EventArgs e) + private void OnDisabled(object? sender, EventArgs e) { PluginFeature.Disabled -= OnDisabled; if (IsInStore) diff --git a/src/Artemis.Core/Stores/Registrations/LayerBrushRegistration.cs b/src/Artemis.Core/Stores/Registrations/LayerBrushRegistration.cs index 8e35bf905..812a2e836 100644 --- a/src/Artemis.Core/Stores/Registrations/LayerBrushRegistration.cs +++ b/src/Artemis.Core/Stores/Registrations/LayerBrushRegistration.cs @@ -31,7 +31,7 @@ namespace Artemis.Core /// public bool IsInStore { get; internal set; } - private void OnDisabled(object sender, EventArgs e) + private void OnDisabled(object? sender, EventArgs e) { PluginFeature.Disabled -= OnDisabled; if (IsInStore) diff --git a/src/Artemis.Core/Stores/Registrations/LayerEffectRegistration.cs b/src/Artemis.Core/Stores/Registrations/LayerEffectRegistration.cs index 0b74524f0..8a53c590a 100644 --- a/src/Artemis.Core/Stores/Registrations/LayerEffectRegistration.cs +++ b/src/Artemis.Core/Stores/Registrations/LayerEffectRegistration.cs @@ -31,7 +31,7 @@ namespace Artemis.Core /// public bool IsInStore { get; internal set; } - private void OnDisabled(object sender, EventArgs e) + private void OnDisabled(object? sender, EventArgs e) { PluginFeature.Disabled -= OnDisabled; if (IsInStore) diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertiesViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertiesViewModel.cs index e8e91d41f..fd038c6f6 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertiesViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertiesViewModel.cs @@ -29,7 +29,7 @@ namespace Artemis.UI.Shared set => SetAndNotify(ref _listType, value); } - public object DisplayValue + public new object DisplayValue { get => _displayValue; set => SetAndNotify(ref _displayValue, value); diff --git a/src/Artemis.UI.Shared/Utilities/PluginUtilities.cs b/src/Artemis.UI.Shared/Utilities/PluginUtilities.cs index cdf75ad1c..03c5b5cb4 100644 --- a/src/Artemis.UI.Shared/Utilities/PluginUtilities.cs +++ b/src/Artemis.UI.Shared/Utilities/PluginUtilities.cs @@ -39,8 +39,6 @@ namespace Artemis.UI.Shared if (parsedIcon == false) iconEnum = PackIconKind.QuestionMarkCircle; return iconEnum; - - return icon; } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs index b18d39f74..b7fed07a5 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs @@ -194,27 +194,31 @@ namespace Artemis.UI.Screens.Sidebar SelectedItem = SidebarModules.ContainsKey(sidebarItem) ? _moduleVmFactory.CreateModuleRootViewModel(SidebarModules[sidebarItem]) : null; } + #region IDisposable + + protected virtual void Dispose(bool disposing) + { + if (disposing) + _activeModulesUpdateTimer?.Dispose(); + } + public void Dispose() { - SelectedItem?.Deactivate(); - SelectedItem = null; - - _pluginManagementService.PluginFeatureEnabled -= OnFeatureEnabled; - _pluginManagementService.PluginFeatureDisabled -= OnFeatureDisabled; - - _activeModulesUpdateTimer.Stop(); - _activeModulesUpdateTimer.Elapsed -= ActiveModulesUpdateTimerOnElapsed; + Dispose(true); + GC.SuppressFinalize(this); } + #endregion + #region Event handlers - private void OnFeatureEnabled(object? sender, PluginFeatureEventArgs e) + private void OnFeatureEnabled(object sender, PluginFeatureEventArgs e) { if (e.PluginFeature is Module module) AddModule(module); } - private void OnFeatureDisabled(object? sender, PluginFeatureEventArgs e) + private void OnFeatureDisabled(object sender, PluginFeatureEventArgs e) { if (e.PluginFeature is Module module) RemoveModule(module);