diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj
index bd0e5eb30..b13eae70e 100644
--- a/src/Artemis.Core/Artemis.Core.csproj
+++ b/src/Artemis.Core/Artemis.Core.csproj
@@ -73,6 +73,9 @@
..\..\..\RGB.NET\bin\net5.0\RGB.NET.Groups.dll
+
+ ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Layout.dll
+
diff --git a/src/Artemis.Core/Artemis.Core.csproj.DotSettings b/src/Artemis.Core/Artemis.Core.csproj.DotSettings
index b1ea041c1..7d6c52363 100644
--- a/src/Artemis.Core/Artemis.Core.csproj.DotSettings
+++ b/src/Artemis.Core/Artemis.Core.csproj.DotSettings
@@ -43,6 +43,7 @@
True
True
True
+ True
True
True
True
diff --git a/src/Artemis.Core/Events/DeviceConfigurationEventArgs.cs b/src/Artemis.Core/Events/DeviceConfigurationEventArgs.cs
deleted file mode 100644
index dbc66515e..000000000
--- a/src/Artemis.Core/Events/DeviceConfigurationEventArgs.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-
-namespace Artemis.Core
-{
- ///
- /// Provides data about surface configuration related events
- ///
- public class SurfaceConfigurationEventArgs : EventArgs
- {
- internal SurfaceConfigurationEventArgs(ArtemisSurface surface)
- {
- Surface = surface;
- }
-
- ///
- /// Gets the active surface at the time the event fired
- ///
- public ArtemisSurface Surface { get; }
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.Core/Events/DeviceEventArgs.cs b/src/Artemis.Core/Events/DeviceEventArgs.cs
index 3999e9f83..3eaf045bf 100644
--- a/src/Artemis.Core/Events/DeviceEventArgs.cs
+++ b/src/Artemis.Core/Events/DeviceEventArgs.cs
@@ -1,5 +1,4 @@
using System;
-using RGB.NET.Core;
namespace Artemis.Core
{
@@ -8,7 +7,7 @@ namespace Artemis.Core
///
public class DeviceEventArgs : EventArgs
{
- internal DeviceEventArgs(IRGBDevice device)
+ internal DeviceEventArgs(ArtemisDevice device)
{
Device = device;
}
@@ -16,6 +15,6 @@ namespace Artemis.Core
///
/// Gets the device this event is related to
///
- public IRGBDevice Device { get; }
+ public ArtemisDevice Device { get; }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Events/SurfaceConfigurationEventArgs.cs b/src/Artemis.Core/Events/SurfaceConfigurationEventArgs.cs
new file mode 100644
index 000000000..52f8192a9
--- /dev/null
+++ b/src/Artemis.Core/Events/SurfaceConfigurationEventArgs.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+
+namespace Artemis.Core
+{
+ ///
+ /// Provides data about device configuration related events
+ ///
+ public class SurfaceConfigurationEventArgs : EventArgs
+ {
+ internal SurfaceConfigurationEventArgs(List devices)
+ {
+ Devices = devices;
+ }
+
+ ///
+ /// Gets the current list of devices
+ ///
+ public List Devices { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Extensions/RgbDeviceExtensions.cs b/src/Artemis.Core/Extensions/RgbDeviceExtensions.cs
index 12c6988d4..5c1e11353 100644
--- a/src/Artemis.Core/Extensions/RgbDeviceExtensions.cs
+++ b/src/Artemis.Core/Extensions/RgbDeviceExtensions.cs
@@ -16,8 +16,6 @@ namespace Artemis.Core
builder.Append(rgbDevice.DeviceInfo.Model);
builder.Append('-');
builder.Append(rgbDevice.DeviceInfo.DeviceType);
- builder.Append('-');
- builder.Append(rgbDevice.DeviceInfo.Lighting);
return builder.ToString();
}
}
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index 5d7bcdb99..ace2734a9 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -287,7 +287,7 @@ namespace Artemis.Core
if (!Enabled || Path == null || LayerShape?.Path == null || !General.PropertiesInitialized || !Transform.PropertiesInitialized)
return;
// Ensure the brush is ready
- if (LayerBrush?.BaseProperties?.PropertiesInitialized == false || LayerBrush?.BrushType != LayerBrushType.Regular)
+ if (LayerBrush?.BaseProperties?.PropertiesInitialized == false)
return;
RenderTimeline(Timeline, canvas);
@@ -322,6 +322,9 @@ namespace Artemis.Core
return;
ApplyTimeline(timeline);
+
+ if (LayerBrush?.BrushType != LayerBrushType.Regular)
+ return;
try
{
@@ -564,7 +567,7 @@ namespace Artemis.Core
CalculateRenderProperties();
}
- internal void PopulateLeds(ArtemisSurface surface)
+ internal void PopulateLeds(IEnumerable devices)
{
if (Disposed)
throw new ObjectDisposedException("Layer");
@@ -572,7 +575,7 @@ namespace Artemis.Core
List leds = new();
// Get the surface LEDs for this layer
- List availableLeds = surface.Devices.Where(d => d.IsEnabled).SelectMany(d => d.Leds).ToList();
+ List availableLeds = devices.SelectMany(d => d.Leds).ToList();
foreach (LedEntity ledEntity in LayerEntity.Leds)
{
ArtemisLed? match = availableLeds.FirstOrDefault(a => a.Device.RgbDevice.GetDeviceIdentifier() == ledEntity.DeviceIdentifier &&
diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs
index 28ee0ac5a..1432d3157 100644
--- a/src/Artemis.Core/Models/Profile/Profile.cs
+++ b/src/Artemis.Core/Models/Profile/Profile.cs
@@ -124,14 +124,14 @@ namespace Artemis.Core
///
/// Populates all the LEDs on the elements in this profile
///
- /// The currently active surface that contains the LEDs
- public void PopulateLeds(ArtemisSurface surface)
+ /// The devices to use while populating LEDs
+ public void PopulateLeds(IEnumerable devices)
{
if (Disposed)
throw new ObjectDisposedException("Profile");
foreach (Layer layer in GetAllLayers())
- layer.PopulateLeds(surface);
+ layer.PopulateLeds(devices);
}
///
@@ -197,7 +197,7 @@ namespace Artemis.Core
ProfileEntity.Layers.AddRange(GetAllLayers().Select(f => f.LayerEntity));
}
- internal void Activate(ArtemisSurface surface)
+ internal void Activate(IEnumerable devices)
{
lock (_lock)
{
@@ -206,7 +206,7 @@ namespace Artemis.Core
if (IsActivated)
return;
- PopulateLeds(surface);
+ PopulateLeds(devices);
OnActivated();
IsActivated = true;
}
diff --git a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
index 107287d53..90aee62a2 100644
--- a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
+++ b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
@@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core.DeviceProviders;
+using Artemis.Core.Services;
using Artemis.Storage.Entities.Surface;
using RGB.NET.Core;
+using RGB.NET.Layout;
using SkiaSharp;
namespace Artemis.Core
@@ -17,12 +19,11 @@ namespace Artemis.Core
private SKPath? _path;
private SKRect _rectangle;
- internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, ArtemisSurface surface)
+ internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider)
{
DeviceEntity = new DeviceEntity();
RgbDevice = rgbDevice;
DeviceProvider = deviceProvider;
- Surface = surface;
Rotation = 0;
Scale = 1;
@@ -31,27 +32,22 @@ namespace Artemis.Core
GreenScale = 1;
BlueScale = 1;
IsEnabled = true;
-
- deviceProvider.DeviceLayoutPaths.TryGetValue(rgbDevice, out string? layoutPath);
- LayoutPath = layoutPath;
InputIdentifiers = new List();
Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
LedIds = new ReadOnlyDictionary(Leds.ToDictionary(l => l.RgbLed.Id, l => l));
+
+ ApplyKeyboardLayout();
ApplyToEntity();
CalculateRenderProperties();
}
- internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, ArtemisSurface surface, DeviceEntity deviceEntity)
+ internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity)
{
DeviceEntity = deviceEntity;
RgbDevice = rgbDevice;
DeviceProvider = deviceProvider;
- Surface = surface;
-
- deviceProvider.DeviceLayoutPaths.TryGetValue(rgbDevice, out string? layoutPath);
- LayoutPath = layoutPath;
InputIdentifiers = new List();
foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers)
@@ -59,6 +55,8 @@ namespace Artemis.Core
Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
LedIds = new ReadOnlyDictionary(Leds.ToDictionary(l => l.RgbLed.Id, l => l));
+
+ ApplyKeyboardLayout();
}
///
@@ -89,21 +87,16 @@ namespace Artemis.Core
///
public DeviceProvider DeviceProvider { get; }
- ///
- /// Gets the surface containing this device
- ///
- public ArtemisSurface Surface { get; }
-
///
/// Gets a read only collection containing the LEDs of this device
///
- public ReadOnlyCollection Leds { get; }
+ public ReadOnlyCollection Leds { get; private set; }
///
/// Gets a dictionary containing all the LEDs of this device with their corresponding RGB.NET as
/// key
///
- public ReadOnlyDictionary LedIds { get; }
+ public ReadOnlyDictionary LedIds { get; private set; }
///
/// Gets a list of input identifiers associated with this device
@@ -215,12 +208,13 @@ namespace Artemis.Core
}
///
- /// Gets or sets a boolean indicating whether this devices is enabled or not
+ /// Gets a boolean indicating whether this devices is enabled or not
+ /// Note: To enable/disable a device use the methods provided by
///
public bool IsEnabled
{
get => DeviceEntity.IsEnabled;
- set
+ internal set
{
DeviceEntity.IsEnabled = value;
OnPropertyChanged(nameof(IsEnabled));
@@ -228,9 +222,51 @@ namespace Artemis.Core
}
///
- /// Gets the path to where the layout of the device was (attempted to be) loaded from
+ /// Gets or sets the physical layout of the device e.g. ISO or ANSI.
+ /// Only applicable to keyboards
///
- public string? LayoutPath { get; internal set; }
+ public KeyboardLayoutType PhysicalLayout
+ {
+ get => (KeyboardLayoutType) DeviceEntity.PhysicalLayout;
+ set
+ {
+ DeviceEntity.PhysicalLayout = (int) value;
+ OnPropertyChanged(nameof(PhysicalLayout));
+ }
+ }
+
+ ///
+ /// Gets or sets the logical layout of the device e.g. DE, UK or US.
+ /// Only applicable to keyboards
+ ///
+ public string? LogicalLayout
+ {
+ get => DeviceEntity.LogicalLayout;
+ set
+ {
+ DeviceEntity.LogicalLayout = value;
+ OnPropertyChanged(nameof(LogicalLayout));
+ }
+ }
+
+ ///
+ /// Gets or sets the path of the custom layout to load when calling
+ /// for this device
+ ///
+ public string? CustomLayoutPath
+ {
+ get => DeviceEntity.CustomLayoutPath;
+ set
+ {
+ DeviceEntity.CustomLayoutPath = value;
+ OnPropertyChanged(nameof(CustomLayoutPath));
+ }
+ }
+
+ ///
+ /// Gets the layout of the device expanded with Artemis-specific data
+ ///
+ public ArtemisLayout? Layout { get; internal set; }
internal DeviceEntity DeviceEntity { get; }
@@ -261,10 +297,46 @@ namespace Artemis.Core
return artemisLed;
}
+ ///
+ /// Generates the default layout file name of the device
+ ///
+ /// If true, the .xml extension is added to the file name
+ /// The resulting file name e.g. CORSAIR GLAIVE.xml or K95 RGB-ISO.xml
+ public string GetLayoutFileName(bool includeExtension = true)
+ {
+ // Take out invalid file name chars, may not be perfect but neither are you
+ string fileName = System.IO.Path.GetInvalidFileNameChars().Aggregate(RgbDevice.DeviceInfo.Model, (current, c) => current.Replace(c, '-'));
+ if (RgbDevice is IKeyboard)
+ fileName = $"{fileName}-{PhysicalLayout.ToString().ToUpper()}";
+ if (includeExtension)
+ fileName = $"{fileName}.xml";
+
+ return fileName;
+ }
+
+ ///
+ /// Applies the provided layout to the device
+ ///
+ /// The layout to apply
+ /// A boolean indicating whether to add missing LEDs defined in the layout but missing on the device
+ /// A boolean indicating whether to remove excess LEDs present in the device but missing in the layout
+ internal void ApplyLayout(ArtemisLayout layout, bool createMissingLeds, bool removeExcessiveLeds)
+ {
+ if (layout.IsValid)
+ layout.RgbLayout!.ApplyTo(RgbDevice, createMissingLeds, removeExcessiveLeds);
+
+ Leds = RgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
+ LedIds = new ReadOnlyDictionary(Leds.ToDictionary(l => l.RgbLed.Id, l => l));
+
+ Layout = layout;
+ Layout.ApplyDevice(this);
+ OnDeviceUpdated();
+ }
+
internal void ApplyToEntity()
{
// Other properties are computed
- DeviceEntity.DeviceIdentifier = RgbDevice.GetDeviceIdentifier();
+ DeviceEntity.Id = RgbDevice.GetDeviceIdentifier();
DeviceEntity.InputIdentifiers.Clear();
foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers)
@@ -295,7 +367,7 @@ namespace Artemis.Core
internal void CalculateRenderProperties()
{
- Rectangle = RgbDevice.DeviceRectangle.ToSKRect();
+ Rectangle = RgbDevice.Boundary.ToSKRect();
if (!Leds.Any())
return;
@@ -309,6 +381,18 @@ namespace Artemis.Core
Path = path;
}
+ private void ApplyKeyboardLayout()
+ {
+ if (!(RgbDevice is IKeyboard keyboard))
+ return;
+
+ // If supported, detect the device layout so that we can load the correct one
+ if (DeviceProvider.CanDetectLogicalLayout)
+ LogicalLayout = DeviceProvider.GetLogicalLayout(keyboard);
+ if (DeviceProvider.CanDetectPhysicalLayout)
+ PhysicalLayout = (KeyboardLayoutType) keyboard.DeviceInfo.Layout;
+ }
+
#region Events
///
diff --git a/src/Artemis.Core/Models/Surface/ArtemisLed.cs b/src/Artemis.Core/Models/Surface/ArtemisLed.cs
index 8085e6e8f..eaf06b73a 100644
--- a/src/Artemis.Core/Models/Surface/ArtemisLed.cs
+++ b/src/Artemis.Core/Models/Surface/ArtemisLed.cs
@@ -46,11 +46,7 @@ namespace Artemis.Core
private set => SetAndNotify(ref _absoluteRectangle, value);
}
- internal void CalculateRectangles()
- {
- Rectangle = RgbLed.LedRectangle.ToSKRect();
- AbsoluteRectangle = RgbLed.AbsoluteLedRectangle.ToSKRect();
- }
+ public ArtemisLedLayout? Layout { get; internal set; }
///
public override string ToString()
@@ -66,5 +62,11 @@ namespace Artemis.Core
{
return RgbLed.Color.DivideRGB(Device.RedScale, Device.GreenScale, Device.BlueScale);
}
+
+ internal void CalculateRectangles()
+ {
+ Rectangle = RgbLed.Boundary.ToSKRect();
+ AbsoluteRectangle = RgbLed.AbsoluteBoundary.ToSKRect();
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/ArtemisSurface.cs b/src/Artemis.Core/Models/Surface/ArtemisSurface.cs
deleted file mode 100644
index c94f26543..000000000
--- a/src/Artemis.Core/Models/Surface/ArtemisSurface.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using Artemis.Storage.Entities.Surface;
-using RGB.NET.Core;
-
-namespace Artemis.Core
-{
- ///
- /// Represents a surface of a specific scale, containing all the available s
- ///
- public class ArtemisSurface : CorePropertyChanged
- {
- private List _devices = new();
- private ReadOnlyDictionary _ledMap = new(new Dictionary());
- private bool _isActive;
- private string _name;
-
- internal ArtemisSurface(RGBSurface rgbSurface, string name)
- {
- SurfaceEntity = new SurfaceEntity {DeviceEntities = new List()};
- EntityId = Guid.NewGuid();
- RgbSurface = rgbSurface;
-
- _name = name;
- _isActive = false;
-
- ApplyToEntity();
- }
-
- internal ArtemisSurface(RGBSurface rgbSurface, SurfaceEntity surfaceEntity)
- {
- SurfaceEntity = surfaceEntity;
- EntityId = surfaceEntity.Id;
- RgbSurface = rgbSurface;
-
- _name = surfaceEntity.Name;
- _isActive = surfaceEntity.IsActive;
- }
-
- ///
- /// Gets the RGB.NET surface backing this Artemis surface
- ///
- public RGBSurface RgbSurface { get; }
-
- ///
- /// Gets the name of the surface
- ///
- public string Name
- {
- get => _name;
- set => SetAndNotify(ref _name, value);
- }
-
- ///
- /// Gets a boolean indicating whether this surface is the currently active surface
- ///
- public bool IsActive
- {
- get => _isActive;
- internal set => SetAndNotify(ref _isActive, value);
- }
-
- ///
- /// Gets a list of devices this surface contains
- ///
- public List Devices
- {
- get => _devices;
- internal set => SetAndNotify(ref _devices, value);
- }
-
- ///
- /// Gets a dictionary containing all s on the surface with their corresponding RGB.NET
- /// as key
- ///
- public ReadOnlyDictionary LedMap
- {
- get => _ledMap;
- private set => SetAndNotify(ref _ledMap, value);
- }
-
- internal SurfaceEntity SurfaceEntity { get; set; }
- internal Guid EntityId { get; set; }
-
- ///
- /// Attempts to retrieve the that corresponds the provided RGB.NET
- ///
- /// The RGB.NET to find the corresponding for
- /// If found, the corresponding ; otherwise .
- public ArtemisLed? GetArtemisLed(Led led)
- {
- LedMap.TryGetValue(led, out ArtemisLed? artemisLed);
- return artemisLed;
- }
-
- internal void UpdateLedMap()
- {
- LedMap = new ReadOnlyDictionary(
- _devices.Where(d => d.IsEnabled).SelectMany(d => d.Leds.Select(al => new KeyValuePair(al.RgbLed, al))).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
- );
- }
-
- internal void ApplyToEntity()
- {
- SurfaceEntity.Id = EntityId;
- SurfaceEntity.Name = Name;
- SurfaceEntity.IsActive = IsActive;
-
- // Add missing device entities, don't remove old ones in case they come back later
- foreach (DeviceEntity deviceEntity in Devices.Select(d => d.DeviceEntity).ToList())
- if (!SurfaceEntity.DeviceEntities.Contains(deviceEntity))
- SurfaceEntity.DeviceEntities.Add(deviceEntity);
- }
-
- #region Events
-
- ///
- /// Occurs when the scale of the surface is changed
- ///
- public event EventHandler? ScaleChanged;
-
- ///
- /// Invokes the event
- ///
- protected virtual void OnScaleChanged()
- {
- ScaleChanged?.Invoke(this, EventArgs.Empty);
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/KeyboardLayoutType.cs b/src/Artemis.Core/Models/Surface/KeyboardLayoutType.cs
new file mode 100644
index 000000000..43cd2597f
--- /dev/null
+++ b/src/Artemis.Core/Models/Surface/KeyboardLayoutType.cs
@@ -0,0 +1,41 @@
+// ReSharper disable InconsistentNaming
+
+namespace Artemis.Core
+{
+ // Copied from RGB.NET to avoid needing to reference RGB.NET
+ ///
+ /// Represents a physical layout type for a keyboard
+ ///
+ public enum KeyboardLayoutType
+ {
+ ///
+ /// An unknown layout type
+ ///
+ Unknown = 0,
+
+ ///
+ /// The ANSI layout type, often used in the US (uses a short enter)
+ ///
+ ANSI = 1,
+
+ ///
+ /// The ISO layout type, often used in the EU (uses a tall enter)
+ ///
+ ISO = 2,
+
+ ///
+ /// The JIS layout type, often used in Japan (based on ISO)
+ ///
+ JIS = 3,
+
+ ///
+ /// The ABNT layout type, often used in Brazil/Portugal (based on ISO)
+ ///
+ ABNT = 4,
+
+ ///
+ /// The KS layout type, often used in South Korea
+ ///
+ KS = 5
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs
new file mode 100644
index 000000000..cc837ca3b
--- /dev/null
+++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using RGB.NET.Layout;
+
+namespace Artemis.Core
+{
+ ///
+ /// Represents a device layout decorated with extra Artemis-specific data
+ ///
+ public class ArtemisLayout
+ {
+ ///
+ /// Creates a new instance of the class
+ ///
+ /// The path of the layout XML file
+ /// The source from where this layout is being loaded
+ public ArtemisLayout(string filePath, LayoutSource source)
+ {
+ FilePath = filePath;
+ Source = source;
+ Leds = new List();
+
+ LoadLayout();
+ }
+
+ ///
+ /// Gets the file path the layout was (attempted to be) loaded from
+ ///
+ public string FilePath { get; }
+
+ ///
+ /// Gets the source from where this layout was loaded
+ ///
+ public LayoutSource Source { get; }
+
+ ///
+ /// Gets the device this layout is applied to
+ ///
+ public ArtemisDevice? Device { get; private set; }
+
+ ///
+ /// Gets a boolean indicating whether a valid layout was loaded
+ ///
+ public bool IsValid { get; private set; }
+
+ ///
+ /// Gets the image of the device
+ ///
+ public Uri? Image { get; private set; }
+
+ ///
+ /// Gets a list of LEDs this layout contains
+ ///
+ public List Leds { get; }
+
+ ///
+ /// Gets the RGB.NET device layout
+ ///
+ public DeviceLayout RgbLayout { get; private set; } = null!;
+
+ ///
+ /// Gets the custom layout data embedded in the RGB.NET layout
+ ///
+ public LayoutCustomDeviceData LayoutCustomDeviceData { get; private set; } = null!;
+
+ public void ReloadFromDisk()
+ {
+ Leds.Clear();
+ LoadLayout();
+ }
+
+ internal void ApplyDevice(ArtemisDevice artemisDevice)
+ {
+ Device = artemisDevice;
+ foreach (ArtemisLedLayout artemisLedLayout in Leds)
+ artemisLedLayout.ApplyDevice(Device);
+ }
+
+ private void LoadLayout()
+ {
+ DeviceLayout? deviceLayout = DeviceLayout.Load(FilePath, typeof(LayoutCustomDeviceData), typeof(LayoutCustomLedData));
+ if (deviceLayout != null)
+ {
+ RgbLayout = deviceLayout;
+ IsValid = true;
+ }
+ else
+ {
+ RgbLayout = new DeviceLayout();
+ IsValid = false;
+ }
+
+ if (IsValid)
+ Leds.AddRange(RgbLayout.Leds.Select(l => new ArtemisLedLayout(this, l)));
+
+ LayoutCustomDeviceData = (LayoutCustomDeviceData?) RgbLayout.CustomData ?? new LayoutCustomDeviceData();
+ ApplyCustomDeviceData();
+ }
+
+ private void ApplyCustomDeviceData()
+ {
+ if (!IsValid)
+ {
+ Image = null;
+ return;
+ }
+
+ Uri layoutDirectory = new(Path.GetDirectoryName(FilePath)! + "/", UriKind.Absolute);
+ if (LayoutCustomDeviceData.DeviceImage != null)
+ Image = new Uri(layoutDirectory, new Uri(LayoutCustomDeviceData.DeviceImage, UriKind.Relative));
+ else
+ Image = null;
+ }
+ }
+
+ ///
+ /// Represents a source from where a layout came
+ ///
+ public enum LayoutSource
+ {
+ ///
+ /// A layout loaded from config
+ ///
+ Configured,
+
+ ///
+ /// A layout loaded from the user layout folder
+ ///
+ User,
+
+ ///
+ /// A layout loaded from the plugin folder
+ ///
+ Plugin,
+
+ ///
+ /// A default layout loaded as a fallback option
+ ///
+ Default
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs
new file mode 100644
index 000000000..e0ee9fa07
--- /dev/null
+++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs
@@ -0,0 +1,72 @@
+using System;
+using System.IO;
+using System.Linq;
+using RGB.NET.Layout;
+
+namespace Artemis.Core
+{
+ public class ArtemisLedLayout
+ {
+ internal ArtemisLedLayout(ArtemisLayout deviceLayout, ILedLayout led)
+ {
+ DeviceLayout = deviceLayout;
+ RgbLayout = led;
+ LayoutCustomLedData = (LayoutCustomLedData?) led.CustomData ?? new LayoutCustomLedData();
+ }
+
+ ///
+ /// Gets the device layout of this LED layout
+ ///
+ public ArtemisLayout DeviceLayout { get; }
+
+ ///
+ /// Gets the RGB.NET LED Layout of this LED layout
+ ///
+ public ILedLayout RgbLayout { get; }
+
+ ///
+ /// Gets the LED this layout is applied to
+ ///
+ public ArtemisLed? Led { get; protected set; }
+
+ ///
+ /// Gets the name of the logical layout this LED belongs to
+ ///
+ public string? LogicalName { get; private set; }
+
+ ///
+ /// Gets the image of the LED
+ ///
+ public Uri? Image { get; private set; }
+
+ ///
+ /// Gets the custom layout data embedded in the RGB.NET layout
+ ///
+ public LayoutCustomLedData LayoutCustomLedData { get; }
+
+ internal void ApplyDevice(ArtemisDevice device)
+ {
+ 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())
+ return;
+
+ Uri layoutDirectory = new(Path.GetDirectoryName(DeviceLayout.FilePath)! + "\\", UriKind.Absolute);
+ // Prefer a matching layout or else a default layout (that has no name)
+ LayoutCustomLedDataLogicalLayout logicalLayout = LayoutCustomLedData.LogicalLayouts
+ .OrderBy(l => l.Name == artemisDevice.LogicalLayout)
+ .ThenBy(l => l.Name == null)
+ .First();
+
+ LogicalName = logicalLayout.Name;
+ Image = new Uri(layoutDirectory, logicalLayout.Image);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/Layout/LayoutCustomDeviceData.cs b/src/Artemis.Core/Models/Surface/Layout/LayoutCustomDeviceData.cs
new file mode 100644
index 000000000..56144ca56
--- /dev/null
+++ b/src/Artemis.Core/Models/Surface/Layout/LayoutCustomDeviceData.cs
@@ -0,0 +1,15 @@
+using System.Xml.Serialization;
+#pragma warning disable 1591
+
+namespace Artemis.Core
+{
+ ///
+ /// Represents extra Artemis-specific information stored in RGB.NET layouts
+ ///
+ [XmlRoot("CustomData")]
+ public class LayoutCustomDeviceData
+ {
+ [XmlElement("DeviceImage")]
+ public string? DeviceImage { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/Layout/LayoutCustomLedData.cs b/src/Artemis.Core/Models/Surface/Layout/LayoutCustomLedData.cs
new file mode 100644
index 000000000..62e029e53
--- /dev/null
+++ b/src/Artemis.Core/Models/Surface/Layout/LayoutCustomLedData.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+#pragma warning disable 1591
+
+namespace Artemis.Core
+{
+ ///
+ /// Represents extra Artemis-specific information stored in RGB.NET layouts
+ ///
+ [XmlRoot("CustomData")]
+ public class LayoutCustomLedData
+ {
+ [XmlArray("LogicalLayouts")]
+ public List? LogicalLayouts { get; set; }
+ }
+
+ ///
+ /// Represents extra Artemis-specific information stored in RGB.NET layouts
+ ///
+ [XmlType("LogicalLayout")]
+ public class LayoutCustomLedDataLogicalLayout
+ {
+ [XmlAttribute("Name")]
+ public string? Name { get; set; }
+
+ [XmlAttribute("Image")]
+ public string? Image { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
index 84b276192..dd3d7ad5b 100644
--- a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
+++ b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.IO;
using Ninject;
using RGB.NET.Core;
@@ -34,7 +33,23 @@ namespace Artemis.Core.DeviceProviders
[Inject]
public ILogger? Logger { get; set; }
- internal Dictionary DeviceLayoutPaths { get; set; } = new();
+ ///
+ /// A boolean indicating whether this device provider detects the physical layout of connected keyboards.
+ ///
+ /// Note: is only called when this or
+ /// is .
+ ///
+ ///
+ public bool CanDetectPhysicalLayout { get; protected set; }
+
+ ///
+ /// A boolean indicating whether this device provider detects the logical layout of connected keyboards
+ ///
+ /// Note: is only called when this or
+ /// is .
+ ///
+ ///
+ public bool CanDetectLogicalLayout { get; protected set; }
///
public override void Disable()
@@ -43,31 +58,49 @@ namespace Artemis.Core.DeviceProviders
}
///
+ /// Loads a layout for the specified device and wraps it in an
///
- ///
- ///
- ///
- protected void ResolveAbsolutePath(Type type, object sender, ResolvePathEventArgs e)
+ /// The device to load the layout for
+ /// The resulting Artemis layout
+ public virtual ArtemisLayout LoadLayout(ArtemisDevice device)
{
- if (sender.GetType() == type || sender.GetType().IsGenericType(type))
- {
- // Start from the plugin directory
- if (e.RelativePart != null && e.FileName != null)
- e.FinalPath = Path.Combine(Plugin.Directory.FullName, e.RelativePart, e.FileName);
- else if (e.RelativePath != null)
- e.FinalPath = Path.Combine(Plugin.Directory.FullName, e.RelativePath);
+ string layoutDir = Path.Combine(Plugin.Directory.FullName, "Layouts");
+ string filePath = Path.Combine(
+ layoutDir,
+ device.RgbDevice.DeviceInfo.Manufacturer,
+ device.RgbDevice.DeviceInfo.DeviceType.ToString(),
+ device.GetLayoutFileName()
+ );
+ return new ArtemisLayout(filePath, LayoutSource.Plugin);
+ }
- IRGBDevice device = (IRGBDevice) sender;
- IRGBDeviceInfo deviceInfo = device.DeviceInfo;
- if (e.FileName != null && !File.Exists(e.FinalPath))
- {
- Logger?.Information("Couldn't find a layout for device {deviceName}, model {deviceModel} at {filePath}",
- deviceInfo.DeviceName, deviceInfo.Model, e.FinalPath);
- }
+ ///
+ /// Loads a layout from the user layout folder for the specified device and wraps it in an
+ ///
+ /// The device to load the layout for
+ /// The resulting Artemis layout
+ public virtual ArtemisLayout LoadUserLayout(ArtemisDevice device)
+ {
+ string layoutDir = Path.Combine(Constants.DataFolder, "user layouts");
+ string filePath = Path.Combine(
+ layoutDir,
+ device.RgbDevice.DeviceInfo.Manufacturer,
+ device.RgbDevice.DeviceInfo.DeviceType.ToString(),
+ device.GetLayoutFileName()
+ );
+ return new ArtemisLayout(filePath, LayoutSource.User);
+ }
- if (e.FileName != null)
- DeviceLayoutPaths[device] = e.FinalPath;
- }
+ ///
+ /// Called when a specific RGB device's logical and physical layout must be detected
+ ///
+ /// Note: Only called when is .
+ ///
+ ///
+ /// The device to detect the layout for, always a keyboard
+ public virtual string GetLogicalLayout(IKeyboard keyboard)
+ {
+ throw new NotImplementedException("Device provider does not support detecting logical layouts (don't call base.GetLogicalLayout())");
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs
index 4028228a7..6424b4f06 100644
--- a/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core.Services;
+using Ninject;
using RGB.NET.Core;
using RGB.NET.Groups;
using SkiaSharp;
@@ -20,7 +21,6 @@ namespace Artemis.Core.LayerBrushes
///
protected RgbNetLayerBrush()
{
- LedGroup = new ListLedGroup();
BrushType = LayerBrushType.RgbNet;
SupportsTransformation = false;
}
@@ -28,7 +28,10 @@ namespace Artemis.Core.LayerBrushes
///
/// The LED group this layer effect is applied to
///
- public ListLedGroup LedGroup { get; internal set; }
+ public ListLedGroup? LedGroup { get; internal set; }
+
+ [Inject]
+ public IRgbService? RgbService { get; set; }
///
/// Called when Artemis needs an instance of the RGB.NET effect you are implementing
@@ -38,9 +41,14 @@ namespace Artemis.Core.LayerBrushes
internal void UpdateLedGroup()
{
- // TODO: This simply renders it on top of the rest, get a ZIndex based on layer position
- LedGroup.ZIndex = 1;
+ if (LedGroup == null)
+ return;
+ if (Layer.Parent != null)
+ LedGroup.ZIndex = Layer.Parent.Children.Count - Layer.Parent.Children.IndexOf(Layer);
+ else
+ LedGroup.ZIndex = 1;
+
List missingLeds = Layer.Leds.Where(l => !LedGroup.ContainsLed(l.RgbLed)).Select(l => l.RgbLed).ToList();
List extraLeds = LedGroup.GetLeds().Where(l => Layer.Leds.All(layerLed => layerLed.RgbLed != l)).ToList();
LedGroup.AddLeds(missingLeds);
@@ -50,7 +58,10 @@ namespace Artemis.Core.LayerBrushes
internal override void Initialize()
{
- LedGroup = new ListLedGroup();
+ if (RgbService == null)
+ throw new ArtemisCoreException("Cannot initialize RGB.NET layer brush because RgbService is not set");
+
+ LedGroup = new ListLedGroup(RgbService.Surface);
Layer.RenderPropertiesUpdated += LayerOnRenderPropertiesUpdated;
InitializeProperties();
@@ -64,8 +75,12 @@ namespace Artemis.Core.LayerBrushes
{
if (disposing)
{
+ if (RgbService == null)
+ throw new ArtemisCoreException("Cannot dispose RGB.NET layer brush because RgbService is not set");
+
Layer.RenderPropertiesUpdated -= LayerOnRenderPropertiesUpdated;
- LedGroup.Detach();
+ LedGroup?.Detach(RgbService.Surface);
+ LedGroup = null;
}
base.Dispose(disposing);
diff --git a/src/Artemis.Core/Plugins/Modules/Module.cs b/src/Artemis.Core/Plugins/Modules/Module.cs
index 99a7b5980..70d4e5bac 100644
--- a/src/Artemis.Core/Plugins/Modules/Module.cs
+++ b/src/Artemis.Core/Plugins/Modules/Module.cs
@@ -146,10 +146,9 @@ namespace Artemis.Core.Modules
/// Called each frame when the module should render
///
/// Time since the last render
- /// The RGB Surface to render to
///
///
- public abstract void Render(double deltaTime, ArtemisSurface surface, SKCanvas canvas, SKImageInfo canvasInfo);
+ public abstract void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo);
///
/// Called when the are met or during an override
@@ -191,9 +190,9 @@ namespace Artemis.Core.Modules
Update(deltaTime);
}
- internal virtual void InternalRender(double deltaTime, ArtemisSurface surface, SKCanvas canvas, SKImageInfo canvasInfo)
+ internal virtual void InternalRender(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
{
- Render(deltaTime, surface, canvas, canvasInfo);
+ Render(deltaTime, canvas, canvasInfo);
}
internal virtual void Activate(bool isOverride)
diff --git a/src/Artemis.Core/Plugins/Modules/ProfileModule.cs b/src/Artemis.Core/Plugins/Modules/ProfileModule.cs
index 6571d4c89..94fbd3125 100644
--- a/src/Artemis.Core/Plugins/Modules/ProfileModule.cs
+++ b/src/Artemis.Core/Plugins/Modules/ProfileModule.cs
@@ -142,10 +142,9 @@ namespace Artemis.Core.Modules
/// Called after the profile has rendered
///
/// Time since the last render
- /// The RGB Surface to render to
///
///
- public virtual void ProfileRendered(double deltaTime, ArtemisSurface surface, SKCanvas canvas, SKImageInfo canvasInfo)
+ public virtual void ProfileRendered(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
{
}
@@ -168,9 +167,9 @@ namespace Artemis.Core.Modules
ProfileUpdated(deltaTime);
}
- internal override void InternalRender(double deltaTime, ArtemisSurface surface, SKCanvas canvas, SKImageInfo canvasInfo)
+ internal override void InternalRender(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
{
- Render(deltaTime, surface, canvas, canvasInfo);
+ Render(deltaTime, canvas, canvasInfo);
lock (_lock)
{
@@ -178,10 +177,10 @@ namespace Artemis.Core.Modules
ActiveProfile?.Render(canvas);
}
- ProfileRendered(deltaTime, surface, canvas, canvasInfo);
+ ProfileRendered(deltaTime, canvas, canvasInfo);
}
- internal async Task ChangeActiveProfileAnimated(Profile? profile, ArtemisSurface? surface)
+ internal async Task ChangeActiveProfileAnimated(Profile? profile, IEnumerable devices)
{
if (profile != null && profile.Module != this)
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}.");
@@ -196,21 +195,19 @@ namespace Artemis.Core.Modules
while (OpacityOverride > 0)
await Task.Delay(50);
- ChangeActiveProfile(profile, surface);
+ ChangeActiveProfile(profile, devices);
AnimatingProfileChange = false;
while (OpacityOverride < 1)
await Task.Delay(50);
}
- internal void ChangeActiveProfile(Profile? profile, ArtemisSurface? surface)
+ internal void ChangeActiveProfile(Profile? profile, IEnumerable devices)
{
if (profile != null && profile.Module != this)
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}.");
if (!IsActivated)
throw new ArtemisCoreException("Cannot activate a profile on a deactivated module");
- if (profile != null && surface == null)
- throw new ArtemisCoreException("If changing the active profile to a non-null profile, a surface is required");
lock (_lock)
{
@@ -220,7 +217,7 @@ namespace Artemis.Core.Modules
ActiveProfile?.Dispose();
ActiveProfile = profile;
- ActiveProfile?.Activate(surface!);
+ ActiveProfile?.Activate(devices);
}
OnActiveProfileChanged();
diff --git a/src/Artemis.Core/RGB.NET/BitmapBrush.cs b/src/Artemis.Core/RGB.NET/BitmapBrush.cs
index 59814336e..11021f317 100644
--- a/src/Artemis.Core/RGB.NET/BitmapBrush.cs
+++ b/src/Artemis.Core/RGB.NET/BitmapBrush.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using Artemis.Core.Services;
using RGB.NET.Core;
using SkiaSharp;
@@ -12,13 +13,15 @@ namespace Artemis.Core
{
private readonly object _disposeLock;
private readonly PluginSetting _sampleSizeSetting;
+ private readonly IRgbService _rgbService;
#region Constructors
- internal BitmapBrush(Scale scale, PluginSetting sampleSizeSetting)
+ internal BitmapBrush(Scale scale, PluginSetting sampleSizeSetting, IRgbService rgbService)
{
_disposeLock = new object();
_sampleSizeSetting = sampleSizeSetting;
+ _rgbService = rgbService;
Scale = scale;
}
@@ -103,7 +106,7 @@ namespace Artemis.Core
if (scaledLocation.X < Bitmap.Width && scaledLocation.Y < Bitmap.Height)
{
Color pixel = Bitmap.GetPixel(scaledLocation.X.RoundToInt(), scaledLocation.Y.RoundToInt()).ToRgbColor();
- ArtemisDevice? artemisDevice = Surface?.GetArtemisLed(renderTarget.Led)?.Device;
+ ArtemisDevice? artemisDevice = _rgbService.GetLed(renderTarget.Led)?.Device;
if (artemisDevice != null)
pixel = pixel.MultiplyRGB(artemisDevice.RedScale, artemisDevice.GreenScale, artemisDevice.BlueScale);
RenderedTargets[renderTarget] = pixel;
@@ -157,7 +160,7 @@ namespace Artemis.Core
Color pixel = new(a / sampleSize, r / sampleSize, g / sampleSize, b / sampleSize);
- ArtemisDevice? artemisDevice = Surface?.GetArtemisLed(renderTarget.Led)?.Device;
+ ArtemisDevice? artemisDevice = _rgbService.GetLed(renderTarget.Led)?.Device;
if (artemisDevice is not null)
pixel = pixel.MultiplyRGB(artemisDevice.RedScale, artemisDevice.GreenScale, artemisDevice.BlueScale);
@@ -188,7 +191,6 @@ namespace Artemis.Core
}
internal bool IsDisposed { get; set; }
- internal ArtemisSurface? Surface { get; set; }
#endregion
}
diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs
index 86c8cb571..ff820f4cb 100644
--- a/src/Artemis.Core/Services/CoreService.cs
+++ b/src/Artemis.Core/Services/CoreService.cs
@@ -34,7 +34,6 @@ namespace Artemis.Core.Services
private readonly IProfileService _profileService;
private readonly PluginSetting _renderScale;
private readonly IRgbService _rgbService;
- private readonly ISurfaceService _surfaceService;
private readonly List _updateExceptions = new();
private List _dataModelExpansions = new();
private DateTime _lastExceptionLog;
@@ -47,7 +46,6 @@ namespace Artemis.Core.Services
ISettingsService settingsService,
IPluginManagementService pluginManagementService,
IRgbService rgbService,
- ISurfaceService surfaceService,
IProfileService profileService,
IModuleService moduleService // injected to ensure module priorities get applied
)
@@ -58,7 +56,6 @@ namespace Artemis.Core.Services
_logger = logger;
_pluginManagementService = pluginManagementService;
_rgbService = rgbService;
- _surfaceService = surfaceService;
_profileService = profileService;
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
_renderScale = settingsService.GetSetting("Core.RenderScale", 0.5);
@@ -121,9 +118,6 @@ namespace Artemis.Core.Services
IsElevated
);
- ArtemisSurface surfaceConfig = _surfaceService.ActiveSurface;
- _logger.Information("Initialized with active surface entity {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.EntityId);
-
OnInitialized();
}
@@ -139,7 +133,7 @@ namespace Artemis.Core.Services
public void PlayIntroAnimation()
{
- IntroAnimation intro = new(_logger, _profileService, _surfaceService);
+ IntroAnimation intro = new(_logger, _profileService, _rgbService.EnabledDevices);
// Draw a white overlay over the device
void DrawOverlay(object? sender, FrameRenderingEventArgs args)
@@ -236,7 +230,7 @@ namespace Artemis.Core.Services
if (!ModuleRenderingDisabled)
// While non-activated modules may be updated above if they expand the main data model, they may never render
foreach (Module module in modules.Where(m => m.IsActivated))
- module.InternalRender(args.DeltaTime, _surfaceService.ActiveSurface, canvas, _rgbService.BitmapBrush.Bitmap.Info);
+ module.InternalRender(args.DeltaTime, canvas, _rgbService.BitmapBrush.Bitmap.Info);
OnFrameRendering(new FrameRenderingEventArgs(canvas, args.DeltaTime, _rgbService.Surface));
}
diff --git a/src/Artemis.Core/Services/DeviceService.cs b/src/Artemis.Core/Services/DeviceService.cs
index 48c64a50f..032c0db53 100644
--- a/src/Artemis.Core/Services/DeviceService.cs
+++ b/src/Artemis.Core/Services/DeviceService.cs
@@ -8,6 +8,13 @@ namespace Artemis.Core.Services
{
internal class DeviceService : IDeviceService
{
+ private readonly IRgbService _rgbService;
+
+ public DeviceService(IRgbService rgbService)
+ {
+ _rgbService = rgbService;
+ }
+
public void IdentifyDevice(ArtemisDevice device)
{
BlinkDevice(device, 0);
@@ -16,9 +23,9 @@ namespace Artemis.Core.Services
private void BlinkDevice(ArtemisDevice device, int blinkCount)
{
// Create a LED group way at the top
- ListLedGroup ledGroup = new(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)),
+ Brush = new SolidColorBrush(new Color(255, 255, 255)),
ZIndex = 999
};
@@ -26,7 +33,7 @@ namespace Artemis.Core.Services
Task.Run(async () =>
{
await Task.Delay(200);
- ledGroup.Detach();
+ ledGroup.Detach(_rgbService.Surface);
if (blinkCount < 5)
{
diff --git a/src/Artemis.Core/Services/Input/InputService.cs b/src/Artemis.Core/Services/Input/InputService.cs
index dff8d7d91..6ca8aa735 100644
--- a/src/Artemis.Core/Services/Input/InputService.cs
+++ b/src/Artemis.Core/Services/Input/InputService.cs
@@ -9,15 +9,13 @@ namespace Artemis.Core.Services
internal class InputService : IInputService
{
private readonly ILogger _logger;
- private readonly ISurfaceService _surfaceService;
+ private readonly IRgbService _rgbService;
- public InputService(ILogger logger, ISurfaceService surfaceService)
+ public InputService(ILogger logger, IRgbService rgbService)
{
_logger = logger;
- _surfaceService = surfaceService;
+ _rgbService = rgbService;
- _surfaceService.ActiveSurfaceConfigurationSelected += SurfaceConfigurationChanged;
- _surfaceService.SurfaceConfigurationUpdated += SurfaceConfigurationChanged;
BustIdentifierCache();
}
@@ -91,7 +89,7 @@ namespace Artemis.Core.Services
_logger.Debug("Stop identifying device {device}", _identifyingDevice);
_identifyingDevice = null;
- _surfaceService.UpdateSurfaceConfiguration(_surfaceService.ActiveSurface, true);
+ _rgbService.SaveDevices();
BustIdentifierCache();
}
@@ -123,7 +121,7 @@ namespace Artemis.Core.Services
{
if (_cachedFallbackKeyboard != null)
return _cachedFallbackKeyboard;
- _cachedFallbackKeyboard = _surfaceService.ActiveSurface.Devices.FirstOrDefault(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard);
+ _cachedFallbackKeyboard = _rgbService.EnabledDevices.FirstOrDefault(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard);
return _cachedFallbackKeyboard;
}
@@ -131,7 +129,7 @@ namespace Artemis.Core.Services
{
if (_cachedFallbackMouse != null)
return _cachedFallbackMouse;
- _cachedFallbackMouse = _surfaceService.ActiveSurface.Devices.FirstOrDefault(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse);
+ _cachedFallbackMouse = _rgbService.EnabledDevices.FirstOrDefault(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse);
return _cachedFallbackMouse;
}
@@ -144,7 +142,7 @@ namespace Artemis.Core.Services
_cachedFallbackKeyboard = null;
_cachedFallbackMouse = null;
- _devices = _surfaceService.ActiveSurface.Devices.Where(d => d.InputIdentifiers.Any()).ToList();
+ _devices = _rgbService.EnabledDevices.Where(d => d.InputIdentifiers.Any()).ToList();
}
private void AddDeviceToCache(ArtemisDevice match, InputProvider provider, object identifier)
diff --git a/src/Artemis.Core/Services/Interfaces/IRgbService.cs b/src/Artemis.Core/Services/Interfaces/IRgbService.cs
index 6e50576fa..838f15ff8 100644
--- a/src/Artemis.Core/Services/Interfaces/IRgbService.cs
+++ b/src/Artemis.Core/Services/Interfaces/IRgbService.cs
@@ -9,6 +9,22 @@ namespace Artemis.Core.Services
///
public interface IRgbService : IArtemisService, IDisposable
{
+ ///
+ /// Gets a read-only collection containing all enabled devices
+ ///
+ IReadOnlyCollection EnabledDevices { get; }
+
+ ///
+ /// Gets a read-only collection containing all registered devices
+ ///
+ IReadOnlyCollection Devices { get; }
+
+ ///
+ /// Gets a dictionary containing all s on the surface with their corresponding RGB.NET
+ /// as key
+ ///
+ IReadOnlyDictionary LedMap { get; }
+
///
/// Gets or sets the RGB surface rendering is performed on
///
@@ -19,16 +35,6 @@ namespace Artemis.Core.Services
///
BitmapBrush? BitmapBrush { get; }
- ///
- /// Gets the scale the frames are rendered on, a scale of 1.0 means 1 pixel = 1mm
- ///
- double RenderScale { get; }
-
- ///
- /// Gets all loaded RGB devices
- ///
- IReadOnlyCollection LoadedDevices { get; }
-
///
/// Gets the update trigger that drives the render loop
///
@@ -46,19 +52,77 @@ namespace Artemis.Core.Services
void AddDeviceProvider(IRGBDeviceProvider deviceProvider);
///
- /// Occurs when a single device has loaded
+ /// Removes the given device provider from the
///
- event EventHandler DeviceLoaded;
+ ///
+ void RemoveDeviceProvider(IRGBDeviceProvider deviceProvider);
///
- /// Occurs when a single device has reloaded
+ /// Applies auto-arranging logic to the surface
///
- event EventHandler DeviceReloaded;
+ void AutoArrangeDevices();
///
- /// Recalculates the LED group used by the
+ /// Applies the best available layout for the given
///
- ///
- void UpdateSurfaceLedGroup(ArtemisSurface artemisSurface);
+ /// The device to apply the best available layout to
+ /// The layout that was applied to the device
+ ArtemisLayout ApplyBestDeviceLayout(ArtemisDevice device);
+
+ ///
+ /// Apples the provided to the provided
+ ///
+ ///
+ ///
+ ///
+ ///
+ void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds);
+
+ ///
+ /// Attempts to retrieve the that corresponds the provided RGB.NET
+ ///
+ ///
+ ///
+ /// The RGB.NET to find the corresponding
+ /// for
+ ///
+ /// If found, the corresponding ; otherwise .
+ ArtemisDevice? GetDevice(IRGBDevice rgbDevice);
+
+ ///
+ /// Attempts to retrieve the that corresponds the provided RGB.NET
+ ///
+ /// The RGB.NET to find the corresponding for
+ /// If found, the corresponding ; otherwise .
+ ArtemisLed? GetLed(Led led);
+
+ ///
+ /// Saves the configuration of the provided device to persistent storage
+ ///
+ ///
+ void SaveDevice(ArtemisDevice artemisDevice);
+
+ ///
+ /// Saves the configuration of all current devices to persistent storage
+ ///
+ void SaveDevices();
+
+ void EnableDevice(ArtemisDevice device);
+ void DisableDevice(ArtemisDevice device);
+
+ ///
+ /// Occurs when a single device was added
+ ///
+ event EventHandler DeviceAdded;
+
+ ///
+ /// Occurs when a single device was removed
+ ///
+ event EventHandler DeviceRemoved;
+
+ ///
+ /// Occurs when the surface has had modifications to its LED collection
+ ///
+ event EventHandler LedsChanged;
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs
index b67c27470..31dc0070c 100644
--- a/src/Artemis.Core/Services/PluginManagementService.cs
+++ b/src/Artemis.Core/Services/PluginManagementService.cs
@@ -9,7 +9,6 @@ using Artemis.Core.DeviceProviders;
using Artemis.Core.Ninject;
using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces;
-using Humanizer;
using McMaster.NETCore.Plugins;
using Ninject;
using Ninject.Extensions.ChildKernel;
@@ -147,7 +146,7 @@ namespace Artemis.Core.Services
// TODO: move to a more appropriate service
public DeviceProvider GetDeviceProviderByDevice(IRGBDevice rgbDevice)
{
- return GetFeaturesOfType().First(d => d.RgbDeviceProvider.Devices != null && d.RgbDeviceProvider.Devices.Contains(rgbDevice));
+ return GetFeaturesOfType().First(d => d.RgbDeviceProvider.Devices.Contains(rgbDevice));
}
public Plugin? GetCallingPlugin()
@@ -186,7 +185,6 @@ namespace Artemis.Core.Services
// Load the plugin assemblies into the plugin context
DirectoryInfo pluginDirectory = new(Path.Combine(Constants.DataFolder, "plugins"));
foreach (DirectoryInfo subDirectory in pluginDirectory.EnumerateDirectories())
- {
try
{
LoadPlugin(subDirectory);
@@ -195,7 +193,6 @@ namespace Artemis.Core.Services
{
_logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception");
}
- }
// ReSharper disable InconsistentlySynchronizedField - It's read-only, idc
_logger.Debug("Loaded {count} plugin(s)", _plugins.Count);
@@ -338,7 +335,6 @@ namespace Artemis.Core.Services
// Create instances of each feature and add them to the plugin
// Construction should be simple and not contain any logic so failure at this point means the entire plugin fails
foreach (Type featureType in featureTypes)
- {
try
{
plugin.Kernel.Bind(featureType).ToSelf().InSingletonScope();
@@ -358,13 +354,11 @@ namespace Artemis.Core.Services
}
catch (Exception e)
{
- throw new ArtemisPluginException(plugin, "Failed to instantiate feature", e);
+ _logger.Warning(new ArtemisPluginException(plugin, "Failed to instantiate feature", e), "Failed to instantiate feature", plugin);
}
- }
// Activate plugins after they are all loaded
foreach (PluginFeature pluginFeature in plugin.Features.Where(i => i.Entity.IsEnabled))
- {
try
{
EnablePluginFeature(pluginFeature, false, !ignorePluginLock);
@@ -373,7 +367,6 @@ namespace Artemis.Core.Services
{
// ignored, logged in EnablePluginFeature
}
- }
if (saveState)
{
@@ -474,13 +467,11 @@ namespace Artemis.Core.Services
Directory.CreateDirectory(directoryInfo.FullName);
string metaDataDirectory = metaDataFileEntry.FullName.Replace(metaDataFileEntry.Name, "");
foreach (ZipArchiveEntry zipArchiveEntry in archive.Entries)
- {
if (zipArchiveEntry.FullName.StartsWith(metaDataDirectory))
{
string target = Path.Combine(directoryInfo.FullName, zipArchiveEntry.FullName.Remove(0, metaDataDirectory.Length));
zipArchiveEntry.ExtractToFile(target);
}
- }
// Load the newly extracted plugin and return the result
return LoadPlugin(directoryInfo);
@@ -563,10 +554,8 @@ namespace Artemis.Core.Services
private void SavePlugin(Plugin plugin)
{
foreach (PluginFeature pluginFeature in plugin.Features)
- {
if (plugin.Entity.Features.All(i => i.Type != pluginFeature.GetType().FullName))
plugin.Entity.Features.Add(pluginFeature.Entity);
- }
_pluginRepository.SavePlugin(plugin.Entity);
}
diff --git a/src/Artemis.Core/Services/RgbService.cs b/src/Artemis.Core/Services/RgbService.cs
index 6d7492100..3cec517d6 100644
--- a/src/Artemis.Core/Services/RgbService.cs
+++ b/src/Artemis.Core/Services/RgbService.cs
@@ -1,6 +1,11 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
+using Artemis.Core.DeviceProviders;
+using Artemis.Core.Services.Models;
+using Artemis.Storage.Entities.Surface;
+using Artemis.Storage.Repositories.Interfaces;
using RGB.NET.Core;
using RGB.NET.Groups;
using Serilog;
@@ -12,72 +17,159 @@ namespace Artemis.Core.Services
///
internal class RgbService : IRgbService
{
- private readonly List _loadedDevices;
- private readonly ILogger _logger;
- private readonly PluginSetting _renderScaleSetting;
- private readonly PluginSetting _sampleSizeSetting;
- private readonly PluginSetting _targetFrameRateSetting;
- private ListLedGroup? _surfaceLedGroup;
+ private readonly List _devices;
+ private readonly List _enabledDevices;
+ private Dictionary _ledMap;
- public RgbService(ILogger logger, ISettingsService settingsService)
+ private readonly ILogger _logger;
+ private readonly IPluginManagementService _pluginManagementService;
+ private readonly IDeviceRepository _deviceRepository;
+ private readonly PluginSetting _renderScaleSetting;
+ private readonly PluginSetting _targetFrameRateSetting;
+ private readonly PluginSetting _sampleSizeSetting;
+ private ListLedGroup? _surfaceLedGroup;
+ private bool _modifyingProviders;
+
+ public RgbService(ILogger logger, ISettingsService settingsService, IPluginManagementService pluginManagementService, IDeviceRepository deviceRepository)
{
_logger = logger;
+ _pluginManagementService = pluginManagementService;
+ _deviceRepository = deviceRepository;
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.5);
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 25);
_sampleSizeSetting = settingsService.GetSetting("Core.SampleSize", 1);
- Surface = RGBSurface.Instance;
+ Surface = new RGBSurface();
// Let's throw these for now
Surface.Exception += SurfaceOnException;
_renderScaleSetting.SettingChanged += RenderScaleSettingOnSettingChanged;
_targetFrameRateSetting.SettingChanged += TargetFrameRateSettingOnSettingChanged;
- _loadedDevices = new List();
+ _enabledDevices = new List();
+ _devices = new List();
+ _ledMap = new Dictionary();
+
UpdateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / _targetFrameRateSetting.Value};
Surface.RegisterUpdateTrigger(UpdateTrigger);
}
+ public IReadOnlyCollection EnabledDevices => _enabledDevices.AsReadOnly();
+ public IReadOnlyCollection Devices => _devices.AsReadOnly();
+ public IReadOnlyDictionary LedMap => new ReadOnlyDictionary(_ledMap);
+
///
public RGBSurface Surface { get; set; }
public TimerUpdateTrigger UpdateTrigger { get; }
public BitmapBrush? BitmapBrush { get; private set; }
- public IReadOnlyCollection LoadedDevices => _loadedDevices.AsReadOnly();
- public double RenderScale => _renderScaleSetting.Value;
+
public bool IsRenderPaused { get; set; }
public void AddDeviceProvider(IRGBDeviceProvider deviceProvider)
{
- try
+ lock (_devices)
{
- Surface.LoadDevices(deviceProvider, RGBDeviceType.All, false, true);
- }
- catch (Exception e)
- {
- _logger.Error(e, "Exception during device loading for device provider {deviceProvider}", deviceProvider.GetType().Name);
- throw;
- }
-
- if (deviceProvider.Devices == null || !deviceProvider.Devices.Any())
- {
- _logger.Warning("Device provider {deviceProvider} has no devices", deviceProvider.GetType().Name);
- return;
- }
-
- foreach (IRGBDevice surfaceDevice in deviceProvider.Devices)
- {
- _logger.Debug("Device provider {deviceProvider} added {deviceName}",
- deviceProvider.GetType().Name, surfaceDevice.DeviceInfo?.DeviceName);
- if (!_loadedDevices.Contains(surfaceDevice))
+ try
{
- _loadedDevices.Add(surfaceDevice);
- OnDeviceLoaded(new DeviceEventArgs(surfaceDevice));
+ _modifyingProviders = true;
+
+ List toRemove = _devices.Where(a => deviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList();
+ Surface.Detach(deviceProvider.Devices);
+ foreach (ArtemisDevice device in toRemove)
+ RemoveDevice(device);
+
+ deviceProvider.Initialize(RGBDeviceType.All, true);
+ Surface.Attach(deviceProvider.Devices);
+
+ if (!deviceProvider.Devices.Any())
+ {
+ _logger.Warning("Device provider {deviceProvider} has no devices", deviceProvider.GetType().Name);
+ return;
+ }
+
+ foreach (IRGBDevice rgbDevice in deviceProvider.Devices)
+ {
+ ArtemisDevice artemisDevice = GetArtemisDevice(rgbDevice);
+ AddDevice(artemisDevice);
+ _logger.Debug("Device provider {deviceProvider} added {deviceName}", deviceProvider.GetType().Name, rgbDevice.DeviceInfo?.DeviceName);
+ }
+
+ _devices.Sort((a, b) => a.ZIndex - b.ZIndex);
+ }
+ catch (Exception e)
+ {
+ _logger.Error(e, "Exception during device loading for device provider {deviceProvider}", deviceProvider.GetType().Name);
+ throw;
+ }
+ finally
+ {
+ _modifyingProviders = false;
+ UpdateBitmapBrush();
}
- else
- OnDeviceReloaded(new DeviceEventArgs(surfaceDevice));
}
}
+ public void RemoveDeviceProvider(IRGBDeviceProvider deviceProvider)
+ {
+ lock (_devices)
+ {
+ try
+ {
+ _modifyingProviders = true;
+
+ List toRemove = _devices.Where(a => deviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList();
+ Surface.Detach(deviceProvider.Devices);
+ foreach (ArtemisDevice device in toRemove)
+ RemoveDevice(device);
+
+ _devices.Sort((a, b) => a.ZIndex - b.ZIndex);
+ }
+ catch (Exception e)
+ {
+ _logger.Error(e, "Exception during device removal for device provider {deviceProvider}", deviceProvider.GetType().Name);
+ throw;
+ }
+ finally
+ {
+ _modifyingProviders = false;
+ UpdateBitmapBrush();
+ }
+ }
+ }
+
+ private void UpdateBitmapBrush()
+ {
+ lock (_devices)
+ {
+ if (_modifyingProviders)
+ return;
+
+ _ledMap = new Dictionary(_devices.SelectMany(d => d.Leds).ToDictionary(l => l.RgbLed));
+
+ if (_surfaceLedGroup == null || BitmapBrush == null)
+ {
+ // Apply the application wide brush and decorator
+ BitmapBrush = new BitmapBrush(new Scale(_renderScaleSetting.Value), _sampleSizeSetting, this);
+ _surfaceLedGroup = new ListLedGroup(Surface, LedMap.Select(l => l.Key)) {Brush = BitmapBrush};
+ OnLedsChanged();
+ return;
+ }
+
+ lock (_surfaceLedGroup)
+ {
+ // Clean up the old background
+ _surfaceLedGroup.Detach(Surface);
+
+ // Apply the application wide brush and decorator
+ BitmapBrush.Scale = new Scale(_renderScaleSetting.Value);
+ _surfaceLedGroup = new ListLedGroup(Surface, LedMap.Select(l => l.Key)) {Brush = BitmapBrush};
+ OnLedsChanged();
+ }
+ }
+ }
+
+ #region IDisposable
+
public void Dispose()
{
Surface.UnregisterUpdateTrigger(UpdateTrigger);
@@ -86,10 +178,181 @@ namespace Artemis.Core.Services
Surface.Dispose();
}
+ #endregion
+
+ #region EnabledDevices
+
+ public void AutoArrangeDevices()
+ {
+ SurfaceArrangement surfaceArrangement = SurfaceArrangement.GetDefaultArrangement();
+ surfaceArrangement.Arrange(_devices);
+ SaveDevices();
+ }
+
+ public ArtemisLayout ApplyBestDeviceLayout(ArtemisDevice device)
+ {
+ 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, true, true);
+ return layout;
+ }
+ }
+
+ // Look for a layout provided by the user
+ layout = device.DeviceProvider.LoadUserLayout(device);
+ if (layout.IsValid)
+ {
+ ApplyDeviceLayout(device, layout, true, true);
+ return layout;
+ }
+
+ // Look for a layout provided by the plugin
+ layout = device.DeviceProvider.LoadLayout(device);
+ if (layout.IsValid)
+ {
+ ApplyDeviceLayout(device, layout, true, true);
+ return layout;
+ }
+
+ // Finally fall back to a default layout
+ layout = LoadDefaultLayout(device);
+ ApplyDeviceLayout(device, layout, true, true);
+ return layout;
+ }
+
+ private ArtemisLayout LoadDefaultLayout(ArtemisDevice device)
+ {
+ return new ArtemisLayout("NYI", LayoutSource.Default);
+ }
+
+ public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds)
+ {
+ device.ApplyLayout(layout, createMissingLeds, removeExessiveLeds);
+ // Applying layouts can affect LEDs, update LED group
+ UpdateBitmapBrush();
+ }
+
+ public ArtemisDevice? GetDevice(IRGBDevice rgbDevice)
+ {
+ return _devices.FirstOrDefault(d => d.RgbDevice == rgbDevice);
+ }
+
+ public ArtemisLed? GetLed(Led led)
+ {
+ LedMap.TryGetValue(led, out ArtemisLed? artemisLed);
+ return artemisLed;
+ }
+
+ public void EnableDevice(ArtemisDevice device)
+ {
+ if (device.IsEnabled)
+ return;
+
+ _enabledDevices.Add(device);
+ device.IsEnabled = true;
+ device.ApplyToEntity();
+ _deviceRepository.Save(device.DeviceEntity);
+
+ UpdateBitmapBrush();
+ OnDeviceAdded(new DeviceEventArgs(device));
+ }
+
+ public void DisableDevice(ArtemisDevice device)
+ {
+ if (!device.IsEnabled)
+ return;
+
+ _enabledDevices.Remove(device);
+ device.IsEnabled = false;
+ device.ApplyToEntity();
+ _deviceRepository.Save(device.DeviceEntity);
+
+ UpdateBitmapBrush();
+ OnDeviceRemoved(new DeviceEventArgs(device));
+ }
+
+ private void AddDevice(ArtemisDevice device)
+ {
+ if (_devices.Any(d => d.RgbDevice == device.RgbDevice))
+ throw new ArtemisCoreException("Attempted to add a duplicate device to the RGB Service");
+
+ device.ApplyToRgbDevice();
+ _devices.Add(device);
+ if (device.IsEnabled)
+ _enabledDevices.Add(device);
+
+ // Will call UpdateBitmapBrush()
+ ApplyBestDeviceLayout(device);
+ OnDeviceAdded(new DeviceEventArgs(device));
+ }
+
+ private void RemoveDevice(ArtemisDevice device)
+ {
+ _devices.Remove(device);
+ if (device.IsEnabled)
+ _enabledDevices.Remove(device);
+
+ UpdateBitmapBrush();
+ OnDeviceRemoved(new DeviceEventArgs(device));
+ }
+
+ #endregion
+
+ #region Storage
+
+ private ArtemisDevice GetArtemisDevice(IRGBDevice rgbDevice)
+ {
+ string deviceIdentifier = rgbDevice.GetDeviceIdentifier();
+ DeviceEntity? deviceEntity = _deviceRepository.Get(deviceIdentifier);
+ DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
+
+ if (deviceEntity != null)
+ return new ArtemisDevice(rgbDevice, deviceProvider, deviceEntity);
+
+ // Fall back on creating a new device
+ _logger.Information(
+ "No device config found for {deviceInfo}, device hash: {deviceHashCode}. Adding a new entry.",
+ rgbDevice.DeviceInfo,
+ deviceIdentifier
+ );
+ return new ArtemisDevice(rgbDevice, deviceProvider);
+ }
+
+ public void SaveDevice(ArtemisDevice artemisDevice)
+ {
+ artemisDevice.ApplyToEntity();
+ artemisDevice.ApplyToRgbDevice();
+
+ _deviceRepository.Save(artemisDevice.DeviceEntity);
+ OnLedsChanged();
+ }
+
+ public void SaveDevices()
+ {
+ foreach (ArtemisDevice artemisDevice in _devices)
+ {
+ artemisDevice.ApplyToEntity();
+ artemisDevice.ApplyToRgbDevice();
+ }
+
+ _deviceRepository.Save(_devices.Select(d => d.DeviceEntity));
+ OnLedsChanged();
+ }
+
+ #endregion
+
+ #region Event handlers
+
private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
{
// The surface hasn't changed so we can safely reuse it
- UpdateSurfaceLedGroup(BitmapBrush?.Surface);
+ UpdateBitmapBrush();
}
private void TargetFrameRateSettingOnSettingChanged(object? sender, EventArgs e)
@@ -103,44 +366,27 @@ namespace Artemis.Core.Services
throw args.Exception;
}
+ #endregion
+
#region Events
- public event EventHandler? DeviceLoaded;
- public event EventHandler? DeviceReloaded;
+ public event EventHandler? DeviceAdded;
+ public event EventHandler? DeviceRemoved;
+ public event EventHandler? LedsChanged;
- public void UpdateSurfaceLedGroup(ArtemisSurface? artemisSurface)
+ private void OnDeviceAdded(DeviceEventArgs e)
{
- if (artemisSurface == null)
- return;
-
- if (_surfaceLedGroup == null || BitmapBrush == null)
- {
- // Apply the application wide brush and decorator
- BitmapBrush = new BitmapBrush(new Scale(_renderScaleSetting.Value), _sampleSizeSetting);
- _surfaceLedGroup = new ListLedGroup(artemisSurface.LedMap.Select(l => l.Key)) {Brush = BitmapBrush};
- return;
- }
-
- lock (_surfaceLedGroup)
- {
- // Clean up the old background
- _surfaceLedGroup.Detach();
-
- // Apply the application wide brush and decorator
- BitmapBrush.Scale = new Scale(_renderScaleSetting.Value);
- BitmapBrush.Surface = artemisSurface;
- _surfaceLedGroup = new ListLedGroup(artemisSurface.LedMap.Select(l => l.Key)) {Brush = BitmapBrush};
- }
+ DeviceAdded?.Invoke(this, e);
}
- private void OnDeviceLoaded(DeviceEventArgs e)
+ protected virtual void OnDeviceRemoved(DeviceEventArgs e)
{
- DeviceLoaded?.Invoke(this, e);
+ DeviceRemoved?.Invoke(this, e);
}
- private void OnDeviceReloaded(DeviceEventArgs e)
+ protected virtual void OnLedsChanged()
{
- DeviceReloaded?.Invoke(this, e);
+ LedsChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
diff --git a/src/Artemis.Core/Services/Storage/Interfaces/ISurfaceService.cs b/src/Artemis.Core/Services/Storage/Interfaces/ISurfaceService.cs
deleted file mode 100644
index 630a65220..000000000
--- a/src/Artemis.Core/Services/Storage/Interfaces/ISurfaceService.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System;
-using System.Collections.ObjectModel;
-
-namespace Artemis.Core.Services
-{
- ///
- /// Provides access to the device surface and its configuration
- ///
- public interface ISurfaceService : IArtemisService
- {
- ///
- /// Gets the currently active surface entity, to change config use
- ///
- ArtemisSurface ActiveSurface { get; }
-
- ///
- /// Gets a read-only list of all surface configurations
- ///
- ReadOnlyCollection SurfaceConfigurations { get; }
-
- ///
- /// Creates a new surface entity with the supplied name
- ///
- /// The name for the new surface entity
- ///
- ArtemisSurface CreateSurfaceConfiguration(string name);
-
- ///
- /// Sets the provided entity as active and applies it to the surface
- ///
- /// The entity to activate and apply
- void SetActiveSurfaceConfiguration(ArtemisSurface surface);
-
- ///
- /// Saves the provided surface entity to permanent storage and if config is active, applies it to the surface
- ///
- /// The entity to save (and apply if active)
- /// Whether to also save devices. If false, devices changes won't be applied either
- void UpdateSurfaceConfiguration(ArtemisSurface surface, bool includeDevices);
-
- ///
- /// Deletes the supplied surface entity, surface entity may not be the active surface entity
- ///
- /// The surface entity to delete, may not be the active surface entity
- void DeleteSurfaceConfiguration(ArtemisSurface surface);
-
- ///
- /// Applies auto-arranging logic to a surface
- ///
- ///
- /// The surface to apply auto-arrange to. If the
- /// is used.
- ///
- void AutoArrange(ArtemisSurface? artemisSurface = null);
-
- ///
- /// Occurs when the active device entity has been changed
- ///
- event EventHandler ActiveSurfaceConfigurationSelected;
-
- ///
- /// Occurs when a surface configuration has been updated
- ///
- event EventHandler SurfaceConfigurationUpdated;
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangement.cs b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangement.cs
index a2bfd0b49..ca0d1ce60 100644
--- a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangement.cs
+++ b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangement.cs
@@ -76,10 +76,10 @@ namespace Artemis.Core.Services.Models
return surfaceArrangementType;
}
- public void Arrange(ArtemisSurface surface)
+ public void Arrange(List devices)
{
ArrangedDevices.Clear();
- foreach (ArtemisDevice surfaceDevice in surface.Devices)
+ foreach (ArtemisDevice surfaceDevice in devices)
{
surfaceDevice.X = 0;
surfaceDevice.Y = 0;
@@ -87,14 +87,14 @@ namespace Artemis.Core.Services.Models
}
foreach (SurfaceArrangementType surfaceArrangementType in Types)
- surfaceArrangementType.Arrange(surface);
+ surfaceArrangementType.Arrange(devices);
// See if we need to move the surface to keep X and Y values positive
- double x = surface.Devices.Min(d => d.RgbDevice.Location.X);
- double y = surface.Devices.Min(d => d.RgbDevice.Location.Y);
+ double x = devices.Min(d => d.RgbDevice.Location.X);
+ double y = devices.Min(d => d.RgbDevice.Location.Y);
if (x < 0)
{
- foreach (ArtemisDevice surfaceDevice in surface.Devices)
+ foreach (ArtemisDevice surfaceDevice in devices)
{
surfaceDevice.X += x * -1;
surfaceDevice.ApplyToRgbDevice();
@@ -103,7 +103,7 @@ namespace Artemis.Core.Services.Models
if (y < 0)
{
- foreach (ArtemisDevice surfaceDevice in surface.Devices)
+ foreach (ArtemisDevice surfaceDevice in devices)
{
surfaceDevice.Y += y * -1;
surfaceDevice.ApplyToRgbDevice();
diff --git a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementConfiguration.cs b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementConfiguration.cs
index fb346fa21..a3ada512d 100644
--- a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementConfiguration.cs
+++ b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementConfiguration.cs
@@ -32,14 +32,14 @@ namespace Artemis.Core.Services.Models
public int MarginBottom { get; }
public SurfaceArrangement SurfaceArrangement { get; set; }
- public bool Apply(List devices, ArtemisSurface surface)
+ public bool Apply(List devices)
{
- if (Anchor != null && !Anchor.HasDevices(surface))
+ if (Anchor != null && !Anchor.HasDevices(devices))
return false;
// Start at the edge of the anchor, if there is no anchor start at any device
- Point startPoint = Anchor?.GetEdge(HorizontalPosition, VerticalPosition, surface) ??
- new SurfaceArrangementType(SurfaceArrangement, RGBDeviceType.All, 1).GetEdge(HorizontalPosition, VerticalPosition, surface);
+ Point startPoint = Anchor?.GetEdge(HorizontalPosition, VerticalPosition) ??
+ new SurfaceArrangementType(SurfaceArrangement, RGBDeviceType.All, 1).GetEdge(HorizontalPosition, VerticalPosition);
// Stack multiple devices of the same type vertically if they are wider than they are tall
bool stackVertically = devices.Average(d => d.RgbDevice.Size.Width) >= devices.Average(d => d.RgbDevice.Size.Height);
diff --git a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementType.cs b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementType.cs
index 2166f34df..6a2c4f185 100644
--- a/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementType.cs
+++ b/src/Artemis.Core/Services/Storage/Models/SurfaceArrangementType.cs
@@ -21,21 +21,21 @@ namespace Artemis.Core.Services.Models
public List Configurations { get; }
public SurfaceArrangementConfiguration? AppliedConfiguration { get; set; }
- public bool HasDevices(ArtemisSurface surface)
+ public bool HasDevices(List devices)
{
- return surface.Devices.Any(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType);
+ return devices.Any(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType);
}
- public void Arrange(ArtemisSurface surface)
+ public void Arrange(List devices)
{
- List devices = surface.Devices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType).ToList();
+ devices = devices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType).ToList();
if (!devices.Any())
return;
AppliedConfiguration = null;
foreach (SurfaceArrangementConfiguration configuration in Configurations)
{
- bool applied = configuration.Apply(devices, surface);
+ bool applied = configuration.Apply(devices);
if (applied)
{
AppliedConfiguration = configuration;
@@ -52,11 +52,11 @@ namespace Artemis.Core.Services.Models
VerticalArrangementPosition.Equal,
10
) {SurfaceArrangement = SurfaceArrangement};
- fallback.Apply(devices, surface);
+ fallback.Apply(devices);
AppliedConfiguration = fallback;
}
- public Point GetEdge(HorizontalArrangementPosition horizontalPosition, VerticalArrangementPosition verticalPosition, ArtemisSurface surface)
+ public Point GetEdge(HorizontalArrangementPosition horizontalPosition, VerticalArrangementPosition verticalPosition)
{
List devices = SurfaceArrangement.ArrangedDevices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType || DeviceType == RGBDeviceType.All).ToList();
if (!devices.Any())
@@ -66,7 +66,7 @@ namespace Artemis.Core.Services.Models
{
HorizontalArrangementPosition.Left => devices.Min(d => d.RgbDevice.Location.X) - (AppliedConfiguration?.MarginLeft ?? 0.0),
HorizontalArrangementPosition.Right => devices.Max(d => d.RgbDevice.Location.X + d.RgbDevice.Size.Width) + (AppliedConfiguration?.MarginRight ?? 0.0),
- HorizontalArrangementPosition.Center => devices.First().RgbDevice.DeviceRectangle.Center.X,
+ HorizontalArrangementPosition.Center => devices.First().RgbDevice.Boundary.Center.X,
HorizontalArrangementPosition.Equal => devices.First().RgbDevice.Location.X,
_ => throw new ArgumentOutOfRangeException(nameof(horizontalPosition), horizontalPosition, null)
};
@@ -74,7 +74,7 @@ namespace Artemis.Core.Services.Models
{
VerticalArrangementPosition.Top => devices.Min(d => d.RgbDevice.Location.Y) - (AppliedConfiguration?.MarginTop ?? 0.0),
VerticalArrangementPosition.Bottom => devices.Max(d => d.RgbDevice.Location.Y + d.RgbDevice.Size.Height) + (AppliedConfiguration?.MarginBottom ?? 0.0),
- VerticalArrangementPosition.Center => devices.First().RgbDevice.DeviceRectangle.Center.Y,
+ VerticalArrangementPosition.Center => devices.First().RgbDevice.Boundary.Center.Y,
VerticalArrangementPosition.Equal => devices.First().RgbDevice.Location.Y,
_ => throw new ArgumentOutOfRangeException(nameof(verticalPosition), verticalPosition, null)
};
diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs
index 1e5b825e9..e69ca7f4f 100644
--- a/src/Artemis.Core/Services/Storage/ProfileService.cs
+++ b/src/Artemis.Core/Services/Storage/ProfileService.cs
@@ -14,23 +14,22 @@ namespace Artemis.Core.Services
{
private readonly ILogger _logger;
private readonly IPluginManagementService _pluginManagementService;
+ private readonly IRgbService _rgbService;
private readonly IProfileRepository _profileRepository;
- private readonly ISurfaceService _surfaceService;
public ProfileService(ILogger logger,
IPluginManagementService pluginManagementService,
- ISurfaceService surfaceService,
+ IRgbService rgbService,
IConditionOperatorService conditionOperatorService,
IDataBindingService dataBindingService,
IProfileRepository profileRepository)
{
_logger = logger;
_pluginManagementService = pluginManagementService;
- _surfaceService = surfaceService;
+ _rgbService = rgbService;
_profileRepository = profileRepository;
- _surfaceService.ActiveSurfaceConfigurationSelected += OnActiveSurfaceConfigurationSelected;
- _surfaceService.SurfaceConfigurationUpdated += OnSurfaceConfigurationUpdated;
+ _rgbService.LedsChanged += RgbServiceOnLedsChanged;
}
public JsonSerializerSettings MementoSettings { get; set; } = new() {TypeNameHandling = TypeNameHandling.All};
@@ -62,12 +61,11 @@ namespace Artemis.Core.Services
///
/// Populates all missing LEDs on all currently active profiles
///
- ///
- private void ActiveProfilesPopulateLeds(ArtemisSurface surface)
+ private void ActiveProfilesPopulateLeds()
{
List profileModules = _pluginManagementService.GetFeaturesOfType();
foreach (ProfileModule profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList())
- profileModule.ActiveProfile?.PopulateLeds(surface); // Avoid race condition
+ profileModule.ActiveProfile?.PopulateLeds(_rgbService.EnabledDevices); // Avoid race condition
}
public List GetProfileDescriptors(ProfileModule module)
@@ -110,7 +108,7 @@ namespace Artemis.Core.Services
Profile profile = new(profileDescriptor.ProfileModule, profileEntity);
InstantiateProfile(profile);
- profileDescriptor.ProfileModule.ChangeActiveProfile(profile, _surfaceService.ActiveSurface);
+ profileDescriptor.ProfileModule.ChangeActiveProfile(profile, _rgbService.EnabledDevices);
SaveActiveProfile(profileDescriptor.ProfileModule);
return profile;
@@ -124,9 +122,9 @@ namespace Artemis.Core.Services
ProfileEntity entity = _profileRepository.Get(module.ActiveProfile.EntityId);
Profile profile = new(module, entity);
InstantiateProfile(profile);
-
- module.ChangeActiveProfile(null, _surfaceService.ActiveSurface);
- module.ChangeActiveProfile(profile, _surfaceService.ActiveSurface);
+
+ module.ChangeActiveProfile(null, _rgbService.EnabledDevices);
+ module.ChangeActiveProfile(profile, _rgbService.EnabledDevices);
}
public async Task ActivateProfileAnimated(ProfileDescriptor profileDescriptor)
@@ -141,9 +139,9 @@ namespace Artemis.Core.Services
Profile profile = new(profileDescriptor.ProfileModule, profileEntity);
InstantiateProfile(profile);
- void ActivatingProfileSurfaceUpdate(object? sender, SurfaceConfigurationEventArgs e)
+ void ActivatingRgbServiceOnLedsChanged(object? sender, EventArgs e)
{
- profile.PopulateLeds(e.Surface);
+ profile.PopulateLeds(_rgbService.EnabledDevices);
}
void ActivatingProfilePluginToggle(object? sender, PluginEventArgs e)
@@ -154,28 +152,29 @@ namespace Artemis.Core.Services
// This could happen during activation so subscribe to it
_pluginManagementService.PluginEnabled += ActivatingProfilePluginToggle;
_pluginManagementService.PluginDisabled += ActivatingProfilePluginToggle;
- _surfaceService.SurfaceConfigurationUpdated += ActivatingProfileSurfaceUpdate;
+ _rgbService.LedsChanged += ActivatingRgbServiceOnLedsChanged;
- await profileDescriptor.ProfileModule.ChangeActiveProfileAnimated(profile, _surfaceService.ActiveSurface);
+ await profileDescriptor.ProfileModule.ChangeActiveProfileAnimated(profile, _rgbService.EnabledDevices);
SaveActiveProfile(profileDescriptor.ProfileModule);
_pluginManagementService.PluginEnabled -= ActivatingProfilePluginToggle;
_pluginManagementService.PluginDisabled -= ActivatingProfilePluginToggle;
- _surfaceService.SurfaceConfigurationUpdated -= ActivatingProfileSurfaceUpdate;
+ _rgbService.LedsChanged -= ActivatingRgbServiceOnLedsChanged;
return profile;
}
+
public void ClearActiveProfile(ProfileModule module)
{
- module.ChangeActiveProfile(null, _surfaceService.ActiveSurface);
+ module.ChangeActiveProfile(null, _rgbService.EnabledDevices);
SaveActiveProfile(module);
}
public async Task ClearActiveProfileAnimated(ProfileModule module)
{
- await module.ChangeActiveProfileAnimated(null, _surfaceService.ActiveSurface);
+ await module.ChangeActiveProfileAnimated(null, _rgbService.EnabledDevices);
}
public void DeleteProfile(Profile profile)
@@ -255,7 +254,7 @@ namespace Artemis.Core.Services
string top = profile.RedoStack.Pop();
string memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
profile.UndoStack.Push(memento);
- profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings)
+ profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings)
?? throw new InvalidOperationException("Failed to deserialize memento");
profile.Load();
@@ -268,7 +267,7 @@ namespace Artemis.Core.Services
public void InstantiateProfile(Profile profile)
{
- profile.PopulateLeds(_surfaceService.ActiveSurface);
+ profile.PopulateLeds(_rgbService.EnabledDevices);
}
public string ExportProfile(ProfileDescriptor profileDescriptor)
@@ -296,17 +295,11 @@ namespace Artemis.Core.Services
#region Event handlers
- private void OnActiveSurfaceConfigurationSelected(object? sender, SurfaceConfigurationEventArgs e)
+ private void RgbServiceOnLedsChanged(object? sender, EventArgs e)
{
- ActiveProfilesPopulateLeds(e.Surface);
+ ActiveProfilesPopulateLeds();
}
-
- private void OnSurfaceConfigurationUpdated(object? sender, SurfaceConfigurationEventArgs e)
- {
- if (e.Surface.IsActive)
- ActiveProfilesPopulateLeds(e.Surface);
- }
-
+
#endregion
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/Storage/SurfaceService.cs b/src/Artemis.Core/Services/Storage/SurfaceService.cs
deleted file mode 100644
index ed8aed266..000000000
--- a/src/Artemis.Core/Services/Storage/SurfaceService.cs
+++ /dev/null
@@ -1,252 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using Artemis.Core.DeviceProviders;
-using Artemis.Core.Services.Models;
-using Artemis.Storage.Entities.Surface;
-using Artemis.Storage.Repositories.Interfaces;
-using RGB.NET.Core;
-using Serilog;
-
-namespace Artemis.Core.Services
-{
- internal class SurfaceService : ISurfaceService
- {
- private readonly ILogger _logger;
- private readonly IPluginManagementService _pluginManagementService;
- private readonly IRgbService _rgbService;
- private readonly List _surfaceConfigurations;
- private readonly ISurfaceRepository _surfaceRepository;
-
- public SurfaceService(ILogger logger, ISurfaceRepository surfaceRepository, IRgbService rgbService, IPluginManagementService pluginManagementService)
- {
- _logger = logger;
- _surfaceRepository = surfaceRepository;
- _rgbService = rgbService;
- _pluginManagementService = pluginManagementService;
- _surfaceConfigurations = new List();
-
- // LoadFromRepository is guaranteed to set the ActiveSurface
- ActiveSurface = null!;
- LoadFromRepository();
-
- _rgbService.DeviceLoaded += RgbServiceOnDeviceLoaded;
- }
-
- public ArtemisSurface ActiveSurface { get; private set; }
- public ReadOnlyCollection SurfaceConfigurations => _surfaceConfigurations.AsReadOnly();
-
- public ArtemisSurface CreateSurfaceConfiguration(string name)
- {
- // Create a blank config
- ArtemisSurface configuration = new(_rgbService.Surface, name);
-
- // Add all current devices
- foreach (IRGBDevice rgbDevice in _rgbService.LoadedDevices)
- {
- DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
- configuration.Devices.Add(new ArtemisDevice(rgbDevice, deviceProvider, configuration));
- }
-
- // Auto-arrange the new config
- AutoArrange(configuration);
-
- lock (_surfaceConfigurations)
- {
- _surfaceRepository.Add(configuration.SurfaceEntity);
- _surfaceConfigurations.Add(configuration);
-
- UpdateSurfaceConfiguration(configuration, true);
- return configuration;
- }
- }
-
- public void SetActiveSurfaceConfiguration(ArtemisSurface surface)
- {
- if (surface == null) throw new ArgumentNullException(nameof(surface));
- if (ActiveSurface == surface)
- return;
-
- // Set the new entity
- ActiveSurface = surface;
-
- // Ensure only the new entity is marked as active
- lock (_surfaceConfigurations)
- {
- // Mark only the new surface as active
- foreach (ArtemisSurface configuration in _surfaceConfigurations)
- {
- configuration.IsActive = configuration == ActiveSurface;
- configuration.ApplyToEntity();
-
- _surfaceRepository.Save(configuration.SurfaceEntity);
- }
- }
-
- // Apply the active surface entity to the devices
- foreach (ArtemisDevice device in ActiveSurface.Devices)
- device.ApplyToRgbDevice();
-
- // Update the RGB service's graphics decorator to work with the new surface entity
- _rgbService.UpdateSurfaceLedGroup(ActiveSurface);
- OnActiveSurfaceConfigurationChanged(new SurfaceConfigurationEventArgs(ActiveSurface));
- }
-
- public void UpdateSurfaceConfiguration(ArtemisSurface surface, bool includeDevices)
- {
- surface.ApplyToEntity();
- if (includeDevices)
- {
- foreach (ArtemisDevice deviceConfiguration in surface.Devices)
- {
- deviceConfiguration.ApplyToEntity();
- if (surface.IsActive)
- deviceConfiguration.ApplyToRgbDevice();
- }
- }
-
- surface.UpdateLedMap();
-
- _surfaceRepository.Save(surface.SurfaceEntity);
- _rgbService.UpdateSurfaceLedGroup(ActiveSurface);
- OnSurfaceConfigurationUpdated(new SurfaceConfigurationEventArgs(surface));
- }
-
- public void DeleteSurfaceConfiguration(ArtemisSurface surface)
- {
- if (surface == ActiveSurface)
- throw new ArtemisCoreException($"Cannot delete surface entity '{surface.Name}' because it is active.");
-
- lock (_surfaceConfigurations)
- {
- SurfaceEntity entity = surface.SurfaceEntity;
- _surfaceConfigurations.Remove(surface);
- _surfaceRepository.Remove(entity);
- }
- }
-
- #region Repository
-
- private void LoadFromRepository()
- {
- List configs = _surfaceRepository.GetAll();
- foreach (SurfaceEntity surfaceEntity in configs)
- {
- // Create the surface entity
- ArtemisSurface surfaceConfiguration = new(_rgbService.Surface, surfaceEntity);
- foreach (DeviceEntity position in surfaceEntity.DeviceEntities)
- {
- IRGBDevice? device = _rgbService.Surface.Devices.FirstOrDefault(d => d.GetDeviceIdentifier() == position.DeviceIdentifier);
- if (device != null)
- {
- DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(device);
- surfaceConfiguration.Devices.Add(new ArtemisDevice(device, deviceProvider, surfaceConfiguration, position));
- }
- }
-
- // Finally, add the surface config to the collection
- lock (_surfaceConfigurations)
- {
- _surfaceConfigurations.Add(surfaceConfiguration);
- }
- }
-
- // When all surface configs are loaded, apply the active surface config
- ArtemisSurface? active = SurfaceConfigurations.FirstOrDefault(c => c.IsActive);
- if (active != null)
- SetActiveSurfaceConfiguration(active);
- else
- {
- active = SurfaceConfigurations.FirstOrDefault();
- if (active != null)
- SetActiveSurfaceConfiguration(active);
- else
- SetActiveSurfaceConfiguration(CreateSurfaceConfiguration("Default"));
- }
- }
-
- #endregion
-
- #region Utilities
-
- private void AddDeviceIfMissing(IRGBDevice rgbDevice, ArtemisSurface surface)
- {
- string deviceIdentifier = rgbDevice.GetDeviceIdentifier();
- ArtemisDevice? device = surface.Devices.FirstOrDefault(d => d.DeviceEntity.DeviceIdentifier == deviceIdentifier);
-
- if (device != null)
- return;
-
- // Find an existing device config and use that
- DeviceEntity? existingDeviceConfig = surface.SurfaceEntity.DeviceEntities.FirstOrDefault(d => d.DeviceIdentifier == deviceIdentifier);
- if (existingDeviceConfig != null)
- {
- DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
- device = new ArtemisDevice(rgbDevice, deviceProvider, surface, existingDeviceConfig);
- }
- // Fall back on creating a new device
- else
- {
- _logger.Information(
- "No device config found for {deviceInfo}, device hash: {deviceHashCode}. Adding a new entry.",
- rgbDevice.DeviceInfo,
- deviceIdentifier
- );
- DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
- device = new ArtemisDevice(rgbDevice, deviceProvider, surface);
- }
-
- surface.Devices.Add(device);
- }
-
- #endregion
-
- #region AutoLayout
-
- public void AutoArrange(ArtemisSurface? artemisSurface = null)
- {
- artemisSurface ??= ActiveSurface;
- if (!artemisSurface.Devices.Any())
- return;
-
- SurfaceArrangement surfaceArrangement = SurfaceArrangement.GetDefaultArrangement();
- surfaceArrangement.Arrange(artemisSurface);
- UpdateSurfaceConfiguration(artemisSurface, true);
- }
-
- #endregion
-
- #region Event handlers
-
- private void RgbServiceOnDeviceLoaded(object? sender, DeviceEventArgs e)
- {
- lock (_surfaceConfigurations)
- {
- foreach (ArtemisSurface surfaceConfiguration in _surfaceConfigurations)
- AddDeviceIfMissing(e.Device, surfaceConfiguration);
- }
-
- UpdateSurfaceConfiguration(ActiveSurface, true);
- }
-
- #endregion
-
- #region Events
-
- public event EventHandler? ActiveSurfaceConfigurationSelected;
- public event EventHandler? SurfaceConfigurationUpdated;
-
- protected virtual void OnActiveSurfaceConfigurationChanged(SurfaceConfigurationEventArgs e)
- {
- ActiveSurfaceConfigurationSelected?.Invoke(this, e);
- }
-
- protected virtual void OnSurfaceConfigurationUpdated(SurfaceConfigurationEventArgs e)
- {
- SurfaceConfigurationUpdated?.Invoke(this, e);
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs
index 061bd984e..ffee5bc63 100644
--- a/src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs
+++ b/src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs
@@ -43,9 +43,9 @@ namespace Artemis.Core.Services
///
protected override async Task ProcessRequest(IHttpContext context)
{
- if (context.Request.HttpVerb != HttpVerbs.Post)
- throw HttpException.MethodNotAllowed("This end point only accepts POST calls");
-
+ if (context.Request.HttpVerb != HttpVerbs.Post && context.Request.HttpVerb != HttpVerbs.Put)
+ throw HttpException.MethodNotAllowed("This end point only accepts POST and PUT calls");
+
context.Response.ContentType = MimeType.Json;
using TextReader reader = context.OpenRequestText();
diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs
index c81c772ee..c456ff220 100644
--- a/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs
+++ b/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs
@@ -29,7 +29,7 @@ namespace Artemis.Core.Services
///
/// Gets the full URL of the end point
///
- public string Url => $"{_pluginsModule.ServerUrl.TrimEnd('/')}{_pluginsModule.BaseRoute}{PluginFeature.Plugin.Guid}/{Name}";
+ public string Url => $"{_pluginsModule.ServerUrl?.TrimEnd('/')}{_pluginsModule.BaseRoute}{PluginFeature.Plugin.Guid}/{Name}";
///
/// Gets the plugin the end point is associated with
@@ -45,12 +45,12 @@ namespace Artemis.Core.Services
///
/// Gets the mime type of the input this end point accepts
///
- public string Accepts { get; protected set; }
+ public string? Accepts { get; protected set; }
///
/// Gets the mime type of the output this end point returns
///
- public string Returns { get; protected set; }
+ public string? Returns { get; protected set; }
///
/// Called whenever the end point has to process a request
diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/StringPluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/StringPluginEndPoint.cs
index b5f65bb2d..b8f996d1c 100644
--- a/src/Artemis.Core/Services/WebServer/EndPoints/StringPluginEndPoint.cs
+++ b/src/Artemis.Core/Services/WebServer/EndPoints/StringPluginEndPoint.cs
@@ -32,8 +32,8 @@ namespace Artemis.Core.Services
///
protected override async Task ProcessRequest(IHttpContext context)
{
- if (context.Request.HttpVerb != HttpVerbs.Post)
- throw HttpException.MethodNotAllowed("This end point only accepts POST calls");
+ if (context.Request.HttpVerb != HttpVerbs.Post && context.Request.HttpVerb != HttpVerbs.Put)
+ throw HttpException.MethodNotAllowed("This end point only accepts POST and PUT calls");
context.Response.ContentType = MimeType.PlainText;
diff --git a/src/Artemis.Core/Utilities/IntroAnimation.cs b/src/Artemis.Core/Utilities/IntroAnimation.cs
index 56b92f1f5..f2f1bb4fb 100644
--- a/src/Artemis.Core/Utilities/IntroAnimation.cs
+++ b/src/Artemis.Core/Utilities/IntroAnimation.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using Artemis.Core.Modules;
@@ -13,13 +14,13 @@ namespace Artemis.Core
{
private readonly ILogger _logger;
private readonly IProfileService _profileService;
- private readonly ISurfaceService _surfaceService;
+ private readonly IEnumerable _devices;
- public IntroAnimation(ILogger logger, IProfileService profileService, ISurfaceService surfaceService)
+ public IntroAnimation(ILogger logger, IProfileService profileService, IEnumerable devices)
{
_logger = logger;
_profileService = profileService;
- _surfaceService = surfaceService;
+ _devices = devices;
AnimationProfile = CreateIntroProfile();
}
@@ -41,14 +42,14 @@ namespace Artemis.Core
ProfileEntity profileEntity = CoreJson.DeserializeObject(json)!;
// Inject every LED on the surface into each layer
foreach (LayerEntity profileEntityLayer in profileEntity.Layers)
- profileEntityLayer.Leds.AddRange(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds).Select(l => new LedEntity
+ profileEntityLayer.Leds.AddRange(_devices.SelectMany(d => d.Leds).Select(l => new LedEntity
{
DeviceIdentifier = l.Device.RgbDevice.GetDeviceIdentifier(),
LedName = l.RgbLed.Id.ToString()
}));
Profile profile = new(new DummyModule(), profileEntity);
- profile.Activate(_surfaceService.ActiveSurface);
+ profile.Activate(_devices);
_profileService.InstantiateProfile(profile);
return profile;
@@ -79,7 +80,7 @@ namespace Artemis.Core
throw new NotImplementedException();
}
- public override void Render(double deltaTime, ArtemisSurface surface, SKCanvas canvas, SKImageInfo canvasInfo)
+ public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
{
throw new NotImplementedException();
}
diff --git a/src/Artemis.Core/Utilities/Utilities.cs b/src/Artemis.Core/Utilities/Utilities.cs
index 9d7e7de8e..8577185bc 100644
--- a/src/Artemis.Core/Utilities/Utilities.cs
+++ b/src/Artemis.Core/Utilities/Utilities.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
@@ -18,8 +17,9 @@ namespace Artemis.Core
///
public static void PrepareFirstLaunch()
{
- CreateArtemisFolderIfMissing(Constants.DataFolder);
- CreateArtemisFolderIfMissing(Constants.DataFolder + "plugins");
+ CreateAccessibleDirectory(Constants.DataFolder);
+ CreateAccessibleDirectory(Constants.DataFolder + "plugins");
+ CreateAccessibleDirectory(Constants.DataFolder + "user layouts");
}
///
@@ -62,15 +62,11 @@ namespace Artemis.Core
}
///
- /// Gets the current application location
+ /// Creates all directories and subdirectories in the specified path unless they already exist with permissions
+ /// allowing access by everyone.
///
- ///
- internal static string GetCurrentLocation()
- {
- return Process.GetCurrentProcess().MainModule!.FileName!;
- }
-
- private static void CreateArtemisFolderIfMissing(string path)
+ /// The directory to create.
+ public static void CreateAccessibleDirectory(string path)
{
if (!Directory.Exists(path))
{
@@ -92,6 +88,15 @@ namespace Artemis.Core
}
}
+ ///
+ /// Gets the current application location
+ ///
+ ///
+ internal static string GetCurrentLocation()
+ {
+ return Process.GetCurrentProcess().MainModule!.FileName!;
+ }
+
private static void OnRestartRequested(RestartEventArgs e)
{
RestartRequested?.Invoke(null, e);
diff --git a/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs b/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs
index b952fc74c..670cec969 100644
--- a/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs
+++ b/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs
@@ -8,8 +8,8 @@ namespace Artemis.Storage.Entities.Surface
{
InputIdentifiers = new List();
}
-
- public string DeviceIdentifier { get; set; }
+
+ public string Id { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double Rotation { get; set; }
@@ -20,7 +20,12 @@ namespace Artemis.Storage.Entities.Surface
public double BlueScale { get; set; }
public bool IsEnabled { get; set; }
+ public int PhysicalLayout { get; set; }
+ public string LogicalLayout { get; set; }
+ public string CustomLayoutPath { get; set; }
+
public List InputIdentifiers { get; set; }
+
}
public class DeviceInputIdentifierEntity
diff --git a/src/Artemis.Storage/Entities/Surface/SurfaceEntity.cs b/src/Artemis.Storage/Entities/Surface/SurfaceEntity.cs
deleted file mode 100644
index 2ff49e492..000000000
--- a/src/Artemis.Storage/Entities/Surface/SurfaceEntity.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Artemis.Storage.Entities.Surface
-{
- public class SurfaceEntity
- {
- public Guid Id { get; set; }
-
- public string Name { get; set; }
- public bool IsActive { get; set; }
-
- public List DeviceEntities { get; set; }
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Repositories/DeviceRepository.cs b/src/Artemis.Storage/Repositories/DeviceRepository.cs
new file mode 100644
index 000000000..9892c328e
--- /dev/null
+++ b/src/Artemis.Storage/Repositories/DeviceRepository.cs
@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using Artemis.Storage.Entities.Surface;
+using Artemis.Storage.Repositories.Interfaces;
+using LiteDB;
+
+namespace Artemis.Storage.Repositories
+{
+ internal class DeviceRepository : IDeviceRepository
+ {
+ private readonly LiteRepository _repository;
+
+ public DeviceRepository(LiteRepository repository)
+ {
+ _repository = repository;
+ _repository.Database.GetCollection().EnsureIndex(s => s.Id);
+ }
+
+ public void Add(DeviceEntity deviceEntity)
+ {
+ _repository.Insert(deviceEntity);
+ }
+
+ public void Remove(DeviceEntity deviceEntity)
+ {
+ _repository.Delete(deviceEntity.Id);
+ }
+
+ public DeviceEntity Get(string id)
+ {
+ return _repository.FirstOrDefault(s => s.Id == id);
+ }
+
+ public List GetAll()
+ {
+ return _repository.Query().Include(s => s.InputIdentifiers).ToList();
+ }
+
+ public void Save(DeviceEntity deviceEntity)
+ {
+ _repository.Upsert(deviceEntity);
+ }
+
+ public void Save(IEnumerable deviceEntities)
+ {
+ _repository.Upsert(deviceEntities);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Repositories/Interfaces/IDeviceRepository.cs b/src/Artemis.Storage/Repositories/Interfaces/IDeviceRepository.cs
new file mode 100644
index 000000000..fc2c8375d
--- /dev/null
+++ b/src/Artemis.Storage/Repositories/Interfaces/IDeviceRepository.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+using Artemis.Storage.Entities.Surface;
+
+namespace Artemis.Storage.Repositories.Interfaces
+{
+ public interface IDeviceRepository : IRepository
+ {
+ void Add(DeviceEntity deviceEntity);
+ void Remove(DeviceEntity deviceEntity);
+ DeviceEntity Get(string id);
+ List GetAll();
+ void Save(DeviceEntity deviceEntity);
+ void Save(IEnumerable deviceEntities);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Repositories/Interfaces/ISurfaceRepository.cs b/src/Artemis.Storage/Repositories/Interfaces/ISurfaceRepository.cs
deleted file mode 100644
index afbd7743b..000000000
--- a/src/Artemis.Storage/Repositories/Interfaces/ISurfaceRepository.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Collections.Generic;
-using Artemis.Storage.Entities.Surface;
-
-namespace Artemis.Storage.Repositories.Interfaces
-{
- public interface ISurfaceRepository : IRepository
- {
- void Add(SurfaceEntity surfaceEntity);
- void Remove(SurfaceEntity surfaceEntity);
- SurfaceEntity GetByName(string name);
- List GetAll();
-
- void Save(SurfaceEntity surfaceEntity);
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Repositories/SurfaceRepository.cs b/src/Artemis.Storage/Repositories/SurfaceRepository.cs
deleted file mode 100644
index 46b8b9e6b..000000000
--- a/src/Artemis.Storage/Repositories/SurfaceRepository.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Artemis.Storage.Entities.Surface;
-using Artemis.Storage.Repositories.Interfaces;
-using LiteDB;
-
-namespace Artemis.Storage.Repositories
-{
- internal class SurfaceRepository : ISurfaceRepository
- {
- private readonly LiteRepository _repository;
-
- public SurfaceRepository(LiteRepository repository)
- {
- _repository = repository;
- _repository.Database.GetCollection().EnsureIndex(s => s.Name);
- }
-
- public void Add(SurfaceEntity surfaceEntity)
- {
- _repository.Insert(surfaceEntity);
- }
-
- public void Remove(SurfaceEntity surfaceEntity)
- {
- _repository.Delete(surfaceEntity.Id);
- }
-
- public SurfaceEntity GetByName(string name)
- {
- return _repository.FirstOrDefault(s => s.Name == name);
- }
-
- public List GetAll()
- {
- return _repository.Query().Include(s => s.DeviceEntities.Select(de => de.InputIdentifiers)).ToList();
- }
-
- public void Save(SurfaceEntity surfaceEntity)
- {
- _repository.Upsert(surfaceEntity);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
index 6021653ba..397dd2dae 100644
--- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
+++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
@@ -182,7 +182,11 @@ namespace Artemis.UI.Shared
if (_oldDevice != null)
{
if (Device != null)
+ {
Device.RgbDevice.PropertyChanged -= DevicePropertyChanged;
+ Device.DeviceUpdated -= DeviceUpdated;
+ }
+
_oldDevice = null;
}
}
@@ -228,15 +232,20 @@ namespace Artemis.UI.Shared
return;
if (_oldDevice != null)
+ {
Device.RgbDevice.PropertyChanged -= DevicePropertyChanged;
+ Device.DeviceUpdated -= DeviceUpdated;
+ }
+
_oldDevice = Device;
Device.RgbDevice.PropertyChanged += DevicePropertyChanged;
+ Device.DeviceUpdated += DeviceUpdated;
UpdateTransform();
// Load the device main image
- if (Device.RgbDevice.DeviceInfo?.Image?.AbsolutePath != null && File.Exists(Device.RgbDevice.DeviceInfo.Image.AbsolutePath))
- _deviceImage = new BitmapImage(Device.RgbDevice.DeviceInfo.Image);
+ if (Device.Layout?.Image != null && File.Exists(Device.Layout.Image.LocalPath))
+ _deviceImage = new BitmapImage(Device.Layout.Image);
// Create all the LEDs
foreach (ArtemisLed artemisLed in Device.Leds)
@@ -277,13 +286,16 @@ namespace Artemis.UI.Shared
InvalidateMeasure();
}
- private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e)
+ private void DeviceUpdated(object? sender, EventArgs e)
{
- if (e.PropertyName == nameof(Device.RgbDevice.Scale) || e.PropertyName == nameof(Device.RgbDevice.Rotation))
- UpdateTransform();
+ SetupForDevice();
}
-
+ private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ SetupForDevice();
+ }
+
private void Render()
{
DrawingContext drawingContext = _backingStore.Open();
diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs
index b271c9c29..c3ba1fd6a 100644
--- a/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs
+++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs
@@ -11,6 +11,9 @@ namespace Artemis.UI.Shared
{
internal class DeviceVisualizerLed
{
+ private SolidColorBrush? _renderColorBrush;
+ private Color _renderColor;
+
public DeviceVisualizerLed(ArtemisLed led)
{
Led = led;
@@ -21,9 +24,9 @@ namespace Artemis.UI.Shared
Led.RgbLed.Size.Height
);
- if (Led.RgbLed.Image != null && File.Exists(Led.RgbLed.Image.AbsolutePath))
- LedImage = new BitmapImage(Led.RgbLed.Image);
-
+ if (Led.Layout?.Image != null && File.Exists(Led.Layout.Image.LocalPath))
+ LedImage = new BitmapImage(Led.Layout.Image);
+
CreateLedGeometry();
}
@@ -38,14 +41,19 @@ namespace Artemis.UI.Shared
if (DisplayGeometry == null)
return;
+ _renderColorBrush ??= new SolidColorBrush();
+
RGB.NET.Core.Color originalColor = Led.GetOriginalColor();
byte r = originalColor.GetR();
byte g = originalColor.GetG();
byte b = originalColor.GetB();
- drawingContext.DrawRectangle(isDimmed
- ? new SolidColorBrush(Color.FromArgb(100, r, g, b))
- : new SolidColorBrush(Color.FromRgb(r, g, b)), null, LedRect);
+ _renderColor.A = isDimmed ? 100 : 255;
+ _renderColor.R = r;
+ _renderColor.G = g;
+ _renderColor.B = b;
+ _renderColorBrush.Color = _renderColor;
+ drawingContext.DrawRectangle(_renderColorBrush, null, LedRect);
}
public void RenderImage(DrawingContext drawingContext)
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
index ee678f4fc..720bf0ad3 100644
--- a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
@@ -8,7 +8,6 @@ using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile;
using Artemis.UI.Shared.Services.Models;
-using Newtonsoft.Json;
using Ninject;
using Ninject.Parameters;
using Serilog;
@@ -20,23 +19,23 @@ namespace Artemis.UI.Shared.Services
internal class ProfileEditorService : IProfileEditorService
{
private readonly ICoreService _coreService;
- private readonly ISurfaceService _surfaceService;
private readonly IKernel _kernel;
private readonly ILogger _logger;
private readonly IProfileService _profileService;
private readonly List _registeredPropertyEditors;
+ private readonly IRgbService _rgbService;
private readonly object _selectedProfileElementLock = new();
private readonly object _selectedProfileLock = new();
private TimeSpan _currentTime;
private int _pixelsPerSecond;
- public ProfileEditorService(IKernel kernel, ILogger logger, IProfileService profileService, ICoreService coreService, ISurfaceService surfaceService)
+ public ProfileEditorService(IKernel kernel, ILogger logger, IProfileService profileService, ICoreService coreService, IRgbService rgbService)
{
_kernel = kernel;
_logger = logger;
_profileService = profileService;
_coreService = coreService;
- _surfaceService = surfaceService;
+ _rgbService = rgbService;
_registeredPropertyEditors = new List();
PixelsPerSecond = 100;
@@ -351,7 +350,7 @@ namespace Artemis.UI.Shared.Services
public List GetLedsInRectangle(Rect rect)
{
- return _surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds).Where(led => led.AbsoluteRectangle.IntersectsWith(rect.ToSKRect())).ToList();
+ return _rgbService.EnabledDevices.SelectMany(d => d.Leds).Where(led => led.AbsoluteRectangle.IntersectsWith(rect.ToSKRect())).ToList();
}
#region Copy/paste
@@ -424,7 +423,7 @@ namespace Artemis.UI.Shared.Services
if (pasted != null)
{
- target.Profile.PopulateLeds(_surfaceService.ActiveSurface);
+ target.Profile.PopulateLeds(_rgbService.EnabledDevices);
UpdateSelectedProfile();
ChangeSelectedProfileElement(pasted);
}
diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index c0eacf441..a78e9658a 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -1,361 +1,364 @@
-
- WinExe
- net5.0-windows
- {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- true
- Artemis
- Artemis
- en-US
- Provides advanced unified lighting across many different brands RGB peripherals
- Copyright © Robert Beekman - 2021
- 2.0.0.0
- bin\$(Platform)\$(Configuration)\
- true
- x64
-
-
- x64
-
+
+ WinExe
+ net5.0-windows
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ true
+ Artemis
+ Artemis
+ en-US
+ Provides advanced unified lighting across many different brands RGB peripherals
+ Copyright © Robert Beekman - 2021
+ 2.0.0.0
+ bin\$(Platform)\$(Configuration)\
+ true
+ x64
+ windows
+
+
+ x64
+
-
- Resources\Images\Logo\logo-512.ico
-
-
-
- 2.0.0.0
- 2.0.0
-
-
- 2.0-{chash:6}
- true
- true
- true
- v[0-9]*
- true
- git
- true
-
-
-
-
-
-
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Brushes.dll
- true
-
-
- ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll
-
-
- ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Groups.dll
-
-
-
-
-
- ResXFileCodeGenerator
- Designer
- Resources.Designer.cs
-
-
-
-
- true
-
-
- true
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- True
- True
- Resources.resx
-
-
- True
- True
- Settings.settings
-
-
-
-
- PreserveNewest
-
-
- SettingsSingleFileGenerator
- Settings.Designer.cs
-
-
-
-
- Designer
-
-
- Designer
-
-
- Designer
-
-
- Designer
-
-
- Designer
-
-
- Designer
-
-
- $(DefaultXamlRuntime)
-
-
+
+ Resources\Images\Logo\logo-512.ico
+
+
+
+ 2.0.0.0
+ 2.0.0
+
+
+ 2.0-{chash:6}
+ true
+ true
+ true
+ v[0-9]*
+ true
+ git
+ true
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Brushes.dll
+
+
+ ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll
+
+
+ ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Groups.dll
+
+
+ ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Layout.dll
+
+
+
+
+
+ ResXFileCodeGenerator
+ Designer
+ Resources.Designer.cs
+
+
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PreserveNewest
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+ Designer
+
+
+ Designer
+
+
+ Designer
+
+
+ Designer
+
+
+ Designer
+
+
+ Designer
+
+
+ $(DefaultXamlRuntime)
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/FolderViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/FolderViewModel.cs
index 310e1324f..ba8de22bd 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/FolderViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/FolderViewModel.cs
@@ -9,12 +9,12 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
{
// I hate this about DI, oh well
public FolderViewModel(ProfileElement folder,
+ IRgbService rgbService,
IProfileEditorService profileEditorService,
IDialogService dialogService,
IProfileTreeVmFactory profileTreeVmFactory,
- ILayerBrushService layerBrushService,
- ISurfaceService surfaceService) :
- base(folder, profileEditorService, dialogService, profileTreeVmFactory, layerBrushService, surfaceService)
+ ILayerBrushService layerBrushService) :
+ base(folder, rgbService, profileEditorService, dialogService, profileTreeVmFactory, layerBrushService)
{
}
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerViewModel.cs
index b338ddd13..56b559862 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerViewModel.cs
@@ -8,12 +8,12 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
public class LayerViewModel : TreeItemViewModel
{
public LayerViewModel(ProfileElement layer,
+ IRgbService rgbService,
IProfileEditorService profileEditorService,
IDialogService dialogService,
IProfileTreeVmFactory profileTreeVmFactory,
- ILayerBrushService layerBrushService,
- ISurfaceService surfaceService) :
- base(layer, profileEditorService, dialogService, profileTreeVmFactory, layerBrushService, surfaceService)
+ ILayerBrushService layerBrushService) :
+ base(layer, rgbService, profileEditorService, dialogService, profileTreeVmFactory, layerBrushService)
{
}
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs
index a0086373f..08eeaac02 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs
@@ -18,23 +18,23 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
{
private readonly IDialogService _dialogService;
private readonly ILayerBrushService _layerBrushService;
+ private readonly IRgbService _rgbService;
private readonly IProfileEditorService _profileEditorService;
private readonly IProfileTreeVmFactory _profileTreeVmFactory;
- private readonly ISurfaceService _surfaceService;
private ProfileElement _profileElement;
protected TreeItemViewModel(ProfileElement profileElement,
+ IRgbService rgbService,
IProfileEditorService profileEditorService,
IDialogService dialogService,
IProfileTreeVmFactory profileTreeVmFactory,
- ILayerBrushService layerBrushService,
- ISurfaceService surfaceService)
+ ILayerBrushService layerBrushService)
{
+ _rgbService = rgbService;
_profileEditorService = profileEditorService;
_dialogService = dialogService;
_profileTreeVmFactory = profileTreeVmFactory;
_layerBrushService = layerBrushService;
- _surfaceService = surfaceService;
ProfileElement = profileElement;
@@ -144,7 +144,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
if (brush != null)
layer.ChangeLayerBrush(brush);
- layer.AddLeds(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds));
+ layer.AddLeds(_rgbService.EnabledDevices.SelectMany(d => d.Leds));
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.ChangeSelectedProfileElement(layer);
}
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileLayerViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileLayerViewModel.cs
index afc9c6c54..b84d36b14 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileLayerViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileLayerViewModel.cs
@@ -134,19 +134,19 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
private Geometry CreateRectangleGeometry(ArtemisLed led)
{
- Rect rect = led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1);
+ Rect rect = led.RgbLed.AbsoluteBoundary.ToWindowsRect(1);
return new RectangleGeometry(rect);
}
private Geometry CreateCircleGeometry(ArtemisLed led)
{
- Rect rect = led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1);
+ Rect rect = led.RgbLed.AbsoluteBoundary.ToWindowsRect(1);
return new EllipseGeometry(rect);
}
private Geometry CreateCustomGeometry(ArtemisLed led, double deflateAmount)
{
- Rect rect = led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1);
+ Rect rect = led.RgbLed.AbsoluteBoundary.ToWindowsRect(1);
try
{
PathGeometry geometry = Geometry.Combine(
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileView.xaml b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileView.xaml
index 1237727f8..8441cd11d 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileView.xaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileView.xaml
@@ -99,7 +99,10 @@
-
+
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs
index e559a30fc..071884dcd 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs
@@ -6,48 +6,48 @@ using System.Windows.Input;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Events;
-using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Visualization.Tools;
using Artemis.UI.Screens.Shared;
using Artemis.UI.Shared.Services;
+using SkiaSharp;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Visualization
{
public class ProfileViewModel : Conductor.Collection.AllActive, IProfileEditorPanelViewModel, IHandle, IHandle
{
- private readonly IProfileEditorService _profileEditorService;
private readonly ICoreService _coreService;
+ private readonly IProfileEditorService _profileEditorService;
private readonly IProfileLayerVmFactory _profileLayerVmFactory;
+ private readonly IRgbService _rgbService;
private readonly ISettingsService _settingsService;
- private readonly ISurfaceService _surfaceService;
private readonly IVisualizationToolVmFactory _visualizationToolVmFactory;
private int _activeToolIndex;
private VisualizationToolViewModel _activeToolViewModel;
+ private PluginSetting _alwaysApplyDataBindings;
private bool _canApplyToLayer;
private bool _canSelectEditTool;
private BindableCollection _devices;
private BindableCollection _highlightedLeds;
private PluginSetting _highlightSelectedLayer;
- private PluginSetting _alwaysApplyDataBindings;
+ private DateTime _lastUpdate;
private PanZoomViewModel _panZoomViewModel;
private Layer _previousSelectedLayer;
private int _previousTool;
- private DateTime _lastUpdate;
public ProfileViewModel(IProfileEditorService profileEditorService,
+ IRgbService rgbService,
ICoreService coreService,
- ISurfaceService surfaceService,
ISettingsService settingsService,
IEventAggregator eventAggregator,
IVisualizationToolVmFactory visualizationToolVmFactory,
IProfileLayerVmFactory profileLayerVmFactory)
{
_profileEditorService = profileEditorService;
+ _rgbService = rgbService;
_coreService = coreService;
- _surfaceService = surfaceService;
_settingsService = settingsService;
_visualizationToolVmFactory = visualizationToolVmFactory;
_profileLayerVmFactory = profileLayerVmFactory;
@@ -99,25 +99,21 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
{
// Remove the tool from the canvas
if (_activeToolViewModel != null)
- {
lock (Items)
{
Items.Remove(_activeToolViewModel);
NotifyOfPropertyChange(() => Items);
}
- }
// Set the new tool
SetAndNotify(ref _activeToolViewModel, value);
// Add the new tool to the canvas
if (_activeToolViewModel != null)
- {
lock (Items)
{
Items.Add(_activeToolViewModel);
NotifyOfPropertyChange(() => Items);
}
- }
}
}
@@ -144,7 +140,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
Devices = new BindableCollection();
HighlightedLeds = new BindableCollection();
- ApplySurfaceConfiguration(_surfaceService.ActiveSurface);
+ ApplyDevices();
ActivateToolByIndex(0);
ApplyActiveProfile();
@@ -156,7 +152,8 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
_coreService.FrameRendered += OnFrameRendered;
HighlightSelectedLayer.SettingChanged += HighlightSelectedLayerOnSettingChanged;
- _surfaceService.ActiveSurfaceConfigurationSelected += OnActiveSurfaceConfigurationSelected;
+ _rgbService.DeviceAdded += RgbServiceOnDevicesModified;
+ _rgbService.DeviceRemoved += RgbServiceOnDevicesModified;
_profileEditorService.ProfileSelected += OnProfileSelected;
_profileEditorService.ProfileElementSelected += OnProfileElementSelected;
_profileEditorService.SelectedProfileElementUpdated += OnSelectedProfileElementUpdated;
@@ -168,7 +165,8 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
{
_coreService.FrameRendered -= OnFrameRendered;
HighlightSelectedLayer.SettingChanged -= HighlightSelectedLayerOnSettingChanged;
- _surfaceService.ActiveSurfaceConfigurationSelected -= OnActiveSurfaceConfigurationSelected;
+ _rgbService.DeviceAdded -= RgbServiceOnDevicesModified;
+ _rgbService.DeviceRemoved -= RgbServiceOnDevicesModified;
_profileEditorService.ProfileSelected -= OnProfileSelected;
_profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
_profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated;
@@ -181,9 +179,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
base.OnClose();
}
- private void OnActiveSurfaceConfigurationSelected(object sender, SurfaceConfigurationEventArgs e)
+ private void RgbServiceOnDevicesModified(object sender, DeviceEventArgs e)
{
- ApplySurfaceConfiguration(e.Surface);
+ ApplyDevices();
}
private void ApplyActiveProfile()
@@ -193,10 +191,8 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
// Add new layers missing a VM
foreach (Layer layer in layers)
- {
if (layerViewModels.All(vm => vm.Layer != layer))
Items.Add(_profileLayerVmFactory.Create(layer, PanZoomViewModel));
- }
// Remove layers that no longer exist
IEnumerable toRemove = layerViewModels.Where(vm => !layers.Contains(vm.Layer));
@@ -204,10 +200,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
Items.Remove(profileLayerViewModel);
}
- private void ApplySurfaceConfiguration(ArtemisSurface surface)
+ private void ApplyDevices()
{
Devices.Clear();
- Devices.AddRange(surface.Devices.Where(d => d.IsEnabled).OrderBy(d => d.ZIndex));
+ Devices.AddRange(_rgbService.EnabledDevices.Where(d => d.IsEnabled).OrderBy(d => d.ZIndex));
}
private void UpdateLedsDimStatus()
@@ -259,7 +255,15 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
public void ResetZoomAndPan()
{
- PanZoomViewModel.Reset();
+ // Create a rect surrounding all devices
+ SKRect rect = new SKRect(
+ Devices.Min(d => d.Rectangle.Left),
+ Devices.Min(d => d.Rectangle.Top),
+ Devices.Max(d => d.Rectangle.Right),
+ Devices.Max(d => d.Rectangle.Bottom)
+ );
+
+ PanZoomViewModel.Reset(rect);
}
#endregion
@@ -339,7 +343,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
_previousSelectedLayer.Transform.LayerPropertyOnCurrentValueSet += TransformValueChanged;
}
else
+ {
_previousSelectedLayer = null;
+ }
ApplyActiveProfile();
UpdateLedsDimStatus();
diff --git a/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugView.xaml b/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugView.xaml
index 6ab2f00bd..80dc57da4 100644
--- a/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugView.xaml
+++ b/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugView.xaml
@@ -40,8 +40,31 @@
-
-
+
+
+
+
+
@@ -55,27 +78,27 @@
-
- In this window you can view detailed information of the device.
- Please note that having this window open can have a performance impact on your system.
+
+ In this window you can view detailed information of the device.
+ Please note that having this window open can have a performance impact on your system.
-
-
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -87,12 +110,12 @@
Device name
+ Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
+ TextWrapping="Wrap"
+ Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName}" />
-
+
@@ -105,30 +128,12 @@
Manufacturer
+ Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
+ TextWrapping="Wrap"
+ Text="{Binding Device.RgbDevice.DeviceInfo.Manufacturer}" />
-
-
-
-
-
-
-
-
-
-
-
- Lighting support
-
-
-
-
+
@@ -141,12 +146,30 @@
Device type
+ Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
+ TextWrapping="Wrap"
+ Text="{Binding Device.RgbDevice.DeviceInfo.DeviceType}" />
-
+
+
+
+
+
+
+
+
+
+
+
+ Physical layout
+
+
+
+
@@ -159,15 +182,13 @@
Device image
+ TextWrapping="Wrap"
+ Text="{Binding Device.Layout.Image, Mode=OneWay}"
+ IsReadOnly="True" />
-
-
-
+
@@ -179,12 +200,12 @@
Size (1px = 1mm)
+ Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
+ TextWrapping="Wrap"
+ Text="{Binding Device.RgbDevice.Size}" />
-
+
@@ -196,12 +217,12 @@
Location (1px = 1mm)
+ Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
+ TextWrapping="Wrap"
+ Text="{Binding Device.RgbDevice.Location}" />
-
+
@@ -214,12 +235,12 @@
Rotation (degrees)
+ Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
+ TextWrapping="Wrap"
+ Text="{Binding Device.RgbDevice.Rotation.Degrees}" />
-
+
@@ -230,14 +251,14 @@
- Syncback supported
+ Logical layout
+ Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
+ TextWrapping="Wrap"
+ Text="{Binding Device.LogicalLayout}" />
-
+
@@ -250,17 +271,17 @@
Layout file path
+ TextWrapping="Wrap"
+ Text="{Binding Device.Layout.FilePath, Mode=OneWay}"
+ IsReadOnly="True" />
-
-
-
+
+
+
-
+
+ CanUserResizeRows="False">
@@ -282,9 +302,9 @@
-
+
-
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugViewModel.cs b/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugViewModel.cs
index 9bfac70e9..0eac26446 100644
--- a/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Debug/DeviceDebugViewModel.cs
@@ -2,10 +2,15 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.Xml.Serialization;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared.Services;
-using Stylet; // using PropertyChanged;
+using Ookii.Dialogs.Wpf;
+using RGB.NET.Layout;
+using Stylet;
+
+// using PropertyChanged;
namespace Artemis.UI.Screens.Settings.Debug
{
@@ -13,11 +18,13 @@ namespace Artemis.UI.Screens.Settings.Debug
{
private readonly IDeviceService _deviceService;
private readonly IDialogService _dialogService;
+ private readonly IRgbService _rgbService;
private ArtemisLed _selectedLed;
- public DeviceDebugViewModel(ArtemisDevice device, IDeviceService deviceService, IDialogService dialogService)
+ public DeviceDebugViewModel(ArtemisDevice device, IDeviceService deviceService, IRgbService rgbService, IDialogService dialogService)
{
_deviceService = deviceService;
+ _rgbService = rgbService;
_dialogService = dialogService;
Device = device;
}
@@ -35,7 +42,7 @@ namespace Artemis.UI.Screens.Settings.Debug
}
}
- public bool CanOpenImageDirectory => Device.RgbDevice.DeviceInfo.Image != null;
+ public bool CanOpenImageDirectory => Device.Layout?.Image != null;
// ReSharper disable UnusedMember.Global
@@ -70,7 +77,7 @@ namespace Artemis.UI.Screens.Settings.Debug
try
{
- Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.GetDirectoryName(Device.RgbDevice.DeviceInfo.Image.AbsolutePath));
+ Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.GetDirectoryName(Device.Layout.Image.AbsolutePath));
}
catch (Exception e)
{
@@ -78,6 +85,73 @@ namespace Artemis.UI.Screens.Settings.Debug
}
}
+ public void ReloadLayout()
+ {
+ _rgbService.ApplyBestDeviceLayout(Device);
+ }
+
+ public void ExportLayout()
+ {
+ if (Device.Layout == null)
+ return;
+
+ VistaFolderBrowserDialog dialog = new()
+ {
+ Description = "Select layout export target folder",
+ UseDescriptionForTitle = true,
+ ShowNewFolderButton = true,
+ SelectedPath = Path.Combine(Constants.DataFolder, "user layouts")
+ };
+
+ bool? result = dialog.ShowDialog();
+ if (result != true)
+ return;
+
+ string directory = Path.Combine(
+ dialog.SelectedPath,
+ Device.RgbDevice.DeviceInfo.Manufacturer,
+ Device.RgbDevice.DeviceInfo.DeviceType.ToString()
+ );
+ string filePath = Path.Combine(directory, Device.GetLayoutFileName());
+ Core.Utilities.CreateAccessibleDirectory(directory);
+
+ // XML
+ XmlSerializer serializer = new(typeof(DeviceLayout));
+ using StreamWriter writer = new(filePath);
+ serializer.Serialize(writer, Device.Layout!.RgbLayout);
+
+ // Device images
+ if (!Uri.IsWellFormedUriString(Device.Layout.FilePath, UriKind.Absolute))
+ return;
+
+ Uri targetDirectory = new(directory + "/", UriKind.Absolute);
+ Uri sourceDirectory = new(Path.GetDirectoryName(Device.Layout.FilePath)! + "/", UriKind.Absolute);
+ Uri deviceImageTarget = new(targetDirectory, Device.Layout.LayoutCustomDeviceData.DeviceImage);
+
+ // Create folder (if needed) and copy image
+ Core.Utilities.CreateAccessibleDirectory(Path.GetDirectoryName(deviceImageTarget.LocalPath)!);
+ if (Device.Layout.Image != null && File.Exists(Device.Layout.Image.LocalPath) && !File.Exists(deviceImageTarget.LocalPath))
+ File.Copy(Device.Layout.Image.LocalPath, deviceImageTarget.LocalPath);
+
+ foreach (ArtemisLedLayout ledLayout in Device.Layout.Leds)
+ {
+ if (ledLayout.LayoutCustomLedData.LogicalLayouts == null)
+ continue;
+
+ // Only the image of the current logical layout is available as an URI, iterate each layout and find the images manually
+ foreach (LayoutCustomLedDataLogicalLayout logicalLayout in ledLayout.LayoutCustomLedData.LogicalLayouts)
+ {
+ Uri image = new(sourceDirectory, logicalLayout.Image);
+ Uri imageTarget = new(targetDirectory, logicalLayout.Image);
+
+ // Create folder (if needed) and copy image
+ Core.Utilities.CreateAccessibleDirectory(Path.GetDirectoryName(imageTarget.LocalPath)!);
+ if (File.Exists(image.LocalPath) && !File.Exists(imageTarget.LocalPath))
+ File.Copy(image.LocalPath, imageTarget.LocalPath);
+ }
+ }
+ }
+
#endregion
// ReSharper restore UnusedMember.Global
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsTabViewModel.cs
index 31ba945e7..6c13a7266 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsTabViewModel.cs
@@ -11,15 +11,15 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
public class DeviceSettingsTabViewModel : Conductor.Collection.AllActive
{
private readonly ISettingsVmFactory _settingsVmFactory;
- private readonly ISurfaceService _surfaceService;
+ private readonly IRgbService _rgbService;
private readonly IDialogService _dialogService;
private bool _confirmedDisable;
- public DeviceSettingsTabViewModel(ISurfaceService surfaceService, IDialogService dialogService, ISettingsVmFactory settingsVmFactory)
+ public DeviceSettingsTabViewModel(IRgbService rgbService, IDialogService dialogService, ISettingsVmFactory settingsVmFactory)
{
DisplayName = "DEVICES";
- _surfaceService = surfaceService;
+ _rgbService = rgbService;
_dialogService = dialogService;
_settingsVmFactory = settingsVmFactory;
}
@@ -32,7 +32,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
Items.Clear();
await Task.Delay(200);
- List instances = _surfaceService.ActiveSurface.Devices.Select(d => _settingsVmFactory.CreateDeviceSettingsViewModel(d)).ToList();
+ List instances = _rgbService.Devices.Select(d => _settingsVmFactory.CreateDeviceSettingsViewModel(d)).ToList();
foreach (DeviceSettingsViewModel deviceSettingsViewModel in instances)
Items.Add(deviceSettingsViewModel);
});
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsView.xaml b/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsView.xaml
index 8b4be68fa..ced4c9131 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsView.xaml
+++ b/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsView.xaml
@@ -76,7 +76,7 @@
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsViewModel.cs
index 4ae2e45e2..f9de3cfe3 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsViewModel.cs
@@ -6,7 +6,6 @@ using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.SurfaceEditor.Dialogs;
-using Artemis.UI.Screens.SurfaceEditor.Visualization;
using Artemis.UI.Shared.Services;
using Humanizer;
using RGB.NET.Core;
@@ -17,9 +16,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
public class DeviceSettingsViewModel : Screen
{
private readonly IDeviceDebugVmFactory _deviceDebugVmFactory;
- private readonly ISurfaceService _surfaceService;
private readonly IDeviceService _deviceService;
private readonly IDialogService _dialogService;
+ private readonly IRgbService _rgbService;
private readonly IWindowManager _windowManager;
public DeviceSettingsViewModel(ArtemisDevice device,
@@ -27,13 +26,13 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
IDialogService dialogService,
IWindowManager windowManager,
IDeviceDebugVmFactory deviceDebugVmFactory,
- ISurfaceService surfaceService)
+ IRgbService rgbService)
{
_deviceService = deviceService;
_dialogService = dialogService;
_windowManager = windowManager;
_deviceDebugVmFactory = deviceDebugVmFactory;
- _surfaceService = surfaceService;
+ _rgbService = rgbService;
Device = device;
Type = Device.RgbDevice.DeviceInfo.DeviceType.ToString().Humanize();
@@ -53,26 +52,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
public bool IsDeviceEnabled
{
get => Device.IsEnabled;
- set
- {
- Task.Run(() => UpdateIsDeviceEnabled(value));
-
- }
- }
-
- private async Task UpdateIsDeviceEnabled(bool value)
- {
- if (!value)
- value = !await ((DeviceSettingsTabViewModel)Parent).ShowDeviceDisableDialog();
-
- Device.IsEnabled = value;
- NotifyOfPropertyChange(nameof(IsDeviceEnabled));
- SaveDevice();
- }
-
- private void SaveDevice()
- {
- _surfaceService.UpdateSurfaceConfiguration(_surfaceService.ActiveSurface, true);
+ set { Task.Run(() => UpdateIsDeviceEnabled(value)); }
}
public void IdentifyDevice()
@@ -104,7 +84,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
);
if ((bool) madeChanges)
- _surfaceService.UpdateSurfaceConfiguration(_surfaceService.ActiveSurface, true);
+ _rgbService.SaveDevice(Device);
}
public async Task ViewProperties()
@@ -114,7 +94,25 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
);
if ((bool) madeChanges)
- _surfaceService.UpdateSurfaceConfiguration(_surfaceService.ActiveSurface, true);
+ _rgbService.SaveDevice(Device);
+ }
+
+ private async Task UpdateIsDeviceEnabled(bool value)
+ {
+ if (!value)
+ value = !await ((DeviceSettingsTabViewModel) Parent).ShowDeviceDisableDialog();
+
+ if (value)
+ _rgbService.EnableDevice(Device);
+ else
+ _rgbService.DisableDevice(Device);
+ NotifyOfPropertyChange(nameof(IsDeviceEnabled));
+ SaveDevice();
+ }
+
+ private void SaveDevice()
+ {
+ _rgbService.SaveDevice(Device);
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs
index 57f54847c..5d17a2c72 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs
@@ -57,6 +57,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
// Take it off the UI thread to avoid freezing on tab change
Task.Run(async () =>
{
+ Items.Clear();
await Task.Delay(200);
_instances = _pluginManagementService.GetAllPlugins()
.Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p))
diff --git a/src/Artemis.UI/Screens/Shared/PanZoomViewModel.cs b/src/Artemis.UI/Screens/Shared/PanZoomViewModel.cs
index e751e5e51..d1e3d88fe 100644
--- a/src/Artemis.UI/Screens/Shared/PanZoomViewModel.cs
+++ b/src/Artemis.UI/Screens/Shared/PanZoomViewModel.cs
@@ -5,6 +5,7 @@ using System.Windows.Input;
using System.Windows.Media;
using Artemis.UI.Extensions;
using RGB.NET.Core;
+using SkiaSharp;
using Stylet;
using Point = System.Windows.Point;
@@ -144,6 +145,13 @@ namespace Artemis.UI.Screens.Shared
PanY = 0;
}
+ public void Reset(SKRect rect)
+ {
+ Zoom = Math.Min(CanvasWidth / rect.Width, CanvasHeight / rect.Height);
+ PanX = rect.Left * -1 * Zoom;
+ PanY = rect.Top * -1 * Zoom;
+ }
+
public Rect TransformContainingRect(Rectangle rect)
{
return TransformContainingRect(rect.ToWindowsRect(1));
@@ -168,11 +176,15 @@ namespace Artemis.UI.Screens.Shared
private void SetZoomFromPercentage(double value)
{
- double newZoom = value / 100;
- // Focus towards the center of the zoomed area
- PanX += newZoom - Zoom;
- PanY += newZoom - Zoom;
+ Point relative = new((PanX * -1 + CanvasWidth / 2) / Zoom, (PanY * -1 + CanvasHeight / 2) / Zoom);
+ double absoluteX = relative.X * Zoom + PanX;
+ double absoluteY = relative.Y * Zoom + PanY;
+
Zoom = value / 100;
+
+ // Focus towards the center of the zoomed area
+ PanX = absoluteX - relative.X * Zoom;
+ PanY = absoluteY - relative.Y * Zoom;
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/DevicesStepView.xaml b/src/Artemis.UI/Screens/StartupWizard/Steps/DevicesStepView.xaml
index bd9c7ddc2..df4f7c13f 100644
--- a/src/Artemis.UI/Screens/StartupWizard/Steps/DevicesStepView.xaml
+++ b/src/Artemis.UI/Screens/StartupWizard/Steps/DevicesStepView.xaml
@@ -16,7 +16,7 @@
- Devices are supported through the use of device providers.
+ EnabledDevices are supported through the use of device providers.
In the list below you can enable device providers for each brand you own by checking .
diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStepViewModel.cs b/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStepViewModel.cs
index dac1ba898..4977b25b2 100644
--- a/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStepViewModel.cs
+++ b/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStepViewModel.cs
@@ -5,23 +5,23 @@ namespace Artemis.UI.Screens.StartupWizard.Steps
{
public class LayoutStepViewModel : Screen
{
- private readonly ISurfaceService _surfaceService;
+ private readonly IRgbService _rgbService;
- public LayoutStepViewModel(ISurfaceService surfaceService)
+ public LayoutStepViewModel(IRgbService rgbService)
{
- _surfaceService = surfaceService;
+ _rgbService = rgbService;
}
public void ApplyLeftHandedPreset()
{
- _surfaceService.AutoArrange();
+ _rgbService.AutoArrangeDevices();
StartupWizardViewModel startupWizardViewModel = (StartupWizardViewModel) Parent;
startupWizardViewModel.Continue();
}
public void ApplyRightHandedPreset()
{
- _surfaceService.AutoArrange();
+ _rgbService.AutoArrangeDevices();
StartupWizardViewModel startupWizardViewModel = (StartupWizardViewModel) Parent;
startupWizardViewModel.Continue();
}
diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigView.xaml b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigView.xaml
index 52b34bab4..561b259f7 100644
--- a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigView.xaml
+++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigView.xaml
@@ -155,12 +155,39 @@
+ Color="{Binding CurrentColor, Converter={StaticResource SKColorToColorConverter}}"
+ VerticalAlignment="Center" />
+
+
+ Custom layout
+
+
+ Select a custom layout below if you want to change the appearance and/or LEDs of this device.
+
+
+
+
+
+
+
+ Layout path
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs
index 8d96c2147..ad06f0ca0 100644
--- a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs
+++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs
@@ -1,7 +1,12 @@
-using System.Threading.Tasks;
+using System.ComponentModel;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows.Input;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared.Services;
+using MaterialDesignThemes.Wpf;
+using Ookii.Dialogs.Wpf;
using SkiaSharp;
using Stylet;
@@ -10,6 +15,8 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
public class SurfaceDeviceConfigViewModel : DialogViewModelBase
{
private readonly ICoreService _coreService;
+ private readonly IRgbService _rgbService;
+ private readonly IMessageService _messageService;
private readonly double _initialRedScale;
private readonly double _initialGreenScale;
private readonly double _initialBlueScale;
@@ -23,41 +30,43 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
private SKColor _currentColor;
private bool _displayOnDevices;
- public SurfaceDeviceConfigViewModel(ArtemisDevice device, ICoreService coreService, IModelValidator validator) : base(validator)
+ public SurfaceDeviceConfigViewModel(ArtemisDevice device,
+ ICoreService coreService,
+ IRgbService rgbService,
+ IMessageService messageService,
+ IModelValidator validator) : base(validator)
{
_coreService = coreService;
+ _rgbService = rgbService;
+ _messageService = messageService;
Device = device;
- X = (int)Device.X;
- Y = (int)Device.Y;
+ X = (int) Device.X;
+ Y = (int) Device.Y;
Scale = Device.Scale;
- Rotation = (int)Device.Rotation;
+ Rotation = (int) Device.Rotation;
RedScale = Device.RedScale * 100d;
GreenScale = Device.GreenScale * 100d;
BlueScale = Device.BlueScale * 100d;
//we need to store the initial values to be able to restore them when the user clicks "Cancel"
- _initialRedScale = Device.RedScale;
+ _initialRedScale = Device.RedScale;
_initialGreenScale = Device.GreenScale;
_initialBlueScale = Device.BlueScale;
CurrentColor = SKColors.White;
_coreService.FrameRendering += OnFrameRendering;
- }
-
- private void OnFrameRendering(object sender, FrameRenderingEventArgs e)
- {
- if (!_displayOnDevices)
- return;
-
- using SKPaint overlayPaint = new()
- {
- Color = CurrentColor
- };
- e.Canvas.DrawRect(0, 0, e.Canvas.LocalClipBounds.Width, e.Canvas.LocalClipBounds.Height, overlayPaint);
+ Device.PropertyChanged += DeviceOnPropertyChanged;
}
public ArtemisDevice Device { get; }
+ public override void OnDialogClosed(object sender, DialogClosingEventArgs e)
+ {
+ _coreService.FrameRendering -= OnFrameRendering;
+ Device.PropertyChanged -= DeviceOnPropertyChanged;
+ base.OnDialogClosed(sender, e);
+ }
+
public int X
{
get => _x;
@@ -130,7 +139,6 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
Device.BlueScale = BlueScale / 100d;
_coreService.ModuleRenderingDisabled = false;
- _coreService.FrameRendering -= OnFrameRendering;
Session.Close(true);
}
@@ -141,6 +149,26 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
Device.BlueScale = BlueScale / 100d;
}
+ public void BrowseCustomLayout(object sender, MouseEventArgs e)
+ {
+ if (e.OriginalSource is Button)
+ {
+ Device.CustomLayoutPath = null;
+ _messageService.ShowMessage("Cleared imported layout");
+ return;
+ }
+
+ VistaOpenFileDialog dialog = new();
+ dialog.Filter = "Layout files (*.xml)|*.xml";
+ dialog.Title = "Select device layout file";
+ bool? result = dialog.ShowDialog();
+ if (result == true)
+ {
+ Device.CustomLayoutPath = dialog.FileName;
+ _messageService.ShowMessage($"Imported layout from {dialog.FileName}");
+ }
+ }
+
public override void Cancel()
{
Device.RedScale = _initialRedScale;
@@ -149,5 +177,25 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
base.Cancel();
}
+
+ private void DeviceOnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Device.CustomLayoutPath))
+ {
+ _rgbService.ApplyBestDeviceLayout(Device);
+ }
+ }
+
+ private void OnFrameRendering(object sender, FrameRenderingEventArgs e)
+ {
+ if (!_displayOnDevices)
+ return;
+
+ using SKPaint overlayPaint = new()
+ {
+ Color = CurrentColor
+ };
+ e.Canvas.DrawRect(0, 0, e.Canvas.LocalClipBounds.Width, e.Canvas.LocalClipBounds.Height, overlayPaint);
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputViewModel.cs
index a3c7d41f9..07f04fce9 100644
--- a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputViewModel.cs
+++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputViewModel.cs
@@ -14,9 +14,10 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
{
private readonly IInputService _inputService;
private readonly IMessageService _messageService;
+ private readonly IRgbService _rgbService;
private readonly ListLedGroup _ledGroup;
- public SurfaceDeviceDetectInputViewModel(ArtemisDevice device, IInputService inputService, IMessageService messageService)
+ public SurfaceDeviceDetectInputViewModel(ArtemisDevice device, IInputService inputService, IMessageService messageService, IRgbService rgbService)
{
Device = device;
Title = $"{Device.RgbDevice.DeviceInfo.DeviceName} - Detect input";
@@ -24,11 +25,12 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
_inputService = inputService;
_messageService = messageService;
+ _rgbService = rgbService;
_inputService.IdentifyDevice(Device);
_inputService.DeviceIdentified += InputServiceOnDeviceIdentified;
// Create a LED group way at the top
- _ledGroup = new ListLedGroup(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
@@ -43,7 +45,7 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
{
base.OnDialogClosed(sender, e);
_inputService.DeviceIdentified -= InputServiceOnDeviceIdentified;
- _ledGroup.Detach();
+ _ledGroup.Detach(_rgbService.Surface);
}
private void InputServiceOnDeviceIdentified(object sender, EventArgs e)
diff --git a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.xaml b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.xaml
index bf909b5d2..6cbafa329 100644
--- a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.xaml
+++ b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.xaml
@@ -5,11 +5,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
- xmlns:Converters="clr-namespace:Artemis.UI.Converters"
- xmlns:models="clr-namespace:Artemis.Core;assembly=Artemis.Core"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
+ xmlns:local="clr-namespace:Artemis.UI.Screens.SurfaceEditor"
x:Class="Artemis.UI.Screens.SurfaceEditor.SurfaceEditorView"
mc:Ignorable="d"
- d:DesignHeight="600" d:DesignWidth="600">
+ d:DesignHeight="600" d:DesignWidth="600"
+ d:DataContext="{d:DesignInstance {x:Type local:SurfaceEditorViewModel}}">
@@ -18,7 +19,6 @@
-
@@ -113,14 +113,17 @@
-
+
-
+
@@ -137,28 +140,28 @@
-