mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 03:48:56 +00:00
Surface - Removed the ArtemisSurface and reworked device loading
This commit is contained in:
1 parent
7ccc9c54fb
commit
056b2bface
50 files changed
+1292
-1506
No files matched your search
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data about surface configuration related events
|
||||
/// </summary>
|
||||
public class SurfaceConfigurationEventArgs : EventArgs
|
||||
{
|
||||
internal SurfaceConfigurationEventArgs(ArtemisSurface surface)
|
||||
{
|
||||
Surface = surface;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active surface at the time the event fired
|
||||
/// </summary>
|
||||
public ArtemisSurface Surface { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
@@ -8,7 +7,7 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public class DeviceEventArgs : EventArgs
|
||||
{
|
||||
internal DeviceEventArgs(IRGBDevice device)
|
||||
internal DeviceEventArgs(ArtemisDevice device)
|
||||
{
|
||||
Device = device;
|
||||
}
|
||||
@@ -16,6 +15,6 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Gets the device this event is related to
|
||||
/// </summary>
|
||||
public IRGBDevice Device { get; }
|
||||
public ArtemisDevice Device { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data about device configuration related events
|
||||
/// </summary>
|
||||
public class SurfaceConfigurationEventArgs : EventArgs
|
||||
{
|
||||
internal SurfaceConfigurationEventArgs(List<ArtemisDevice> devices)
|
||||
{
|
||||
Devices = devices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current list of devices
|
||||
/// </summary>
|
||||
public List<ArtemisDevice> Devices { get; }
|
||||
}
|
||||
}
|
||||
@@ -567,7 +567,7 @@ namespace Artemis.Core
|
||||
CalculateRenderProperties();
|
||||
}
|
||||
|
||||
internal void PopulateLeds(ArtemisSurface surface)
|
||||
internal void PopulateLeds(IEnumerable<ArtemisDevice> devices)
|
||||
{
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
@@ -575,7 +575,7 @@ namespace Artemis.Core
|
||||
List<ArtemisLed> leds = new();
|
||||
|
||||
// Get the surface LEDs for this layer
|
||||
List<ArtemisLed> availableLeds = surface.Devices.Where(d => d.IsEnabled).SelectMany(d => d.Leds).ToList();
|
||||
List<ArtemisLed> availableLeds = devices.SelectMany(d => d.Leds).ToList();
|
||||
foreach (LedEntity ledEntity in LayerEntity.Leds)
|
||||
{
|
||||
ArtemisLed? match = availableLeds.FirstOrDefault(a => a.Device.RgbDevice.GetDeviceIdentifier() == ledEntity.DeviceIdentifier &&
|
||||
|
||||
@@ -124,14 +124,14 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Populates all the LEDs on the elements in this profile
|
||||
/// </summary>
|
||||
/// <param name="surface">The currently active surface that contains the LEDs</param>
|
||||
public void PopulateLeds(ArtemisSurface surface)
|
||||
/// <param name="devices">The devices to use while populating LEDs</param>
|
||||
public void PopulateLeds(IEnumerable<ArtemisDevice> devices)
|
||||
{
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
|
||||
foreach (Layer layer in GetAllLayers())
|
||||
layer.PopulateLeds(surface);
|
||||
layer.PopulateLeds(devices);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -197,7 +197,7 @@ namespace Artemis.Core
|
||||
ProfileEntity.Layers.AddRange(GetAllLayers().Select(f => f.LayerEntity));
|
||||
}
|
||||
|
||||
internal void Activate(ArtemisSurface surface)
|
||||
internal void Activate(IEnumerable<ArtemisDevice> devices)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
@@ -206,7 +206,7 @@ namespace Artemis.Core
|
||||
if (IsActivated)
|
||||
return;
|
||||
|
||||
PopulateLeds(surface);
|
||||
PopulateLeds(devices);
|
||||
OnActivated();
|
||||
IsActivated = true;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core.DeviceProviders;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Storage.Entities.Surface;
|
||||
using RGB.NET.Core;
|
||||
using RGB.NET.Layout;
|
||||
@@ -18,12 +19,11 @@ namespace Artemis.Core
|
||||
private SKPath? _path;
|
||||
private SKRect _rectangle;
|
||||
|
||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, ArtemisSurface surface)
|
||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider)
|
||||
{
|
||||
DeviceEntity = new DeviceEntity();
|
||||
RgbDevice = rgbDevice;
|
||||
DeviceProvider = deviceProvider;
|
||||
Surface = surface;
|
||||
|
||||
Rotation = 0;
|
||||
Scale = 1;
|
||||
@@ -43,12 +43,11 @@ namespace Artemis.Core
|
||||
CalculateRenderProperties();
|
||||
}
|
||||
|
||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, ArtemisSurface surface, DeviceEntity deviceEntity)
|
||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity)
|
||||
{
|
||||
DeviceEntity = deviceEntity;
|
||||
RgbDevice = rgbDevice;
|
||||
DeviceProvider = deviceProvider;
|
||||
Surface = surface;
|
||||
|
||||
InputIdentifiers = new List<ArtemisDeviceInputIdentifier>();
|
||||
foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers)
|
||||
@@ -88,11 +87,6 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public DeviceProvider DeviceProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the surface containing this device
|
||||
/// </summary>
|
||||
public ArtemisSurface Surface { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection containing the LEDs of this device
|
||||
/// </summary>
|
||||
@@ -214,12 +208,13 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether this devices is enabled or not
|
||||
/// Gets a boolean indicating whether this devices is enabled or not
|
||||
/// <para>Note: To enable/disable a device use the methods provided by <see cref="IRgbService" /></para>
|
||||
/// </summary>
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => DeviceEntity.IsEnabled;
|
||||
set
|
||||
internal set
|
||||
{
|
||||
DeviceEntity.IsEnabled = value;
|
||||
OnPropertyChanged(nameof(IsEnabled));
|
||||
@@ -305,37 +300,6 @@ namespace Artemis.Core
|
||||
return fileName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the best layout for this device, preferring user layouts and falling back to default layouts
|
||||
/// </summary>
|
||||
public void LoadBestLayout()
|
||||
{
|
||||
|
||||
|
||||
// Look for a user layout
|
||||
// ... here
|
||||
|
||||
// Try loading a device provider layout, if that comes back valid we use that
|
||||
ArtemisLayout deviceProviderLayout = DeviceProvider.LoadLayout(this);
|
||||
|
||||
// Finally fall back to a default layout
|
||||
// .. do it!
|
||||
|
||||
ApplyLayout(deviceProviderLayout);
|
||||
}
|
||||
|
||||
private void ApplyKeyboardLayout()
|
||||
{
|
||||
if (!(RgbDevice is IKeyboard keyboard))
|
||||
return;
|
||||
|
||||
// If supported, detect the device layout so that we can load the correct one
|
||||
if (DeviceProvider.CanDetectLogicalLayout )
|
||||
LogicalLayout = DeviceProvider.GetLogicalLayout(keyboard);
|
||||
if (DeviceProvider.CanDetectPhysicalLayout)
|
||||
PhysicalLayout = (KeyboardLayoutType) keyboard.DeviceInfo.Layout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the provided layout to the device
|
||||
/// </summary>
|
||||
@@ -350,12 +314,13 @@ namespace Artemis.Core
|
||||
|
||||
Layout = layout;
|
||||
Layout.ApplyDevice(this);
|
||||
OnDeviceUpdated();
|
||||
}
|
||||
|
||||
internal void ApplyToEntity()
|
||||
{
|
||||
// Other properties are computed
|
||||
DeviceEntity.DeviceIdentifier = RgbDevice.GetDeviceIdentifier();
|
||||
DeviceEntity.Id = RgbDevice.GetDeviceIdentifier();
|
||||
|
||||
DeviceEntity.InputIdentifiers.Clear();
|
||||
foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers)
|
||||
@@ -400,6 +365,18 @@ namespace Artemis.Core
|
||||
Path = path;
|
||||
}
|
||||
|
||||
private void ApplyKeyboardLayout()
|
||||
{
|
||||
if (!(RgbDevice is IKeyboard keyboard))
|
||||
return;
|
||||
|
||||
// If supported, detect the device layout so that we can load the correct one
|
||||
if (DeviceProvider.CanDetectLogicalLayout)
|
||||
LogicalLayout = DeviceProvider.GetLogicalLayout(keyboard);
|
||||
if (DeviceProvider.CanDetectPhysicalLayout)
|
||||
PhysicalLayout = (KeyboardLayoutType) keyboard.DeviceInfo.Layout;
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Storage.Entities.Surface;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a surface of a specific scale, containing all the available <see cref="ArtemisDevice" />s
|
||||
/// </summary>
|
||||
public class ArtemisSurface : CorePropertyChanged
|
||||
{
|
||||
private List<ArtemisDevice> _devices = new();
|
||||
private ReadOnlyDictionary<Led, ArtemisLed> _ledMap = new(new Dictionary<Led, ArtemisLed>());
|
||||
private bool _isActive;
|
||||
private string _name;
|
||||
|
||||
internal ArtemisSurface(RGBSurface rgbSurface, string name)
|
||||
{
|
||||
SurfaceEntity = new SurfaceEntity {DeviceEntities = new List<DeviceEntity>()};
|
||||
EntityId = Guid.NewGuid();
|
||||
RgbSurface = rgbSurface;
|
||||
|
||||
_name = name;
|
||||
_isActive = false;
|
||||
|
||||
ApplyToEntity();
|
||||
}
|
||||
|
||||
internal ArtemisSurface(RGBSurface rgbSurface, SurfaceEntity surfaceEntity)
|
||||
{
|
||||
SurfaceEntity = surfaceEntity;
|
||||
EntityId = surfaceEntity.Id;
|
||||
RgbSurface = rgbSurface;
|
||||
|
||||
_name = surfaceEntity.Name;
|
||||
_isActive = surfaceEntity.IsActive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RGB.NET surface backing this Artemis surface
|
||||
/// </summary>
|
||||
public RGBSurface RgbSurface { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the surface
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetAndNotify(ref _name, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this surface is the currently active surface
|
||||
/// </summary>
|
||||
public bool IsActive
|
||||
{
|
||||
get => _isActive;
|
||||
internal set => SetAndNotify(ref _isActive, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of devices this surface contains
|
||||
/// </summary>
|
||||
public List<ArtemisDevice> Devices
|
||||
{
|
||||
get => _devices;
|
||||
internal set => SetAndNotify(ref _devices, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary containing all <see cref="ArtemisLed" />s on the surface with their corresponding RGB.NET
|
||||
/// <see cref="Led" /> as key
|
||||
/// </summary>
|
||||
public ReadOnlyDictionary<Led, ArtemisLed> LedMap
|
||||
{
|
||||
get => _ledMap;
|
||||
private set => SetAndNotify(ref _ledMap, value);
|
||||
}
|
||||
|
||||
internal SurfaceEntity SurfaceEntity { get; set; }
|
||||
internal Guid EntityId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the <see cref="ArtemisLed" /> that corresponds the provided RGB.NET <see cref="Led" />
|
||||
/// </summary>
|
||||
/// <param name="led">The RGB.NET <see cref="Led" /> to find the corresponding <see cref="ArtemisLed" /> for </param>
|
||||
/// <returns>If found, the corresponding <see cref="ArtemisLed" />; otherwise <see langword="null" />.</returns>
|
||||
public ArtemisLed? GetArtemisLed(Led led)
|
||||
{
|
||||
LedMap.TryGetValue(led, out ArtemisLed? artemisLed);
|
||||
return artemisLed;
|
||||
}
|
||||
|
||||
internal void UpdateLedMap()
|
||||
{
|
||||
LedMap = new ReadOnlyDictionary<Led, ArtemisLed>(
|
||||
_devices.Where(d => d.IsEnabled).SelectMany(d => d.Leds.Select(al => new KeyValuePair<Led, ArtemisLed>(al.RgbLed, al))).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
|
||||
);
|
||||
}
|
||||
|
||||
internal void ApplyToEntity()
|
||||
{
|
||||
SurfaceEntity.Id = EntityId;
|
||||
SurfaceEntity.Name = Name;
|
||||
SurfaceEntity.IsActive = IsActive;
|
||||
|
||||
// Add missing device entities, don't remove old ones in case they come back later
|
||||
foreach (DeviceEntity deviceEntity in Devices.Select(d => d.DeviceEntity).ToList())
|
||||
if (!SurfaceEntity.DeviceEntities.Contains(deviceEntity))
|
||||
SurfaceEntity.DeviceEntities.Add(deviceEntity);
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the scale of the surface is changed
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs>? ScaleChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ScaleChanged" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnScaleChanged()
|
||||
{
|
||||
ScaleChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,53 @@ namespace Artemis.Core
|
||||
FilePath = filePath;
|
||||
Leds = new List<ArtemisLedLayout>();
|
||||
|
||||
LoadLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file path the layout was (attempted to be) loaded from
|
||||
/// </summary>
|
||||
public string FilePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RGB.NET device layout
|
||||
/// </summary>
|
||||
public DeviceLayout RgbLayout { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device this layout is applied to
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a valid layout was loaded
|
||||
/// </summary>
|
||||
public bool IsValid { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image of the device
|
||||
/// </summary>
|
||||
public Uri? Image { get; private set; }
|
||||
|
||||
public List<ArtemisLedLayout> Leds { get; }
|
||||
|
||||
internal LayoutCustomDeviceData LayoutCustomDeviceData { get; set; }
|
||||
|
||||
public void ReloadFromDisk()
|
||||
{
|
||||
Leds.Clear();
|
||||
LoadLayout();
|
||||
}
|
||||
|
||||
internal void ApplyDevice(ArtemisDevice artemisDevice)
|
||||
{
|
||||
Device = artemisDevice;
|
||||
foreach (ArtemisLedLayout artemisLedLayout in Leds)
|
||||
artemisLedLayout.ApplyDevice(Device);
|
||||
}
|
||||
|
||||
private void LoadLayout()
|
||||
{
|
||||
DeviceLayout? deviceLayout = DeviceLayout.Load(FilePath, typeof(LayoutCustomDeviceData), typeof(LayoutCustomLedData));
|
||||
if (deviceLayout != null)
|
||||
{
|
||||
@@ -39,47 +86,13 @@ namespace Artemis.Core
|
||||
ApplyCustomDeviceData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file path the layout was (attempted to be) loaded from
|
||||
/// </summary>
|
||||
public string FilePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RGB.NET device layout
|
||||
/// </summary>
|
||||
public DeviceLayout RgbLayout { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device this layout is applied to
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a valid layout was loaded
|
||||
/// </summary>
|
||||
public bool IsValid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image of the device
|
||||
/// </summary>
|
||||
public Uri? Image { get; private set; }
|
||||
|
||||
public List<ArtemisLedLayout> Leds { get; }
|
||||
|
||||
internal LayoutCustomDeviceData LayoutCustomDeviceData { get; set; }
|
||||
|
||||
internal void ApplyDevice(ArtemisDevice artemisDevice)
|
||||
{
|
||||
Device = artemisDevice;
|
||||
foreach (ArtemisLedLayout artemisLedLayout in Leds)
|
||||
artemisLedLayout.ApplyDevice(Device);
|
||||
}
|
||||
|
||||
private void ApplyCustomDeviceData()
|
||||
{
|
||||
Uri layoutDirectory = new(Path.GetDirectoryName(FilePath)! + "\\", UriKind.Absolute);
|
||||
if (LayoutCustomDeviceData.DeviceImage != null)
|
||||
Image = new Uri(layoutDirectory, new Uri(LayoutCustomDeviceData.DeviceImage, UriKind.Relative));
|
||||
else
|
||||
Image = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,10 +146,9 @@ namespace Artemis.Core.Modules
|
||||
/// Called each frame when the module should render
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">Time since the last render</param>
|
||||
/// <param name="surface">The RGB Surface to render to</param>
|
||||
/// <param name="canvas"></param>
|
||||
/// <param name="canvasInfo"></param>
|
||||
public abstract void Render(double deltaTime, ArtemisSurface surface, SKCanvas canvas, SKImageInfo canvasInfo);
|
||||
public abstract void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="ActivationRequirements" /> are met or during an override
|
||||
@@ -191,9 +190,9 @@ namespace Artemis.Core.Modules
|
||||
Update(deltaTime);
|
||||
}
|
||||
|
||||
internal virtual void InternalRender(double deltaTime, ArtemisSurface surface, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
internal virtual void InternalRender(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
{
|
||||
Render(deltaTime, surface, canvas, canvasInfo);
|
||||
Render(deltaTime, canvas, canvasInfo);
|
||||
}
|
||||
|
||||
internal virtual void Activate(bool isOverride)
|
||||
|
||||
@@ -142,10 +142,9 @@ namespace Artemis.Core.Modules
|
||||
/// Called after the profile has rendered
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">Time since the last render</param>
|
||||
/// <param name="surface">The RGB Surface to render to</param>
|
||||
/// <param name="canvas"></param>
|
||||
/// <param name="canvasInfo"></param>
|
||||
public virtual void ProfileRendered(double deltaTime, ArtemisSurface surface, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
public virtual void ProfileRendered(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -168,9 +167,9 @@ namespace Artemis.Core.Modules
|
||||
ProfileUpdated(deltaTime);
|
||||
}
|
||||
|
||||
internal override void InternalRender(double deltaTime, ArtemisSurface surface, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
internal override void InternalRender(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
{
|
||||
Render(deltaTime, surface, canvas, canvasInfo);
|
||||
Render(deltaTime, canvas, canvasInfo);
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
@@ -178,10 +177,10 @@ namespace Artemis.Core.Modules
|
||||
ActiveProfile?.Render(canvas);
|
||||
}
|
||||
|
||||
ProfileRendered(deltaTime, surface, canvas, canvasInfo);
|
||||
ProfileRendered(deltaTime, canvas, canvasInfo);
|
||||
}
|
||||
|
||||
internal async Task ChangeActiveProfileAnimated(Profile? profile, ArtemisSurface? surface)
|
||||
internal async Task ChangeActiveProfileAnimated(Profile? profile, IEnumerable<ArtemisDevice> devices)
|
||||
{
|
||||
if (profile != null && profile.Module != this)
|
||||
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}.");
|
||||
@@ -196,21 +195,19 @@ namespace Artemis.Core.Modules
|
||||
while (OpacityOverride > 0)
|
||||
await Task.Delay(50);
|
||||
|
||||
ChangeActiveProfile(profile, surface);
|
||||
ChangeActiveProfile(profile, devices);
|
||||
AnimatingProfileChange = false;
|
||||
|
||||
while (OpacityOverride < 1)
|
||||
await Task.Delay(50);
|
||||
}
|
||||
|
||||
internal void ChangeActiveProfile(Profile? profile, ArtemisSurface? surface)
|
||||
internal void ChangeActiveProfile(Profile? profile, IEnumerable<ArtemisDevice> devices)
|
||||
{
|
||||
if (profile != null && profile.Module != this)
|
||||
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}.");
|
||||
if (!IsActivated)
|
||||
throw new ArtemisCoreException("Cannot activate a profile on a deactivated module");
|
||||
if (profile != null && surface == null)
|
||||
throw new ArtemisCoreException("If changing the active profile to a non-null profile, a surface is required");
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
@@ -220,7 +217,7 @@ namespace Artemis.Core.Modules
|
||||
ActiveProfile?.Dispose();
|
||||
|
||||
ActiveProfile = profile;
|
||||
ActiveProfile?.Activate(surface!);
|
||||
ActiveProfile?.Activate(devices);
|
||||
}
|
||||
|
||||
OnActiveProfileChanged();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.Services;
|
||||
using RGB.NET.Core;
|
||||
using SkiaSharp;
|
||||
|
||||
@@ -12,13 +13,15 @@ namespace Artemis.Core
|
||||
{
|
||||
private readonly object _disposeLock;
|
||||
private readonly PluginSetting<int> _sampleSizeSetting;
|
||||
private readonly IRgbService _rgbService;
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal BitmapBrush(Scale scale, PluginSetting<int> sampleSizeSetting)
|
||||
internal BitmapBrush(Scale scale, PluginSetting<int> sampleSizeSetting, IRgbService rgbService)
|
||||
{
|
||||
_disposeLock = new object();
|
||||
_sampleSizeSetting = sampleSizeSetting;
|
||||
_rgbService = rgbService;
|
||||
Scale = scale;
|
||||
}
|
||||
|
||||
@@ -103,7 +106,7 @@ namespace Artemis.Core
|
||||
if (scaledLocation.X < Bitmap.Width && scaledLocation.Y < Bitmap.Height)
|
||||
{
|
||||
Color pixel = Bitmap.GetPixel(scaledLocation.X.RoundToInt(), scaledLocation.Y.RoundToInt()).ToRgbColor();
|
||||
ArtemisDevice? artemisDevice = Surface?.GetArtemisLed(renderTarget.Led)?.Device;
|
||||
ArtemisDevice? artemisDevice = _rgbService.GetLed(renderTarget.Led)?.Device;
|
||||
if (artemisDevice != null)
|
||||
pixel = pixel.MultiplyRGB(artemisDevice.RedScale, artemisDevice.GreenScale, artemisDevice.BlueScale);
|
||||
RenderedTargets[renderTarget] = pixel;
|
||||
@@ -157,7 +160,7 @@ namespace Artemis.Core
|
||||
|
||||
Color pixel = new(a / sampleSize, r / sampleSize, g / sampleSize, b / sampleSize);
|
||||
|
||||
ArtemisDevice? artemisDevice = Surface?.GetArtemisLed(renderTarget.Led)?.Device;
|
||||
ArtemisDevice? artemisDevice = _rgbService.GetLed(renderTarget.Led)?.Device;
|
||||
if (artemisDevice is not null)
|
||||
pixel = pixel.MultiplyRGB(artemisDevice.RedScale, artemisDevice.GreenScale, artemisDevice.BlueScale);
|
||||
|
||||
@@ -188,7 +191,6 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
internal bool IsDisposed { get; set; }
|
||||
internal ArtemisSurface? Surface { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ namespace Artemis.Core.Services
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly PluginSetting<double> _renderScale;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly ISurfaceService _surfaceService;
|
||||
private readonly List<Exception> _updateExceptions = new();
|
||||
private List<BaseDataModelExpansion> _dataModelExpansions = new();
|
||||
private DateTime _lastExceptionLog;
|
||||
@@ -47,7 +46,6 @@ namespace Artemis.Core.Services
|
||||
ISettingsService settingsService,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IRgbService rgbService,
|
||||
ISurfaceService surfaceService,
|
||||
IProfileService profileService,
|
||||
IModuleService moduleService // injected to ensure module priorities get applied
|
||||
)
|
||||
@@ -58,7 +56,6 @@ namespace Artemis.Core.Services
|
||||
_logger = logger;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_rgbService = rgbService;
|
||||
_surfaceService = surfaceService;
|
||||
_profileService = profileService;
|
||||
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
|
||||
_renderScale = settingsService.GetSetting("Core.RenderScale", 0.5);
|
||||
@@ -121,9 +118,6 @@ namespace Artemis.Core.Services
|
||||
IsElevated
|
||||
);
|
||||
|
||||
ArtemisSurface surfaceConfig = _surfaceService.ActiveSurface;
|
||||
_logger.Information("Initialized with active surface entity {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.EntityId);
|
||||
|
||||
OnInitialized();
|
||||
}
|
||||
|
||||
@@ -139,7 +133,7 @@ namespace Artemis.Core.Services
|
||||
|
||||
public void PlayIntroAnimation()
|
||||
{
|
||||
IntroAnimation intro = new(_logger, _profileService, _surfaceService);
|
||||
IntroAnimation intro = new(_logger, _profileService, _rgbService.EnabledDevices);
|
||||
|
||||
// Draw a white overlay over the device
|
||||
void DrawOverlay(object? sender, FrameRenderingEventArgs args)
|
||||
@@ -236,7 +230,7 @@ namespace Artemis.Core.Services
|
||||
if (!ModuleRenderingDisabled)
|
||||
// While non-activated modules may be updated above if they expand the main data model, they may never render
|
||||
foreach (Module module in modules.Where(m => m.IsActivated))
|
||||
module.InternalRender(args.DeltaTime, _surfaceService.ActiveSurface, canvas, _rgbService.BitmapBrush.Bitmap.Info);
|
||||
module.InternalRender(args.DeltaTime, canvas, _rgbService.BitmapBrush.Bitmap.Info);
|
||||
|
||||
OnFrameRendering(new FrameRenderingEventArgs(canvas, args.DeltaTime, _rgbService.Surface));
|
||||
}
|
||||
|
||||
@@ -9,15 +9,13 @@ namespace Artemis.Core.Services
|
||||
internal class InputService : IInputService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly ISurfaceService _surfaceService;
|
||||
private readonly IRgbService _rgbService;
|
||||
|
||||
public InputService(ILogger logger, ISurfaceService surfaceService)
|
||||
public InputService(ILogger logger, IRgbService rgbService)
|
||||
{
|
||||
_logger = logger;
|
||||
_surfaceService = surfaceService;
|
||||
_rgbService = rgbService;
|
||||
|
||||
_surfaceService.ActiveSurfaceConfigurationSelected += SurfaceConfigurationChanged;
|
||||
_surfaceService.SurfaceConfigurationUpdated += SurfaceConfigurationChanged;
|
||||
BustIdentifierCache();
|
||||
}
|
||||
|
||||
@@ -91,7 +89,7 @@ namespace Artemis.Core.Services
|
||||
_logger.Debug("Stop identifying device {device}", _identifyingDevice);
|
||||
|
||||
_identifyingDevice = null;
|
||||
_surfaceService.UpdateSurfaceConfiguration(_surfaceService.ActiveSurface, true);
|
||||
_rgbService.SaveDevices();
|
||||
|
||||
BustIdentifierCache();
|
||||
}
|
||||
@@ -123,7 +121,7 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
if (_cachedFallbackKeyboard != null)
|
||||
return _cachedFallbackKeyboard;
|
||||
_cachedFallbackKeyboard = _surfaceService.ActiveSurface.Devices.FirstOrDefault(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard);
|
||||
_cachedFallbackKeyboard = _rgbService.EnabledDevices.FirstOrDefault(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard);
|
||||
return _cachedFallbackKeyboard;
|
||||
}
|
||||
|
||||
@@ -131,7 +129,7 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
if (_cachedFallbackMouse != null)
|
||||
return _cachedFallbackMouse;
|
||||
_cachedFallbackMouse = _surfaceService.ActiveSurface.Devices.FirstOrDefault(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse);
|
||||
_cachedFallbackMouse = _rgbService.EnabledDevices.FirstOrDefault(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse);
|
||||
return _cachedFallbackMouse;
|
||||
}
|
||||
|
||||
@@ -144,7 +142,7 @@ namespace Artemis.Core.Services
|
||||
_cachedFallbackKeyboard = null;
|
||||
_cachedFallbackMouse = null;
|
||||
|
||||
_devices = _surfaceService.ActiveSurface.Devices.Where(d => d.InputIdentifiers.Any()).ToList();
|
||||
_devices = _rgbService.EnabledDevices.Where(d => d.InputIdentifiers.Any()).ToList();
|
||||
}
|
||||
|
||||
private void AddDeviceToCache(ArtemisDevice match, InputProvider provider, object identifier)
|
||||
|
||||
@@ -9,6 +9,22 @@ namespace Artemis.Core.Services
|
||||
/// </summary>
|
||||
public interface IRgbService : IArtemisService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a read-only collection containing all enabled devices
|
||||
/// </summary>
|
||||
IReadOnlyCollection<ArtemisDevice> EnabledDevices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only collection containing all registered devices
|
||||
/// </summary>
|
||||
IReadOnlyCollection<ArtemisDevice> Devices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary containing all <see cref="ArtemisLed" />s on the surface with their corresponding RGB.NET
|
||||
/// <see cref="Led" /> as key
|
||||
/// </summary>
|
||||
IReadOnlyDictionary<Led, ArtemisLed> LedMap { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the RGB surface rendering is performed on
|
||||
/// </summary>
|
||||
@@ -19,16 +35,6 @@ namespace Artemis.Core.Services
|
||||
/// </summary>
|
||||
BitmapBrush? BitmapBrush { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scale the frames are rendered on, a scale of 1.0 means 1 pixel = 1mm
|
||||
/// </summary>
|
||||
double RenderScale { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all loaded RGB devices
|
||||
/// </summary>
|
||||
IReadOnlyCollection<IRGBDevice> LoadedDevices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the update trigger that drives the render loop
|
||||
/// </summary>
|
||||
@@ -46,14 +52,66 @@ namespace Artemis.Core.Services
|
||||
void AddDeviceProvider(IRGBDeviceProvider deviceProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a single device has loaded
|
||||
/// Removes the given device provider from the <see cref="Surface" />
|
||||
/// </summary>
|
||||
event EventHandler<DeviceEventArgs> DeviceLoaded;
|
||||
/// <param name="deviceProvider"></param>
|
||||
void RemoveDeviceProvider(IRGBDeviceProvider deviceProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a single device has reloaded
|
||||
/// Applies auto-arranging logic to the surface
|
||||
/// </summary>
|
||||
event EventHandler<DeviceEventArgs> DeviceReloaded;
|
||||
void AutoArrangeDevices();
|
||||
|
||||
/// <summary>
|
||||
/// Applies the best available layout for the given <see cref="ArtemisDevice" />
|
||||
/// </summary>
|
||||
/// <param name="device">The device to apply the best available layout to</param>
|
||||
/// <returns>The layout that was applied to the device</returns>
|
||||
ArtemisLayout ApplyBestDeviceLayout(ArtemisDevice device);
|
||||
|
||||
/// <summary>
|
||||
/// Apples the provided <see cref="ArtemisLayout" /> to the provided <see cref="ArtemisDevice" />
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
/// <param name="layout"></param>
|
||||
void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the <see cref="ArtemisDevice" /> that corresponds the provided RGB.NET
|
||||
/// <see cref="IRGBDevice" />
|
||||
/// </summary>
|
||||
/// <param name="rgbDevice">
|
||||
/// The RGB.NET <see cref="IRGBDevice" /> to find the corresponding <see cref="ArtemisDevice" />
|
||||
/// for
|
||||
/// </param>
|
||||
/// <returns>If found, the corresponding <see cref="ArtemisDevice" />; otherwise <see langword="null" />.</returns>
|
||||
ArtemisDevice? GetDevice(IRGBDevice rgbDevice);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the <see cref="ArtemisLed" /> that corresponds the provided RGB.NET <see cref="Led" />
|
||||
/// </summary>
|
||||
/// <param name="led">The RGB.NET <see cref="Led" /> to find the corresponding <see cref="ArtemisLed" /> for </param>
|
||||
/// <returns>If found, the corresponding <see cref="ArtemisLed" />; otherwise <see langword="null" />.</returns>
|
||||
ArtemisLed? GetLed(Led led);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the configuration of the provided device to persistent storage
|
||||
/// </summary>
|
||||
/// <param name="artemisDevice"></param>
|
||||
void SaveDevice(ArtemisDevice artemisDevice);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the configuration of all current devices to persistent storage
|
||||
/// </summary>
|
||||
void SaveDevices();
|
||||
|
||||
void EnableDevice(ArtemisDevice device);
|
||||
void DisableDevice(ArtemisDevice device);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a single device was added
|
||||
/// </summary>
|
||||
event EventHandler<DeviceEventArgs> DeviceAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a single device was removed
|
||||
@@ -61,9 +119,8 @@ namespace Artemis.Core.Services
|
||||
event EventHandler<DeviceEventArgs> DeviceRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the LED group used by the <see cref="BitmapBrush" />
|
||||
/// Occurs when the surface has had modifications to its LED collection
|
||||
/// </summary>
|
||||
/// <param name="artemisSurface"></param>
|
||||
void UpdateSurfaceLedGroup(ArtemisSurface artemisSurface);
|
||||
event EventHandler LedsChanged;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core.DeviceProviders;
|
||||
using Artemis.Core.Services.Models;
|
||||
using Artemis.Storage.Entities.Surface;
|
||||
using Artemis.Storage.Repositories.Interfaces;
|
||||
using RGB.NET.Core;
|
||||
using RGB.NET.Groups;
|
||||
using RGB.NET.Layout;
|
||||
using Serilog;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
@@ -13,16 +17,24 @@ namespace Artemis.Core.Services
|
||||
/// </summary>
|
||||
internal class RgbService : IRgbService
|
||||
{
|
||||
private readonly List<IRGBDevice> _loadedDevices;
|
||||
private readonly ILogger _logger;
|
||||
private readonly PluginSetting<double> _renderScaleSetting;
|
||||
private readonly PluginSetting<int> _sampleSizeSetting;
|
||||
private readonly PluginSetting<int> _targetFrameRateSetting;
|
||||
private ListLedGroup? _surfaceLedGroup;
|
||||
private readonly List<ArtemisDevice> _devices;
|
||||
private readonly List<ArtemisDevice> _enabledDevices;
|
||||
private Dictionary<Led, ArtemisLed> _ledMap;
|
||||
|
||||
public RgbService(ILogger logger, ISettingsService settingsService)
|
||||
private readonly ILogger _logger;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly PluginSetting<double> _renderScaleSetting;
|
||||
private readonly PluginSetting<int> _targetFrameRateSetting;
|
||||
private readonly PluginSetting<int> _sampleSizeSetting;
|
||||
private ListLedGroup? _surfaceLedGroup;
|
||||
private bool _modifyingProviders;
|
||||
|
||||
public RgbService(ILogger logger, ISettingsService settingsService, IPluginManagementService pluginManagementService, IDeviceRepository deviceRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_deviceRepository = deviceRepository;
|
||||
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.5);
|
||||
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 25);
|
||||
_sampleSizeSetting = settingsService.GetSetting("Core.SampleSize", 1);
|
||||
@@ -33,57 +45,131 @@ namespace Artemis.Core.Services
|
||||
Surface.Exception += SurfaceOnException;
|
||||
_renderScaleSetting.SettingChanged += RenderScaleSettingOnSettingChanged;
|
||||
_targetFrameRateSetting.SettingChanged += TargetFrameRateSettingOnSettingChanged;
|
||||
_loadedDevices = new List<IRGBDevice>();
|
||||
_enabledDevices = new List<ArtemisDevice>();
|
||||
_devices = new List<ArtemisDevice>();
|
||||
_ledMap = new Dictionary<Led, ArtemisLed>();
|
||||
|
||||
UpdateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / _targetFrameRateSetting.Value};
|
||||
Surface.RegisterUpdateTrigger(UpdateTrigger);
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<ArtemisDevice> EnabledDevices => _enabledDevices.AsReadOnly();
|
||||
public IReadOnlyCollection<ArtemisDevice> Devices => _devices.AsReadOnly();
|
||||
public IReadOnlyDictionary<Led, ArtemisLed> LedMap => new ReadOnlyDictionary<Led, ArtemisLed>(_ledMap);
|
||||
|
||||
/// <inheritdoc />
|
||||
public RGBSurface Surface { get; set; }
|
||||
|
||||
public TimerUpdateTrigger UpdateTrigger { get; }
|
||||
public BitmapBrush? BitmapBrush { get; private set; }
|
||||
public IReadOnlyCollection<IRGBDevice> LoadedDevices => _loadedDevices.AsReadOnly();
|
||||
public double RenderScale => _renderScaleSetting.Value;
|
||||
|
||||
public bool IsRenderPaused { get; set; }
|
||||
|
||||
public void AddDeviceProvider(IRGBDeviceProvider deviceProvider)
|
||||
{
|
||||
try
|
||||
lock (_devices)
|
||||
{
|
||||
List<IRGBDevice> toRemove = deviceProvider.Devices.Where(d => Surface.Devices.Contains(d)).ToList();
|
||||
Surface.Detach(deviceProvider.Devices);
|
||||
foreach (IRGBDevice rgbDevice in toRemove)
|
||||
OnDeviceRemoved(new DeviceEventArgs(rgbDevice));
|
||||
|
||||
deviceProvider.Initialize(RGBDeviceType.All, true);
|
||||
Surface.Attach(deviceProvider.Devices);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Exception during device loading for device provider {deviceProvider}", deviceProvider.GetType().Name);
|
||||
throw;
|
||||
}
|
||||
|
||||
if (deviceProvider.Devices == null || !deviceProvider.Devices.Any())
|
||||
{
|
||||
_logger.Warning("Device provider {deviceProvider} has no devices", deviceProvider.GetType().Name);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (IRGBDevice surfaceDevice in deviceProvider.Devices)
|
||||
{
|
||||
_logger.Debug("Device provider {deviceProvider} added {deviceName}", deviceProvider.GetType().Name, surfaceDevice.DeviceInfo?.DeviceName);
|
||||
if (!_loadedDevices.Contains(surfaceDevice))
|
||||
try
|
||||
{
|
||||
_loadedDevices.Add(surfaceDevice);
|
||||
OnDeviceLoaded(new DeviceEventArgs(surfaceDevice));
|
||||
_modifyingProviders = true;
|
||||
|
||||
List<ArtemisDevice> toRemove = _devices.Where(a => deviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList();
|
||||
Surface.Detach(deviceProvider.Devices);
|
||||
foreach (ArtemisDevice device in toRemove)
|
||||
RemoveDevice(device);
|
||||
|
||||
deviceProvider.Initialize(RGBDeviceType.All, true);
|
||||
Surface.Attach(deviceProvider.Devices);
|
||||
|
||||
if (!deviceProvider.Devices.Any())
|
||||
{
|
||||
_logger.Warning("Device provider {deviceProvider} has no devices", deviceProvider.GetType().Name);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (IRGBDevice rgbDevice in deviceProvider.Devices)
|
||||
{
|
||||
ArtemisDevice artemisDevice = GetArtemisDevice(rgbDevice);
|
||||
AddDevice(artemisDevice);
|
||||
_logger.Debug("Device provider {deviceProvider} added {deviceName}", deviceProvider.GetType().Name, rgbDevice.DeviceInfo?.DeviceName);
|
||||
}
|
||||
|
||||
_devices.Sort((a, b) => a.ZIndex - b.ZIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Exception during device loading for device provider {deviceProvider}", deviceProvider.GetType().Name);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_modifyingProviders = false;
|
||||
UpdateBitmapBrush();
|
||||
}
|
||||
else
|
||||
OnDeviceReloaded(new DeviceEventArgs(surfaceDevice));
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveDeviceProvider(IRGBDeviceProvider deviceProvider)
|
||||
{
|
||||
lock (_devices)
|
||||
{
|
||||
try
|
||||
{
|
||||
_modifyingProviders = true;
|
||||
|
||||
List<ArtemisDevice> toRemove = _devices.Where(a => deviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList();
|
||||
Surface.Detach(deviceProvider.Devices);
|
||||
foreach (ArtemisDevice device in toRemove)
|
||||
RemoveDevice(device);
|
||||
|
||||
_devices.Sort((a, b) => a.ZIndex - b.ZIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Exception during device removal for device provider {deviceProvider}", deviceProvider.GetType().Name);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_modifyingProviders = false;
|
||||
UpdateBitmapBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBitmapBrush()
|
||||
{
|
||||
lock (_devices)
|
||||
{
|
||||
if (_modifyingProviders)
|
||||
return;
|
||||
|
||||
_ledMap = new Dictionary<Led, ArtemisLed>(_devices.SelectMany(d => d.Leds).ToDictionary(l => l.RgbLed));
|
||||
|
||||
if (_surfaceLedGroup == null || BitmapBrush == null)
|
||||
{
|
||||
// Apply the application wide brush and decorator
|
||||
BitmapBrush = new BitmapBrush(new Scale(_renderScaleSetting.Value), _sampleSizeSetting, this);
|
||||
_surfaceLedGroup = new ListLedGroup(Surface, LedMap.Select(l => l.Key)) {Brush = BitmapBrush};
|
||||
OnLedsChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_surfaceLedGroup)
|
||||
{
|
||||
// Clean up the old background
|
||||
_surfaceLedGroup.Detach(Surface);
|
||||
|
||||
// Apply the application wide brush and decorator
|
||||
BitmapBrush.Scale = new Scale(_renderScaleSetting.Value);
|
||||
_surfaceLedGroup = new ListLedGroup(Surface, LedMap.Select(l => l.Key)) {Brush = BitmapBrush};
|
||||
OnLedsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Surface.UnregisterUpdateTrigger(UpdateTrigger);
|
||||
@@ -92,10 +178,154 @@ namespace Artemis.Core.Services
|
||||
Surface.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EnabledDevices
|
||||
|
||||
public void AutoArrangeDevices()
|
||||
{
|
||||
SurfaceArrangement surfaceArrangement = SurfaceArrangement.GetDefaultArrangement();
|
||||
surfaceArrangement.Arrange(_devices);
|
||||
SaveDevices();
|
||||
}
|
||||
|
||||
public ArtemisLayout ApplyBestDeviceLayout(ArtemisDevice device)
|
||||
{
|
||||
// Look for a user layout
|
||||
// ... here
|
||||
|
||||
// Try loading a device provider layout, if that comes back valid we use that
|
||||
ArtemisLayout layout = device.DeviceProvider.LoadLayout(device);
|
||||
|
||||
// Finally fall back to a default layout
|
||||
// .. do it!
|
||||
|
||||
ApplyDeviceLayout(device, layout);
|
||||
return layout;
|
||||
}
|
||||
|
||||
public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout)
|
||||
{
|
||||
device.ApplyLayout(layout);
|
||||
// Applying layouts can affect LEDs, update LED group
|
||||
UpdateBitmapBrush();
|
||||
}
|
||||
|
||||
public ArtemisDevice? GetDevice(IRGBDevice rgbDevice)
|
||||
{
|
||||
return _devices.FirstOrDefault(d => d.RgbDevice == rgbDevice);
|
||||
}
|
||||
|
||||
public ArtemisLed? GetLed(Led led)
|
||||
{
|
||||
LedMap.TryGetValue(led, out ArtemisLed? artemisLed);
|
||||
return artemisLed;
|
||||
}
|
||||
|
||||
public void EnableDevice(ArtemisDevice device)
|
||||
{
|
||||
if (device.IsEnabled)
|
||||
return;
|
||||
|
||||
_enabledDevices.Add(device);
|
||||
device.IsEnabled = true;
|
||||
device.ApplyToEntity();
|
||||
_deviceRepository.Save(device.DeviceEntity);
|
||||
|
||||
UpdateBitmapBrush();
|
||||
OnDeviceAdded(new DeviceEventArgs(device));
|
||||
}
|
||||
|
||||
public void DisableDevice(ArtemisDevice device)
|
||||
{
|
||||
if (!device.IsEnabled)
|
||||
return;
|
||||
|
||||
_enabledDevices.Remove(device);
|
||||
device.IsEnabled = false;
|
||||
device.ApplyToEntity();
|
||||
_deviceRepository.Save(device.DeviceEntity);
|
||||
|
||||
UpdateBitmapBrush();
|
||||
OnDeviceRemoved(new DeviceEventArgs(device));
|
||||
}
|
||||
|
||||
private void AddDevice(ArtemisDevice device)
|
||||
{
|
||||
if (_devices.Any(d => d.RgbDevice == device.RgbDevice))
|
||||
throw new ArtemisCoreException("Attempted to add a duplicate device to the RGB Service");
|
||||
|
||||
device.ApplyToRgbDevice();
|
||||
_devices.Add(device);
|
||||
if (device.IsEnabled)
|
||||
_enabledDevices.Add(device);
|
||||
|
||||
// Will call UpdateBitmapBrush()
|
||||
ApplyBestDeviceLayout(device);
|
||||
OnDeviceAdded(new DeviceEventArgs(device));
|
||||
}
|
||||
|
||||
private void RemoveDevice(ArtemisDevice device)
|
||||
{
|
||||
_devices.Remove(device);
|
||||
if (device.IsEnabled)
|
||||
_enabledDevices.Remove(device);
|
||||
|
||||
UpdateBitmapBrush();
|
||||
OnDeviceRemoved(new DeviceEventArgs(device));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Storage
|
||||
|
||||
private ArtemisDevice GetArtemisDevice(IRGBDevice rgbDevice)
|
||||
{
|
||||
string deviceIdentifier = rgbDevice.GetDeviceIdentifier();
|
||||
DeviceEntity? deviceEntity = _deviceRepository.Get(deviceIdentifier);
|
||||
DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
|
||||
|
||||
if (deviceEntity != null)
|
||||
return new ArtemisDevice(rgbDevice, deviceProvider, deviceEntity);
|
||||
|
||||
// Fall back on creating a new device
|
||||
_logger.Information(
|
||||
"No device config found for {deviceInfo}, device hash: {deviceHashCode}. Adding a new entry.",
|
||||
rgbDevice.DeviceInfo,
|
||||
deviceIdentifier
|
||||
);
|
||||
return new ArtemisDevice(rgbDevice, deviceProvider);
|
||||
}
|
||||
|
||||
public void SaveDevice(ArtemisDevice artemisDevice)
|
||||
{
|
||||
artemisDevice.ApplyToEntity();
|
||||
artemisDevice.ApplyToRgbDevice();
|
||||
|
||||
_deviceRepository.Save(artemisDevice.DeviceEntity);
|
||||
OnLedsChanged();
|
||||
}
|
||||
|
||||
public void SaveDevices()
|
||||
{
|
||||
foreach (ArtemisDevice artemisDevice in _devices)
|
||||
{
|
||||
artemisDevice.ApplyToEntity();
|
||||
artemisDevice.ApplyToRgbDevice();
|
||||
}
|
||||
|
||||
_deviceRepository.Save(_devices.Select(d => d.DeviceEntity));
|
||||
OnLedsChanged();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
// The surface hasn't changed so we can safely reuse it
|
||||
UpdateSurfaceLedGroup(BitmapBrush?.Surface);
|
||||
UpdateBitmapBrush();
|
||||
}
|
||||
|
||||
private void TargetFrameRateSettingOnSettingChanged(object? sender, EventArgs e)
|
||||
@@ -109,52 +339,29 @@ namespace Artemis.Core.Services
|
||||
throw args.Exception;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler<DeviceEventArgs>? DeviceLoaded;
|
||||
public event EventHandler<DeviceEventArgs>? DeviceReloaded;
|
||||
public event EventHandler<DeviceEventArgs> DeviceRemoved;
|
||||
public event EventHandler<DeviceEventArgs>? DeviceAdded;
|
||||
public event EventHandler<DeviceEventArgs>? DeviceRemoved;
|
||||
public event EventHandler? LedsChanged;
|
||||
|
||||
public void UpdateSurfaceLedGroup(ArtemisSurface? artemisSurface)
|
||||
private void OnDeviceAdded(DeviceEventArgs e)
|
||||
{
|
||||
if (artemisSurface == null)
|
||||
return;
|
||||
|
||||
if (_surfaceLedGroup == null || BitmapBrush == null)
|
||||
{
|
||||
// Apply the application wide brush and decorator
|
||||
BitmapBrush = new BitmapBrush(new Scale(_renderScaleSetting.Value), _sampleSizeSetting);
|
||||
_surfaceLedGroup = new ListLedGroup(Surface, artemisSurface.LedMap.Select(l => l.Key)) {Brush = BitmapBrush};
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_surfaceLedGroup)
|
||||
{
|
||||
// Clean up the old background
|
||||
_surfaceLedGroup.Detach(Surface);
|
||||
|
||||
// Apply the application wide brush and decorator
|
||||
BitmapBrush.Scale = new Scale(_renderScaleSetting.Value);
|
||||
BitmapBrush.Surface = artemisSurface;
|
||||
_surfaceLedGroup = new ListLedGroup(Surface, artemisSurface.LedMap.Select(l => l.Key)) {Brush = BitmapBrush};
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDeviceLoaded(DeviceEventArgs e)
|
||||
{
|
||||
DeviceLoaded?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void OnDeviceReloaded(DeviceEventArgs e)
|
||||
{
|
||||
DeviceReloaded?.Invoke(this, e);
|
||||
DeviceAdded?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnDeviceRemoved(DeviceEventArgs e)
|
||||
{
|
||||
DeviceRemoved?.Invoke(this, e);
|
||||
}
|
||||
#endregion
|
||||
|
||||
protected virtual void OnLedsChanged()
|
||||
{
|
||||
LedsChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the device surface and its configuration
|
||||
/// </summary>
|
||||
public interface ISurfaceService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the currently active surface entity, to change config use <see cref="SetActiveSurfaceConfiguration" />
|
||||
/// </summary>
|
||||
ArtemisSurface ActiveSurface { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only list of all surface configurations
|
||||
/// </summary>
|
||||
ReadOnlyCollection<ArtemisSurface> SurfaceConfigurations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new surface entity with the supplied name
|
||||
/// </summary>
|
||||
/// <param name="name">The name for the new surface entity</param>
|
||||
/// <returns></returns>
|
||||
ArtemisSurface CreateSurfaceConfiguration(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the provided entity as active and applies it to the surface
|
||||
/// </summary>
|
||||
/// <param name="surface">The entity to activate and apply</param>
|
||||
void SetActiveSurfaceConfiguration(ArtemisSurface surface);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the provided surface entity to permanent storage and if config is active, applies it to the surface
|
||||
/// </summary>
|
||||
/// <param name="surface">The entity to save (and apply if active)</param>
|
||||
/// <param name="includeDevices">Whether to also save devices. If false, devices changes won't be applied either</param>
|
||||
void UpdateSurfaceConfiguration(ArtemisSurface surface, bool includeDevices);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the supplied surface entity, surface entity may not be the active surface entity
|
||||
/// </summary>
|
||||
/// <param name="surface">The surface entity to delete, may not be the active surface entity</param>
|
||||
void DeleteSurfaceConfiguration(ArtemisSurface surface);
|
||||
|
||||
/// <summary>
|
||||
/// Applies auto-arranging logic to a surface
|
||||
/// </summary>
|
||||
/// <param name="artemisSurface">
|
||||
/// The surface to apply auto-arrange to. If <see langword="null" /> the
|
||||
/// <see cref="ActiveSurface" /> is used.
|
||||
/// </param>
|
||||
void AutoArrange(ArtemisSurface? artemisSurface = null);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the active device entity has been changed
|
||||
/// </summary>
|
||||
event EventHandler<SurfaceConfigurationEventArgs> ActiveSurfaceConfigurationSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a surface configuration has been updated
|
||||
/// </summary>
|
||||
event EventHandler<SurfaceConfigurationEventArgs> SurfaceConfigurationUpdated;
|
||||
}
|
||||
}
|
||||
@@ -76,10 +76,10 @@ namespace Artemis.Core.Services.Models
|
||||
return surfaceArrangementType;
|
||||
}
|
||||
|
||||
public void Arrange(ArtemisSurface surface)
|
||||
public void Arrange(List<ArtemisDevice> devices)
|
||||
{
|
||||
ArrangedDevices.Clear();
|
||||
foreach (ArtemisDevice surfaceDevice in surface.Devices)
|
||||
foreach (ArtemisDevice surfaceDevice in devices)
|
||||
{
|
||||
surfaceDevice.X = 0;
|
||||
surfaceDevice.Y = 0;
|
||||
@@ -87,14 +87,14 @@ namespace Artemis.Core.Services.Models
|
||||
}
|
||||
|
||||
foreach (SurfaceArrangementType surfaceArrangementType in Types)
|
||||
surfaceArrangementType.Arrange(surface);
|
||||
surfaceArrangementType.Arrange(devices);
|
||||
|
||||
// See if we need to move the surface to keep X and Y values positive
|
||||
double x = surface.Devices.Min(d => d.RgbDevice.Location.X);
|
||||
double y = surface.Devices.Min(d => d.RgbDevice.Location.Y);
|
||||
double x = devices.Min(d => d.RgbDevice.Location.X);
|
||||
double y = devices.Min(d => d.RgbDevice.Location.Y);
|
||||
if (x < 0)
|
||||
{
|
||||
foreach (ArtemisDevice surfaceDevice in surface.Devices)
|
||||
foreach (ArtemisDevice surfaceDevice in devices)
|
||||
{
|
||||
surfaceDevice.X += x * -1;
|
||||
surfaceDevice.ApplyToRgbDevice();
|
||||
@@ -103,7 +103,7 @@ namespace Artemis.Core.Services.Models
|
||||
|
||||
if (y < 0)
|
||||
{
|
||||
foreach (ArtemisDevice surfaceDevice in surface.Devices)
|
||||
foreach (ArtemisDevice surfaceDevice in devices)
|
||||
{
|
||||
surfaceDevice.Y += y * -1;
|
||||
surfaceDevice.ApplyToRgbDevice();
|
||||
|
||||
@@ -32,14 +32,14 @@ namespace Artemis.Core.Services.Models
|
||||
public int MarginBottom { get; }
|
||||
public SurfaceArrangement SurfaceArrangement { get; set; }
|
||||
|
||||
public bool Apply(List<ArtemisDevice> devices, ArtemisSurface surface)
|
||||
public bool Apply(List<ArtemisDevice> devices)
|
||||
{
|
||||
if (Anchor != null && !Anchor.HasDevices(surface))
|
||||
if (Anchor != null && !Anchor.HasDevices(devices))
|
||||
return false;
|
||||
|
||||
// Start at the edge of the anchor, if there is no anchor start at any device
|
||||
Point startPoint = Anchor?.GetEdge(HorizontalPosition, VerticalPosition, surface) ??
|
||||
new SurfaceArrangementType(SurfaceArrangement, RGBDeviceType.All, 1).GetEdge(HorizontalPosition, VerticalPosition, surface);
|
||||
Point startPoint = Anchor?.GetEdge(HorizontalPosition, VerticalPosition) ??
|
||||
new SurfaceArrangementType(SurfaceArrangement, RGBDeviceType.All, 1).GetEdge(HorizontalPosition, VerticalPosition);
|
||||
|
||||
// Stack multiple devices of the same type vertically if they are wider than they are tall
|
||||
bool stackVertically = devices.Average(d => d.RgbDevice.Size.Width) >= devices.Average(d => d.RgbDevice.Size.Height);
|
||||
|
||||
@@ -21,21 +21,21 @@ namespace Artemis.Core.Services.Models
|
||||
public List<SurfaceArrangementConfiguration> Configurations { get; }
|
||||
public SurfaceArrangementConfiguration? AppliedConfiguration { get; set; }
|
||||
|
||||
public bool HasDevices(ArtemisSurface surface)
|
||||
public bool HasDevices(List<ArtemisDevice> devices)
|
||||
{
|
||||
return surface.Devices.Any(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType);
|
||||
return devices.Any(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType);
|
||||
}
|
||||
|
||||
public void Arrange(ArtemisSurface surface)
|
||||
public void Arrange(List<ArtemisDevice> devices)
|
||||
{
|
||||
List<ArtemisDevice> devices = surface.Devices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType).ToList();
|
||||
devices = devices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType).ToList();
|
||||
if (!devices.Any())
|
||||
return;
|
||||
|
||||
AppliedConfiguration = null;
|
||||
foreach (SurfaceArrangementConfiguration configuration in Configurations)
|
||||
{
|
||||
bool applied = configuration.Apply(devices, surface);
|
||||
bool applied = configuration.Apply(devices);
|
||||
if (applied)
|
||||
{
|
||||
AppliedConfiguration = configuration;
|
||||
@@ -52,11 +52,11 @@ namespace Artemis.Core.Services.Models
|
||||
VerticalArrangementPosition.Equal,
|
||||
10
|
||||
) {SurfaceArrangement = SurfaceArrangement};
|
||||
fallback.Apply(devices, surface);
|
||||
fallback.Apply(devices);
|
||||
AppliedConfiguration = fallback;
|
||||
}
|
||||
|
||||
public Point GetEdge(HorizontalArrangementPosition horizontalPosition, VerticalArrangementPosition verticalPosition, ArtemisSurface surface)
|
||||
public Point GetEdge(HorizontalArrangementPosition horizontalPosition, VerticalArrangementPosition verticalPosition)
|
||||
{
|
||||
List<ArtemisDevice> devices = SurfaceArrangement.ArrangedDevices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType || DeviceType == RGBDeviceType.All).ToList();
|
||||
if (!devices.Any())
|
||||
|
||||
@@ -14,23 +14,22 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IProfileRepository _profileRepository;
|
||||
private readonly ISurfaceService _surfaceService;
|
||||
|
||||
public ProfileService(ILogger logger,
|
||||
IPluginManagementService pluginManagementService,
|
||||
ISurfaceService surfaceService,
|
||||
IRgbService rgbService,
|
||||
IConditionOperatorService conditionOperatorService,
|
||||
IDataBindingService dataBindingService,
|
||||
IProfileRepository profileRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_surfaceService = surfaceService;
|
||||
_rgbService = rgbService;
|
||||
_profileRepository = profileRepository;
|
||||
|
||||
_surfaceService.ActiveSurfaceConfigurationSelected += OnActiveSurfaceConfigurationSelected;
|
||||
_surfaceService.SurfaceConfigurationUpdated += OnSurfaceConfigurationUpdated;
|
||||
_rgbService.LedsChanged += RgbServiceOnLedsChanged;
|
||||
}
|
||||
|
||||
public JsonSerializerSettings MementoSettings { get; set; } = new() {TypeNameHandling = TypeNameHandling.All};
|
||||
@@ -62,12 +61,11 @@ namespace Artemis.Core.Services
|
||||
/// <summary>
|
||||
/// Populates all missing LEDs on all currently active profiles
|
||||
/// </summary>
|
||||
/// <param name="surface"></param>
|
||||
private void ActiveProfilesPopulateLeds(ArtemisSurface surface)
|
||||
private void ActiveProfilesPopulateLeds()
|
||||
{
|
||||
List<ProfileModule> profileModules = _pluginManagementService.GetFeaturesOfType<ProfileModule>();
|
||||
foreach (ProfileModule profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList())
|
||||
profileModule.ActiveProfile?.PopulateLeds(surface); // Avoid race condition
|
||||
profileModule.ActiveProfile?.PopulateLeds(_rgbService.EnabledDevices); // Avoid race condition
|
||||
}
|
||||
|
||||
public List<ProfileDescriptor> GetProfileDescriptors(ProfileModule module)
|
||||
@@ -110,7 +108,7 @@ namespace Artemis.Core.Services
|
||||
Profile profile = new(profileDescriptor.ProfileModule, profileEntity);
|
||||
InstantiateProfile(profile);
|
||||
|
||||
profileDescriptor.ProfileModule.ChangeActiveProfile(profile, _surfaceService.ActiveSurface);
|
||||
profileDescriptor.ProfileModule.ChangeActiveProfile(profile, _rgbService.EnabledDevices);
|
||||
SaveActiveProfile(profileDescriptor.ProfileModule);
|
||||
|
||||
return profile;
|
||||
@@ -124,9 +122,9 @@ namespace Artemis.Core.Services
|
||||
ProfileEntity entity = _profileRepository.Get(module.ActiveProfile.EntityId);
|
||||
Profile profile = new(module, entity);
|
||||
InstantiateProfile(profile);
|
||||
|
||||
module.ChangeActiveProfile(null, _surfaceService.ActiveSurface);
|
||||
module.ChangeActiveProfile(profile, _surfaceService.ActiveSurface);
|
||||
|
||||
module.ChangeActiveProfile(null, _rgbService.EnabledDevices);
|
||||
module.ChangeActiveProfile(profile, _rgbService.EnabledDevices);
|
||||
}
|
||||
|
||||
public async Task<Profile> ActivateProfileAnimated(ProfileDescriptor profileDescriptor)
|
||||
@@ -141,9 +139,9 @@ namespace Artemis.Core.Services
|
||||
Profile profile = new(profileDescriptor.ProfileModule, profileEntity);
|
||||
InstantiateProfile(profile);
|
||||
|
||||
void ActivatingProfileSurfaceUpdate(object? sender, SurfaceConfigurationEventArgs e)
|
||||
void ActivatingRgbServiceOnLedsChanged(object? sender, EventArgs e)
|
||||
{
|
||||
profile.PopulateLeds(e.Surface);
|
||||
profile.PopulateLeds(_rgbService.EnabledDevices);
|
||||
}
|
||||
|
||||
void ActivatingProfilePluginToggle(object? sender, PluginEventArgs e)
|
||||
@@ -154,28 +152,29 @@ namespace Artemis.Core.Services
|
||||
// This could happen during activation so subscribe to it
|
||||
_pluginManagementService.PluginEnabled += ActivatingProfilePluginToggle;
|
||||
_pluginManagementService.PluginDisabled += ActivatingProfilePluginToggle;
|
||||
_surfaceService.SurfaceConfigurationUpdated += ActivatingProfileSurfaceUpdate;
|
||||
_rgbService.LedsChanged += ActivatingRgbServiceOnLedsChanged;
|
||||
|
||||
await profileDescriptor.ProfileModule.ChangeActiveProfileAnimated(profile, _surfaceService.ActiveSurface);
|
||||
await profileDescriptor.ProfileModule.ChangeActiveProfileAnimated(profile, _rgbService.EnabledDevices);
|
||||
SaveActiveProfile(profileDescriptor.ProfileModule);
|
||||
|
||||
_pluginManagementService.PluginEnabled -= ActivatingProfilePluginToggle;
|
||||
_pluginManagementService.PluginDisabled -= ActivatingProfilePluginToggle;
|
||||
_surfaceService.SurfaceConfigurationUpdated -= ActivatingProfileSurfaceUpdate;
|
||||
_rgbService.LedsChanged -= ActivatingRgbServiceOnLedsChanged;
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void ClearActiveProfile(ProfileModule module)
|
||||
{
|
||||
module.ChangeActiveProfile(null, _surfaceService.ActiveSurface);
|
||||
module.ChangeActiveProfile(null, _rgbService.EnabledDevices);
|
||||
SaveActiveProfile(module);
|
||||
}
|
||||
|
||||
public async Task ClearActiveProfileAnimated(ProfileModule module)
|
||||
{
|
||||
await module.ChangeActiveProfileAnimated(null, _surfaceService.ActiveSurface);
|
||||
await module.ChangeActiveProfileAnimated(null, _rgbService.EnabledDevices);
|
||||
}
|
||||
|
||||
public void DeleteProfile(Profile profile)
|
||||
@@ -255,7 +254,7 @@ namespace Artemis.Core.Services
|
||||
string top = profile.RedoStack.Pop();
|
||||
string memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
|
||||
profile.UndoStack.Push(memento);
|
||||
profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top, MementoSettings)
|
||||
profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top, MementoSettings)
|
||||
?? throw new InvalidOperationException("Failed to deserialize memento");
|
||||
|
||||
profile.Load();
|
||||
@@ -268,7 +267,7 @@ namespace Artemis.Core.Services
|
||||
|
||||
public void InstantiateProfile(Profile profile)
|
||||
{
|
||||
profile.PopulateLeds(_surfaceService.ActiveSurface);
|
||||
profile.PopulateLeds(_rgbService.EnabledDevices);
|
||||
}
|
||||
|
||||
public string ExportProfile(ProfileDescriptor profileDescriptor)
|
||||
@@ -296,17 +295,11 @@ namespace Artemis.Core.Services
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void OnActiveSurfaceConfigurationSelected(object? sender, SurfaceConfigurationEventArgs e)
|
||||
private void RgbServiceOnLedsChanged(object? sender, EventArgs e)
|
||||
{
|
||||
ActiveProfilesPopulateLeds(e.Surface);
|
||||
ActiveProfilesPopulateLeds();
|
||||
}
|
||||
|
||||
private void OnSurfaceConfigurationUpdated(object? sender, SurfaceConfigurationEventArgs e)
|
||||
{
|
||||
if (e.Surface.IsActive)
|
||||
ActiveProfilesPopulateLeds(e.Surface);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core.DeviceProviders;
|
||||
using Artemis.Core.Services.Models;
|
||||
using Artemis.Storage.Entities.Surface;
|
||||
using Artemis.Storage.Repositories.Interfaces;
|
||||
using RGB.NET.Core;
|
||||
using Serilog;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
internal class SurfaceService : ISurfaceService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly List<ArtemisSurface> _surfaceConfigurations;
|
||||
private readonly ISurfaceRepository _surfaceRepository;
|
||||
|
||||
public SurfaceService(ILogger logger, ISurfaceRepository surfaceRepository, IRgbService rgbService, IPluginManagementService pluginManagementService)
|
||||
{
|
||||
_logger = logger;
|
||||
_surfaceRepository = surfaceRepository;
|
||||
_rgbService = rgbService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_surfaceConfigurations = new List<ArtemisSurface>();
|
||||
|
||||
// LoadFromRepository is guaranteed to set the ActiveSurface
|
||||
ActiveSurface = null!;
|
||||
LoadFromRepository();
|
||||
|
||||
_rgbService.DeviceLoaded += RgbServiceOnDeviceLoaded;
|
||||
_rgbService.DeviceRemoved += RgbServiceOnDeviceRemoved;
|
||||
}
|
||||
|
||||
#region Repository
|
||||
|
||||
private void LoadFromRepository()
|
||||
{
|
||||
List<SurfaceEntity> configs = _surfaceRepository.GetAll();
|
||||
foreach (SurfaceEntity surfaceEntity in configs)
|
||||
{
|
||||
// Create the surface entity
|
||||
ArtemisSurface surfaceConfiguration = new(_rgbService.Surface, surfaceEntity);
|
||||
foreach (DeviceEntity position in surfaceEntity.DeviceEntities)
|
||||
{
|
||||
IRGBDevice? device = _rgbService.Surface.Devices.FirstOrDefault(d => d.GetDeviceIdentifier() == position.DeviceIdentifier);
|
||||
if (device != null)
|
||||
{
|
||||
DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(device);
|
||||
surfaceConfiguration.Devices.Add(new ArtemisDevice(device, deviceProvider, surfaceConfiguration, position));
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, add the surface config to the collection
|
||||
lock (_surfaceConfigurations)
|
||||
{
|
||||
_surfaceConfigurations.Add(surfaceConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
// When all surface configs are loaded, apply the active surface config
|
||||
ArtemisSurface? active = SurfaceConfigurations.FirstOrDefault(c => c.IsActive);
|
||||
if (active != null)
|
||||
{
|
||||
SetActiveSurfaceConfiguration(active);
|
||||
}
|
||||
else
|
||||
{
|
||||
active = SurfaceConfigurations.FirstOrDefault();
|
||||
if (active != null)
|
||||
SetActiveSurfaceConfiguration(active);
|
||||
else
|
||||
SetActiveSurfaceConfiguration(CreateSurfaceConfiguration("Default"));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private void AddDeviceIfMissing(IRGBDevice rgbDevice, ArtemisSurface surface)
|
||||
{
|
||||
string deviceIdentifier = rgbDevice.GetDeviceIdentifier();
|
||||
ArtemisDevice? device = surface.Devices.FirstOrDefault(d => d.DeviceEntity.DeviceIdentifier == deviceIdentifier);
|
||||
|
||||
if (device != null)
|
||||
return;
|
||||
|
||||
// Find an existing device config and use that
|
||||
DeviceEntity? existingDeviceConfig = surface.SurfaceEntity.DeviceEntities.FirstOrDefault(d => d.DeviceIdentifier == deviceIdentifier);
|
||||
if (existingDeviceConfig != null)
|
||||
{
|
||||
DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
|
||||
device = new ArtemisDevice(rgbDevice, deviceProvider, surface, existingDeviceConfig);
|
||||
}
|
||||
// Fall back on creating a new device
|
||||
else
|
||||
{
|
||||
_logger.Information(
|
||||
"No device config found for {deviceInfo}, device hash: {deviceHashCode}. Adding a new entry.",
|
||||
rgbDevice.DeviceInfo,
|
||||
deviceIdentifier
|
||||
);
|
||||
DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
|
||||
device = new ArtemisDevice(rgbDevice, deviceProvider, surface);
|
||||
}
|
||||
|
||||
surface.Devices.Add(device);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public ArtemisSurface ActiveSurface { get; private set; }
|
||||
public ReadOnlyCollection<ArtemisSurface> SurfaceConfigurations => _surfaceConfigurations.AsReadOnly();
|
||||
|
||||
public ArtemisSurface CreateSurfaceConfiguration(string name)
|
||||
{
|
||||
// Create a blank config
|
||||
ArtemisSurface configuration = new(_rgbService.Surface, name);
|
||||
|
||||
// Add all current devices
|
||||
foreach (IRGBDevice rgbDevice in _rgbService.LoadedDevices)
|
||||
{
|
||||
DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
|
||||
configuration.Devices.Add(new ArtemisDevice(rgbDevice, deviceProvider, configuration));
|
||||
}
|
||||
|
||||
// Auto-arrange the new config
|
||||
AutoArrange(configuration);
|
||||
|
||||
lock (_surfaceConfigurations)
|
||||
{
|
||||
_surfaceRepository.Add(configuration.SurfaceEntity);
|
||||
_surfaceConfigurations.Add(configuration);
|
||||
|
||||
UpdateSurfaceConfiguration(configuration, true);
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetActiveSurfaceConfiguration(ArtemisSurface surface)
|
||||
{
|
||||
if (surface == null) throw new ArgumentNullException(nameof(surface));
|
||||
if (ActiveSurface == surface)
|
||||
return;
|
||||
|
||||
// Set the new entity
|
||||
ActiveSurface = surface;
|
||||
|
||||
// Ensure only the new entity is marked as active
|
||||
lock (_surfaceConfigurations)
|
||||
{
|
||||
// Mark only the new surface as active
|
||||
foreach (ArtemisSurface configuration in _surfaceConfigurations)
|
||||
{
|
||||
configuration.IsActive = configuration == ActiveSurface;
|
||||
configuration.ApplyToEntity();
|
||||
|
||||
_surfaceRepository.Save(configuration.SurfaceEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the active surface entity to the devices
|
||||
foreach (ArtemisDevice device in ActiveSurface.Devices)
|
||||
device.ApplyToRgbDevice();
|
||||
|
||||
// Update the RGB service's graphics decorator to work with the new surface entity
|
||||
_rgbService.UpdateSurfaceLedGroup(ActiveSurface);
|
||||
OnActiveSurfaceConfigurationChanged(new SurfaceConfigurationEventArgs(ActiveSurface));
|
||||
}
|
||||
|
||||
public void UpdateSurfaceConfiguration(ArtemisSurface surface, bool includeDevices)
|
||||
{
|
||||
surface.ApplyToEntity();
|
||||
if (includeDevices)
|
||||
foreach (ArtemisDevice deviceConfiguration in surface.Devices)
|
||||
{
|
||||
deviceConfiguration.ApplyToEntity();
|
||||
if (surface.IsActive)
|
||||
deviceConfiguration.ApplyToRgbDevice();
|
||||
}
|
||||
|
||||
surface.UpdateLedMap();
|
||||
|
||||
_surfaceRepository.Save(surface.SurfaceEntity);
|
||||
_rgbService.UpdateSurfaceLedGroup(ActiveSurface);
|
||||
OnSurfaceConfigurationUpdated(new SurfaceConfigurationEventArgs(surface));
|
||||
}
|
||||
|
||||
public void DeleteSurfaceConfiguration(ArtemisSurface surface)
|
||||
{
|
||||
if (surface == ActiveSurface)
|
||||
throw new ArtemisCoreException($"Cannot delete surface entity '{surface.Name}' because it is active.");
|
||||
|
||||
lock (_surfaceConfigurations)
|
||||
{
|
||||
SurfaceEntity entity = surface.SurfaceEntity;
|
||||
_surfaceConfigurations.Remove(surface);
|
||||
_surfaceRepository.Remove(entity);
|
||||
}
|
||||
}
|
||||
|
||||
#region AutoLayout
|
||||
|
||||
public void AutoArrange(ArtemisSurface? artemisSurface = null)
|
||||
{
|
||||
artemisSurface ??= ActiveSurface;
|
||||
if (!artemisSurface.Devices.Any())
|
||||
return;
|
||||
|
||||
SurfaceArrangement surfaceArrangement = SurfaceArrangement.GetDefaultArrangement();
|
||||
surfaceArrangement.Arrange(artemisSurface);
|
||||
UpdateSurfaceConfiguration(artemisSurface, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void RgbServiceOnDeviceLoaded(object? sender, DeviceEventArgs e)
|
||||
{
|
||||
lock (_surfaceConfigurations)
|
||||
{
|
||||
foreach (ArtemisSurface surfaceConfiguration in _surfaceConfigurations)
|
||||
AddDeviceIfMissing(e.Device, surfaceConfiguration);
|
||||
}
|
||||
|
||||
UpdateSurfaceConfiguration(ActiveSurface, true);
|
||||
}
|
||||
|
||||
private void RgbServiceOnDeviceRemoved(object? sender, DeviceEventArgs e)
|
||||
{
|
||||
lock (_surfaceConfigurations)
|
||||
{
|
||||
foreach (ArtemisSurface surfaceConfiguration in _surfaceConfigurations)
|
||||
surfaceConfiguration.Devices.RemoveAll(d => d.RgbDevice == e.Device);
|
||||
}
|
||||
|
||||
UpdateSurfaceConfiguration(ActiveSurface, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler<SurfaceConfigurationEventArgs>? ActiveSurfaceConfigurationSelected;
|
||||
public event EventHandler<SurfaceConfigurationEventArgs>? SurfaceConfigurationUpdated;
|
||||
|
||||
protected virtual void OnActiveSurfaceConfigurationChanged(SurfaceConfigurationEventArgs e)
|
||||
{
|
||||
ActiveSurfaceConfigurationSelected?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnSurfaceConfigurationUpdated(SurfaceConfigurationEventArgs e)
|
||||
{
|
||||
SurfaceConfigurationUpdated?.Invoke(this, e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Modules;
|
||||
@@ -13,13 +14,13 @@ namespace Artemis.Core
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly ISurfaceService _surfaceService;
|
||||
private readonly IEnumerable<ArtemisDevice> _devices;
|
||||
|
||||
public IntroAnimation(ILogger logger, IProfileService profileService, ISurfaceService surfaceService)
|
||||
public IntroAnimation(ILogger logger, IProfileService profileService, IEnumerable<ArtemisDevice> devices)
|
||||
{
|
||||
_logger = logger;
|
||||
_profileService = profileService;
|
||||
_surfaceService = surfaceService;
|
||||
_devices = devices;
|
||||
|
||||
AnimationProfile = CreateIntroProfile();
|
||||
}
|
||||
@@ -41,14 +42,14 @@ namespace Artemis.Core
|
||||
ProfileEntity profileEntity = CoreJson.DeserializeObject<ProfileEntity>(json)!;
|
||||
// Inject every LED on the surface into each layer
|
||||
foreach (LayerEntity profileEntityLayer in profileEntity.Layers)
|
||||
profileEntityLayer.Leds.AddRange(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds).Select(l => new LedEntity
|
||||
profileEntityLayer.Leds.AddRange(_devices.SelectMany(d => d.Leds).Select(l => new LedEntity
|
||||
{
|
||||
DeviceIdentifier = l.Device.RgbDevice.GetDeviceIdentifier(),
|
||||
LedName = l.RgbLed.Id.ToString()
|
||||
}));
|
||||
|
||||
Profile profile = new(new DummyModule(), profileEntity);
|
||||
profile.Activate(_surfaceService.ActiveSurface);
|
||||
profile.Activate(_devices);
|
||||
|
||||
_profileService.InstantiateProfile(profile);
|
||||
return profile;
|
||||
@@ -79,7 +80,7 @@ namespace Artemis.Core
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Render(double deltaTime, ArtemisSurface surface, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user