mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 21:38:38 +00:00
Merge branch 'feature/device-refactor' into development
This commit is contained in:
commit
e866afbfb0
27
src/Artemis.Core/Events/Plugins/DeviceProviderEventArgs.cs
Normal file
27
src/Artemis.Core/Events/Plugins/DeviceProviderEventArgs.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Core.DeviceProviders;
|
||||||
|
|
||||||
|
namespace Artemis.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides data about device provider related events
|
||||||
|
/// </summary>
|
||||||
|
public class DeviceProviderEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
internal DeviceProviderEventArgs(DeviceProvider deviceProvider, List<ArtemisDevice> devices)
|
||||||
|
{
|
||||||
|
DeviceProvider = deviceProvider;
|
||||||
|
Devices = devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the device provider the event is related to.
|
||||||
|
/// </summary>
|
||||||
|
public DeviceProvider DeviceProvider { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of the affected devices.
|
||||||
|
/// </summary>
|
||||||
|
public List<ArtemisDevice> Devices { get; set; }
|
||||||
|
}
|
||||||
@ -15,11 +15,17 @@ namespace Artemis.Core;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ArtemisDevice : CorePropertyChanged
|
public class ArtemisDevice : CorePropertyChanged
|
||||||
{
|
{
|
||||||
|
private readonly List<OriginalLed> _originalLeds;
|
||||||
|
private readonly Size _originalSize;
|
||||||
private SKPath? _path;
|
private SKPath? _path;
|
||||||
private SKRect _rectangle;
|
private SKRect _rectangle;
|
||||||
|
|
||||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider)
|
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider)
|
||||||
{
|
{
|
||||||
|
_originalLeds = new List<OriginalLed>(rgbDevice.Select(l => new OriginalLed(l)));
|
||||||
|
Rectangle ledRectangle = new(rgbDevice.Select(x => x.Boundary));
|
||||||
|
_originalSize = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
|
||||||
|
|
||||||
Identifier = rgbDevice.GetDeviceIdentifier();
|
Identifier = rgbDevice.GetDeviceIdentifier();
|
||||||
DeviceEntity = new DeviceEntity();
|
DeviceEntity = new DeviceEntity();
|
||||||
RgbDevice = rgbDevice;
|
RgbDevice = rgbDevice;
|
||||||
@ -48,6 +54,10 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
|
|
||||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity)
|
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity)
|
||||||
{
|
{
|
||||||
|
_originalLeds = new List<OriginalLed>(rgbDevice.Select(l => new OriginalLed(l)));
|
||||||
|
Rectangle ledRectangle = new(rgbDevice.Select(x => x.Boundary));
|
||||||
|
_originalSize = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
|
||||||
|
|
||||||
Identifier = rgbDevice.GetDeviceIdentifier();
|
Identifier = rgbDevice.GetDeviceIdentifier();
|
||||||
DeviceEntity = deviceEntity;
|
DeviceEntity = deviceEntity;
|
||||||
RgbDevice = rgbDevice;
|
RgbDevice = rgbDevice;
|
||||||
@ -350,6 +360,40 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
return artemisLed;
|
return artemisLed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the most preferred device layout for this device.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The most preferred device layout for this device.</returns>
|
||||||
|
public ArtemisLayout? GetBestDeviceLayout()
|
||||||
|
{
|
||||||
|
ArtemisLayout? layout;
|
||||||
|
|
||||||
|
// Configured layout path takes precedence over all other options
|
||||||
|
if (CustomLayoutPath != null)
|
||||||
|
{
|
||||||
|
layout = new ArtemisLayout(CustomLayoutPath, LayoutSource.Configured);
|
||||||
|
if (layout.IsValid)
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for a layout provided by the user
|
||||||
|
layout = DeviceProvider.LoadUserLayout(this);
|
||||||
|
if (layout.IsValid)
|
||||||
|
return layout;
|
||||||
|
|
||||||
|
if (DisableDefaultLayout)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Look for a layout provided by the plugin
|
||||||
|
layout = DeviceProvider.LoadLayout(this);
|
||||||
|
if (layout.IsValid)
|
||||||
|
return layout;
|
||||||
|
|
||||||
|
// Finally fall back to a default layout
|
||||||
|
layout = ArtemisLayout.GetDefaultLayout(this);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when the underlying RGB.NET device was updated
|
/// Occurs when the underlying RGB.NET device was updated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -415,8 +459,18 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
/// A boolean indicating whether to remove excess LEDs present in the device but missing
|
/// A boolean indicating whether to remove excess LEDs present in the device but missing
|
||||||
/// in the layout
|
/// in the layout
|
||||||
/// </param>
|
/// </param>
|
||||||
internal void ApplyLayout(ArtemisLayout layout, bool createMissingLeds, bool removeExcessiveLeds)
|
internal void ApplyLayout(ArtemisLayout? layout, bool createMissingLeds, bool removeExcessiveLeds)
|
||||||
{
|
{
|
||||||
|
if (layout == null)
|
||||||
|
{
|
||||||
|
ClearLayout();
|
||||||
|
UpdateLeds();
|
||||||
|
|
||||||
|
CalculateRenderProperties();
|
||||||
|
OnDeviceUpdated();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (createMissingLeds && !DeviceProvider.CreateMissingLedsSupported)
|
if (createMissingLeds && !DeviceProvider.CreateMissingLedsSupported)
|
||||||
throw new ArtemisCoreException($"Cannot apply layout with {nameof(createMissingLeds)} " +
|
throw new ArtemisCoreException($"Cannot apply layout with {nameof(createMissingLeds)} " +
|
||||||
"set to true because the device provider does not support it");
|
"set to true because the device provider does not support it");
|
||||||
@ -424,18 +478,33 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
throw new ArtemisCoreException($"Cannot apply layout with {nameof(removeExcessiveLeds)} " +
|
throw new ArtemisCoreException($"Cannot apply layout with {nameof(removeExcessiveLeds)} " +
|
||||||
"set to true because the device provider does not support it");
|
"set to true because the device provider does not support it");
|
||||||
|
|
||||||
|
ClearLayout();
|
||||||
if (layout.IsValid)
|
if (layout.IsValid)
|
||||||
layout.ApplyTo(RgbDevice, createMissingLeds, removeExcessiveLeds);
|
layout.ApplyTo(RgbDevice, createMissingLeds, removeExcessiveLeds);
|
||||||
|
|
||||||
|
|
||||||
UpdateLeds();
|
UpdateLeds();
|
||||||
|
|
||||||
Layout = layout;
|
Layout = layout;
|
||||||
Layout.ApplyDevice(this);
|
Layout.ApplyDevice(this);
|
||||||
|
|
||||||
CalculateRenderProperties();
|
CalculateRenderProperties();
|
||||||
OnDeviceUpdated();
|
OnDeviceUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ClearLayout()
|
||||||
|
{
|
||||||
|
if (Layout == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RgbDevice.DeviceInfo.LayoutMetadata = null;
|
||||||
|
RgbDevice.Size = _originalSize;
|
||||||
|
Layout = null;
|
||||||
|
|
||||||
|
while (RgbDevice.Any())
|
||||||
|
RgbDevice.RemoveLed(RgbDevice.First().Id);
|
||||||
|
foreach (OriginalLed originalLed in _originalLeds)
|
||||||
|
RgbDevice.AddLed(originalLed.Id, originalLed.Location, originalLed.Size, originalLed.CustomData);
|
||||||
|
}
|
||||||
|
|
||||||
internal void ApplyToEntity()
|
internal void ApplyToEntity()
|
||||||
{
|
{
|
||||||
// Other properties are computed
|
// Other properties are computed
|
||||||
|
|||||||
@ -59,13 +59,13 @@ public class ArtemisLed : CorePropertyChanged
|
|||||||
|
|
||||||
internal void CalculateRectangles()
|
internal void CalculateRectangles()
|
||||||
{
|
{
|
||||||
Rectangle = Utilities.CreateScaleCompatibleRect(
|
Rectangle = RenderScale.CreateScaleCompatibleRect(
|
||||||
RgbLed.Boundary.Location.X,
|
RgbLed.Boundary.Location.X,
|
||||||
RgbLed.Boundary.Location.Y,
|
RgbLed.Boundary.Location.Y,
|
||||||
RgbLed.Boundary.Size.Width,
|
RgbLed.Boundary.Size.Width,
|
||||||
RgbLed.Boundary.Size.Height
|
RgbLed.Boundary.Size.Height
|
||||||
);
|
);
|
||||||
AbsoluteRectangle = Utilities.CreateScaleCompatibleRect(
|
AbsoluteRectangle = RenderScale.CreateScaleCompatibleRect(
|
||||||
RgbLed.AbsoluteBoundary.Location.X,
|
RgbLed.AbsoluteBoundary.Location.X,
|
||||||
RgbLed.AbsoluteBoundary.Location.Y,
|
RgbLed.AbsoluteBoundary.Location.Y,
|
||||||
RgbLed.AbsoluteBoundary.Size.Width,
|
RgbLed.AbsoluteBoundary.Size.Width,
|
||||||
|
|||||||
19
src/Artemis.Core/Models/Surface/OriginalLed.cs
Normal file
19
src/Artemis.Core/Models/Surface/OriginalLed.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using RGB.NET.Core;
|
||||||
|
|
||||||
|
namespace Artemis.Core;
|
||||||
|
|
||||||
|
internal class OriginalLed
|
||||||
|
{
|
||||||
|
public OriginalLed(Led source)
|
||||||
|
{
|
||||||
|
Id = source.Id;
|
||||||
|
Location = source.Location;
|
||||||
|
Size = source.Size;
|
||||||
|
CustomData = source.CustomData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LedId Id { get; set; }
|
||||||
|
public Point Location { get; set; }
|
||||||
|
public Size Size { get; set; }
|
||||||
|
public object? CustomData { get; set; }
|
||||||
|
}
|
||||||
23
src/Artemis.Core/Services/Core/IRenderer.cs
Normal file
23
src/Artemis.Core/Services/Core/IRenderer.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a renderer that renders to a canvas.
|
||||||
|
/// </summary>
|
||||||
|
public interface IRenderer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Renders to the provided canvas, the delta is the time in seconds since the last time <see cref="Render" /> was
|
||||||
|
/// called.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="canvas">The canvas to render to.</param>
|
||||||
|
/// <param name="delta">The time in seconds since the last time <see cref="Render" /> was called.</param>
|
||||||
|
void Render(SKCanvas canvas, double delta);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after the rendering has taken place.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The texture that the render resulted in.</param>
|
||||||
|
void PostRender(SKTexture texture);
|
||||||
|
}
|
||||||
222
src/Artemis.Core/Services/Core/SurfaceManager.cs
Normal file
222
src/Artemis.Core/Services/Core/SurfaceManager.cs
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Artemis.Core.SkiaSharp;
|
||||||
|
using RGB.NET.Core;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An engine drivers an update loop for a set of devices using a graphics context
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class SurfaceManager : IDisposable
|
||||||
|
{
|
||||||
|
private readonly IRenderer _renderer;
|
||||||
|
private readonly TimerUpdateTrigger _updateTrigger;
|
||||||
|
private readonly object _renderLock = new();
|
||||||
|
private readonly List<ArtemisDevice> _devices = new();
|
||||||
|
private readonly SKTextureBrush _textureBrush = new(null) {CalculationMode = RenderMode.Absolute};
|
||||||
|
|
||||||
|
private ListLedGroup? _surfaceLedGroup;
|
||||||
|
private SKTexture? _texture;
|
||||||
|
|
||||||
|
public SurfaceManager(IRenderer renderer, IManagedGraphicsContext? graphicsContext, int targetFrameRate, float renderScale)
|
||||||
|
{
|
||||||
|
_renderer = renderer;
|
||||||
|
_updateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / targetFrameRate};
|
||||||
|
|
||||||
|
GraphicsContext = graphicsContext;
|
||||||
|
TargetFrameRate = targetFrameRate;
|
||||||
|
RenderScale = renderScale;
|
||||||
|
Surface = new RGBSurface();
|
||||||
|
Surface.Updating += SurfaceOnUpdating;
|
||||||
|
Surface.RegisterUpdateTrigger(_updateTrigger);
|
||||||
|
|
||||||
|
SetPaused(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SurfaceOnUpdating(UpdatingEventArgs args)
|
||||||
|
{
|
||||||
|
lock (_renderLock)
|
||||||
|
{
|
||||||
|
SKTexture? texture = _texture;
|
||||||
|
if (texture == null || texture.IsInvalid)
|
||||||
|
texture = CreateTexture();
|
||||||
|
|
||||||
|
// Prepare a canvas
|
||||||
|
SKCanvas canvas = texture.Surface.Canvas;
|
||||||
|
canvas.Save();
|
||||||
|
|
||||||
|
// Apply scaling if necessary
|
||||||
|
if (Math.Abs(texture.RenderScale - 1) > 0.001)
|
||||||
|
canvas.Scale(texture.RenderScale);
|
||||||
|
|
||||||
|
// Fresh start!
|
||||||
|
canvas.Clear(new SKColor(0, 0, 0));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_renderer.Render(canvas, args.DeltaTime);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
canvas.RestoreToCount(-1);
|
||||||
|
canvas.Flush();
|
||||||
|
texture.CopyPixelData();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_renderer.PostRender(texture);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IManagedGraphicsContext? GraphicsContext { get; private set; }
|
||||||
|
public int TargetFrameRate { get; private set; }
|
||||||
|
public float RenderScale { get; private set; }
|
||||||
|
public RGBSurface Surface { get; }
|
||||||
|
|
||||||
|
public bool IsPaused { get; private set; }
|
||||||
|
|
||||||
|
public void AddDevices(IEnumerable<ArtemisDevice> devices)
|
||||||
|
{
|
||||||
|
lock (_renderLock)
|
||||||
|
{
|
||||||
|
foreach (ArtemisDevice artemisDevice in devices)
|
||||||
|
{
|
||||||
|
if (_devices.Contains(artemisDevice))
|
||||||
|
continue;
|
||||||
|
_devices.Add(artemisDevice);
|
||||||
|
Surface.Attach(artemisDevice.RgbDevice);
|
||||||
|
artemisDevice.DeviceUpdated += ArtemisDeviceOnDeviceUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveDevices(IEnumerable<ArtemisDevice> devices)
|
||||||
|
{
|
||||||
|
lock (_renderLock)
|
||||||
|
{
|
||||||
|
foreach (ArtemisDevice artemisDevice in devices)
|
||||||
|
{
|
||||||
|
artemisDevice.DeviceUpdated -= ArtemisDeviceOnDeviceUpdated;
|
||||||
|
Surface.Detach(artemisDevice.RgbDevice);
|
||||||
|
_devices.Remove(artemisDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetPaused(bool paused)
|
||||||
|
{
|
||||||
|
if (IsPaused == paused)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (paused)
|
||||||
|
_updateTrigger.Stop();
|
||||||
|
else
|
||||||
|
_updateTrigger.Start();
|
||||||
|
|
||||||
|
IsPaused = paused;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
lock (_renderLock)
|
||||||
|
{
|
||||||
|
UpdateLedGroup();
|
||||||
|
CreateTexture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLedGroup()
|
||||||
|
{
|
||||||
|
List<Led> leds = _devices.SelectMany(d => d.Leds).Select(l => l.RgbLed).ToList();
|
||||||
|
|
||||||
|
if (_surfaceLedGroup == null)
|
||||||
|
{
|
||||||
|
_surfaceLedGroup = new ListLedGroup(Surface, leds) {Brush = _textureBrush};
|
||||||
|
LedsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the old background
|
||||||
|
_surfaceLedGroup.Detach();
|
||||||
|
|
||||||
|
// Apply the application wide brush and decorator
|
||||||
|
_surfaceLedGroup = new ListLedGroup(Surface, leds) {Brush = _textureBrush};
|
||||||
|
LedsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SKTexture CreateTexture()
|
||||||
|
{
|
||||||
|
float evenWidth = Surface.Boundary.Size.Width;
|
||||||
|
if (evenWidth % 2 != 0)
|
||||||
|
evenWidth++;
|
||||||
|
float evenHeight = Surface.Boundary.Size.Height;
|
||||||
|
if (evenHeight % 2 != 0)
|
||||||
|
evenHeight++;
|
||||||
|
|
||||||
|
int width = Math.Max(1, MathF.Min(evenWidth * RenderScale, 4096).RoundToInt());
|
||||||
|
int height = Math.Max(1, MathF.Min(evenHeight * RenderScale, 4096).RoundToInt());
|
||||||
|
|
||||||
|
_texture?.Dispose();
|
||||||
|
_texture = new SKTexture(GraphicsContext, width, height, RenderScale, _devices);
|
||||||
|
_textureBrush.Texture = _texture;
|
||||||
|
|
||||||
|
return _texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ArtemisDeviceOnDeviceUpdated(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler? LedsChanged;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
SetPaused(true);
|
||||||
|
|
||||||
|
Surface.UnregisterUpdateTrigger(_updateTrigger);
|
||||||
|
_updateTrigger.Dispose();
|
||||||
|
_texture?.Dispose();
|
||||||
|
Surface.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateTargetFrameRate(int targetFrameRate)
|
||||||
|
{
|
||||||
|
TargetFrameRate = targetFrameRate;
|
||||||
|
_updateTrigger.UpdateFrequency = 1.0 / TargetFrameRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateRenderScale(float renderScale)
|
||||||
|
{
|
||||||
|
lock (_renderLock)
|
||||||
|
{
|
||||||
|
RenderScale = renderScale;
|
||||||
|
_texture?.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateGraphicsContext(IManagedGraphicsContext? graphicsContext)
|
||||||
|
{
|
||||||
|
lock (_renderLock)
|
||||||
|
{
|
||||||
|
GraphicsContext = graphicsContext;
|
||||||
|
_texture?.Dispose();
|
||||||
|
_texture = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,19 +20,11 @@ namespace Artemis.Core.Services;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class CoreService : ICoreService
|
internal class CoreService : ICoreService
|
||||||
{
|
{
|
||||||
private readonly Stopwatch _frameStopWatch;
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly PluginSetting<LogEventLevel> _loggingLevel;
|
private readonly PluginSetting<LogEventLevel> _loggingLevel;
|
||||||
private readonly IModuleService _moduleService;
|
|
||||||
private readonly IPluginManagementService _pluginManagementService;
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
private readonly IProfileService _profileService;
|
private readonly IRenderService _renderService;
|
||||||
private readonly IRgbService _rgbService;
|
|
||||||
private readonly IScriptingService _scriptingService;
|
|
||||||
private readonly List<Exception> _updateExceptions = new();
|
|
||||||
|
|
||||||
private int _frames;
|
|
||||||
private DateTime _lastExceptionLog;
|
|
||||||
private DateTime _lastFrameRateSample;
|
|
||||||
|
|
||||||
// ReSharper disable UnusedParameter.Local
|
// ReSharper disable UnusedParameter.Local
|
||||||
public CoreService(IContainer container,
|
public CoreService(IContainer container,
|
||||||
@ -40,36 +32,49 @@ internal class CoreService : ICoreService
|
|||||||
StorageMigrationService _1, // injected to ensure migration runs early
|
StorageMigrationService _1, // injected to ensure migration runs early
|
||||||
ISettingsService settingsService,
|
ISettingsService settingsService,
|
||||||
IPluginManagementService pluginManagementService,
|
IPluginManagementService pluginManagementService,
|
||||||
IRgbService rgbService,
|
|
||||||
IProfileService profileService,
|
IProfileService profileService,
|
||||||
IModuleService moduleService,
|
IModuleService moduleService,
|
||||||
IScriptingService scriptingService)
|
IScriptingService scriptingService,
|
||||||
|
IRenderService renderService)
|
||||||
{
|
{
|
||||||
Constants.CorePlugin.Container = container;
|
Constants.CorePlugin.Container = container;
|
||||||
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_pluginManagementService = pluginManagementService;
|
_pluginManagementService = pluginManagementService;
|
||||||
_rgbService = rgbService;
|
_renderService = renderService;
|
||||||
_profileService = profileService;
|
|
||||||
_moduleService = moduleService;
|
|
||||||
_scriptingService = scriptingService;
|
|
||||||
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
|
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
|
||||||
_frameStopWatch = new Stopwatch();
|
|
||||||
|
|
||||||
_rgbService.Surface.Updating += SurfaceOnUpdating;
|
|
||||||
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
|
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsElevated { get; set; }
|
||||||
|
|
||||||
// ReSharper restore UnusedParameter.Local
|
public bool IsInitialized { get; set; }
|
||||||
|
|
||||||
protected virtual void OnFrameRendering(FrameRenderingEventArgs e)
|
public void Initialize()
|
||||||
{
|
{
|
||||||
FrameRendering?.Invoke(this, e);
|
if (IsInitialized)
|
||||||
}
|
throw new ArtemisCoreException("Cannot initialize the core as it is already initialized.");
|
||||||
|
|
||||||
protected virtual void OnFrameRendered(FrameRenderedEventArgs e)
|
_logger.Information("Initializing Artemis Core version {CurrentVersion}", Constants.CurrentVersion);
|
||||||
{
|
_logger.Information("Startup arguments: {StartupArguments}", Constants.StartupArguments);
|
||||||
FrameRendered?.Invoke(this, e);
|
_logger.Information("Elevated permissions: {IsElevated}", IsElevated);
|
||||||
|
_logger.Information("Stopwatch high resolution: {IsHighResolution}", Stopwatch.IsHighResolution);
|
||||||
|
|
||||||
|
ApplyLoggingLevel();
|
||||||
|
|
||||||
|
ProcessMonitor.Start();
|
||||||
|
|
||||||
|
// Don't remove even if it looks useless
|
||||||
|
// Just this line should prevent a certain someone from removing HidSharp as an unused dependency as well
|
||||||
|
Version? hidSharpVersion = Assembly.GetAssembly(typeof(HidDevice))!.GetName().Version;
|
||||||
|
_logger.Debug("Forcing plugins to use HidSharp {HidSharpVersion}", hidSharpVersion);
|
||||||
|
|
||||||
|
// Initialize the services
|
||||||
|
_pluginManagementService.CopyBuiltInPlugins();
|
||||||
|
_pluginManagementService.LoadPlugins(IsElevated);
|
||||||
|
_renderService.Initialize();
|
||||||
|
|
||||||
|
OnInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyLoggingLevel()
|
private void ApplyLoggingLevel()
|
||||||
@ -98,131 +103,11 @@ internal class CoreService : ICoreService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SurfaceOnUpdating(UpdatingEventArgs args)
|
public event EventHandler? Initialized;
|
||||||
{
|
|
||||||
if (_rgbService.IsRenderPaused)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_rgbService.FlushLeds)
|
|
||||||
{
|
|
||||||
_rgbService.FlushLeds = false;
|
|
||||||
_rgbService.Surface.Update(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_frameStopWatch.Restart();
|
|
||||||
|
|
||||||
foreach (GlobalScript script in _scriptingService.GlobalScripts)
|
|
||||||
script.OnCoreUpdating(args.DeltaTime);
|
|
||||||
|
|
||||||
_moduleService.UpdateActiveModules(args.DeltaTime);
|
|
||||||
SKTexture texture = _rgbService.OpenRender();
|
|
||||||
SKCanvas canvas = texture.Surface.Canvas;
|
|
||||||
canvas.Save();
|
|
||||||
if (Math.Abs(texture.RenderScale - 1) > 0.001)
|
|
||||||
canvas.Scale(texture.RenderScale);
|
|
||||||
canvas.Clear(new SKColor(0, 0, 0));
|
|
||||||
|
|
||||||
if (!ProfileRenderingDisabled)
|
|
||||||
{
|
|
||||||
_profileService.UpdateProfiles(args.DeltaTime);
|
|
||||||
_profileService.RenderProfiles(canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
OnFrameRendering(new FrameRenderingEventArgs(canvas, args.DeltaTime, _rgbService.Surface));
|
|
||||||
canvas.RestoreToCount(-1);
|
|
||||||
canvas.Flush();
|
|
||||||
|
|
||||||
OnFrameRendered(new FrameRenderedEventArgs(texture, _rgbService.Surface));
|
|
||||||
|
|
||||||
foreach (GlobalScript script in _scriptingService.GlobalScripts)
|
|
||||||
script.OnCoreUpdated(args.DeltaTime);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_updateExceptions.Add(e);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_rgbService.CloseRender();
|
|
||||||
_frameStopWatch.Stop();
|
|
||||||
_frames++;
|
|
||||||
|
|
||||||
if ((DateTime.Now - _lastFrameRateSample).TotalSeconds >= 1)
|
|
||||||
{
|
|
||||||
FrameRate = _frames;
|
|
||||||
_frames = 0;
|
|
||||||
_lastFrameRateSample = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
FrameTime = _frameStopWatch.Elapsed;
|
|
||||||
|
|
||||||
LogUpdateExceptions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LogUpdateExceptions()
|
|
||||||
{
|
|
||||||
// Only log update exceptions every 10 seconds to avoid spamming the logs
|
|
||||||
if (DateTime.Now - _lastExceptionLog < TimeSpan.FromSeconds(10))
|
|
||||||
return;
|
|
||||||
_lastExceptionLog = DateTime.Now;
|
|
||||||
|
|
||||||
if (!_updateExceptions.Any())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Group by stack trace, that should gather up duplicate exceptions
|
|
||||||
foreach (IGrouping<string?, Exception> exceptions in _updateExceptions.GroupBy(e => e.StackTrace))
|
|
||||||
_logger.Warning(exceptions.First(), "Exception was thrown {count} times during update in the last 10 seconds", exceptions.Count());
|
|
||||||
|
|
||||||
// When logging is finished start with a fresh slate
|
|
||||||
_updateExceptions.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnInitialized()
|
private void OnInitialized()
|
||||||
{
|
{
|
||||||
IsInitialized = true;
|
IsInitialized = true;
|
||||||
Initialized?.Invoke(this, EventArgs.Empty);
|
Initialized?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int FrameRate { get; private set; }
|
|
||||||
public TimeSpan FrameTime { get; private set; }
|
|
||||||
public bool ProfileRenderingDisabled { get; set; }
|
|
||||||
public bool IsElevated { get; set; }
|
|
||||||
|
|
||||||
public bool IsInitialized { get; set; }
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
throw new ArtemisCoreException("Cannot initialize the core as it is already initialized.");
|
|
||||||
|
|
||||||
_logger.Information("Initializing Artemis Core version {CurrentVersion}", Constants.CurrentVersion);
|
|
||||||
_logger.Information("Startup arguments: {StartupArguments}", Constants.StartupArguments);
|
|
||||||
_logger.Information("Elevated permissions: {IsElevated}", IsElevated);
|
|
||||||
_logger.Information("Stopwatch high resolution: {IsHighResolution}", Stopwatch.IsHighResolution);
|
|
||||||
|
|
||||||
ApplyLoggingLevel();
|
|
||||||
|
|
||||||
ProcessMonitor.Start();
|
|
||||||
|
|
||||||
// Don't remove even if it looks useless
|
|
||||||
// Just this line should prevent a certain someone from removing HidSharp as an unused dependency as well
|
|
||||||
Version? hidSharpVersion = Assembly.GetAssembly(typeof(HidDevice))!.GetName().Version;
|
|
||||||
_logger.Debug("Forcing plugins to use HidSharp {HidSharpVersion}", hidSharpVersion);
|
|
||||||
|
|
||||||
// Initialize the services
|
|
||||||
_pluginManagementService.CopyBuiltInPlugins();
|
|
||||||
_pluginManagementService.LoadPlugins(IsElevated);
|
|
||||||
|
|
||||||
_rgbService.ApplyPreferredGraphicsContext(Constants.StartupArguments.Contains("--force-software-render"));
|
|
||||||
_rgbService.SetRenderPaused(false);
|
|
||||||
OnInitialized();
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler? Initialized;
|
|
||||||
public event EventHandler<FrameRenderingEventArgs>? FrameRendering;
|
|
||||||
public event EventHandler<FrameRenderedEventArgs>? FrameRendered;
|
|
||||||
}
|
}
|
||||||
@ -1,22 +1,252 @@
|
|||||||
using System.Linq;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
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.Core;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace Artemis.Core.Services;
|
namespace Artemis.Core.Services;
|
||||||
|
|
||||||
internal class DeviceService : IDeviceService
|
internal class DeviceService : IDeviceService
|
||||||
{
|
{
|
||||||
private readonly IRgbService _rgbService;
|
private readonly ILogger _logger;
|
||||||
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
|
private readonly IDeviceRepository _deviceRepository;
|
||||||
|
private readonly Lazy<IRenderService> _renderService;
|
||||||
|
private readonly List<ArtemisDevice> _enabledDevices = new();
|
||||||
|
private readonly List<ArtemisDevice> _devices = new();
|
||||||
|
|
||||||
public DeviceService(IRgbService rgbService)
|
public DeviceService(ILogger logger, IPluginManagementService pluginManagementService, IDeviceRepository deviceRepository, Lazy<IRenderService> renderService)
|
||||||
{
|
{
|
||||||
_rgbService = rgbService;
|
_logger = logger;
|
||||||
|
_pluginManagementService = pluginManagementService;
|
||||||
|
_deviceRepository = deviceRepository;
|
||||||
|
_renderService = renderService;
|
||||||
|
|
||||||
|
EnabledDevices = new ReadOnlyCollection<ArtemisDevice>(_enabledDevices);
|
||||||
|
Devices = new ReadOnlyCollection<ArtemisDevice>(_devices);
|
||||||
|
|
||||||
|
RenderScale.RenderScaleMultiplierChanged += RenderScaleOnRenderScaleMultiplierChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IReadOnlyCollection<ArtemisDevice> EnabledDevices { get; }
|
||||||
|
public IReadOnlyCollection<ArtemisDevice> Devices { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void IdentifyDevice(ArtemisDevice device)
|
||||||
|
{
|
||||||
|
BlinkDevice(device, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void AddDeviceProvider(DeviceProvider deviceProvider)
|
||||||
|
{
|
||||||
|
_logger.Verbose("[AddDeviceProvider] Adding {DeviceProvider}", deviceProvider.GetType().Name);
|
||||||
|
IRGBDeviceProvider rgbDeviceProvider = deviceProvider.RgbDeviceProvider;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Can't see why this would happen, RgbService used to do this though
|
||||||
|
List<ArtemisDevice> toRemove = _devices.Where(a => rgbDeviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList();
|
||||||
|
_logger.Verbose("[AddDeviceProvider] Removing {Count} old device(s)", toRemove.Count);
|
||||||
|
foreach (ArtemisDevice device in toRemove)
|
||||||
|
{
|
||||||
|
_devices.Remove(device);
|
||||||
|
OnDeviceRemoved(new DeviceEventArgs(device));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Exception> providerExceptions = new();
|
||||||
|
|
||||||
|
void DeviceProviderOnException(object? sender, ExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.IsCritical)
|
||||||
|
providerExceptions.Add(e.Exception);
|
||||||
|
else
|
||||||
|
_logger.Warning(e.Exception, "Device provider {deviceProvider} threw non-critical exception", deviceProvider.GetType().Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Verbose("[AddDeviceProvider] Initializing device provider");
|
||||||
|
rgbDeviceProvider.Exception += DeviceProviderOnException;
|
||||||
|
rgbDeviceProvider.Initialize();
|
||||||
|
_logger.Verbose("[AddDeviceProvider] Attaching devices of device provider");
|
||||||
|
rgbDeviceProvider.Exception -= DeviceProviderOnException;
|
||||||
|
if (providerExceptions.Count == 1)
|
||||||
|
throw new ArtemisPluginException("RGB.NET threw exception: " + providerExceptions.First().Message, providerExceptions.First());
|
||||||
|
if (providerExceptions.Count > 1)
|
||||||
|
throw new ArtemisPluginException("RGB.NET threw multiple exceptions", new AggregateException(providerExceptions));
|
||||||
|
|
||||||
|
if (!rgbDeviceProvider.Devices.Any())
|
||||||
|
{
|
||||||
|
_logger.Warning("Device provider {deviceProvider} has no devices", deviceProvider.GetType().Name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ArtemisDevice> addedDevices = new();
|
||||||
|
foreach (IRGBDevice rgbDevice in rgbDeviceProvider.Devices)
|
||||||
|
{
|
||||||
|
ArtemisDevice artemisDevice = GetArtemisDevice(rgbDevice);
|
||||||
|
addedDevices.Add(artemisDevice);
|
||||||
|
_devices.Add(artemisDevice);
|
||||||
|
if (artemisDevice.IsEnabled)
|
||||||
|
_enabledDevices.Add(artemisDevice);
|
||||||
|
|
||||||
|
_logger.Debug("Device provider {deviceProvider} added {deviceName}", deviceProvider.GetType().Name, rgbDevice.DeviceInfo.DeviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
_devices.Sort((a, b) => a.ZIndex - b.ZIndex);
|
||||||
|
_enabledDevices.Sort((a, b) => a.ZIndex - b.ZIndex);
|
||||||
|
|
||||||
|
OnDeviceProviderAdded(new DeviceProviderEventArgs(deviceProvider, addedDevices));
|
||||||
|
foreach (ArtemisDevice artemisDevice in addedDevices)
|
||||||
|
OnDeviceAdded(new DeviceEventArgs(artemisDevice));
|
||||||
|
|
||||||
|
UpdateLeds();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Error(e, "Exception during device loading for device provider {deviceProvider}", deviceProvider.GetType().Name);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void RemoveDeviceProvider(DeviceProvider deviceProvider)
|
||||||
|
{
|
||||||
|
_logger.Verbose("[RemoveDeviceProvider] Pausing rendering to remove {DeviceProvider}", deviceProvider.GetType().Name);
|
||||||
|
IRGBDeviceProvider rgbDeviceProvider = deviceProvider.RgbDeviceProvider;
|
||||||
|
List<ArtemisDevice> toRemove = _devices.Where(a => rgbDeviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Verbose("[RemoveDeviceProvider] Removing {Count} old device(s)", toRemove.Count);
|
||||||
|
foreach (ArtemisDevice device in toRemove)
|
||||||
|
{
|
||||||
|
_devices.Remove(device);
|
||||||
|
_enabledDevices.Remove(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
_devices.Sort((a, b) => a.ZIndex - b.ZIndex);
|
||||||
|
|
||||||
|
OnDeviceProviderRemoved(new DeviceProviderEventArgs(deviceProvider, toRemove));
|
||||||
|
foreach (ArtemisDevice artemisDevice in toRemove)
|
||||||
|
OnDeviceRemoved(new DeviceEventArgs(artemisDevice));
|
||||||
|
|
||||||
|
UpdateLeds();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Error(e, "Exception during device removal for device provider {deviceProvider}", deviceProvider.GetType().Name);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void AutoArrangeDevices()
|
||||||
|
{
|
||||||
|
SurfaceArrangement surfaceArrangement = SurfaceArrangement.GetDefaultArrangement();
|
||||||
|
surfaceArrangement.Arrange(_devices);
|
||||||
|
foreach (ArtemisDevice artemisDevice in _devices)
|
||||||
|
artemisDevice.ApplyDefaultCategories();
|
||||||
|
|
||||||
|
SaveDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout? layout)
|
||||||
|
{
|
||||||
|
if (layout == null || layout.Source == LayoutSource.Default)
|
||||||
|
device.ApplyLayout(layout, false, false);
|
||||||
|
else
|
||||||
|
device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
|
||||||
|
|
||||||
|
UpdateLeds();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void EnableDevice(ArtemisDevice device)
|
||||||
|
{
|
||||||
|
if (device.IsEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_enabledDevices.Add(device);
|
||||||
|
device.IsEnabled = true;
|
||||||
|
device.ApplyToEntity();
|
||||||
|
_deviceRepository.Save(device.DeviceEntity);
|
||||||
|
|
||||||
|
OnDeviceEnabled(new DeviceEventArgs(device));
|
||||||
|
UpdateLeds();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void DisableDevice(ArtemisDevice device)
|
||||||
|
{
|
||||||
|
if (!device.IsEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_enabledDevices.Remove(device);
|
||||||
|
device.IsEnabled = false;
|
||||||
|
device.ApplyToEntity();
|
||||||
|
_deviceRepository.Save(device.DeviceEntity);
|
||||||
|
|
||||||
|
OnDeviceDisabled(new DeviceEventArgs(device));
|
||||||
|
UpdateLeds();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void SaveDevice(ArtemisDevice artemisDevice)
|
||||||
|
{
|
||||||
|
artemisDevice.ApplyToEntity();
|
||||||
|
artemisDevice.ApplyToRgbDevice();
|
||||||
|
|
||||||
|
_deviceRepository.Save(artemisDevice.DeviceEntity);
|
||||||
|
UpdateLeds();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void SaveDevices()
|
||||||
|
{
|
||||||
|
foreach (ArtemisDevice artemisDevice in _devices)
|
||||||
|
{
|
||||||
|
artemisDevice.ApplyToEntity();
|
||||||
|
artemisDevice.ApplyToRgbDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
_deviceRepository.Save(_devices.Select(d => d.DeviceEntity));
|
||||||
|
UpdateLeds();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArtemisDevice GetArtemisDevice(IRGBDevice rgbDevice)
|
||||||
|
{
|
||||||
|
string deviceIdentifier = rgbDevice.GetDeviceIdentifier();
|
||||||
|
DeviceEntity? deviceEntity = _deviceRepository.Get(deviceIdentifier);
|
||||||
|
DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
|
||||||
|
|
||||||
|
ArtemisDevice device;
|
||||||
|
if (deviceEntity != null)
|
||||||
|
device = new ArtemisDevice(rgbDevice, deviceProvider, deviceEntity);
|
||||||
|
// 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);
|
||||||
|
device = new ArtemisDevice(rgbDevice, deviceProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
device.ApplyToRgbDevice();
|
||||||
|
ApplyDeviceLayout(device, device.GetBestDeviceLayout());
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
private void BlinkDevice(ArtemisDevice device, int blinkCount)
|
private void BlinkDevice(ArtemisDevice device, int blinkCount)
|
||||||
{
|
{
|
||||||
|
RGBSurface surface = _renderService.Value.Surface;
|
||||||
|
|
||||||
// Create a LED group way at the top
|
// Create a LED group way at the top
|
||||||
ListLedGroup ledGroup = new(_rgbService.Surface, device.Leds.Select(l => l.RgbLed))
|
ListLedGroup ledGroup = new(surface, device.Leds.Select(l => l.RgbLed))
|
||||||
{
|
{
|
||||||
Brush = new SolidColorBrush(new Color(255, 255, 255)),
|
Brush = new SolidColorBrush(new Color(255, 255, 255)),
|
||||||
ZIndex = 999
|
ZIndex = 999
|
||||||
@ -36,9 +266,81 @@ internal class DeviceService : IDeviceService
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void IdentifyDevice(ArtemisDevice device)
|
private void CalculateRenderProperties()
|
||||||
{
|
{
|
||||||
BlinkDevice(device, 0);
|
foreach (ArtemisDevice artemisDevice in Devices)
|
||||||
|
artemisDevice.CalculateRenderProperties();
|
||||||
|
UpdateLeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateLeds()
|
||||||
|
{
|
||||||
|
OnLedsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderScaleOnRenderScaleMultiplierChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
CalculateRenderProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event EventHandler<DeviceEventArgs>? DeviceAdded;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event EventHandler<DeviceEventArgs>? DeviceRemoved;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event EventHandler<DeviceEventArgs>? DeviceEnabled;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event EventHandler<DeviceEventArgs>? DeviceDisabled;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event EventHandler<DeviceProviderEventArgs>? DeviceProviderAdded;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event EventHandler<DeviceProviderEventArgs>? DeviceProviderRemoved;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event EventHandler? LedsChanged;
|
||||||
|
|
||||||
|
protected virtual void OnDeviceAdded(DeviceEventArgs e)
|
||||||
|
{
|
||||||
|
DeviceAdded?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDeviceRemoved(DeviceEventArgs e)
|
||||||
|
{
|
||||||
|
DeviceRemoved?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDeviceEnabled(DeviceEventArgs e)
|
||||||
|
{
|
||||||
|
DeviceEnabled?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDeviceDisabled(DeviceEventArgs e)
|
||||||
|
{
|
||||||
|
DeviceDisabled?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDeviceProviderAdded(DeviceProviderEventArgs e)
|
||||||
|
{
|
||||||
|
DeviceProviderAdded?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDeviceProviderRemoved(DeviceProviderEventArgs e)
|
||||||
|
{
|
||||||
|
DeviceProviderRemoved?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnLedsChanged()
|
||||||
|
{
|
||||||
|
LedsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@ -9,19 +9,19 @@ namespace Artemis.Core.Services;
|
|||||||
internal class InputService : IInputService
|
internal class InputService : IInputService
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IDeviceService _deviceService;
|
||||||
private ArtemisDevice? _firstKeyboard;
|
private ArtemisDevice? _firstKeyboard;
|
||||||
private ArtemisDevice? _firstMouse;
|
private ArtemisDevice? _firstMouse;
|
||||||
private int _keyboardCount;
|
private int _keyboardCount;
|
||||||
private int _mouseCount;
|
private int _mouseCount;
|
||||||
|
|
||||||
public InputService(ILogger logger, IRgbService rgbService)
|
public InputService(ILogger logger, IDeviceService deviceService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_rgbService = rgbService;
|
_deviceService = deviceService;
|
||||||
|
|
||||||
_rgbService.DeviceAdded += RgbServiceOnDevicesModified;
|
_deviceService.DeviceAdded += DeviceServiceOnDevicesModified;
|
||||||
_rgbService.DeviceRemoved += RgbServiceOnDevicesModified;
|
_deviceService.DeviceRemoved += DeviceServiceOnDevicesModified;
|
||||||
BustIdentifierCache();
|
BustIdentifierCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +157,7 @@ internal class InputService : IInputService
|
|||||||
|
|
||||||
_logger.Debug("Stop identifying device {device}", _identifyingDevice);
|
_logger.Debug("Stop identifying device {device}", _identifyingDevice);
|
||||||
_identifyingDevice = null;
|
_identifyingDevice = null;
|
||||||
_rgbService.SaveDevices();
|
_deviceService.SaveDevices();
|
||||||
|
|
||||||
BustIdentifierCache();
|
BustIdentifierCache();
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ internal class InputService : IInputService
|
|||||||
public void BustIdentifierCache()
|
public void BustIdentifierCache()
|
||||||
{
|
{
|
||||||
_deviceCache.Clear();
|
_deviceCache.Clear();
|
||||||
_devices = _rgbService.EnabledDevices.Where(d => d.InputIdentifiers.Any()).ToList();
|
_devices = _deviceService.EnabledDevices.Where(d => d.InputIdentifiers.Any()).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddDeviceToCache(ArtemisDevice match, InputProvider provider, object identifier)
|
private void AddDeviceToCache(ArtemisDevice match, InputProvider provider, object identifier)
|
||||||
@ -241,12 +241,12 @@ internal class InputService : IInputService
|
|||||||
OnDeviceIdentified();
|
OnDeviceIdentified();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RgbServiceOnDevicesModified(object? sender, DeviceEventArgs args)
|
private void DeviceServiceOnDevicesModified(object? sender, DeviceEventArgs args)
|
||||||
{
|
{
|
||||||
_firstKeyboard = _rgbService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Keyboard);
|
_firstKeyboard = _deviceService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Keyboard);
|
||||||
_firstMouse = _rgbService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Mouse);
|
_firstMouse = _deviceService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Mouse);
|
||||||
_keyboardCount = _rgbService.Devices.Count(d => d.DeviceType == RGBDeviceType.Keyboard);
|
_keyboardCount = _deviceService.Devices.Count(d => d.DeviceType == RGBDeviceType.Keyboard);
|
||||||
_mouseCount = _rgbService.Devices.Count(d => d.DeviceType == RGBDeviceType.Mouse);
|
_mouseCount = _deviceService.Devices.Count(d => d.DeviceType == RGBDeviceType.Mouse);
|
||||||
|
|
||||||
BustIdentifierCache();
|
BustIdentifierCache();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,21 +12,6 @@ public interface ICoreService : IArtemisService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsInitialized { get; }
|
bool IsInitialized { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The time the last frame took to render
|
|
||||||
/// </summary>
|
|
||||||
TimeSpan FrameTime { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of frames rendered each second
|
|
||||||
/// </summary>
|
|
||||||
public int FrameRate { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether profiles are rendered each frame by calling their Render method
|
|
||||||
/// </summary>
|
|
||||||
bool ProfileRenderingDisabled { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions)
|
/// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -41,14 +26,4 @@ public interface ICoreService : IArtemisService
|
|||||||
/// Occurs the core has finished initializing
|
/// Occurs the core has finished initializing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event EventHandler Initialized;
|
event EventHandler Initialized;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs whenever a frame is rendering, after modules have rendered
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler<FrameRenderingEventArgs> FrameRendering;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs whenever a frame is finished rendering and the render pipeline is closed
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler<FrameRenderedEventArgs> FrameRendered;
|
|
||||||
}
|
}
|
||||||
@ -1,13 +1,109 @@
|
|||||||
namespace Artemis.Core.Services;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Core.DeviceProviders;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A service that allows you manage an <see cref="ArtemisDevice" />
|
/// A service that allows you manage an <see cref="ArtemisDevice" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IDeviceService : IArtemisService
|
public interface IDeviceService : IArtemisService
|
||||||
{
|
{
|
||||||
|
/// <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>
|
/// <summary>
|
||||||
/// Identifies the device by making it blink white 5 times
|
/// Identifies the device by making it blink white 5 times
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="device"></param>
|
/// <param name="device"></param>
|
||||||
void IdentifyDevice(ArtemisDevice device);
|
void IdentifyDevice(ArtemisDevice device);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the given device provider and its devices.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deviceProvider"></param>
|
||||||
|
void AddDeviceProvider(DeviceProvider deviceProvider);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the given device provider and its devices.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deviceProvider"></param>
|
||||||
|
void RemoveDeviceProvider(DeviceProvider deviceProvider);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies auto-arranging logic to the surface
|
||||||
|
/// </summary>
|
||||||
|
void AutoArrangeDevices();
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
/// Enables the provided device
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="device">The device to enable</param>
|
||||||
|
void EnableDevice(ArtemisDevice device);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables the provided device
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="device">The device to disable</param>
|
||||||
|
void DisableDevice(ArtemisDevice device);
|
||||||
|
|
||||||
|
/// <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();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a single device was added.
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler<DeviceEventArgs> DeviceAdded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a single device was removed.
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler<DeviceEventArgs> DeviceRemoved;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a single device was disabled
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler<DeviceEventArgs> DeviceEnabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a single device was disabled.
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler<DeviceEventArgs> DeviceDisabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a device provider was added.
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler<DeviceProviderEventArgs> DeviceProviderAdded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a device provider was removed.
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler<DeviceProviderEventArgs> DeviceProviderRemoved;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the surface has had modifications to its LED collection
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler LedsChanged;
|
||||||
}
|
}
|
||||||
63
src/Artemis.Core/Services/Interfaces/IRenderService.cs
Normal file
63
src/Artemis.Core/Services/Interfaces/IRenderService.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Core.Services.Core;
|
||||||
|
using Artemis.Core.SkiaSharp;
|
||||||
|
using RGB.NET.Core;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a service that manages the render loop and renderers.
|
||||||
|
/// </summary>
|
||||||
|
public interface IRenderService : IArtemisService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the graphics context to be used for rendering
|
||||||
|
/// </summary>
|
||||||
|
IManagedGraphicsContext? GraphicsContext { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the RGB surface to which is being rendered.
|
||||||
|
/// </summary>
|
||||||
|
RGBSurface Surface { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of registered renderers.
|
||||||
|
/// </summary>
|
||||||
|
List<IRenderer> Renderers { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a boolean indicating whether rendering is paused.
|
||||||
|
/// </summary>
|
||||||
|
bool IsPaused { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a boolean indicating whether to flush the RGB.NET LEDs during next update
|
||||||
|
/// </summary>
|
||||||
|
bool FlushLeds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time the last frame took to render
|
||||||
|
/// </summary>
|
||||||
|
TimeSpan FrameTime { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of frames rendered each second
|
||||||
|
/// </summary>
|
||||||
|
public int FrameRate { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the render service and starts rendering.
|
||||||
|
/// </summary>
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs whenever a frame is rendering, after modules have rendered
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler<FrameRenderingEventArgs> FrameRendering;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs whenever a frame is finished rendering and the render pipeline is closed
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler<FrameRenderedEventArgs> FrameRendered;
|
||||||
|
}
|
||||||
@ -1,168 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Artemis.Core.SkiaSharp;
|
|
||||||
using RGB.NET.Core;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A service that allows you to manage the <see cref="RGBSurface" /> and its contents
|
|
||||||
/// </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>
|
|
||||||
RGBSurface Surface { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether rendering should be paused
|
|
||||||
/// </summary>
|
|
||||||
bool IsRenderPaused { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a boolean indicating whether the render pipeline is open
|
|
||||||
/// </summary>
|
|
||||||
bool RenderOpen { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a boolean indicating whether to flush the RGB.NET LEDs during next update
|
|
||||||
/// </summary>
|
|
||||||
bool FlushLeds { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opens the render pipeline
|
|
||||||
/// </summary>
|
|
||||||
SKTexture OpenRender();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Closes the render pipeline
|
|
||||||
/// </summary>
|
|
||||||
void CloseRender();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies the current value of the <c>Core.PreferredGraphicsContext</c> setting to the graphics context.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="forceSoftware">A boolean to indicate whether or not to force the graphics context to software mode.</param>
|
|
||||||
void ApplyPreferredGraphicsContext(bool forceSoftware);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the graphics context to the provided <paramref name="managedGraphicsContext"></paramref>.
|
|
||||||
/// <para>Note: The old graphics context will be used until the next frame starts rendering and is disposed afterwards.</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="managedGraphicsContext">
|
|
||||||
/// The new managed graphics context. If <see langword="null" />, software rendering
|
|
||||||
/// is used.
|
|
||||||
/// </param>
|
|
||||||
void UpdateGraphicsContext(IManagedGraphicsContext? managedGraphicsContext);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds the given device provider to the <see cref="Surface" />
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="deviceProvider"></param>
|
|
||||||
void AddDeviceProvider(IRGBDeviceProvider deviceProvider);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the given device provider from the <see cref="Surface" />
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="deviceProvider"></param>
|
|
||||||
void RemoveDeviceProvider(IRGBDeviceProvider deviceProvider);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies auto-arranging logic to the surface
|
|
||||||
/// </summary>
|
|
||||||
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();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enables the provided device
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="device">The device to enable</param>
|
|
||||||
void EnableDevice(ArtemisDevice device);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disables the provided device
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="device">The device to disable</param>
|
|
||||||
void DisableDevice(ArtemisDevice device);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pauses or resumes rendering, method won't return until the current frame finished rendering
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="paused"></param>
|
|
||||||
/// <returns><see langword="true" /> if the pause state was changed; otherwise <see langword="false" />.</returns>
|
|
||||||
bool SetRenderPaused(bool paused);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a single device was added
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler<DeviceEventArgs> DeviceAdded;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a single device was removed
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler<DeviceEventArgs> DeviceRemoved;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when the surface has had modifications to its LED collection
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler LedsChanged;
|
|
||||||
}
|
|
||||||
262
src/Artemis.Core/Services/RenderService.cs
Normal file
262
src/Artemis.Core/Services/RenderService.cs
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using Artemis.Core.Providers;
|
||||||
|
using Artemis.Core.Services.Core;
|
||||||
|
using Artemis.Core.SkiaSharp;
|
||||||
|
using DryIoc;
|
||||||
|
using RGB.NET.Core;
|
||||||
|
using Serilog;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services;
|
||||||
|
|
||||||
|
internal class RenderService : IRenderService, IRenderer, IDisposable
|
||||||
|
{
|
||||||
|
private readonly Stopwatch _frameStopWatch;
|
||||||
|
private readonly List<Exception> _updateExceptions = new();
|
||||||
|
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly IDeviceService _deviceService;
|
||||||
|
private readonly LazyEnumerable<IGraphicsContextProvider> _graphicsContextProviders;
|
||||||
|
private readonly PluginSetting<int> _targetFrameRateSetting;
|
||||||
|
private readonly PluginSetting<double> _renderScaleSetting;
|
||||||
|
private readonly PluginSetting<string> _preferredGraphicsContext;
|
||||||
|
|
||||||
|
private SurfaceManager _surfaceManager;
|
||||||
|
private int _frames;
|
||||||
|
private DateTime _lastExceptionLog;
|
||||||
|
private DateTime _lastFrameRateSample;
|
||||||
|
private bool _initialized;
|
||||||
|
|
||||||
|
public RenderService(ILogger logger, ISettingsService settingsService, IDeviceService deviceService, LazyEnumerable<IGraphicsContextProvider> graphicsContextProviders)
|
||||||
|
{
|
||||||
|
_frameStopWatch = new Stopwatch();
|
||||||
|
_logger = logger;
|
||||||
|
_deviceService = deviceService;
|
||||||
|
_graphicsContextProviders = graphicsContextProviders;
|
||||||
|
|
||||||
|
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 30);
|
||||||
|
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.25);
|
||||||
|
_preferredGraphicsContext = settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
|
||||||
|
_targetFrameRateSetting.SettingChanged += OnRenderSettingsChanged;
|
||||||
|
_renderScaleSetting.SettingChanged += RenderScaleSettingOnSettingChanged;
|
||||||
|
_preferredGraphicsContext.SettingChanged += PreferredGraphicsContextOnSettingChanged;
|
||||||
|
|
||||||
|
Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
||||||
|
_surfaceManager = new SurfaceManager(this, GraphicsContext, _targetFrameRateSetting.Value, (float) _renderScaleSetting.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IManagedGraphicsContext? GraphicsContext { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public RGBSurface Surface => _surfaceManager.Surface;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public List<IRenderer> Renderers { get; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsPaused
|
||||||
|
{
|
||||||
|
get => _surfaceManager.IsPaused;
|
||||||
|
set => _surfaceManager.SetPaused(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int FrameRate { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public TimeSpan FrameTime { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool FlushLeds { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Render(SKCanvas canvas, double delta)
|
||||||
|
{
|
||||||
|
_frameStopWatch.Restart();
|
||||||
|
|
||||||
|
if (FlushLeds)
|
||||||
|
{
|
||||||
|
FlushLeds = false;
|
||||||
|
Surface.Update(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OnFrameRendering(new FrameRenderingEventArgs(canvas, delta, _surfaceManager.Surface));
|
||||||
|
foreach (IRenderer renderer in Renderers)
|
||||||
|
renderer.Render(canvas, delta);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_updateExceptions.Add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void PostRender(SKTexture texture)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (IRenderer renderer in Renderers)
|
||||||
|
renderer.PostRender(texture);
|
||||||
|
OnFrameRendered(new FrameRenderedEventArgs(texture, _surfaceManager.Surface));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_updateExceptions.Add(e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_frameStopWatch.Stop();
|
||||||
|
_frames++;
|
||||||
|
|
||||||
|
if ((DateTime.Now - _lastFrameRateSample).TotalSeconds >= 1)
|
||||||
|
{
|
||||||
|
FrameRate = _frames;
|
||||||
|
_frames = 0;
|
||||||
|
_lastFrameRateSample = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameTime = _frameStopWatch.Elapsed;
|
||||||
|
|
||||||
|
LogUpdateExceptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetGraphicsContext()
|
||||||
|
{
|
||||||
|
if (Constants.StartupArguments.Contains("--force-software-render"))
|
||||||
|
{
|
||||||
|
_logger.Warning("Startup argument '--force-software-render' is applied, forcing software rendering");
|
||||||
|
GraphicsContext = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_preferredGraphicsContext.Value == "Software")
|
||||||
|
{
|
||||||
|
GraphicsContext = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IGraphicsContextProvider> providers = _graphicsContextProviders.ToList();
|
||||||
|
if (!providers.Any())
|
||||||
|
{
|
||||||
|
_logger.Warning("No graphics context provider found, defaulting to software rendering");
|
||||||
|
GraphicsContext = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IManagedGraphicsContext? context = providers.FirstOrDefault(p => p.GraphicsContextName == _preferredGraphicsContext.Value)?.GetGraphicsContext();
|
||||||
|
if (context == null)
|
||||||
|
_logger.Warning("No graphics context named '{Context}' found, defaulting to software rendering", _preferredGraphicsContext.Value);
|
||||||
|
|
||||||
|
GraphicsContext = context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogUpdateExceptions()
|
||||||
|
{
|
||||||
|
// Only log update exceptions every 10 seconds to avoid spamming the logs
|
||||||
|
if (DateTime.Now - _lastExceptionLog < TimeSpan.FromSeconds(10))
|
||||||
|
return;
|
||||||
|
_lastExceptionLog = DateTime.Now;
|
||||||
|
|
||||||
|
if (!_updateExceptions.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Group by stack trace, that should gather up duplicate exceptions
|
||||||
|
foreach (IGrouping<string?, Exception> exceptions in _updateExceptions.GroupBy(e => e.StackTrace))
|
||||||
|
_logger.Warning(exceptions.First(), "Exception was thrown {count} times during update in the last 10 seconds", exceptions.Count());
|
||||||
|
|
||||||
|
// When logging is finished start with a fresh slate
|
||||||
|
_updateExceptions.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeviceServiceOnDeviceProviderAdded(object? sender, DeviceProviderEventArgs e)
|
||||||
|
{
|
||||||
|
_surfaceManager.AddDevices(e.Devices.Where(d => d.IsEnabled));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeviceServiceOnDeviceProviderRemoved(object? sender, DeviceProviderEventArgs e)
|
||||||
|
{
|
||||||
|
_surfaceManager.RemoveDevices(e.Devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeviceServiceOnDeviceEnabled(object? sender, DeviceEventArgs e)
|
||||||
|
{
|
||||||
|
_surfaceManager.AddDevices(new List<ArtemisDevice> {e.Device});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeviceServiceOnDeviceDisabled(object? sender, DeviceEventArgs e)
|
||||||
|
{
|
||||||
|
_surfaceManager.RemoveDevices(new List<ArtemisDevice> {e.Device});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRenderSettingsChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_surfaceManager.UpdateTargetFrameRate(_targetFrameRateSetting.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
RenderScale.SetRenderScaleMultiplier((int) (1 / _renderScaleSetting.Value));
|
||||||
|
_surfaceManager.UpdateRenderScale((float) _renderScaleSetting.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PreferredGraphicsContextOnSettingChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
SetGraphicsContext();
|
||||||
|
_surfaceManager.UpdateGraphicsContext(GraphicsContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
IsPaused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
IsPaused = true;
|
||||||
|
_surfaceManager.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event EventHandler<FrameRenderingEventArgs>? FrameRendering;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event EventHandler<FrameRenderedEventArgs>? FrameRendered;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
if (_initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetGraphicsContext();
|
||||||
|
_surfaceManager.AddDevices(_deviceService.EnabledDevices);
|
||||||
|
|
||||||
|
_deviceService.DeviceProviderAdded += DeviceServiceOnDeviceProviderAdded;
|
||||||
|
_deviceService.DeviceProviderRemoved += DeviceServiceOnDeviceProviderRemoved;
|
||||||
|
_deviceService.DeviceEnabled += DeviceServiceOnDeviceEnabled;
|
||||||
|
_deviceService.DeviceDisabled += DeviceServiceOnDeviceDisabled;
|
||||||
|
|
||||||
|
IsPaused = false;
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnFrameRendering(FrameRenderingEventArgs e)
|
||||||
|
{
|
||||||
|
FrameRendering?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnFrameRendered(FrameRenderedEventArgs e)
|
||||||
|
{
|
||||||
|
FrameRendered?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,613 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using Artemis.Core.DeviceProviders;
|
|
||||||
using Artemis.Core.Providers;
|
|
||||||
using Artemis.Core.Services.Models;
|
|
||||||
using Artemis.Core.SkiaSharp;
|
|
||||||
using Artemis.Storage.Entities.Surface;
|
|
||||||
using Artemis.Storage.Repositories.Interfaces;
|
|
||||||
using DryIoc;
|
|
||||||
using RGB.NET.Core;
|
|
||||||
using Serilog;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides wrapped access the RGB.NET
|
|
||||||
/// </summary>
|
|
||||||
internal class RgbService : IRgbService
|
|
||||||
{
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
private readonly IPluginManagementService _pluginManagementService;
|
|
||||||
private readonly IDeviceRepository _deviceRepository;
|
|
||||||
private readonly LazyEnumerable<IGraphicsContextProvider> _graphicsContextProviders;
|
|
||||||
|
|
||||||
private readonly PluginSetting<string> _preferredGraphicsContext;
|
|
||||||
private readonly PluginSetting<double> _renderScaleSetting;
|
|
||||||
private readonly PluginSetting<int> _targetFrameRateSetting;
|
|
||||||
|
|
||||||
private readonly List<ArtemisDevice> _devices;
|
|
||||||
private readonly List<ArtemisDevice> _enabledDevices;
|
|
||||||
private readonly SKTextureBrush _textureBrush = new(null) {CalculationMode = RenderMode.Absolute};
|
|
||||||
private Dictionary<Led, ArtemisLed> _ledMap;
|
|
||||||
private ListLedGroup? _surfaceLedGroup;
|
|
||||||
private SKTexture? _texture;
|
|
||||||
|
|
||||||
public RgbService(ILogger logger,
|
|
||||||
ISettingsService settingsService,
|
|
||||||
IPluginManagementService pluginManagementService,
|
|
||||||
IDeviceRepository deviceRepository,
|
|
||||||
LazyEnumerable<IGraphicsContextProvider> graphicsContextProviders)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_pluginManagementService = pluginManagementService;
|
|
||||||
_deviceRepository = deviceRepository;
|
|
||||||
_graphicsContextProviders = graphicsContextProviders;
|
|
||||||
|
|
||||||
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 30);
|
|
||||||
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.25);
|
|
||||||
_preferredGraphicsContext = settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
|
|
||||||
|
|
||||||
Surface = new RGBSurface();
|
|
||||||
Utilities.RenderScaleMultiplier = (int) (1 / _renderScaleSetting.Value);
|
|
||||||
|
|
||||||
// Let's throw these for now
|
|
||||||
Surface.Exception += SurfaceOnException;
|
|
||||||
Surface.SurfaceLayoutChanged += SurfaceOnLayoutChanged;
|
|
||||||
_targetFrameRateSetting.SettingChanged += TargetFrameRateSettingOnSettingChanged;
|
|
||||||
_renderScaleSetting.SettingChanged += RenderScaleSettingOnSettingChanged;
|
|
||||||
_preferredGraphicsContext.SettingChanged += PreferredGraphicsContextOnSettingChanged;
|
|
||||||
_enabledDevices = new List<ArtemisDevice>();
|
|
||||||
_devices = new List<ArtemisDevice>();
|
|
||||||
_ledMap = new Dictionary<Led, ArtemisLed>();
|
|
||||||
|
|
||||||
EnabledDevices = new ReadOnlyCollection<ArtemisDevice>(_enabledDevices);
|
|
||||||
Devices = new ReadOnlyCollection<ArtemisDevice>(_devices);
|
|
||||||
LedMap = new ReadOnlyDictionary<Led, ArtemisLed>(_ledMap);
|
|
||||||
|
|
||||||
UpdateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / _targetFrameRateSetting.Value};
|
|
||||||
SetRenderPaused(true);
|
|
||||||
Surface.RegisterUpdateTrigger(UpdateTrigger);
|
|
||||||
|
|
||||||
Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public TimerUpdateTrigger UpdateTrigger { get; }
|
|
||||||
|
|
||||||
protected virtual void OnDeviceRemoved(DeviceEventArgs e)
|
|
||||||
{
|
|
||||||
DeviceRemoved?.Invoke(this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnLedsChanged()
|
|
||||||
{
|
|
||||||
LedsChanged?.Invoke(this, EventArgs.Empty);
|
|
||||||
_texture?.Invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
SetRenderPaused(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SurfaceOnLayoutChanged(SurfaceLayoutChangedEventArgs args)
|
|
||||||
{
|
|
||||||
UpdateLedGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateLedGroup()
|
|
||||||
{
|
|
||||||
bool changedRenderPaused = SetRenderPaused(true);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_ledMap = new Dictionary<Led, ArtemisLed>(_devices.SelectMany(d => d.Leds).ToDictionary(l => l.RgbLed));
|
|
||||||
LedMap = new ReadOnlyDictionary<Led, ArtemisLed>(_ledMap);
|
|
||||||
|
|
||||||
if (_surfaceLedGroup == null)
|
|
||||||
{
|
|
||||||
_surfaceLedGroup = new ListLedGroup(Surface, LedMap.Select(l => l.Key)) {Brush = _textureBrush};
|
|
||||||
OnLedsChanged();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_surfaceLedGroup)
|
|
||||||
{
|
|
||||||
// Clean up the old background
|
|
||||||
_surfaceLedGroup.Detach();
|
|
||||||
|
|
||||||
// Apply the application wide brush and decorator
|
|
||||||
_surfaceLedGroup = new ListLedGroup(Surface, LedMap.Select(l => l.Key)) {Brush = _textureBrush};
|
|
||||||
OnLedsChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (changedRenderPaused)
|
|
||||||
SetRenderPaused(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TargetFrameRateSettingOnSettingChanged(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
UpdateTrigger.UpdateFrequency = 1.0 / _targetFrameRateSetting.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SurfaceOnException(ExceptionEventArgs args)
|
|
||||||
{
|
|
||||||
_logger.Warning(args.Exception, "Surface caught exception");
|
|
||||||
throw args.Exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDeviceAdded(DeviceEventArgs e)
|
|
||||||
{
|
|
||||||
DeviceAdded?.Invoke(this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
Utilities.RenderScaleMultiplier = (int) (1 / _renderScaleSetting.Value);
|
|
||||||
|
|
||||||
_texture?.Invalidate();
|
|
||||||
foreach (ArtemisDevice artemisDevice in Devices)
|
|
||||||
artemisDevice.CalculateRenderProperties();
|
|
||||||
OnLedsChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PreferredGraphicsContextOnSettingChanged(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
ApplyPreferredGraphicsContext(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IReadOnlyCollection<ArtemisDevice> EnabledDevices { get; }
|
|
||||||
public IReadOnlyCollection<ArtemisDevice> Devices { get; }
|
|
||||||
public IReadOnlyDictionary<Led, ArtemisLed> LedMap { get; private set; }
|
|
||||||
|
|
||||||
public RGBSurface Surface { get; set; }
|
|
||||||
public bool IsRenderPaused { get; set; }
|
|
||||||
public bool RenderOpen { get; private set; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool FlushLeds { get; set; }
|
|
||||||
|
|
||||||
public void AddDeviceProvider(IRGBDeviceProvider deviceProvider)
|
|
||||||
{
|
|
||||||
_logger.Verbose("[AddDeviceProvider] Pausing rendering to add {DeviceProvider}", deviceProvider.GetType().Name);
|
|
||||||
bool changedRenderPaused = SetRenderPaused(true);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
List<ArtemisDevice> toRemove = _devices.Where(a => deviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList();
|
|
||||||
_logger.Verbose("[AddDeviceProvider] Removing {Count} old device(s)", toRemove.Count);
|
|
||||||
Surface.Detach(toRemove.Select(d => d.RgbDevice));
|
|
||||||
foreach (ArtemisDevice device in toRemove)
|
|
||||||
RemoveDevice(device);
|
|
||||||
|
|
||||||
List<Exception> providerExceptions = new();
|
|
||||||
|
|
||||||
void DeviceProviderOnException(object? sender, ExceptionEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.IsCritical)
|
|
||||||
providerExceptions.Add(e.Exception);
|
|
||||||
else
|
|
||||||
_logger.Warning(e.Exception, "Device provider {deviceProvider} threw non-critical exception", deviceProvider.GetType().Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Verbose("[AddDeviceProvider] Initializing device provider");
|
|
||||||
deviceProvider.Exception += DeviceProviderOnException;
|
|
||||||
deviceProvider.Initialize();
|
|
||||||
_logger.Verbose("[AddDeviceProvider] Attaching devices of device provider");
|
|
||||||
Surface.Attach(deviceProvider.Devices);
|
|
||||||
deviceProvider.Exception -= DeviceProviderOnException;
|
|
||||||
if (providerExceptions.Count == 1)
|
|
||||||
throw new ArtemisPluginException("RGB.NET threw exception: " + providerExceptions.First().Message, providerExceptions.First());
|
|
||||||
if (providerExceptions.Count > 1)
|
|
||||||
throw new ArtemisPluginException("RGB.NET threw multiple exceptions", new AggregateException(providerExceptions));
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
_logger.Verbose("[AddDeviceProvider] Updating the LED group");
|
|
||||||
UpdateLedGroup();
|
|
||||||
|
|
||||||
_logger.Verbose("[AddDeviceProvider] Resuming rendering after adding {DeviceProvider}", deviceProvider.GetType().Name);
|
|
||||||
if (changedRenderPaused)
|
|
||||||
SetRenderPaused(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveDeviceProvider(IRGBDeviceProvider deviceProvider)
|
|
||||||
{
|
|
||||||
_logger.Verbose("[RemoveDeviceProvider] Pausing rendering to remove {DeviceProvider}", deviceProvider.GetType().Name);
|
|
||||||
bool changedRenderPaused = SetRenderPaused(true);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
List<ArtemisDevice> toRemove = _devices.Where(a => deviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList();
|
|
||||||
_logger.Verbose("[RemoveDeviceProvider] Removing {Count} old device(s)", toRemove.Count);
|
|
||||||
Surface.Detach(toRemove.Select(d => d.RgbDevice));
|
|
||||||
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
|
|
||||||
{
|
|
||||||
_logger.Verbose("[RemoveDeviceProvider] Updating the LED group");
|
|
||||||
UpdateLedGroup();
|
|
||||||
|
|
||||||
_logger.Verbose("[RemoveDeviceProvider] Resuming rendering after adding {DeviceProvider}", deviceProvider.GetType().Name);
|
|
||||||
if (changedRenderPaused)
|
|
||||||
SetRenderPaused(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Surface.UnregisterUpdateTrigger(UpdateTrigger);
|
|
||||||
|
|
||||||
UpdateTrigger.Dispose();
|
|
||||||
Surface.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SetRenderPaused(bool paused)
|
|
||||||
{
|
|
||||||
if (IsRenderPaused == paused)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (paused)
|
|
||||||
UpdateTrigger.Stop();
|
|
||||||
else
|
|
||||||
UpdateTrigger.Start();
|
|
||||||
|
|
||||||
IsRenderPaused = paused;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler<DeviceEventArgs>? DeviceAdded;
|
|
||||||
public event EventHandler<DeviceEventArgs>? DeviceRemoved;
|
|
||||||
public event EventHandler? LedsChanged;
|
|
||||||
|
|
||||||
#region Rendering
|
|
||||||
|
|
||||||
private IManagedGraphicsContext? _newGraphicsContext;
|
|
||||||
|
|
||||||
|
|
||||||
public SKTexture OpenRender()
|
|
||||||
{
|
|
||||||
if (RenderOpen)
|
|
||||||
throw new ArtemisCoreException("Render pipeline is already open");
|
|
||||||
|
|
||||||
if (_texture == null || _texture.IsInvalid)
|
|
||||||
CreateTexture();
|
|
||||||
|
|
||||||
RenderOpen = true;
|
|
||||||
return _texture!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CloseRender()
|
|
||||||
{
|
|
||||||
if (!RenderOpen)
|
|
||||||
throw new ArtemisCoreException("Render pipeline is already closed");
|
|
||||||
|
|
||||||
RenderOpen = false;
|
|
||||||
_texture?.CopyPixelData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CreateTexture()
|
|
||||||
{
|
|
||||||
if (RenderOpen)
|
|
||||||
throw new ArtemisCoreException("Cannot update the texture while rendering");
|
|
||||||
|
|
||||||
lock (_devices)
|
|
||||||
{
|
|
||||||
IManagedGraphicsContext? graphicsContext = Constants.ManagedGraphicsContext = _newGraphicsContext;
|
|
||||||
if (!ReferenceEquals(graphicsContext, _newGraphicsContext))
|
|
||||||
graphicsContext = _newGraphicsContext;
|
|
||||||
|
|
||||||
if (graphicsContext != null)
|
|
||||||
_logger.Debug("Creating SKTexture with graphics context {graphicsContext}", graphicsContext.GetType().Name);
|
|
||||||
else
|
|
||||||
_logger.Debug("Creating SKTexture with software-based graphics context");
|
|
||||||
|
|
||||||
float evenWidth = Surface.Boundary.Size.Width;
|
|
||||||
if (evenWidth % 2 != 0)
|
|
||||||
evenWidth++;
|
|
||||||
float evenHeight = Surface.Boundary.Size.Height;
|
|
||||||
if (evenHeight % 2 != 0)
|
|
||||||
evenHeight++;
|
|
||||||
|
|
||||||
float renderScale = (float) _renderScaleSetting.Value;
|
|
||||||
int width = Math.Max(1, MathF.Min(evenWidth * renderScale, 4096).RoundToInt());
|
|
||||||
int height = Math.Max(1, MathF.Min(evenHeight * renderScale, 4096).RoundToInt());
|
|
||||||
|
|
||||||
_texture?.Dispose();
|
|
||||||
_texture = new SKTexture(graphicsContext, width, height, renderScale, Devices);
|
|
||||||
_textureBrush.Texture = _texture;
|
|
||||||
|
|
||||||
|
|
||||||
if (!ReferenceEquals(_newGraphicsContext, Constants.ManagedGraphicsContext = _newGraphicsContext))
|
|
||||||
{
|
|
||||||
Constants.ManagedGraphicsContext?.Dispose();
|
|
||||||
Constants.ManagedGraphicsContext = _newGraphicsContext;
|
|
||||||
_newGraphicsContext = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ApplyPreferredGraphicsContext(bool forceSoftware)
|
|
||||||
{
|
|
||||||
if (forceSoftware)
|
|
||||||
{
|
|
||||||
_logger.Warning("Startup argument '--force-software-render' is applied, forcing software rendering");
|
|
||||||
UpdateGraphicsContext(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_preferredGraphicsContext.Value == "Software")
|
|
||||||
{
|
|
||||||
UpdateGraphicsContext(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
List<IGraphicsContextProvider> providers = _graphicsContextProviders.ToList();
|
|
||||||
if (!providers.Any())
|
|
||||||
{
|
|
||||||
_logger.Warning("No graphics context provider found, defaulting to software rendering");
|
|
||||||
UpdateGraphicsContext(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IManagedGraphicsContext? context = providers.FirstOrDefault(p => p.GraphicsContextName == _preferredGraphicsContext.Value)?.GetGraphicsContext();
|
|
||||||
if (context == null)
|
|
||||||
{
|
|
||||||
_logger.Warning("No graphics context named '{Context}' found, defaulting to software rendering", _preferredGraphicsContext.Value);
|
|
||||||
UpdateGraphicsContext(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateGraphicsContext(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateGraphicsContext(IManagedGraphicsContext? managedGraphicsContext)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(managedGraphicsContext, Constants.ManagedGraphicsContext))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_newGraphicsContext = managedGraphicsContext;
|
|
||||||
_texture?.Invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region EnabledDevices
|
|
||||||
|
|
||||||
public void AutoArrangeDevices()
|
|
||||||
{
|
|
||||||
bool changedRenderPaused = SetRenderPaused(true);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SurfaceArrangement surfaceArrangement = SurfaceArrangement.GetDefaultArrangement();
|
|
||||||
surfaceArrangement.Arrange(_devices);
|
|
||||||
foreach (ArtemisDevice artemisDevice in _devices)
|
|
||||||
artemisDevice.ApplyDefaultCategories();
|
|
||||||
|
|
||||||
SaveDevices();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (changedRenderPaused)
|
|
||||||
SetRenderPaused(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArtemisLayout? ApplyBestDeviceLayout(ArtemisDevice device)
|
|
||||||
{
|
|
||||||
ArtemisLayout? layout;
|
|
||||||
|
|
||||||
// Configured layout path takes precedence over all other options
|
|
||||||
if (device.CustomLayoutPath != null)
|
|
||||||
{
|
|
||||||
layout = new ArtemisLayout(device.CustomLayoutPath, LayoutSource.Configured);
|
|
||||||
if (layout.IsValid)
|
|
||||||
{
|
|
||||||
ApplyDeviceLayout(device, layout);
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for a layout provided by the user
|
|
||||||
layout = device.DeviceProvider.LoadUserLayout(device);
|
|
||||||
if (layout.IsValid)
|
|
||||||
{
|
|
||||||
ApplyDeviceLayout(device, layout);
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device.DisableDefaultLayout)
|
|
||||||
{
|
|
||||||
layout = null;
|
|
||||||
ApplyDeviceLayout(device, layout);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for a layout provided by the plugin
|
|
||||||
layout = device.DeviceProvider.LoadLayout(device);
|
|
||||||
if (layout.IsValid)
|
|
||||||
{
|
|
||||||
ApplyDeviceLayout(device, layout);
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally fall back to a default layout
|
|
||||||
layout = ArtemisLayout.GetDefaultLayout(device);
|
|
||||||
ApplyDeviceLayout(device, layout);
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout? layout)
|
|
||||||
{
|
|
||||||
if (layout == null)
|
|
||||||
{
|
|
||||||
if (device.Layout != null)
|
|
||||||
ReloadDevice(device);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layout.Source == LayoutSource.Default)
|
|
||||||
device.ApplyLayout(layout, false, false);
|
|
||||||
else
|
|
||||||
device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
|
|
||||||
|
|
||||||
UpdateLedGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReloadDevice(ArtemisDevice device)
|
|
||||||
{
|
|
||||||
// Any pending changes are otherwise lost including DisableDefaultLayout
|
|
||||||
device.ApplyToEntity();
|
|
||||||
_deviceRepository.Save(device.DeviceEntity);
|
|
||||||
|
|
||||||
DeviceProvider deviceProvider = device.DeviceProvider;
|
|
||||||
|
|
||||||
// Feels bad but need to in order to get the initial LEDs back
|
|
||||||
_pluginManagementService.DisablePluginFeature(deviceProvider, false);
|
|
||||||
Thread.Sleep(500);
|
|
||||||
_pluginManagementService.EnablePluginFeature(deviceProvider, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@ -41,6 +41,11 @@ public interface IProfileService : IArtemisService
|
|||||||
/// Gets or sets a value indicating whether the currently focused profile should receive updates.
|
/// Gets or sets a value indicating whether the currently focused profile should receive updates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool UpdateFocusProfile { get; set; }
|
bool UpdateFocusProfile { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether profiles are rendered each frame by calling their Render method
|
||||||
|
/// </summary>
|
||||||
|
bool ProfileRenderingDisabled { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a copy of the provided profile configuration.
|
/// Creates a copy of the provided profile configuration.
|
||||||
|
|||||||
@ -5,9 +5,9 @@ using System.IO;
|
|||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core.Modules;
|
using Artemis.Core.Modules;
|
||||||
|
using Artemis.Core.Services.Core;
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
using Artemis.Storage.Repositories.Interfaces;
|
using Artemis.Storage.Repositories.Interfaces;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -16,36 +16,37 @@ using SkiaSharp;
|
|||||||
|
|
||||||
namespace Artemis.Core.Services;
|
namespace Artemis.Core.Services;
|
||||||
|
|
||||||
internal class ProfileService : IProfileService
|
internal class ProfileService : IProfileService, IRenderer
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IRgbService _rgbService;
|
|
||||||
private readonly IProfileCategoryRepository _profileCategoryRepository;
|
private readonly IProfileCategoryRepository _profileCategoryRepository;
|
||||||
private readonly IPluginManagementService _pluginManagementService;
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
|
private readonly IDeviceService _deviceService;
|
||||||
private readonly List<ArtemisKeyboardKeyEventArgs> _pendingKeyboardEvents = new();
|
private readonly List<ArtemisKeyboardKeyEventArgs> _pendingKeyboardEvents = new();
|
||||||
private readonly List<ProfileCategory> _profileCategories;
|
private readonly List<ProfileCategory> _profileCategories;
|
||||||
private readonly IProfileRepository _profileRepository;
|
private readonly IProfileRepository _profileRepository;
|
||||||
private readonly List<Exception> _renderExceptions = new();
|
private readonly List<Exception> _renderExceptions = new();
|
||||||
private readonly List<Exception> _updateExceptions = new();
|
private readonly List<Exception> _updateExceptions = new();
|
||||||
|
|
||||||
private DateTime _lastRenderExceptionLog;
|
private DateTime _lastRenderExceptionLog;
|
||||||
private DateTime _lastUpdateExceptionLog;
|
private DateTime _lastUpdateExceptionLog;
|
||||||
|
|
||||||
public ProfileService(ILogger logger,
|
public ProfileService(ILogger logger,
|
||||||
IRgbService rgbService,
|
|
||||||
IProfileCategoryRepository profileCategoryRepository,
|
IProfileCategoryRepository profileCategoryRepository,
|
||||||
IPluginManagementService pluginManagementService,
|
IPluginManagementService pluginManagementService,
|
||||||
IInputService inputService,
|
IInputService inputService,
|
||||||
|
IDeviceService deviceService,
|
||||||
|
IRenderService renderService,
|
||||||
IProfileRepository profileRepository)
|
IProfileRepository profileRepository)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_rgbService = rgbService;
|
|
||||||
_profileCategoryRepository = profileCategoryRepository;
|
_profileCategoryRepository = profileCategoryRepository;
|
||||||
_pluginManagementService = pluginManagementService;
|
_pluginManagementService = pluginManagementService;
|
||||||
|
_deviceService = deviceService;
|
||||||
_profileRepository = profileRepository;
|
_profileRepository = profileRepository;
|
||||||
_profileCategories = new List<ProfileCategory>(_profileCategoryRepository.GetAll().Select(c => new ProfileCategory(c)).OrderBy(c => c.Order));
|
_profileCategories = new List<ProfileCategory>(_profileCategoryRepository.GetAll().Select(c => new ProfileCategory(c)).OrderBy(c => c.Order));
|
||||||
|
|
||||||
_rgbService.LedsChanged += RgbServiceOnLedsChanged;
|
_deviceService.LedsChanged += DeviceServiceOnLedsChanged;
|
||||||
_pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureToggled;
|
_pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureToggled;
|
||||||
_pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureToggled;
|
_pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureToggled;
|
||||||
|
|
||||||
@ -54,11 +55,15 @@ internal class ProfileService : IProfileService
|
|||||||
if (!_profileCategories.Any())
|
if (!_profileCategories.Any())
|
||||||
CreateDefaultProfileCategories();
|
CreateDefaultProfileCategories();
|
||||||
UpdateModules();
|
UpdateModules();
|
||||||
|
|
||||||
|
renderService.Renderers.Add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProfileConfiguration? FocusProfile { get; set; }
|
public ProfileConfiguration? FocusProfile { get; set; }
|
||||||
public ProfileElement? FocusProfileElement { get; set; }
|
public ProfileElement? FocusProfileElement { get; set; }
|
||||||
public bool UpdateFocusProfile { get; set; }
|
public bool UpdateFocusProfile { get; set; }
|
||||||
|
|
||||||
|
public bool ProfileRenderingDisabled { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void UpdateProfiles(double deltaTime)
|
public void UpdateProfiles(double deltaTime)
|
||||||
@ -182,6 +187,21 @@ internal class ProfileService : IProfileService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Render(SKCanvas canvas, double delta)
|
||||||
|
{
|
||||||
|
if (ProfileRenderingDisabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UpdateProfiles(delta);
|
||||||
|
RenderProfiles(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void PostRender(SKTexture texture)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void LoadProfileConfigurationIcon(ProfileConfiguration profileConfiguration)
|
public void LoadProfileConfigurationIcon(ProfileConfiguration profileConfiguration)
|
||||||
@ -235,7 +255,7 @@ internal class ProfileService : IProfileService
|
|||||||
throw new ArtemisCoreException($"Cannot find profile named: {profileConfiguration.Name} ID: {profileConfiguration.Entity.ProfileId}");
|
throw new ArtemisCoreException($"Cannot find profile named: {profileConfiguration.Name} ID: {profileConfiguration.Entity.ProfileId}");
|
||||||
|
|
||||||
Profile profile = new(profileConfiguration, profileEntity);
|
Profile profile = new(profileConfiguration, profileEntity);
|
||||||
profile.PopulateLeds(_rgbService.EnabledDevices);
|
profile.PopulateLeds(_deviceService.EnabledDevices);
|
||||||
|
|
||||||
if (profile.IsFreshImport)
|
if (profile.IsFreshImport)
|
||||||
{
|
{
|
||||||
@ -523,7 +543,7 @@ internal class ProfileService : IProfileService
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void AdaptProfile(Profile profile)
|
public void AdaptProfile(Profile profile)
|
||||||
{
|
{
|
||||||
List<ArtemisDevice> devices = _rgbService.EnabledDevices.ToList();
|
List<ArtemisDevice> devices = _deviceService.EnabledDevices.ToList();
|
||||||
foreach (Layer layer in profile.GetAllLayers())
|
foreach (Layer layer in profile.GetAllLayers())
|
||||||
layer.Adapter.Adapt(devices);
|
layer.Adapter.Adapt(devices);
|
||||||
|
|
||||||
@ -552,7 +572,7 @@ internal class ProfileService : IProfileService
|
|||||||
foreach (ProfileConfiguration profileConfiguration in ProfileConfigurations)
|
foreach (ProfileConfiguration profileConfiguration in ProfileConfigurations)
|
||||||
{
|
{
|
||||||
if (profileConfiguration.Profile == null) continue;
|
if (profileConfiguration.Profile == null) continue;
|
||||||
profileConfiguration.Profile.PopulateLeds(_rgbService.EnabledDevices);
|
profileConfiguration.Profile.PopulateLeds(_deviceService.EnabledDevices);
|
||||||
|
|
||||||
if (!profileConfiguration.Profile.IsFreshImport) continue;
|
if (!profileConfiguration.Profile.IsFreshImport) continue;
|
||||||
_logger.Debug("Profile is a fresh import, adapting to surface - {profile}", profileConfiguration.Profile);
|
_logger.Debug("Profile is a fresh import, adapting to surface - {profile}", profileConfiguration.Profile);
|
||||||
@ -573,7 +593,7 @@ internal class ProfileService : IProfileService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RgbServiceOnLedsChanged(object? sender, EventArgs e)
|
private void DeviceServiceOnLedsChanged(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ActiveProfilesPopulateLeds();
|
ActiveProfilesPopulateLeds();
|
||||||
}
|
}
|
||||||
|
|||||||
35
src/Artemis.Core/Utilities/RenderScale.cs
Normal file
35
src/Artemis.Core/Utilities/RenderScale.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core;
|
||||||
|
|
||||||
|
internal static class RenderScale
|
||||||
|
{
|
||||||
|
internal static int RenderScaleMultiplier { get; private set; } = 2;
|
||||||
|
|
||||||
|
internal static event EventHandler? RenderScaleMultiplierChanged;
|
||||||
|
|
||||||
|
internal static void SetRenderScaleMultiplier(int renderScaleMultiplier)
|
||||||
|
{
|
||||||
|
RenderScaleMultiplier = renderScaleMultiplier;
|
||||||
|
RenderScaleMultiplierChanged?.Invoke(null, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static SKRectI CreateScaleCompatibleRect(float x, float y, float width, float height)
|
||||||
|
{
|
||||||
|
int roundX = (int) MathF.Floor(x);
|
||||||
|
int roundY = (int) MathF.Floor(y);
|
||||||
|
int roundWidth = (int) MathF.Ceiling(width);
|
||||||
|
int roundHeight = (int) MathF.Ceiling(height);
|
||||||
|
|
||||||
|
if (RenderScaleMultiplier == 1)
|
||||||
|
return SKRectI.Create(roundX, roundY, roundWidth, roundHeight);
|
||||||
|
|
||||||
|
return SKRectI.Create(
|
||||||
|
roundX - roundX % RenderScaleMultiplier,
|
||||||
|
roundY - roundY % RenderScaleMultiplier,
|
||||||
|
roundWidth - roundWidth % RenderScaleMultiplier,
|
||||||
|
roundHeight - roundHeight % RenderScaleMultiplier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -156,28 +156,4 @@ public static class Utilities
|
|||||||
{
|
{
|
||||||
UpdateRequested?.Invoke(null, e);
|
UpdateRequested?.Invoke(null, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Scaling
|
|
||||||
|
|
||||||
internal static int RenderScaleMultiplier { get; set; } = 2;
|
|
||||||
|
|
||||||
internal static SKRectI CreateScaleCompatibleRect(float x, float y, float width, float height)
|
|
||||||
{
|
|
||||||
int roundX = (int) MathF.Floor(x);
|
|
||||||
int roundY = (int) MathF.Floor(y);
|
|
||||||
int roundWidth = (int) MathF.Ceiling(width);
|
|
||||||
int roundHeight = (int) MathF.Ceiling(height);
|
|
||||||
|
|
||||||
if (RenderScaleMultiplier == 1)
|
|
||||||
return SKRectI.Create(roundX, roundY, roundWidth, roundHeight);
|
|
||||||
|
|
||||||
return SKRectI.Create(
|
|
||||||
roundX - roundX % RenderScaleMultiplier,
|
|
||||||
roundY - roundY % RenderScaleMultiplier,
|
|
||||||
roundWidth - roundWidth % RenderScaleMultiplier,
|
|
||||||
roundHeight - roundHeight % RenderScaleMultiplier
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
@ -27,8 +27,8 @@ namespace Artemis.UI.Shared;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DeviceVisualizer : Control
|
public class DeviceVisualizer : Control
|
||||||
{
|
{
|
||||||
internal static readonly Dictionary<ArtemisDevice, RenderTargetBitmap?> BitmapCache = new();
|
internal static readonly Dictionary<string, RenderTargetBitmap?> BitmapCache = new();
|
||||||
private readonly ICoreService _coreService;
|
private readonly IRenderService _renderService;
|
||||||
private readonly List<DeviceVisualizerLed> _deviceVisualizerLeds;
|
private readonly List<DeviceVisualizerLed> _deviceVisualizerLeds;
|
||||||
|
|
||||||
private Rect _deviceBounds;
|
private Rect _deviceBounds;
|
||||||
@ -40,7 +40,7 @@ public class DeviceVisualizer : Control
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public DeviceVisualizer()
|
public DeviceVisualizer()
|
||||||
{
|
{
|
||||||
_coreService = UI.Locator.Resolve<ICoreService>();
|
_renderService = UI.Locator.Resolve<IRenderService>();
|
||||||
_deviceVisualizerLeds = new List<DeviceVisualizerLed>();
|
_deviceVisualizerLeds = new List<DeviceVisualizerLed>();
|
||||||
|
|
||||||
PointerReleased += OnPointerReleased;
|
PointerReleased += OnPointerReleased;
|
||||||
@ -195,16 +195,6 @@ public class DeviceVisualizer : Control
|
|||||||
SetupForDevice();
|
SetupForDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Invoke(async () =>
|
|
||||||
{
|
|
||||||
if (Device != null)
|
|
||||||
BitmapCache.Remove(Device);
|
|
||||||
await SetupForDevice();
|
|
||||||
}, DispatcherPriority.Background);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeviceUpdated(object? sender, EventArgs e)
|
private void DeviceUpdated(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Invoke(SetupForDevice, DispatcherPriority.Background);
|
Dispatcher.UIThread.Invoke(SetupForDevice, DispatcherPriority.Background);
|
||||||
@ -251,7 +241,6 @@ public class DeviceVisualizer : Control
|
|||||||
{
|
{
|
||||||
if (Device != null)
|
if (Device != null)
|
||||||
{
|
{
|
||||||
Device.RgbDevice.PropertyChanged -= DevicePropertyChanged;
|
|
||||||
Device.DeviceUpdated -= DeviceUpdated;
|
Device.DeviceUpdated -= DeviceUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +250,7 @@ public class DeviceVisualizer : Control
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
_coreService.FrameRendered += OnFrameRendered;
|
_renderService.FrameRendered += OnFrameRendered;
|
||||||
|
|
||||||
base.OnAttachedToLogicalTree(e);
|
base.OnAttachedToLogicalTree(e);
|
||||||
}
|
}
|
||||||
@ -269,7 +258,7 @@ public class DeviceVisualizer : Control
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
|
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
_coreService.FrameRendered -= OnFrameRendered;
|
_renderService.FrameRendered -= OnFrameRendered;
|
||||||
|
|
||||||
base.OnDetachedFromLogicalTree(e);
|
base.OnDetachedFromLogicalTree(e);
|
||||||
}
|
}
|
||||||
@ -283,7 +272,6 @@ public class DeviceVisualizer : Control
|
|||||||
|
|
||||||
if (_oldDevice != null)
|
if (_oldDevice != null)
|
||||||
{
|
{
|
||||||
_oldDevice.RgbDevice.PropertyChanged -= DevicePropertyChanged;
|
|
||||||
_oldDevice.DeviceUpdated -= DeviceUpdated;
|
_oldDevice.DeviceUpdated -= DeviceUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +282,6 @@ public class DeviceVisualizer : Control
|
|||||||
_deviceBounds = MeasureDevice();
|
_deviceBounds = MeasureDevice();
|
||||||
_loading = true;
|
_loading = true;
|
||||||
|
|
||||||
Device.RgbDevice.PropertyChanged += DevicePropertyChanged;
|
|
||||||
Device.DeviceUpdated += DeviceUpdated;
|
Device.DeviceUpdated += DeviceUpdated;
|
||||||
|
|
||||||
// Create all the LEDs
|
// Create all the LEDs
|
||||||
@ -321,12 +308,15 @@ public class DeviceVisualizer : Control
|
|||||||
|
|
||||||
private RenderTargetBitmap? GetDeviceImage(ArtemisDevice device)
|
private RenderTargetBitmap? GetDeviceImage(ArtemisDevice device)
|
||||||
{
|
{
|
||||||
if (BitmapCache.TryGetValue(device, out RenderTargetBitmap? existingBitmap))
|
string? path = device.Layout?.Image?.LocalPath;
|
||||||
|
if (path == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (BitmapCache.TryGetValue(path, out RenderTargetBitmap? existingBitmap))
|
||||||
return existingBitmap;
|
return existingBitmap;
|
||||||
|
if (!File.Exists(path))
|
||||||
if (device.Layout?.Image == null || !File.Exists(device.Layout.Image.LocalPath))
|
|
||||||
{
|
{
|
||||||
BitmapCache[device] = null;
|
BitmapCache[path] = null;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +325,7 @@ public class DeviceVisualizer : Control
|
|||||||
RenderTargetBitmap renderTargetBitmap = new(new PixelSize((int) device.RgbDevice.ActualSize.Width * 2, (int) device.RgbDevice.ActualSize.Height * 2));
|
RenderTargetBitmap renderTargetBitmap = new(new PixelSize((int) device.RgbDevice.ActualSize.Width * 2, (int) device.RgbDevice.ActualSize.Height * 2));
|
||||||
|
|
||||||
using DrawingContext context = renderTargetBitmap.CreateDrawingContext();
|
using DrawingContext context = renderTargetBitmap.CreateDrawingContext();
|
||||||
using Bitmap bitmap = new(device.Layout.Image.LocalPath);
|
using Bitmap bitmap = new(path);
|
||||||
using Bitmap scaledBitmap = bitmap.CreateScaledBitmap(renderTargetBitmap.PixelSize);
|
using Bitmap scaledBitmap = bitmap.CreateScaledBitmap(renderTargetBitmap.PixelSize);
|
||||||
|
|
||||||
context.DrawImage(scaledBitmap, new Rect(scaledBitmap.Size));
|
context.DrawImage(scaledBitmap, new Rect(scaledBitmap.Size));
|
||||||
@ -345,7 +335,7 @@ public class DeviceVisualizer : Control
|
|||||||
deviceVisualizerLed.DrawBitmap(context, 2 * device.Scale);
|
deviceVisualizerLed.DrawBitmap(context, 2 * device.Scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
BitmapCache[device] = renderTargetBitmap;
|
BitmapCache[path] = renderTargetBitmap;
|
||||||
return renderTargetBitmap;
|
return renderTargetBitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@ internal class ProfileEditorService : IProfileEditorService
|
|||||||
private readonly ILayerBrushService _layerBrushService;
|
private readonly ILayerBrushService _layerBrushService;
|
||||||
private readonly BehaviorSubject<ILayerProperty?> _layerPropertySubject = new(null);
|
private readonly BehaviorSubject<ILayerProperty?> _layerPropertySubject = new(null);
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly IDeviceService _deviceService;
|
||||||
private readonly IModuleService _moduleService;
|
private readonly IModuleService _moduleService;
|
||||||
private readonly BehaviorSubject<int> _pixelsPerSecondSubject = new(120);
|
private readonly BehaviorSubject<int> _pixelsPerSecondSubject = new(120);
|
||||||
private readonly BehaviorSubject<bool> _playingSubject = new(false);
|
private readonly BehaviorSubject<bool> _playingSubject = new(false);
|
||||||
@ -28,7 +29,6 @@ internal class ProfileEditorService : IProfileEditorService
|
|||||||
private readonly Dictionary<ProfileConfiguration, ProfileEditorHistory> _profileEditorHistories = new();
|
private readonly Dictionary<ProfileConfiguration, ProfileEditorHistory> _profileEditorHistories = new();
|
||||||
private readonly BehaviorSubject<RenderProfileElement?> _profileElementSubject = new(null);
|
private readonly BehaviorSubject<RenderProfileElement?> _profileElementSubject = new(null);
|
||||||
private readonly IProfileService _profileService;
|
private readonly IProfileService _profileService;
|
||||||
private readonly IRgbService _rgbService;
|
|
||||||
private readonly SourceList<ILayerPropertyKeyframe> _selectedKeyframes;
|
private readonly SourceList<ILayerPropertyKeyframe> _selectedKeyframes;
|
||||||
private readonly BehaviorSubject<bool> _suspendedEditingSubject = new(false);
|
private readonly BehaviorSubject<bool> _suspendedEditingSubject = new(false);
|
||||||
private readonly BehaviorSubject<TimeSpan> _timeSubject = new(TimeSpan.Zero);
|
private readonly BehaviorSubject<TimeSpan> _timeSubject = new(TimeSpan.Zero);
|
||||||
@ -36,17 +36,17 @@ internal class ProfileEditorService : IProfileEditorService
|
|||||||
private ProfileEditorCommandScope? _profileEditorHistoryScope;
|
private ProfileEditorCommandScope? _profileEditorHistoryScope;
|
||||||
|
|
||||||
public ProfileEditorService(ILogger logger,
|
public ProfileEditorService(ILogger logger,
|
||||||
|
IDeviceService deviceService,
|
||||||
IProfileService profileService,
|
IProfileService profileService,
|
||||||
IModuleService moduleService,
|
IModuleService moduleService,
|
||||||
IRgbService rgbService,
|
|
||||||
ILayerBrushService layerBrushService,
|
ILayerBrushService layerBrushService,
|
||||||
IMainWindowService mainWindowService,
|
IMainWindowService mainWindowService,
|
||||||
IWindowService windowService)
|
IWindowService windowService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_deviceService = deviceService;
|
||||||
_profileService = profileService;
|
_profileService = profileService;
|
||||||
_moduleService = moduleService;
|
_moduleService = moduleService;
|
||||||
_rgbService = rgbService;
|
|
||||||
_layerBrushService = layerBrushService;
|
_layerBrushService = layerBrushService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
|
|
||||||
@ -369,7 +369,7 @@ internal class ProfileEditorService : IProfileEditorService
|
|||||||
Layer layer = new(targetLayer.Parent, targetLayer.GetNewLayerName());
|
Layer layer = new(targetLayer.Parent, targetLayer.GetNewLayerName());
|
||||||
_layerBrushService.ApplyDefaultBrush(layer);
|
_layerBrushService.ApplyDefaultBrush(layer);
|
||||||
|
|
||||||
layer.AddLeds(_rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
layer.AddLeds(_deviceService.EnabledDevices.SelectMany(d => d.Leds));
|
||||||
ExecuteCommand(new AddProfileElement(layer, targetLayer.Parent, targetLayer.Order));
|
ExecuteCommand(new AddProfileElement(layer, targetLayer.Parent, targetLayer.Order));
|
||||||
|
|
||||||
return layer;
|
return layer;
|
||||||
@ -379,7 +379,7 @@ internal class ProfileEditorService : IProfileEditorService
|
|||||||
Layer layer = new(target, target.GetNewLayerName());
|
Layer layer = new(target, target.GetNewLayerName());
|
||||||
_layerBrushService.ApplyDefaultBrush(layer);
|
_layerBrushService.ApplyDefaultBrush(layer);
|
||||||
|
|
||||||
layer.AddLeds(_rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
layer.AddLeds(_deviceService.EnabledDevices.SelectMany(d => d.Leds));
|
||||||
ExecuteCommand(new AddProfileElement(layer, target, 0));
|
ExecuteCommand(new AddProfileElement(layer, target, 0));
|
||||||
|
|
||||||
return layer;
|
return layer;
|
||||||
|
|||||||
@ -14,7 +14,7 @@ namespace Artemis.UI.Screens.Debugger.Performance;
|
|||||||
|
|
||||||
public class PerformanceDebugViewModel : ActivatableViewModelBase
|
public class PerformanceDebugViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
private readonly ICoreService _coreService;
|
private readonly IRenderService _renderService;
|
||||||
private readonly IPluginManagementService _pluginManagementService;
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
private readonly DispatcherTimer _updateTimer;
|
private readonly DispatcherTimer _updateTimer;
|
||||||
private double _currentFps;
|
private double _currentFps;
|
||||||
@ -22,9 +22,9 @@ public class PerformanceDebugViewModel : ActivatableViewModelBase
|
|||||||
private int _renderHeight;
|
private int _renderHeight;
|
||||||
private int _renderWidth;
|
private int _renderWidth;
|
||||||
|
|
||||||
public PerformanceDebugViewModel(ICoreService coreService, IPluginManagementService pluginManagementService)
|
public PerformanceDebugViewModel(IRenderService renderService, IPluginManagementService pluginManagementService)
|
||||||
{
|
{
|
||||||
_coreService = coreService;
|
_renderService = renderService;
|
||||||
_pluginManagementService = pluginManagementService;
|
_pluginManagementService = pluginManagementService;
|
||||||
_updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Background, (_, _) => Update());
|
_updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Background, (_, _) => Update());
|
||||||
|
|
||||||
@ -87,12 +87,12 @@ public class PerformanceDebugViewModel : ActivatableViewModelBase
|
|||||||
private void HandleActivation()
|
private void HandleActivation()
|
||||||
{
|
{
|
||||||
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
||||||
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
_renderService.FrameRendered += RenderServiceOnFrameRendered;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleDeactivation()
|
private void HandleDeactivation()
|
||||||
{
|
{
|
||||||
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
|
_renderService.FrameRendered -= RenderServiceOnFrameRendered;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopulateItems()
|
private void PopulateItems()
|
||||||
@ -116,9 +116,9 @@ public class PerformanceDebugViewModel : ActivatableViewModelBase
|
|||||||
viewModel.Update();
|
viewModel.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
private void RenderServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
||||||
{
|
{
|
||||||
CurrentFps = _coreService.FrameRate;
|
CurrentFps = _renderService.FrameRate;
|
||||||
SKImageInfo bitmapInfo = e.Texture.ImageInfo;
|
SKImageInfo bitmapInfo = e.Texture.ImageInfo;
|
||||||
|
|
||||||
RenderHeight = bitmapInfo.Height;
|
RenderHeight = bitmapInfo.Height;
|
||||||
|
|||||||
@ -11,7 +11,7 @@ namespace Artemis.UI.Screens.Debugger.Render;
|
|||||||
|
|
||||||
public class RenderDebugViewModel : ActivatableViewModelBase
|
public class RenderDebugViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
private readonly ICoreService _coreService;
|
private readonly IRenderService _renderService;
|
||||||
private double _currentFps;
|
private double _currentFps;
|
||||||
|
|
||||||
private Bitmap? _currentFrame;
|
private Bitmap? _currentFrame;
|
||||||
@ -20,9 +20,9 @@ public class RenderDebugViewModel : ActivatableViewModelBase
|
|||||||
private int _renderHeight;
|
private int _renderHeight;
|
||||||
private int _renderWidth;
|
private int _renderWidth;
|
||||||
|
|
||||||
public RenderDebugViewModel(ICoreService coreService)
|
public RenderDebugViewModel(IRenderService renderService)
|
||||||
{
|
{
|
||||||
_coreService = coreService;
|
_renderService = renderService;
|
||||||
|
|
||||||
DisplayName = "Rendering";
|
DisplayName = "Rendering";
|
||||||
|
|
||||||
@ -66,17 +66,17 @@ public class RenderDebugViewModel : ActivatableViewModelBase
|
|||||||
private void HandleActivation()
|
private void HandleActivation()
|
||||||
{
|
{
|
||||||
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
||||||
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
_renderService.FrameRendered += RenderServiceOnFrameRendered;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleDeactivation()
|
private void HandleDeactivation()
|
||||||
{
|
{
|
||||||
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
|
_renderService.FrameRendered -= RenderServiceOnFrameRendered;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
private void RenderServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
||||||
{
|
{
|
||||||
CurrentFps = _coreService.FrameRate;
|
CurrentFps = _renderService.FrameRate;
|
||||||
using SKImage skImage = e.Texture.Surface.Snapshot();
|
using SKImage skImage = e.Texture.Surface.Snapshot();
|
||||||
SKImageInfo bitmapInfo = e.Texture.ImageInfo;
|
SKImageInfo bitmapInfo = e.Texture.ImageInfo;
|
||||||
|
|
||||||
|
|||||||
@ -17,20 +17,16 @@ namespace Artemis.UI.Screens.Device;
|
|||||||
public class DeviceDetectInputViewModel : ContentDialogViewModelBase
|
public class DeviceDetectInputViewModel : ContentDialogViewModelBase
|
||||||
{
|
{
|
||||||
private readonly IInputService _inputService;
|
private readonly IInputService _inputService;
|
||||||
private readonly ListLedGroup _ledGroup;
|
|
||||||
private readonly INotificationService _notificationService;
|
private readonly INotificationService _notificationService;
|
||||||
private readonly IRgbService _rgbService;
|
|
||||||
|
|
||||||
public DeviceDetectInputViewModel(ArtemisDevice device, IInputService inputService, INotificationService notificationService, IRgbService rgbService)
|
public DeviceDetectInputViewModel(ArtemisDevice device, IInputService inputService, INotificationService notificationService, IRenderService renderService)
|
||||||
{
|
{
|
||||||
_inputService = inputService;
|
_inputService = inputService;
|
||||||
_notificationService = notificationService;
|
_notificationService = notificationService;
|
||||||
_rgbService = rgbService;
|
|
||||||
|
|
||||||
Device = device;
|
Device = device;
|
||||||
|
|
||||||
// Create a LED group way at the top
|
// Create a LED group way at the top
|
||||||
_ledGroup = new ListLedGroup(_rgbService.Surface, Device.Leds.Select(l => l.RgbLed))
|
ListLedGroup ledGroup = new(renderService.Surface, Device.Leds.Select(l => l.RgbLed))
|
||||||
{
|
{
|
||||||
Brush = new SolidColorBrush(new Color(255, 255, 0)),
|
Brush = new SolidColorBrush(new Color(255, 255, 0)),
|
||||||
ZIndex = 999
|
ZIndex = 999
|
||||||
@ -46,7 +42,7 @@ public class DeviceDetectInputViewModel : ContentDialogViewModelBase
|
|||||||
Disposable.Create(() =>
|
Disposable.Create(() =>
|
||||||
{
|
{
|
||||||
_inputService.StopIdentify();
|
_inputService.StopIdentify();
|
||||||
_ledGroup.Detach();
|
ledGroup.Detach();
|
||||||
}).DisposeWith(disposables);
|
}).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
|
|||||||
private readonly IDeviceVmFactory _deviceVmFactory;
|
private readonly IDeviceVmFactory _deviceVmFactory;
|
||||||
private ArtemisDevice _device;
|
private ArtemisDevice _device;
|
||||||
|
|
||||||
public DevicePropertiesViewModel(ArtemisDevice device, ICoreService coreService, IRgbService rgbService, IDeviceVmFactory deviceVmFactory)
|
public DevicePropertiesViewModel(ArtemisDevice device, IRenderService renderService, IDeviceService deviceService, IDeviceVmFactory deviceVmFactory)
|
||||||
{
|
{
|
||||||
_deviceVmFactory = deviceVmFactory;
|
_deviceVmFactory = deviceVmFactory;
|
||||||
_device = device;
|
_device = device;
|
||||||
@ -28,10 +28,15 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
|
|||||||
AddTabs();
|
AddTabs();
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
|
deviceService.DeviceAdded += DeviceServiceOnDeviceAdded;
|
||||||
rgbService.DeviceRemoved += RgbServiceOnDeviceRemoved;
|
deviceService.DeviceRemoved += DeviceServiceOnDeviceRemoved;
|
||||||
coreService.FrameRendering += CoreServiceOnFrameRendering;
|
renderService.FrameRendering += RenderServiceOnFrameRendering;
|
||||||
Disposable.Create(() => coreService.FrameRendering -= CoreServiceOnFrameRendering).DisposeWith(d);
|
Disposable.Create(() =>
|
||||||
|
{
|
||||||
|
deviceService.DeviceAdded -= DeviceServiceOnDeviceAdded;
|
||||||
|
deviceService.DeviceRemoved -= DeviceServiceOnDeviceRemoved;
|
||||||
|
renderService.FrameRendering -= RenderServiceOnFrameRendering;
|
||||||
|
}).DisposeWith(d);
|
||||||
});
|
});
|
||||||
|
|
||||||
ClearSelectedLeds = ReactiveCommand.Create(ExecuteClearSelectedLeds);
|
ClearSelectedLeds = ReactiveCommand.Create(ExecuteClearSelectedLeds);
|
||||||
@ -47,7 +52,7 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
|
|||||||
public ObservableCollection<ActivatableViewModelBase> Tabs { get; }
|
public ObservableCollection<ActivatableViewModelBase> Tabs { get; }
|
||||||
public ReactiveCommand<Unit, Unit> ClearSelectedLeds { get; }
|
public ReactiveCommand<Unit, Unit> ClearSelectedLeds { get; }
|
||||||
|
|
||||||
private void RgbServiceOnDeviceAdded(object? sender, DeviceEventArgs e)
|
private void DeviceServiceOnDeviceAdded(object? sender, DeviceEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Device.Identifier != Device.Identifier || Device == e.Device)
|
if (e.Device.Identifier != Device.Identifier || Device == e.Device)
|
||||||
return;
|
return;
|
||||||
@ -56,7 +61,7 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
|
|||||||
AddTabs();
|
AddTabs();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RgbServiceOnDeviceRemoved(object? sender, DeviceEventArgs e)
|
private void DeviceServiceOnDeviceRemoved(object? sender, DeviceEventArgs e)
|
||||||
{
|
{
|
||||||
Tabs.Clear();
|
Tabs.Clear();
|
||||||
SelectedLeds.Clear();
|
SelectedLeds.Clear();
|
||||||
@ -76,7 +81,7 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
|
|||||||
SelectedLeds.Clear();
|
SelectedLeds.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CoreServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)
|
private void RenderServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)
|
||||||
{
|
{
|
||||||
if (!SelectedLeds.Any())
|
if (!SelectedLeds.Any())
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -18,18 +18,15 @@ public class DeviceSettingsViewModel : ActivatableViewModelBase
|
|||||||
private readonly IDeviceService _deviceService;
|
private readonly IDeviceService _deviceService;
|
||||||
private readonly DevicesTabViewModel _devicesTabViewModel;
|
private readonly DevicesTabViewModel _devicesTabViewModel;
|
||||||
private readonly IDeviceVmFactory _deviceVmFactory;
|
private readonly IDeviceVmFactory _deviceVmFactory;
|
||||||
private readonly IRgbService _rgbService;
|
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private bool _togglingDevice;
|
private bool _togglingDevice;
|
||||||
|
|
||||||
public DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel, IDeviceService deviceService, IWindowService windowService, IDeviceVmFactory deviceVmFactory,
|
public DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel, IDeviceService deviceService, IWindowService windowService, IDeviceVmFactory deviceVmFactory)
|
||||||
IRgbService rgbService)
|
|
||||||
{
|
{
|
||||||
_devicesTabViewModel = devicesTabViewModel;
|
_devicesTabViewModel = devicesTabViewModel;
|
||||||
_deviceService = deviceService;
|
_deviceService = deviceService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_deviceVmFactory = deviceVmFactory;
|
_deviceVmFactory = deviceVmFactory;
|
||||||
_rgbService = rgbService;
|
|
||||||
Device = device;
|
Device = device;
|
||||||
|
|
||||||
Type = Device.DeviceType.ToString().Humanize();
|
Type = Device.DeviceType.ToString().Humanize();
|
||||||
@ -87,7 +84,7 @@ public class DeviceSettingsViewModel : ActivatableViewModelBase
|
|||||||
.ShowAsync();
|
.ShowAsync();
|
||||||
|
|
||||||
if (viewModel.MadeChanges)
|
if (viewModel.MadeChanges)
|
||||||
_rgbService.SaveDevice(Device);
|
_deviceService.SaveDevice(Device);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateIsDeviceEnabled(bool value)
|
private async Task UpdateIsDeviceEnabled(bool value)
|
||||||
@ -103,9 +100,9 @@ public class DeviceSettingsViewModel : ActivatableViewModelBase
|
|||||||
value = !await _devicesTabViewModel.ShowDeviceDisableDialog();
|
value = !await _devicesTabViewModel.ShowDeviceDisableDialog();
|
||||||
|
|
||||||
if (value)
|
if (value)
|
||||||
_rgbService.EnableDevice(Device);
|
_deviceService.EnableDevice(Device);
|
||||||
else
|
else
|
||||||
_rgbService.DisableDevice(Device);
|
_deviceService.DisableDevice(Device);
|
||||||
|
|
||||||
this.RaisePropertyChanged(nameof(IsDeviceEnabled));
|
this.RaisePropertyChanged(nameof(IsDeviceEnabled));
|
||||||
SaveDevice();
|
SaveDevice();
|
||||||
@ -118,6 +115,6 @@ public class DeviceSettingsViewModel : ActivatableViewModelBase
|
|||||||
|
|
||||||
private void SaveDevice()
|
private void SaveDevice()
|
||||||
{
|
{
|
||||||
_rgbService.SaveDevice(Device);
|
_deviceService.SaveDevice(Device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -20,7 +20,8 @@ namespace Artemis.UI.Screens.Device;
|
|||||||
public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
private readonly ICoreService _coreService;
|
private readonly ICoreService _coreService;
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IDeviceService _deviceService;
|
||||||
|
private readonly IRenderService _renderService;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private readonly List<DeviceCategory> _categories;
|
private readonly List<DeviceCategory> _categories;
|
||||||
|
|
||||||
@ -39,10 +40,11 @@ public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
|||||||
private SKColor _currentColor;
|
private SKColor _currentColor;
|
||||||
private bool _displayOnDevices;
|
private bool _displayOnDevices;
|
||||||
|
|
||||||
public DeviceGeneralTabViewModel(ArtemisDevice device, ICoreService coreService, IRgbService rgbService, IWindowService windowService)
|
public DeviceGeneralTabViewModel(ArtemisDevice device, ICoreService coreService, IDeviceService deviceService, IRenderService renderService, IWindowService windowService)
|
||||||
{
|
{
|
||||||
_coreService = coreService;
|
_coreService = coreService;
|
||||||
_rgbService = rgbService;
|
_deviceService = deviceService;
|
||||||
|
_renderService = renderService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_categories = new List<DeviceCategory>(device.Categories);
|
_categories = new List<DeviceCategory>(device.Categories);
|
||||||
|
|
||||||
@ -66,11 +68,11 @@ public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
|||||||
|
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
_coreService.FrameRendering += OnFrameRendering;
|
_renderService.FrameRendering += OnFrameRendering;
|
||||||
|
|
||||||
Disposable.Create(() =>
|
Disposable.Create(() =>
|
||||||
{
|
{
|
||||||
_coreService.FrameRendering -= OnFrameRendering;
|
_renderService.FrameRendering -= OnFrameRendering;
|
||||||
Apply();
|
Apply();
|
||||||
}).DisposeWith(d);
|
}).DisposeWith(d);
|
||||||
});
|
});
|
||||||
@ -191,17 +193,12 @@ public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
await Task.Delay(400);
|
await Task.Delay(400);
|
||||||
_rgbService.SaveDevice(Device);
|
_deviceService.SaveDevice(Device);
|
||||||
_rgbService.ApplyBestDeviceLayout(Device);
|
_deviceService.ApplyDeviceLayout(Device, Device.GetBestDeviceLayout());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Apply()
|
private void Apply()
|
||||||
{
|
{
|
||||||
// TODO: Validation
|
|
||||||
|
|
||||||
_coreService.ProfileRenderingDisabled = true;
|
|
||||||
Thread.Sleep(100);
|
|
||||||
|
|
||||||
Device.X = X;
|
Device.X = X;
|
||||||
Device.Y = Y;
|
Device.Y = Y;
|
||||||
Device.Scale = Scale;
|
Device.Scale = Scale;
|
||||||
@ -213,9 +210,7 @@ public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
|||||||
foreach (DeviceCategory deviceCategory in _categories)
|
foreach (DeviceCategory deviceCategory in _categories)
|
||||||
Device.Categories.Add(deviceCategory);
|
Device.Categories.Add(deviceCategory);
|
||||||
|
|
||||||
_rgbService.SaveDevice(Device);
|
_deviceService.SaveDevice(Device);
|
||||||
|
|
||||||
_coreService.ProfileRenderingDisabled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyScaling()
|
public void ApplyScaling()
|
||||||
@ -224,7 +219,7 @@ public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
|||||||
Device.GreenScale = GreenScale / 100f;
|
Device.GreenScale = GreenScale / 100f;
|
||||||
Device.BlueScale = BlueScale / 100f;
|
Device.BlueScale = BlueScale / 100f;
|
||||||
|
|
||||||
_rgbService.FlushLeds = true;
|
_renderService.FlushLeds = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetScaling()
|
public void ResetScaling()
|
||||||
|
|||||||
@ -22,20 +22,13 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
|||||||
{
|
{
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private readonly INotificationService _notificationService;
|
private readonly INotificationService _notificationService;
|
||||||
private readonly ICoreService _coreService;
|
private readonly IDeviceService _deviceService;
|
||||||
private readonly IRgbService _rgbService;
|
|
||||||
|
|
||||||
public DeviceLayoutTabViewModel(
|
public DeviceLayoutTabViewModel(IWindowService windowService, INotificationService notificationService, IDeviceService deviceService, ArtemisDevice device)
|
||||||
IWindowService windowService,
|
|
||||||
INotificationService notificationService,
|
|
||||||
ICoreService coreService,
|
|
||||||
IRgbService rgbService,
|
|
||||||
ArtemisDevice device)
|
|
||||||
{
|
{
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_notificationService = notificationService;
|
_notificationService = notificationService;
|
||||||
_coreService = coreService;
|
_deviceService = deviceService;
|
||||||
_rgbService = rgbService;
|
|
||||||
|
|
||||||
Device = device;
|
Device = device;
|
||||||
DisplayName = "Layout";
|
DisplayName = "Layout";
|
||||||
@ -160,6 +153,6 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
|||||||
private void DeviceOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
private void DeviceOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PropertyName is nameof(Device.CustomLayoutPath) or nameof(Device.DisableDefaultLayout))
|
if (e.PropertyName is nameof(Device.CustomLayoutPath) or nameof(Device.DisableDefaultLayout))
|
||||||
Task.Run(() => _rgbService.ApplyBestDeviceLayout(Device));
|
Task.Run(() => _deviceService.ApplyDeviceLayout(Device, Device.GetBestDeviceLayout()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -16,17 +16,17 @@ namespace Artemis.UI.Screens.Device;
|
|||||||
public class InputMappingsTabViewModel : ActivatableViewModelBase
|
public class InputMappingsTabViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
private readonly IInputService _inputService;
|
private readonly IInputService _inputService;
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IDeviceService _deviceService;
|
||||||
private readonly ObservableCollection<ArtemisLed> _selectedLeds;
|
private readonly ObservableCollection<ArtemisLed> _selectedLeds;
|
||||||
private ArtemisLed? _selectedLed;
|
private ArtemisLed? _selectedLed;
|
||||||
|
|
||||||
public InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds, IRgbService rgbService, IInputService inputService)
|
public InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds, IInputService inputService, IDeviceService deviceService)
|
||||||
{
|
{
|
||||||
if (device.DeviceType != RGBDeviceType.Keyboard)
|
if (device.DeviceType != RGBDeviceType.Keyboard)
|
||||||
throw new ArtemisUIException("The input mappings tab only supports keyboards");
|
throw new ArtemisUIException("The input mappings tab only supports keyboards");
|
||||||
|
|
||||||
_rgbService = rgbService;
|
|
||||||
_inputService = inputService;
|
_inputService = inputService;
|
||||||
|
_deviceService = deviceService;
|
||||||
_selectedLeds = selectedLeds;
|
_selectedLeds = selectedLeds;
|
||||||
|
|
||||||
Device = device;
|
Device = device;
|
||||||
@ -81,7 +81,7 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase
|
|||||||
|
|
||||||
// Apply the new LED mapping
|
// Apply the new LED mapping
|
||||||
Device.InputMappings[SelectedLed] = artemisLed;
|
Device.InputMappings[SelectedLed] = artemisLed;
|
||||||
_rgbService.SaveDevice(Device);
|
_deviceService.SaveDevice(Device);
|
||||||
_selectedLeds.Clear();
|
_selectedLeds.Clear();
|
||||||
|
|
||||||
UpdateInputMappings();
|
UpdateInputMappings();
|
||||||
|
|||||||
@ -15,12 +15,12 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs;
|
|||||||
|
|
||||||
public class LayerHintsDialogViewModel : DialogViewModelBase<bool>
|
public class LayerHintsDialogViewModel : DialogViewModelBase<bool>
|
||||||
{
|
{
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IDeviceService _deviceService;
|
||||||
private readonly ILayerHintVmFactory _vmFactory;
|
private readonly ILayerHintVmFactory _vmFactory;
|
||||||
|
|
||||||
public LayerHintsDialogViewModel(Layer layer, IRgbService rgbService, ILayerHintVmFactory vmFactory)
|
public LayerHintsDialogViewModel(Layer layer, IDeviceService deviceService, ILayerHintVmFactory vmFactory)
|
||||||
{
|
{
|
||||||
_rgbService = rgbService;
|
_deviceService = deviceService;
|
||||||
_vmFactory = vmFactory;
|
_vmFactory = vmFactory;
|
||||||
|
|
||||||
Layer = layer;
|
Layer = layer;
|
||||||
@ -49,7 +49,7 @@ public class LayerHintsDialogViewModel : DialogViewModelBase<bool>
|
|||||||
|
|
||||||
public void AutoDetermineHints()
|
public void AutoDetermineHints()
|
||||||
{
|
{
|
||||||
Layer.Adapter.DetermineHints(_rgbService.EnabledDevices);
|
Layer.Adapter.DetermineHints(_deviceService.EnabledDevices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddCategoryHint()
|
public void AddCategoryHint()
|
||||||
|
|||||||
@ -14,17 +14,17 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree;
|
|||||||
|
|
||||||
public class FolderTreeItemViewModel : TreeItemViewModel
|
public class FolderTreeItemViewModel : TreeItemViewModel
|
||||||
{
|
{
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IDeviceService _deviceService;
|
||||||
|
|
||||||
public FolderTreeItemViewModel(TreeItemViewModel? parent,
|
public FolderTreeItemViewModel(TreeItemViewModel? parent,
|
||||||
Folder folder,
|
Folder folder,
|
||||||
IWindowService windowService,
|
IWindowService windowService,
|
||||||
|
IDeviceService deviceService,
|
||||||
IProfileEditorService profileEditorService,
|
IProfileEditorService profileEditorService,
|
||||||
IRgbService rgbService,
|
|
||||||
IProfileEditorVmFactory profileEditorVmFactory)
|
IProfileEditorVmFactory profileEditorVmFactory)
|
||||||
: base(parent, folder, windowService, rgbService, profileEditorService, profileEditorVmFactory)
|
: base(parent, folder, windowService, deviceService, profileEditorService, profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
_rgbService = rgbService;
|
_deviceService = deviceService;
|
||||||
Folder = folder;
|
Folder = folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ public class FolderTreeItemViewModel : TreeItemViewModel
|
|||||||
pasted.Name = parent.GetNewFolderName(pasted.Name + " - copy");
|
pasted.Name = parent.GetNewFolderName(pasted.Name + " - copy");
|
||||||
|
|
||||||
ProfileEditorService.ExecuteCommand(new AddProfileElement(pasted, parent, Folder.Order - 1));
|
ProfileEditorService.ExecuteCommand(new AddProfileElement(pasted, parent, Folder.Order - 1));
|
||||||
Folder.Profile.PopulateLeds(_rgbService.EnabledDevices);
|
Folder.Profile.PopulateLeds(_deviceService.EnabledDevices);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecutePasteInto()
|
private async Task ExecutePasteInto()
|
||||||
|
|||||||
@ -14,17 +14,17 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree;
|
|||||||
|
|
||||||
public class LayerTreeItemViewModel : TreeItemViewModel
|
public class LayerTreeItemViewModel : TreeItemViewModel
|
||||||
{
|
{
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IDeviceService _deviceService;
|
||||||
|
|
||||||
public LayerTreeItemViewModel(TreeItemViewModel? parent,
|
public LayerTreeItemViewModel(TreeItemViewModel? parent,
|
||||||
Layer layer,
|
Layer layer,
|
||||||
IWindowService windowService,
|
IWindowService windowService,
|
||||||
IProfileEditorService profileEditorService,
|
IProfileEditorService profileEditorService,
|
||||||
IRgbService rgbService,
|
IDeviceService deviceService,
|
||||||
IProfileEditorVmFactory profileEditorVmFactory)
|
IProfileEditorVmFactory profileEditorVmFactory)
|
||||||
: base(parent, layer, windowService, rgbService, profileEditorService, profileEditorVmFactory)
|
: base(parent, layer, windowService, deviceService, profileEditorService, profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
_rgbService = rgbService;
|
_deviceService = deviceService;
|
||||||
Layer = layer;
|
Layer = layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ public class LayerTreeItemViewModel : TreeItemViewModel
|
|||||||
|
|
||||||
Layer copied = new(Layer.Profile, Layer.Parent, copy, true);
|
Layer copied = new(Layer.Profile, Layer.Parent, copy, true);
|
||||||
ProfileEditorService.ExecuteCommand(new AddProfileElement(copied, Layer.Parent, Layer.Order - 1));
|
ProfileEditorService.ExecuteCommand(new AddProfileElement(copied, Layer.Parent, Layer.Order - 1));
|
||||||
Layer.Profile.PopulateLeds(_rgbService.EnabledDevices);
|
Layer.Profile.PopulateLeds(_deviceService.EnabledDevices);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task ExecuteCopy()
|
protected override async Task ExecuteCopy()
|
||||||
@ -65,7 +65,7 @@ public class LayerTreeItemViewModel : TreeItemViewModel
|
|||||||
pasted.Name = parent.GetNewLayerName(pasted.Name + " - copy");
|
pasted.Name = parent.GetNewLayerName(pasted.Name + " - copy");
|
||||||
|
|
||||||
ProfileEditorService.ExecuteCommand(new AddProfileElement(pasted, parent, Layer.Order - 1));
|
ProfileEditorService.ExecuteCommand(new AddProfileElement(pasted, parent, Layer.Order - 1));
|
||||||
Layer.Profile.PopulateLeds(_rgbService.EnabledDevices);
|
Layer.Profile.PopulateLeds(_deviceService.EnabledDevices);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@ -23,8 +23,8 @@ public class ProfileTreeViewModel : TreeItemViewModel
|
|||||||
private ObservableAsPropertyHelper<bool>? _keyBindingsEnabled;
|
private ObservableAsPropertyHelper<bool>? _keyBindingsEnabled;
|
||||||
private TreeItemViewModel? _selectedChild;
|
private TreeItemViewModel? _selectedChild;
|
||||||
|
|
||||||
public ProfileTreeViewModel(IWindowService windowService, IRgbService rgbService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory)
|
public ProfileTreeViewModel(IWindowService windowService, IDeviceService deviceService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory)
|
||||||
: base(null, null, windowService, rgbService, profileEditorService, profileEditorVmFactory)
|
: base(null, null, windowService, deviceService, profileEditorService, profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
|
|||||||
@ -28,7 +28,7 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
|
|||||||
{
|
{
|
||||||
private readonly IProfileEditorVmFactory _profileEditorVmFactory;
|
private readonly IProfileEditorVmFactory _profileEditorVmFactory;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IDeviceService _deviceService;
|
||||||
protected readonly IProfileEditorService ProfileEditorService;
|
protected readonly IProfileEditorService ProfileEditorService;
|
||||||
private bool _canPaste;
|
private bool _canPaste;
|
||||||
private RenderProfileElement? _currentProfileElement;
|
private RenderProfileElement? _currentProfileElement;
|
||||||
@ -41,13 +41,13 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
|
|||||||
protected TreeItemViewModel(TreeItemViewModel? parent,
|
protected TreeItemViewModel(TreeItemViewModel? parent,
|
||||||
ProfileElement? profileElement,
|
ProfileElement? profileElement,
|
||||||
IWindowService windowService,
|
IWindowService windowService,
|
||||||
IRgbService rgbService,
|
IDeviceService deviceService,
|
||||||
IProfileEditorService profileEditorService,
|
IProfileEditorService profileEditorService,
|
||||||
IProfileEditorVmFactory profileEditorVmFactory)
|
IProfileEditorVmFactory profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
ProfileEditorService = profileEditorService;
|
ProfileEditorService = profileEditorService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_rgbService = rgbService;
|
_deviceService = deviceService;
|
||||||
_profileEditorVmFactory = profileEditorVmFactory;
|
_profileEditorVmFactory = profileEditorVmFactory;
|
||||||
|
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
@ -267,7 +267,7 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
|
|||||||
if (ProfileElement is not Layer layer)
|
if (ProfileElement is not Layer layer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ProfileEditorService.ExecuteCommand(new ApplyAdaptionHints(layer, _rgbService.EnabledDevices.ToList()));
|
ProfileEditorService.ExecuteCommand(new ApplyAdaptionHints(layer, _deviceService.EnabledDevices.ToList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void UpdateCanPaste(bool isFlyoutOpen)
|
private async void UpdateCanPaste(bool isFlyoutOpen)
|
||||||
|
|||||||
@ -17,14 +17,14 @@ public class SelectionAddToolViewModel : ToolViewModel
|
|||||||
{
|
{
|
||||||
private readonly ObservableAsPropertyHelper<bool>? _isEnabled;
|
private readonly ObservableAsPropertyHelper<bool>? _isEnabled;
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IDeviceService _deviceService;
|
||||||
private Layer? _layer;
|
private Layer? _layer;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public SelectionAddToolViewModel(IProfileEditorService profileEditorService, IRgbService rgbService)
|
public SelectionAddToolViewModel(IProfileEditorService profileEditorService, IDeviceService deviceService)
|
||||||
{
|
{
|
||||||
_profileEditorService = profileEditorService;
|
_profileEditorService = profileEditorService;
|
||||||
_rgbService = rgbService;
|
_deviceService = deviceService;
|
||||||
// Not disposed when deactivated but when really disposed
|
// Not disposed when deactivated but when really disposed
|
||||||
_isEnabled = profileEditorService.ProfileElement.Select(p => p is Layer).ToProperty(this, vm => vm.IsEnabled);
|
_isEnabled = profileEditorService.ProfileElement.Select(p => p is Layer).ToProperty(this, vm => vm.IsEnabled);
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ public class SelectionAddToolViewModel : ToolViewModel
|
|||||||
if (inverse)
|
if (inverse)
|
||||||
{
|
{
|
||||||
List<ArtemisLed> toRemove = _layer.Leds.Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).ToList();
|
List<ArtemisLed> toRemove = _layer.Leds.Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).ToList();
|
||||||
List<ArtemisLed> toAdd = _rgbService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).Except(toRemove).ToList();
|
List<ArtemisLed> toAdd = _deviceService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).Except(toRemove).ToList();
|
||||||
List<ArtemisLed> leds = _layer.Leds.Except(toRemove).ToList();
|
List<ArtemisLed> leds = _layer.Leds.Except(toRemove).ToList();
|
||||||
leds.AddRange(toAdd);
|
leds.AddRange(toAdd);
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ public class SelectionAddToolViewModel : ToolViewModel
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
List<ArtemisLed> leds = _rgbService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).ToList();
|
List<ArtemisLed> leds = _deviceService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).ToList();
|
||||||
if (expand)
|
if (expand)
|
||||||
leds.AddRange(_layer.Leds);
|
leds.AddRange(_layer.Leds);
|
||||||
_profileEditorService.ExecuteCommand(new ChangeLayerLeds(_layer, leds.Distinct().ToList()));
|
_profileEditorService.ExecuteCommand(new ChangeLayerLeds(_layer, leds.Distinct().ToList()));
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public class VisualEditorViewModel : ActivatableViewModelBase
|
|||||||
private ObservableAsPropertyHelper<bool>? _suspendedEditing;
|
private ObservableAsPropertyHelper<bool>? _suspendedEditing;
|
||||||
private ReadOnlyObservableCollection<IToolViewModel>? _tools;
|
private ReadOnlyObservableCollection<IToolViewModel>? _tools;
|
||||||
|
|
||||||
public VisualEditorViewModel(IProfileEditorService profileEditorService, IRgbService rgbService, IProfileEditorVmFactory vmFactory)
|
public VisualEditorViewModel(IProfileEditorService profileEditorService, IDeviceService deviceService, IProfileEditorVmFactory vmFactory)
|
||||||
{
|
{
|
||||||
_vmFactory = vmFactory;
|
_vmFactory = vmFactory;
|
||||||
_visualizers = new SourceList<IVisualizerViewModel>();
|
_visualizers = new SourceList<IVisualizerViewModel>();
|
||||||
@ -34,7 +34,7 @@ public class VisualEditorViewModel : ActivatableViewModelBase
|
|||||||
.Bind(out ReadOnlyObservableCollection<IVisualizerViewModel> visualizers)
|
.Bind(out ReadOnlyObservableCollection<IVisualizerViewModel> visualizers)
|
||||||
.Subscribe();
|
.Subscribe();
|
||||||
|
|
||||||
Devices = new ObservableCollection<ArtemisDevice>(rgbService.EnabledDevices.OrderBy(d => d.ZIndex));
|
Devices = new ObservableCollection<ArtemisDevice>(deviceService.EnabledDevices.OrderBy(d => d.ZIndex));
|
||||||
Visualizers = visualizers;
|
Visualizers = visualizers;
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
|
|||||||
@ -19,15 +19,15 @@ namespace Artemis.UI.Screens.Settings;
|
|||||||
public class DevicesTabViewModel : RoutableScreen
|
public class DevicesTabViewModel : RoutableScreen
|
||||||
{
|
{
|
||||||
private readonly IDeviceVmFactory _deviceVmFactory;
|
private readonly IDeviceVmFactory _deviceVmFactory;
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IDeviceService _deviceService;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private bool _confirmedDisable;
|
private bool _confirmedDisable;
|
||||||
|
|
||||||
public DevicesTabViewModel(IRgbService rgbService, IWindowService windowService, IDeviceVmFactory deviceVmFactory)
|
public DevicesTabViewModel(IDeviceService deviceService, IWindowService windowService, IDeviceVmFactory deviceVmFactory)
|
||||||
{
|
{
|
||||||
DisplayName = "Devices";
|
DisplayName = "Devices";
|
||||||
|
|
||||||
_rgbService = rgbService;
|
_deviceService = deviceService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_deviceVmFactory = deviceVmFactory;
|
_deviceVmFactory = deviceVmFactory;
|
||||||
|
|
||||||
@ -36,10 +36,10 @@ public class DevicesTabViewModel : RoutableScreen
|
|||||||
{
|
{
|
||||||
GetDevices();
|
GetDevices();
|
||||||
|
|
||||||
Observable.FromEventPattern<DeviceEventArgs>(x => rgbService.DeviceAdded += x, x => rgbService.DeviceAdded -= x)
|
Observable.FromEventPattern<DeviceEventArgs>(x => _deviceService.DeviceAdded += x, x => _deviceService.DeviceAdded -= x)
|
||||||
.Subscribe(d => AddDevice(d.EventArgs.Device))
|
.Subscribe(d => AddDevice(d.EventArgs.Device))
|
||||||
.DisposeWith(disposables);
|
.DisposeWith(disposables);
|
||||||
Observable.FromEventPattern<DeviceEventArgs>(x => rgbService.DeviceRemoved += x, x => rgbService.DeviceRemoved -= x)
|
Observable.FromEventPattern<DeviceEventArgs>(x => _deviceService.DeviceRemoved += x, x => _deviceService.DeviceRemoved -= x)
|
||||||
.Subscribe(d => RemoveDevice(d.EventArgs.Device))
|
.Subscribe(d => RemoveDevice(d.EventArgs.Device))
|
||||||
.DisposeWith(disposables);
|
.DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
@ -66,7 +66,7 @@ public class DevicesTabViewModel : RoutableScreen
|
|||||||
private void GetDevices()
|
private void GetDevices()
|
||||||
{
|
{
|
||||||
Devices.Clear();
|
Devices.Clear();
|
||||||
Dispatcher.UIThread.InvokeAsync(() => { Devices.AddRange(_rgbService.Devices.Select(d => _deviceVmFactory.DeviceSettingsViewModel(d, this))); }, DispatcherPriority.Background);
|
Dispatcher.UIThread.InvokeAsync(() => { Devices.AddRange(_deviceService.Devices.Select(d => _deviceVmFactory.DeviceSettingsViewModel(d, this))); }, DispatcherPriority.Background);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddDevice(ArtemisDevice device)
|
private void AddDevice(ArtemisDevice device)
|
||||||
@ -81,7 +81,7 @@ public class DevicesTabViewModel : RoutableScreen
|
|||||||
private void RemoveDevice(ArtemisDevice device)
|
private void RemoveDevice(ArtemisDevice device)
|
||||||
{
|
{
|
||||||
// If the device was only disabled don't remove it
|
// If the device was only disabled don't remove it
|
||||||
if (_rgbService.Devices.Contains(device))
|
if (_deviceService.Devices.Contains(device))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DeviceSettingsViewModel? viewModel = Devices.FirstOrDefault(i => i.Device == device);
|
DeviceSettingsViewModel? viewModel = Devices.FirstOrDefault(i => i.Device == device);
|
||||||
|
|||||||
@ -21,9 +21,9 @@ public class StartupWizardViewModel : DialogViewModelBase<bool>
|
|||||||
{
|
{
|
||||||
private readonly IAutoRunProvider? _autoRunProvider;
|
private readonly IAutoRunProvider? _autoRunProvider;
|
||||||
private readonly IProtocolProvider? _protocolProvider;
|
private readonly IProtocolProvider? _protocolProvider;
|
||||||
private readonly IRgbService _rgbService;
|
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
|
private readonly IDeviceService _deviceService;
|
||||||
private int _currentStep;
|
private int _currentStep;
|
||||||
private bool _showContinue;
|
private bool _showContinue;
|
||||||
private bool _showFinish;
|
private bool _showFinish;
|
||||||
@ -31,14 +31,14 @@ public class StartupWizardViewModel : DialogViewModelBase<bool>
|
|||||||
|
|
||||||
public StartupWizardViewModel(IContainer container,
|
public StartupWizardViewModel(IContainer container,
|
||||||
ISettingsService settingsService,
|
ISettingsService settingsService,
|
||||||
IRgbService rgbService,
|
|
||||||
IPluginManagementService pluginManagementService,
|
IPluginManagementService pluginManagementService,
|
||||||
IWindowService windowService,
|
IWindowService windowService,
|
||||||
|
IDeviceService deviceService,
|
||||||
ISettingsVmFactory settingsVmFactory)
|
ISettingsVmFactory settingsVmFactory)
|
||||||
{
|
{
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_rgbService = rgbService;
|
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
|
_deviceService = deviceService;
|
||||||
_autoRunProvider = container.Resolve<IAutoRunProvider>(IfUnresolved.ReturnDefault);
|
_autoRunProvider = container.Resolve<IAutoRunProvider>(IfUnresolved.ReturnDefault);
|
||||||
_protocolProvider = container.Resolve<IProtocolProvider>(IfUnresolved.ReturnDefault);
|
_protocolProvider = container.Resolve<IProtocolProvider>(IfUnresolved.ReturnDefault);
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ public class StartupWizardViewModel : DialogViewModelBase<bool>
|
|||||||
private void ExecuteSelectLayout(string layout)
|
private void ExecuteSelectLayout(string layout)
|
||||||
{
|
{
|
||||||
// TODO: Implement the layout
|
// TODO: Implement the layout
|
||||||
_rgbService.AutoArrangeDevices();
|
_deviceService.AutoArrangeDevices();
|
||||||
|
|
||||||
ExecuteContinue();
|
ExecuteContinue();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,16 +15,16 @@ namespace Artemis.UI.Screens.SurfaceEditor;
|
|||||||
public class ListDeviceViewModel : ViewModelBase
|
public class ListDeviceViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private static readonly Random Random = new();
|
private static readonly Random Random = new();
|
||||||
private readonly IRgbService _rgbService;
|
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
|
private readonly IDeviceService _deviceService;
|
||||||
|
|
||||||
private SKColor _color;
|
private SKColor _color;
|
||||||
private bool _isSelected;
|
private bool _isSelected;
|
||||||
|
|
||||||
public ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel, IWindowService windowService, IRgbService rgbService)
|
public ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel, IWindowService windowService, IDeviceService deviceService)
|
||||||
{
|
{
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_rgbService = rgbService;
|
_deviceService = deviceService;
|
||||||
|
|
||||||
Device = device;
|
Device = device;
|
||||||
SurfaceEditorViewModel = surfaceEditorViewModel;
|
SurfaceEditorViewModel = surfaceEditorViewModel;
|
||||||
@ -62,6 +62,6 @@ public class ListDeviceViewModel : ViewModelBase
|
|||||||
.ShowAsync();
|
.ShowAsync();
|
||||||
|
|
||||||
if (viewModel.MadeChanges)
|
if (viewModel.MadeChanges)
|
||||||
_rgbService.SaveDevice(Device);
|
_deviceService.SaveDevice(Device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -17,16 +17,16 @@ namespace Artemis.UI.Screens.SurfaceEditor;
|
|||||||
|
|
||||||
public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IDeviceService _deviceService;
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private double _dragOffsetX;
|
private double _dragOffsetX;
|
||||||
private double _dragOffsetY;
|
private double _dragOffsetY;
|
||||||
private bool _isSelected;
|
private bool _isSelected;
|
||||||
|
|
||||||
public SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel, IRgbService rgbService, ISettingsService settingsService, IWindowService windowService)
|
public SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel, IDeviceService deviceService, ISettingsService settingsService, IWindowService windowService)
|
||||||
{
|
{
|
||||||
_rgbService = rgbService;
|
_deviceService = deviceService;
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
|||||||
|
|
||||||
IEnumerable<SKRect> own = Device.Leds
|
IEnumerable<SKRect> own = Device.Leds
|
||||||
.Select(l => SKRect.Create(l.Rectangle.Left + x, l.Rectangle.Top + y, l.Rectangle.Width, l.Rectangle.Height));
|
.Select(l => SKRect.Create(l.Rectangle.Left + x, l.Rectangle.Top + y, l.Rectangle.Width, l.Rectangle.Height));
|
||||||
IEnumerable<SKRect> others = _rgbService.EnabledDevices
|
IEnumerable<SKRect> others = _deviceService.EnabledDevices
|
||||||
.Where(d => d != Device && d.IsEnabled)
|
.Where(d => d != Device && d.IsEnabled)
|
||||||
.SelectMany(d => d.Leds)
|
.SelectMany(d => d.Leds)
|
||||||
.Select(l => SKRect.Create(l.Rectangle.Left + l.Device.X, l.Rectangle.Top + l.Device.Y, l.Rectangle.Width, l.Rectangle.Height));
|
.Select(l => SKRect.Create(l.Rectangle.Left + l.Device.X, l.Rectangle.Top + l.Device.Y, l.Rectangle.Width, l.Rectangle.Height));
|
||||||
@ -120,6 +120,6 @@ public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
|||||||
.ShowAsync();
|
.ShowAsync();
|
||||||
|
|
||||||
if (viewModel.MadeChanges)
|
if (viewModel.MadeChanges)
|
||||||
_rgbService.SaveDevice(Device);
|
_deviceService.SaveDevice(Device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,8 +21,8 @@ namespace Artemis.UI.Screens.SurfaceEditor;
|
|||||||
public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
||||||
{
|
{
|
||||||
private readonly IDeviceService _deviceService;
|
private readonly IDeviceService _deviceService;
|
||||||
|
private readonly IRenderService _renderService;
|
||||||
private readonly IDeviceVmFactory _deviceVmFactory;
|
private readonly IDeviceVmFactory _deviceVmFactory;
|
||||||
private readonly IRgbService _rgbService;
|
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly ISurfaceVmFactory _surfaceVmFactory;
|
private readonly ISurfaceVmFactory _surfaceVmFactory;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
@ -33,23 +33,23 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
|||||||
private bool _saving;
|
private bool _saving;
|
||||||
|
|
||||||
public SurfaceEditorViewModel(ICoreService coreService,
|
public SurfaceEditorViewModel(ICoreService coreService,
|
||||||
IRgbService rgbService,
|
|
||||||
ISurfaceVmFactory surfaceVmFactory,
|
ISurfaceVmFactory surfaceVmFactory,
|
||||||
ISettingsService settingsService,
|
ISettingsService settingsService,
|
||||||
IDeviceVmFactory deviceVmFactory,
|
IDeviceVmFactory deviceVmFactory,
|
||||||
IWindowService windowService,
|
IWindowService windowService,
|
||||||
IDeviceService deviceService)
|
IDeviceService deviceService,
|
||||||
|
IRenderService renderService)
|
||||||
{
|
{
|
||||||
_rgbService = rgbService;
|
|
||||||
_surfaceVmFactory = surfaceVmFactory;
|
_surfaceVmFactory = surfaceVmFactory;
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_deviceVmFactory = deviceVmFactory;
|
_deviceVmFactory = deviceVmFactory;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_deviceService = deviceService;
|
_deviceService = deviceService;
|
||||||
|
_renderService = renderService;
|
||||||
|
|
||||||
DisplayName = "Surface Editor";
|
DisplayName = "Surface Editor";
|
||||||
SurfaceDeviceViewModels = new ObservableCollection<SurfaceDeviceViewModel>(rgbService.EnabledDevices.OrderBy(d => d.ZIndex).Select(d => surfaceVmFactory.SurfaceDeviceViewModel(d, this)));
|
SurfaceDeviceViewModels = new ObservableCollection<SurfaceDeviceViewModel>(deviceService.EnabledDevices.OrderBy(d => d.ZIndex).Select(d => surfaceVmFactory.SurfaceDeviceViewModel(d, this)));
|
||||||
ListDeviceViewModels = new ObservableCollection<ListDeviceViewModel>(rgbService.EnabledDevices.OrderBy(d => d.ZIndex).Select(d => surfaceVmFactory.ListDeviceViewModel(d, this)));
|
ListDeviceViewModels = new ObservableCollection<ListDeviceViewModel>(deviceService.EnabledDevices.OrderBy(d => d.ZIndex).Select(d => surfaceVmFactory.ListDeviceViewModel(d, this)));
|
||||||
|
|
||||||
AutoArrange = ReactiveCommand.CreateFromTask(ExecuteAutoArrange);
|
AutoArrange = ReactiveCommand.CreateFromTask(ExecuteAutoArrange);
|
||||||
IdentifyDevice = ReactiveCommand.Create<ArtemisDevice>(ExecuteIdentifyDevice);
|
IdentifyDevice = ReactiveCommand.Create<ArtemisDevice>(ExecuteIdentifyDevice);
|
||||||
@ -61,14 +61,14 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
|||||||
|
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
coreService.FrameRendering += CoreServiceOnFrameRendering;
|
_renderService.FrameRendering += RenderServiceOnFrameRendering;
|
||||||
_rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
|
_deviceService.DeviceAdded += DeviceServiceOnDeviceAdded;
|
||||||
_rgbService.DeviceRemoved += RgbServiceOnDeviceRemoved;
|
_deviceService.DeviceRemoved += DeviceServiceOnDeviceRemoved;
|
||||||
Disposable.Create(() =>
|
Disposable.Create(() =>
|
||||||
{
|
{
|
||||||
coreService.FrameRendering -= CoreServiceOnFrameRendering;
|
_renderService.FrameRendering -= RenderServiceOnFrameRendering;
|
||||||
_rgbService.DeviceAdded -= RgbServiceOnDeviceAdded;
|
_deviceService.DeviceAdded -= DeviceServiceOnDeviceAdded;
|
||||||
_rgbService.DeviceRemoved -= RgbServiceOnDeviceRemoved;
|
_deviceService.DeviceRemoved -= DeviceServiceOnDeviceRemoved;
|
||||||
}).DisposeWith(d);
|
}).DisposeWith(d);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -135,7 +135,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_saving = true;
|
_saving = true;
|
||||||
_rgbService.SaveDevices();
|
_deviceService.SaveDevices();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -166,7 +166,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
|||||||
surfaceDeviceViewModel.UpdateMouseDrag(mousePosition, round, ignoreOverlap);
|
surfaceDeviceViewModel.UpdateMouseDrag(mousePosition, round, ignoreOverlap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RgbServiceOnDeviceAdded(object? sender, DeviceEventArgs e)
|
private void DeviceServiceOnDeviceAdded(object? sender, DeviceEventArgs e)
|
||||||
{
|
{
|
||||||
if (!e.Device.IsEnabled)
|
if (!e.Device.IsEnabled)
|
||||||
return;
|
return;
|
||||||
@ -177,7 +177,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
|||||||
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RgbServiceOnDeviceRemoved(object? sender, DeviceEventArgs e)
|
private void DeviceServiceOnDeviceRemoved(object? sender, DeviceEventArgs e)
|
||||||
{
|
{
|
||||||
SurfaceDeviceViewModel? surfaceVm = SurfaceDeviceViewModels.FirstOrDefault(vm => vm.Device == e.Device);
|
SurfaceDeviceViewModel? surfaceVm = SurfaceDeviceViewModels.FirstOrDefault(vm => vm.Device == e.Device);
|
||||||
ListDeviceViewModel? listVm = ListDeviceViewModels.FirstOrDefault(vm => vm.Device == e.Device);
|
ListDeviceViewModel? listVm = ListDeviceViewModels.FirstOrDefault(vm => vm.Device == e.Device);
|
||||||
@ -193,10 +193,10 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
|||||||
if (!confirmed)
|
if (!confirmed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_rgbService.AutoArrangeDevices();
|
_deviceService.AutoArrangeDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CoreServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)
|
private void RenderServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)
|
||||||
{
|
{
|
||||||
// Animate the overlay because I'm vain
|
// Animate the overlay because I'm vain
|
||||||
if (ColorDevices && _overlayOpacity < 1)
|
if (ColorDevices && _overlayOpacity < 1)
|
||||||
@ -255,7 +255,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
|||||||
|
|
||||||
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||||
|
|
||||||
_rgbService.SaveDevices();
|
_deviceService.SaveDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteBringForward(ArtemisDevice device)
|
private void ExecuteBringForward(ArtemisDevice device)
|
||||||
@ -273,7 +273,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
|||||||
|
|
||||||
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||||
|
|
||||||
_rgbService.SaveDevices();
|
_deviceService.SaveDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteSendToBack(ArtemisDevice device)
|
private void ExecuteSendToBack(ArtemisDevice device)
|
||||||
@ -288,7 +288,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
|||||||
|
|
||||||
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||||
|
|
||||||
_rgbService.SaveDevices();
|
_deviceService.SaveDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteSendBackward(ArtemisDevice device)
|
private void ExecuteSendBackward(ArtemisDevice device)
|
||||||
@ -305,7 +305,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
|||||||
|
|
||||||
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||||
|
|
||||||
_rgbService.SaveDevices();
|
_deviceService.SaveDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -16,12 +16,12 @@ public class ProfilePreviewViewModel : ActivatableViewModelBase
|
|||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private ProfileConfiguration? _profileConfiguration;
|
private ProfileConfiguration? _profileConfiguration;
|
||||||
|
|
||||||
public ProfilePreviewViewModel(IProfileService profileService, IRgbService rgbService, IWindowService windowService)
|
public ProfilePreviewViewModel(IProfileService profileService, IDeviceService deviceService, IWindowService windowService)
|
||||||
{
|
{
|
||||||
_profileService = profileService;
|
_profileService = profileService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
|
|
||||||
Devices = new ObservableCollection<ArtemisDevice>(rgbService.EnabledDevices.OrderBy(d => d.ZIndex));
|
Devices = new ObservableCollection<ArtemisDevice>(deviceService.EnabledDevices.OrderBy(d => d.ZIndex));
|
||||||
|
|
||||||
this.WhenAnyValue(vm => vm.ProfileConfiguration).Subscribe(_ => Update());
|
this.WhenAnyValue(vm => vm.ProfileConfiguration).Subscribe(_ => Update());
|
||||||
this.WhenActivated(d => Disposable.Create(() => PreviewProfile(null)).DisposeWith(d));
|
this.WhenActivated(d => Disposable.Create(() => PreviewProfile(null)).DisposeWith(d));
|
||||||
|
|||||||
@ -17,18 +17,18 @@ namespace Artemis.UI.Services;
|
|||||||
public class DeviceLayoutService : IDeviceLayoutService
|
public class DeviceLayoutService : IDeviceLayoutService
|
||||||
{
|
{
|
||||||
private readonly List<ArtemisDevice> _ignoredDevices;
|
private readonly List<ArtemisDevice> _ignoredDevices;
|
||||||
|
private readonly IDeviceService _deviceService;
|
||||||
private readonly IMainWindowService _mainWindowService;
|
private readonly IMainWindowService _mainWindowService;
|
||||||
private readonly IRgbService _rgbService;
|
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
|
|
||||||
public DeviceLayoutService(IRgbService rgbService, IMainWindowService mainWindowService, IWindowService windowService)
|
public DeviceLayoutService(IDeviceService deviceService, IMainWindowService mainWindowService, IWindowService windowService)
|
||||||
{
|
{
|
||||||
_rgbService = rgbService;
|
_deviceService = deviceService;
|
||||||
_mainWindowService = mainWindowService;
|
_mainWindowService = mainWindowService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_ignoredDevices = new List<ArtemisDevice>();
|
_ignoredDevices = new List<ArtemisDevice>();
|
||||||
|
|
||||||
rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
|
deviceService.DeviceAdded += RgbServiceOnDeviceAdded;
|
||||||
mainWindowService.MainWindowOpened += WindowServiceOnMainWindowOpened;
|
mainWindowService.MainWindowOpened += WindowServiceOnMainWindowOpened;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,8 +60,8 @@ public class DeviceLayoutService : IDeviceLayoutService
|
|||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(400);
|
await Task.Delay(400);
|
||||||
_rgbService.SaveDevice(artemisDevice);
|
_deviceService.SaveDevice(artemisDevice);
|
||||||
_rgbService.ApplyBestDeviceLayout(artemisDevice);
|
_deviceService.ApplyDeviceLayout(artemisDevice, artemisDevice.GetBestDeviceLayout());
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool DeviceNeedsLayout(ArtemisDevice d)
|
private bool DeviceNeedsLayout(ArtemisDevice d)
|
||||||
@ -73,7 +73,7 @@ public class DeviceLayoutService : IDeviceLayoutService
|
|||||||
|
|
||||||
private async void WindowServiceOnMainWindowOpened(object? sender, EventArgs e)
|
private async void WindowServiceOnMainWindowOpened(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
List<ArtemisDevice> devices = _rgbService.Devices.Where(device => DeviceNeedsLayout(device) && !_ignoredDevices.Contains(device)).ToList();
|
List<ArtemisDevice> devices = _deviceService.Devices.Where(device => DeviceNeedsLayout(device) && !_ignoredDevices.Contains(device)).ToList();
|
||||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
foreach (ArtemisDevice artemisDevice in devices)
|
foreach (ArtemisDevice artemisDevice in devices)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user