mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Merge branch 'development'
This commit is contained in:
commit
1f3f213405
@ -1,4 +1,5 @@
|
|||||||
using System.Text;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
@ -18,6 +19,18 @@ internal static class RgbDeviceExtensions
|
|||||||
builder.Append(rgbDevice.DeviceInfo.DeviceType);
|
builder.Append(rgbDevice.DeviceInfo.DeviceType);
|
||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void EnsureValidDimensions(this IRGBDevice rgbDevice)
|
||||||
|
{
|
||||||
|
if (rgbDevice.Location == Point.Invalid)
|
||||||
|
rgbDevice.Location = new Point(0, 0);
|
||||||
|
|
||||||
|
if (rgbDevice.Size == Size.Invalid)
|
||||||
|
{
|
||||||
|
Rectangle ledRectangle = new(rgbDevice.Select(x => x.Boundary));
|
||||||
|
rgbDevice.Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class RgbRectangleExtensions
|
internal static class RgbRectangleExtensions
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.Core.DeviceProviders;
|
using Artemis.Core.DeviceProviders;
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Surface;
|
using Artemis.Storage.Entities.Surface;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
@ -22,13 +23,13 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
|
|
||||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider)
|
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider)
|
||||||
{
|
{
|
||||||
|
rgbDevice.EnsureValidDimensions();
|
||||||
_originalLeds = new List<OriginalLed>(rgbDevice.Select(l => new OriginalLed(l)));
|
_originalLeds = new List<OriginalLed>(rgbDevice.Select(l => new OriginalLed(l)));
|
||||||
Rectangle ledRectangle = new(rgbDevice.Select(x => x.Boundary));
|
_originalSize = rgbDevice.Size;
|
||||||
_originalSize = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
|
|
||||||
|
|
||||||
|
RgbDevice = rgbDevice;
|
||||||
Identifier = rgbDevice.GetDeviceIdentifier();
|
Identifier = rgbDevice.GetDeviceIdentifier();
|
||||||
DeviceEntity = new DeviceEntity();
|
DeviceEntity = new DeviceEntity();
|
||||||
RgbDevice = rgbDevice;
|
|
||||||
DeviceProvider = deviceProvider;
|
DeviceProvider = deviceProvider;
|
||||||
|
|
||||||
Rotation = 0;
|
Rotation = 0;
|
||||||
@ -45,22 +46,26 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
InputMappings = new Dictionary<ArtemisLed, ArtemisLed>();
|
InputMappings = new Dictionary<ArtemisLed, ArtemisLed>();
|
||||||
Categories = new HashSet<DeviceCategory>();
|
Categories = new HashSet<DeviceCategory>();
|
||||||
|
|
||||||
UpdateLeds();
|
RgbDevice.ColorCorrections.Clear();
|
||||||
|
RgbDevice.ColorCorrections.Add(new ScaleColorCorrection(this));
|
||||||
|
|
||||||
|
CreateArtemisLeds(false);
|
||||||
ApplyKeyboardLayout();
|
ApplyKeyboardLayout();
|
||||||
ApplyToEntity();
|
|
||||||
ApplyDefaultCategories();
|
|
||||||
CalculateRenderProperties();
|
CalculateRenderProperties();
|
||||||
|
Save();
|
||||||
|
|
||||||
|
RgbDevice.PropertyChanged += RgbDeviceOnPropertyChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity)
|
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity)
|
||||||
{
|
{
|
||||||
|
rgbDevice.EnsureValidDimensions();
|
||||||
_originalLeds = new List<OriginalLed>(rgbDevice.Select(l => new OriginalLed(l)));
|
_originalLeds = new List<OriginalLed>(rgbDevice.Select(l => new OriginalLed(l)));
|
||||||
Rectangle ledRectangle = new(rgbDevice.Select(x => x.Boundary));
|
_originalSize = rgbDevice.Size;
|
||||||
_originalSize = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
|
|
||||||
|
|
||||||
|
RgbDevice = rgbDevice;
|
||||||
Identifier = rgbDevice.GetDeviceIdentifier();
|
Identifier = rgbDevice.GetDeviceIdentifier();
|
||||||
DeviceEntity = deviceEntity;
|
DeviceEntity = deviceEntity;
|
||||||
RgbDevice = rgbDevice;
|
|
||||||
DeviceProvider = deviceProvider;
|
DeviceProvider = deviceProvider;
|
||||||
|
|
||||||
LedIds = new ReadOnlyDictionary<LedId, ArtemisLed>(new Dictionary<LedId, ArtemisLed>());
|
LedIds = new ReadOnlyDictionary<LedId, ArtemisLed>(new Dictionary<LedId, ArtemisLed>());
|
||||||
@ -72,8 +77,15 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers)
|
foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers)
|
||||||
InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier));
|
InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier));
|
||||||
|
|
||||||
UpdateLeds();
|
RgbDevice.ColorCorrections.Clear();
|
||||||
|
RgbDevice.ColorCorrections.Add(new ScaleColorCorrection(this));
|
||||||
|
|
||||||
|
CreateArtemisLeds(false);
|
||||||
|
Load();
|
||||||
ApplyKeyboardLayout();
|
ApplyKeyboardLayout();
|
||||||
|
CalculateRenderProperties();
|
||||||
|
|
||||||
|
RgbDevice.PropertyChanged += RgbDeviceOnPropertyChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -149,6 +161,8 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
DeviceEntity.X = value;
|
DeviceEntity.X = value;
|
||||||
|
if (RgbDevice.Surface != null)
|
||||||
|
ApplyLocation(DeviceEntity.X, DeviceEntity.Y);
|
||||||
OnPropertyChanged(nameof(X));
|
OnPropertyChanged(nameof(X));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,6 +176,8 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
DeviceEntity.Y = value;
|
DeviceEntity.Y = value;
|
||||||
|
if (RgbDevice.Surface != null)
|
||||||
|
ApplyLocation(DeviceEntity.X, DeviceEntity.Y);
|
||||||
OnPropertyChanged(nameof(Y));
|
OnPropertyChanged(nameof(Y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,6 +191,8 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
DeviceEntity.Rotation = value;
|
DeviceEntity.Rotation = value;
|
||||||
|
if (RgbDevice.Surface != null)
|
||||||
|
RgbDevice.Rotation = DeviceEntity.Rotation;
|
||||||
OnPropertyChanged(nameof(Rotation));
|
OnPropertyChanged(nameof(Rotation));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,6 +206,8 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
DeviceEntity.Scale = value;
|
DeviceEntity.Scale = value;
|
||||||
|
if (RgbDevice.Surface != null)
|
||||||
|
RgbDevice.Scale = DeviceEntity.Scale;
|
||||||
OnPropertyChanged(nameof(Scale));
|
OnPropertyChanged(nameof(Scale));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -412,8 +432,7 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
case RGBDeviceType.Mousepad:
|
case RGBDeviceType.Mousepad:
|
||||||
case RGBDeviceType.HeadsetStand:
|
case RGBDeviceType.HeadsetStand:
|
||||||
case RGBDeviceType.Keypad:
|
case RGBDeviceType.Keypad:
|
||||||
if (!Categories.Contains(DeviceCategory.Peripherals))
|
Categories.Add(DeviceCategory.Peripherals);
|
||||||
Categories.Add(DeviceCategory.Peripherals);
|
|
||||||
break;
|
break;
|
||||||
case RGBDeviceType.Mainboard:
|
case RGBDeviceType.Mainboard:
|
||||||
case RGBDeviceType.GraphicsCard:
|
case RGBDeviceType.GraphicsCard:
|
||||||
@ -421,20 +440,16 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
case RGBDeviceType.Fan:
|
case RGBDeviceType.Fan:
|
||||||
case RGBDeviceType.LedStripe:
|
case RGBDeviceType.LedStripe:
|
||||||
case RGBDeviceType.Cooler:
|
case RGBDeviceType.Cooler:
|
||||||
if (!Categories.Contains(DeviceCategory.Case))
|
Categories.Add(DeviceCategory.Case);
|
||||||
Categories.Add(DeviceCategory.Case);
|
|
||||||
break;
|
break;
|
||||||
case RGBDeviceType.Speaker:
|
case RGBDeviceType.Speaker:
|
||||||
if (!Categories.Contains(DeviceCategory.Desk))
|
Categories.Add(DeviceCategory.Desk);
|
||||||
Categories.Add(DeviceCategory.Desk);
|
|
||||||
break;
|
break;
|
||||||
case RGBDeviceType.Monitor:
|
case RGBDeviceType.Monitor:
|
||||||
if (!Categories.Contains(DeviceCategory.Monitor))
|
Categories.Add(DeviceCategory.Monitor);
|
||||||
Categories.Add(DeviceCategory.Monitor);
|
|
||||||
break;
|
break;
|
||||||
case RGBDeviceType.LedMatrix:
|
case RGBDeviceType.LedMatrix:
|
||||||
if (!Categories.Contains(DeviceCategory.Room))
|
Categories.Add(DeviceCategory.Room);
|
||||||
Categories.Add(DeviceCategory.Room);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -461,40 +476,36 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
/// </param>
|
/// </param>
|
||||||
internal void ApplyLayout(ArtemisLayout? layout, bool createMissingLeds, bool removeExcessiveLeds)
|
internal void ApplyLayout(ArtemisLayout? layout, bool createMissingLeds, bool removeExcessiveLeds)
|
||||||
{
|
{
|
||||||
if (layout == null)
|
if (layout != null && layout.IsValid && createMissingLeds && !DeviceProvider.CreateMissingLedsSupported)
|
||||||
|
throw new ArtemisCoreException($"Cannot apply layout with {nameof(createMissingLeds)} set to true because the device provider does not support it");
|
||||||
|
if (layout != null && layout.IsValid && removeExcessiveLeds && !DeviceProvider.RemoveExcessiveLedsSupported)
|
||||||
|
throw new ArtemisCoreException($"Cannot apply layout with {nameof(removeExcessiveLeds)} set to true because the device provider does not support it");
|
||||||
|
|
||||||
|
// Always clear the current layout
|
||||||
|
ClearLayout();
|
||||||
|
|
||||||
|
// If a valid layout was supplied, apply the layout to the device
|
||||||
|
if (layout != null && layout.IsValid)
|
||||||
{
|
{
|
||||||
ClearLayout();
|
layout.ApplyToDevice(RgbDevice, createMissingLeds, removeExcessiveLeds);
|
||||||
UpdateLeds();
|
Layout = layout;
|
||||||
|
}
|
||||||
CalculateRenderProperties();
|
else
|
||||||
OnDeviceUpdated();
|
{
|
||||||
return;
|
Layout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (createMissingLeds && !DeviceProvider.CreateMissingLedsSupported)
|
// Recreate Artemis LEDs
|
||||||
throw new ArtemisCoreException($"Cannot apply layout with {nameof(createMissingLeds)} " +
|
CreateArtemisLeds(true);
|
||||||
"set to true because the device provider does not support it");
|
// Calculate render properties with the new layout
|
||||||
if (removeExcessiveLeds && !DeviceProvider.RemoveExcessiveLedsSupported)
|
|
||||||
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();
|
CalculateRenderProperties();
|
||||||
OnDeviceUpdated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearLayout()
|
private void ClearLayout()
|
||||||
{
|
{
|
||||||
if (Layout == null)
|
if (Layout == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RgbDevice.DeviceInfo.LayoutMetadata = null;
|
RgbDevice.DeviceInfo.LayoutMetadata = null;
|
||||||
RgbDevice.Size = _originalSize;
|
RgbDevice.Size = _originalSize;
|
||||||
Layout = null;
|
Layout = null;
|
||||||
@ -505,7 +516,7 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
RgbDevice.AddLed(originalLed.Id, originalLed.Location, originalLed.Size, originalLed.CustomData);
|
RgbDevice.AddLed(originalLed.Id, originalLed.Location, originalLed.Size, originalLed.CustomData);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ApplyToEntity()
|
internal void Save()
|
||||||
{
|
{
|
||||||
// Other properties are computed
|
// Other properties are computed
|
||||||
DeviceEntity.Id = Identifier;
|
DeviceEntity.Id = Identifier;
|
||||||
@ -513,13 +524,7 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
|
|
||||||
DeviceEntity.InputIdentifiers.Clear();
|
DeviceEntity.InputIdentifiers.Clear();
|
||||||
foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers)
|
foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers)
|
||||||
{
|
DeviceEntity.InputIdentifiers.Add(new DeviceInputIdentifierEntity {InputProvider = identifier.InputProvider, Identifier = identifier.Identifier});
|
||||||
DeviceEntity.InputIdentifiers.Add(new DeviceInputIdentifierEntity
|
|
||||||
{
|
|
||||||
InputProvider = identifier.InputProvider,
|
|
||||||
Identifier = identifier.Identifier
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceEntity.InputMappings.Clear();
|
DeviceEntity.InputMappings.Clear();
|
||||||
foreach ((ArtemisLed? original, ArtemisLed? mapped) in InputMappings)
|
foreach ((ArtemisLed? original, ArtemisLed? mapped) in InputMappings)
|
||||||
@ -530,31 +535,19 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
DeviceEntity.Categories.Add((int) deviceCategory);
|
DeviceEntity.Categories.Add((int) deviceCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ApplyToRgbDevice()
|
internal void Load()
|
||||||
{
|
{
|
||||||
RgbDevice.Rotation = DeviceEntity.Rotation;
|
|
||||||
RgbDevice.Scale = DeviceEntity.Scale;
|
|
||||||
|
|
||||||
// Workaround for device rotation not applying
|
|
||||||
if (DeviceEntity.X == 0 && DeviceEntity.Y == 0)
|
|
||||||
RgbDevice.Location = new Point(1, 1);
|
|
||||||
RgbDevice.Location = new Point(DeviceEntity.X, DeviceEntity.Y);
|
|
||||||
|
|
||||||
InputIdentifiers.Clear();
|
InputIdentifiers.Clear();
|
||||||
foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers)
|
foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers)
|
||||||
InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier));
|
InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier));
|
||||||
|
|
||||||
if (!RgbDevice.ColorCorrections.Any())
|
|
||||||
RgbDevice.ColorCorrections.Add(new ScaleColorCorrection(this));
|
|
||||||
|
|
||||||
Categories.Clear();
|
Categories.Clear();
|
||||||
foreach (int deviceEntityCategory in DeviceEntity.Categories)
|
foreach (int deviceEntityCategory in DeviceEntity.Categories)
|
||||||
Categories.Add((DeviceCategory) deviceEntityCategory);
|
Categories.Add((DeviceCategory) deviceEntityCategory);
|
||||||
if (!Categories.Any())
|
if (!Categories.Any())
|
||||||
ApplyDefaultCategories();
|
ApplyDefaultCategories();
|
||||||
|
|
||||||
CalculateRenderProperties();
|
LoadInputMappings();
|
||||||
OnDeviceUpdated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void CalculateRenderProperties()
|
internal void CalculateRenderProperties()
|
||||||
@ -571,13 +564,27 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
path.AddRect(artemisLed.AbsoluteRectangle);
|
path.AddRect(artemisLed.AbsoluteRectangle);
|
||||||
|
|
||||||
Path = path;
|
Path = path;
|
||||||
|
|
||||||
|
OnDeviceUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateLeds()
|
private void CreateArtemisLeds(bool loadInputMappings)
|
||||||
{
|
{
|
||||||
Leds = RgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
|
Leds = RgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
|
||||||
LedIds = new ReadOnlyDictionary<LedId, ArtemisLed>(Leds.ToDictionary(l => l.RgbLed.Id, l => l));
|
LedIds = new ReadOnlyDictionary<LedId, ArtemisLed>(Leds.ToDictionary(l => l.RgbLed.Id, l => l));
|
||||||
|
|
||||||
|
if (loadInputMappings)
|
||||||
|
LoadInputMappings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateArtemisLeds()
|
||||||
|
{
|
||||||
|
foreach (ArtemisLed artemisLed in Leds)
|
||||||
|
artemisLed.CalculateRectangles();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadInputMappings()
|
||||||
|
{
|
||||||
InputMappings.Clear();
|
InputMappings.Clear();
|
||||||
foreach (InputMappingEntity deviceEntityInputMapping in DeviceEntity.InputMappings)
|
foreach (InputMappingEntity deviceEntityInputMapping in DeviceEntity.InputMappings)
|
||||||
{
|
{
|
||||||
@ -604,6 +611,27 @@ public class ArtemisDevice : CorePropertyChanged
|
|||||||
else
|
else
|
||||||
LogicalLayout = DeviceEntity.LogicalLayout;
|
LogicalLayout = DeviceEntity.LogicalLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RgbDeviceOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName != nameof(IRGBDevice.Surface) || RgbDevice.Surface == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RgbDevice.Rotation = DeviceEntity.Rotation;
|
||||||
|
RgbDevice.Scale = DeviceEntity.Scale;
|
||||||
|
ApplyLocation(DeviceEntity.X, DeviceEntity.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyLocation(float x, float y)
|
||||||
|
{
|
||||||
|
// Workaround for device rotation not applying
|
||||||
|
if (x == 0 && y == 0)
|
||||||
|
RgbDevice.Location = new Point(1, 1);
|
||||||
|
RgbDevice.Location = new Point(x, y);
|
||||||
|
|
||||||
|
UpdateArtemisLeds();
|
||||||
|
CalculateRenderProperties();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using RGB.NET.Core;
|
using System.Linq;
|
||||||
|
using RGB.NET.Core;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Core;
|
namespace Artemis.Core;
|
||||||
@ -15,6 +16,9 @@ public class ArtemisLed : CorePropertyChanged
|
|||||||
{
|
{
|
||||||
RgbLed = led;
|
RgbLed = led;
|
||||||
Device = device;
|
Device = device;
|
||||||
|
Layout = device.Layout?.Leds.FirstOrDefault(l => l.RgbLayout.Id == led.Id.ToString());
|
||||||
|
Layout?.ApplyCustomLedData(Device);
|
||||||
|
|
||||||
CalculateRectangles();
|
CalculateRectangles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,11 +36,6 @@ public class ArtemisLayout
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public LayoutSource Source { get; }
|
public LayoutSource Source { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the device this layout is applied to
|
|
||||||
/// </summary>
|
|
||||||
public ArtemisDevice? Device { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether a valid layout was loaded
|
/// Gets a boolean indicating whether a valid layout was loaded
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -69,7 +64,7 @@ public class ArtemisLayout
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies the layout to the provided device
|
/// Applies the layout to the provided device
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ApplyTo(IRGBDevice device, bool createMissingLeds = false, bool removeExcessiveLeds = false)
|
public void ApplyToDevice(IRGBDevice device, bool createMissingLeds = false, bool removeExcessiveLeds = false)
|
||||||
{
|
{
|
||||||
device.Size = new Size(MathF.Round(RgbLayout.Width), MathF.Round(RgbLayout.Height));
|
device.Size = new Size(MathF.Round(RgbLayout.Width), MathF.Round(RgbLayout.Height));
|
||||||
device.DeviceInfo.LayoutMetadata = RgbLayout.CustomData;
|
device.DeviceInfo.LayoutMetadata = RgbLayout.CustomData;
|
||||||
@ -124,13 +119,6 @@ public class ArtemisLayout
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ApplyDevice(ArtemisDevice artemisDevice)
|
|
||||||
{
|
|
||||||
Device = artemisDevice;
|
|
||||||
foreach (ArtemisLedLayout artemisLedLayout in Leds)
|
|
||||||
artemisLedLayout.ApplyDevice(Device);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ArtemisLayout? GetDefaultLayout(ArtemisDevice device)
|
internal static ArtemisLayout? GetDefaultLayout(ArtemisDevice device)
|
||||||
{
|
{
|
||||||
string layoutFolder = Path.Combine(Constants.ApplicationFolder, "DefaultLayouts", "Artemis");
|
string layoutFolder = Path.Combine(Constants.ApplicationFolder, "DefaultLayouts", "Artemis");
|
||||||
|
|||||||
@ -27,11 +27,6 @@ public class ArtemisLedLayout
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ILedLayout RgbLayout { get; }
|
public ILedLayout RgbLayout { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the LED this layout is applied to
|
|
||||||
/// </summary>
|
|
||||||
public ArtemisLed? Led { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the logical layout this LED belongs to
|
/// Gets the name of the logical layout this LED belongs to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -46,17 +41,9 @@ public class ArtemisLedLayout
|
|||||||
/// Gets the custom layout data embedded in the RGB.NET layout
|
/// Gets the custom layout data embedded in the RGB.NET layout
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LayoutCustomLedData LayoutCustomLedData { get; }
|
public LayoutCustomLedData LayoutCustomLedData { get; }
|
||||||
|
|
||||||
|
|
||||||
internal void ApplyDevice(ArtemisDevice device)
|
internal void ApplyCustomLedData(ArtemisDevice artemisDevice)
|
||||||
{
|
|
||||||
Led = device.Leds.FirstOrDefault(d => d.RgbLed.Id.ToString() == RgbLayout.Id);
|
|
||||||
if (Led != null)
|
|
||||||
Led.Layout = this;
|
|
||||||
|
|
||||||
ApplyCustomLedData(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyCustomLedData(ArtemisDevice artemisDevice)
|
|
||||||
{
|
{
|
||||||
if (LayoutCustomLedData.LogicalLayouts == null || !LayoutCustomLedData.LogicalLayouts.Any())
|
if (LayoutCustomLedData.LogicalLayouts == null || !LayoutCustomLedData.LogicalLayouts.Any())
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -14,8 +14,9 @@ namespace Artemis.Core.Modules;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DataModel
|
public abstract class DataModel
|
||||||
{
|
{
|
||||||
|
private const StringComparison PathsStringComparison = StringComparison.OrdinalIgnoreCase;
|
||||||
private readonly List<DataModelPath> _activePaths = new();
|
private readonly List<DataModelPath> _activePaths = new();
|
||||||
private readonly HashSet<string> _activePathsHashSet = new();
|
private readonly HashSet<string> _activePathsHashSet = new(StringComparer.FromComparison(PathsStringComparison));
|
||||||
private readonly Dictionary<string, DynamicChild> _dynamicChildren = new();
|
private readonly Dictionary<string, DynamicChild> _dynamicChildren = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -332,11 +333,10 @@ public abstract class DataModel
|
|||||||
/// </param>
|
/// </param>
|
||||||
internal bool IsPropertyInUse(string path, bool includeChildren)
|
internal bool IsPropertyInUse(string path, bool includeChildren)
|
||||||
{
|
{
|
||||||
path = path.ToUpperInvariant();
|
|
||||||
lock (_activePaths)
|
lock (_activePaths)
|
||||||
{
|
{
|
||||||
return includeChildren
|
return includeChildren
|
||||||
? _activePathsHashSet.Any(p => p.StartsWith(path, StringComparison.Ordinal))
|
? _activePathsHashSet.Any(p => p.StartsWith(path, PathsStringComparison))
|
||||||
: _activePathsHashSet.Contains(path);
|
: _activePathsHashSet.Contains(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,9 +351,7 @@ public abstract class DataModel
|
|||||||
_activePaths.Add(path);
|
_activePaths.Add(path);
|
||||||
|
|
||||||
// Add to the hashset if this is the first path pointing
|
// Add to the hashset if this is the first path pointing
|
||||||
string hashPath = path.Path.ToUpperInvariant();
|
_activePathsHashSet.Add(path.Path);
|
||||||
if (!_activePathsHashSet.Contains(hashPath))
|
|
||||||
_activePathsHashSet.Add(hashPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OnActivePathAdded(new DataModelPathEventArgs(path));
|
OnActivePathAdded(new DataModelPathEventArgs(path));
|
||||||
@ -368,7 +366,7 @@ public abstract class DataModel
|
|||||||
|
|
||||||
// Remove from the hashset if this was the last path pointing there
|
// Remove from the hashset if this was the last path pointing there
|
||||||
if (_activePaths.All(p => p.Path != path.Path))
|
if (_activePaths.All(p => p.Path != path.Path))
|
||||||
_activePathsHashSet.Remove(path.Path.ToUpperInvariant());
|
_activePathsHashSet.Remove(path.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
OnActivePathRemoved(new DataModelPathEventArgs(path));
|
OnActivePathRemoved(new DataModelPathEventArgs(path));
|
||||||
|
|||||||
@ -14,7 +14,6 @@ internal sealed class SurfaceManager : IDisposable
|
|||||||
{
|
{
|
||||||
private readonly IRenderer _renderer;
|
private readonly IRenderer _renderer;
|
||||||
private readonly TimerUpdateTrigger _updateTrigger;
|
private readonly TimerUpdateTrigger _updateTrigger;
|
||||||
private readonly object _renderLock = new();
|
|
||||||
private readonly List<ArtemisDevice> _devices = new();
|
private readonly List<ArtemisDevice> _devices = new();
|
||||||
private readonly SKTextureBrush _textureBrush = new(null) {CalculationMode = RenderMode.Absolute};
|
private readonly SKTextureBrush _textureBrush = new(null) {CalculationMode = RenderMode.Absolute};
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ internal sealed class SurfaceManager : IDisposable
|
|||||||
public SurfaceManager(IRenderer renderer, IManagedGraphicsContext? graphicsContext, int targetFrameRate, float renderScale)
|
public SurfaceManager(IRenderer renderer, IManagedGraphicsContext? graphicsContext, int targetFrameRate, float renderScale)
|
||||||
{
|
{
|
||||||
_renderer = renderer;
|
_renderer = renderer;
|
||||||
_updateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / targetFrameRate};
|
_updateTrigger = new TimerUpdateTrigger(false) {UpdateFrequency = 1.0 / targetFrameRate};
|
||||||
|
|
||||||
GraphicsContext = graphicsContext;
|
GraphicsContext = graphicsContext;
|
||||||
TargetFrameRate = targetFrameRate;
|
TargetFrameRate = targetFrameRate;
|
||||||
@ -36,47 +35,6 @@ internal sealed class SurfaceManager : IDisposable
|
|||||||
SetPaused(true);
|
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 IManagedGraphicsContext? GraphicsContext { get; private set; }
|
||||||
public int TargetFrameRate { get; private set; }
|
public int TargetFrameRate { get; private set; }
|
||||||
public float RenderScale { get; private set; }
|
public float RenderScale { get; private set; }
|
||||||
@ -86,34 +44,46 @@ internal sealed class SurfaceManager : IDisposable
|
|||||||
|
|
||||||
public void AddDevices(IEnumerable<ArtemisDevice> devices)
|
public void AddDevices(IEnumerable<ArtemisDevice> devices)
|
||||||
{
|
{
|
||||||
lock (_renderLock)
|
List<IRGBDevice> newDevices = new();
|
||||||
|
lock (_devices)
|
||||||
{
|
{
|
||||||
foreach (ArtemisDevice artemisDevice in devices)
|
foreach (ArtemisDevice artemisDevice in devices)
|
||||||
{
|
{
|
||||||
if (_devices.Contains(artemisDevice))
|
if (_devices.Contains(artemisDevice))
|
||||||
continue;
|
continue;
|
||||||
_devices.Add(artemisDevice);
|
_devices.Add(artemisDevice);
|
||||||
Surface.Attach(artemisDevice.RgbDevice);
|
newDevices.Add(artemisDevice.RgbDevice);
|
||||||
artemisDevice.DeviceUpdated += ArtemisDeviceOnDeviceUpdated;
|
artemisDevice.DeviceUpdated += ArtemisDeviceOnDeviceUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
Update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!newDevices.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Surface.Attach(newDevices);
|
||||||
|
_texture?.Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveDevices(IEnumerable<ArtemisDevice> devices)
|
public void RemoveDevices(IEnumerable<ArtemisDevice> devices)
|
||||||
{
|
{
|
||||||
lock (_renderLock)
|
List<IRGBDevice> removedDevices = new();
|
||||||
|
lock (_devices)
|
||||||
{
|
{
|
||||||
foreach (ArtemisDevice artemisDevice in devices)
|
foreach (ArtemisDevice artemisDevice in devices)
|
||||||
{
|
{
|
||||||
|
if (!_devices.Remove(artemisDevice))
|
||||||
|
continue;
|
||||||
artemisDevice.DeviceUpdated -= ArtemisDeviceOnDeviceUpdated;
|
artemisDevice.DeviceUpdated -= ArtemisDeviceOnDeviceUpdated;
|
||||||
Surface.Detach(artemisDevice.RgbDevice);
|
removedDevices.Add(artemisDevice.RgbDevice);
|
||||||
_devices.Remove(artemisDevice);
|
_devices.Remove(artemisDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
Update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!removedDevices.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Surface.Detach(removedDevices);
|
||||||
|
_texture?.Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SetPaused(bool paused)
|
public bool SetPaused(bool paused)
|
||||||
@ -130,32 +100,33 @@ internal sealed class SurfaceManager : IDisposable
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Update()
|
public void UpdateTargetFrameRate(int targetFrameRate)
|
||||||
{
|
{
|
||||||
lock (_renderLock)
|
TargetFrameRate = targetFrameRate;
|
||||||
{
|
_updateTrigger.UpdateFrequency = 1.0 / TargetFrameRate;
|
||||||
UpdateLedGroup();
|
|
||||||
CreateTexture();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateLedGroup()
|
public void UpdateRenderScale(float renderScale)
|
||||||
{
|
{
|
||||||
List<Led> leds = _devices.SelectMany(d => d.Leds).Select(l => l.RgbLed).ToList();
|
RenderScale = renderScale;
|
||||||
|
_texture?.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
if (_surfaceLedGroup == null)
|
public void UpdateGraphicsContext(IManagedGraphicsContext? graphicsContext)
|
||||||
{
|
{
|
||||||
_surfaceLedGroup = new ListLedGroup(Surface, leds) {Brush = _textureBrush};
|
GraphicsContext = graphicsContext;
|
||||||
LedsChanged?.Invoke(this, EventArgs.Empty);
|
_texture?.Invalidate();
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up the old background
|
/// <inheritdoc />
|
||||||
_surfaceLedGroup.Detach();
|
public void Dispose()
|
||||||
|
{
|
||||||
|
SetPaused(true);
|
||||||
|
Surface.UnregisterUpdateTrigger(_updateTrigger);
|
||||||
|
|
||||||
// Apply the application wide brush and decorator
|
_updateTrigger.Dispose();
|
||||||
_surfaceLedGroup = new ListLedGroup(Surface, leds) {Brush = _textureBrush};
|
_texture?.Dispose();
|
||||||
LedsChanged?.Invoke(this, EventArgs.Empty);
|
Surface.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SKTexture CreateTexture()
|
private SKTexture CreateTexture()
|
||||||
@ -170,53 +141,59 @@ internal sealed class SurfaceManager : IDisposable
|
|||||||
int width = Math.Max(1, MathF.Min(evenWidth * RenderScale, 4096).RoundToInt());
|
int width = Math.Max(1, MathF.Min(evenWidth * RenderScale, 4096).RoundToInt());
|
||||||
int height = Math.Max(1, MathF.Min(evenHeight * RenderScale, 4096).RoundToInt());
|
int height = Math.Max(1, MathF.Min(evenHeight * RenderScale, 4096).RoundToInt());
|
||||||
|
|
||||||
_texture?.Dispose();
|
lock (_devices)
|
||||||
_texture = new SKTexture(GraphicsContext, width, height, RenderScale, _devices);
|
{
|
||||||
_textureBrush.Texture = _texture;
|
_texture?.Dispose();
|
||||||
|
_texture = new SKTexture(GraphicsContext, width, height, RenderScale, _devices);
|
||||||
|
_textureBrush.Texture = _texture;
|
||||||
|
|
||||||
|
_surfaceLedGroup?.Detach();
|
||||||
|
_surfaceLedGroup = new ListLedGroup(Surface, _devices.SelectMany(d => d.Leds).Select(l => l.RgbLed)) {Brush = _textureBrush};
|
||||||
|
}
|
||||||
|
|
||||||
return _texture;
|
return _texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SurfaceOnUpdating(UpdatingEventArgs args)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ArtemisDeviceOnDeviceUpdated(object? sender, EventArgs e)
|
private void ArtemisDeviceOnDeviceUpdated(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Update();
|
_texture?.Invalidate();
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
42
src/Artemis.Core/Services/CoreRenderer.cs
Normal file
42
src/Artemis.Core/Services/CoreRenderer.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using Artemis.Core.ScriptingProviders;
|
||||||
|
using Artemis.Core.Services.Core;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services;
|
||||||
|
|
||||||
|
internal class CoreRenderer : IRenderer
|
||||||
|
{
|
||||||
|
private readonly IModuleService _moduleService;
|
||||||
|
private readonly IScriptingService _scriptingService;
|
||||||
|
private readonly IProfileService _profileService;
|
||||||
|
|
||||||
|
public CoreRenderer(IModuleService moduleService, IScriptingService scriptingService, IProfileService profileService)
|
||||||
|
{
|
||||||
|
_moduleService = moduleService;
|
||||||
|
_scriptingService = scriptingService;
|
||||||
|
_profileService = profileService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Render(SKCanvas canvas, double delta)
|
||||||
|
{
|
||||||
|
foreach (GlobalScript scriptingServiceGlobalScript in _scriptingService.GlobalScripts)
|
||||||
|
scriptingServiceGlobalScript.OnCoreUpdating(delta);
|
||||||
|
|
||||||
|
_moduleService.UpdateActiveModules(delta);
|
||||||
|
|
||||||
|
if (!_profileService.ProfileRenderingDisabled)
|
||||||
|
{
|
||||||
|
_profileService.UpdateProfiles(delta);
|
||||||
|
_profileService.RenderProfiles(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (GlobalScript scriptingServiceGlobalScript in _scriptingService.GlobalScripts)
|
||||||
|
scriptingServiceGlobalScript.OnCoreUpdated(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void PostRender(SKTexture texture)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,13 +30,13 @@ internal class DeviceService : IDeviceService
|
|||||||
|
|
||||||
EnabledDevices = new ReadOnlyCollection<ArtemisDevice>(_enabledDevices);
|
EnabledDevices = new ReadOnlyCollection<ArtemisDevice>(_enabledDevices);
|
||||||
Devices = new ReadOnlyCollection<ArtemisDevice>(_devices);
|
Devices = new ReadOnlyCollection<ArtemisDevice>(_devices);
|
||||||
|
|
||||||
RenderScale.RenderScaleMultiplierChanged += RenderScaleOnRenderScaleMultiplierChanged;
|
RenderScale.RenderScaleMultiplierChanged += RenderScaleOnRenderScaleMultiplierChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyCollection<ArtemisDevice> EnabledDevices { get; }
|
public IReadOnlyCollection<ArtemisDevice> EnabledDevices { get; }
|
||||||
public IReadOnlyCollection<ArtemisDevice> Devices { get; }
|
public IReadOnlyCollection<ArtemisDevice> Devices { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void IdentifyDevice(ArtemisDevice device)
|
public void IdentifyDevice(ArtemisDevice device)
|
||||||
{
|
{
|
||||||
@ -52,11 +52,12 @@ internal class DeviceService : IDeviceService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Can't see why this would happen, RgbService used to do this though
|
// 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();
|
List<ArtemisDevice> toRemove = _devices.Where(a => a.DeviceProvider.Id == deviceProvider.Id).ToList();
|
||||||
_logger.Verbose("[AddDeviceProvider] Removing {Count} old device(s)", toRemove.Count);
|
_logger.Verbose("[AddDeviceProvider] Removing {Count} old device(s)", toRemove.Count);
|
||||||
foreach (ArtemisDevice device in toRemove)
|
foreach (ArtemisDevice device in toRemove)
|
||||||
{
|
{
|
||||||
_devices.Remove(device);
|
_devices.Remove(device);
|
||||||
|
_enabledDevices.Remove(device);
|
||||||
OnDeviceRemoved(new DeviceEventArgs(device));
|
OnDeviceRemoved(new DeviceEventArgs(device));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +95,7 @@ internal class DeviceService : IDeviceService
|
|||||||
_devices.Add(artemisDevice);
|
_devices.Add(artemisDevice);
|
||||||
if (artemisDevice.IsEnabled)
|
if (artemisDevice.IsEnabled)
|
||||||
_enabledDevices.Add(artemisDevice);
|
_enabledDevices.Add(artemisDevice);
|
||||||
|
|
||||||
_logger.Debug("Device provider {deviceProvider} added {deviceName}", deviceProvider.GetType().Name, rgbDevice.DeviceInfo.DeviceName);
|
_logger.Debug("Device provider {deviceProvider} added {deviceName}", deviceProvider.GetType().Name, rgbDevice.DeviceInfo.DeviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ internal class DeviceService : IDeviceService
|
|||||||
OnDeviceProviderAdded(new DeviceProviderEventArgs(deviceProvider, addedDevices));
|
OnDeviceProviderAdded(new DeviceProviderEventArgs(deviceProvider, addedDevices));
|
||||||
foreach (ArtemisDevice artemisDevice in addedDevices)
|
foreach (ArtemisDevice artemisDevice in addedDevices)
|
||||||
OnDeviceAdded(new DeviceEventArgs(artemisDevice));
|
OnDeviceAdded(new DeviceEventArgs(artemisDevice));
|
||||||
|
|
||||||
UpdateLeds();
|
UpdateLeds();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -118,9 +119,8 @@ internal class DeviceService : IDeviceService
|
|||||||
public void RemoveDeviceProvider(DeviceProvider deviceProvider)
|
public void RemoveDeviceProvider(DeviceProvider deviceProvider)
|
||||||
{
|
{
|
||||||
_logger.Verbose("[RemoveDeviceProvider] Pausing rendering to remove {DeviceProvider}", deviceProvider.GetType().Name);
|
_logger.Verbose("[RemoveDeviceProvider] Pausing rendering to remove {DeviceProvider}", deviceProvider.GetType().Name);
|
||||||
IRGBDeviceProvider rgbDeviceProvider = deviceProvider.RgbDeviceProvider;
|
List<ArtemisDevice> toRemove = _devices.Where(a => a.DeviceProvider.Id == deviceProvider.Id).ToList();
|
||||||
List<ArtemisDevice> toRemove = _devices.Where(a => rgbDeviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.Verbose("[RemoveDeviceProvider] Removing {Count} old device(s)", toRemove.Count);
|
_logger.Verbose("[RemoveDeviceProvider] Removing {Count} old device(s)", toRemove.Count);
|
||||||
@ -131,11 +131,11 @@ internal class DeviceService : IDeviceService
|
|||||||
}
|
}
|
||||||
|
|
||||||
_devices.Sort((a, b) => a.ZIndex - b.ZIndex);
|
_devices.Sort((a, b) => a.ZIndex - b.ZIndex);
|
||||||
|
|
||||||
OnDeviceProviderRemoved(new DeviceProviderEventArgs(deviceProvider, toRemove));
|
OnDeviceProviderRemoved(new DeviceProviderEventArgs(deviceProvider, toRemove));
|
||||||
foreach (ArtemisDevice artemisDevice in toRemove)
|
foreach (ArtemisDevice artemisDevice in toRemove)
|
||||||
OnDeviceRemoved(new DeviceEventArgs(artemisDevice));
|
OnDeviceRemoved(new DeviceEventArgs(artemisDevice));
|
||||||
|
|
||||||
UpdateLeds();
|
UpdateLeds();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -155,7 +155,7 @@ internal class DeviceService : IDeviceService
|
|||||||
|
|
||||||
SaveDevices();
|
SaveDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout? layout)
|
public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout? layout)
|
||||||
{
|
{
|
||||||
@ -163,10 +163,10 @@ internal class DeviceService : IDeviceService
|
|||||||
device.ApplyLayout(layout, false, false);
|
device.ApplyLayout(layout, false, false);
|
||||||
else
|
else
|
||||||
device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
|
device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
|
||||||
|
|
||||||
UpdateLeds();
|
UpdateLeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void EnableDevice(ArtemisDevice device)
|
public void EnableDevice(ArtemisDevice device)
|
||||||
{
|
{
|
||||||
@ -175,7 +175,7 @@ internal class DeviceService : IDeviceService
|
|||||||
|
|
||||||
_enabledDevices.Add(device);
|
_enabledDevices.Add(device);
|
||||||
device.IsEnabled = true;
|
device.IsEnabled = true;
|
||||||
device.ApplyToEntity();
|
device.Save();
|
||||||
_deviceRepository.Save(device.DeviceEntity);
|
_deviceRepository.Save(device.DeviceEntity);
|
||||||
|
|
||||||
OnDeviceEnabled(new DeviceEventArgs(device));
|
OnDeviceEnabled(new DeviceEventArgs(device));
|
||||||
@ -190,7 +190,7 @@ internal class DeviceService : IDeviceService
|
|||||||
|
|
||||||
_enabledDevices.Remove(device);
|
_enabledDevices.Remove(device);
|
||||||
device.IsEnabled = false;
|
device.IsEnabled = false;
|
||||||
device.ApplyToEntity();
|
device.Save();
|
||||||
_deviceRepository.Save(device.DeviceEntity);
|
_deviceRepository.Save(device.DeviceEntity);
|
||||||
|
|
||||||
OnDeviceDisabled(new DeviceEventArgs(device));
|
OnDeviceDisabled(new DeviceEventArgs(device));
|
||||||
@ -200,9 +200,7 @@ internal class DeviceService : IDeviceService
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void SaveDevice(ArtemisDevice artemisDevice)
|
public void SaveDevice(ArtemisDevice artemisDevice)
|
||||||
{
|
{
|
||||||
artemisDevice.ApplyToEntity();
|
artemisDevice.Save();
|
||||||
artemisDevice.ApplyToRgbDevice();
|
|
||||||
|
|
||||||
_deviceRepository.Save(artemisDevice.DeviceEntity);
|
_deviceRepository.Save(artemisDevice.DeviceEntity);
|
||||||
UpdateLeds();
|
UpdateLeds();
|
||||||
}
|
}
|
||||||
@ -211,15 +209,11 @@ internal class DeviceService : IDeviceService
|
|||||||
public void SaveDevices()
|
public void SaveDevices()
|
||||||
{
|
{
|
||||||
foreach (ArtemisDevice artemisDevice in _devices)
|
foreach (ArtemisDevice artemisDevice in _devices)
|
||||||
{
|
artemisDevice.Save();
|
||||||
artemisDevice.ApplyToEntity();
|
|
||||||
artemisDevice.ApplyToRgbDevice();
|
|
||||||
}
|
|
||||||
|
|
||||||
_deviceRepository.Save(_devices.Select(d => d.DeviceEntity));
|
_deviceRepository.Save(_devices.Select(d => d.DeviceEntity));
|
||||||
UpdateLeds();
|
UpdateLeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArtemisDevice GetArtemisDevice(IRGBDevice rgbDevice)
|
private ArtemisDevice GetArtemisDevice(IRGBDevice rgbDevice)
|
||||||
{
|
{
|
||||||
string deviceIdentifier = rgbDevice.GetDeviceIdentifier();
|
string deviceIdentifier = rgbDevice.GetDeviceIdentifier();
|
||||||
@ -236,11 +230,10 @@ internal class DeviceService : IDeviceService
|
|||||||
device = new ArtemisDevice(rgbDevice, deviceProvider);
|
device = new ArtemisDevice(rgbDevice, deviceProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
device.ApplyToRgbDevice();
|
|
||||||
ApplyDeviceLayout(device, device.GetBestDeviceLayout());
|
ApplyDeviceLayout(device, device.GetBestDeviceLayout());
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BlinkDevice(ArtemisDevice device, int blinkCount)
|
private void BlinkDevice(ArtemisDevice device, int blinkCount)
|
||||||
{
|
{
|
||||||
RGBSurface surface = _renderService.Value.Surface;
|
RGBSurface surface = _renderService.Value.Surface;
|
||||||
@ -266,7 +259,7 @@ internal class DeviceService : IDeviceService
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CalculateRenderProperties()
|
private void CalculateRenderProperties()
|
||||||
{
|
{
|
||||||
foreach (ArtemisDevice artemisDevice in Devices)
|
foreach (ArtemisDevice artemisDevice in Devices)
|
||||||
@ -278,7 +271,7 @@ internal class DeviceService : IDeviceService
|
|||||||
{
|
{
|
||||||
OnLedsChanged();
|
OnLedsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderScaleOnRenderScaleMultiplierChanged(object? sender, EventArgs e)
|
private void RenderScaleOnRenderScaleMultiplierChanged(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
CalculateRenderProperties();
|
CalculateRenderProperties();
|
||||||
@ -303,7 +296,7 @@ internal class DeviceService : IDeviceService
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler<DeviceProviderEventArgs>? DeviceProviderRemoved;
|
public event EventHandler<DeviceProviderEventArgs>? DeviceProviderRemoved;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler? LedsChanged;
|
public event EventHandler? LedsChanged;
|
||||||
|
|
||||||
@ -336,7 +329,7 @@ internal class DeviceService : IDeviceService
|
|||||||
{
|
{
|
||||||
DeviceProviderRemoved?.Invoke(this, e);
|
DeviceProviderRemoved?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnLedsChanged()
|
protected virtual void OnLedsChanged()
|
||||||
{
|
{
|
||||||
LedsChanged?.Invoke(this, EventArgs.Empty);
|
LedsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
|||||||
@ -21,11 +21,6 @@ public interface IRenderService : IArtemisService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
RGBSurface Surface { get; }
|
RGBSurface Surface { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list of registered renderers.
|
|
||||||
/// </summary>
|
|
||||||
List<IRenderer> Renderers { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a boolean indicating whether rendering is paused.
|
/// Gets or sets a boolean indicating whether rendering is paused.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -19,22 +19,24 @@ internal class RenderService : IRenderService, IRenderer, IDisposable
|
|||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IDeviceService _deviceService;
|
private readonly IDeviceService _deviceService;
|
||||||
|
private readonly CoreRenderer _coreRenderer;
|
||||||
private readonly LazyEnumerable<IGraphicsContextProvider> _graphicsContextProviders;
|
private readonly LazyEnumerable<IGraphicsContextProvider> _graphicsContextProviders;
|
||||||
private readonly PluginSetting<int> _targetFrameRateSetting;
|
private readonly PluginSetting<int> _targetFrameRateSetting;
|
||||||
private readonly PluginSetting<double> _renderScaleSetting;
|
private readonly PluginSetting<double> _renderScaleSetting;
|
||||||
private readonly PluginSetting<string> _preferredGraphicsContext;
|
private readonly PluginSetting<string> _preferredGraphicsContext;
|
||||||
|
private readonly SurfaceManager _surfaceManager;
|
||||||
|
|
||||||
private SurfaceManager _surfaceManager;
|
|
||||||
private int _frames;
|
private int _frames;
|
||||||
private DateTime _lastExceptionLog;
|
private DateTime _lastExceptionLog;
|
||||||
private DateTime _lastFrameRateSample;
|
private DateTime _lastFrameRateSample;
|
||||||
private bool _initialized;
|
private bool _initialized;
|
||||||
|
|
||||||
public RenderService(ILogger logger, ISettingsService settingsService, IDeviceService deviceService, LazyEnumerable<IGraphicsContextProvider> graphicsContextProviders)
|
public RenderService(ILogger logger, ISettingsService settingsService, IDeviceService deviceService, CoreRenderer coreRenderer, LazyEnumerable<IGraphicsContextProvider> graphicsContextProviders)
|
||||||
{
|
{
|
||||||
_frameStopWatch = new Stopwatch();
|
_frameStopWatch = new Stopwatch();
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_deviceService = deviceService;
|
_deviceService = deviceService;
|
||||||
|
_coreRenderer = coreRenderer;
|
||||||
_graphicsContextProviders = graphicsContextProviders;
|
_graphicsContextProviders = graphicsContextProviders;
|
||||||
|
|
||||||
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 30);
|
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 30);
|
||||||
@ -54,9 +56,6 @@ internal class RenderService : IRenderService, IRenderer, IDisposable
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public RGBSurface Surface => _surfaceManager.Surface;
|
public RGBSurface Surface => _surfaceManager.Surface;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public List<IRenderer> Renderers { get; } = new();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsPaused
|
public bool IsPaused
|
||||||
{
|
{
|
||||||
@ -77,8 +76,7 @@ internal class RenderService : IRenderService, IRenderer, IDisposable
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
OnFrameRendering(new FrameRenderingEventArgs(canvas, delta, _surfaceManager.Surface));
|
OnFrameRendering(new FrameRenderingEventArgs(canvas, delta, _surfaceManager.Surface));
|
||||||
foreach (IRenderer renderer in Renderers)
|
_coreRenderer.Render(canvas, delta);
|
||||||
renderer.Render(canvas, delta);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -91,8 +89,7 @@ internal class RenderService : IRenderService, IRenderer, IDisposable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (IRenderer renderer in Renderers)
|
_coreRenderer.PostRender(texture);
|
||||||
renderer.PostRender(texture);
|
|
||||||
OnFrameRendered(new FrameRenderedEventArgs(texture, _surfaceManager.Surface));
|
OnFrameRendered(new FrameRenderedEventArgs(texture, _surfaceManager.Surface));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@ -12,7 +12,7 @@ internal class ScriptingService : IScriptingService
|
|||||||
private readonly IPluginManagementService _pluginManagementService;
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
private readonly IProfileService _profileService;
|
private readonly IProfileService _profileService;
|
||||||
private readonly List<ScriptingProvider> _scriptingProviders;
|
private readonly List<ScriptingProvider> _scriptingProviders;
|
||||||
|
|
||||||
public ScriptingService(IPluginManagementService pluginManagementService, IProfileService profileService)
|
public ScriptingService(IPluginManagementService pluginManagementService, IProfileService profileService)
|
||||||
{
|
{
|
||||||
_pluginManagementService = pluginManagementService;
|
_pluginManagementService = pluginManagementService;
|
||||||
|
|||||||
@ -27,7 +27,6 @@ internal class SurfaceArrangement
|
|||||||
{
|
{
|
||||||
surfaceDevice.X = 0;
|
surfaceDevice.X = 0;
|
||||||
surfaceDevice.Y = 0;
|
surfaceDevice.Y = 0;
|
||||||
surfaceDevice.ApplyToRgbDevice();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (SurfaceArrangementType surfaceArrangementType in Types)
|
foreach (SurfaceArrangementType surfaceArrangementType in Types)
|
||||||
@ -37,18 +36,16 @@ internal class SurfaceArrangement
|
|||||||
float x = devices.Min(d => d.RgbDevice.Location.X);
|
float x = devices.Min(d => d.RgbDevice.Location.X);
|
||||||
float y = devices.Min(d => d.RgbDevice.Location.Y);
|
float y = devices.Min(d => d.RgbDevice.Location.Y);
|
||||||
if (x < 0)
|
if (x < 0)
|
||||||
|
{
|
||||||
foreach (ArtemisDevice surfaceDevice in devices)
|
foreach (ArtemisDevice surfaceDevice in devices)
|
||||||
{
|
|
||||||
surfaceDevice.X += x * -1;
|
surfaceDevice.X += x * -1;
|
||||||
surfaceDevice.ApplyToRgbDevice();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (y < 0)
|
if (y < 0)
|
||||||
|
{
|
||||||
foreach (ArtemisDevice surfaceDevice in devices)
|
foreach (ArtemisDevice surfaceDevice in devices)
|
||||||
{
|
|
||||||
surfaceDevice.Y += y * -1;
|
surfaceDevice.Y += y * -1;
|
||||||
surfaceDevice.ApplyToRgbDevice();
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static SurfaceArrangement GetDefaultArrangement()
|
internal static SurfaceArrangement GetDefaultArrangement()
|
||||||
|
|||||||
@ -80,9 +80,7 @@ internal class SurfaceArrangementConfiguration
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
artemisDevice.ApplyToRgbDevice();
|
|
||||||
previous = artemisDevice;
|
previous = artemisDevice;
|
||||||
|
|
||||||
SurfaceArrangement.ArrangedDevices.Add(artemisDevice);
|
SurfaceArrangement.ArrangedDevices.Add(artemisDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,6 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core.Modules;
|
using Artemis.Core.Modules;
|
||||||
using Artemis.Core.Services.Core;
|
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
using Artemis.Storage.Repositories.Interfaces;
|
using Artemis.Storage.Repositories.Interfaces;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -16,7 +15,7 @@ using SkiaSharp;
|
|||||||
|
|
||||||
namespace Artemis.Core.Services;
|
namespace Artemis.Core.Services;
|
||||||
|
|
||||||
internal class ProfileService : IProfileService, IRenderer
|
internal class ProfileService : IProfileService
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IProfileCategoryRepository _profileCategoryRepository;
|
private readonly IProfileCategoryRepository _profileCategoryRepository;
|
||||||
@ -36,7 +35,6 @@ internal class ProfileService : IProfileService, IRenderer
|
|||||||
IPluginManagementService pluginManagementService,
|
IPluginManagementService pluginManagementService,
|
||||||
IInputService inputService,
|
IInputService inputService,
|
||||||
IDeviceService deviceService,
|
IDeviceService deviceService,
|
||||||
IRenderService renderService,
|
|
||||||
IProfileRepository profileRepository)
|
IProfileRepository profileRepository)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@ -55,8 +53,6 @@ internal class ProfileService : IProfileService, IRenderer
|
|||||||
if (!_profileCategories.Any())
|
if (!_profileCategories.Any())
|
||||||
CreateDefaultProfileCategories();
|
CreateDefaultProfileCategories();
|
||||||
UpdateModules();
|
UpdateModules();
|
||||||
|
|
||||||
renderService.Renderers.Add(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProfileConfiguration? FocusProfile { get; set; }
|
public ProfileConfiguration? FocusProfile { get; set; }
|
||||||
@ -187,21 +183,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 />
|
/// <inheritdoc />
|
||||||
public void LoadProfileConfigurationIcon(ProfileConfiguration profileConfiguration)
|
public void LoadProfileConfigurationIcon(ProfileConfiguration profileConfiguration)
|
||||||
|
|||||||
@ -95,35 +95,29 @@ public interface IWebServerService : IArtemisService
|
|||||||
/// Adds a new Web API controller and restarts the web server
|
/// Adds a new Web API controller and restarts the web server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
|
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
|
||||||
void AddController<T>(PluginFeature feature) where T : WebApiController;
|
WebApiControllerRegistration AddController<T>(PluginFeature feature) where T : WebApiController;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes an existing Web API controller and restarts the web server
|
/// Removes an existing Web API controller and restarts the web server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
|
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
|
||||||
void RemoveController<T>() where T : WebApiController;
|
void RemoveController(WebApiControllerRegistration registration);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a new EmbedIO module and restarts the web server
|
/// Adds a new EmbedIO module and restarts the web server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void AddModule(PluginFeature feature, Func<IWebModule> create);
|
WebModuleRegistration AddModule(PluginFeature feature, Func<IWebModule> create);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a EmbedIO module and restarts the web server
|
/// Removes a EmbedIO module and restarts the web server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void RemoveModule(Func<IWebModule> create);
|
void RemoveModule(WebModuleRegistration create);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a new EmbedIO module and restarts the web server
|
/// Adds a new EmbedIO module and restarts the web server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of module to add</typeparam>
|
/// <typeparam name="T">The type of module to add</typeparam>
|
||||||
void AddModule<T>(PluginFeature feature) where T : IWebModule;
|
WebModuleRegistration AddModule<T>(PluginFeature feature) where T : IWebModule;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a EmbedIO module and restarts the web server
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of module to remove</typeparam>
|
|
||||||
void RemoveModule<T>() where T : IWebModule;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when the web server has been created and is about to start. This is the ideal place to add your own modules.
|
/// Occurs when the web server has been created and is about to start. This is the ideal place to add your own modules.
|
||||||
|
|||||||
@ -3,26 +3,55 @@ using EmbedIO.WebApi;
|
|||||||
|
|
||||||
namespace Artemis.Core.Services;
|
namespace Artemis.Core.Services;
|
||||||
|
|
||||||
internal class WebApiControllerRegistration<T> : WebApiControllerRegistration where T : WebApiController
|
/// <summary>
|
||||||
|
/// Represents a web API controller registration.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the web API controller.</typeparam>
|
||||||
|
public class WebApiControllerRegistration<T> : WebApiControllerRegistration where T : WebApiController
|
||||||
{
|
{
|
||||||
public WebApiControllerRegistration(PluginFeature feature) : base(feature, typeof(T))
|
internal WebApiControllerRegistration(IWebServerService webServerService, PluginFeature feature) : base(webServerService, feature, typeof(T))
|
||||||
{
|
{
|
||||||
Factory = () => feature.Plugin.Resolve<T>();
|
Factory = () => feature.Plugin.Resolve<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Func<T> Factory { get; set; }
|
internal Func<T> Factory { get; set; }
|
||||||
public override object UntypedFactory => Factory;
|
internal override object UntypedFactory => Factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal abstract class WebApiControllerRegistration
|
/// <summary>
|
||||||
|
/// Represents a web API controller registration.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class WebApiControllerRegistration
|
||||||
{
|
{
|
||||||
protected WebApiControllerRegistration(PluginFeature feature, Type controllerType)
|
private readonly IWebServerService _webServerService;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="WebApiControllerRegistration"/> class.
|
||||||
|
/// </summary>
|
||||||
|
protected internal WebApiControllerRegistration(IWebServerService webServerService, PluginFeature feature, Type controllerType)
|
||||||
{
|
{
|
||||||
|
_webServerService = webServerService;
|
||||||
Feature = feature;
|
Feature = feature;
|
||||||
ControllerType = controllerType;
|
ControllerType = controllerType;
|
||||||
|
|
||||||
|
Feature.Disabled += FeatureOnDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract object UntypedFactory { get; }
|
private void FeatureOnDisabled(object? sender, EventArgs e)
|
||||||
public Type ControllerType { get; set; }
|
{
|
||||||
|
_webServerService.RemoveController(this);
|
||||||
|
Feature.Disabled -= FeatureOnDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal abstract object UntypedFactory { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the web API controller.
|
||||||
|
/// </summary>
|
||||||
|
public Type ControllerType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the plugin feature that provided the web API controller.
|
||||||
|
/// </summary>
|
||||||
public PluginFeature Feature { get; }
|
public PluginFeature Feature { get; }
|
||||||
}
|
}
|
||||||
@ -3,25 +3,44 @@ using EmbedIO;
|
|||||||
|
|
||||||
namespace Artemis.Core.Services;
|
namespace Artemis.Core.Services;
|
||||||
|
|
||||||
internal class WebModuleRegistration
|
/// <summary>
|
||||||
|
/// Represents a registration for a web module.
|
||||||
|
/// </summary>
|
||||||
|
public class WebModuleRegistration
|
||||||
{
|
{
|
||||||
public WebModuleRegistration(PluginFeature feature, Type webModuleType)
|
private readonly IWebServerService _webServerService;
|
||||||
|
|
||||||
|
internal WebModuleRegistration(IWebServerService webServerService, PluginFeature feature, Type webModuleType)
|
||||||
{
|
{
|
||||||
|
_webServerService = webServerService;
|
||||||
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
|
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
|
||||||
WebModuleType = webModuleType ?? throw new ArgumentNullException(nameof(webModuleType));
|
WebModuleType = webModuleType ?? throw new ArgumentNullException(nameof(webModuleType));
|
||||||
|
|
||||||
|
Feature.Disabled += FeatureOnDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebModuleRegistration(PluginFeature feature, Func<IWebModule> create)
|
internal WebModuleRegistration(IWebServerService webServerService, PluginFeature feature, Func<IWebModule> create)
|
||||||
{
|
{
|
||||||
|
_webServerService = webServerService;
|
||||||
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
|
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
|
||||||
Create = create ?? throw new ArgumentNullException(nameof(create));
|
Create = create ?? throw new ArgumentNullException(nameof(create));
|
||||||
|
|
||||||
|
Feature.Disabled += FeatureOnDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The plugin feature that provided the web module.
|
||||||
|
/// </summary>
|
||||||
public PluginFeature Feature { get; }
|
public PluginFeature Feature { get; }
|
||||||
public Type? WebModuleType { get; }
|
|
||||||
public Func<IWebModule>? Create { get; }
|
|
||||||
|
|
||||||
public IWebModule CreateInstance()
|
/// <summary>
|
||||||
|
/// The type of the web module.
|
||||||
|
/// </summary>
|
||||||
|
public Type? WebModuleType { get; }
|
||||||
|
|
||||||
|
internal Func<IWebModule>? Create { get; }
|
||||||
|
|
||||||
|
internal IWebModule CreateInstance()
|
||||||
{
|
{
|
||||||
if (Create != null)
|
if (Create != null)
|
||||||
return Create();
|
return Create();
|
||||||
@ -29,4 +48,10 @@ internal class WebModuleRegistration
|
|||||||
return (IWebModule) Feature.Plugin.Resolve(WebModuleType);
|
return (IWebModule) Feature.Plugin.Resolve(WebModuleType);
|
||||||
throw new ArtemisCoreException("WebModuleRegistration doesn't have a create function nor a web module type :(");
|
throw new ArtemisCoreException("WebModuleRegistration doesn't have a create function nor a web module type :(");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void FeatureOnDisabled(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_webServerService.RemoveModule(this);
|
||||||
|
Feature.Disabled -= FeatureOnDisabled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -39,9 +39,9 @@ internal class WebServerService : IWebServerService, IDisposable
|
|||||||
|
|
||||||
PluginsModule = new PluginsModule("/plugins");
|
PluginsModule = new PluginsModule("/plugins");
|
||||||
if (coreService.IsInitialized)
|
if (coreService.IsInitialized)
|
||||||
StartWebServer();
|
AutoStartWebServer();
|
||||||
else
|
else
|
||||||
coreService.Initialized += (_, _) => StartWebServer();
|
coreService.Initialized += (sender, args) => AutoStartWebServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler? WebServerStopped;
|
public event EventHandler? WebServerStopped;
|
||||||
@ -138,7 +138,7 @@ internal class WebServerService : IWebServerService, IDisposable
|
|||||||
// Add registered controllers to the API module
|
// Add registered controllers to the API module
|
||||||
foreach (WebApiControllerRegistration registration in _controllers)
|
foreach (WebApiControllerRegistration registration in _controllers)
|
||||||
apiModule.RegisterController(registration.ControllerType, (Func<WebApiController>) registration.UntypedFactory);
|
apiModule.RegisterController(registration.ControllerType, (Func<WebApiController>) registration.UntypedFactory);
|
||||||
|
|
||||||
// Listen for state changes.
|
// Listen for state changes.
|
||||||
server.StateChanged += (s, e) => _logger.Verbose("WebServer new state - {state}", e.NewState);
|
server.StateChanged += (s, e) => _logger.Verbose("WebServer new state - {state}", e.NewState);
|
||||||
|
|
||||||
@ -173,6 +173,18 @@ internal class WebServerService : IWebServerService, IDisposable
|
|||||||
OnWebServerStarted();
|
OnWebServerStarted();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AutoStartWebServer()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StartWebServer();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.Warning(exception, "Failed to initially start webserver");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -237,10 +249,6 @@ internal class WebServerService : IWebServerService, IDisposable
|
|||||||
return endPoint;
|
return endPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleDataModelRequest<T>(Module<T> module, T value) where T : DataModel, new()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemovePluginEndPoint(PluginEndPoint endPoint)
|
public void RemovePluginEndPoint(PluginEndPoint endPoint)
|
||||||
{
|
{
|
||||||
PluginsModule.RemovePluginEndPoint(endPoint);
|
PluginsModule.RemovePluginEndPoint(endPoint);
|
||||||
@ -250,15 +258,20 @@ internal class WebServerService : IWebServerService, IDisposable
|
|||||||
|
|
||||||
#region Controller management
|
#region Controller management
|
||||||
|
|
||||||
public void AddController<T>(PluginFeature feature) where T : WebApiController
|
public WebApiControllerRegistration AddController<T>(PluginFeature feature) where T : WebApiController
|
||||||
{
|
{
|
||||||
_controllers.Add(new WebApiControllerRegistration<T>(feature));
|
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||||
|
|
||||||
|
WebApiControllerRegistration<T> registration = new(this, feature);
|
||||||
|
_controllers.Add(registration);
|
||||||
StartWebServer();
|
StartWebServer();
|
||||||
|
|
||||||
|
return registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveController<T>() where T : WebApiController
|
public void RemoveController(WebApiControllerRegistration registration)
|
||||||
{
|
{
|
||||||
_controllers.RemoveAll(r => r.ControllerType == typeof(T));
|
_controllers.Remove(registration);
|
||||||
StartWebServer();
|
StartWebServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,33 +279,31 @@ internal class WebServerService : IWebServerService, IDisposable
|
|||||||
|
|
||||||
#region Module management
|
#region Module management
|
||||||
|
|
||||||
public void AddModule(PluginFeature feature, Func<IWebModule> create)
|
public WebModuleRegistration AddModule(PluginFeature feature, Func<IWebModule> create)
|
||||||
{
|
{
|
||||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||||
|
|
||||||
_modules.Add(new WebModuleRegistration(feature, create));
|
WebModuleRegistration registration = new(this, feature, create);
|
||||||
|
_modules.Add(registration);
|
||||||
StartWebServer();
|
StartWebServer();
|
||||||
|
|
||||||
|
return registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveModule(Func<IWebModule> create)
|
public WebModuleRegistration AddModule<T>(PluginFeature feature) where T : IWebModule
|
||||||
{
|
|
||||||
_modules.RemoveAll(r => r.Create == create);
|
|
||||||
StartWebServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddModule<T>(PluginFeature feature) where T : IWebModule
|
|
||||||
{
|
{
|
||||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||||
if (_modules.Any(r => r.WebModuleType == typeof(T)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_modules.Add(new WebModuleRegistration(feature, typeof(T)));
|
WebModuleRegistration registration = new(this, feature, typeof(T));
|
||||||
|
_modules.Add(registration);
|
||||||
StartWebServer();
|
StartWebServer();
|
||||||
|
|
||||||
|
return registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveModule<T>() where T : IWebModule
|
public void RemoveModule(WebModuleRegistration registration)
|
||||||
{
|
{
|
||||||
_modules.RemoveAll(r => r.WebModuleType == typeof(T));
|
_modules.Remove(registration);
|
||||||
StartWebServer();
|
StartWebServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -123,27 +123,35 @@ public class DeviceVisualizer : Control
|
|||||||
if (Device == null)
|
if (Device == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool difference = false;
|
// Device might be modified mid-check, in that case just pretend it was not dirty
|
||||||
|
try
|
||||||
int newLedCount = Device.RgbDevice.Count();
|
|
||||||
if (_previousState.Length != newLedCount)
|
|
||||||
{
|
{
|
||||||
_previousState = new Color[newLedCount];
|
bool difference = false;
|
||||||
difference = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check all LEDs for differences and copy the colors to a new state
|
int newLedCount = Device.RgbDevice.Count();
|
||||||
int index = 0;
|
if (_previousState.Length != newLedCount)
|
||||||
foreach (Led led in Device.RgbDevice)
|
{
|
||||||
{
|
_previousState = new Color[newLedCount];
|
||||||
if (_previousState[index] != led.Color)
|
|
||||||
difference = true;
|
difference = true;
|
||||||
|
}
|
||||||
|
|
||||||
_previousState[index] = led.Color;
|
// Check all LEDs for differences and copy the colors to a new state
|
||||||
index++;
|
int index = 0;
|
||||||
|
foreach (Led led in Device.RgbDevice)
|
||||||
|
{
|
||||||
|
if (_previousState[index] != led.Color)
|
||||||
|
difference = true;
|
||||||
|
|
||||||
|
_previousState[index] = led.Color;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return difference;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return difference;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
@ -153,9 +161,9 @@ public class DeviceVisualizer : Control
|
|||||||
|
|
||||||
private Rect MeasureDevice()
|
private Rect MeasureDevice()
|
||||||
{
|
{
|
||||||
if (Device == null)
|
if (Device == null || float.IsNaN(Device.RgbDevice.ActualSize.Width) || float.IsNaN(Device.RgbDevice.ActualSize.Height))
|
||||||
return new Rect();
|
return new Rect();
|
||||||
|
|
||||||
Rect deviceRect = new(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height);
|
Rect deviceRect = new(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height);
|
||||||
Geometry geometry = new RectangleGeometry(deviceRect);
|
Geometry geometry = new RectangleGeometry(deviceRect);
|
||||||
geometry.Transform = new RotateTransform(Device.Rotation);
|
geometry.Transform = new RotateTransform(Device.Rotation);
|
||||||
@ -335,7 +343,7 @@ public class DeviceVisualizer : Control
|
|||||||
deviceVisualizerLed.DrawBitmap(context, 2 * device.Scale);
|
deviceVisualizerLed.DrawBitmap(context, 2 * device.Scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
BitmapCache[path] = renderTargetBitmap;
|
// BitmapCache[path] = renderTargetBitmap;
|
||||||
return renderTargetBitmap;
|
return renderTargetBitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,9 +45,9 @@ public class RemoteController : WebApiController
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Route(HttpVerbs.Post, "/remote/restart")]
|
[Route(HttpVerbs.Post, "/remote/restart")]
|
||||||
public void PostRestart()
|
public void PostRestart([FormField] string[] args)
|
||||||
{
|
{
|
||||||
Utilities.Restart(_coreService.IsElevated, TimeSpan.FromMilliseconds(500));
|
Utilities.Restart(_coreService.IsElevated, TimeSpan.FromMilliseconds(500), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route(HttpVerbs.Post, "/remote/shutdown")]
|
[Route(HttpVerbs.Post, "/remote/shutdown")]
|
||||||
|
|||||||
@ -52,7 +52,7 @@
|
|||||||
<TextBlock Classes="subtitle" FontSize="12" Text="With this checked, Artemis will not load a layout for this device unless you specifically provide one." />
|
<TextBlock Classes="subtitle" FontSize="12" Text="With this checked, Artemis will not load a layout for this device unless you specifically provide one." />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="1" Grid.Column="1" VerticalAlignment="Center">
|
<StackPanel Grid.Row="1" Grid.Column="1" VerticalAlignment="Center">
|
||||||
<CheckBox HorizontalAlignment="Right" Margin="0,0,-10,0" IsChecked="{CompiledBinding Device.DisableDefaultLayout}" />
|
<CheckBox HorizontalAlignment="Right" Margin="0,0,-10,0" IsChecked="{CompiledBinding Device.DisableDefaultLayout}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Border Classes="card-separator" />
|
<Border Classes="card-separator" />
|
||||||
|
|||||||
@ -33,7 +33,7 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
|||||||
Device = device;
|
Device = device;
|
||||||
DisplayName = "Layout";
|
DisplayName = "Layout";
|
||||||
DefaultLayoutPath = Device.DeviceProvider.LoadLayout(Device).FilePath;
|
DefaultLayoutPath = Device.DeviceProvider.LoadLayout(Device).FilePath;
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
Device.PropertyChanged += DeviceOnPropertyChanged;
|
Device.PropertyChanged += DeviceOnPropertyChanged;
|
||||||
@ -42,23 +42,21 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ArtemisDevice Device { get; }
|
public ArtemisDevice Device { get; }
|
||||||
|
|
||||||
public string DefaultLayoutPath { get; }
|
public string DefaultLayoutPath { get; }
|
||||||
|
|
||||||
public string? ImagePath => Device.Layout?.Image?.LocalPath;
|
public string? ImagePath => Device.Layout?.Image?.LocalPath;
|
||||||
|
|
||||||
public string? CustomLayoutPath => Device.CustomLayoutPath;
|
public string? CustomLayoutPath => Device.CustomLayoutPath;
|
||||||
|
|
||||||
public bool HasCustomLayout => Device.CustomLayoutPath != null;
|
public bool HasCustomLayout => Device.CustomLayoutPath != null;
|
||||||
|
|
||||||
public void ClearCustomLayout()
|
public void ClearCustomLayout()
|
||||||
{
|
{
|
||||||
Device.CustomLayoutPath = null;
|
Device.CustomLayoutPath = null;
|
||||||
_notificationService.CreateNotification()
|
_notificationService.CreateNotification()
|
||||||
.WithMessage("Cleared imported layout.")
|
.WithMessage("Cleared imported layout.")
|
||||||
.WithSeverity(NotificationSeverity.Informational);
|
.WithSeverity(NotificationSeverity.Informational);
|
||||||
|
|
||||||
_deviceService.SaveDevice(Device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task BrowseCustomLayout()
|
public async Task BrowseCustomLayout()
|
||||||
@ -75,8 +73,6 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
|||||||
.WithTitle("Imported layout")
|
.WithTitle("Imported layout")
|
||||||
.WithMessage($"File loaded from {files[0]}")
|
.WithMessage($"File loaded from {files[0]}")
|
||||||
.WithSeverity(NotificationSeverity.Informational);
|
.WithSeverity(NotificationSeverity.Informational);
|
||||||
|
|
||||||
_deviceService.SaveDevice(Device);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +150,12 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase
|
|||||||
{
|
{
|
||||||
if (e.PropertyName is nameof(Device.CustomLayoutPath) or nameof(Device.DisableDefaultLayout))
|
if (e.PropertyName is nameof(Device.CustomLayoutPath) or nameof(Device.DisableDefaultLayout))
|
||||||
{
|
{
|
||||||
Task.Run(() => _deviceService.ApplyDeviceLayout(Device, Device.GetBestDeviceLayout()));
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
_deviceService.ApplyDeviceLayout(Device, Device.GetBestDeviceLayout());
|
||||||
|
_deviceService.SaveDevice(Device);
|
||||||
|
});
|
||||||
|
|
||||||
this.RaisePropertyChanged(nameof(CustomLayoutPath));
|
this.RaisePropertyChanged(nameof(CustomLayoutPath));
|
||||||
this.RaisePropertyChanged(nameof(HasCustomLayout));
|
this.RaisePropertyChanged(nameof(HasCustomLayout));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,8 +10,6 @@ namespace Artemis.UI.Screens.Device;
|
|||||||
|
|
||||||
public partial class DeviceLogicalLayoutDialogView : ReactiveUserControl<DeviceLogicalLayoutDialogViewModel>
|
public partial class DeviceLogicalLayoutDialogView : ReactiveUserControl<DeviceLogicalLayoutDialogViewModel>
|
||||||
{
|
{
|
||||||
private readonly AutoCompleteBox _autoCompleteBox;
|
|
||||||
|
|
||||||
public DeviceLogicalLayoutDialogView()
|
public DeviceLogicalLayoutDialogView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -23,8 +21,8 @@ public partial class DeviceLogicalLayoutDialogView : ReactiveUserControl<DeviceL
|
|||||||
private async Task DelayedAutoFocus()
|
private async Task DelayedAutoFocus()
|
||||||
{
|
{
|
||||||
await Task.Delay(200);
|
await Task.Delay(200);
|
||||||
_autoCompleteBox.Focus();
|
RegionsAutoCompleteBox.Focus();
|
||||||
_autoCompleteBox.PopulateComplete();
|
RegionsAutoCompleteBox.PopulateComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SearchRegions(string search, object item)
|
private bool SearchRegions(string search, object item)
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
@ -23,6 +25,8 @@ public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
|||||||
private double _dragOffsetX;
|
private double _dragOffsetX;
|
||||||
private double _dragOffsetY;
|
private double _dragOffsetY;
|
||||||
private bool _isSelected;
|
private bool _isSelected;
|
||||||
|
private float _x;
|
||||||
|
private float _y;
|
||||||
|
|
||||||
public SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel, IDeviceService deviceService, ISettingsService settingsService, IWindowService windowService)
|
public SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel, IDeviceService deviceService, ISettingsService settingsService, IWindowService windowService)
|
||||||
{
|
{
|
||||||
@ -33,6 +37,14 @@ public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
|||||||
Device = device;
|
Device = device;
|
||||||
SurfaceEditorViewModel = surfaceEditorViewModel;
|
SurfaceEditorViewModel = surfaceEditorViewModel;
|
||||||
DetectInput = ReactiveCommand.CreateFromTask(ExecuteDetectInput, this.WhenAnyValue(vm => vm.CanDetectInput));
|
DetectInput = ReactiveCommand.CreateFromTask(ExecuteDetectInput, this.WhenAnyValue(vm => vm.CanDetectInput));
|
||||||
|
X = device.X;
|
||||||
|
Y = device.Y;
|
||||||
|
|
||||||
|
this.WhenActivated(d =>
|
||||||
|
{
|
||||||
|
Device.PropertyChanged += DeviceOnPropertyChanged;
|
||||||
|
Disposable.Create(() => Device.PropertyChanged -= DeviceOnPropertyChanged).DisposeWith(d);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> DetectInput { get; }
|
public ReactiveCommand<Unit, Unit> DetectInput { get; }
|
||||||
@ -47,6 +59,18 @@ public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
|||||||
set => RaiseAndSetIfChanged(ref _isSelected, value);
|
set => RaiseAndSetIfChanged(ref _isSelected, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float X
|
||||||
|
{
|
||||||
|
get => _x;
|
||||||
|
set => RaiseAndSetIfChanged(ref _x, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Y
|
||||||
|
{
|
||||||
|
get => _y;
|
||||||
|
set => RaiseAndSetIfChanged(ref _y, value);
|
||||||
|
}
|
||||||
|
|
||||||
public void StartMouseDrag(Point mouseStartPosition)
|
public void StartMouseDrag(Point mouseStartPosition)
|
||||||
{
|
{
|
||||||
if (!IsSelected)
|
if (!IsSelected)
|
||||||
@ -73,16 +97,16 @@ public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
|||||||
|
|
||||||
if (Fits(x, y, ignoreOverlap))
|
if (Fits(x, y, ignoreOverlap))
|
||||||
{
|
{
|
||||||
Device.X = x;
|
X = x;
|
||||||
Device.Y = y;
|
Y = y;
|
||||||
}
|
}
|
||||||
else if (Fits(x, Device.Y, ignoreOverlap))
|
else if (Fits(x, Device.Y, ignoreOverlap))
|
||||||
{
|
{
|
||||||
Device.X = x;
|
X = x;
|
||||||
}
|
}
|
||||||
else if (Fits(Device.X, y, ignoreOverlap))
|
else if (Fits(Device.X, y, ignoreOverlap))
|
||||||
{
|
{
|
||||||
Device.Y = y;
|
Y = y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,10 +124,9 @@ public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
|||||||
|
|
||||||
IEnumerable<SKRect> own = Device.Leds
|
IEnumerable<SKRect> own = Device.Leds
|
||||||
.Select(l => SKRect.Create(l.Rectangle.Left + x, l.Rectangle.Top + y, l.Rectangle.Width, l.Rectangle.Height));
|
.Select(l => SKRect.Create(l.Rectangle.Left + x, l.Rectangle.Top + y, l.Rectangle.Width, l.Rectangle.Height));
|
||||||
IEnumerable<SKRect> others = _deviceService.EnabledDevices
|
IEnumerable<SKRect> others = SurfaceEditorViewModel.SurfaceDeviceViewModels
|
||||||
.Where(d => d != Device && d.IsEnabled)
|
.Where(vm => vm != this && vm.Device.IsEnabled)
|
||||||
.SelectMany(d => d.Leds)
|
.SelectMany(vm => vm.Device.Leds.Select(l => SKRect.Create(l.Rectangle.Left + vm.X, l.Rectangle.Top + vm.Y, l.Rectangle.Width, l.Rectangle.Height)));
|
||||||
.Select(l => SKRect.Create(l.Rectangle.Left + l.Device.X, l.Rectangle.Top + l.Device.Y, l.Rectangle.Width, l.Rectangle.Height));
|
|
||||||
|
|
||||||
return !own.Any(o => others.Any(l => l.IntersectsWith(o)));
|
return !own.Any(o => others.Any(l => l.IntersectsWith(o)));
|
||||||
}
|
}
|
||||||
@ -122,4 +145,18 @@ public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
|||||||
if (viewModel.MadeChanges)
|
if (viewModel.MadeChanges)
|
||||||
_deviceService.SaveDevice(Device);
|
_deviceService.SaveDevice(Device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Apply()
|
||||||
|
{
|
||||||
|
Device.X = X;
|
||||||
|
Device.Y = Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeviceOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(Device.X))
|
||||||
|
X = Device.X;
|
||||||
|
if (e.PropertyName == nameof(Device.Y))
|
||||||
|
Y = Device.Y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -82,8 +82,8 @@
|
|||||||
<ItemsControl Name="DeviceContainer" ItemsSource="{CompiledBinding SurfaceDeviceViewModels}" ClipToBounds="False">
|
<ItemsControl Name="DeviceContainer" ItemsSource="{CompiledBinding SurfaceDeviceViewModels}" ClipToBounds="False">
|
||||||
<ItemsControl.Styles>
|
<ItemsControl.Styles>
|
||||||
<Style Selector="ContentPresenter">
|
<Style Selector="ContentPresenter">
|
||||||
<Setter Property="Canvas.Left" Value="{Binding Device.X}" />
|
<Setter Property="Canvas.Left" Value="{CompiledBinding X, DataType=surfaceEditor:SurfaceDeviceViewModel}" />
|
||||||
<Setter Property="Canvas.Top" Value="{Binding Device.Y}" />
|
<Setter Property="Canvas.Top" Value="{CompiledBinding Y, DataType=surfaceEditor:SurfaceDeviceViewModel}" />
|
||||||
</Style>
|
</Style>
|
||||||
</ItemsControl.Styles>
|
</ItemsControl.Styles>
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
|
|||||||
@ -135,6 +135,8 @@ public class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewModel
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_saving = true;
|
_saving = true;
|
||||||
|
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
|
||||||
|
surfaceDeviceViewModel.Apply();
|
||||||
_deviceService.SaveDevices();
|
_deviceService.SaveDevices();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user