diff --git a/src/Artemis.ConsoleUI/packages.lock.json b/src/Artemis.ConsoleUI/packages.lock.json index bbb02c9a2..feeaa74a8 100644 --- a/src/Artemis.ConsoleUI/packages.lock.json +++ b/src/Artemis.ConsoleUI/packages.lock.json @@ -313,13 +313,10 @@ }, "Serilog.Sinks.Console": { "type": "Transitive", - "resolved": "3.1.1", - "contentHash": "56mI5AqvyF/i/c2451nvV71kq370XOCE4Uu5qiaJ295sOhMb9q3BWwG7mWLOVSnmpWiq0SBT3SXfgRXGNP6vzA==", + "resolved": "4.0.0", + "contentHash": "yJQit9sTJ4xGLKgCujqDJsaGqBNJwGB/H898z+xYlMG06twy4//6LLnSrsmpduZxcHIG4im7cv+JmXLzXz2EkQ==", "dependencies": { - "Serilog": "2.5.0", - "System.Console": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0" + "Serilog": "2.10.0" } }, "Serilog.Sinks.Debug": { @@ -332,19 +329,16 @@ }, "Serilog.Sinks.File": { "type": "Transitive", - "resolved": "4.1.0", - "contentHash": "U0b34w+ZikbqWEZ3ui7BdzxY/19zwrdhLtI3o6tfmLdD3oXxg7n2TZJjwCCTlKPgRuYic9CBWfrZevbb70mTaw==", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", "dependencies": { - "Serilog": "2.5.0", - "System.IO.FileSystem": "4.0.1", - "System.Text.Encoding.Extensions": "4.0.11", - "System.Threading.Timer": "4.0.1" + "Serilog": "2.10.0" } }, "SkiaSharp": { "type": "Transitive", - "resolved": "2.80.2", - "contentHash": "D25rzdCwh+3L+XyXqpNa+H/yiLJbE3/R3K/XexwHyQjGdzZvSufFW3oqf3En7hhqSIsxsJ8f5NEZ0J5W5wlGBg==", + "resolved": "2.80.3", + "contentHash": "qX6tGNP3+MXNYe2pKm0PCRiJ/cx+LTeLaggwZifB7sUMXhECfKKKHJq45VqZKt37xQegnCCdf1jHXwmHeJQs5Q==", "dependencies": { "System.Memory": "4.5.3" } @@ -1230,10 +1224,10 @@ "Ninject.Extensions.ChildKernel": "3.3.0", "Ninject.Extensions.Conventions": "3.3.0", "Serilog": "2.10.0", - "Serilog.Sinks.Console": "3.1.1", + "Serilog.Sinks.Console": "4.0.0", "Serilog.Sinks.Debug": "2.0.0", - "Serilog.Sinks.File": "4.1.0", - "SkiaSharp": "2.80.2", + "Serilog.Sinks.File": "5.0.0", + "SkiaSharp": "2.80.3", "System.Buffers": "4.5.1", "System.IO.FileSystem.AccessControl": "5.0.0", "System.Numerics.Vectors": "4.5.0", diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index 6e416d8dd..a8b6dd96c 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -48,10 +48,10 @@ - + - - + + diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs index e978e8598..7921af0bc 100644 --- a/src/Artemis.Core/Constants.cs +++ b/src/Artemis.Core/Constants.cs @@ -27,7 +27,7 @@ namespace Artemis.Core /// /// The full path to the Artemis data folder /// - public static readonly string DataFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\Artemis\\"; + public static readonly string DataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Artemis"); /// /// The plugin info used by core components of Artemis diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs index 8d46099e0..245b9eeb1 100644 --- a/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs +++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs @@ -169,29 +169,29 @@ namespace Artemis.Core internal static ArtemisLayout? GetDefaultLayout(ArtemisDevice device) { - string layoutFolder = Path.Combine(Constants.ApplicationFolder, "DefaultLayouts\\Artemis"); + string layoutFolder = Path.Combine(Constants.ApplicationFolder, "DefaultLayouts", "Artemis"); if (device.DeviceType == RGBDeviceType.Keyboard) { // XL layout is defined by its programmable macro keys if (device.Leds.Any(l => l.RgbLed.Id >= LedId.Keyboard_Programmable1 && l.RgbLed.Id <= LedId.Keyboard_Programmable32)) { if (device.PhysicalLayout == KeyboardLayoutType.ANSI) - return new ArtemisLayout(layoutFolder + "\\Keyboard\\Artemis XL keyboard-ANSI.xml", LayoutSource.Default); - return new ArtemisLayout(layoutFolder + "\\Keyboard\\Artemis XL keyboard-ISO.xml", LayoutSource.Default); + return new ArtemisLayout(Path.Combine(layoutFolder, "Keyboard", "Artemis XL keyboard-ANSI.xml"), LayoutSource.Default); + return new ArtemisLayout(Path.Combine(layoutFolder, "Keyboard", "Artemis XL keyboard-ISO.xml"), LayoutSource.Default); } // L layout is defined by its numpad if (device.Leds.Any(l => l.RgbLed.Id >= LedId.Keyboard_NumLock && l.RgbLed.Id <= LedId.Keyboard_NumPeriodAndDelete)) { if (device.PhysicalLayout == KeyboardLayoutType.ANSI) - return new ArtemisLayout(layoutFolder + "\\Keyboard\\Artemis L keyboard-ANSI.xml", LayoutSource.Default); - return new ArtemisLayout(layoutFolder + "\\Keyboard\\Artemis L keyboard-ISO.xml", LayoutSource.Default); + return new ArtemisLayout(Path.Combine(layoutFolder + "Keyboard","Artemis L keyboard-ANSI.xml"), LayoutSource.Default); + return new ArtemisLayout(Path.Combine(layoutFolder + "Keyboard","Artemis L keyboard-ISO.xml"), LayoutSource.Default); } // No numpad will result in TKL if (device.PhysicalLayout == KeyboardLayoutType.ANSI) - return new ArtemisLayout(layoutFolder + "\\Keyboard\\Artemis TKL keyboard-ANSI.xml", LayoutSource.Default); - return new ArtemisLayout(layoutFolder + "\\Keyboard\\Artemis TKL keyboard-ISO.xml", LayoutSource.Default); + return new ArtemisLayout(Path.Combine(layoutFolder + "Keyboard","Artemis TKL keyboard-ANSI.xml"), LayoutSource.Default); + return new ArtemisLayout(Path.Combine(layoutFolder + "Keyboard","Artemis TKL keyboard-ISO.xml"), LayoutSource.Default); } // if (device.DeviceType == RGBDeviceType.Mouse) @@ -199,21 +199,21 @@ namespace Artemis.Core // if (device.Leds.Count == 1) // { // if (device.Leds.Any(l => l.RgbLed.Id == LedId.Logo)) - // return new ArtemisLayout(layoutFolder + "\\Mouse\\1 LED mouse logo.xml", LayoutSource.Default); - // return new ArtemisLayout(layoutFolder + "\\Mouse\\1 LED mouse.xml", LayoutSource.Default); + // return new ArtemisLayout(Path.Combine(layoutFolder + "Mouse", "1 LED mouse logo.xml"), LayoutSource.Default); + // return new ArtemisLayout(Path.Combine(layoutFolder + "Mouse", "1 LED mouse.xml"), LayoutSource.Default); // } // if (device.Leds.Any(l => l.RgbLed.Id == LedId.Logo)) - // return new ArtemisLayout(layoutFolder + "\\Mouse\\4 LED mouse logo.xml", LayoutSource.Default); - // return new ArtemisLayout(layoutFolder + "\\Mouse\\4 LED mouse.xml", LayoutSource.Default); + // return new ArtemisLayout(Path.Combine(layoutFolder + "Mouse", "4 LED mouse logo.xml"), LayoutSource.Default); + // return new ArtemisLayout(Path.Combine(layoutFolder + "Mouse", "4 LED mouse.xml"), LayoutSource.Default); // } if (device.DeviceType == RGBDeviceType.Headset) { if (device.Leds.Count == 1) - return new ArtemisLayout(layoutFolder + "\\Headset\\Artemis 1 LED headset.xml", LayoutSource.Default); + return new ArtemisLayout(Path.Combine(layoutFolder + "Headset", "Artemis 1 LED headset.xml"), LayoutSource.Default); if (device.Leds.Count == 2) - return new ArtemisLayout(layoutFolder + "\\Headset\\Artemis 2 LED headset.xml", LayoutSource.Default); - return new ArtemisLayout(layoutFolder + "\\Headset\\Artemis 4 LED headset.xml", LayoutSource.Default); + return new ArtemisLayout(Path.Combine(layoutFolder + "Headset", "Artemis 2 LED headset.xml"), LayoutSource.Default); + return new ArtemisLayout(Path.Combine(layoutFolder + "Headset", "Artemis 4 LED headset.xml"), LayoutSource.Default); } return null; diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs index 838c62881..01fcce12d 100644 --- a/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs +++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs @@ -61,7 +61,6 @@ namespace Artemis.Core 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) @@ -69,7 +68,7 @@ namespace Artemis.Core .First(); LogicalName = logicalLayout.Name; - Image = new Uri(layoutDirectory, logicalLayout.Image); + Image = new Uri(Path.Combine(Path.GetDirectoryName(DeviceLayout.FilePath)!, logicalLayout.Image!), UriKind.Absolute); } } } \ No newline at end of file diff --git a/src/Artemis.Core/Ninject/LoggerProvider.cs b/src/Artemis.Core/Ninject/LoggerProvider.cs index c39f7bace..64f5efcf3 100644 --- a/src/Artemis.Core/Ninject/LoggerProvider.cs +++ b/src/Artemis.Core/Ninject/LoggerProvider.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Ninject.Activation; using Serilog; using Serilog.Core; @@ -12,7 +13,7 @@ namespace Artemis.Core.Ninject private static readonly ILogger Logger = new LoggerConfiguration() .Enrich.FromLogContext() - .WriteTo.File(Constants.DataFolder + "logs/Artemis log-.log", + .WriteTo.File(Path.Combine(Constants.DataFolder, "logs", "Artemis log-.log"), rollingInterval: RollingInterval.Day, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}") .WriteTo.Console() diff --git a/src/Artemis.Core/Plugins/PluginFeature.cs b/src/Artemis.Core/Plugins/PluginFeature.cs index 983f20438..149d42632 100644 --- a/src/Artemis.Core/Plugins/PluginFeature.cs +++ b/src/Artemis.Core/Plugins/PluginFeature.cs @@ -11,8 +11,8 @@ namespace Artemis.Core public abstract class PluginFeature : CorePropertyChanged, IDisposable { private bool _isEnabled; - private Exception? _loadException; - + + /// /// Gets the plugin feature info related to this feature /// @@ -37,14 +37,7 @@ namespace Artemis.Core internal set => SetAndNotify(ref _isEnabled, value); } - /// - /// Gets the exception thrown while loading - /// - public Exception? LoadException - { - get => _loadException; - internal set => SetAndNotify(ref _loadException, value); - } + internal int AutoEnableAttempts { get; set; } /// /// Gets the identifier of this plugin feature @@ -146,13 +139,16 @@ namespace Artemis.Core try { + if (isAutoEnable) + AutoEnableAttempts++; + if (isAutoEnable && GetLockFileCreated()) { // Don't wrap existing lock exceptions, simply rethrow them - if (LoadException is ArtemisPluginLockException) - throw LoadException; + if (Info.LoadException is ArtemisPluginLockException) + throw Info.LoadException; - throw new ArtemisPluginLockException(LoadException); + throw new ArtemisPluginLockException(Info.LoadException); } CreateLockFile(); @@ -165,21 +161,22 @@ namespace Artemis.Core if (!enableTask.Wait(TimeSpan.FromSeconds(15))) throw new ArtemisPluginException(Plugin, "Plugin load timeout"); - LoadException = null; + Info.LoadException = null; + AutoEnableAttempts = 0; OnEnabled(); } // If enable failed, put it back in a disabled state catch (Exception e) { IsEnabled = false; - LoadException = e; + Info.LoadException = e; throw; } finally { // Clean up the lock file unless the failure was due to the lock file // After all, we failed but not miserably :) - if (!(LoadException is ArtemisPluginLockException)) + if (Info.LoadException is not ArtemisPluginLockException) DeleteLockFile(); } } diff --git a/src/Artemis.Core/Plugins/PluginFeatureInfo.cs b/src/Artemis.Core/Plugins/PluginFeatureInfo.cs index 4947ab20a..62b4a6c5c 100644 --- a/src/Artemis.Core/Plugins/PluginFeatureInfo.cs +++ b/src/Artemis.Core/Plugins/PluginFeatureInfo.cs @@ -17,6 +17,7 @@ namespace Artemis.Core [JsonObject(MemberSerialization.OptIn)] public class PluginFeatureInfo : CorePropertyChanged, IPrerequisitesSubject { + private Exception? _loadException; private string? _description; private string? _icon; private PluginFeature? _instance; @@ -80,6 +81,15 @@ namespace Artemis.Core /// public Type FeatureType { get; } + /// + /// Gets the exception thrown while loading + /// + public Exception? LoadException + { + get => _loadException; + internal set => SetAndNotify(ref _loadException, value); + } + /// /// The name of the plugin /// diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs index 00a74d2a5..5e4841829 100644 --- a/src/Artemis.Core/Services/PluginManagementService.cs +++ b/src/Artemis.Core/Services/PluginManagementService.cs @@ -5,6 +5,7 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using Artemis.Core.DeviceProviders; using Artemis.Core.Ninject; using Artemis.Storage.Entities.General; @@ -90,11 +91,6 @@ namespace Artemis.Core.Services using StreamReader reader = new(metaDataFileEntry.Open()); PluginInfo builtInPluginInfo = CoreJson.DeserializeObject(reader.ReadToEnd())!; string preferred = builtInPluginInfo.PreferredPluginDirectory; - string oldPreferred = Path.GetFileNameWithoutExtension(zipFile.Name); - // Rename folders to the new format - // TODO: Get rid of this eventually, it's nice to keep around but it's extra IO that's best avoided - if (pluginDirectory.EnumerateDirectories().FirstOrDefault(d => d.Name == oldPreferred) != null) - Directory.Move(Path.Combine(pluginDirectory.FullName, oldPreferred), Path.Combine(pluginDirectory.FullName, preferred)); // Find the matching plugin in the plugin folder DirectoryInfo? match = pluginDirectory.EnumerateDirectories().FirstOrDefault(d => d.Name == preferred); @@ -431,6 +427,7 @@ namespace Artemis.Core.Services catch (Exception e) { _logger.Warning(new ArtemisPluginException(plugin, "Failed to instantiate feature", e), "Failed to instantiate feature", plugin); + featureInfo.LoadException = e; } } @@ -614,10 +611,38 @@ namespace Artemis.Core.Services } catch (Exception e) { - _logger.Warning( - new ArtemisPluginException(pluginFeature.Plugin, $"Exception during SetEnabled(true) on {pluginFeature}", e), - "Failed to enable plugin" - ); + if (isAutoEnable) + { + // Schedule a retry based on the amount of attempts + if (pluginFeature.AutoEnableAttempts < 4) + { + TimeSpan retryDelay = TimeSpan.FromSeconds(pluginFeature.AutoEnableAttempts * 10); + _logger.Warning( + e, + "Plugin feature '{feature} - {plugin}' failed to enable during attempt ({attempt}/3), scheduling a retry in {retryDelay}.", + pluginFeature, + pluginFeature.Plugin, + pluginFeature.AutoEnableAttempts, + retryDelay + ); + + Task.Run(async () => + { + await Task.Delay(retryDelay); + if (!pluginFeature.IsEnabled) + EnablePluginFeature(pluginFeature, saveState, true); + }); + } + else + { + _logger.Warning(e, "Plugin feature '{feature} - {plugin}' failed to enable after 3 attempts, giving up.", pluginFeature, pluginFeature.Plugin); + } + } + else + { + _logger.Warning(e, "Plugin feature '{feature} - {plugin}' failed to enable.", pluginFeature, pluginFeature.Plugin); + throw; + } } finally { diff --git a/src/Artemis.Core/Utilities/Utilities.cs b/src/Artemis.Core/Utilities/Utilities.cs index 8577185bc..c55b07c93 100644 --- a/src/Artemis.Core/Utilities/Utilities.cs +++ b/src/Artemis.Core/Utilities/Utilities.cs @@ -18,8 +18,8 @@ namespace Artemis.Core public static void PrepareFirstLaunch() { CreateAccessibleDirectory(Constants.DataFolder); - CreateAccessibleDirectory(Constants.DataFolder + "plugins"); - CreateAccessibleDirectory(Constants.DataFolder + "user layouts"); + CreateAccessibleDirectory(Path.Combine(Constants.DataFolder ,"plugins")); + CreateAccessibleDirectory(Path.Combine(Constants.DataFolder ,"user layouts")); } /// diff --git a/src/Artemis.Core/packages.lock.json b/src/Artemis.Core/packages.lock.json index 3f8b70e19..ea7b5f80c 100644 --- a/src/Artemis.Core/packages.lock.json +++ b/src/Artemis.Core/packages.lock.json @@ -81,14 +81,11 @@ }, "Serilog.Sinks.Console": { "type": "Direct", - "requested": "[3.1.1, )", - "resolved": "3.1.1", - "contentHash": "56mI5AqvyF/i/c2451nvV71kq370XOCE4Uu5qiaJ295sOhMb9q3BWwG7mWLOVSnmpWiq0SBT3SXfgRXGNP6vzA==", + "requested": "[4.0.0, )", + "resolved": "4.0.0", + "contentHash": "yJQit9sTJ4xGLKgCujqDJsaGqBNJwGB/H898z+xYlMG06twy4//6LLnSrsmpduZxcHIG4im7cv+JmXLzXz2EkQ==", "dependencies": { - "Serilog": "2.5.0", - "System.Console": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0" + "Serilog": "2.10.0" } }, "Serilog.Sinks.Debug": { @@ -102,21 +99,18 @@ }, "Serilog.Sinks.File": { "type": "Direct", - "requested": "[4.1.0, )", - "resolved": "4.1.0", - "contentHash": "U0b34w+ZikbqWEZ3ui7BdzxY/19zwrdhLtI3o6tfmLdD3oXxg7n2TZJjwCCTlKPgRuYic9CBWfrZevbb70mTaw==", + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", "dependencies": { - "Serilog": "2.5.0", - "System.IO.FileSystem": "4.0.1", - "System.Text.Encoding.Extensions": "4.0.11", - "System.Threading.Timer": "4.0.1" + "Serilog": "2.10.0" } }, "SkiaSharp": { "type": "Direct", - "requested": "[2.80.2, )", - "resolved": "2.80.2", - "contentHash": "D25rzdCwh+3L+XyXqpNa+H/yiLJbE3/R3K/XexwHyQjGdzZvSufFW3oqf3En7hhqSIsxsJ8f5NEZ0J5W5wlGBg==", + "requested": "[2.80.3, )", + "resolved": "2.80.3", + "contentHash": "qX6tGNP3+MXNYe2pKm0PCRiJ/cx+LTeLaggwZifB7sUMXhECfKKKHJq45VqZKt37xQegnCCdf1jHXwmHeJQs5Q==", "dependencies": { "System.Memory": "4.5.3" } diff --git a/src/Artemis.Storage/StorageManager.cs b/src/Artemis.Storage/StorageManager.cs index 267f86fea..93775b16d 100644 --- a/src/Artemis.Storage/StorageManager.cs +++ b/src/Artemis.Storage/StorageManager.cs @@ -19,11 +19,11 @@ namespace Artemis.Storage if (_inUse) throw new Exception("Storage is already in use, can't backup now."); - string database = $"{dataFolder}\\database.db"; + string database = Path.Combine(dataFolder, "database.db"); if (!File.Exists(database)) return; - string backupFolder = $"{dataFolder}\\database backups"; + string backupFolder = Path.Combine(dataFolder, "database backups"); Directory.CreateDirectory(backupFolder); FileSystemInfo[] files = new DirectoryInfo(backupFolder).GetFileSystemInfos(); if (files.Length >= 5) @@ -36,7 +36,7 @@ namespace Artemis.Storage oldest.Delete(); } - File.Copy(database, $"{backupFolder}\\database-{DateTime.Now:yyyy-dd-M--HH-mm-ss}.db"); + File.Copy(database, Path.Combine(backupFolder, $"database-{DateTime.Now:yyyy-dd-M--HH-mm-ss}.db")); } /// @@ -51,7 +51,7 @@ namespace Artemis.Storage try { _inUse = true; - return new LiteRepository($"FileName={dataFolder}\\database.db"); + return new LiteRepository($"FileName={Path.Combine(dataFolder, "database.db")}"); } catch (LiteException e) { diff --git a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj index 33e955e6c..aebafad56 100644 --- a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj +++ b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj @@ -40,8 +40,8 @@ - - + + diff --git a/src/Artemis.UI.Shared/packages.lock.json b/src/Artemis.UI.Shared/packages.lock.json index f83560583..3ed7085b2 100644 --- a/src/Artemis.UI.Shared/packages.lock.json +++ b/src/Artemis.UI.Shared/packages.lock.json @@ -60,21 +60,21 @@ }, "SkiaSharp": { "type": "Direct", - "requested": "[2.80.2, )", - "resolved": "2.80.2", - "contentHash": "D25rzdCwh+3L+XyXqpNa+H/yiLJbE3/R3K/XexwHyQjGdzZvSufFW3oqf3En7hhqSIsxsJ8f5NEZ0J5W5wlGBg==", + "requested": "[2.80.3, )", + "resolved": "2.80.3", + "contentHash": "qX6tGNP3+MXNYe2pKm0PCRiJ/cx+LTeLaggwZifB7sUMXhECfKKKHJq45VqZKt37xQegnCCdf1jHXwmHeJQs5Q==", "dependencies": { "System.Memory": "4.5.3" } }, "SkiaSharp.Views.WPF": { "type": "Direct", - "requested": "[2.80.2, )", - "resolved": "2.80.2", - "contentHash": "Fzo2+MNwHDh9Cob8sk7OO26kp3bhofjXMwlEK8IncF1ehu9hi3sH9iQDJrue9a88VEJJ+yyLISPUFcmXlGHSyQ==", + "requested": "[2.80.3, )", + "resolved": "2.80.3", + "contentHash": "oKUMm7WzFeoie6rW5nnwSGKZ94misyRsAc1wU6SEqgd6ssW17nyfohHxHuBHtmpLtIRwvjhfAu3cMLrpX/oNcw==", "dependencies": { - "SkiaSharp": "2.80.2", - "SkiaSharp.Views.Desktop.Common": "2.80.2" + "SkiaSharp": "2.80.3", + "SkiaSharp.Views.Desktop.Common": "2.80.3" } }, "Stylet": { @@ -378,13 +378,10 @@ }, "Serilog.Sinks.Console": { "type": "Transitive", - "resolved": "3.1.1", - "contentHash": "56mI5AqvyF/i/c2451nvV71kq370XOCE4Uu5qiaJ295sOhMb9q3BWwG7mWLOVSnmpWiq0SBT3SXfgRXGNP6vzA==", + "resolved": "4.0.0", + "contentHash": "yJQit9sTJ4xGLKgCujqDJsaGqBNJwGB/H898z+xYlMG06twy4//6LLnSrsmpduZxcHIG4im7cv+JmXLzXz2EkQ==", "dependencies": { - "Serilog": "2.5.0", - "System.Console": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0" + "Serilog": "2.10.0" } }, "Serilog.Sinks.Debug": { @@ -397,21 +394,19 @@ }, "Serilog.Sinks.File": { "type": "Transitive", - "resolved": "4.1.0", - "contentHash": "U0b34w+ZikbqWEZ3ui7BdzxY/19zwrdhLtI3o6tfmLdD3oXxg7n2TZJjwCCTlKPgRuYic9CBWfrZevbb70mTaw==", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", "dependencies": { - "Serilog": "2.5.0", - "System.IO.FileSystem": "4.0.1", - "System.Text.Encoding.Extensions": "4.0.11", - "System.Threading.Timer": "4.0.1" + "Serilog": "2.10.0" } }, "SkiaSharp.Views.Desktop.Common": { "type": "Transitive", - "resolved": "2.80.2", - "contentHash": "0vBvweMysgl1wgjuTQUhdJMD5z5nBjtYqmnHPeX+qHfkc336Wj2L3jEqwmGb0YP+RV47gFGz0EzMAW6szZch9w==", + "resolved": "2.80.3", + "contentHash": "CMQu9fr3BxGRsRryDC6lkYbYaSI2CI+RqisFX0WIdbOdbigUOLhqchmKIMb4EdFAZk13vk862qiE9v97iDZS7g==", "dependencies": { - "SkiaSharp": "2.80.2" + "SkiaSharp": "2.80.3", + "System.Drawing.Common": "4.5.1" } }, "System.AppContext": { @@ -1309,10 +1304,10 @@ "Ninject.Extensions.ChildKernel": "3.3.0", "Ninject.Extensions.Conventions": "3.3.0", "Serilog": "2.10.0", - "Serilog.Sinks.Console": "3.1.1", + "Serilog.Sinks.Console": "4.0.0", "Serilog.Sinks.Debug": "2.0.0", - "Serilog.Sinks.File": "4.1.0", - "SkiaSharp": "2.80.2", + "Serilog.Sinks.File": "5.0.0", + "SkiaSharp": "2.80.3", "System.Buffers": "4.5.1", "System.IO.FileSystem.AccessControl": "5.0.0", "System.Numerics.Vectors": "4.5.0", diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 372979bc4..409b3797d 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -137,7 +137,7 @@ - + @@ -152,8 +152,8 @@ - - + + diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs index 6f4fea3d0..498eee76b 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs @@ -261,8 +261,14 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization public void ResetZoomAndPan() { + if (!Devices.Any()) + { + PanZoomViewModel.Reset(); + return; + } + // Create a rect surrounding all devices - SKRect rect = new SKRect( + SKRect rect = new( Devices.Min(d => d.Rectangle.Left), Devices.Min(d => d.Rectangle.Top), Devices.Max(d => d.Rectangle.Right), diff --git a/src/Artemis.UI/Screens/Settings/SettingsTabsView.xaml b/src/Artemis.UI/Screens/Settings/SettingsTabsView.xaml index 4b4908289..5c0c68c7a 100644 --- a/src/Artemis.UI/Screens/Settings/SettingsTabsView.xaml +++ b/src/Artemis.UI/Screens/Settings/SettingsTabsView.xaml @@ -5,6 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Artemis.UI.Screens.Settings" xmlns:s="https://github.com/canton7/Stylet" + xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:SettingsTabsViewModel}"> @@ -16,18 +17,31 @@ - - - - - - - + + + + + + + + + + + + + diff --git a/src/Artemis.UI/Screens/Settings/SettingsTabsViewModel.cs b/src/Artemis.UI/Screens/Settings/SettingsTabsViewModel.cs index b85fd4c8e..fd3cbb27c 100644 --- a/src/Artemis.UI/Screens/Settings/SettingsTabsViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/SettingsTabsViewModel.cs @@ -2,18 +2,23 @@ using Artemis.UI.Screens.Settings.Tabs.Devices; using Artemis.UI.Screens.Settings.Tabs.General; using Artemis.UI.Screens.Settings.Tabs.Plugins; +using Artemis.UI.Services; using Stylet; namespace Artemis.UI.Screens.Settings { public class SettingsTabsViewModel : Conductor.Collection.OneActive { + private readonly IDebugService _debugService; + public SettingsTabsViewModel( GeneralSettingsTabViewModel generalSettingsTabViewModel, PluginSettingsTabViewModel pluginSettingsTabViewModel, DeviceSettingsTabViewModel deviceSettingsTabViewModel, - AboutTabViewModel aboutTabViewModel) + AboutTabViewModel aboutTabViewModel, + IDebugService debugService) { + _debugService = debugService; DisplayName = "Settings"; Items.Add(generalSettingsTabViewModel); @@ -23,5 +28,10 @@ namespace Artemis.UI.Screens.Settings ActiveItem = generalSettingsTabViewModel; } + + public void ShowDebugger() + { + _debugService.ShowDebugger(); + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs index 97ca37389..542a24c5a 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs @@ -37,7 +37,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins } public PluginFeatureInfo FeatureInfo { get; } - public Exception LoadException => FeatureInfo.Instance?.LoadException; + public Exception LoadException => FeatureInfo.LoadException; public bool ShowShield { get; } @@ -81,7 +81,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins public async Task InstallPrerequisites() { if (FeatureInfo.Prerequisites.Any()) - await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, new List { FeatureInfo }); + await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, new List {FeatureInfo}); } public async Task RemovePrerequisites() @@ -119,12 +119,19 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins private async Task UpdateEnabled(bool enable) { - if (IsEnabled == enable || FeatureInfo.Instance == null) + if (IsEnabled == enable) { NotifyOfPropertyChange(nameof(IsEnabled)); return; } + if (FeatureInfo.Instance == null) + { + NotifyOfPropertyChange(nameof(IsEnabled)); + _messageService.ShowMessage($"Feature '{FeatureInfo.Name}' is in a broken state and cannot enable.", "VIEW LOGS", ShowLogsFolder); + return; + } + if (enable) { Enabling = true; @@ -144,7 +151,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins // Check if all prerequisites are met async if (!FeatureInfo.ArePrerequisitesMet()) { - await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, new List { FeatureInfo }); + await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, new List {FeatureInfo}); if (!FeatureInfo.ArePrerequisitesMet()) { NotifyOfPropertyChange(nameof(IsEnabled)); @@ -156,7 +163,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins } catch (Exception e) { - _messageService.ShowMessage($"Failed to enable {FeatureInfo.Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder); + _messageService.ShowMessage($"Failed to enable '{FeatureInfo.Name}'.\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder); } finally { diff --git a/src/Artemis.UI/packages.lock.json b/src/Artemis.UI/packages.lock.json index ef2b15f3e..a235de541 100644 --- a/src/Artemis.UI/packages.lock.json +++ b/src/Artemis.UI/packages.lock.json @@ -4,9 +4,9 @@ ".NETCoreApp,Version=v5.0": { "FluentValidation": { "type": "Direct", - "requested": "[10.2.3, )", - "resolved": "10.2.3", - "contentHash": "R2w/E6jgg9RPSlg7JQSTHg6AWDwlOXARaV4ZUKrPJ1gi1e+oaBEcomxmo29j9BLZivkyQhOpAboJ9nKZS4/xYA==" + "requested": "[10.3.0, )", + "resolved": "10.3.0", + "contentHash": "ujEB9UMBPDLib6dyhSRhl93IE6ko4ZU6Nz9MFqohaKcvyf06Hk0yDQUFJGF6RgAOsm27O7ZZHVDpXx7oU5vBcg==" }, "Flurl.Http": { "type": "Direct", @@ -130,22 +130,22 @@ }, "SkiaSharp.Views.WPF": { "type": "Direct", - "requested": "[2.80.2, )", - "resolved": "2.80.2", - "contentHash": "Fzo2+MNwHDh9Cob8sk7OO26kp3bhofjXMwlEK8IncF1ehu9hi3sH9iQDJrue9a88VEJJ+yyLISPUFcmXlGHSyQ==", + "requested": "[2.80.3, )", + "resolved": "2.80.3", + "contentHash": "oKUMm7WzFeoie6rW5nnwSGKZ94misyRsAc1wU6SEqgd6ssW17nyfohHxHuBHtmpLtIRwvjhfAu3cMLrpX/oNcw==", "dependencies": { - "SkiaSharp": "2.80.2", - "SkiaSharp.Views.Desktop.Common": "2.80.2" + "SkiaSharp": "2.80.3", + "SkiaSharp.Views.Desktop.Common": "2.80.3" } }, "SkiaSharp.Vulkan.SharpVk": { "type": "Direct", - "requested": "[2.80.2, )", - "resolved": "2.80.2", - "contentHash": "qiqlbgMsSdxTsaPErtE1lXoMXolVVF9E6irmSTzlW++6BbW8tzA89n7GNsgMYJgyo2ljHZhX5ydhFn0Rkj7VHw==", + "requested": "[2.80.3, )", + "resolved": "2.80.3", + "contentHash": "IeR9oOHBsJUqpuVs23XgZXnrFV6WuOTaLpFhLVlXt2XILWIRrlrqx1PILgJm5bLqesceJLYZyMxVb/Ow7/uReA==", "dependencies": { "SharpVk": "0.4.2", - "SkiaSharp": "2.80.2" + "SkiaSharp": "2.80.3" } }, "Stylet": { @@ -486,13 +486,10 @@ }, "Serilog.Sinks.Console": { "type": "Transitive", - "resolved": "3.1.1", - "contentHash": "56mI5AqvyF/i/c2451nvV71kq370XOCE4Uu5qiaJ295sOhMb9q3BWwG7mWLOVSnmpWiq0SBT3SXfgRXGNP6vzA==", + "resolved": "4.0.0", + "contentHash": "yJQit9sTJ4xGLKgCujqDJsaGqBNJwGB/H898z+xYlMG06twy4//6LLnSrsmpduZxcHIG4im7cv+JmXLzXz2EkQ==", "dependencies": { - "Serilog": "2.5.0", - "System.Console": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0" + "Serilog": "2.10.0" } }, "Serilog.Sinks.Debug": { @@ -505,13 +502,10 @@ }, "Serilog.Sinks.File": { "type": "Transitive", - "resolved": "4.1.0", - "contentHash": "U0b34w+ZikbqWEZ3ui7BdzxY/19zwrdhLtI3o6tfmLdD3oXxg7n2TZJjwCCTlKPgRuYic9CBWfrZevbb70mTaw==", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", "dependencies": { - "Serilog": "2.5.0", - "System.IO.FileSystem": "4.0.1", - "System.Text.Encoding.Extensions": "4.0.11", - "System.Threading.Timer": "4.0.1" + "Serilog": "2.10.0" } }, "SharpVectors.Reloaded": { @@ -530,18 +524,19 @@ }, "SkiaSharp": { "type": "Transitive", - "resolved": "2.80.2", - "contentHash": "D25rzdCwh+3L+XyXqpNa+H/yiLJbE3/R3K/XexwHyQjGdzZvSufFW3oqf3En7hhqSIsxsJ8f5NEZ0J5W5wlGBg==", + "resolved": "2.80.3", + "contentHash": "qX6tGNP3+MXNYe2pKm0PCRiJ/cx+LTeLaggwZifB7sUMXhECfKKKHJq45VqZKt37xQegnCCdf1jHXwmHeJQs5Q==", "dependencies": { "System.Memory": "4.5.3" } }, "SkiaSharp.Views.Desktop.Common": { "type": "Transitive", - "resolved": "2.80.2", - "contentHash": "0vBvweMysgl1wgjuTQUhdJMD5z5nBjtYqmnHPeX+qHfkc336Wj2L3jEqwmGb0YP+RV47gFGz0EzMAW6szZch9w==", + "resolved": "2.80.3", + "contentHash": "CMQu9fr3BxGRsRryDC6lkYbYaSI2CI+RqisFX0WIdbOdbigUOLhqchmKIMb4EdFAZk13vk862qiE9v97iDZS7g==", "dependencies": { - "SkiaSharp": "2.80.2" + "SkiaSharp": "2.80.3", + "System.Drawing.Common": "4.5.1" } }, "System.AppContext": { @@ -1437,10 +1432,10 @@ "Ninject.Extensions.ChildKernel": "3.3.0", "Ninject.Extensions.Conventions": "3.3.0", "Serilog": "2.10.0", - "Serilog.Sinks.Console": "3.1.1", + "Serilog.Sinks.Console": "4.0.0", "Serilog.Sinks.Debug": "2.0.0", - "Serilog.Sinks.File": "4.1.0", - "SkiaSharp": "2.80.2", + "Serilog.Sinks.File": "5.0.0", + "SkiaSharp": "2.80.3", "System.Buffers": "4.5.1", "System.IO.FileSystem.AccessControl": "5.0.0", "System.Numerics.Vectors": "4.5.0", @@ -1466,8 +1461,8 @@ "Ninject": "3.3.4", "Ninject.Extensions.Conventions": "3.3.0", "SharpVectors.Reloaded": "1.7.5", - "SkiaSharp": "2.80.2", - "SkiaSharp.Views.WPF": "2.80.2", + "SkiaSharp": "2.80.3", + "SkiaSharp.Views.WPF": "2.80.3", "Stylet": "1.3.6", "System.Buffers": "4.5.1", "System.Numerics.Vectors": "4.5.0"