1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Core - Corrected disposable pattern usage

Core - Further nullable refactoring..
This commit is contained in:
Robert 2020-11-17 20:54:49 +01:00
parent 27e25f22ed
commit b185b28645
38 changed files with 429 additions and 162 deletions

View File

@ -13,7 +13,8 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<DocumentationFile>bin\x64\Debug\Artemis.Core.xml</DocumentationFile> <DocumentationFile>bin\x64\Debug\Artemis.Core.xml</DocumentationFile>
<NoWarn>1701;1702</NoWarn> <NoWarn></NoWarn>
<WarningLevel>5</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
@ -26,6 +27,7 @@
<NrtRequiredVcs>git</NrtRequiredVcs> <NrtRequiredVcs>git</NrtRequiredVcs>
<NrtShowRevision>true</NrtShowRevision> <NrtShowRevision>true</NrtShowRevision>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AnalysisLevel>latest</AnalysisLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

View File

@ -7,9 +7,9 @@ namespace Artemis.Core
public class DataBinding<TLayerProperty, TProperty> : IDataBinding public class DataBinding<TLayerProperty, TProperty> : IDataBinding
{ {
private TProperty _currentValue = default!; private TProperty _currentValue = default!;
private TProperty _previousValue = default!;
private bool _disposed; private bool _disposed;
private TimeSpan _easingProgress; private TimeSpan _easingProgress;
private TProperty _previousValue = default!;
internal DataBinding(DataBindingRegistration<TLayerProperty, TProperty> dataBindingRegistration) internal DataBinding(DataBindingRegistration<TLayerProperty, TProperty> dataBindingRegistration)
{ {
@ -98,6 +98,24 @@ namespace Artemis.Core
return Registration?.PropertyExpression.ReturnType; return Registration?.PropertyExpression.ReturnType;
} }
/// <summary>
/// Updates the smoothing progress of the data binding
/// </summary>
/// <param name="delta">The delta to apply during update</param>
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) private void ResetEasing(TProperty value)
{ {
_previousValue = GetInterpolatedValue(); _previousValue = GetInterpolatedValue();
@ -143,24 +161,6 @@ namespace Artemis.Core
UpdateWithDelta(timeline.Delta); UpdateWithDelta(timeline.Delta);
} }
/// <summary>
/// Updates the smoothing progress of the data binding
/// </summary>
/// <param name="delta">The delta to apply during update</param>
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;
}
/// <inheritdoc /> /// <inheritdoc />
public void Apply() public void Apply()
{ {
@ -175,16 +175,36 @@ namespace Artemis.Core
Converter.ApplyValue(value); Converter.ApplyValue(value);
} }
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposed = true;
if (Registration != null)
Registration.DataBinding = null;
DataBindingMode?.Dispose();
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_disposed = true; Dispose(true);
GC.SuppressFinalize(this);
if (Registration != null)
Registration.DataBinding = null;
DataBindingMode?.Dispose();
} }
#endregion
#region Mode management #region Mode management
/// <summary> /// <summary>
@ -255,7 +275,7 @@ namespace Artemis.Core
LayerProperty.Entity.DataBindingEntities.Add(Entity); LayerProperty.Entity.DataBindingEntities.Add(Entity);
// Don't save an invalid state // Don't save an invalid state
if (Registration != null) if (Registration != null)
Entity.TargetExpression = Registration.PropertyExpression.ToString(); Entity.TargetExpression = Registration.PropertyExpression.ToString();
Entity.EasingTime = EasingTime; Entity.EasingTime = EasingTime;

View File

@ -43,13 +43,29 @@ namespace Artemis.Core
#region IDisposable #region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposed = true;
foreach (DataBindingCondition<TLayerProperty, TProperty> dataBindingCondition in Conditions)
dataBindingCondition.Dispose();
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_disposed = true; Dispose(true);
GC.SuppressFinalize(this);
foreach (DataBindingCondition<TLayerProperty, TProperty> dataBindingCondition in Conditions)
dataBindingCondition.Dispose();
} }
#endregion #endregion

View File

@ -92,12 +92,31 @@ namespace Artemis.Core
Order = Entity.Order; Order = Entity.Order;
} }
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposed = true;
ConditionalDataBinding.Dispose();
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_disposed = true; Dispose(true);
GC.SuppressFinalize(this);
Condition.Dispose();
} }
#endregion
} }
} }

View File

@ -270,17 +270,37 @@ namespace Artemis.Core
// Parameter is done during initialize // Parameter is done during initialize
} }
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposed = true;
DataBindingModifierTypeStore.DataBindingModifierAdded -= DataBindingModifierTypeStoreOnDataBindingModifierAdded;
DataBindingModifierTypeStore.DataBindingModifierRemoved -= DataBindingModifierTypeStoreOnDataBindingModifierRemoved;
ParameterPath?.Dispose();
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_disposed = true; Dispose(true);
GC.SuppressFinalize(this);
DataBindingModifierTypeStore.DataBindingModifierAdded -= DataBindingModifierTypeStoreOnDataBindingModifierAdded;
DataBindingModifierTypeStore.DataBindingModifierRemoved -= DataBindingModifierTypeStoreOnDataBindingModifierRemoved;
ParameterPath?.Dispose();
} }
#endregion
#region Event handlers #region Event handlers
private void DataBindingModifierTypeStoreOnDataBindingModifierAdded(object? sender, DataBindingModifierTypeStoreEvent e) private void DataBindingModifierTypeStoreOnDataBindingModifierAdded(object? sender, DataBindingModifierTypeStoreEvent e)

View File

@ -54,15 +54,31 @@ namespace Artemis.Core
#region IDisposable #region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposed = true;
foreach (DataBindingModifier<TLayerProperty, TProperty> dataBindingModifier in Modifiers)
dataBindingModifier.Dispose();
SourcePath?.Dispose();
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_disposed = true; Dispose(true);
GC.SuppressFinalize(this);
foreach (DataBindingModifier<TLayerProperty, TProperty> dataBindingModifier in Modifiers)
dataBindingModifier.Dispose();
SourcePath?.Dispose();
} }
#endregion #endregion

View File

@ -247,15 +247,31 @@ namespace Artemis.Core
#region IDisposable #region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposed = true;
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
Invalidate();
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_disposed = true; Dispose(true);
GC.SuppressFinalize(this);
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
Invalidate();
} }
#endregion #endregion

View File

@ -250,19 +250,35 @@ namespace Artemis.Core
#region IDisposable #region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_dynamicDataModel != null)
{
_dynamicDataModel.DynamicDataModelAdded -= DynamicDataModelOnDynamicDataModelAdded;
_dynamicDataModel.DynamicDataModelRemoved -= DynamicDataModelOnDynamicDataModelRemoved;
}
Type = DataModelPathSegmentType.Invalid;
_accessorLambda = null;
Accessor = null;
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
if (_dynamicDataModel != null) Dispose(true);
{ GC.SuppressFinalize(this);
_dynamicDataModel.DynamicDataModelAdded -= DynamicDataModelOnDynamicDataModelAdded;
_dynamicDataModel.DynamicDataModelRemoved -= DynamicDataModelOnDynamicDataModelRemoved;
}
Type = DataModelPathSegmentType.Invalid;
_accessorLambda = null;
Accessor = null;
} }
#endregion #endregion

View File

@ -28,7 +28,7 @@ namespace Artemis.Core
// Cant define generic types as nullable ¯\_(ツ)_/¯ // Cant define generic types as nullable ¯\_(ツ)_/¯
CurrentValue = default!; CurrentValue = default!;
DefaultValue = default!; DefaultValue = default!;
_baseValue = default!; _baseValue = default!;
_keyframes = new List<LayerPropertyKeyframe<T>>(); _keyframes = new List<LayerPropertyKeyframe<T>>();
} }
@ -61,15 +61,35 @@ namespace Artemis.Core
OnUpdated(); OnUpdated();
} }
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposed = true;
foreach (IDataBinding dataBinding in _dataBindings)
dataBinding.Dispose();
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_disposed = true; Dispose(true);
GC.SuppressFinalize(this);
foreach (IDataBinding dataBinding in _dataBindings)
dataBinding.Dispose();
} }
#endregion
#region Hierarchy #region Hierarchy
private bool _isHidden; private bool _isHidden;

View File

@ -263,16 +263,32 @@ namespace Artemis.Core
#region IDisposable #region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
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();
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_disposed = true; Dispose(true);
DisableProperties(); GC.SuppressFinalize(this);
foreach (ILayerProperty layerProperty in _layerProperties)
layerProperty.Dispose();
foreach (LayerPropertyGroup layerPropertyGroup in _layerPropertyGroups)
layerPropertyGroup.Dispose();
} }
#endregion #endregion

View File

@ -13,6 +13,7 @@ namespace Artemis.Core
public sealed class Profile : ProfileElement public sealed class Profile : ProfileElement
{ {
private bool _isActivated; private bool _isActivated;
private readonly object _lock = new object();
internal Profile(ProfileModule module, string name) internal Profile(ProfileModule module, string name)
{ {
@ -64,7 +65,7 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public override void Update(double deltaTime) public override void Update(double deltaTime)
{ {
lock (this) lock (_lock)
{ {
if (Disposed) if (Disposed)
throw new ObjectDisposedException("Profile"); throw new ObjectDisposedException("Profile");
@ -79,7 +80,7 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public override void Render(SKCanvas canvas) public override void Render(SKCanvas canvas)
{ {
lock (this) lock (_lock)
{ {
if (Disposed) if (Disposed)
throw new ObjectDisposedException("Profile"); throw new ObjectDisposedException("Profile");
@ -182,7 +183,7 @@ namespace Artemis.Core
internal void Activate(ArtemisSurface surface) internal void Activate(ArtemisSurface surface)
{ {
lock (this) lock (_lock)
{ {
if (Disposed) if (Disposed)
throw new ObjectDisposedException("Profile"); throw new ObjectDisposedException("Profile");

View File

@ -287,7 +287,7 @@ namespace Artemis.Core
OnLayerEffectsUpdated(); 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 // If effects provided by the plugin are on the element, replace them with placeholders
List<BaseLayerEffect> pluginEffects = LayerEffectsList.Where(ef => ef.Descriptor.Provider != null && List<BaseLayerEffect> 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)) if (RenderElementEntity.LayerEffects.Any(ef => ef.ProviderId == e.Registration.PluginFeature.Id))
ActivateEffects(); ActivateEffects();

View File

@ -12,6 +12,7 @@ namespace Artemis.Core
public class Timeline : CorePropertyChanged, IStorageModel public class Timeline : CorePropertyChanged, IStorageModel
{ {
private const int MaxExtraTimelines = 15; private const int MaxExtraTimelines = 15;
private readonly object _lock = new object();
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="Timeline" /> class /// Creates a new instance of the <see cref="Timeline" /> class
@ -19,9 +20,10 @@ namespace Artemis.Core
public Timeline() public Timeline()
{ {
Entity = new TimelineEntity(); Entity = new TimelineEntity();
_extraTimelines = new List<Timeline>();
MainSegmentLength = TimeSpan.FromSeconds(5); MainSegmentLength = TimeSpan.FromSeconds(5);
_extraTimelines = new List<Timeline>();
Save(); Save();
} }
@ -35,6 +37,7 @@ namespace Artemis.Core
private Timeline(Timeline parent) private Timeline(Timeline parent)
{ {
Entity = new TimelineEntity();
Parent = parent; Parent = parent;
StartSegmentLength = Parent.StartSegmentLength; StartSegmentLength = Parent.StartSegmentLength;
MainSegmentLength = Parent.MainSegmentLength; MainSegmentLength = Parent.MainSegmentLength;
@ -303,7 +306,7 @@ namespace Artemis.Core
/// <param name="stickToMainSegment">Whether to stick to the main segment, wrapping around if needed</param> /// <param name="stickToMainSegment">Whether to stick to the main segment, wrapping around if needed</param>
public void Update(TimeSpan delta, bool stickToMainSegment) public void Update(TimeSpan delta, bool stickToMainSegment)
{ {
lock (this) lock (_lock)
{ {
Delta += delta; Delta += delta;
Position += delta; Position += delta;
@ -330,7 +333,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public void JumpToStart() public void JumpToStart()
{ {
lock (this) lock (_lock)
{ {
if (Position == TimeSpan.Zero) if (Position == TimeSpan.Zero)
return; return;
@ -345,7 +348,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public void JumpToEndSegment() public void JumpToEndSegment()
{ {
lock (this) lock (_lock)
{ {
if (Position >= EndSegmentStartPosition) if (Position >= EndSegmentStartPosition)
return; return;
@ -360,7 +363,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public void JumpToEnd() public void JumpToEnd()
{ {
lock (this) lock (_lock)
{ {
if (Position >= EndSegmentEndPosition) if (Position >= EndSegmentEndPosition)
return; return;
@ -377,7 +380,7 @@ namespace Artemis.Core
/// <param name="stickToMainSegment">Whether to stick to the main segment, wrapping around if needed</param> /// <param name="stickToMainSegment">Whether to stick to the main segment, wrapping around if needed</param>
public void Override(TimeSpan position, bool stickToMainSegment) public void Override(TimeSpan position, bool stickToMainSegment)
{ {
lock (this) lock (_lock)
{ {
Delta += position - Position; Delta += position - Position;
Position = position; Position = position;
@ -395,7 +398,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public void ClearDelta() public void ClearDelta()
{ {
lock (this) lock (_lock)
{ {
Delta = TimeSpan.Zero; Delta = TimeSpan.Zero;
} }

View File

@ -97,19 +97,32 @@ namespace Artemis.Core.LayerBrushes
internal abstract void InternalRender(SKCanvas canvas, SKRect path, SKPaint paint); internal abstract void InternalRender(SKCanvas canvas, SKRect path, SKPaint paint);
internal virtual void Dispose(bool disposing) #region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{ {
if (disposing)
{
DisableLayerBrush();
BaseProperties?.Dispose();
}
} }
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
DisableLayerBrush();
BaseProperties?.Dispose();
Dispose(true); Dispose(true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
#endregion
} }
/// <summary> /// <summary>

View File

@ -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 // I imagine a null reference here can be confusing, so lets throw an exception explaining what to do
if (_properties == null) 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; return _properties;
} }
internal set => _properties = value; internal set => _properties = value;

View File

@ -46,7 +46,7 @@ namespace Artemis.Core.LayerBrushes
LayerBrushStore.Add(descriptor); 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 // The store will clean up the registrations by itself, the plugin just needs to clear its own list
_layerBrushDescriptors.Clear(); _layerBrushDescriptors.Clear();

View File

@ -56,7 +56,10 @@ namespace Artemis.Core.LayerBrushes
UpdateLedGroup(); UpdateLedGroup();
} }
internal override void Dispose(bool disposing) #region IDisposable
/// <inheritdoc />
protected override void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {
@ -67,13 +70,15 @@ namespace Artemis.Core.LayerBrushes
base.Dispose(disposing); base.Dispose(disposing);
} }
#endregion
// Not used in this effect type // Not used in this effect type
internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint) internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint)
{ {
throw new NotImplementedException("RGB.NET layer effectes do not implement InternalRender"); 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(); UpdateLedGroup();
} }

View File

@ -103,13 +103,33 @@ namespace Artemis.Core.LayerEffects
internal string PropertyRootPath => $"LayerEffect.{EntityId}.{GetType().Name}."; internal string PropertyRootPath => $"LayerEffect.{EntityId}.{GetType().Name}.";
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
DisableLayerEffect();
BaseProperties?.Dispose();
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
DisableLayerEffect(); Dispose(true);
BaseProperties?.Dispose(); GC.SuppressFinalize(this);
} }
#endregion
/// <summary> /// <summary>
/// Called when the layer effect is activated /// Called when the layer effect is activated
/// </summary> /// </summary>

View File

@ -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 // I imagine a null reference here can be confusing, so lets throw an exception explaining what to do
if (_properties == null) 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; return _properties;
} }
internal set => _properties = value; internal set => _properties = value;

View File

@ -46,7 +46,7 @@ namespace Artemis.Core.LayerEffects
LayerEffectStore.Add(descriptor); 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 // The store will clean up the registrations by itself, the plugin just needs to clear its own list
_layerEffectDescriptors.Clear(); _layerEffectDescriptors.Clear();

View File

@ -140,7 +140,7 @@ namespace Artemis.Core.Modules
internal DataModel? InternalDataModel { get; set; } internal DataModel? InternalDataModel { get; set; }
internal bool InternalExpandsMainDataModel { get; set; } internal bool InternalExpandsMainDataModel { get; set; }
internal ModuleSettingsEntity? Entity { get; set; } internal ModuleSettingsEntity? SettingsEntity { get; set; }
/// <summary> /// <summary>
/// Called each frame when the module should update /// Called each frame when the module should update
@ -233,12 +233,12 @@ namespace Artemis.Core.Modules
internal void ApplyToEntity() internal void ApplyToEntity()
{ {
if (Entity == null) if (SettingsEntity == null)
Entity = new ModuleSettingsEntity(); SettingsEntity = new ModuleSettingsEntity();
Entity.ModuleId = Id; SettingsEntity.ModuleId = Id;
Entity.PriorityCategory = (int) PriorityCategory; SettingsEntity.PriorityCategory = (int) PriorityCategory;
Entity.Priority = Priority; SettingsEntity.Priority = Priority;
} }
} }

View File

@ -96,6 +96,7 @@ namespace Artemis.Core.Modules
/// Gets a list of all properties ignored at runtime using <c>IgnoreProperty(x => x.y)</c> /// Gets a list of all properties ignored at runtime using <c>IgnoreProperty(x => x.y)</c>
/// </summary> /// </summary>
protected internal readonly List<PropertyInfo> HiddenPropertiesList = new List<PropertyInfo>(); protected internal readonly List<PropertyInfo> HiddenPropertiesList = new List<PropertyInfo>();
private readonly object _lock = new object();
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="ProfileModule" /> class /// Creates a new instance of the <see cref="ProfileModule" /> class
@ -154,7 +155,7 @@ namespace Artemis.Core.Modules
if (IsUpdateAllowed) if (IsUpdateAllowed)
Update(deltaTime); Update(deltaTime);
lock (this) lock (_lock)
{ {
OpacityOverride = AnimatingProfileChange OpacityOverride = AnimatingProfileChange
? Math.Max(0, OpacityOverride - 0.1) ? Math.Max(0, OpacityOverride - 0.1)
@ -172,7 +173,7 @@ namespace Artemis.Core.Modules
{ {
Render(deltaTime, surface, canvas, canvasInfo); Render(deltaTime, surface, canvas, canvasInfo);
lock (this) lock (_lock)
{ {
// Render the profile // Render the profile
ActiveProfile?.Render(canvas); ActiveProfile?.Render(canvas);
@ -210,7 +211,7 @@ namespace Artemis.Core.Modules
if (!IsActivated) if (!IsActivated)
throw new ArtemisCoreException("Cannot activate a profile on a deactivated module"); throw new ArtemisCoreException("Cannot activate a profile on a deactivated module");
lock (this) lock (_lock)
{ {
if (profile == ActiveProfile) if (profile == ActiveProfile)
return; return;

View File

@ -160,19 +160,39 @@ namespace Artemis.Core
} }
} }
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
foreach (PluginFeature feature in Features)
feature.Dispose();
Kernel?.Dispose();
PluginLoader?.Dispose();
_features.Clear();
SetEnabled(false);
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
foreach (PluginFeature feature in Features) Dispose(true);
feature.Dispose(); GC.SuppressFinalize(this);
Kernel?.Dispose();
PluginLoader?.Dispose();
_features.Clear();
SetEnabled(false);
} }
#endregion
#region Events #region Events
/// <summary> /// <summary>

View File

@ -127,10 +127,30 @@ namespace Artemis.Core
Disable(); Disable();
} }
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the plugin feature and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Disable();
}
}
#endregion
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
Disable(); Dispose(true);
GC.SuppressFinalize(this);
} }
#region Loading #region Loading

View File

@ -13,6 +13,7 @@ namespace Artemis.Core
private DateTime _lastEvent; private DateTime _lastEvent;
private Timer _timer; private Timer _timer;
private bool _disposed; private bool _disposed;
private readonly object _lock = new object();
internal TimedUpdateRegistration(PluginFeature feature, TimeSpan interval, Action<double> action) internal TimedUpdateRegistration(PluginFeature feature, TimeSpan interval, Action<double> action)
{ {
@ -68,7 +69,7 @@ namespace Artemis.Core
if (_disposed) if (_disposed)
throw new ObjectDisposedException("TimedUpdateRegistration"); throw new ObjectDisposedException("TimedUpdateRegistration");
lock (this) lock (_lock)
{ {
if (!Feature.IsEnabled) if (!Feature.IsEnabled)
throw new ArtemisPluginException("Cannot start a timed update for a disabled plugin feature"); throw new ArtemisPluginException("Cannot start a timed update for a disabled plugin feature");
@ -92,7 +93,7 @@ namespace Artemis.Core
if (_disposed) if (_disposed)
throw new ObjectDisposedException("TimedUpdateRegistration"); throw new ObjectDisposedException("TimedUpdateRegistration");
lock (this) lock (_lock)
{ {
if (_timer == null) if (_timer == null)
return; return;
@ -109,7 +110,7 @@ namespace Artemis.Core
if (!Feature.IsEnabled) if (!Feature.IsEnabled)
return; return;
lock (this) lock (_lock)
{ {
TimeSpan interval = DateTime.Now - _lastEvent; TimeSpan interval = DateTime.Now - _lastEvent;
_lastEvent = DateTime.Now; _lastEvent = DateTime.Now;
@ -138,15 +139,35 @@ namespace Artemis.Core
Stop(); Stop();
} }
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Stop();
Feature.Enabled -= FeatureOnEnabled;
Feature.Disabled -= FeatureOnDisabled;
_disposed = true;
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
Stop(); Dispose(true);
GC.SuppressFinalize(this);
Feature.Enabled -= FeatureOnEnabled;
Feature.Disabled -= FeatureOnDisabled;
_disposed = true;
} }
#endregion
} }
} }

View File

@ -8,7 +8,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// The RGB.NET brush Artemis uses to map the SkiaSharp bitmap to LEDs /// The RGB.NET brush Artemis uses to map the SkiaSharp bitmap to LEDs
/// </summary> /// </summary>
public class BitmapBrush : AbstractDecoratable<IBrushDecorator>, IBrush, IDisposable public sealed class BitmapBrush : AbstractDecoratable<IBrushDecorator>, IBrush, IDisposable
{ {
private readonly object _disposeLock; private readonly object _disposeLock;
private readonly PluginSetting<int> _sampleSizeSetting; private readonly PluginSetting<int> _sampleSizeSetting;
@ -67,7 +67,7 @@ namespace Artemis.Core
#region Methods #region Methods
/// <inheritdoc /> /// <inheritdoc />
public virtual void PerformRender(Rectangle rectangle, IEnumerable<BrushRenderTarget> renderTargets) public void PerformRender(Rectangle rectangle, IEnumerable<BrushRenderTarget> renderTargets)
{ {
lock (_disposeLock) lock (_disposeLock)
{ {
@ -155,7 +155,7 @@ namespace Artemis.Core
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual void PerformFinalize() public void PerformFinalize()
{ {
} }

View File

@ -139,11 +139,11 @@ namespace Artemis.Core.Services
ModulePriorityCategory category = module.DefaultPriorityCategory; ModulePriorityCategory category = module.DefaultPriorityCategory;
int priority = 1; int priority = 1;
module.Entity = _moduleRepository.GetByModuleId(module.Id); module.SettingsEntity = _moduleRepository.GetByModuleId(module.Id);
if (module.Entity != null) if (module.SettingsEntity != null)
{ {
category = (ModulePriorityCategory) module.Entity.PriorityCategory; category = (ModulePriorityCategory) module.SettingsEntity.PriorityCategory;
priority = module.Entity.Priority; priority = module.SettingsEntity.Priority;
} }
UpdateModulePriority(module, category, priority); UpdateModulePriority(module, category, priority);
@ -255,10 +255,10 @@ namespace Artemis.Core.Services
categoryModule.Priority = index; categoryModule.Priority = index;
// Don't save modules whose priority hasn't been initialized yet // 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(); categoryModule.ApplyToEntity();
_moduleRepository.Save(categoryModule.Entity); _moduleRepository.Save(categoryModule.SettingsEntity);
} }
} }
} }

View File

@ -55,7 +55,7 @@ namespace Artemis.Core.Services
catch (Exception e) catch (Exception e)
{ {
_logger.Error(e, "Exception during device loading for device provider {deviceProvider}", deviceProvider.GetType().Name); _logger.Error(e, "Exception during device loading for device provider {deviceProvider}", deviceProvider.GetType().Name);
throw e; throw;
} }
if (deviceProvider.Devices == null || !deviceProvider.Devices.Any()) if (deviceProvider.Devices == null || !deviceProvider.Devices.Any())
@ -86,12 +86,12 @@ namespace Artemis.Core.Services
Surface.Dispose(); Surface.Dispose();
} }
private void RenderScaleSettingOnSettingChanged(object sender, EventArgs e) private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
{ {
UpdateSurfaceLedGroup(); UpdateSurfaceLedGroup();
} }
private void TargetFrameRateSettingOnSettingChanged(object sender, EventArgs e) private void TargetFrameRateSettingOnSettingChanged(object? sender, EventArgs e)
{ {
UpdateTrigger.UpdateFrequency = 1.0 / _targetFrameRateSetting.Value; UpdateTrigger.UpdateFrequency = 1.0 / _targetFrameRateSetting.Value;
} }
@ -104,8 +104,8 @@ namespace Artemis.Core.Services
#region Events #region Events
public event EventHandler<DeviceEventArgs> DeviceLoaded; public event EventHandler<DeviceEventArgs>? DeviceLoaded;
public event EventHandler<DeviceEventArgs> DeviceReloaded; public event EventHandler<DeviceEventArgs>? DeviceReloaded;
public void UpdateSurfaceLedGroup() public void UpdateSurfaceLedGroup()
{ {

View File

@ -107,12 +107,12 @@ namespace Artemis.Core.Services
Profile profile = new Profile(profileDescriptor.ProfileModule, profileEntity); Profile profile = new Profile(profileDescriptor.ProfileModule, profileEntity);
InstantiateProfile(profile); InstantiateProfile(profile);
void ActivatingProfileSurfaceUpdate(object sender, SurfaceConfigurationEventArgs e) void ActivatingProfileSurfaceUpdate(object? sender, SurfaceConfigurationEventArgs e)
{ {
profile.PopulateLeds(e.Surface); profile.PopulateLeds(e.Surface);
} }
void ActivatingProfilePluginToggle(object sender, PluginEventArgs e) void ActivatingProfilePluginToggle(object? sender, PluginEventArgs e)
{ {
InstantiateProfile(profile); InstantiateProfile(profile);
} }
@ -292,12 +292,12 @@ namespace Artemis.Core.Services
#region Event handlers #region Event handlers
private void OnActiveSurfaceConfigurationSelected(object sender, SurfaceConfigurationEventArgs e) private void OnActiveSurfaceConfigurationSelected(object? sender, SurfaceConfigurationEventArgs e)
{ {
ActiveProfilesPopulateLeds(e.Surface); ActiveProfilesPopulateLeds(e.Surface);
} }
private void OnSurfaceConfigurationUpdated(object sender, SurfaceConfigurationEventArgs e) private void OnSurfaceConfigurationUpdated(object? sender, SurfaceConfigurationEventArgs e)
{ {
if (e.Surface.IsActive) if (e.Surface.IsActive)
ActiveProfilesPopulateLeds(e.Surface); ActiveProfilesPopulateLeds(e.Surface);

View File

@ -201,7 +201,7 @@ namespace Artemis.Core.Services
#region Event handlers #region Event handlers
private void RgbServiceOnDeviceLoaded(object sender, DeviceEventArgs e) private void RgbServiceOnDeviceLoaded(object? sender, DeviceEventArgs e)
{ {
lock (_surfaceConfigurations) lock (_surfaceConfigurations)
{ {
@ -212,7 +212,7 @@ namespace Artemis.Core.Services
UpdateSurfaceConfiguration(ActiveSurface, true); UpdateSurfaceConfiguration(ActiveSurface, true);
} }
private void RenderScaleSettingOnSettingChanged(object sender, EventArgs e) private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
{ {
foreach (ArtemisSurface surfaceConfiguration in SurfaceConfigurations) foreach (ArtemisSurface surfaceConfiguration in SurfaceConfigurations)
{ {

View File

@ -30,7 +30,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public bool IsInStore { get; internal set; } public bool IsInStore { get; internal set; }
private void OnDisabled(object sender, EventArgs e) private void OnDisabled(object? sender, EventArgs e)
{ {
Plugin.Disabled -= OnDisabled; Plugin.Disabled -= OnDisabled;
if (IsInStore) if (IsInStore)

View File

@ -30,7 +30,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public bool IsInStore { get; internal set; } public bool IsInStore { get; internal set; }
private void OnDisabled(object sender, EventArgs e) private void OnDisabled(object? sender, EventArgs e)
{ {
Plugin.Disabled -= OnDisabled; Plugin.Disabled -= OnDisabled;
if (IsInStore) if (IsInStore)

View File

@ -31,7 +31,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public bool IsInStore { get; internal set; } public bool IsInStore { get; internal set; }
private void OnDisabled(object sender, EventArgs e) private void OnDisabled(object? sender, EventArgs e)
{ {
PluginFeature.Disabled -= OnDisabled; PluginFeature.Disabled -= OnDisabled;
if (IsInStore) if (IsInStore)

View File

@ -31,7 +31,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public bool IsInStore { get; internal set; } public bool IsInStore { get; internal set; }
private void OnDisabled(object sender, EventArgs e) private void OnDisabled(object? sender, EventArgs e)
{ {
PluginFeature.Disabled -= OnDisabled; PluginFeature.Disabled -= OnDisabled;
if (IsInStore) if (IsInStore)

View File

@ -31,7 +31,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public bool IsInStore { get; internal set; } public bool IsInStore { get; internal set; }
private void OnDisabled(object sender, EventArgs e) private void OnDisabled(object? sender, EventArgs e)
{ {
PluginFeature.Disabled -= OnDisabled; PluginFeature.Disabled -= OnDisabled;
if (IsInStore) if (IsInStore)

View File

@ -29,7 +29,7 @@ namespace Artemis.UI.Shared
set => SetAndNotify(ref _listType, value); set => SetAndNotify(ref _listType, value);
} }
public object DisplayValue public new object DisplayValue
{ {
get => _displayValue; get => _displayValue;
set => SetAndNotify(ref _displayValue, value); set => SetAndNotify(ref _displayValue, value);

View File

@ -39,8 +39,6 @@ namespace Artemis.UI.Shared
if (parsedIcon == false) if (parsedIcon == false)
iconEnum = PackIconKind.QuestionMarkCircle; iconEnum = PackIconKind.QuestionMarkCircle;
return iconEnum; return iconEnum;
return icon;
} }
} }
} }

View File

@ -194,27 +194,31 @@ namespace Artemis.UI.Screens.Sidebar
SelectedItem = SidebarModules.ContainsKey(sidebarItem) ? _moduleVmFactory.CreateModuleRootViewModel(SidebarModules[sidebarItem]) : null; SelectedItem = SidebarModules.ContainsKey(sidebarItem) ? _moduleVmFactory.CreateModuleRootViewModel(SidebarModules[sidebarItem]) : null;
} }
#region IDisposable
protected virtual void Dispose(bool disposing)
{
if (disposing)
_activeModulesUpdateTimer?.Dispose();
}
public void Dispose() public void Dispose()
{ {
SelectedItem?.Deactivate(); Dispose(true);
SelectedItem = null; GC.SuppressFinalize(this);
_pluginManagementService.PluginFeatureEnabled -= OnFeatureEnabled;
_pluginManagementService.PluginFeatureDisabled -= OnFeatureDisabled;
_activeModulesUpdateTimer.Stop();
_activeModulesUpdateTimer.Elapsed -= ActiveModulesUpdateTimerOnElapsed;
} }
#endregion
#region Event handlers #region Event handlers
private void OnFeatureEnabled(object? sender, PluginFeatureEventArgs e) private void OnFeatureEnabled(object sender, PluginFeatureEventArgs e)
{ {
if (e.PluginFeature is Module module) if (e.PluginFeature is Module module)
AddModule(module); AddModule(module);
} }
private void OnFeatureDisabled(object? sender, PluginFeatureEventArgs e) private void OnFeatureDisabled(object sender, PluginFeatureEventArgs e)
{ {
if (e.PluginFeature is Module module) if (e.PluginFeature is Module module)
RemoveModule(module); RemoveModule(module);