mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Devices - Added custom layout loading
This commit is contained in:
1 parent
056b2bface
commit
b154badd9c
21 files changed
+414
-110
No files matched your search
@@ -249,6 +249,20 @@ namespace Artemis.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path of the custom layout to load when calling <see cref="IRgbService.ApplyBestDeviceLayout" />
|
||||
/// for this device
|
||||
/// </summary>
|
||||
public string? CustomLayoutPath
|
||||
{
|
||||
get => DeviceEntity.CustomLayoutPath;
|
||||
set
|
||||
{
|
||||
DeviceEntity.CustomLayoutPath = value;
|
||||
OnPropertyChanged(nameof(CustomLayoutPath));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the layout of the device expanded with Artemis-specific data
|
||||
/// </summary>
|
||||
@@ -304,10 +318,12 @@ namespace Artemis.Core
|
||||
/// Applies the provided layout to the device
|
||||
/// </summary>
|
||||
/// <param name="layout">The layout to apply</param>
|
||||
internal void ApplyLayout(ArtemisLayout layout)
|
||||
/// <param name="createMissingLeds">A boolean indicating whether to add missing LEDs defined in the layout but missing on the device</param>
|
||||
/// <param name="removeExcessiveLeds">A boolean indicating whether to remove excess LEDs present in the device but missing in the layout</param>
|
||||
internal void ApplyLayout(ArtemisLayout layout, bool createMissingLeds, bool removeExcessiveLeds)
|
||||
{
|
||||
if (layout.IsValid)
|
||||
layout.RgbLayout!.ApplyTo(RgbDevice);
|
||||
layout.RgbLayout!.ApplyTo(RgbDevice, createMissingLeds, removeExcessiveLeds);
|
||||
|
||||
Leds = RgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
|
||||
LedIds = new ReadOnlyDictionary<LedId, ArtemisLed>(Leds.ToDictionary(l => l.RgbLed.Id, l => l));
|
||||
|
||||
@@ -15,9 +15,11 @@ namespace Artemis.Core
|
||||
/// Creates a new instance of the <see cref="ArtemisLayout" /> class
|
||||
/// </summary>
|
||||
/// <param name="filePath">The path of the layout XML file</param>
|
||||
public ArtemisLayout(string filePath)
|
||||
/// <param name="source">The source from where this layout is being loaded</param>
|
||||
public ArtemisLayout(string filePath, LayoutSource source)
|
||||
{
|
||||
FilePath = filePath;
|
||||
Source = source;
|
||||
Leds = new List<ArtemisLedLayout>();
|
||||
|
||||
LoadLayout();
|
||||
@@ -29,9 +31,9 @@ namespace Artemis.Core
|
||||
public string FilePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RGB.NET device layout
|
||||
/// Gets the source from where this layout was loaded
|
||||
/// </summary>
|
||||
public DeviceLayout RgbLayout { get; private set; }
|
||||
public LayoutSource Source { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device this layout is applied to
|
||||
@@ -48,9 +50,20 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public Uri? Image { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of LEDs this layout contains
|
||||
/// </summary>
|
||||
public List<ArtemisLedLayout> Leds { get; }
|
||||
|
||||
internal LayoutCustomDeviceData LayoutCustomDeviceData { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the RGB.NET device layout
|
||||
/// </summary>
|
||||
public DeviceLayout RgbLayout { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom layout data embedded in the RGB.NET layout
|
||||
/// </summary>
|
||||
public LayoutCustomDeviceData LayoutCustomDeviceData { get; private set; } = null!;
|
||||
|
||||
public void ReloadFromDisk()
|
||||
{
|
||||
@@ -88,11 +101,43 @@ namespace Artemis.Core
|
||||
|
||||
private void ApplyCustomDeviceData()
|
||||
{
|
||||
Uri layoutDirectory = new(Path.GetDirectoryName(FilePath)! + "\\", UriKind.Absolute);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a source from where a layout came
|
||||
/// </summary>
|
||||
public enum LayoutSource
|
||||
{
|
||||
/// <summary>
|
||||
/// A layout loaded from config
|
||||
/// </summary>
|
||||
Configured,
|
||||
|
||||
/// <summary>
|
||||
/// A layout loaded from the user layout folder
|
||||
/// </summary>
|
||||
User,
|
||||
|
||||
/// <summary>
|
||||
/// A layout loaded from the plugin folder
|
||||
/// </summary>
|
||||
Plugin,
|
||||
|
||||
/// <summary>
|
||||
/// A default layout loaded as a fallback option
|
||||
/// </summary>
|
||||
Default
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,10 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public Uri? Image { get; private set; }
|
||||
|
||||
internal LayoutCustomLedData LayoutCustomLedData { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the custom layout data embedded in the RGB.NET layout
|
||||
/// </summary>
|
||||
public LayoutCustomLedData LayoutCustomLedData { get; }
|
||||
|
||||
internal void ApplyDevice(ArtemisDevice device)
|
||||
{
|
||||
|
||||
@@ -60,18 +60,35 @@ namespace Artemis.Core.DeviceProviders
|
||||
/// <summary>
|
||||
/// Loads a layout for the specified device and wraps it in an <see cref="ArtemisLayout" />
|
||||
/// </summary>
|
||||
/// <param name="rgbDevice">The device to load the layout for</param>
|
||||
/// <param name="device">The device to load the layout for</param>
|
||||
/// <returns>The resulting Artemis layout</returns>
|
||||
public virtual ArtemisLayout LoadLayout(ArtemisDevice rgbDevice)
|
||||
public virtual ArtemisLayout LoadLayout(ArtemisDevice device)
|
||||
{
|
||||
string layoutDir = Path.Combine(Plugin.Directory.FullName, "Layouts");
|
||||
string filePath = Path.Combine(
|
||||
layoutDir,
|
||||
rgbDevice.RgbDevice.DeviceInfo.Manufacturer,
|
||||
rgbDevice.RgbDevice.DeviceInfo.DeviceType.ToString(),
|
||||
rgbDevice.GetLayoutFileName()
|
||||
device.RgbDevice.DeviceInfo.Manufacturer,
|
||||
device.RgbDevice.DeviceInfo.DeviceType.ToString(),
|
||||
device.GetLayoutFileName()
|
||||
);
|
||||
return new ArtemisLayout(filePath);
|
||||
return new ArtemisLayout(filePath, LayoutSource.Plugin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a layout from the user layout folder for the specified device and wraps it in an <see cref="ArtemisLayout" />
|
||||
/// </summary>
|
||||
/// <param name="device">The device to load the layout for</param>
|
||||
/// <returns>The resulting Artemis layout</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -74,7 +74,9 @@ namespace Artemis.Core.Services
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
/// <param name="layout"></param>
|
||||
void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout);
|
||||
/// <param name="createMissingLeds"></param>
|
||||
/// <param name="removeExessiveLeds"></param>
|
||||
void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the <see cref="ArtemisDevice" /> that corresponds the provided RGB.NET
|
||||
|
||||
@@ -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<DeviceProvider>().First(d => d.RgbDeviceProvider.Devices != null && d.RgbDeviceProvider.Devices.Contains(rgbDevice));
|
||||
return GetFeaturesOfType<DeviceProvider>().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();
|
||||
@@ -360,11 +356,9 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
_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);
|
||||
}
|
||||
|
||||
@@ -191,26 +191,53 @@ namespace Artemis.Core.Services
|
||||
|
||||
public ArtemisLayout ApplyBestDeviceLayout(ArtemisDevice device)
|
||||
{
|
||||
// Look for a user layout
|
||||
// ... here
|
||||
ArtemisLayout layout;
|
||||
|
||||
// Try loading a device provider layout, if that comes back valid we use that
|
||||
ArtemisLayout layout = device.DeviceProvider.LoadLayout(device);
|
||||
// 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
|
||||
// .. do it!
|
||||
|
||||
ApplyDeviceLayout(device, layout);
|
||||
layout = LoadDefaultLayout(device);
|
||||
ApplyDeviceLayout(device, layout, true, true);
|
||||
return layout;
|
||||
}
|
||||
|
||||
public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout)
|
||||
private ArtemisLayout LoadDefaultLayout(ArtemisDevice device)
|
||||
{
|
||||
device.ApplyLayout(layout);
|
||||
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);
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
public static void PrepareFirstLaunch()
|
||||
{
|
||||
CreateArtemisFolderIfMissing(Constants.DataFolder);
|
||||
CreateArtemisFolderIfMissing(Constants.DataFolder + "plugins");
|
||||
CreateAccessibleDirectory(Constants.DataFolder);
|
||||
CreateAccessibleDirectory(Constants.DataFolder + "plugins");
|
||||
CreateAccessibleDirectory(Constants.DataFolder + "user layouts");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -62,15 +62,11 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current application location
|
||||
/// Creates all directories and subdirectories in the specified path unless they already exist with permissions
|
||||
/// allowing access by everyone.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal static string GetCurrentLocation()
|
||||
{
|
||||
return Process.GetCurrentProcess().MainModule!.FileName!;
|
||||
}
|
||||
|
||||
private static void CreateArtemisFolderIfMissing(string path)
|
||||
/// <param name="path">The directory to create.</param>
|
||||
public static void CreateAccessibleDirectory(string path)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
@@ -92,6 +88,15 @@ namespace Artemis.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current application location
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal static string GetCurrentLocation()
|
||||
{
|
||||
return Process.GetCurrentProcess().MainModule!.FileName!;
|
||||
}
|
||||
|
||||
private static void OnRestartRequested(RestartEventArgs e)
|
||||
{
|
||||
RestartRequested?.Invoke(null, e);
|
||||
|
||||
Reference in new issue
Block a user