mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
Revert "Merge branch 'development'"
This reverts commit be217ffc02e793d44a031aace51a92ee15d87b40, reversing changes made to 524296e4e9891a1c4ccedd5f0dcfcbe54b0c8006.
This commit is contained in:
parent
be217ffc02
commit
ebf40992bc
@ -1,27 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
|
||||
namespace Artemis.Core;
|
||||
@ -17,14 +16,6 @@ public class Hotkey : CorePropertyChanged, IStorageModel
|
||||
Entity = new ProfileConfigurationHotkeyEntity();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Hotkey(KeyboardKey? key, KeyboardModifierKey? modifiers)
|
||||
{
|
||||
Key = key;
|
||||
Modifiers = modifiers;
|
||||
Entity = new ProfileConfigurationHotkeyEntity();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="Hotkey" /> based on the provided entity
|
||||
/// </summary>
|
||||
@ -55,7 +46,7 @@ public class Hotkey : CorePropertyChanged, IStorageModel
|
||||
/// <returns><see langword="true" /> if the event args match the hotkey; otherwise <see langword="false" /></returns>
|
||||
public bool MatchesEventArgs(ArtemisKeyboardKeyEventArgs eventArgs)
|
||||
{
|
||||
return eventArgs.Key == Key && (eventArgs.Modifiers == Modifiers || (eventArgs.Modifiers == KeyboardModifierKey.None && Modifiers == null));
|
||||
return eventArgs.Key == Key && eventArgs.Modifiers == Modifiers;
|
||||
}
|
||||
|
||||
#region Implementation of IStorageModel
|
||||
|
||||
@ -15,17 +15,11 @@ namespace Artemis.Core;
|
||||
/// </summary>
|
||||
public class ArtemisDevice : CorePropertyChanged
|
||||
{
|
||||
private readonly List<OriginalLed> _originalLeds;
|
||||
private readonly Size _originalSize;
|
||||
private SKPath? _path;
|
||||
private SKRect _rectangle;
|
||||
|
||||
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();
|
||||
DeviceEntity = new DeviceEntity();
|
||||
RgbDevice = rgbDevice;
|
||||
@ -54,10 +48,6 @@ public class ArtemisDevice : CorePropertyChanged
|
||||
|
||||
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();
|
||||
DeviceEntity = deviceEntity;
|
||||
RgbDevice = rgbDevice;
|
||||
@ -360,40 +350,6 @@ public class ArtemisDevice : CorePropertyChanged
|
||||
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>
|
||||
/// Occurs when the underlying RGB.NET device was updated
|
||||
/// </summary>
|
||||
@ -459,18 +415,8 @@ public class ArtemisDevice : CorePropertyChanged
|
||||
/// A boolean indicating whether to remove excess LEDs present in the device but missing
|
||||
/// in the layout
|
||||
/// </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)
|
||||
throw new ArtemisCoreException($"Cannot apply layout with {nameof(createMissingLeds)} " +
|
||||
"set to true because the device provider does not support it");
|
||||
@ -478,33 +424,18 @@ public class ArtemisDevice : CorePropertyChanged
|
||||
throw new ArtemisCoreException($"Cannot apply layout with {nameof(removeExcessiveLeds)} " +
|
||||
"set to true because the device provider does not support it");
|
||||
|
||||
ClearLayout();
|
||||
if (layout.IsValid)
|
||||
layout.ApplyTo(RgbDevice, createMissingLeds, removeExcessiveLeds);
|
||||
|
||||
|
||||
UpdateLeds();
|
||||
|
||||
|
||||
Layout = layout;
|
||||
Layout.ApplyDevice(this);
|
||||
|
||||
CalculateRenderProperties();
|
||||
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()
|
||||
{
|
||||
// Other properties are computed
|
||||
|
||||
@ -59,13 +59,13 @@ public class ArtemisLed : CorePropertyChanged
|
||||
|
||||
internal void CalculateRectangles()
|
||||
{
|
||||
Rectangle = RenderScale.CreateScaleCompatibleRect(
|
||||
Rectangle = Utilities.CreateScaleCompatibleRect(
|
||||
RgbLed.Boundary.Location.X,
|
||||
RgbLed.Boundary.Location.Y,
|
||||
RgbLed.Boundary.Size.Width,
|
||||
RgbLed.Boundary.Size.Height
|
||||
);
|
||||
AbsoluteRectangle = RenderScale.CreateScaleCompatibleRect(
|
||||
AbsoluteRectangle = Utilities.CreateScaleCompatibleRect(
|
||||
RgbLed.AbsoluteBoundary.Location.X,
|
||||
RgbLed.AbsoluteBoundary.Location.Y,
|
||||
RgbLed.AbsoluteBoundary.Size.Width,
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,222 +0,0 @@
|
||||
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,11 +20,19 @@ namespace Artemis.Core.Services;
|
||||
/// </summary>
|
||||
internal class CoreService : ICoreService
|
||||
{
|
||||
private readonly Stopwatch _frameStopWatch;
|
||||
private readonly ILogger _logger;
|
||||
private readonly PluginSetting<LogEventLevel> _loggingLevel;
|
||||
private readonly IModuleService _moduleService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IRenderService _renderService;
|
||||
private readonly IProfileService _profileService;
|
||||
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
|
||||
public CoreService(IContainer container,
|
||||
@ -32,49 +40,36 @@ internal class CoreService : ICoreService
|
||||
StorageMigrationService _1, // injected to ensure migration runs early
|
||||
ISettingsService settingsService,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IRgbService rgbService,
|
||||
IProfileService profileService,
|
||||
IModuleService moduleService,
|
||||
IScriptingService scriptingService,
|
||||
IRenderService renderService)
|
||||
IScriptingService scriptingService)
|
||||
{
|
||||
Constants.CorePlugin.Container = container;
|
||||
|
||||
_logger = logger;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_renderService = renderService;
|
||||
_rgbService = rgbService;
|
||||
_profileService = profileService;
|
||||
_moduleService = moduleService;
|
||||
_scriptingService = scriptingService;
|
||||
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
|
||||
_frameStopWatch = new Stopwatch();
|
||||
|
||||
_rgbService.Surface.Updating += SurfaceOnUpdating;
|
||||
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
|
||||
}
|
||||
|
||||
public bool IsElevated { get; set; }
|
||||
|
||||
public bool IsInitialized { get; set; }
|
||||
// ReSharper restore UnusedParameter.Local
|
||||
|
||||
public void Initialize()
|
||||
protected virtual void OnFrameRendering(FrameRenderingEventArgs e)
|
||||
{
|
||||
if (IsInitialized)
|
||||
throw new ArtemisCoreException("Cannot initialize the core as it is already initialized.");
|
||||
FrameRendering?.Invoke(this, e);
|
||||
}
|
||||
|
||||
_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);
|
||||
_renderService.Initialize();
|
||||
|
||||
OnInitialized();
|
||||
protected virtual void OnFrameRendered(FrameRenderedEventArgs e)
|
||||
{
|
||||
FrameRendered?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void ApplyLoggingLevel()
|
||||
@ -103,11 +98,131 @@ internal class CoreService : ICoreService
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler? Initialized;
|
||||
private void SurfaceOnUpdating(UpdatingEventArgs args)
|
||||
{
|
||||
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()
|
||||
{
|
||||
IsInitialized = true;
|
||||
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,252 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
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 Serilog;
|
||||
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class DeviceService : IDeviceService
|
||||
{
|
||||
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();
|
||||
private readonly IRgbService _rgbService;
|
||||
|
||||
public DeviceService(ILogger logger, IPluginManagementService pluginManagementService, IDeviceRepository deviceRepository, Lazy<IRenderService> renderService)
|
||||
public DeviceService(IRgbService rgbService)
|
||||
{
|
||||
_logger = logger;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_deviceRepository = deviceRepository;
|
||||
_renderService = renderService;
|
||||
|
||||
EnabledDevices = new ReadOnlyCollection<ArtemisDevice>(_enabledDevices);
|
||||
Devices = new ReadOnlyCollection<ArtemisDevice>(_devices);
|
||||
|
||||
RenderScale.RenderScaleMultiplierChanged += RenderScaleOnRenderScaleMultiplierChanged;
|
||||
_rgbService = rgbService;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
RGBSurface surface = _renderService.Value.Surface;
|
||||
|
||||
// Create a LED group way at the top
|
||||
ListLedGroup ledGroup = new(surface, device.Leds.Select(l => l.RgbLed))
|
||||
ListLedGroup ledGroup = new(_rgbService.Surface, device.Leds.Select(l => l.RgbLed))
|
||||
{
|
||||
Brush = new SolidColorBrush(new Color(255, 255, 255)),
|
||||
ZIndex = 999
|
||||
@ -266,81 +36,9 @@ internal class DeviceService : IDeviceService
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void CalculateRenderProperties()
|
||||
|
||||
public void IdentifyDevice(ArtemisDevice device)
|
||||
{
|
||||
foreach (ArtemisDevice artemisDevice in Devices)
|
||||
artemisDevice.CalculateRenderProperties();
|
||||
UpdateLeds();
|
||||
BlinkDevice(device, 0);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@ -34,13 +34,4 @@ public class ArtemisKeyboardKeyEventArgs : EventArgs
|
||||
/// Gets the modifiers that are pressed
|
||||
/// </summary>
|
||||
public KeyboardModifierKey Modifiers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a hotkey matching the event.
|
||||
/// </summary>
|
||||
/// <returns>The resulting hotkey.</returns>
|
||||
public Hotkey ToHotkey()
|
||||
{
|
||||
return new Hotkey {Key = Key, Modifiers = Modifiers};
|
||||
}
|
||||
}
|
||||
@ -9,19 +9,19 @@ namespace Artemis.Core.Services;
|
||||
internal class InputService : IInputService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private ArtemisDevice? _firstKeyboard;
|
||||
private ArtemisDevice? _firstMouse;
|
||||
private int _keyboardCount;
|
||||
private int _mouseCount;
|
||||
|
||||
public InputService(ILogger logger, IDeviceService deviceService)
|
||||
public InputService(ILogger logger, IRgbService rgbService)
|
||||
{
|
||||
_logger = logger;
|
||||
_deviceService = deviceService;
|
||||
_rgbService = rgbService;
|
||||
|
||||
_deviceService.DeviceAdded += DeviceServiceOnDevicesModified;
|
||||
_deviceService.DeviceRemoved += DeviceServiceOnDevicesModified;
|
||||
_rgbService.DeviceAdded += RgbServiceOnDevicesModified;
|
||||
_rgbService.DeviceRemoved += RgbServiceOnDevicesModified;
|
||||
BustIdentifierCache();
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ internal class InputService : IInputService
|
||||
|
||||
_logger.Debug("Stop identifying device {device}", _identifyingDevice);
|
||||
_identifyingDevice = null;
|
||||
_deviceService.SaveDevices();
|
||||
_rgbService.SaveDevices();
|
||||
|
||||
BustIdentifierCache();
|
||||
}
|
||||
@ -209,7 +209,7 @@ internal class InputService : IInputService
|
||||
public void BustIdentifierCache()
|
||||
{
|
||||
_deviceCache.Clear();
|
||||
_devices = _deviceService.EnabledDevices.Where(d => d.InputIdentifiers.Any()).ToList();
|
||||
_devices = _rgbService.EnabledDevices.Where(d => d.InputIdentifiers.Any()).ToList();
|
||||
}
|
||||
|
||||
private void AddDeviceToCache(ArtemisDevice match, InputProvider provider, object identifier)
|
||||
@ -241,12 +241,12 @@ internal class InputService : IInputService
|
||||
OnDeviceIdentified();
|
||||
}
|
||||
|
||||
private void DeviceServiceOnDevicesModified(object? sender, DeviceEventArgs args)
|
||||
private void RgbServiceOnDevicesModified(object? sender, DeviceEventArgs args)
|
||||
{
|
||||
_firstKeyboard = _deviceService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Keyboard);
|
||||
_firstMouse = _deviceService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Mouse);
|
||||
_keyboardCount = _deviceService.Devices.Count(d => d.DeviceType == RGBDeviceType.Keyboard);
|
||||
_mouseCount = _deviceService.Devices.Count(d => d.DeviceType == RGBDeviceType.Mouse);
|
||||
_firstKeyboard = _rgbService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Keyboard);
|
||||
_firstMouse = _rgbService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Mouse);
|
||||
_keyboardCount = _rgbService.Devices.Count(d => d.DeviceType == RGBDeviceType.Keyboard);
|
||||
_mouseCount = _rgbService.Devices.Count(d => d.DeviceType == RGBDeviceType.Mouse);
|
||||
|
||||
BustIdentifierCache();
|
||||
}
|
||||
|
||||
@ -12,6 +12,21 @@ public interface ICoreService : IArtemisService
|
||||
/// </summary>
|
||||
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>
|
||||
/// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions)
|
||||
/// </summary>
|
||||
@ -26,4 +41,14 @@ public interface ICoreService : IArtemisService
|
||||
/// Occurs the core has finished initializing
|
||||
/// </summary>
|
||||
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,109 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.DeviceProviders;
|
||||
|
||||
namespace Artemis.Core.Services;
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service that allows you manage an <see cref="ArtemisDevice" />
|
||||
/// </summary>
|
||||
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>
|
||||
/// Identifies the device by making it blink white 5 times
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
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;
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
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>
|
||||
/// 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;
|
||||
}
|
||||
168
src/Artemis.Core/Services/Interfaces/IRgbService.cs
Normal file
168
src/Artemis.Core/Services/Interfaces/IRgbService.cs
Normal file
@ -0,0 +1,168 @@
|
||||
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;
|
||||
}
|
||||
@ -1,251 +0,0 @@
|
||||
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 void Render(SKCanvas canvas, double delta)
|
||||
{
|
||||
_frameStopWatch.Restart();
|
||||
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);
|
||||
}
|
||||
}
|
||||
613
src/Artemis.Core/Services/RgbService.cs
Normal file
613
src/Artemis.Core/Services/RgbService.cs
Normal file
@ -0,0 +1,613 @@
|
||||
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,11 +41,6 @@ public interface IProfileService : IArtemisService
|
||||
/// Gets or sets a value indicating whether the currently focused profile should receive updates.
|
||||
/// </summary>
|
||||
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>
|
||||
/// Creates a copy of the provided profile configuration.
|
||||
|
||||
@ -5,9 +5,9 @@ using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Core.Services.Core;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.Storage.Repositories.Interfaces;
|
||||
using Newtonsoft.Json;
|
||||
@ -16,37 +16,36 @@ using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class ProfileService : IProfileService, IRenderer
|
||||
internal class ProfileService : IProfileService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IProfileCategoryRepository _profileCategoryRepository;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IDeviceService _deviceService;
|
||||
|
||||
private readonly List<ArtemisKeyboardKeyEventArgs> _pendingKeyboardEvents = new();
|
||||
private readonly List<ProfileCategory> _profileCategories;
|
||||
private readonly IProfileRepository _profileRepository;
|
||||
private readonly List<Exception> _renderExceptions = new();
|
||||
private readonly List<Exception> _updateExceptions = new();
|
||||
|
||||
private DateTime _lastRenderExceptionLog;
|
||||
private DateTime _lastUpdateExceptionLog;
|
||||
|
||||
public ProfileService(ILogger logger,
|
||||
IRgbService rgbService,
|
||||
IProfileCategoryRepository profileCategoryRepository,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IInputService inputService,
|
||||
IDeviceService deviceService,
|
||||
IRenderService renderService,
|
||||
IProfileRepository profileRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
_rgbService = rgbService;
|
||||
_profileCategoryRepository = profileCategoryRepository;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_deviceService = deviceService;
|
||||
_profileRepository = profileRepository;
|
||||
_profileCategories = new List<ProfileCategory>(_profileCategoryRepository.GetAll().Select(c => new ProfileCategory(c)).OrderBy(c => c.Order));
|
||||
|
||||
_deviceService.LedsChanged += DeviceServiceOnLedsChanged;
|
||||
_rgbService.LedsChanged += RgbServiceOnLedsChanged;
|
||||
_pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureToggled;
|
||||
_pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureToggled;
|
||||
|
||||
@ -55,15 +54,11 @@ internal class ProfileService : IProfileService, IRenderer
|
||||
if (!_profileCategories.Any())
|
||||
CreateDefaultProfileCategories();
|
||||
UpdateModules();
|
||||
|
||||
renderService.Renderers.Add(this);
|
||||
}
|
||||
|
||||
public ProfileConfiguration? FocusProfile { get; set; }
|
||||
public ProfileElement? FocusProfileElement { get; set; }
|
||||
public bool UpdateFocusProfile { get; set; }
|
||||
|
||||
public bool ProfileRenderingDisabled { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UpdateProfiles(double deltaTime)
|
||||
@ -187,21 +182,6 @@ internal class ProfileService : IProfileService, IRenderer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Render(SKCanvas canvas, double delta)
|
||||
{
|
||||
if (ProfileRenderingDisabled)
|
||||
return;
|
||||
|
||||
UpdateProfiles(delta);
|
||||
RenderProfiles(canvas);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PostRender(SKTexture texture)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void LoadProfileConfigurationIcon(ProfileConfiguration profileConfiguration)
|
||||
@ -255,7 +235,7 @@ internal class ProfileService : IProfileService, IRenderer
|
||||
throw new ArtemisCoreException($"Cannot find profile named: {profileConfiguration.Name} ID: {profileConfiguration.Entity.ProfileId}");
|
||||
|
||||
Profile profile = new(profileConfiguration, profileEntity);
|
||||
profile.PopulateLeds(_deviceService.EnabledDevices);
|
||||
profile.PopulateLeds(_rgbService.EnabledDevices);
|
||||
|
||||
if (profile.IsFreshImport)
|
||||
{
|
||||
@ -543,7 +523,7 @@ internal class ProfileService : IProfileService, IRenderer
|
||||
/// <inheritdoc />
|
||||
public void AdaptProfile(Profile profile)
|
||||
{
|
||||
List<ArtemisDevice> devices = _deviceService.EnabledDevices.ToList();
|
||||
List<ArtemisDevice> devices = _rgbService.EnabledDevices.ToList();
|
||||
foreach (Layer layer in profile.GetAllLayers())
|
||||
layer.Adapter.Adapt(devices);
|
||||
|
||||
@ -572,7 +552,7 @@ internal class ProfileService : IProfileService, IRenderer
|
||||
foreach (ProfileConfiguration profileConfiguration in ProfileConfigurations)
|
||||
{
|
||||
if (profileConfiguration.Profile == null) continue;
|
||||
profileConfiguration.Profile.PopulateLeds(_deviceService.EnabledDevices);
|
||||
profileConfiguration.Profile.PopulateLeds(_rgbService.EnabledDevices);
|
||||
|
||||
if (!profileConfiguration.Profile.IsFreshImport) continue;
|
||||
_logger.Debug("Profile is a fresh import, adapting to surface - {profile}", profileConfiguration.Profile);
|
||||
@ -593,7 +573,7 @@ internal class ProfileService : IProfileService, IRenderer
|
||||
}
|
||||
}
|
||||
|
||||
private void DeviceServiceOnLedsChanged(object? sender, EventArgs e)
|
||||
private void RgbServiceOnLedsChanged(object? sender, EventArgs e)
|
||||
{
|
||||
ActiveProfilesPopulateLeds();
|
||||
}
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
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,4 +156,28 @@ public static class Utilities
|
||||
{
|
||||
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>
|
||||
public class DeviceVisualizer : Control
|
||||
{
|
||||
internal static readonly Dictionary<string, RenderTargetBitmap?> BitmapCache = new();
|
||||
private readonly IRenderService _renderService;
|
||||
internal static readonly Dictionary<ArtemisDevice, RenderTargetBitmap?> BitmapCache = new();
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly List<DeviceVisualizerLed> _deviceVisualizerLeds;
|
||||
|
||||
private Rect _deviceBounds;
|
||||
@ -40,7 +40,7 @@ public class DeviceVisualizer : Control
|
||||
/// <inheritdoc />
|
||||
public DeviceVisualizer()
|
||||
{
|
||||
_renderService = UI.Locator.Resolve<IRenderService>();
|
||||
_coreService = UI.Locator.Resolve<ICoreService>();
|
||||
_deviceVisualizerLeds = new List<DeviceVisualizerLed>();
|
||||
|
||||
PointerReleased += OnPointerReleased;
|
||||
@ -195,6 +195,16 @@ public class DeviceVisualizer : Control
|
||||
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)
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(SetupForDevice, DispatcherPriority.Background);
|
||||
@ -241,6 +251,7 @@ public class DeviceVisualizer : Control
|
||||
{
|
||||
if (Device != null)
|
||||
{
|
||||
Device.RgbDevice.PropertyChanged -= DevicePropertyChanged;
|
||||
Device.DeviceUpdated -= DeviceUpdated;
|
||||
}
|
||||
|
||||
@ -250,7 +261,7 @@ public class DeviceVisualizer : Control
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
_renderService.FrameRendered += OnFrameRendered;
|
||||
_coreService.FrameRendered += OnFrameRendered;
|
||||
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
}
|
||||
@ -258,7 +269,7 @@ public class DeviceVisualizer : Control
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
_renderService.FrameRendered -= OnFrameRendered;
|
||||
_coreService.FrameRendered -= OnFrameRendered;
|
||||
|
||||
base.OnDetachedFromLogicalTree(e);
|
||||
}
|
||||
@ -272,6 +283,7 @@ public class DeviceVisualizer : Control
|
||||
|
||||
if (_oldDevice != null)
|
||||
{
|
||||
_oldDevice.RgbDevice.PropertyChanged -= DevicePropertyChanged;
|
||||
_oldDevice.DeviceUpdated -= DeviceUpdated;
|
||||
}
|
||||
|
||||
@ -282,6 +294,7 @@ public class DeviceVisualizer : Control
|
||||
_deviceBounds = MeasureDevice();
|
||||
_loading = true;
|
||||
|
||||
Device.RgbDevice.PropertyChanged += DevicePropertyChanged;
|
||||
Device.DeviceUpdated += DeviceUpdated;
|
||||
|
||||
// Create all the LEDs
|
||||
@ -308,15 +321,12 @@ public class DeviceVisualizer : Control
|
||||
|
||||
private RenderTargetBitmap? GetDeviceImage(ArtemisDevice device)
|
||||
{
|
||||
string? path = device.Layout?.Image?.LocalPath;
|
||||
if (path == null)
|
||||
return null;
|
||||
|
||||
if (BitmapCache.TryGetValue(path, out RenderTargetBitmap? existingBitmap))
|
||||
if (BitmapCache.TryGetValue(device, out RenderTargetBitmap? existingBitmap))
|
||||
return existingBitmap;
|
||||
if (!File.Exists(path))
|
||||
|
||||
if (device.Layout?.Image == null || !File.Exists(device.Layout.Image.LocalPath))
|
||||
{
|
||||
BitmapCache[path] = null;
|
||||
BitmapCache[device] = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -325,7 +335,7 @@ public class DeviceVisualizer : Control
|
||||
RenderTargetBitmap renderTargetBitmap = new(new PixelSize((int) device.RgbDevice.ActualSize.Width * 2, (int) device.RgbDevice.ActualSize.Height * 2));
|
||||
|
||||
using DrawingContext context = renderTargetBitmap.CreateDrawingContext();
|
||||
using Bitmap bitmap = new(path);
|
||||
using Bitmap bitmap = new(device.Layout.Image.LocalPath);
|
||||
using Bitmap scaledBitmap = bitmap.CreateScaledBitmap(renderTargetBitmap.PixelSize);
|
||||
|
||||
context.DrawImage(scaledBitmap, new Rect(scaledBitmap.Size));
|
||||
@ -335,7 +345,7 @@ public class DeviceVisualizer : Control
|
||||
deviceVisualizerLed.DrawBitmap(context, 2 * device.Scale);
|
||||
}
|
||||
|
||||
BitmapCache[path] = renderTargetBitmap;
|
||||
BitmapCache[device] = renderTargetBitmap;
|
||||
return renderTargetBitmap;
|
||||
}
|
||||
|
||||
|
||||
@ -11,12 +11,7 @@ public interface IMainWindowService : IArtemisSharedUIService
|
||||
/// Gets a boolean indicating whether the main window is currently open
|
||||
/// </summary>
|
||||
bool IsMainWindowOpen { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the main window is currently focused
|
||||
/// </summary>
|
||||
bool IsMainWindowFocused { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the main window provider that controls the state of the main window
|
||||
/// </summary>
|
||||
|
||||
@ -10,9 +10,6 @@ internal class MainWindowService : IMainWindowService
|
||||
/// <inheritdoc />
|
||||
public bool IsMainWindowOpen { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsMainWindowFocused { get; private set; }
|
||||
|
||||
protected virtual void OnMainWindowOpened()
|
||||
{
|
||||
MainWindowOpened?.Invoke(this, EventArgs.Empty);
|
||||
@ -27,13 +24,11 @@ internal class MainWindowService : IMainWindowService
|
||||
protected virtual void OnMainWindowFocused()
|
||||
{
|
||||
MainWindowFocused?.Invoke(this, EventArgs.Empty);
|
||||
IsMainWindowFocused = true;
|
||||
}
|
||||
|
||||
protected virtual void OnMainWindowUnfocused()
|
||||
{
|
||||
MainWindowUnfocused?.Invoke(this, EventArgs.Empty);
|
||||
IsMainWindowFocused = false;
|
||||
}
|
||||
|
||||
private void SyncWithManager()
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Avalonia.Input;
|
||||
using Material.Icons;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.ProfileEditor;
|
||||
@ -45,11 +43,6 @@ public interface IToolViewModel : IDisposable
|
||||
/// Gets the tooltip which this tool should show in the toolbar.
|
||||
/// </summary>
|
||||
public string ToolTip { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keyboard hotkey that activates the tool.
|
||||
/// </summary>
|
||||
Hotkey? Hotkey { get; }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IToolViewModel" />
|
||||
@ -105,8 +98,5 @@ public abstract class ToolViewModel : ActivatableViewModelBase, IToolViewModel
|
||||
/// <inheritdoc />
|
||||
public abstract string ToolTip { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract Hotkey? Hotkey { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -21,7 +21,6 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
private readonly ILayerBrushService _layerBrushService;
|
||||
private readonly BehaviorSubject<ILayerProperty?> _layerPropertySubject = new(null);
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IModuleService _moduleService;
|
||||
private readonly BehaviorSubject<int> _pixelsPerSecondSubject = new(120);
|
||||
private readonly BehaviorSubject<bool> _playingSubject = new(false);
|
||||
@ -29,6 +28,7 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
private readonly Dictionary<ProfileConfiguration, ProfileEditorHistory> _profileEditorHistories = new();
|
||||
private readonly BehaviorSubject<RenderProfileElement?> _profileElementSubject = new(null);
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly SourceList<ILayerPropertyKeyframe> _selectedKeyframes;
|
||||
private readonly BehaviorSubject<bool> _suspendedEditingSubject = new(false);
|
||||
private readonly BehaviorSubject<TimeSpan> _timeSubject = new(TimeSpan.Zero);
|
||||
@ -36,17 +36,17 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
private ProfileEditorCommandScope? _profileEditorHistoryScope;
|
||||
|
||||
public ProfileEditorService(ILogger logger,
|
||||
IDeviceService deviceService,
|
||||
IProfileService profileService,
|
||||
IModuleService moduleService,
|
||||
IRgbService rgbService,
|
||||
ILayerBrushService layerBrushService,
|
||||
IMainWindowService mainWindowService,
|
||||
IWindowService windowService)
|
||||
{
|
||||
_logger = logger;
|
||||
_deviceService = deviceService;
|
||||
_profileService = profileService;
|
||||
_moduleService = moduleService;
|
||||
_rgbService = rgbService;
|
||||
_layerBrushService = layerBrushService;
|
||||
_windowService = windowService;
|
||||
|
||||
@ -369,7 +369,7 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
Layer layer = new(targetLayer.Parent, targetLayer.GetNewLayerName());
|
||||
_layerBrushService.ApplyDefaultBrush(layer);
|
||||
|
||||
layer.AddLeds(_deviceService.EnabledDevices.SelectMany(d => d.Leds));
|
||||
layer.AddLeds(_rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
||||
ExecuteCommand(new AddProfileElement(layer, targetLayer.Parent, targetLayer.Order));
|
||||
|
||||
return layer;
|
||||
@ -379,7 +379,7 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
Layer layer = new(target, target.GetNewLayerName());
|
||||
_layerBrushService.ApplyDefaultBrush(layer);
|
||||
|
||||
layer.AddLeds(_deviceService.EnabledDevices.SelectMany(d => d.Leds));
|
||||
layer.AddLeds(_rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
||||
ExecuteCommand(new AddProfileElement(layer, target, 0));
|
||||
|
||||
return layer;
|
||||
|
||||
@ -22,11 +22,10 @@ public static class UI
|
||||
|
||||
static UI()
|
||||
{
|
||||
CurrentKeyBindingsEnabled = InputElement.GotFocusEvent.Raised.Select(e => e.Item2.Source is not TextBox).StartWith(true);
|
||||
CurrentKeyBindingsEnabled.Subscribe(b => KeyBindingsEnabled = b);
|
||||
KeyBindingsEnabled = InputElement.GotFocusEvent.Raised.Select(e => e.Item2.Source is not TextBox).StartWith(true);
|
||||
MicaEnabled = MicaEnabledSubject.AsObservable();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current IoC locator.
|
||||
/// </summary>
|
||||
@ -37,15 +36,10 @@ public static class UI
|
||||
/// </summary>
|
||||
public static IClipboard Clipboard { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an observable boolean indicating whether hotkeys are to be disabled.
|
||||
/// </summary>
|
||||
public static IObservable<bool> CurrentKeyBindingsEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether hotkeys are to be disabled.
|
||||
/// </summary>
|
||||
public static bool KeyBindingsEnabled { get; private set; }
|
||||
public static IObservable<bool> KeyBindingsEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the Mica effect should be enabled.
|
||||
|
||||
@ -14,7 +14,7 @@ namespace Artemis.UI.Screens.Debugger.Performance;
|
||||
|
||||
public class PerformanceDebugViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly IRenderService _renderService;
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly DispatcherTimer _updateTimer;
|
||||
private double _currentFps;
|
||||
@ -22,9 +22,9 @@ public class PerformanceDebugViewModel : ActivatableViewModelBase
|
||||
private int _renderHeight;
|
||||
private int _renderWidth;
|
||||
|
||||
public PerformanceDebugViewModel(IRenderService renderService, IPluginManagementService pluginManagementService)
|
||||
public PerformanceDebugViewModel(ICoreService coreService, IPluginManagementService pluginManagementService)
|
||||
{
|
||||
_renderService = renderService;
|
||||
_coreService = coreService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Background, (_, _) => Update());
|
||||
|
||||
@ -87,12 +87,12 @@ public class PerformanceDebugViewModel : ActivatableViewModelBase
|
||||
private void HandleActivation()
|
||||
{
|
||||
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
||||
_renderService.FrameRendered += RenderServiceOnFrameRendered;
|
||||
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
||||
}
|
||||
|
||||
private void HandleDeactivation()
|
||||
{
|
||||
_renderService.FrameRendered -= RenderServiceOnFrameRendered;
|
||||
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
|
||||
}
|
||||
|
||||
private void PopulateItems()
|
||||
@ -116,9 +116,9 @@ public class PerformanceDebugViewModel : ActivatableViewModelBase
|
||||
viewModel.Update();
|
||||
}
|
||||
|
||||
private void RenderServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
||||
private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
||||
{
|
||||
CurrentFps = _renderService.FrameRate;
|
||||
CurrentFps = _coreService.FrameRate;
|
||||
SKImageInfo bitmapInfo = e.Texture.ImageInfo;
|
||||
|
||||
RenderHeight = bitmapInfo.Height;
|
||||
|
||||
@ -11,7 +11,7 @@ namespace Artemis.UI.Screens.Debugger.Render;
|
||||
|
||||
public class RenderDebugViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly IRenderService _renderService;
|
||||
private readonly ICoreService _coreService;
|
||||
private double _currentFps;
|
||||
|
||||
private Bitmap? _currentFrame;
|
||||
@ -20,9 +20,9 @@ public class RenderDebugViewModel : ActivatableViewModelBase
|
||||
private int _renderHeight;
|
||||
private int _renderWidth;
|
||||
|
||||
public RenderDebugViewModel(IRenderService renderService)
|
||||
public RenderDebugViewModel(ICoreService coreService)
|
||||
{
|
||||
_renderService = renderService;
|
||||
_coreService = coreService;
|
||||
|
||||
DisplayName = "Rendering";
|
||||
|
||||
@ -66,17 +66,17 @@ public class RenderDebugViewModel : ActivatableViewModelBase
|
||||
private void HandleActivation()
|
||||
{
|
||||
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
||||
_renderService.FrameRendered += RenderServiceOnFrameRendered;
|
||||
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
||||
}
|
||||
|
||||
private void HandleDeactivation()
|
||||
{
|
||||
_renderService.FrameRendered -= RenderServiceOnFrameRendered;
|
||||
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
|
||||
}
|
||||
|
||||
private void RenderServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
||||
private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
||||
{
|
||||
CurrentFps = _renderService.FrameRate;
|
||||
CurrentFps = _coreService.FrameRate;
|
||||
using SKImage skImage = e.Texture.Surface.Snapshot();
|
||||
SKImageInfo bitmapInfo = e.Texture.ImageInfo;
|
||||
|
||||
|
||||
@ -17,16 +17,20 @@ namespace Artemis.UI.Screens.Device;
|
||||
public class DeviceDetectInputViewModel : ContentDialogViewModelBase
|
||||
{
|
||||
private readonly IInputService _inputService;
|
||||
private readonly ListLedGroup _ledGroup;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly IRgbService _rgbService;
|
||||
|
||||
public DeviceDetectInputViewModel(ArtemisDevice device, IInputService inputService, INotificationService notificationService, IRenderService renderService)
|
||||
public DeviceDetectInputViewModel(ArtemisDevice device, IInputService inputService, INotificationService notificationService, IRgbService rgbService)
|
||||
{
|
||||
_inputService = inputService;
|
||||
_notificationService = notificationService;
|
||||
_rgbService = rgbService;
|
||||
|
||||
Device = device;
|
||||
|
||||
// Create a LED group way at the top
|
||||
ListLedGroup ledGroup = new(renderService.Surface, Device.Leds.Select(l => l.RgbLed))
|
||||
_ledGroup = new ListLedGroup(_rgbService.Surface, Device.Leds.Select(l => l.RgbLed))
|
||||
{
|
||||
Brush = new SolidColorBrush(new Color(255, 255, 0)),
|
||||
ZIndex = 999
|
||||
@ -42,7 +46,7 @@ public class DeviceDetectInputViewModel : ContentDialogViewModelBase
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
_inputService.StopIdentify();
|
||||
ledGroup.Detach();
|
||||
_ledGroup.Detach();
|
||||
}).DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
|
||||
private readonly IDeviceVmFactory _deviceVmFactory;
|
||||
private ArtemisDevice _device;
|
||||
|
||||
public DevicePropertiesViewModel(ArtemisDevice device, IRenderService renderService, IDeviceService deviceService, IDeviceVmFactory deviceVmFactory)
|
||||
public DevicePropertiesViewModel(ArtemisDevice device, ICoreService coreService, IRgbService rgbService, IDeviceVmFactory deviceVmFactory)
|
||||
{
|
||||
_deviceVmFactory = deviceVmFactory;
|
||||
_device = device;
|
||||
@ -28,15 +28,10 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
|
||||
AddTabs();
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
deviceService.DeviceAdded += DeviceServiceOnDeviceAdded;
|
||||
deviceService.DeviceRemoved += DeviceServiceOnDeviceRemoved;
|
||||
renderService.FrameRendering += RenderServiceOnFrameRendering;
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
deviceService.DeviceAdded -= DeviceServiceOnDeviceAdded;
|
||||
deviceService.DeviceRemoved -= DeviceServiceOnDeviceRemoved;
|
||||
renderService.FrameRendering -= RenderServiceOnFrameRendering;
|
||||
}).DisposeWith(d);
|
||||
rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
|
||||
rgbService.DeviceRemoved += RgbServiceOnDeviceRemoved;
|
||||
coreService.FrameRendering += CoreServiceOnFrameRendering;
|
||||
Disposable.Create(() => coreService.FrameRendering -= CoreServiceOnFrameRendering).DisposeWith(d);
|
||||
});
|
||||
|
||||
ClearSelectedLeds = ReactiveCommand.Create(ExecuteClearSelectedLeds);
|
||||
@ -52,7 +47,7 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
|
||||
public ObservableCollection<ActivatableViewModelBase> Tabs { get; }
|
||||
public ReactiveCommand<Unit, Unit> ClearSelectedLeds { get; }
|
||||
|
||||
private void DeviceServiceOnDeviceAdded(object? sender, DeviceEventArgs e)
|
||||
private void RgbServiceOnDeviceAdded(object? sender, DeviceEventArgs e)
|
||||
{
|
||||
if (e.Device.Identifier != Device.Identifier || Device == e.Device)
|
||||
return;
|
||||
@ -61,7 +56,7 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
|
||||
AddTabs();
|
||||
}
|
||||
|
||||
private void DeviceServiceOnDeviceRemoved(object? sender, DeviceEventArgs e)
|
||||
private void RgbServiceOnDeviceRemoved(object? sender, DeviceEventArgs e)
|
||||
{
|
||||
Tabs.Clear();
|
||||
SelectedLeds.Clear();
|
||||
@ -81,7 +76,7 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
|
||||
SelectedLeds.Clear();
|
||||
}
|
||||
|
||||
private void RenderServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)
|
||||
private void CoreServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)
|
||||
{
|
||||
if (!SelectedLeds.Any())
|
||||
return;
|
||||
|
||||
@ -18,15 +18,18 @@ public class DeviceSettingsViewModel : ActivatableViewModelBase
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly DevicesTabViewModel _devicesTabViewModel;
|
||||
private readonly IDeviceVmFactory _deviceVmFactory;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IWindowService _windowService;
|
||||
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;
|
||||
_deviceService = deviceService;
|
||||
_windowService = windowService;
|
||||
_deviceVmFactory = deviceVmFactory;
|
||||
_rgbService = rgbService;
|
||||
Device = device;
|
||||
|
||||
Type = Device.DeviceType.ToString().Humanize();
|
||||
@ -84,7 +87,7 @@ public class DeviceSettingsViewModel : ActivatableViewModelBase
|
||||
.ShowAsync();
|
||||
|
||||
if (viewModel.MadeChanges)
|
||||
_deviceService.SaveDevice(Device);
|
||||
_rgbService.SaveDevice(Device);
|
||||
}
|
||||
|
||||
private async Task UpdateIsDeviceEnabled(bool value)
|
||||
@ -100,9 +103,9 @@ public class DeviceSettingsViewModel : ActivatableViewModelBase
|
||||
value = !await _devicesTabViewModel.ShowDeviceDisableDialog();
|
||||
|
||||
if (value)
|
||||
_deviceService.EnableDevice(Device);
|
||||
_rgbService.EnableDevice(Device);
|
||||
else
|
||||
_deviceService.DisableDevice(Device);
|
||||
_rgbService.DisableDevice(Device);
|
||||
|
||||
this.RaisePropertyChanged(nameof(IsDeviceEnabled));
|
||||
SaveDevice();
|
||||
@ -115,6 +118,6 @@ public class DeviceSettingsViewModel : ActivatableViewModelBase
|
||||
|
||||
private void SaveDevice()
|
||||
{
|
||||
_deviceService.SaveDevice(Device);
|
||||
_rgbService.SaveDevice(Device);
|
||||
}
|
||||
}
|
||||
@ -20,8 +20,7 @@ namespace Artemis.UI.Screens.Device;
|
||||
public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRenderService _renderService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly List<DeviceCategory> _categories;
|
||||
|
||||
@ -40,11 +39,10 @@ public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
||||
private SKColor _currentColor;
|
||||
private bool _displayOnDevices;
|
||||
|
||||
public DeviceGeneralTabViewModel(ArtemisDevice device, ICoreService coreService, IDeviceService deviceService, IRenderService renderService, IWindowService windowService)
|
||||
public DeviceGeneralTabViewModel(ArtemisDevice device, ICoreService coreService, IRgbService rgbService, IWindowService windowService)
|
||||
{
|
||||
_coreService = coreService;
|
||||
_deviceService = deviceService;
|
||||
_renderService = renderService;
|
||||
_rgbService = rgbService;
|
||||
_windowService = windowService;
|
||||
_categories = new List<DeviceCategory>(device.Categories);
|
||||
|
||||
@ -68,11 +66,11 @@ public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
_renderService.FrameRendering += OnFrameRendering;
|
||||
_coreService.FrameRendering += OnFrameRendering;
|
||||
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
_renderService.FrameRendering -= OnFrameRendering;
|
||||
_coreService.FrameRendering -= OnFrameRendering;
|
||||
Apply();
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
@ -193,12 +191,17 @@ public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
||||
return;
|
||||
|
||||
await Task.Delay(400);
|
||||
_deviceService.SaveDevice(Device);
|
||||
_deviceService.ApplyDeviceLayout(Device, Device.GetBestDeviceLayout());
|
||||
_rgbService.SaveDevice(Device);
|
||||
_rgbService.ApplyBestDeviceLayout(Device);
|
||||
}
|
||||
|
||||
private void Apply()
|
||||
{
|
||||
// TODO: Validation
|
||||
|
||||
_coreService.ProfileRenderingDisabled = true;
|
||||
Thread.Sleep(100);
|
||||
|
||||
Device.X = X;
|
||||
Device.Y = Y;
|
||||
Device.Scale = Scale;
|
||||
@ -210,7 +213,9 @@ public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
||||
foreach (DeviceCategory deviceCategory in _categories)
|
||||
Device.Categories.Add(deviceCategory);
|
||||
|
||||
_deviceService.SaveDevice(Device);
|
||||
_rgbService.SaveDevice(Device);
|
||||
|
||||
_coreService.ProfileRenderingDisabled = false;
|
||||
}
|
||||
|
||||
public void ApplyScaling()
|
||||
@ -218,7 +223,8 @@ public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
||||
Device.RedScale = RedScale / 100f;
|
||||
Device.GreenScale = GreenScale / 100f;
|
||||
Device.BlueScale = BlueScale / 100f;
|
||||
Device.RgbDevice.Update(true);
|
||||
|
||||
_rgbService.FlushLeds = true;
|
||||
}
|
||||
|
||||
public void ResetScaling()
|
||||
@ -226,7 +232,6 @@ public class DeviceGeneralTabViewModel : ActivatableViewModelBase
|
||||
RedScale = _initialRedScale * 100;
|
||||
GreenScale = _initialGreenScale * 100;
|
||||
BlueScale = _initialBlueScale * 100;
|
||||
Device.RgbDevice.Update(true);
|
||||
}
|
||||
|
||||
private void OnFrameRendering(object? sender, FrameRenderingEventArgs e)
|
||||
|
||||
@ -22,13 +22,20 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IRgbService _rgbService;
|
||||
|
||||
public DeviceLayoutTabViewModel(IWindowService windowService, INotificationService notificationService, IDeviceService deviceService, ArtemisDevice device)
|
||||
public DeviceLayoutTabViewModel(
|
||||
IWindowService windowService,
|
||||
INotificationService notificationService,
|
||||
ICoreService coreService,
|
||||
IRgbService rgbService,
|
||||
ArtemisDevice device)
|
||||
{
|
||||
_windowService = windowService;
|
||||
_notificationService = notificationService;
|
||||
_deviceService = deviceService;
|
||||
_coreService = coreService;
|
||||
_rgbService = rgbService;
|
||||
|
||||
Device = device;
|
||||
DisplayName = "Layout";
|
||||
@ -37,7 +44,11 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Device.PropertyChanged += DeviceOnPropertyChanged;
|
||||
Disposable.Create(() => Device.PropertyChanged -= DeviceOnPropertyChanged).DisposeWith(d);
|
||||
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
Device.PropertyChanged -= DeviceOnPropertyChanged;
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
@ -47,7 +58,7 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
||||
|
||||
public string? ImagePath => Device.Layout?.Image?.LocalPath;
|
||||
|
||||
public string? CustomLayoutPath => Device.CustomLayoutPath;
|
||||
public string CustomLayoutPath => Device.CustomLayoutPath;
|
||||
|
||||
public bool HasCustomLayout => Device.CustomLayoutPath != null;
|
||||
|
||||
@ -57,8 +68,6 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
||||
_notificationService.CreateNotification()
|
||||
.WithMessage("Cleared imported layout.")
|
||||
.WithSeverity(NotificationSeverity.Informational);
|
||||
|
||||
_deviceService.SaveDevice(Device);
|
||||
}
|
||||
|
||||
public async Task BrowseCustomLayout()
|
||||
@ -75,8 +84,6 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
||||
.WithTitle("Imported layout")
|
||||
.WithMessage($"File loaded from {files[0]}")
|
||||
.WithSeverity(NotificationSeverity.Informational);
|
||||
|
||||
_deviceService.SaveDevice(Device);
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,10 +160,6 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
||||
private void DeviceOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName is nameof(Device.CustomLayoutPath) or nameof(Device.DisableDefaultLayout))
|
||||
{
|
||||
Task.Run(() => _deviceService.ApplyDeviceLayout(Device, Device.GetBestDeviceLayout()));
|
||||
this.RaisePropertyChanged(nameof(CustomLayoutPath));
|
||||
this.RaisePropertyChanged(nameof(HasCustomLayout));
|
||||
}
|
||||
Task.Run(() => _rgbService.ApplyBestDeviceLayout(Device));
|
||||
}
|
||||
}
|
||||
@ -16,17 +16,17 @@ namespace Artemis.UI.Screens.Device;
|
||||
public class InputMappingsTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly IInputService _inputService;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly ObservableCollection<ArtemisLed> _selectedLeds;
|
||||
private ArtemisLed? _selectedLed;
|
||||
|
||||
public InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds, IInputService inputService, IDeviceService deviceService)
|
||||
public InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds, IRgbService rgbService, IInputService inputService)
|
||||
{
|
||||
if (device.DeviceType != RGBDeviceType.Keyboard)
|
||||
throw new ArtemisUIException("The input mappings tab only supports keyboards");
|
||||
|
||||
_rgbService = rgbService;
|
||||
_inputService = inputService;
|
||||
_deviceService = deviceService;
|
||||
_selectedLeds = selectedLeds;
|
||||
|
||||
Device = device;
|
||||
@ -81,7 +81,7 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase
|
||||
|
||||
// Apply the new LED mapping
|
||||
Device.InputMappings[SelectedLed] = artemisLed;
|
||||
_deviceService.SaveDevice(Device);
|
||||
_rgbService.SaveDevice(Device);
|
||||
_selectedLeds.Clear();
|
||||
|
||||
UpdateInputMappings();
|
||||
|
||||
@ -58,7 +58,7 @@ public class MenuBarViewModel : ActivatableViewModelBase
|
||||
_focusNone = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.None).ToProperty(this, vm => vm.FocusNone).DisposeWith(d);
|
||||
_focusFolder = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.Folder).ToProperty(this, vm => vm.FocusFolder).DisposeWith(d);
|
||||
_focusSelection = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.Selection).ToProperty(this, vm => vm.FocusSelection).DisposeWith(d);
|
||||
_keyBindingsEnabled = Shared.UI.CurrentKeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
|
||||
_keyBindingsEnabled = Shared.UI.KeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
|
||||
});
|
||||
|
||||
AddFolder = ReactiveCommand.Create(ExecuteAddFolder);
|
||||
|
||||
@ -53,7 +53,7 @@ public class PlaybackViewModel : ActivatableViewModelBase
|
||||
_currentTime = _profileEditorService.Time.ToProperty(this, vm => vm.CurrentTime).DisposeWith(d);
|
||||
_formattedCurrentTime = _profileEditorService.Time.Select(t => $"{Math.Floor(t.TotalSeconds):00}.{t.Milliseconds:000}").ToProperty(this, vm => vm.FormattedCurrentTime).DisposeWith(d);
|
||||
_playing = _profileEditorService.Playing.ToProperty(this, vm => vm.Playing).DisposeWith(d);
|
||||
_keyBindingsEnabled = Shared.UI.CurrentKeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
|
||||
_keyBindingsEnabled = Shared.UI.KeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
|
||||
|
||||
// Update timer
|
||||
Timer updateTimer = new(TimeSpan.FromMilliseconds(60.0 / 1000));
|
||||
|
||||
@ -15,12 +15,12 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs;
|
||||
|
||||
public class LayerHintsDialogViewModel : DialogViewModelBase<bool>
|
||||
{
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly ILayerHintVmFactory _vmFactory;
|
||||
|
||||
public LayerHintsDialogViewModel(Layer layer, IDeviceService deviceService, ILayerHintVmFactory vmFactory)
|
||||
public LayerHintsDialogViewModel(Layer layer, IRgbService rgbService, ILayerHintVmFactory vmFactory)
|
||||
{
|
||||
_deviceService = deviceService;
|
||||
_rgbService = rgbService;
|
||||
_vmFactory = vmFactory;
|
||||
|
||||
Layer = layer;
|
||||
@ -49,7 +49,7 @@ public class LayerHintsDialogViewModel : DialogViewModelBase<bool>
|
||||
|
||||
public void AutoDetermineHints()
|
||||
{
|
||||
Layer.Adapter.DetermineHints(_deviceService.EnabledDevices);
|
||||
Layer.Adapter.DetermineHints(_rgbService.EnabledDevices);
|
||||
}
|
||||
|
||||
public void AddCategoryHint()
|
||||
|
||||
@ -14,17 +14,17 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree;
|
||||
|
||||
public class FolderTreeItemViewModel : TreeItemViewModel
|
||||
{
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRgbService _rgbService;
|
||||
|
||||
public FolderTreeItemViewModel(TreeItemViewModel? parent,
|
||||
Folder folder,
|
||||
IWindowService windowService,
|
||||
IDeviceService deviceService,
|
||||
IProfileEditorService profileEditorService,
|
||||
IRgbService rgbService,
|
||||
IProfileEditorVmFactory profileEditorVmFactory)
|
||||
: base(parent, folder, windowService, deviceService, profileEditorService, profileEditorVmFactory)
|
||||
: base(parent, folder, windowService, rgbService, profileEditorService, profileEditorVmFactory)
|
||||
{
|
||||
_deviceService = deviceService;
|
||||
_rgbService = rgbService;
|
||||
Folder = folder;
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ public class FolderTreeItemViewModel : TreeItemViewModel
|
||||
pasted.Name = parent.GetNewFolderName(pasted.Name + " - copy");
|
||||
|
||||
ProfileEditorService.ExecuteCommand(new AddProfileElement(pasted, parent, Folder.Order - 1));
|
||||
Folder.Profile.PopulateLeds(_deviceService.EnabledDevices);
|
||||
Folder.Profile.PopulateLeds(_rgbService.EnabledDevices);
|
||||
}
|
||||
|
||||
private async Task ExecutePasteInto()
|
||||
|
||||
@ -14,17 +14,17 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree;
|
||||
|
||||
public class LayerTreeItemViewModel : TreeItemViewModel
|
||||
{
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRgbService _rgbService;
|
||||
|
||||
public LayerTreeItemViewModel(TreeItemViewModel? parent,
|
||||
Layer layer,
|
||||
IWindowService windowService,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDeviceService deviceService,
|
||||
IRgbService rgbService,
|
||||
IProfileEditorVmFactory profileEditorVmFactory)
|
||||
: base(parent, layer, windowService, deviceService, profileEditorService, profileEditorVmFactory)
|
||||
: base(parent, layer, windowService, rgbService, profileEditorService, profileEditorVmFactory)
|
||||
{
|
||||
_deviceService = deviceService;
|
||||
_rgbService = rgbService;
|
||||
Layer = layer;
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ public class LayerTreeItemViewModel : TreeItemViewModel
|
||||
|
||||
Layer copied = new(Layer.Profile, Layer.Parent, copy, true);
|
||||
ProfileEditorService.ExecuteCommand(new AddProfileElement(copied, Layer.Parent, Layer.Order - 1));
|
||||
Layer.Profile.PopulateLeds(_deviceService.EnabledDevices);
|
||||
Layer.Profile.PopulateLeds(_rgbService.EnabledDevices);
|
||||
}
|
||||
|
||||
protected override async Task ExecuteCopy()
|
||||
@ -65,7 +65,7 @@ public class LayerTreeItemViewModel : TreeItemViewModel
|
||||
pasted.Name = parent.GetNewLayerName(pasted.Name + " - copy");
|
||||
|
||||
ProfileEditorService.ExecuteCommand(new AddProfileElement(pasted, parent, Layer.Order - 1));
|
||||
Layer.Profile.PopulateLeds(_deviceService.EnabledDevices);
|
||||
Layer.Profile.PopulateLeds(_rgbService.EnabledDevices);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@ -23,8 +23,8 @@ public class ProfileTreeViewModel : TreeItemViewModel
|
||||
private ObservableAsPropertyHelper<bool>? _keyBindingsEnabled;
|
||||
private TreeItemViewModel? _selectedChild;
|
||||
|
||||
public ProfileTreeViewModel(IWindowService windowService, IDeviceService deviceService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory)
|
||||
: base(null, null, windowService, deviceService, profileEditorService, profileEditorVmFactory)
|
||||
public ProfileTreeViewModel(IWindowService windowService, IRgbService rgbService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory)
|
||||
: base(null, null, windowService, rgbService, profileEditorService, profileEditorVmFactory)
|
||||
{
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
@ -46,7 +46,7 @@ public class ProfileTreeViewModel : TreeItemViewModel
|
||||
_focusNone = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.None).ToProperty(this, vm => vm.FocusNone).DisposeWith(d);
|
||||
_focusFolder = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.Folder).ToProperty(this, vm => vm.FocusFolder).DisposeWith(d);
|
||||
_focusSelection = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.Selection).ToProperty(this, vm => vm.FocusSelection).DisposeWith(d);
|
||||
_keyBindingsEnabled = Shared.UI.CurrentKeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
|
||||
_keyBindingsEnabled = Shared.UI.KeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
|
||||
});
|
||||
|
||||
ClearSelection = ReactiveCommand.Create(() => profileEditorService.ChangeCurrentProfileElement(null), this.WhenAnyValue(vm => vm.KeyBindingsEnabled));
|
||||
|
||||
@ -28,7 +28,7 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly IProfileEditorVmFactory _profileEditorVmFactory;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRgbService _rgbService;
|
||||
protected readonly IProfileEditorService ProfileEditorService;
|
||||
private bool _canPaste;
|
||||
private RenderProfileElement? _currentProfileElement;
|
||||
@ -41,13 +41,13 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
|
||||
protected TreeItemViewModel(TreeItemViewModel? parent,
|
||||
ProfileElement? profileElement,
|
||||
IWindowService windowService,
|
||||
IDeviceService deviceService,
|
||||
IRgbService rgbService,
|
||||
IProfileEditorService profileEditorService,
|
||||
IProfileEditorVmFactory profileEditorVmFactory)
|
||||
{
|
||||
ProfileEditorService = profileEditorService;
|
||||
_windowService = windowService;
|
||||
_deviceService = deviceService;
|
||||
_rgbService = rgbService;
|
||||
_profileEditorVmFactory = profileEditorVmFactory;
|
||||
|
||||
Parent = parent;
|
||||
@ -267,7 +267,7 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
|
||||
if (ProfileElement is not Layer layer)
|
||||
return;
|
||||
|
||||
ProfileEditorService.ExecuteCommand(new ApplyAdaptionHints(layer, _deviceService.EnabledDevices.ToList()));
|
||||
ProfileEditorService.ExecuteCommand(new ApplyAdaptionHints(layer, _rgbService.EnabledDevices.ToList()));
|
||||
}
|
||||
|
||||
private async void UpdateCanPaste(bool isFlyoutOpen)
|
||||
|
||||
@ -3,21 +3,17 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:keyframes="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes.TimelineEasingView"
|
||||
x:DataType="keyframes:TimelineEasingViewModel">
|
||||
<Grid ColumnDefinitions="25,30,*">
|
||||
<avalonia:MaterialIcon Grid.Column="0" Kind="Check" IsVisible="{CompiledBinding IsEasingModeSelected}" HorizontalAlignment="Left"/>
|
||||
<Polyline Grid.Column="1"
|
||||
Stroke="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Polyline Stroke="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
StrokeThickness="1"
|
||||
Points="{CompiledBinding EasingPoints}"
|
||||
Stretch="Uniform"
|
||||
Width="20"
|
||||
Height="20"
|
||||
HorizontalAlignment="Left"/>
|
||||
<TextBlock Grid.Column="2" Text="{CompiledBinding Description}" HorizontalAlignment="Left"/>
|
||||
</Grid>
|
||||
|
||||
Margin="0 0 10 0" />
|
||||
<TextBlock Text="{CompiledBinding Description}" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@ -1,10 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reactive;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Humanizer;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes;
|
||||
|
||||
@ -12,12 +10,11 @@ public class TimelineEasingViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ILayerPropertyKeyframe _keyframe;
|
||||
|
||||
public TimelineEasingViewModel(Easings.Functions easingFunction, ILayerPropertyKeyframe keyframe, ReactiveCommand<Easings.Functions, Unit> selectEasingFunction)
|
||||
public TimelineEasingViewModel(Easings.Functions easingFunction, ILayerPropertyKeyframe keyframe)
|
||||
{
|
||||
_keyframe = keyframe;
|
||||
|
||||
EasingFunction = easingFunction;
|
||||
SelectEasingFunction = selectEasingFunction;
|
||||
Description = easingFunction.Humanize();
|
||||
|
||||
EasingPoints = new List<Point>();
|
||||
@ -30,7 +27,6 @@ public class TimelineEasingViewModel : ViewModelBase
|
||||
}
|
||||
|
||||
public Easings.Functions EasingFunction { get; }
|
||||
public ReactiveCommand<Easings.Functions, Unit> SelectEasingFunction { get; }
|
||||
public List<Point> EasingPoints { get; }
|
||||
public string Description { get; }
|
||||
public bool IsEasingModeSelected => _keyframe.EasingFunction == EasingFunction;
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:keyframes="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes.TimelineKeyframeView"
|
||||
ClipToBounds="False">
|
||||
@ -35,9 +34,16 @@
|
||||
<MenuFlyout Opening="FlyoutBase_OnOpening">
|
||||
<MenuItem Header="Easing" ItemsSource="{Binding EasingViewModels}">
|
||||
<MenuItem.Styles>
|
||||
<Style Selector="MenuItem > MenuItem" x:DataType="keyframes:TimelineEasingViewModel">
|
||||
<Setter Property="Command" Value="{CompiledBinding SelectEasingFunction}" />
|
||||
<Setter Property="CommandParameter" Value="{CompiledBinding EasingFunction}" />
|
||||
<Style Selector="MenuItem > MenuItem">
|
||||
<Setter Property="Icon">
|
||||
<Setter.Value>
|
||||
<Template>
|
||||
<CheckBox IsHitTestVisible="False" IsChecked="{Binding IsEasingModeSelected}" />
|
||||
</Template>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Command" Value="{Binding $parent[UserControl].DataContext.SelectEasingFunction}" />
|
||||
<Setter Property="CommandParameter" Value="{Binding EasingFunction}" />
|
||||
</Style>
|
||||
</MenuItem.Styles>
|
||||
<MenuItem.Icon>
|
||||
|
||||
@ -54,12 +54,10 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
|
||||
Copy = ReactiveCommand.CreateFromTask(ExecuteCopy);
|
||||
Paste = ReactiveCommand.CreateFromTask(ExecutePaste);
|
||||
Delete = ReactiveCommand.Create(ExecuteDelete);
|
||||
SelectEasingFunction = ReactiveCommand.Create<Easings.Functions>(ExecuteSelectEasingFunction);
|
||||
}
|
||||
|
||||
public LayerPropertyKeyframe<T> LayerPropertyKeyframe { get; }
|
||||
public ObservableCollection<TimelineEasingViewModel> EasingViewModels { get; }
|
||||
|
||||
|
||||
public double X
|
||||
{
|
||||
@ -95,8 +93,7 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
|
||||
public ReactiveCommand<Unit, Unit> Copy { get; }
|
||||
public ReactiveCommand<Unit, Unit> Paste { get; }
|
||||
public ReactiveCommand<Unit, Unit> Delete { get; }
|
||||
public ReactiveCommand<Easings.Functions, Unit> SelectEasingFunction { get; }
|
||||
|
||||
|
||||
public bool IsSelected => _isSelected?.Value ?? false;
|
||||
public TimeSpan Position => LayerPropertyKeyframe.Position;
|
||||
public ILayerPropertyKeyframe Keyframe => LayerPropertyKeyframe;
|
||||
@ -258,10 +255,10 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
|
||||
|
||||
EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions))
|
||||
.Cast<Easings.Functions>()
|
||||
.Select(e => new TimelineEasingViewModel(e, Keyframe, SelectEasingFunction)));
|
||||
.Select(e => new TimelineEasingViewModel(e, Keyframe)));
|
||||
}
|
||||
|
||||
private void ExecuteSelectEasingFunction(Easings.Functions easingFunction)
|
||||
public void SelectEasingFunction(Easings.Functions easingFunction)
|
||||
{
|
||||
_profileEditorService.ExecuteCommand(new ChangeKeyframeEasing(Keyframe, easingFunction));
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@ using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||
using Avalonia.Input;
|
||||
using Material.Icons;
|
||||
using ReactiveUI;
|
||||
using SkiaSharp;
|
||||
@ -18,14 +17,14 @@ public class SelectionAddToolViewModel : ToolViewModel
|
||||
{
|
||||
private readonly ObservableAsPropertyHelper<bool>? _isEnabled;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private Layer? _layer;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SelectionAddToolViewModel(IProfileEditorService profileEditorService, IDeviceService deviceService)
|
||||
public SelectionAddToolViewModel(IProfileEditorService profileEditorService, IRgbService rgbService)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_deviceService = deviceService;
|
||||
_rgbService = rgbService;
|
||||
// Not disposed when deactivated but when really disposed
|
||||
_isEnabled = profileEditorService.ProfileElement.Select(p => p is Layer).ToProperty(this, vm => vm.IsEnabled);
|
||||
|
||||
@ -46,12 +45,9 @@ public class SelectionAddToolViewModel : ToolViewModel
|
||||
|
||||
/// <inheritdoc />
|
||||
public override MaterialIconKind Icon => MaterialIconKind.SelectionDrag;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Hotkey? Hotkey { get; } = new(KeyboardKey.OemPlus, KeyboardModifierKey.Control);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToolTip => "Add LEDs to the current layer (Ctrl + +)";
|
||||
public override string ToolTip => "Add LEDs to the current layer";
|
||||
|
||||
public void AddLedsInRectangle(SKRect rect, bool expand, bool inverse)
|
||||
{
|
||||
@ -61,7 +57,7 @@ public class SelectionAddToolViewModel : ToolViewModel
|
||||
if (inverse)
|
||||
{
|
||||
List<ArtemisLed> toRemove = _layer.Leds.Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).ToList();
|
||||
List<ArtemisLed> toAdd = _deviceService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).Except(toRemove).ToList();
|
||||
List<ArtemisLed> toAdd = _rgbService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).Except(toRemove).ToList();
|
||||
List<ArtemisLed> leds = _layer.Leds.Except(toRemove).ToList();
|
||||
leds.AddRange(toAdd);
|
||||
|
||||
@ -69,7 +65,7 @@ public class SelectionAddToolViewModel : ToolViewModel
|
||||
}
|
||||
else
|
||||
{
|
||||
List<ArtemisLed> leds = _deviceService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).ToList();
|
||||
List<ArtemisLed> leds = _rgbService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).ToList();
|
||||
if (expand)
|
||||
leds.AddRange(_layer.Leds);
|
||||
_profileEditorService.ExecuteCommand(new ChangeLayerLeds(_layer, leds.Distinct().ToList()));
|
||||
|
||||
@ -4,10 +4,8 @@ using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||
using Avalonia.Input;
|
||||
using Material.Icons;
|
||||
using ReactiveUI;
|
||||
using SkiaSharp;
|
||||
@ -40,15 +38,12 @@ public class SelectionRemoveToolViewModel : ToolViewModel
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Order => 3;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Hotkey? Hotkey { get; } = new(KeyboardKey.OemMinus, KeyboardModifierKey.Control);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override MaterialIconKind Icon => MaterialIconKind.SelectOff;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToolTip => "Remove LEDs from the current layer (Ctrl + -)";
|
||||
public override string ToolTip => "Remove LEDs from the current layer";
|
||||
|
||||
public void RemoveLedsInRectangle(SKRect rect)
|
||||
{
|
||||
|
||||
@ -3,13 +3,11 @@ using System.Reactive;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Exceptions;
|
||||
using Artemis.UI.Shared.Extensions;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||
using Avalonia;
|
||||
using Avalonia.Input;
|
||||
using Material.Icons;
|
||||
using ReactiveUI;
|
||||
using SkiaSharp;
|
||||
@ -97,15 +95,12 @@ public class TransformToolViewModel : ToolViewModel
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Order => 3;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Hotkey? Hotkey { get; } = new(KeyboardKey.T, KeyboardModifierKey.Control);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override MaterialIconKind Icon => MaterialIconKind.TransitConnectionVariant;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToolTip => "Transform the shape of the current layer (Ctrl+T)";
|
||||
public override string ToolTip => "Transform the shape of the current layer";
|
||||
|
||||
public Rect ShapeBounds
|
||||
{
|
||||
|
||||
@ -25,7 +25,7 @@ public class VisualEditorViewModel : ActivatableViewModelBase
|
||||
private ObservableAsPropertyHelper<bool>? _suspendedEditing;
|
||||
private ReadOnlyObservableCollection<IToolViewModel>? _tools;
|
||||
|
||||
public VisualEditorViewModel(IProfileEditorService profileEditorService, IDeviceService deviceService, IProfileEditorVmFactory vmFactory)
|
||||
public VisualEditorViewModel(IProfileEditorService profileEditorService, IRgbService rgbService, IProfileEditorVmFactory vmFactory)
|
||||
{
|
||||
_vmFactory = vmFactory;
|
||||
_visualizers = new SourceList<IVisualizerViewModel>();
|
||||
@ -34,7 +34,7 @@ public class VisualEditorViewModel : ActivatableViewModelBase
|
||||
.Bind(out ReadOnlyObservableCollection<IVisualizerViewModel> visualizers)
|
||||
.Subscribe();
|
||||
|
||||
Devices = new ObservableCollection<ArtemisDevice>(deviceService.EnabledDevices.OrderBy(d => d.ZIndex));
|
||||
Devices = new ObservableCollection<ArtemisDevice>(rgbService.EnabledDevices.OrderBy(d => d.ZIndex));
|
||||
Visualizers = visualizers;
|
||||
|
||||
this.WhenActivated(d =>
|
||||
|
||||
@ -8,11 +8,19 @@
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared.Services.ProfileEditor;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileEditorView"
|
||||
x:DataType="profileEditor:ProfileEditorViewModel"
|
||||
Focusable="True">
|
||||
x:DataType="profileEditor:ProfileEditorViewModel">
|
||||
<UserControl.Resources>
|
||||
<converters:DoubleToGridLengthConverter x:Key="DoubleToGridLengthConverter" />
|
||||
</UserControl.Resources>
|
||||
<UserControl.KeyBindings>
|
||||
<KeyBinding Command="{CompiledBinding History.Undo}" Gesture="Ctrl+Z" />
|
||||
<KeyBinding Command="{CompiledBinding History.Redo}" Gesture="Ctrl+Y" />
|
||||
<KeyBinding Command="{CompiledBinding ToggleSuspend}" Gesture="F5" />
|
||||
<KeyBinding Command="{CompiledBinding ToggleAutoSuspend}" Gesture="Shift+F5" />
|
||||
<KeyBinding Command="{CompiledBinding PropertiesViewModel.PlaybackViewModel.TogglePlay}" Gesture="Space" />
|
||||
<KeyBinding Command="{CompiledBinding PropertiesViewModel.PlaybackViewModel.PlayFromStart}" Gesture="Shift+Space" />
|
||||
<KeyBinding Command="{Binding TitleBarViewModel.MenuBarViewModel.CycleFocusMode}" Gesture="F" />
|
||||
</UserControl.KeyBindings>
|
||||
<UserControl.Styles>
|
||||
<Style Selector="Border.suspended-editing">
|
||||
<Setter Property="Margin" Value="-10" />
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor;
|
||||
@ -8,4 +10,16 @@ public partial class ProfileEditorView : ReactiveUserControl<ProfileEditorViewMo
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
#region Overrides of Visual
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
Focus();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
@ -28,7 +28,6 @@ public class ProfileEditorViewModel : RoutableScreen<ProfileEditorViewModelParam
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IMainWindowService _mainWindowService;
|
||||
private readonly SourceList<IToolViewModel> _tools;
|
||||
private ObservableAsPropertyHelper<ProfileEditorHistory?>? _history;
|
||||
private ProfileConfiguration? _profileConfiguration;
|
||||
@ -45,13 +44,11 @@ public class ProfileEditorViewModel : RoutableScreen<ProfileEditorViewModelParam
|
||||
DisplayConditionScriptViewModel displayConditionScriptViewModel,
|
||||
StatusBarViewModel statusBarViewModel,
|
||||
IEnumerable<IToolViewModel> toolViewModels,
|
||||
IMainWindowService mainWindowService,
|
||||
IInputService inputService)
|
||||
IMainWindowService mainWindowService)
|
||||
{
|
||||
_profileService = profileService;
|
||||
_profileEditorService = profileEditorService;
|
||||
_settingsService = settingsService;
|
||||
_mainWindowService = mainWindowService;
|
||||
|
||||
_tools = new SourceList<IToolViewModel>();
|
||||
_tools.AddRange(toolViewModels);
|
||||
@ -75,13 +72,11 @@ public class ProfileEditorViewModel : RoutableScreen<ProfileEditorViewModelParam
|
||||
_history = profileEditorService.History.ToProperty(this, vm => vm.History).DisposeWith(d);
|
||||
_suspendedEditing = profileEditorService.SuspendedEditing.ToProperty(this, vm => vm.SuspendedEditing).DisposeWith(d);
|
||||
|
||||
inputService.KeyboardKeyDown += InputServiceOnKeyboardKeyDown;
|
||||
mainWindowService.MainWindowFocused += MainWindowServiceOnMainWindowFocused;
|
||||
mainWindowService.MainWindowUnfocused += MainWindowServiceOnMainWindowUnfocused;
|
||||
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
inputService.KeyboardKeyDown -= InputServiceOnKeyboardKeyDown;
|
||||
mainWindowService.MainWindowFocused -= MainWindowServiceOnMainWindowFocused;
|
||||
mainWindowService.MainWindowUnfocused -= MainWindowServiceOnMainWindowUnfocused;
|
||||
foreach (IToolViewModel toolViewModel in _tools.Items)
|
||||
@ -144,33 +139,6 @@ public class ProfileEditorViewModel : RoutableScreen<ProfileEditorViewModelParam
|
||||
});
|
||||
}
|
||||
|
||||
private void InputServiceOnKeyboardKeyDown(object? sender, ArtemisKeyboardKeyEventArgs e)
|
||||
{
|
||||
if (!Shared.UI.KeyBindingsEnabled || !_mainWindowService.IsMainWindowFocused)
|
||||
return;
|
||||
|
||||
if (e.Modifiers == KeyboardModifierKey.Control && e.Key == KeyboardKey.Z)
|
||||
History?.Undo.Execute().Subscribe();
|
||||
else if (e.Modifiers == KeyboardModifierKey.Control && e.Key == KeyboardKey.Y)
|
||||
History?.Redo.Execute().Subscribe();
|
||||
else if (e.Modifiers == KeyboardModifierKey.None && e.Key == KeyboardKey.F5)
|
||||
ToggleSuspend.Execute().Subscribe();
|
||||
else if (e.Modifiers == KeyboardModifierKey.Shift && e.Key == KeyboardKey.F5)
|
||||
ToggleAutoSuspend.Execute().Subscribe();
|
||||
else if (e.Modifiers == KeyboardModifierKey.None && e.Key == KeyboardKey.Space)
|
||||
PropertiesViewModel?.PlaybackViewModel.TogglePlay.Execute().Subscribe();
|
||||
else if (e.Modifiers == KeyboardModifierKey.Shift && e.Key == KeyboardKey.Space)
|
||||
PropertiesViewModel?.PlaybackViewModel.PlayFromStart.Execute().Subscribe();
|
||||
else if (e.Modifiers == KeyboardModifierKey.None && e.Key == KeyboardKey.F)
|
||||
(TitleBarViewModel as ProfileEditorTitleBarViewModel)?.MenuBarViewModel.CycleFocusMode.Execute().Subscribe();
|
||||
else
|
||||
{
|
||||
IToolViewModel? tool = Tools.FirstOrDefault(t => t.Hotkey != null && t.Hotkey.MatchesEventArgs(e));
|
||||
if (tool != null)
|
||||
tool.IsSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void MainWindowServiceOnMainWindowFocused(object? sender, EventArgs e)
|
||||
{
|
||||
if (_settingsService.GetSetting("ProfileEditor.AutoSuspend", true).Value)
|
||||
|
||||
@ -19,15 +19,15 @@ namespace Artemis.UI.Screens.Settings;
|
||||
public class DevicesTabViewModel : RoutableScreen
|
||||
{
|
||||
private readonly IDeviceVmFactory _deviceVmFactory;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IWindowService _windowService;
|
||||
private bool _confirmedDisable;
|
||||
|
||||
public DevicesTabViewModel(IDeviceService deviceService, IWindowService windowService, IDeviceVmFactory deviceVmFactory)
|
||||
public DevicesTabViewModel(IRgbService rgbService, IWindowService windowService, IDeviceVmFactory deviceVmFactory)
|
||||
{
|
||||
DisplayName = "Devices";
|
||||
|
||||
_deviceService = deviceService;
|
||||
_rgbService = rgbService;
|
||||
_windowService = windowService;
|
||||
_deviceVmFactory = deviceVmFactory;
|
||||
|
||||
@ -36,10 +36,10 @@ public class DevicesTabViewModel : RoutableScreen
|
||||
{
|
||||
GetDevices();
|
||||
|
||||
Observable.FromEventPattern<DeviceEventArgs>(x => _deviceService.DeviceAdded += x, x => _deviceService.DeviceAdded -= x)
|
||||
Observable.FromEventPattern<DeviceEventArgs>(x => rgbService.DeviceAdded += x, x => rgbService.DeviceAdded -= x)
|
||||
.Subscribe(d => AddDevice(d.EventArgs.Device))
|
||||
.DisposeWith(disposables);
|
||||
Observable.FromEventPattern<DeviceEventArgs>(x => _deviceService.DeviceRemoved += x, x => _deviceService.DeviceRemoved -= x)
|
||||
Observable.FromEventPattern<DeviceEventArgs>(x => rgbService.DeviceRemoved += x, x => rgbService.DeviceRemoved -= x)
|
||||
.Subscribe(d => RemoveDevice(d.EventArgs.Device))
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
@ -66,7 +66,7 @@ public class DevicesTabViewModel : RoutableScreen
|
||||
private void GetDevices()
|
||||
{
|
||||
Devices.Clear();
|
||||
Dispatcher.UIThread.InvokeAsync(() => { Devices.AddRange(_deviceService.Devices.Select(d => _deviceVmFactory.DeviceSettingsViewModel(d, this))); }, DispatcherPriority.Background);
|
||||
Dispatcher.UIThread.InvokeAsync(() => { Devices.AddRange(_rgbService.Devices.Select(d => _deviceVmFactory.DeviceSettingsViewModel(d, this))); }, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
private void AddDevice(ArtemisDevice device)
|
||||
@ -81,7 +81,7 @@ public class DevicesTabViewModel : RoutableScreen
|
||||
private void RemoveDevice(ArtemisDevice device)
|
||||
{
|
||||
// If the device was only disabled don't remove it
|
||||
if (_deviceService.Devices.Contains(device))
|
||||
if (_rgbService.Devices.Contains(device))
|
||||
return;
|
||||
|
||||
DeviceSettingsViewModel? viewModel = Devices.FirstOrDefault(i => i.Device == device);
|
||||
|
||||
@ -21,9 +21,9 @@ public class StartupWizardViewModel : DialogViewModelBase<bool>
|
||||
{
|
||||
private readonly IAutoRunProvider? _autoRunProvider;
|
||||
private readonly IProtocolProvider? _protocolProvider;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private int _currentStep;
|
||||
private bool _showContinue;
|
||||
private bool _showFinish;
|
||||
@ -31,14 +31,14 @@ public class StartupWizardViewModel : DialogViewModelBase<bool>
|
||||
|
||||
public StartupWizardViewModel(IContainer container,
|
||||
ISettingsService settingsService,
|
||||
IRgbService rgbService,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IWindowService windowService,
|
||||
IDeviceService deviceService,
|
||||
ISettingsVmFactory settingsVmFactory)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_rgbService = rgbService;
|
||||
_windowService = windowService;
|
||||
_deviceService = deviceService;
|
||||
_autoRunProvider = container.Resolve<IAutoRunProvider>(IfUnresolved.ReturnDefault);
|
||||
_protocolProvider = container.Resolve<IProtocolProvider>(IfUnresolved.ReturnDefault);
|
||||
|
||||
@ -159,7 +159,7 @@ public class StartupWizardViewModel : DialogViewModelBase<bool>
|
||||
private void ExecuteSelectLayout(string layout)
|
||||
{
|
||||
// TODO: Implement the layout
|
||||
_deviceService.AutoArrangeDevices();
|
||||
_rgbService.AutoArrangeDevices();
|
||||
|
||||
ExecuteContinue();
|
||||
}
|
||||
|
||||
@ -15,16 +15,16 @@ namespace Artemis.UI.Screens.SurfaceEditor;
|
||||
public class ListDeviceViewModel : ViewModelBase
|
||||
{
|
||||
private static readonly Random Random = new();
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly IDeviceService _deviceService;
|
||||
|
||||
private SKColor _color;
|
||||
private bool _isSelected;
|
||||
|
||||
public ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel, IWindowService windowService, IDeviceService deviceService)
|
||||
public ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel, IWindowService windowService, IRgbService rgbService)
|
||||
{
|
||||
_windowService = windowService;
|
||||
_deviceService = deviceService;
|
||||
_rgbService = rgbService;
|
||||
|
||||
Device = device;
|
||||
SurfaceEditorViewModel = surfaceEditorViewModel;
|
||||
@ -62,6 +62,6 @@ public class ListDeviceViewModel : ViewModelBase
|
||||
.ShowAsync();
|
||||
|
||||
if (viewModel.MadeChanges)
|
||||
_deviceService.SaveDevice(Device);
|
||||
_rgbService.SaveDevice(Device);
|
||||
}
|
||||
}
|
||||
@ -17,16 +17,16 @@ namespace Artemis.UI.Screens.SurfaceEditor;
|
||||
|
||||
public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IWindowService _windowService;
|
||||
private double _dragOffsetX;
|
||||
private double _dragOffsetY;
|
||||
private bool _isSelected;
|
||||
|
||||
public SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel, IDeviceService deviceService, ISettingsService settingsService, IWindowService windowService)
|
||||
public SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel, IRgbService rgbService, ISettingsService settingsService, IWindowService windowService)
|
||||
{
|
||||
_deviceService = deviceService;
|
||||
_rgbService = rgbService;
|
||||
_settingsService = settingsService;
|
||||
_windowService = windowService;
|
||||
|
||||
@ -100,7 +100,7 @@ public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
||||
|
||||
IEnumerable<SKRect> own = Device.Leds
|
||||
.Select(l => SKRect.Create(l.Rectangle.Left + x, l.Rectangle.Top + y, l.Rectangle.Width, l.Rectangle.Height));
|
||||
IEnumerable<SKRect> others = _deviceService.EnabledDevices
|
||||
IEnumerable<SKRect> others = _rgbService.EnabledDevices
|
||||
.Where(d => d != Device && d.IsEnabled)
|
||||
.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));
|
||||
@ -120,6 +120,6 @@ public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
||||
.ShowAsync();
|
||||
|
||||
if (viewModel.MadeChanges)
|
||||
_deviceService.SaveDevice(Device);
|
||||
_rgbService.SaveDevice(Device);
|
||||
}
|
||||
}
|
||||
@ -99,7 +99,7 @@
|
||||
</ItemsControl>
|
||||
|
||||
<shared:SelectionRectangle Name="SelectionRectangle"
|
||||
InputElement="{Binding #ContainerZoomBorder}"
|
||||
InputElement="{Binding #ZoomBorder}"
|
||||
SelectionUpdated="SelectionRectangle_OnSelectionUpdated"
|
||||
BorderBrush="{DynamicResource SystemAccentColor}"
|
||||
BorderRadius="8"
|
||||
|
||||
@ -21,8 +21,8 @@ namespace Artemis.UI.Screens.SurfaceEditor;
|
||||
public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
||||
{
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRenderService _renderService;
|
||||
private readonly IDeviceVmFactory _deviceVmFactory;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ISurfaceVmFactory _surfaceVmFactory;
|
||||
private readonly IWindowService _windowService;
|
||||
@ -33,23 +33,23 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
||||
private bool _saving;
|
||||
|
||||
public SurfaceEditorViewModel(ICoreService coreService,
|
||||
IRgbService rgbService,
|
||||
ISurfaceVmFactory surfaceVmFactory,
|
||||
ISettingsService settingsService,
|
||||
IDeviceVmFactory deviceVmFactory,
|
||||
IWindowService windowService,
|
||||
IDeviceService deviceService,
|
||||
IRenderService renderService)
|
||||
IDeviceService deviceService)
|
||||
{
|
||||
_rgbService = rgbService;
|
||||
_surfaceVmFactory = surfaceVmFactory;
|
||||
_settingsService = settingsService;
|
||||
_deviceVmFactory = deviceVmFactory;
|
||||
_windowService = windowService;
|
||||
_deviceService = deviceService;
|
||||
_renderService = renderService;
|
||||
|
||||
DisplayName = "Surface Editor";
|
||||
SurfaceDeviceViewModels = new ObservableCollection<SurfaceDeviceViewModel>(deviceService.EnabledDevices.OrderBy(d => d.ZIndex).Select(d => surfaceVmFactory.SurfaceDeviceViewModel(d, this)));
|
||||
ListDeviceViewModels = new ObservableCollection<ListDeviceViewModel>(deviceService.EnabledDevices.OrderBy(d => d.ZIndex).Select(d => surfaceVmFactory.ListDeviceViewModel(d, this)));
|
||||
SurfaceDeviceViewModels = new ObservableCollection<SurfaceDeviceViewModel>(rgbService.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)));
|
||||
|
||||
AutoArrange = ReactiveCommand.CreateFromTask(ExecuteAutoArrange);
|
||||
IdentifyDevice = ReactiveCommand.Create<ArtemisDevice>(ExecuteIdentifyDevice);
|
||||
@ -61,14 +61,14 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
_renderService.FrameRendering += RenderServiceOnFrameRendering;
|
||||
_deviceService.DeviceAdded += DeviceServiceOnDeviceAdded;
|
||||
_deviceService.DeviceRemoved += DeviceServiceOnDeviceRemoved;
|
||||
coreService.FrameRendering += CoreServiceOnFrameRendering;
|
||||
_rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
|
||||
_rgbService.DeviceRemoved += RgbServiceOnDeviceRemoved;
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
_renderService.FrameRendering -= RenderServiceOnFrameRendering;
|
||||
_deviceService.DeviceAdded -= DeviceServiceOnDeviceAdded;
|
||||
_deviceService.DeviceRemoved -= DeviceServiceOnDeviceRemoved;
|
||||
coreService.FrameRendering -= CoreServiceOnFrameRendering;
|
||||
_rgbService.DeviceAdded -= RgbServiceOnDeviceAdded;
|
||||
_rgbService.DeviceRemoved -= RgbServiceOnDeviceRemoved;
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
@ -135,7 +135,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
||||
try
|
||||
{
|
||||
_saving = true;
|
||||
_deviceService.SaveDevices();
|
||||
_rgbService.SaveDevices();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -166,7 +166,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
||||
surfaceDeviceViewModel.UpdateMouseDrag(mousePosition, round, ignoreOverlap);
|
||||
}
|
||||
|
||||
private void DeviceServiceOnDeviceAdded(object? sender, DeviceEventArgs e)
|
||||
private void RgbServiceOnDeviceAdded(object? sender, DeviceEventArgs e)
|
||||
{
|
||||
if (!e.Device.IsEnabled)
|
||||
return;
|
||||
@ -177,7 +177,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
||||
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||
}
|
||||
|
||||
private void DeviceServiceOnDeviceRemoved(object? sender, DeviceEventArgs e)
|
||||
private void RgbServiceOnDeviceRemoved(object? sender, DeviceEventArgs e)
|
||||
{
|
||||
SurfaceDeviceViewModel? surfaceVm = SurfaceDeviceViewModels.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)
|
||||
return;
|
||||
|
||||
_deviceService.AutoArrangeDevices();
|
||||
_rgbService.AutoArrangeDevices();
|
||||
}
|
||||
|
||||
private void RenderServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)
|
||||
private void CoreServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)
|
||||
{
|
||||
// Animate the overlay because I'm vain
|
||||
if (ColorDevices && _overlayOpacity < 1)
|
||||
@ -255,7 +255,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
||||
|
||||
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||
|
||||
_deviceService.SaveDevices();
|
||||
_rgbService.SaveDevices();
|
||||
}
|
||||
|
||||
private void ExecuteBringForward(ArtemisDevice device)
|
||||
@ -273,7 +273,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
||||
|
||||
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||
|
||||
_deviceService.SaveDevices();
|
||||
_rgbService.SaveDevices();
|
||||
}
|
||||
|
||||
private void ExecuteSendToBack(ArtemisDevice device)
|
||||
@ -288,7 +288,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
||||
|
||||
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||
|
||||
_deviceService.SaveDevices();
|
||||
_rgbService.SaveDevices();
|
||||
}
|
||||
|
||||
private void ExecuteSendBackward(ArtemisDevice device)
|
||||
@ -305,7 +305,7 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
||||
|
||||
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||
|
||||
_deviceService.SaveDevices();
|
||||
_rgbService.SaveDevices();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -64,7 +64,7 @@ public class NodeScriptWindowViewModel : NodeScriptWindowViewModelBase
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
_keyBindingsEnabled = Shared.UI.CurrentKeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
|
||||
_keyBindingsEnabled = Shared.UI.KeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
|
||||
|
||||
Timer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000));
|
||||
Timer saveTimer = new(TimeSpan.FromMinutes(2));
|
||||
|
||||
@ -16,12 +16,12 @@ public class ProfilePreviewViewModel : ActivatableViewModelBase
|
||||
private readonly IWindowService _windowService;
|
||||
private ProfileConfiguration? _profileConfiguration;
|
||||
|
||||
public ProfilePreviewViewModel(IProfileService profileService, IDeviceService deviceService, IWindowService windowService)
|
||||
public ProfilePreviewViewModel(IProfileService profileService, IRgbService rgbService, IWindowService windowService)
|
||||
{
|
||||
_profileService = profileService;
|
||||
_windowService = windowService;
|
||||
|
||||
Devices = new ObservableCollection<ArtemisDevice>(deviceService.EnabledDevices.OrderBy(d => d.ZIndex));
|
||||
Devices = new ObservableCollection<ArtemisDevice>(rgbService.EnabledDevices.OrderBy(d => d.ZIndex));
|
||||
|
||||
this.WhenAnyValue(vm => vm.ProfileConfiguration).Subscribe(_ => Update());
|
||||
this.WhenActivated(d => Disposable.Create(() => PreviewProfile(null)).DisposeWith(d));
|
||||
|
||||
@ -17,18 +17,18 @@ namespace Artemis.UI.Services;
|
||||
public class DeviceLayoutService : IDeviceLayoutService
|
||||
{
|
||||
private readonly List<ArtemisDevice> _ignoredDevices;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IMainWindowService _mainWindowService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IWindowService _windowService;
|
||||
|
||||
public DeviceLayoutService(IDeviceService deviceService, IMainWindowService mainWindowService, IWindowService windowService)
|
||||
public DeviceLayoutService(IRgbService rgbService, IMainWindowService mainWindowService, IWindowService windowService)
|
||||
{
|
||||
_deviceService = deviceService;
|
||||
_rgbService = rgbService;
|
||||
_mainWindowService = mainWindowService;
|
||||
_windowService = windowService;
|
||||
_ignoredDevices = new List<ArtemisDevice>();
|
||||
|
||||
deviceService.DeviceAdded += RgbServiceOnDeviceAdded;
|
||||
rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
|
||||
mainWindowService.MainWindowOpened += WindowServiceOnMainWindowOpened;
|
||||
}
|
||||
|
||||
@ -60,8 +60,8 @@ public class DeviceLayoutService : IDeviceLayoutService
|
||||
}
|
||||
|
||||
await Task.Delay(400);
|
||||
_deviceService.SaveDevice(artemisDevice);
|
||||
_deviceService.ApplyDeviceLayout(artemisDevice, artemisDevice.GetBestDeviceLayout());
|
||||
_rgbService.SaveDevice(artemisDevice);
|
||||
_rgbService.ApplyBestDeviceLayout(artemisDevice);
|
||||
}
|
||||
|
||||
private bool DeviceNeedsLayout(ArtemisDevice d)
|
||||
@ -73,7 +73,7 @@ public class DeviceLayoutService : IDeviceLayoutService
|
||||
|
||||
private async void WindowServiceOnMainWindowOpened(object? sender, EventArgs e)
|
||||
{
|
||||
List<ArtemisDevice> devices = _deviceService.Devices.Where(device => DeviceNeedsLayout(device) && !_ignoredDevices.Contains(device)).ToList();
|
||||
List<ArtemisDevice> devices = _rgbService.Devices.Where(device => DeviceNeedsLayout(device) && !_ignoredDevices.Contains(device)).ToList();
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
foreach (ArtemisDevice artemisDevice in devices)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user