mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-01-02 10:43:31 +00:00
Merge branch 'development'
This commit is contained in:
commit
7f66ccaa4d
@ -46,7 +46,14 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly BuildInfo BuildInfo = File.Exists(Path.Combine(ApplicationFolder, "buildinfo.json"))
|
public static readonly BuildInfo BuildInfo = File.Exists(Path.Combine(ApplicationFolder, "buildinfo.json"))
|
||||||
? JsonConvert.DeserializeObject<BuildInfo>(File.ReadAllText(Path.Combine(ApplicationFolder, "buildinfo.json")))
|
? JsonConvert.DeserializeObject<BuildInfo>(File.ReadAllText(Path.Combine(ApplicationFolder, "buildinfo.json")))
|
||||||
: new BuildInfo();
|
: new BuildInfo
|
||||||
|
{
|
||||||
|
IsLocalBuild = true,
|
||||||
|
BuildId = 1337,
|
||||||
|
BuildNumber = 1337,
|
||||||
|
SourceBranch = "local",
|
||||||
|
SourceVersion = "local"
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The plugin used by core components of Artemis
|
/// The plugin used by core components of Artemis
|
||||||
|
|||||||
@ -17,4 +17,20 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public PluginFeature PluginFeature { get; }
|
public PluginFeature PluginFeature { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides data about plugin feature info related events
|
||||||
|
/// </summary>
|
||||||
|
public class PluginFeatureInfoEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
internal PluginFeatureInfoEventArgs(PluginFeatureInfo pluginFeatureInfo)
|
||||||
|
{
|
||||||
|
PluginFeatureInfo = pluginFeatureInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the plugin feature this event is related to
|
||||||
|
/// </summary>
|
||||||
|
public PluginFeatureInfo PluginFeatureInfo { get; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -38,6 +38,9 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DataModelPath? EventPath { get; private set; }
|
public DataModelPath? EventPath { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the last time the event this condition is applied to was triggered
|
||||||
|
/// </summary>
|
||||||
public DateTime LastTrigger { get; private set; }
|
public DateTime LastTrigger { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -67,7 +70,6 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
// If there are no children, we always evaluate to true whenever the event triggered
|
// If there are no children, we always evaluate to true whenever the event triggered
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -225,7 +225,7 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
LedEntity ledEntity = new()
|
LedEntity ledEntity = new()
|
||||||
{
|
{
|
||||||
DeviceIdentifier = artemisLed.Device.RgbDevice.GetDeviceIdentifier(),
|
DeviceIdentifier = artemisLed.Device.Identifier,
|
||||||
LedName = artemisLed.RgbLed.Id.ToString()
|
LedName = artemisLed.RgbLed.Id.ToString()
|
||||||
};
|
};
|
||||||
LayerEntity.Leds.Add(ledEntity);
|
LayerEntity.Leds.Add(ledEntity);
|
||||||
@ -578,7 +578,7 @@ namespace Artemis.Core
|
|||||||
List<ArtemisLed> availableLeds = devices.SelectMany(d => d.Leds).ToList();
|
List<ArtemisLed> availableLeds = devices.SelectMany(d => d.Leds).ToList();
|
||||||
foreach (LedEntity ledEntity in LayerEntity.Leds)
|
foreach (LedEntity ledEntity in LayerEntity.Leds)
|
||||||
{
|
{
|
||||||
ArtemisLed? match = availableLeds.FirstOrDefault(a => a.Device.RgbDevice.GetDeviceIdentifier() == ledEntity.DeviceIdentifier &&
|
ArtemisLed? match = availableLeds.FirstOrDefault(a => a.Device.Identifier == ledEntity.DeviceIdentifier &&
|
||||||
a.RgbLed.Id.ToString() == ledEntity.LedName);
|
a.RgbLed.Id.ToString() == ledEntity.LedName);
|
||||||
if (match != null)
|
if (match != null)
|
||||||
leds.Add(match);
|
leds.Add(match);
|
||||||
|
|||||||
@ -21,6 +21,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider)
|
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider)
|
||||||
{
|
{
|
||||||
|
Identifier = rgbDevice.GetDeviceIdentifier();
|
||||||
DeviceEntity = new DeviceEntity();
|
DeviceEntity = new DeviceEntity();
|
||||||
RgbDevice = rgbDevice;
|
RgbDevice = rgbDevice;
|
||||||
DeviceProvider = deviceProvider;
|
DeviceProvider = deviceProvider;
|
||||||
@ -45,6 +46,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity)
|
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity)
|
||||||
{
|
{
|
||||||
|
Identifier = rgbDevice.GetDeviceIdentifier();
|
||||||
DeviceEntity = deviceEntity;
|
DeviceEntity = deviceEntity;
|
||||||
RgbDevice = rgbDevice;
|
RgbDevice = rgbDevice;
|
||||||
DeviceProvider = deviceProvider;
|
DeviceProvider = deviceProvider;
|
||||||
@ -59,6 +61,11 @@ namespace Artemis.Core
|
|||||||
ApplyKeyboardLayout();
|
ApplyKeyboardLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the (hopefully unique and persistent) ID of this device
|
||||||
|
/// </summary>
|
||||||
|
public string Identifier { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the rectangle covering the device
|
/// Gets the rectangle covering the device
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -336,7 +343,7 @@ namespace Artemis.Core
|
|||||||
internal void ApplyToEntity()
|
internal void ApplyToEntity()
|
||||||
{
|
{
|
||||||
// Other properties are computed
|
// Other properties are computed
|
||||||
DeviceEntity.Id = RgbDevice.GetDeviceIdentifier();
|
DeviceEntity.Id = Identifier;
|
||||||
|
|
||||||
DeviceEntity.InputIdentifiers.Clear();
|
DeviceEntity.InputIdentifiers.Clear();
|
||||||
foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers)
|
foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers)
|
||||||
|
|||||||
@ -46,6 +46,9 @@ namespace Artemis.Core
|
|||||||
private set => SetAndNotify(ref _absoluteRectangle, value);
|
private set => SetAndNotify(ref _absoluteRectangle, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the layout applied to this LED
|
||||||
|
/// </summary>
|
||||||
public ArtemisLedLayout? Layout { get; internal set; }
|
public ArtemisLedLayout? Layout { get; internal set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@ -5,6 +5,9 @@ using RGB.NET.Layout;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a LED layout decorated with extra Artemis-specific data
|
||||||
|
/// </summary>
|
||||||
public class ArtemisLedLayout
|
public class ArtemisLedLayout
|
||||||
{
|
{
|
||||||
internal ArtemisLedLayout(ArtemisLayout deviceLayout, ILedLayout led)
|
internal ArtemisLedLayout(ArtemisLayout deviceLayout, ILedLayout led)
|
||||||
|
|||||||
@ -29,6 +29,9 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ListLedGroup? LedGroup { get; internal set; }
|
public ListLedGroup? LedGroup { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For internal use only, is public for dependency injection but ignore pl0x
|
||||||
|
/// </summary>
|
||||||
[Inject]
|
[Inject]
|
||||||
public IRgbService? RgbService { get; set; }
|
public IRgbService? RgbService { get; set; }
|
||||||
|
|
||||||
@ -38,6 +41,26 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
/// <returns>Your RGB.NET effect</returns>
|
/// <returns>Your RGB.NET effect</returns>
|
||||||
public abstract IBrush GetBrush();
|
public abstract IBrush GetBrush();
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
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 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
internal void UpdateLedGroup()
|
internal void UpdateLedGroup()
|
||||||
{
|
{
|
||||||
if (LedGroup == null)
|
if (LedGroup == null)
|
||||||
@ -67,26 +90,6 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
UpdateLedGroup();
|
UpdateLedGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDisposable
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
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 = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
// Not used in this effect type
|
// Not used in this effect type
|
||||||
internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint)
|
internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -16,7 +16,7 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Plugin : CorePropertyChanged, IDisposable
|
public class Plugin : CorePropertyChanged, IDisposable
|
||||||
{
|
{
|
||||||
private readonly List<PluginFeature> _features;
|
private readonly List<PluginFeatureInfo> _features;
|
||||||
|
|
||||||
private bool _isEnabled;
|
private bool _isEnabled;
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ namespace Artemis.Core
|
|||||||
Directory = directory;
|
Directory = directory;
|
||||||
Entity = pluginEntity ?? new PluginEntity {Id = Guid, IsEnabled = true};
|
Entity = pluginEntity ?? new PluginEntity {Id = Guid, IsEnabled = true};
|
||||||
|
|
||||||
_features = new List<PluginFeature>();
|
_features = new List<PluginFeatureInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -61,7 +61,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a read-only collection of all features this plugin provides
|
/// Gets a read-only collection of all features this plugin provides
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyCollection<PluginFeature> Features => _features.AsReadOnly();
|
public ReadOnlyCollection<PluginFeatureInfo> Features => _features.AsReadOnly();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The assembly the plugin code lives in
|
/// The assembly the plugin code lives in
|
||||||
@ -107,7 +107,7 @@ namespace Artemis.Core
|
|||||||
/// <returns>If found, the instance of the feature</returns>
|
/// <returns>If found, the instance of the feature</returns>
|
||||||
public T? GetFeature<T>() where T : PluginFeature
|
public T? GetFeature<T>() where T : PluginFeature
|
||||||
{
|
{
|
||||||
return _features.FirstOrDefault(i => i is T) as T;
|
return _features.FirstOrDefault(i => i.Instance is T) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -122,23 +122,24 @@ namespace Artemis.Core
|
|||||||
Entity.IsEnabled = IsEnabled;
|
Entity.IsEnabled = IsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddFeature(PluginFeature feature)
|
internal void AddFeature(PluginFeatureInfo featureInfo)
|
||||||
{
|
{
|
||||||
feature.Plugin = this;
|
if (featureInfo.Plugin != this)
|
||||||
_features.Add(feature);
|
throw new ArtemisCoreException("Feature is not associated with this plugin");
|
||||||
|
_features.Add(featureInfo);
|
||||||
|
|
||||||
OnFeatureAdded(new PluginFeatureEventArgs(feature));
|
OnFeatureAdded(new PluginFeatureInfoEventArgs(featureInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveFeature(PluginFeature feature)
|
internal void RemoveFeature(PluginFeatureInfo featureInfo)
|
||||||
{
|
{
|
||||||
if (feature.IsEnabled)
|
if (featureInfo.Instance != null && featureInfo.Instance.IsEnabled)
|
||||||
throw new ArtemisCoreException("Cannot remove an enabled feature from a plugin");
|
throw new ArtemisCoreException("Cannot remove an enabled feature from a plugin");
|
||||||
|
|
||||||
_features.Remove(feature);
|
_features.Remove(featureInfo);
|
||||||
feature.Dispose();
|
featureInfo.Instance?.Dispose();
|
||||||
|
|
||||||
OnFeatureRemoved(new PluginFeatureEventArgs(feature));
|
OnFeatureRemoved(new PluginFeatureInfoEventArgs(featureInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetEnabled(bool enable)
|
internal void SetEnabled(bool enable)
|
||||||
@ -146,7 +147,7 @@ namespace Artemis.Core
|
|||||||
if (IsEnabled == enable)
|
if (IsEnabled == enable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!enable && Features.Any(e => e.IsEnabled))
|
if (!enable && Features.Any(e => e.Instance != null && e.Instance.IsEnabled))
|
||||||
throw new ArtemisCoreException("Cannot disable this plugin because it still has enabled features");
|
throw new ArtemisCoreException("Cannot disable this plugin because it still has enabled features");
|
||||||
|
|
||||||
IsEnabled = enable;
|
IsEnabled = enable;
|
||||||
@ -176,8 +177,8 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
foreach (PluginFeature feature in Features)
|
foreach (PluginFeatureInfo feature in Features)
|
||||||
feature.Dispose();
|
feature.Instance?.Dispose();
|
||||||
SetEnabled(false);
|
SetEnabled(false);
|
||||||
|
|
||||||
Kernel?.Dispose();
|
Kernel?.Dispose();
|
||||||
@ -214,12 +215,12 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when an feature is loaded and added to the plugin
|
/// Occurs when an feature is loaded and added to the plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<PluginFeatureEventArgs>? FeatureAdded;
|
public event EventHandler<PluginFeatureInfoEventArgs>? FeatureAdded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when an feature is disabled and removed from the plugin
|
/// Occurs when an feature is disabled and removed from the plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<PluginFeatureEventArgs>? FeatureRemoved;
|
public event EventHandler<PluginFeatureInfoEventArgs>? FeatureRemoved;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invokes the Enabled event
|
/// Invokes the Enabled event
|
||||||
@ -240,7 +241,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invokes the FeatureAdded event
|
/// Invokes the FeatureAdded event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnFeatureAdded(PluginFeatureEventArgs e)
|
protected virtual void OnFeatureAdded(PluginFeatureInfoEventArgs e)
|
||||||
{
|
{
|
||||||
FeatureAdded?.Invoke(this, e);
|
FeatureAdded?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
@ -248,7 +249,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invokes the FeatureRemoved event
|
/// Invokes the FeatureRemoved event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnFeatureRemoved(PluginFeatureEventArgs e)
|
protected virtual void OnFeatureRemoved(PluginFeatureInfoEventArgs e)
|
||||||
{
|
{
|
||||||
FeatureRemoved?.Invoke(this, e);
|
FeatureRemoved?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Artemis.Core.DataModelExpansions;
|
using System;
|
||||||
|
using Artemis.Core.DataModelExpansions;
|
||||||
using Artemis.Core.DeviceProviders;
|
using Artemis.Core.DeviceProviders;
|
||||||
using Artemis.Core.LayerBrushes;
|
using Artemis.Core.LayerBrushes;
|
||||||
using Artemis.Core.LayerEffects;
|
using Artemis.Core.LayerEffects;
|
||||||
@ -16,22 +17,48 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
private string? _description;
|
private string? _description;
|
||||||
private string? _icon;
|
private string? _icon;
|
||||||
|
private PluginFeature? _instance;
|
||||||
private string _name = null!;
|
private string _name = null!;
|
||||||
private PluginFeature _pluginFeature = null!;
|
|
||||||
|
|
||||||
internal PluginFeatureInfo()
|
internal PluginFeatureInfo(Plugin plugin, Type featureType, PluginFeatureAttribute? attribute)
|
||||||
{
|
{
|
||||||
|
Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin));
|
||||||
|
FeatureType = featureType ?? throw new ArgumentNullException(nameof(featureType));
|
||||||
|
|
||||||
|
Name = attribute?.Name ?? featureType.Name.Humanize(LetterCasing.Title);
|
||||||
|
Description = attribute?.Description;
|
||||||
|
Icon = attribute?.Icon;
|
||||||
|
|
||||||
|
if (Icon != null) return;
|
||||||
|
if (typeof(BaseDataModelExpansion).IsAssignableFrom(featureType))
|
||||||
|
Icon = "TableAdd";
|
||||||
|
else if (typeof(DeviceProvider).IsAssignableFrom(featureType))
|
||||||
|
Icon = "Devices";
|
||||||
|
else if (typeof(ProfileModule).IsAssignableFrom(featureType))
|
||||||
|
Icon = "VectorRectangle";
|
||||||
|
else if (typeof(Module).IsAssignableFrom(featureType))
|
||||||
|
Icon = "GearBox";
|
||||||
|
else if (typeof(LayerBrushProvider).IsAssignableFrom(featureType))
|
||||||
|
Icon = "Brush";
|
||||||
|
else if (typeof(LayerEffectProvider).IsAssignableFrom(featureType))
|
||||||
|
Icon = "AutoAwesome";
|
||||||
|
else
|
||||||
|
Icon = "Plugin";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal PluginFeatureInfo(PluginFeature instance, PluginFeatureAttribute? attribute)
|
internal PluginFeatureInfo(Plugin plugin, PluginFeatureAttribute? attribute, PluginFeature instance)
|
||||||
{
|
{
|
||||||
|
if (instance == null) throw new ArgumentNullException(nameof(instance));
|
||||||
|
Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin));
|
||||||
|
FeatureType = instance.GetType();
|
||||||
|
|
||||||
Name = attribute?.Name ?? instance.GetType().Name.Humanize(LetterCasing.Title);
|
Name = attribute?.Name ?? instance.GetType().Name.Humanize(LetterCasing.Title);
|
||||||
Description = attribute?.Description;
|
Description = attribute?.Description;
|
||||||
Icon = attribute?.Icon;
|
Icon = attribute?.Icon;
|
||||||
PluginFeature = instance;
|
Instance = instance;
|
||||||
|
|
||||||
if (Icon != null) return;
|
if (Icon != null) return;
|
||||||
Icon = PluginFeature switch
|
Icon = Instance switch
|
||||||
{
|
{
|
||||||
BaseDataModelExpansion => "TableAdd",
|
BaseDataModelExpansion => "TableAdd",
|
||||||
DeviceProvider => "Devices",
|
DeviceProvider => "Devices",
|
||||||
@ -43,6 +70,16 @@ namespace Artemis.Core
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the plugin this feature info is associated with
|
||||||
|
/// </summary>
|
||||||
|
public Plugin Plugin { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the feature
|
||||||
|
/// </summary>
|
||||||
|
public Type FeatureType { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the plugin
|
/// The name of the plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -75,18 +112,18 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the plugin this info is associated with
|
/// Gets the feature this info is associated with
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PluginFeature PluginFeature
|
public PluginFeature? Instance
|
||||||
{
|
{
|
||||||
get => _pluginFeature;
|
get => _instance;
|
||||||
internal set => SetAndNotify(ref _pluginFeature, value);
|
internal set => SetAndNotify(ref _instance, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return PluginFeature.Id;
|
return Instance?.Id ?? "Uninitialized feature";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,5 +38,10 @@ namespace Artemis.Core.Services.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty]
|
[JsonProperty]
|
||||||
public string SourceVersion { get; internal set; } = null!;
|
public string SourceVersion { get; internal set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a boolean indicating whether the current build is a local build
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLocalBuild { get; internal set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,6 +127,13 @@ namespace Artemis.Core.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
DeviceProvider GetDeviceProviderByDevice(IRGBDevice device);
|
DeviceProvider GetDeviceProviderByDevice(IRGBDevice device);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues an action for the provided plugin for the next time Artemis starts, before plugins are loaded
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="plugin">The plugin to queue the action for</param>
|
||||||
|
/// <param name="pluginAction">The action to take</param>
|
||||||
|
void QueuePluginAction(Plugin plugin, PluginManagementAction pluginAction);
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -52,7 +52,7 @@ namespace Artemis.Core.Services
|
|||||||
bool IsRenderPaused { get; set; }
|
bool IsRenderPaused { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recreates the Texture to use the given <see cref="SKBitmap"/>
|
/// Recreates the Texture to use the given <see cref="SKBitmap" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bitmap"></param>
|
/// <param name="bitmap"></param>
|
||||||
void UpdateTexture(SKBitmap bitmap);
|
void UpdateTexture(SKBitmap bitmap);
|
||||||
@ -119,7 +119,16 @@ namespace Artemis.Core.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
void SaveDevices();
|
void SaveDevices();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables the provided device
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="device">The device to enable</param>
|
||||||
void EnableDevice(ArtemisDevice device);
|
void EnableDevice(ArtemisDevice device);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables the provided device
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="device">The device to disable</param>
|
||||||
void DisableDevice(ArtemisDevice device);
|
void DisableDevice(ArtemisDevice device);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -67,7 +67,7 @@ namespace Artemis.Core.Services
|
|||||||
// If this is a profile module, animate profile disable
|
// If this is a profile module, animate profile disable
|
||||||
// module.Deactivate would do the same but without animation
|
// module.Deactivate would do the same but without animation
|
||||||
if (module.IsActivated && module is ProfileModule profileModule)
|
if (module.IsActivated && module is ProfileModule profileModule)
|
||||||
await profileModule.ChangeActiveProfileAnimated(null, null);
|
await profileModule.ChangeActiveProfileAnimated(null, Enumerable.Empty<ArtemisDevice>());
|
||||||
|
|
||||||
module.Deactivate(false);
|
module.Deactivate(false);
|
||||||
}
|
}
|
||||||
@ -210,6 +210,7 @@ namespace Artemis.Core.Services
|
|||||||
List<Module> modules = _pluginManagementService.GetFeaturesOfType<Module>().ToList();
|
List<Module> modules = _pluginManagementService.GetFeaturesOfType<Module>().ToList();
|
||||||
List<Task> tasks = new();
|
List<Task> tasks = new();
|
||||||
foreach (Module module in modules)
|
foreach (Module module in modules)
|
||||||
|
{
|
||||||
lock (module)
|
lock (module)
|
||||||
{
|
{
|
||||||
bool shouldBeActivated = module.EvaluateActivationRequirements() && module.IsEnabled;
|
bool shouldBeActivated = module.EvaluateActivationRequirements() && module.IsEnabled;
|
||||||
@ -218,6 +219,7 @@ namespace Artemis.Core.Services
|
|||||||
else if (!shouldBeActivated && module.IsActivated)
|
else if (!shouldBeActivated && module.IsActivated)
|
||||||
tasks.Add(DeactivateModule(module));
|
tasks.Add(DeactivateModule(module));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await Task.WhenAll(tasks);
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,6 @@ using System.IO;
|
|||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Loader;
|
|
||||||
using Artemis.Core.DeviceProviders;
|
using Artemis.Core.DeviceProviders;
|
||||||
using Artemis.Core.Ninject;
|
using Artemis.Core.Ninject;
|
||||||
using Artemis.Storage.Entities.Plugins;
|
using Artemis.Storage.Entities.Plugins;
|
||||||
@ -28,6 +27,7 @@ namespace Artemis.Core.Services
|
|||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IPluginRepository _pluginRepository;
|
private readonly IPluginRepository _pluginRepository;
|
||||||
private readonly List<Plugin> _plugins;
|
private readonly List<Plugin> _plugins;
|
||||||
|
private bool _isElevated;
|
||||||
|
|
||||||
public PluginManagementService(IKernel kernel, ILogger logger, IPluginRepository pluginRepository)
|
public PluginManagementService(IKernel kernel, ILogger logger, IPluginRepository pluginRepository)
|
||||||
{
|
{
|
||||||
@ -35,6 +35,8 @@ namespace Artemis.Core.Services
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
_pluginRepository = pluginRepository;
|
_pluginRepository = pluginRepository;
|
||||||
_plugins = new List<Plugin>();
|
_plugins = new List<Plugin>();
|
||||||
|
|
||||||
|
ProcessQueuedActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyBuiltInPlugin(FileInfo zipFileInfo, ZipArchive zipArchive)
|
private void CopyBuiltInPlugin(FileInfo zipFileInfo, ZipArchive zipArchive)
|
||||||
@ -130,7 +132,11 @@ namespace Artemis.Core.Services
|
|||||||
{
|
{
|
||||||
lock (_plugins)
|
lock (_plugins)
|
||||||
{
|
{
|
||||||
return _plugins.Where(p => p.IsEnabled).SelectMany(p => p.Features.Where(i => i.IsEnabled && i is T)).Cast<T>().ToList();
|
return _plugins.Where(p => p.IsEnabled)
|
||||||
|
.SelectMany(p => p.Features.Where(f => f.Instance != null && f.Instance.IsEnabled && f.Instance is T))
|
||||||
|
.Select(f => f.Instance)
|
||||||
|
.Cast<T>()
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,6 +184,7 @@ namespace Artemis.Core.Services
|
|||||||
if (LoadingPlugins)
|
if (LoadingPlugins)
|
||||||
throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet.");
|
throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet.");
|
||||||
|
|
||||||
|
_isElevated = isElevated;
|
||||||
LoadingPlugins = true;
|
LoadingPlugins = true;
|
||||||
|
|
||||||
// Unload all currently loaded plugins first
|
// Unload all currently loaded plugins first
|
||||||
@ -186,6 +193,7 @@ namespace Artemis.Core.Services
|
|||||||
// Load the plugin assemblies into the plugin context
|
// Load the plugin assemblies into the plugin context
|
||||||
DirectoryInfo pluginDirectory = new(Path.Combine(Constants.DataFolder, "plugins"));
|
DirectoryInfo pluginDirectory = new(Path.Combine(Constants.DataFolder, "plugins"));
|
||||||
foreach (DirectoryInfo subDirectory in pluginDirectory.EnumerateDirectories())
|
foreach (DirectoryInfo subDirectory in pluginDirectory.EnumerateDirectories())
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LoadPlugin(subDirectory);
|
LoadPlugin(subDirectory);
|
||||||
@ -194,11 +202,12 @@ namespace Artemis.Core.Services
|
|||||||
{
|
{
|
||||||
_logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception");
|
_logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ReSharper disable InconsistentlySynchronizedField - It's read-only, idc
|
// ReSharper disable InconsistentlySynchronizedField - It's read-only, idc
|
||||||
_logger.Debug("Loaded {count} plugin(s)", _plugins.Count);
|
_logger.Debug("Loaded {count} plugin(s)", _plugins.Count);
|
||||||
|
|
||||||
bool adminRequired = _plugins.Any(p => p.Entity.IsEnabled && p.Info.RequiresAdmin);
|
bool adminRequired = _plugins.Any(p => p.Info.RequiresAdmin && p.Entity.IsEnabled && p.Entity.Features.Any(f => f.IsEnabled));
|
||||||
if (!isElevated && adminRequired)
|
if (!isElevated && adminRequired)
|
||||||
{
|
{
|
||||||
_logger.Information("Restarting because one or more plugins requires elevation");
|
_logger.Information("Restarting because one or more plugins requires elevation");
|
||||||
@ -218,7 +227,7 @@ namespace Artemis.Core.Services
|
|||||||
foreach (Plugin plugin in _plugins.Where(p => p.Entity.IsEnabled))
|
foreach (Plugin plugin in _plugins.Where(p => p.Entity.IsEnabled))
|
||||||
EnablePlugin(plugin, false, ignorePluginLock);
|
EnablePlugin(plugin, false, ignorePluginLock);
|
||||||
|
|
||||||
_logger.Debug("Enabled {count} plugin(s)", _plugins.Where(p => p.IsEnabled).Sum(p => p.Features.Count(f => f.IsEnabled)));
|
_logger.Debug("Enabled {count} plugin(s)", _plugins.Count(p => p.IsEnabled));
|
||||||
// ReSharper restore InconsistentlySynchronizedField
|
// ReSharper restore InconsistentlySynchronizedField
|
||||||
|
|
||||||
LoadingPlugins = false;
|
LoadingPlugins = false;
|
||||||
@ -289,6 +298,28 @@ namespace Artemis.Core.Services
|
|||||||
throw new ArtemisPluginException(plugin, "Failed to load the plugins assembly", e);
|
throw new ArtemisPluginException(plugin, "Failed to load the plugins assembly", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the Plugin feature from the main assembly and if there is only one, instantiate it
|
||||||
|
List<Type> featureTypes;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
featureTypes = plugin.Assembly.GetTypes().Where(t => typeof(PluginFeature).IsAssignableFrom(t)).ToList();
|
||||||
|
}
|
||||||
|
catch (ReflectionTypeLoadException e)
|
||||||
|
{
|
||||||
|
throw new ArtemisPluginException(
|
||||||
|
plugin,
|
||||||
|
"Failed to initialize the plugin assembly",
|
||||||
|
// ReSharper disable once RedundantEnumerableCastCall - Casting from nullable to non-nullable here
|
||||||
|
new AggregateException(e.LoaderExceptions.Where(le => le != null).Cast<Exception>().ToArray())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Type featureType in featureTypes)
|
||||||
|
plugin.AddFeature(new PluginFeatureInfo(plugin, featureType, (PluginFeatureAttribute?) Attribute.GetCustomAttribute(featureType, typeof(PluginFeatureAttribute))));
|
||||||
|
|
||||||
|
if (!featureTypes.Any())
|
||||||
|
_logger.Warning("Plugin {plugin} contains no features", plugin);
|
||||||
|
|
||||||
List<Type> bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(IPluginBootstrapper).IsAssignableFrom(t)).ToList();
|
List<Type> bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(IPluginBootstrapper).IsAssignableFrom(t)).ToList();
|
||||||
if (bootstrappers.Count > 1)
|
if (bootstrappers.Count > 1)
|
||||||
_logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}");
|
_logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}");
|
||||||
@ -309,66 +340,64 @@ namespace Artemis.Core.Services
|
|||||||
if (plugin.Assembly == null)
|
if (plugin.Assembly == null)
|
||||||
throw new ArtemisPluginException(plugin, "Cannot enable a plugin that hasn't successfully been loaded");
|
throw new ArtemisPluginException(plugin, "Cannot enable a plugin that hasn't successfully been loaded");
|
||||||
|
|
||||||
|
if (plugin.Info.RequiresAdmin && plugin.Entity.Features.Any(f => f.IsEnabled) && !_isElevated)
|
||||||
|
{
|
||||||
|
if (!saveState)
|
||||||
|
throw new ArtemisCoreException("Cannot enable a plugin that requires elevation without saving it's state.");
|
||||||
|
|
||||||
|
plugin.Entity.IsEnabled = true;
|
||||||
|
SavePlugin(plugin);
|
||||||
|
|
||||||
|
_logger.Information("Restarting because a newly enabled plugin requires elevation");
|
||||||
|
Utilities.Restart(true, TimeSpan.FromMilliseconds(500));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create the Ninject child kernel and load the module
|
// Create the Ninject child kernel and load the module
|
||||||
plugin.Kernel = new ChildKernel(_kernel, new PluginModule(plugin));
|
plugin.Kernel = new ChildKernel(_kernel, new PluginModule(plugin));
|
||||||
OnPluginEnabling(new PluginEventArgs(plugin));
|
OnPluginEnabling(new PluginEventArgs(plugin));
|
||||||
|
|
||||||
plugin.SetEnabled(true);
|
plugin.SetEnabled(true);
|
||||||
|
|
||||||
// Get the Plugin feature from the main assembly and if there is only one, instantiate it
|
// Create instances of each feature
|
||||||
List<Type> featureTypes;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
featureTypes = plugin.Assembly.GetTypes().Where(t => typeof(PluginFeature).IsAssignableFrom(t)).ToList();
|
|
||||||
}
|
|
||||||
catch (ReflectionTypeLoadException e)
|
|
||||||
{
|
|
||||||
throw new ArtemisPluginException(
|
|
||||||
plugin,
|
|
||||||
"Failed to initialize the plugin assembly",
|
|
||||||
// ReSharper disable once RedundantEnumerableCastCall - Casting from nullable to non-nullable here
|
|
||||||
new AggregateException(e.LoaderExceptions.Where(le => le != null).Cast<Exception>().ToArray())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!featureTypes.Any())
|
|
||||||
_logger.Warning("Plugin {plugin} contains no features", plugin);
|
|
||||||
|
|
||||||
// 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
|
// Construction should be simple and not contain any logic so failure at this point means the entire plugin fails
|
||||||
foreach (Type featureType in featureTypes)
|
foreach (PluginFeatureInfo featureInfo in plugin.Features)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
plugin.Kernel.Bind(featureType).ToSelf().InSingletonScope();
|
plugin.Kernel.Bind(featureInfo.FeatureType).ToSelf().InSingletonScope();
|
||||||
|
|
||||||
// Include Plugin as a parameter for the PluginSettingsProvider
|
// Include Plugin as a parameter for the PluginSettingsProvider
|
||||||
IParameter[] parameters = {new Parameter("Plugin", plugin, false)};
|
IParameter[] parameters = {new Parameter("Plugin", plugin, false)};
|
||||||
PluginFeature instance = (PluginFeature) plugin.Kernel.Get(featureType, parameters);
|
PluginFeature instance = (PluginFeature) plugin.Kernel.Get(featureInfo.FeatureType, parameters);
|
||||||
|
|
||||||
// Get the PluginFeature attribute which contains extra info on the feature
|
// Get the PluginFeature attribute which contains extra info on the feature
|
||||||
PluginFeatureAttribute? pluginFeatureAttribute = (PluginFeatureAttribute?) Attribute.GetCustomAttribute(featureType, typeof(PluginFeatureAttribute));
|
featureInfo.Instance = instance;
|
||||||
instance.Info = new PluginFeatureInfo(instance, pluginFeatureAttribute);
|
instance.Info = featureInfo;
|
||||||
plugin.AddFeature(instance);
|
instance.Plugin = plugin;
|
||||||
|
|
||||||
// Load the enabled state and if not found, default to true
|
// Load the enabled state and if not found, default to true
|
||||||
instance.Entity = plugin.Entity.Features.FirstOrDefault(i => i.Type == featureType.FullName) ??
|
instance.Entity = plugin.Entity.Features.FirstOrDefault(i => i.Type == featureInfo.FeatureType.FullName) ??
|
||||||
new PluginFeatureEntity {IsEnabled = plugin.Info.AutoEnableFeatures, Type = featureType.FullName!};
|
new PluginFeatureEntity {IsEnabled = plugin.Info.AutoEnableFeatures, Type = featureInfo.FeatureType.FullName!};
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.Warning(new ArtemisPluginException(plugin, "Failed to instantiate feature", e), "Failed to instantiate feature", plugin);
|
_logger.Warning(new ArtemisPluginException(plugin, "Failed to instantiate feature", e), "Failed to instantiate feature", plugin);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Activate plugins after they are all loaded
|
// Activate features after they are all loaded
|
||||||
foreach (PluginFeature pluginFeature in plugin.Features.Where(i => i.Entity.IsEnabled))
|
foreach (PluginFeatureInfo pluginFeature in plugin.Features.Where(f => f.Instance != null && f.Instance.Entity.IsEnabled))
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
EnablePluginFeature(pluginFeature, false, !ignorePluginLock);
|
EnablePluginFeature(pluginFeature.Instance!, false, !ignorePluginLock);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
// ignored, logged in EnablePluginFeature
|
// ignored, logged in EnablePluginFeature
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (saveState)
|
if (saveState)
|
||||||
{
|
{
|
||||||
@ -406,12 +435,10 @@ namespace Artemis.Core.Services
|
|||||||
if (!plugin.IsEnabled)
|
if (!plugin.IsEnabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while (plugin.Features.Any())
|
foreach (PluginFeatureInfo pluginFeatureInfo in plugin.Features)
|
||||||
{
|
{
|
||||||
PluginFeature feature = plugin.Features[0];
|
if (pluginFeatureInfo.Instance != null && pluginFeatureInfo.Instance.IsEnabled)
|
||||||
if (feature.IsEnabled)
|
DisablePluginFeature(pluginFeatureInfo.Instance, false);
|
||||||
DisablePluginFeature(feature, false);
|
|
||||||
plugin.RemoveFeature(feature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin.SetEnabled(false);
|
plugin.SetEnabled(false);
|
||||||
@ -450,7 +477,6 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
Plugin? existing = _plugins.FirstOrDefault(p => p.Guid == pluginInfo.Guid);
|
Plugin? existing = _plugins.FirstOrDefault(p => p.Guid == pluginInfo.Guid);
|
||||||
if (existing != null)
|
if (existing != null)
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RemovePlugin(existing);
|
RemovePlugin(existing);
|
||||||
@ -459,7 +485,6 @@ namespace Artemis.Core.Services
|
|||||||
{
|
{
|
||||||
throw new ArtemisPluginException("A plugin with the same GUID is already loaded, failed to remove old version", e);
|
throw new ArtemisPluginException("A plugin with the same GUID is already loaded, failed to remove old version", e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
string targetDirectory = pluginInfo.Main.Split(".dll")[0].Replace("/", "").Replace("\\", "");
|
string targetDirectory = pluginInfo.Main.Split(".dll")[0].Replace("/", "").Replace("\\", "");
|
||||||
string uniqueTargetDirectory = targetDirectory;
|
string uniqueTargetDirectory = targetDirectory;
|
||||||
@ -515,6 +540,21 @@ namespace Artemis.Core.Services
|
|||||||
_logger.Verbose("Enabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
_logger.Verbose("Enabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
||||||
|
|
||||||
OnPluginFeatureEnabling(new PluginFeatureEventArgs(pluginFeature));
|
OnPluginFeatureEnabling(new PluginFeatureEventArgs(pluginFeature));
|
||||||
|
|
||||||
|
if (pluginFeature.Plugin.Info.RequiresAdmin && !_isElevated)
|
||||||
|
{
|
||||||
|
if (!saveState)
|
||||||
|
throw new ArtemisCoreException("Cannot enable a feature that requires elevation without saving it's state.");
|
||||||
|
|
||||||
|
pluginFeature.Entity.IsEnabled = true;
|
||||||
|
pluginFeature.Plugin.Entity.IsEnabled = true;
|
||||||
|
SavePlugin(pluginFeature.Plugin);
|
||||||
|
|
||||||
|
_logger.Information("Restarting because a newly enabled feature requires elevation");
|
||||||
|
Utilities.Restart(true, TimeSpan.FromMilliseconds(500));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
pluginFeature.SetEnabled(true, isAutoEnable);
|
pluginFeature.SetEnabled(true, isAutoEnable);
|
||||||
@ -578,13 +618,43 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Queued actions
|
||||||
|
|
||||||
|
public void QueuePluginAction(Plugin plugin, PluginManagementAction pluginAction)
|
||||||
|
{
|
||||||
|
List<PluginQueuedActionEntity> existing = _pluginRepository.GetQueuedActions(plugin.Guid);
|
||||||
|
if (existing.Any(e => pluginAction == PluginManagementAction.Delete && e is PluginQueuedDeleteEntity))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (pluginAction == PluginManagementAction.Delete)
|
||||||
|
_pluginRepository.AddQueuedAction(new PluginQueuedDeleteEntity {PluginGuid = plugin.Guid, Directory = plugin.Directory.FullName});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessQueuedActions()
|
||||||
|
{
|
||||||
|
foreach (PluginQueuedActionEntity pluginQueuedActionEntity in _pluginRepository.GetQueuedActions())
|
||||||
|
{
|
||||||
|
if (pluginQueuedActionEntity is PluginQueuedDeleteEntity deleteAction)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(deleteAction.Directory))
|
||||||
|
Directory.Delete(deleteAction.Directory, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pluginRepository.RemoveQueuedAction(pluginQueuedActionEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Storage
|
#region Storage
|
||||||
|
|
||||||
private void SavePlugin(Plugin plugin)
|
private void SavePlugin(Plugin plugin)
|
||||||
{
|
{
|
||||||
foreach (PluginFeature pluginFeature in plugin.Features)
|
foreach (PluginFeatureInfo featureInfo in plugin.Features.Where(i => i.Instance != null))
|
||||||
if (plugin.Entity.Features.All(i => i.Type != pluginFeature.GetType().FullName))
|
{
|
||||||
plugin.Entity.Features.Add(pluginFeature.Entity);
|
if (plugin.Entity.Features.All(i => i.Type != featureInfo.FeatureType.FullName))
|
||||||
|
plugin.Entity.Features.Add(featureInfo.Instance!.Entity);
|
||||||
|
}
|
||||||
|
|
||||||
_pluginRepository.SavePlugin(plugin.Entity);
|
_pluginRepository.SavePlugin(plugin.Entity);
|
||||||
}
|
}
|
||||||
@ -663,4 +733,20 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a type of plugin management action
|
||||||
|
/// </summary>
|
||||||
|
public enum PluginManagementAction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A plugin management action that removes a plugin
|
||||||
|
/// </summary>
|
||||||
|
Delete,
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// A plugin management action that updates a plugin
|
||||||
|
// /// </summary>
|
||||||
|
// Update
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -239,6 +239,7 @@ namespace Artemis.Core.Services
|
|||||||
public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds)
|
public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds)
|
||||||
{
|
{
|
||||||
device.ApplyLayout(layout, createMissingLeds, removeExessiveLeds);
|
device.ApplyLayout(layout, createMissingLeds, removeExessiveLeds);
|
||||||
|
UpdateLedGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArtemisDevice? GetDevice(IRGBDevice rgbDevice)
|
public ArtemisDevice? GetDevice(IRGBDevice rgbDevice)
|
||||||
|
|||||||
@ -146,6 +146,7 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
void ActivatingProfilePluginToggle(object? sender, PluginEventArgs e)
|
void ActivatingProfilePluginToggle(object? sender, PluginEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (!profile.Disposed)
|
||||||
InstantiateProfile(profile);
|
InstantiateProfile(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -61,7 +61,7 @@ namespace Artemis.Core.Services
|
|||||||
server.StateChanged += (s, e) => _logger.Verbose("WebServer new state - {state}", e.NewState);
|
server.StateChanged += (s, e) => _logger.Verbose("WebServer new state - {state}", e.NewState);
|
||||||
|
|
||||||
// Store the URL in a webserver.txt file so that remote applications can find it
|
// Store the URL in a webserver.txt file so that remote applications can find it
|
||||||
File.WriteAllText(Path.Combine(Constants.DataFolder, "webserver.txt"), url);
|
File.WriteAllText(Path.Combine(Constants.DataFolder, "webserver.txt"), $"http://localhost:{_webServerPortSetting.Value}/");
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
public CorePluginFeature()
|
public CorePluginFeature()
|
||||||
{
|
{
|
||||||
Constants.CorePlugin.AddFeature(this);
|
Constants.CorePlugin.AddFeature(new PluginFeatureInfo(Constants.CorePlugin, null, this));
|
||||||
IsEnabled = true;
|
IsEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ namespace Artemis.Core
|
|||||||
foreach (LayerEntity profileEntityLayer in profileEntity.Layers)
|
foreach (LayerEntity profileEntityLayer in profileEntity.Layers)
|
||||||
profileEntityLayer.Leds.AddRange(_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(),
|
DeviceIdentifier = l.Device.Identifier,
|
||||||
LedName = l.RgbLed.Id.ToString()
|
LedName = l.RgbLed.Id.ToString()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@ -13,4 +13,21 @@ namespace Artemis.Storage.Entities.Plugins
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a queued action for a plugin
|
||||||
|
/// </summary>
|
||||||
|
public abstract class PluginQueuedActionEntity
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid PluginGuid { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a queued delete action for a plugin
|
||||||
|
/// </summary>
|
||||||
|
public class PluginQueuedDeleteEntity : PluginQueuedActionEntity
|
||||||
|
{
|
||||||
|
public string Directory { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Artemis.Storage.Entities.Plugins;
|
using Artemis.Storage.Entities.Plugins;
|
||||||
|
|
||||||
namespace Artemis.Storage.Repositories.Interfaces
|
namespace Artemis.Storage.Repositories.Interfaces
|
||||||
@ -8,9 +9,15 @@ namespace Artemis.Storage.Repositories.Interfaces
|
|||||||
void AddPlugin(PluginEntity pluginEntity);
|
void AddPlugin(PluginEntity pluginEntity);
|
||||||
PluginEntity GetPluginByGuid(Guid pluginGuid);
|
PluginEntity GetPluginByGuid(Guid pluginGuid);
|
||||||
void SavePlugin(PluginEntity pluginEntity);
|
void SavePlugin(PluginEntity pluginEntity);
|
||||||
|
|
||||||
void AddSetting(PluginSettingEntity pluginSettingEntity);
|
void AddSetting(PluginSettingEntity pluginSettingEntity);
|
||||||
PluginSettingEntity GetSettingByGuid(Guid pluginGuid);
|
PluginSettingEntity GetSettingByGuid(Guid pluginGuid);
|
||||||
PluginSettingEntity GetSettingByNameAndGuid(string name, Guid pluginGuid);
|
PluginSettingEntity GetSettingByNameAndGuid(string name, Guid pluginGuid);
|
||||||
void SaveSetting(PluginSettingEntity pluginSettingEntity);
|
void SaveSetting(PluginSettingEntity pluginSettingEntity);
|
||||||
|
|
||||||
|
void AddQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity);
|
||||||
|
List<PluginQueuedActionEntity> GetQueuedActions();
|
||||||
|
List<PluginQueuedActionEntity> GetQueuedActions(Guid pluginGuid);
|
||||||
|
void RemoveQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Artemis.Storage.Entities.Plugins;
|
using Artemis.Storage.Entities.Plugins;
|
||||||
using Artemis.Storage.Repositories.Interfaces;
|
using Artemis.Storage.Repositories.Interfaces;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
@ -14,6 +15,7 @@ namespace Artemis.Storage.Repositories
|
|||||||
_repository = repository;
|
_repository = repository;
|
||||||
|
|
||||||
_repository.Database.GetCollection<PluginSettingEntity>().EnsureIndex(s => new {s.Name, s.PluginGuid}, true);
|
_repository.Database.GetCollection<PluginSettingEntity>().EnsureIndex(s => new {s.Name, s.PluginGuid}, true);
|
||||||
|
_repository.Database.GetCollection<PluginQueuedActionEntity>().EnsureIndex(s => s.PluginGuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddPlugin(PluginEntity pluginEntity)
|
public void AddPlugin(PluginEntity pluginEntity)
|
||||||
@ -51,5 +53,24 @@ namespace Artemis.Storage.Repositories
|
|||||||
{
|
{
|
||||||
_repository.Upsert(pluginSettingEntity);
|
_repository.Upsert(pluginSettingEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<PluginQueuedActionEntity> GetQueuedActions()
|
||||||
|
{
|
||||||
|
return _repository.Query<PluginQueuedActionEntity>().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PluginQueuedActionEntity> GetQueuedActions(Guid pluginGuid)
|
||||||
|
{
|
||||||
|
return _repository.Query<PluginQueuedActionEntity>().Where(q => q.PluginGuid == pluginGuid).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity)
|
||||||
|
{
|
||||||
|
_repository.Upsert(pluginQueuedActionEntity);
|
||||||
|
}
|
||||||
|
public void RemoveQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity)
|
||||||
|
{
|
||||||
|
_repository.Delete<PluginQueuedActionEntity>(pluginQueuedActionEntity.Id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ namespace Artemis.UI.Shared
|
|||||||
byte g = Led.RgbLed.Color.GetG();
|
byte g = Led.RgbLed.Color.GetG();
|
||||||
byte b = Led.RgbLed.Color.GetB();
|
byte b = Led.RgbLed.Color.GetB();
|
||||||
|
|
||||||
_renderColor.A = (byte)(isDimmed ? 100 : 255);
|
_renderColor.A = (byte) (isDimmed ? 100 : 255);
|
||||||
_renderColor.A = isDimmed ? Dimmed : NonDimmed;
|
_renderColor.A = isDimmed ? Dimmed : NonDimmed;
|
||||||
_renderColor.R = r;
|
_renderColor.R = r;
|
||||||
_renderColor.G = g;
|
_renderColor.G = g;
|
||||||
@ -135,6 +135,8 @@ namespace Artemis.UI.Shared
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
double width = Led.RgbLed.Size.Width - deflateAmount;
|
||||||
|
double height = Led.RgbLed.Size.Height - deflateAmount;
|
||||||
// DisplayGeometry = Geometry.Parse(Led.RgbLed.ShapeData);
|
// DisplayGeometry = Geometry.Parse(Led.RgbLed.ShapeData);
|
||||||
DisplayGeometry = Geometry.Combine(
|
DisplayGeometry = Geometry.Combine(
|
||||||
Geometry.Empty,
|
Geometry.Empty,
|
||||||
@ -144,11 +146,27 @@ namespace Artemis.UI.Shared
|
|||||||
{
|
{
|
||||||
Children = new TransformCollection
|
Children = new TransformCollection
|
||||||
{
|
{
|
||||||
new ScaleTransform(Led.RgbLed.Size.Width - deflateAmount, Led.RgbLed.Size.Height - deflateAmount),
|
new ScaleTransform(width, height),
|
||||||
new TranslateTransform(deflateAmount / 2, deflateAmount / 2)
|
new TranslateTransform(deflateAmount / 2, deflateAmount / 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (DisplayGeometry.Bounds.Width > width)
|
||||||
|
{
|
||||||
|
DisplayGeometry = Geometry.Combine(Geometry.Empty, DisplayGeometry, GeometryCombineMode.Union, new TransformGroup
|
||||||
|
{
|
||||||
|
Children = new TransformCollection {new ScaleTransform(width / DisplayGeometry.Bounds.Width, 1)}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DisplayGeometry.Bounds.Height > height)
|
||||||
|
{
|
||||||
|
DisplayGeometry = Geometry.Combine(Geometry.Empty, DisplayGeometry, GeometryCombineMode.Union, new TransformGroup
|
||||||
|
{
|
||||||
|
Children = new TransformCollection {new ScaleTransform(1, height / DisplayGeometry.Bounds.Height)}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
@ -139,7 +140,11 @@ namespace Artemis.UI.Shared.Input
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (!SetAndNotify(ref _isDataModelViewModelOpen, value)) return;
|
if (!SetAndNotify(ref _isDataModelViewModelOpen, value)) return;
|
||||||
if (value) UpdateDataModelVisualization();
|
if (value)
|
||||||
|
{
|
||||||
|
UpdateDataModelVisualization();
|
||||||
|
OpenSelectedValue(DataModelViewModel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,6 +308,21 @@ namespace Artemis.UI.Shared.Input
|
|||||||
extraDataModelViewModel.Update(_dataModelUIService, new DataModelUpdateConfiguration(LoadEventChildren));
|
extraDataModelViewModel.Update(_dataModelUIService, new DataModelUpdateConfiguration(LoadEventChildren));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OpenSelectedValue(DataModelVisualizationViewModel dataModelPropertiesViewModel)
|
||||||
|
{
|
||||||
|
if (DataModelPath == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (dataModelPropertiesViewModel.Children.Any(c => c.DataModelPath != null && DataModelPath.Path.StartsWith(c.DataModelPath.Path)))
|
||||||
|
{
|
||||||
|
dataModelPropertiesViewModel.IsVisualizationExpanded = true;
|
||||||
|
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in dataModelPropertiesViewModel.Children)
|
||||||
|
{
|
||||||
|
OpenSelectedValue(dataModelVisualizationViewModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|||||||
@ -334,9 +334,6 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="buildinfo.json">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="Properties\Settings.settings">
|
<None Update="Properties\Settings.settings">
|
||||||
<Generator>SettingsSingleFileGenerator</Generator>
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
|||||||
@ -39,7 +39,7 @@ namespace Artemis.UI.Ninject.Factories
|
|||||||
public interface ISettingsVmFactory : IVmFactory
|
public interface ISettingsVmFactory : IVmFactory
|
||||||
{
|
{
|
||||||
PluginSettingsViewModel CreatePluginSettingsViewModel(Plugin plugin);
|
PluginSettingsViewModel CreatePluginSettingsViewModel(Plugin plugin);
|
||||||
PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeature feature);
|
PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield);
|
||||||
DeviceSettingsViewModel CreateDeviceSettingsViewModel(ArtemisDevice device);
|
DeviceSettingsViewModel CreateDeviceSettingsViewModel(ArtemisDevice device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -136,7 +136,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
RenderProfileElement.DisplayCondition.ChildRemoved += DisplayConditionOnChildrenModified;
|
RenderProfileElement.DisplayCondition.ChildRemoved += DisplayConditionOnChildrenModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
private void CoreServiceOnFrameRendered(object sender, FrameRenderedEventArgs e)
|
||||||
{
|
{
|
||||||
ActiveItem?.Evaluate();
|
ActiveItem?.Evaluate();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
@ -14,7 +13,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
{
|
{
|
||||||
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
|
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
private ILayerProperty? _selectedDataBinding;
|
private ILayerProperty _selectedDataBinding;
|
||||||
private int _selectedItemIndex;
|
private int _selectedItemIndex;
|
||||||
private bool _updating;
|
private bool _updating;
|
||||||
|
|
||||||
|
|||||||
@ -14,8 +14,8 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
public class ProfileLayerViewModel : CanvasViewModel
|
public class ProfileLayerViewModel : CanvasViewModel
|
||||||
{
|
{
|
||||||
private readonly ILayerEditorService _layerEditorService;
|
private readonly ILayerEditorService _layerEditorService;
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
|
||||||
private readonly PanZoomViewModel _panZoomViewModel;
|
private readonly PanZoomViewModel _panZoomViewModel;
|
||||||
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
private bool _isSelected;
|
private bool _isSelected;
|
||||||
private Geometry _shapeGeometry;
|
private Geometry _shapeGeometry;
|
||||||
private Rect _viewportRectangle;
|
private Rect _viewportRectangle;
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
d:DesignHeight="510.9" d:DesignWidth="800"
|
d:DesignHeight="510.9" d:DesignWidth="800"
|
||||||
d:DataContext="{d:DesignInstance {x:Type visualization:ProfileViewModel}}">
|
d:DataContext="{d:DesignInstance {x:Type visualization:ProfileViewModel}}">
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
@ -23,16 +24,16 @@
|
|||||||
<StackPanel Orientation="Vertical" />
|
<StackPanel Orientation="Vertical" />
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</ListBox.ItemsPanel>
|
</ListBox.ItemsPanel>
|
||||||
<ListBoxItem ToolTip="Pan over different parts of the surface">
|
<ListBoxItem ToolTip="Pan over different parts of the surface - Ctrl">
|
||||||
<materialDesign:PackIcon Kind="HandLeft" />
|
<materialDesign:PackIcon Kind="HandLeft" />
|
||||||
</ListBoxItem>
|
</ListBoxItem>
|
||||||
<ListBoxItem ToolTip="Transform layer shape (hold SHIFT for incremental changes and X/Y snapping)" IsEnabled="{Binding CanSelectEditTool}">
|
<ListBoxItem ToolTip="Transform layer shape (hold SHIFT for incremental changes and X/Y snapping) - Ctrl+T" IsEnabled="{Binding CanSelectEditTool}">
|
||||||
<materialDesign:PackIcon Kind="TransitConnectionVariant" />
|
<materialDesign:PackIcon Kind="TransitConnectionVariant" />
|
||||||
</ListBoxItem>
|
</ListBoxItem>
|
||||||
<ListBoxItem ToolTip="Change layer selection (hold SHIFT to add to existing selection)">
|
<ListBoxItem ToolTip="Change layer selection (hold SHIFT to add to existing selection) - Ctrl+Q">
|
||||||
<materialDesign:PackIcon Kind="SelectionDrag" />
|
<materialDesign:PackIcon Kind="SelectionDrag" />
|
||||||
</ListBoxItem>
|
</ListBoxItem>
|
||||||
<ListBoxItem ToolTip="Remove from layer selection">
|
<ListBoxItem ToolTip="Remove from layer selection - Ctrl+W">
|
||||||
<materialDesign:PackIcon Kind="SelectOff" />
|
<materialDesign:PackIcon Kind="SelectOff" />
|
||||||
</ListBoxItem>
|
</ListBoxItem>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
@ -133,9 +134,9 @@
|
|||||||
<materialDesign:Card Padding="8">
|
<materialDesign:Card Padding="8">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}"
|
<CheckBox Style="{StaticResource MaterialDesignCheckBox}"
|
||||||
IsChecked="{Binding HighlightSelectedLayer.Value}"
|
IsChecked="{Binding FocusSelectedLayer.Value}"
|
||||||
ToolTip="If selected, dims all LEDs that are not part of the selected layer">
|
ToolTip="If selected, dims all LEDs that are not part of the selected layer">
|
||||||
Highlight LEDs of selected layer
|
Focus selected layer
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}"
|
<CheckBox Style="{StaticResource MaterialDesignCheckBox}"
|
||||||
Margin="10 0 0 0"
|
Margin="10 0 0 0"
|
||||||
|
|||||||
@ -15,7 +15,7 @@ using Stylet;
|
|||||||
|
|
||||||
namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
||||||
{
|
{
|
||||||
public class ProfileViewModel : Conductor<CanvasViewModel>.Collection.AllActive, IProfileEditorPanelViewModel, IHandle<MainWindowFocusChangedEvent>, IHandle<MainWindowKeyEvent>
|
public class ProfileViewModel : Conductor<CanvasViewModel>.Collection.AllActive, IProfileEditorPanelViewModel, IHandle<MainWindowKeyEvent>
|
||||||
{
|
{
|
||||||
private readonly ICoreService _coreService;
|
private readonly ICoreService _coreService;
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
@ -31,7 +31,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
private bool _canSelectEditTool;
|
private bool _canSelectEditTool;
|
||||||
private BindableCollection<ArtemisDevice> _devices;
|
private BindableCollection<ArtemisDevice> _devices;
|
||||||
private BindableCollection<ArtemisLed> _highlightedLeds;
|
private BindableCollection<ArtemisLed> _highlightedLeds;
|
||||||
private PluginSetting<bool> _highlightSelectedLayer;
|
private PluginSetting<bool> _focusSelectedLayer;
|
||||||
private DateTime _lastUpdate;
|
private DateTime _lastUpdate;
|
||||||
private PanZoomViewModel _panZoomViewModel;
|
private PanZoomViewModel _panZoomViewModel;
|
||||||
private Layer _previousSelectedLayer;
|
private Layer _previousSelectedLayer;
|
||||||
@ -55,7 +55,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
eventAggregator.Subscribe(this);
|
eventAggregator.Subscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool CanSelectEditTool
|
public bool CanSelectEditTool
|
||||||
{
|
{
|
||||||
get => _canSelectEditTool;
|
get => _canSelectEditTool;
|
||||||
@ -86,10 +85,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
set => SetAndNotify(ref _alwaysApplyDataBindings, value);
|
set => SetAndNotify(ref _alwaysApplyDataBindings, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PluginSetting<bool> HighlightSelectedLayer
|
public PluginSetting<bool> FocusSelectedLayer
|
||||||
{
|
{
|
||||||
get => _highlightSelectedLayer;
|
get => _focusSelectedLayer;
|
||||||
set => SetAndNotify(ref _highlightSelectedLayer, value);
|
set => SetAndNotify(ref _focusSelectedLayer, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VisualizationToolViewModel ActiveToolViewModel
|
public VisualizationToolViewModel ActiveToolViewModel
|
||||||
@ -146,12 +145,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
ApplyActiveProfile();
|
ApplyActiveProfile();
|
||||||
|
|
||||||
AlwaysApplyDataBindings = _settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true);
|
AlwaysApplyDataBindings = _settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true);
|
||||||
HighlightSelectedLayer = _settingsService.GetSetting("ProfileEditor.HighlightSelectedLayer", true);
|
FocusSelectedLayer = _settingsService.GetSetting("ProfileEditor.FocusSelectedLayer", true);
|
||||||
|
|
||||||
_lastUpdate = DateTime.Now;
|
_lastUpdate = DateTime.Now;
|
||||||
_coreService.FrameRendered += OnFrameRendered;
|
_coreService.FrameRendered += OnFrameRendered;
|
||||||
|
|
||||||
HighlightSelectedLayer.SettingChanged += HighlightSelectedLayerOnSettingChanged;
|
FocusSelectedLayer.SettingChanged += HighlightSelectedLayerOnSettingChanged;
|
||||||
_rgbService.DeviceAdded += RgbServiceOnDevicesModified;
|
_rgbService.DeviceAdded += RgbServiceOnDevicesModified;
|
||||||
_rgbService.DeviceRemoved += RgbServiceOnDevicesModified;
|
_rgbService.DeviceRemoved += RgbServiceOnDevicesModified;
|
||||||
_profileEditorService.ProfileSelected += OnProfileSelected;
|
_profileEditorService.ProfileSelected += OnProfileSelected;
|
||||||
@ -164,7 +163,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
protected override void OnClose()
|
protected override void OnClose()
|
||||||
{
|
{
|
||||||
_coreService.FrameRendered -= OnFrameRendered;
|
_coreService.FrameRendered -= OnFrameRendered;
|
||||||
HighlightSelectedLayer.SettingChanged -= HighlightSelectedLayerOnSettingChanged;
|
FocusSelectedLayer.SettingChanged -= HighlightSelectedLayerOnSettingChanged;
|
||||||
_rgbService.DeviceAdded -= RgbServiceOnDevicesModified;
|
_rgbService.DeviceAdded -= RgbServiceOnDevicesModified;
|
||||||
_rgbService.DeviceRemoved -= RgbServiceOnDevicesModified;
|
_rgbService.DeviceRemoved -= RgbServiceOnDevicesModified;
|
||||||
_profileEditorService.ProfileSelected -= OnProfileSelected;
|
_profileEditorService.ProfileSelected -= OnProfileSelected;
|
||||||
@ -174,7 +173,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
_previousSelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated;
|
_previousSelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated;
|
||||||
|
|
||||||
AlwaysApplyDataBindings.Save();
|
AlwaysApplyDataBindings.Save();
|
||||||
HighlightSelectedLayer.Save();
|
FocusSelectedLayer.Save();
|
||||||
|
|
||||||
base.OnClose();
|
base.OnClose();
|
||||||
}
|
}
|
||||||
@ -191,8 +190,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
|
|
||||||
// Add new layers missing a VM
|
// Add new layers missing a VM
|
||||||
foreach (Layer layer in layers)
|
foreach (Layer layer in layers)
|
||||||
|
{
|
||||||
if (layerViewModels.All(vm => vm.Layer != layer))
|
if (layerViewModels.All(vm => vm.Layer != layer))
|
||||||
Items.Add(_profileLayerVmFactory.Create(layer, PanZoomViewModel));
|
Items.Add(_profileLayerVmFactory.Create(layer, PanZoomViewModel));
|
||||||
|
}
|
||||||
|
|
||||||
// Remove layers that no longer exist
|
// Remove layers that no longer exist
|
||||||
IEnumerable<ProfileLayerViewModel> toRemove = layerViewModels.Where(vm => !layers.Contains(vm.Layer));
|
IEnumerable<ProfileLayerViewModel> toRemove = layerViewModels.Where(vm => !layers.Contains(vm.Layer));
|
||||||
@ -209,7 +210,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
private void UpdateLedsDimStatus()
|
private void UpdateLedsDimStatus()
|
||||||
{
|
{
|
||||||
HighlightedLeds.Clear();
|
HighlightedLeds.Clear();
|
||||||
if (HighlightSelectedLayer.Value && _profileEditorService.SelectedProfileElement is Layer layer)
|
if (FocusSelectedLayer.Value && _profileEditorService.SelectedProfileElement is Layer layer)
|
||||||
HighlightedLeds.AddRange(layer.Leds);
|
HighlightedLeds.AddRange(layer.Leds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,6 +235,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
|
|
||||||
private void ActivateToolByIndex(int value)
|
private void ActivateToolByIndex(int value)
|
||||||
{
|
{
|
||||||
|
if (value == 1 && !CanSelectEditTool)
|
||||||
|
return;
|
||||||
|
|
||||||
switch (value)
|
switch (value)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
@ -274,12 +278,14 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
{
|
{
|
||||||
((IInputElement) sender).CaptureMouse();
|
((IInputElement) sender).CaptureMouse();
|
||||||
ActiveToolViewModel?.MouseDown(sender, e);
|
ActiveToolViewModel?.MouseDown(sender, e);
|
||||||
|
e.Handled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CanvasMouseUp(object sender, MouseButtonEventArgs e)
|
public void CanvasMouseUp(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
((IInputElement) sender).ReleaseMouseCapture();
|
((IInputElement) sender).ReleaseMouseCapture();
|
||||||
ActiveToolViewModel?.MouseUp(sender, e);
|
ActiveToolViewModel?.MouseUp(sender, e);
|
||||||
|
e.Handled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CanvasMouseMove(object sender, MouseEventArgs e)
|
public void CanvasMouseMove(object sender, MouseEventArgs e)
|
||||||
@ -382,24 +388,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
ActivateToolByIndex(2);
|
ActivateToolByIndex(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(MainWindowFocusChangedEvent message)
|
|
||||||
{
|
|
||||||
// if (PauseRenderingOnFocusLoss == null || ScreenState != ScreenState.Active)
|
|
||||||
// return;
|
|
||||||
//
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// if (PauseRenderingOnFocusLoss.Value && !message.IsFocused)
|
|
||||||
// _updateTrigger.Stop();
|
|
||||||
// else if (PauseRenderingOnFocusLoss.Value && message.IsFocused)
|
|
||||||
// _updateTrigger.Start();
|
|
||||||
// }
|
|
||||||
// catch (NullReferenceException)
|
|
||||||
// {
|
|
||||||
// // TODO: Remove when fixed in RGB.NET, or avoid double stopping
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(MainWindowKeyEvent message)
|
public void Handle(MainWindowKeyEvent message)
|
||||||
{
|
{
|
||||||
if (message.KeyDown)
|
if (message.KeyDown)
|
||||||
@ -412,6 +400,17 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
|||||||
}
|
}
|
||||||
|
|
||||||
ActiveToolViewModel?.KeyDown(message.EventArgs);
|
ActiveToolViewModel?.KeyDown(message.EventArgs);
|
||||||
|
|
||||||
|
// If T is pressed while Ctrl is down, that makes it Ctrl+T > swap to transformation tool on Ctrl release
|
||||||
|
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.LeftCtrl))
|
||||||
|
{
|
||||||
|
if (message.EventArgs.Key == Key.T)
|
||||||
|
_previousTool = 1;
|
||||||
|
else if (message.EventArgs.Key == Key.Q)
|
||||||
|
_previousTool = 2;
|
||||||
|
else if (message.EventArgs.Key == Key.W)
|
||||||
|
_previousTool = 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@ -24,6 +24,12 @@
|
|||||||
<mde:MaterialWindow.InputBindings>
|
<mde:MaterialWindow.InputBindings>
|
||||||
<KeyBinding Command="{s:Action ClearSelection}" Key="Escape" />
|
<KeyBinding Command="{s:Action ClearSelection}" Key="Escape" />
|
||||||
</mde:MaterialWindow.InputBindings>
|
</mde:MaterialWindow.InputBindings>
|
||||||
|
|
||||||
|
<materialDesign:DialogHost IsTabStop="False"
|
||||||
|
Focusable="False"
|
||||||
|
Identifier="DeviceDialog"
|
||||||
|
DialogTheme="Inherit"
|
||||||
|
SnackbarMessageQueue="{Binding DeviceMessageQueue}">
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<mde:AppBar Type="Dense"
|
<mde:AppBar Type="Dense"
|
||||||
Title="{Binding Device.RgbDevice.DeviceInfo.Model}"
|
Title="{Binding Device.RgbDevice.DeviceInfo.Model}"
|
||||||
@ -112,6 +118,7 @@
|
|||||||
<materialDesign:Card Grid.Column="2"
|
<materialDesign:Card Grid.Column="2"
|
||||||
materialDesign:ShadowAssist.ShadowDepth="Depth3"
|
materialDesign:ShadowAssist.ShadowDepth="Depth3"
|
||||||
Background="{DynamicResource MaterialDesignPaper}">
|
Background="{DynamicResource MaterialDesignPaper}">
|
||||||
|
<Grid>
|
||||||
<TabControl
|
<TabControl
|
||||||
Style="{StaticResource MaterialDesignTabControl}"
|
Style="{StaticResource MaterialDesignTabControl}"
|
||||||
ItemsSource="{Binding Items}"
|
ItemsSource="{Binding Items}"
|
||||||
@ -127,7 +134,14 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</TabControl.ContentTemplate>
|
</TabControl.ContentTemplate>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
|
<materialDesign:Snackbar x:Name="DeviceSnackbar"
|
||||||
|
MessageQueue="{Binding DeviceMessageQueue}"
|
||||||
|
materialDesign:SnackbarMessage.InlineActionButtonMaxHeight="80"
|
||||||
|
materialDesign:SnackbarMessage.ContentMaxHeight="200" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</materialDesign:Card>
|
</materialDesign:Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
</materialDesign:DialogHost>
|
||||||
</mde:MaterialWindow>
|
</mde:MaterialWindow>
|
||||||
@ -9,6 +9,7 @@ using Artemis.Core.Services;
|
|||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Screens.Shared;
|
using Artemis.UI.Screens.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
using MaterialDesignThemes.Wpf;
|
||||||
using Ookii.Dialogs.Wpf;
|
using Ookii.Dialogs.Wpf;
|
||||||
using RGB.NET.Layout;
|
using RGB.NET.Layout;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
@ -21,6 +22,7 @@ namespace Artemis.UI.Screens.Settings.Device
|
|||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IRgbService _rgbService;
|
||||||
private ArtemisLed _selectedLed;
|
private ArtemisLed _selectedLed;
|
||||||
|
private SnackbarMessageQueue _deviceMessageQueue;
|
||||||
|
|
||||||
public DeviceDialogViewModel(ArtemisDevice device, IDeviceService deviceService, IRgbService rgbService, IDialogService dialogService, IDeviceDebugVmFactory factory)
|
public DeviceDialogViewModel(ArtemisDevice device, IDeviceService deviceService, IRgbService rgbService, IDialogService dialogService, IDeviceDebugVmFactory factory)
|
||||||
{
|
{
|
||||||
@ -38,9 +40,28 @@ namespace Artemis.UI.Screens.Settings.Device
|
|||||||
DisplayName = $"{device.RgbDevice.DeviceInfo.Model} | Artemis";
|
DisplayName = $"{device.RgbDevice.DeviceInfo.Model} | Artemis";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnInitialActivate()
|
||||||
|
{
|
||||||
|
DeviceMessageQueue = new SnackbarMessageQueue(TimeSpan.FromSeconds(5));
|
||||||
|
Device.DeviceUpdated += DeviceOnDeviceUpdated;
|
||||||
|
base.OnInitialActivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnClose()
|
||||||
|
{
|
||||||
|
Device.DeviceUpdated -= DeviceOnDeviceUpdated;
|
||||||
|
base.OnClose();
|
||||||
|
}
|
||||||
|
|
||||||
public ArtemisDevice Device { get; }
|
public ArtemisDevice Device { get; }
|
||||||
public PanZoomViewModel PanZoomViewModel { get; }
|
public PanZoomViewModel PanZoomViewModel { get; }
|
||||||
|
|
||||||
|
public SnackbarMessageQueue DeviceMessageQueue
|
||||||
|
{
|
||||||
|
get => _deviceMessageQueue;
|
||||||
|
set => SetAndNotify(ref _deviceMessageQueue, value);
|
||||||
|
}
|
||||||
|
|
||||||
public ArtemisLed SelectedLed
|
public ArtemisLed SelectedLed
|
||||||
{
|
{
|
||||||
get => _selectedLed;
|
get => _selectedLed;
|
||||||
@ -50,7 +71,10 @@ namespace Artemis.UI.Screens.Settings.Device
|
|||||||
NotifyOfPropertyChange(nameof(SelectedLeds));
|
NotifyOfPropertyChange(nameof(SelectedLeds));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public List<ArtemisLed> SelectedLeds => SelectedLed != null ? new List<ArtemisLed> { SelectedLed } : null;
|
|
||||||
|
public bool CanExportLayout => Device.Layout?.IsValid ?? false;
|
||||||
|
|
||||||
|
public List<ArtemisLed> SelectedLeds => SelectedLed != null ? new List<ArtemisLed> {SelectedLed} : null;
|
||||||
|
|
||||||
public bool CanOpenImageDirectory => Device.Layout?.Image != null;
|
public bool CanOpenImageDirectory => Device.Layout?.Image != null;
|
||||||
|
|
||||||
@ -165,5 +189,14 @@ namespace Artemis.UI.Screens.Settings.Device
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
// ReSharper restore UnusedMember.Global
|
// ReSharper restore UnusedMember.Global
|
||||||
|
|
||||||
|
#region Event handlers
|
||||||
|
|
||||||
|
private void DeviceOnDeviceUpdated(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
NotifyOfPropertyChange(nameof(CanExportLayout));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,15 +91,21 @@
|
|||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Layout file path</TextBlock>
|
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Default layout file path</TextBlock>
|
||||||
<Button Grid.Column="1" Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Copy path to clipboard" Width="24" Height="24">
|
<Button Grid.Column="1"
|
||||||
|
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||||
|
ToolTip="Copy path to clipboard"
|
||||||
|
Width="24"
|
||||||
|
Height="24"
|
||||||
|
Command="{s:Action CopyToClipboard}"
|
||||||
|
CommandParameter="{Binding DefaultLayoutPath}">
|
||||||
<materialDesign:PackIcon Kind="ContentCopy" Width="18" Height="18" />
|
<materialDesign:PackIcon Kind="ContentCopy" Width="18" Height="18" />
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
Text="{Binding Device.Layout.FilePath}" />
|
Text="{Binding DefaultLayoutPath}" />
|
||||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
@ -108,14 +114,19 @@
|
|||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Image file path</TextBlock>
|
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Image file path</TextBlock>
|
||||||
<Button Grid.Column="1" Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Copy path to clipboard" Width="24" Height="24">
|
<Button Grid.Column="1" Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||||
|
ToolTip="Copy path to clipboard"
|
||||||
|
Width="24"
|
||||||
|
Height="24"
|
||||||
|
Command="{s:Action CopyToClipboard}"
|
||||||
|
CommandParameter="{Binding Device.Layout.Image.LocalPath}">
|
||||||
<materialDesign:PackIcon Kind="ContentCopy" Width="18" Height="18" />
|
<materialDesign:PackIcon Kind="ContentCopy" Width="18" Height="18" />
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
Text="{Binding Device.Layout.Image}" />
|
Text="{Binding Device.Layout.Image.LocalPath}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using Artemis.Core;
|
using System.Windows;
|
||||||
|
using Artemis.Core;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
|
|
||||||
@ -6,6 +7,8 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
|||||||
{
|
{
|
||||||
public class DeviceInfoTabViewModel : Screen
|
public class DeviceInfoTabViewModel : Screen
|
||||||
{
|
{
|
||||||
|
private string _defaultLayoutPath;
|
||||||
|
|
||||||
public DeviceInfoTabViewModel(ArtemisDevice device)
|
public DeviceInfoTabViewModel(ArtemisDevice device)
|
||||||
{
|
{
|
||||||
Device = device;
|
Device = device;
|
||||||
@ -14,5 +17,23 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
|||||||
|
|
||||||
public bool IsKeyboard => Device.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard;
|
public bool IsKeyboard => Device.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard;
|
||||||
public ArtemisDevice Device { get; }
|
public ArtemisDevice Device { get; }
|
||||||
|
|
||||||
|
public string DefaultLayoutPath
|
||||||
|
{
|
||||||
|
get => _defaultLayoutPath;
|
||||||
|
set => SetAndNotify(ref _defaultLayoutPath, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyToClipboard(string content)
|
||||||
|
{
|
||||||
|
Clipboard.SetText(content);
|
||||||
|
((DeviceDialogViewModel) Parent).DeviceMessageQueue.Enqueue("Copied path to clipboard.");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInitialActivate()
|
||||||
|
{
|
||||||
|
DefaultLayoutPath = Device.DeviceProvider.LoadLayout(Device).FilePath;
|
||||||
|
base.OnInitialActivate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,14 +174,23 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</materialDesign:HintAssist.Hint>
|
</materialDesign:HintAssist.Hint>
|
||||||
</TextBox>
|
</TextBox>
|
||||||
|
|
||||||
<Button Style="{StaticResource MaterialDesignRaisedButton}" Margin="0 8 8 0" Command="{s:Action SelectPhysicalLayout}">
|
|
||||||
SELECT PHYSICAL LAYOUT (PLACEHOLDER)
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
|
<Grid Grid.Row="1" >
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button Grid.Column="0"
|
||||||
|
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||||
|
Margin="0 8 8 0"
|
||||||
|
Command="{s:Action SelectPhysicalLayout}"
|
||||||
|
ToolTip="Restart device setup, allowing you to select a new physical and logical layout">
|
||||||
|
RESTART SETUP
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
<Button Style="{StaticResource MaterialDesignOutlinedButton}" IsCancel="True" Margin="0 8 8 0" Command="{s:Action Reset}">
|
<Button Style="{StaticResource MaterialDesignOutlinedButton}" IsCancel="True" Margin="0 8 8 0" Command="{s:Action Reset}">
|
||||||
RESET
|
RESET
|
||||||
</Button>
|
</Button>
|
||||||
@ -190,4 +199,6 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -15,7 +15,6 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
|||||||
public class DevicePropertiesTabViewModel : Screen
|
public class DevicePropertiesTabViewModel : Screen
|
||||||
{
|
{
|
||||||
private readonly ICoreService _coreService;
|
private readonly ICoreService _coreService;
|
||||||
private readonly IMessageService _messageService;
|
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IRgbService _rgbService;
|
||||||
private float _blueScale;
|
private float _blueScale;
|
||||||
@ -34,13 +33,11 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
|||||||
public DevicePropertiesTabViewModel(ArtemisDevice device,
|
public DevicePropertiesTabViewModel(ArtemisDevice device,
|
||||||
ICoreService coreService,
|
ICoreService coreService,
|
||||||
IRgbService rgbService,
|
IRgbService rgbService,
|
||||||
IMessageService messageService,
|
|
||||||
IDialogService dialogService,
|
IDialogService dialogService,
|
||||||
IModelValidator<DevicePropertiesTabViewModel> validator) : base(validator)
|
IModelValidator<DevicePropertiesTabViewModel> validator) : base(validator)
|
||||||
{
|
{
|
||||||
_coreService = coreService;
|
_coreService = coreService;
|
||||||
_rgbService = rgbService;
|
_rgbService = rgbService;
|
||||||
_messageService = messageService;
|
|
||||||
_dialogService = dialogService;
|
_dialogService = dialogService;
|
||||||
|
|
||||||
Device = device;
|
Device = device;
|
||||||
@ -115,7 +112,7 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
|||||||
if (e.OriginalSource is Button)
|
if (e.OriginalSource is Button)
|
||||||
{
|
{
|
||||||
Device.CustomLayoutPath = null;
|
Device.CustomLayoutPath = null;
|
||||||
_messageService.ShowMessage("Cleared imported layout");
|
((DeviceDialogViewModel) Parent).DeviceMessageQueue.Enqueue("Cleared imported layout.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,13 +123,13 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
|||||||
if (result == true)
|
if (result == true)
|
||||||
{
|
{
|
||||||
Device.CustomLayoutPath = dialog.FileName;
|
Device.CustomLayoutPath = dialog.FileName;
|
||||||
_messageService.ShowMessage($"Imported layout from {dialog.FileName}");
|
((DeviceDialogViewModel) Parent).DeviceMessageQueue.Enqueue($"Imported layout from {dialog.FileName}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SelectPhysicalLayout()
|
public async Task SelectPhysicalLayout()
|
||||||
{
|
{
|
||||||
await _dialogService.ShowDialog<DeviceLayoutDialogViewModel>(new Dictionary<string, object> {{"device", Device}});
|
await _dialogService.ShowDialogAt<DeviceLayoutDialogViewModel>("DeviceDialog", new Dictionary<string, object> {{"device", Device}});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Apply()
|
public async Task Apply()
|
||||||
@ -151,6 +148,7 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
|||||||
Device.RedScale = RedScale / 100f;
|
Device.RedScale = RedScale / 100f;
|
||||||
Device.GreenScale = GreenScale / 100f;
|
Device.GreenScale = GreenScale / 100f;
|
||||||
Device.BlueScale = BlueScale / 100f;
|
Device.BlueScale = BlueScale / 100f;
|
||||||
|
_rgbService.SaveDevice(Device);
|
||||||
|
|
||||||
_coreService.ModuleRenderingDisabled = false;
|
_coreService.ModuleRenderingDisabled = false;
|
||||||
}
|
}
|
||||||
@ -195,7 +193,8 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
|||||||
|
|
||||||
private void DeviceOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
private void DeviceOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PropertyName == nameof(Device.CustomLayoutPath)) _rgbService.ApplyBestDeviceLayout(Device);
|
if (e.PropertyName == nameof(Device.CustomLayoutPath))
|
||||||
|
_rgbService.ApplyBestDeviceLayout(Device);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFrameRendering(object sender, FrameRenderingEventArgs e)
|
private void OnFrameRendering(object sender, FrameRenderingEventArgs e)
|
||||||
|
|||||||
@ -32,13 +32,20 @@
|
|||||||
<ProgressBar IsIndeterminate="True" />
|
<ProgressBar IsIndeterminate="True" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<ItemsControl ItemsSource="{Binding Changes}" Visibility="{Binding RetrievingChanges, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
<Grid Visibility="{Binding RetrievingChanges, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
||||||
|
<ItemsControl ItemsSource="{Binding Changes}" Visibility="{Binding HasChanges, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<TextBlock Text="{Binding}" Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Foreground="{DynamicResource MaterialDesignBodyLight}"></TextBlock>
|
<TextBlock Text="{Binding}" Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Foreground="{DynamicResource MaterialDesignBodyLight}"></TextBlock>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
<TextBlock Visibility="{Binding HasChanges, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}"
|
||||||
|
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||||
|
Foreground="{DynamicResource MaterialDesignBodyLight}">
|
||||||
|
Couldn't retrieve changes, sorry :(
|
||||||
|
</TextBlock>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 8 0 0">
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 8 0 0">
|
||||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||||
|
|||||||
@ -1,28 +1,42 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Services;
|
using Artemis.UI.Services;
|
||||||
using Artemis.UI.Services.Models.UpdateService;
|
using Artemis.UI.Services.Models.UpdateService;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Stylet;
|
using Stylet;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Settings.Dialogs
|
namespace Artemis.UI.Screens.Settings.Dialogs
|
||||||
{
|
{
|
||||||
public class UpdateDialogViewModel : DialogViewModelBase
|
public class UpdateDialogViewModel : DialogViewModelBase
|
||||||
{
|
{
|
||||||
private readonly DevOpsBuild _buildInfo;
|
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
|
|
||||||
|
// Based on https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#skipping-ci-for-individual-commits
|
||||||
|
private readonly string[] _excludedCommitMessages =
|
||||||
|
{
|
||||||
|
"[skip ci]",
|
||||||
|
"[ci skip]",
|
||||||
|
"skip-checks: true",
|
||||||
|
"skip-checks:true",
|
||||||
|
"[skip azurepipelines]",
|
||||||
|
"[azurepipelines skip]",
|
||||||
|
"[skip azpipelines]",
|
||||||
|
"[azpipelines skip]",
|
||||||
|
"[skip azp]",
|
||||||
|
"[azp skip]",
|
||||||
|
"***NO_CI***"
|
||||||
|
};
|
||||||
|
|
||||||
private readonly IMessageService _messageService;
|
private readonly IMessageService _messageService;
|
||||||
private readonly IUpdateService _updateService;
|
private readonly IUpdateService _updateService;
|
||||||
private bool _canUpdate = true;
|
private bool _canUpdate = true;
|
||||||
|
private bool _hasChanges;
|
||||||
private bool _retrievingChanges;
|
private bool _retrievingChanges;
|
||||||
|
|
||||||
public UpdateDialogViewModel(DevOpsBuild buildInfo, IUpdateService updateService, IDialogService dialogService, IMessageService messageService)
|
public UpdateDialogViewModel(DevOpsBuild buildInfo, IUpdateService updateService, IDialogService dialogService, IMessageService messageService)
|
||||||
{
|
{
|
||||||
_buildInfo = buildInfo;
|
|
||||||
_updateService = updateService;
|
_updateService = updateService;
|
||||||
_dialogService = dialogService;
|
_dialogService = dialogService;
|
||||||
_messageService = messageService;
|
_messageService = messageService;
|
||||||
@ -44,12 +58,37 @@ namespace Artemis.UI.Screens.Settings.Dialogs
|
|||||||
set => SetAndNotify(ref _retrievingChanges, value);
|
set => SetAndNotify(ref _retrievingChanges, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasChanges
|
||||||
|
{
|
||||||
|
get => _hasChanges;
|
||||||
|
set => SetAndNotify(ref _hasChanges, value);
|
||||||
|
}
|
||||||
|
|
||||||
public bool CanUpdate
|
public bool CanUpdate
|
||||||
{
|
{
|
||||||
get => _canUpdate;
|
get => _canUpdate;
|
||||||
set => SetAndNotify(ref _canUpdate, value);
|
set => SetAndNotify(ref _canUpdate, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Update()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CanUpdate = false;
|
||||||
|
await _updateService.ApplyUpdate();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_dialogService.ShowExceptionDialog("An exception occurred while applying the update", e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CanUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Session.Close(true);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task GetBuildChanges()
|
private async Task GetBuildChanges()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -70,9 +109,10 @@ namespace Artemis.UI.Screens.Settings.Dialogs
|
|||||||
Changes.AddRange(difference.Commits.Where(c => c.Parents.Count == 1)
|
Changes.AddRange(difference.Commits.Where(c => c.Parents.Count == 1)
|
||||||
.SelectMany(c => c.Commit.Message.Split("\n"))
|
.SelectMany(c => c.Commit.Message.Split("\n"))
|
||||||
.Select(m => m.Trim())
|
.Select(m => m.Trim())
|
||||||
.Where(m => !string.IsNullOrWhiteSpace(m))
|
.Where(m => !string.IsNullOrWhiteSpace(m) && !_excludedCommitMessages.Contains(m))
|
||||||
.OrderBy(m => m)
|
.OrderBy(m => m)
|
||||||
);
|
);
|
||||||
|
HasChanges = Changes.Any();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -84,24 +124,5 @@ namespace Artemis.UI.Screens.Settings.Dialogs
|
|||||||
RetrievingChanges = false;
|
RetrievingChanges = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Update()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CanUpdate = false;
|
|
||||||
await _updateService.ApplyUpdate();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_dialogService.ShowExceptionDialog("An exception occurred while applying the update", e);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
CanUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Session.Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,17 +3,20 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.LayerBrushes;
|
using Artemis.Core.LayerBrushes;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.Core.Services.Core;
|
||||||
using Artemis.UI.Properties;
|
using Artemis.UI.Properties;
|
||||||
using Artemis.UI.Screens.StartupWizard;
|
using Artemis.UI.Screens.StartupWizard;
|
||||||
using Artemis.UI.Services;
|
using Artemis.UI.Services;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Artemis.UI.Utilities;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
@ -70,7 +73,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
|
|||||||
_defaultLayerBrushDescriptor = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
|
_defaultLayerBrushDescriptor = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
|
||||||
{
|
{
|
||||||
LayerBrushProviderId = "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba",
|
LayerBrushProviderId = "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba",
|
||||||
BrushType = "ColorBrush"
|
BrushType = "SolidBrush"
|
||||||
});
|
});
|
||||||
|
|
||||||
WebServerPortSetting = _settingsService.GetSetting("WebServer.Port", 9696);
|
WebServerPortSetting = _settingsService.GetSetting("WebServer.Port", 9696);
|
||||||
@ -274,11 +277,21 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
CanOfferUpdatesIfFound = false;
|
CanOfferUpdatesIfFound = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
bool updateFound = await _updateService.OfferUpdateIfFound();
|
bool updateFound = await _updateService.OfferUpdateIfFound();
|
||||||
if (!updateFound)
|
if (!updateFound)
|
||||||
_messageService.ShowMessage("You are already running the latest Artemis build. (☞゚ヮ゚)☞");
|
_messageService.ShowMessage("You are already running the latest Artemis build. (☞゚ヮ゚)☞");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_messageService.ShowMessage($"Failed to check for updates: {exception.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
CanOfferUpdatesIfFound = true;
|
CanOfferUpdatesIfFound = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnInitialActivate()
|
protected override void OnInitialActivate()
|
||||||
{
|
{
|
||||||
@ -296,7 +309,8 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
|
|||||||
if (File.Exists(autoRunFile))
|
if (File.Exists(autoRunFile))
|
||||||
File.Delete(autoRunFile);
|
File.Delete(autoRunFile);
|
||||||
|
|
||||||
// TODO: Don't do anything if running a development build, only auto-run release builds
|
if (Constants.BuildInfo.IsLocalBuild)
|
||||||
|
return;
|
||||||
|
|
||||||
// Create or remove the task if necessary
|
// Create or remove the task if necessary
|
||||||
try
|
try
|
||||||
@ -304,26 +318,13 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
|
|||||||
bool taskCreated = false;
|
bool taskCreated = false;
|
||||||
if (!recreate)
|
if (!recreate)
|
||||||
{
|
{
|
||||||
Process schtasks = new()
|
taskCreated = SettingsUtilities.IsAutoRunTaskCreated();
|
||||||
{
|
|
||||||
StartInfo =
|
|
||||||
{
|
|
||||||
WindowStyle = ProcessWindowStyle.Hidden,
|
|
||||||
UseShellExecute = true,
|
|
||||||
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
|
|
||||||
Arguments = "/TN \"Artemis 2 autorun\""
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
schtasks.Start();
|
|
||||||
schtasks.WaitForExit();
|
|
||||||
taskCreated = schtasks.ExitCode == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StartWithWindows && !taskCreated)
|
if (StartWithWindows && !taskCreated)
|
||||||
CreateAutoRunTask();
|
SettingsUtilities.CreateAutoRunTask(TimeSpan.FromSeconds(AutoRunDelay));
|
||||||
else if (!StartWithWindows && taskCreated)
|
else if (!StartWithWindows && taskCreated)
|
||||||
RemoveAutoRunTask();
|
SettingsUtilities.RemoveAutoRunTask();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -331,70 +332,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateAutoRunTask()
|
|
||||||
{
|
|
||||||
XDocument document = XDocument.Parse(Resources.artemis_autorun);
|
|
||||||
XElement task = document.Descendants().First();
|
|
||||||
|
|
||||||
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Date")
|
|
||||||
.SetValue(DateTime.Now);
|
|
||||||
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Author")
|
|
||||||
.SetValue(WindowsIdentity.GetCurrent().Name);
|
|
||||||
|
|
||||||
task.Descendants().First(d => d.Name.LocalName == "Triggers").Descendants().First(d => d.Name.LocalName == "LogonTrigger").Descendants().First(d => d.Name.LocalName == "Delay")
|
|
||||||
.SetValue(TimeSpan.FromSeconds(AutoRunDelay));
|
|
||||||
|
|
||||||
task.Descendants().First(d => d.Name.LocalName == "Principals").Descendants().First(d => d.Name.LocalName == "Principal").Descendants().First(d => d.Name.LocalName == "UserId")
|
|
||||||
.SetValue(WindowsIdentity.GetCurrent().User.Value);
|
|
||||||
|
|
||||||
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "WorkingDirectory")
|
|
||||||
.SetValue(Constants.ApplicationFolder);
|
|
||||||
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "Command")
|
|
||||||
.SetValue("\"" + Constants.ExecutablePath + "\"");
|
|
||||||
|
|
||||||
string xmlPath = Path.GetTempFileName();
|
|
||||||
using (Stream fileStream = new FileStream(xmlPath, FileMode.Create))
|
|
||||||
{
|
|
||||||
document.Save(fileStream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Process schtasks = new()
|
|
||||||
{
|
|
||||||
StartInfo =
|
|
||||||
{
|
|
||||||
WindowStyle = ProcessWindowStyle.Hidden,
|
|
||||||
UseShellExecute = true,
|
|
||||||
Verb = "runas",
|
|
||||||
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
|
|
||||||
Arguments = $"/Create /XML \"{xmlPath}\" /tn \"Artemis 2 autorun\" /F"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
schtasks.Start();
|
|
||||||
schtasks.WaitForExit();
|
|
||||||
|
|
||||||
File.Delete(xmlPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveAutoRunTask()
|
|
||||||
{
|
|
||||||
Process schtasks = new()
|
|
||||||
{
|
|
||||||
StartInfo =
|
|
||||||
{
|
|
||||||
WindowStyle = ProcessWindowStyle.Hidden,
|
|
||||||
UseShellExecute = true,
|
|
||||||
Verb = "runas",
|
|
||||||
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
|
|
||||||
Arguments = "/Delete /TN \"Artemis 2 autorun\" /f"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
schtasks.Start();
|
|
||||||
schtasks.WaitForExit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ApplicationColorScheme
|
public enum ApplicationColorScheme
|
||||||
{
|
{
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<!-- Icon column -->
|
<!-- Icon column -->
|
||||||
<shared:ArtemisIcon Grid.Column="0"
|
<shared:ArtemisIcon Grid.Column="0"
|
||||||
Icon="{Binding Feature.Info.Icon}"
|
Icon="{Binding FeatureInfo.Icon}"
|
||||||
Width="20"
|
Width="20"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@ -42,12 +42,21 @@
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<!-- Display name column -->
|
<!-- Display name column -->
|
||||||
<TextBlock Grid.Column="1" Text="{Binding Feature.Info.Name}" Style="{StaticResource MaterialDesignTextBlock}" VerticalAlignment="Center" ToolTip="{Binding Feature.Info.Description}" />
|
<TextBlock Grid.Column="1" Text="{Binding FeatureInfo.Name}" Style="{StaticResource MaterialDesignTextBlock}" VerticalAlignment="Center" ToolTip="{Binding FeatureInfo.Description}" />
|
||||||
|
|
||||||
<!-- Enable toggle column -->
|
<!-- Enable toggle column -->
|
||||||
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="8"
|
<StackPanel Grid.Column="2"
|
||||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}">
|
HorizontalAlignment="Right"
|
||||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding IsEnabled}">
|
VerticalAlignment="Center"
|
||||||
|
Margin="8"
|
||||||
|
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="ShieldHalfFull"
|
||||||
|
ToolTip="Plugin requires admin rights"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="0 0 5 0"
|
||||||
|
Visibility="{Binding ShowShield, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
||||||
|
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding IsEnabled}" IsEnabled="{Binding FeatureInfo.Plugin.IsEnabled}">
|
||||||
Feature enabled
|
Feature enabled
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@ -11,25 +11,32 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
{
|
{
|
||||||
public class PluginFeatureViewModel : Screen
|
public class PluginFeatureViewModel : Screen
|
||||||
{
|
{
|
||||||
|
private readonly ICoreService _coreService;
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
private readonly IPluginManagementService _pluginManagementService;
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
private bool _enabling;
|
private bool _enabling;
|
||||||
private readonly IMessageService _messageService;
|
private readonly IMessageService _messageService;
|
||||||
|
|
||||||
public PluginFeatureViewModel(PluginFeature feature,
|
public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo,
|
||||||
|
bool showShield,
|
||||||
|
ICoreService coreService,
|
||||||
IDialogService dialogService,
|
IDialogService dialogService,
|
||||||
IPluginManagementService pluginManagementService,
|
IPluginManagementService pluginManagementService,
|
||||||
IMessageService messageService)
|
IMessageService messageService)
|
||||||
{
|
{
|
||||||
|
_coreService = coreService;
|
||||||
_dialogService = dialogService;
|
_dialogService = dialogService;
|
||||||
_pluginManagementService = pluginManagementService;
|
_pluginManagementService = pluginManagementService;
|
||||||
_messageService = messageService;
|
_messageService = messageService;
|
||||||
|
|
||||||
Feature = feature;
|
FeatureInfo = pluginFeatureInfo;
|
||||||
|
ShowShield = FeatureInfo.Plugin.Info.RequiresAdmin && showShield;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PluginFeature Feature { get; }
|
public PluginFeatureInfo FeatureInfo { get; }
|
||||||
public Exception LoadException => Feature.LoadException;
|
public Exception LoadException => FeatureInfo.Instance?.LoadException;
|
||||||
|
|
||||||
|
public bool ShowShield { get; }
|
||||||
|
|
||||||
public bool Enabling
|
public bool Enabling
|
||||||
{
|
{
|
||||||
@ -39,7 +46,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
|
|
||||||
public bool IsEnabled
|
public bool IsEnabled
|
||||||
{
|
{
|
||||||
get => Feature.IsEnabled;
|
get => FeatureInfo.Instance != null && FeatureInfo.Instance.IsEnabled;
|
||||||
set => Task.Run(() => UpdateEnabled(value));
|
set => Task.Run(() => UpdateEnabled(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +67,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
if (LoadException == null)
|
if (LoadException == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_dialogService.ShowExceptionDialog("Feature failed to enable", Feature.LoadException);
|
_dialogService.ShowExceptionDialog("Feature failed to enable", LoadException);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnInitialActivate()
|
protected override void OnInitialActivate()
|
||||||
@ -83,7 +90,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
|
|
||||||
private async Task UpdateEnabled(bool enable)
|
private async Task UpdateEnabled(bool enable)
|
||||||
{
|
{
|
||||||
if (IsEnabled == enable)
|
if (IsEnabled == enable || FeatureInfo.Instance == null)
|
||||||
{
|
{
|
||||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
return;
|
return;
|
||||||
@ -95,11 +102,21 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Run(() => _pluginManagementService.EnablePluginFeature(Feature, true));
|
if (FeatureInfo.Plugin.Info.RequiresAdmin && !_coreService.IsElevated)
|
||||||
|
{
|
||||||
|
bool confirmed = await _dialogService.ShowConfirmDialog("Enable feature", "The plugin of this feature requires admin rights, are you sure you want to enable it?");
|
||||||
|
if (!confirmed)
|
||||||
|
{
|
||||||
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Run(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance, true));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_messageService.ShowMessage($"Failed to enable {Feature.Info.Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder);
|
_messageService.ShowMessage($"Failed to enable {FeatureInfo.Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -108,7 +125,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_pluginManagementService.DisablePluginFeature(Feature, true);
|
_pluginManagementService.DisablePluginFeature(FeatureInfo.Instance, true);
|
||||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,13 +134,13 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
|
|
||||||
private void OnFeatureEnabling(object sender, PluginFeatureEventArgs e)
|
private void OnFeatureEnabling(object sender, PluginFeatureEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PluginFeature != Feature) return;
|
if (e.PluginFeature != FeatureInfo.Instance) return;
|
||||||
Enabling = true;
|
Enabling = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFeatureEnableStopped(object sender, PluginFeatureEventArgs e)
|
private void OnFeatureEnableStopped(object sender, PluginFeatureEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PluginFeature != Feature) return;
|
if (e.PluginFeature != FeatureInfo.Instance) return;
|
||||||
Enabling = false;
|
Enabling = false;
|
||||||
|
|
||||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
|
|||||||
@ -116,21 +116,11 @@
|
|||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<TextBlock Margin="10 10 0 5" Style="{StaticResource MaterialDesignBody2TextBlock}">Plugin features</TextBlock>
|
<TextBlock Margin="10 10 0 5" Style="{StaticResource MaterialDesignBody2TextBlock}">Plugin features</TextBlock>
|
||||||
<TextBlock Grid.Row="1"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Margin="0 30 0 0"
|
|
||||||
Style="{StaticResource MaterialDesignTextBlock}"
|
|
||||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
|
||||||
Visibility="{Binding IsEnabled, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
|
||||||
Enable the plugin to view its features
|
|
||||||
</TextBlock>
|
|
||||||
|
|
||||||
<ListBox Grid.Row="1"
|
<ListBox Grid.Row="1"
|
||||||
MaxHeight="135"
|
MaxHeight="135"
|
||||||
ItemsSource="{Binding Items}"
|
ItemsSource="{Binding Items}"
|
||||||
HorizontalContentAlignment="Stretch"
|
HorizontalContentAlignment="Stretch"
|
||||||
VirtualizingPanel.ScrollUnit="Pixel"
|
VirtualizingPanel.ScrollUnit="Pixel">
|
||||||
Visibility="{Binding IsEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
|
||||||
<b:Interaction.Behaviors>
|
<b:Interaction.Behaviors>
|
||||||
<shared:ScrollParentWhenAtMax />
|
<shared:ScrollParentWhenAtMax />
|
||||||
</b:Interaction.Behaviors>
|
</b:Interaction.Behaviors>
|
||||||
|
|||||||
@ -117,22 +117,12 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
|
|
||||||
protected override void OnInitialActivate()
|
protected override void OnInitialActivate()
|
||||||
{
|
{
|
||||||
Plugin.FeatureAdded += PluginOnFeatureAdded;
|
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
||||||
Plugin.FeatureRemoved += PluginOnFeatureRemoved;
|
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
|
||||||
foreach (PluginFeature pluginFeature in Plugin.Features)
|
|
||||||
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeature));
|
|
||||||
|
|
||||||
base.OnInitialActivate();
|
base.OnInitialActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnClose()
|
|
||||||
{
|
|
||||||
Plugin.FeatureAdded -= PluginOnFeatureAdded;
|
|
||||||
Plugin.FeatureRemoved -= PluginOnFeatureRemoved;
|
|
||||||
|
|
||||||
base.OnClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UpdateEnabled(bool enable)
|
private async Task UpdateEnabled(bool enable)
|
||||||
{
|
{
|
||||||
if (IsEnabled == enable)
|
if (IsEnabled == enable)
|
||||||
@ -178,17 +168,5 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PluginOnFeatureRemoved(object sender, PluginFeatureEventArgs e)
|
|
||||||
{
|
|
||||||
PluginFeatureViewModel viewModel = Items.FirstOrDefault(i => i.Feature == e.PluginFeature);
|
|
||||||
if (viewModel != null)
|
|
||||||
Items.Remove(viewModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PluginOnFeatureAdded(object sender, PluginFeatureEventArgs e)
|
|
||||||
{
|
|
||||||
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(e.PluginFeature));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,6 +5,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mde="https://spiegelp.github.io/MaterialDesignExtensions/winfx/xaml"
|
xmlns:mde="https://spiegelp.github.io/MaterialDesignExtensions/winfx/xaml"
|
||||||
xmlns:s="https://github.com/canton7/Stylet"
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Width="800"
|
Width="800"
|
||||||
Height="600"
|
Height="600"
|
||||||
@ -17,7 +18,12 @@
|
|||||||
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
|
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
|
||||||
UseLayoutRounding="True"
|
UseLayoutRounding="True"
|
||||||
d:DesignHeight="450" d:DesignWidth="800">
|
d:DesignHeight="450" d:DesignWidth="800">
|
||||||
|
<materialDesign:DialogHost IsTabStop="False"
|
||||||
|
Focusable="False"
|
||||||
|
Identifier="RootDialog"
|
||||||
|
DialogTheme="Inherit"
|
||||||
|
SnackbarMessageQueue="{Binding MainMessageQueue}">
|
||||||
|
<Grid>
|
||||||
<mde:Stepper IsLinear="True" Layout="Horizontal" ActiveStepChanged="{s:Action ActiveStepChanged}" CancelNavigation="{s:Action SkipOrFinishWizard}" Margin="15">
|
<mde:Stepper IsLinear="True" Layout="Horizontal" ActiveStepChanged="{s:Action ActiveStepChanged}" CancelNavigation="{s:Action SkipOrFinishWizard}" Margin="15">
|
||||||
<mde:Step>
|
<mde:Step>
|
||||||
<mde:Step.Header>
|
<mde:Step.Header>
|
||||||
@ -60,4 +66,10 @@
|
|||||||
</mde:Step.Content>
|
</mde:Step.Content>
|
||||||
</mde:Step>
|
</mde:Step>
|
||||||
</mde:Stepper>
|
</mde:Stepper>
|
||||||
|
<materialDesign:Snackbar x:Name="MainSnackbar"
|
||||||
|
MessageQueue="{Binding MainMessageQueue}"
|
||||||
|
materialDesign:SnackbarMessage.InlineActionButtonMaxHeight="80"
|
||||||
|
materialDesign:SnackbarMessage.ContentMaxHeight="200" />
|
||||||
|
</Grid>
|
||||||
|
</materialDesign:DialogHost>
|
||||||
</mde:MaterialWindow>
|
</mde:MaterialWindow>
|
||||||
@ -2,18 +2,22 @@
|
|||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Screens.StartupWizard.Steps;
|
using Artemis.UI.Screens.StartupWizard.Steps;
|
||||||
|
using Artemis.UI.Shared.Services;
|
||||||
using MaterialDesignExtensions.Controllers;
|
using MaterialDesignExtensions.Controllers;
|
||||||
using MaterialDesignExtensions.Controls;
|
using MaterialDesignExtensions.Controls;
|
||||||
|
using MaterialDesignThemes.Wpf;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.StartupWizard
|
namespace Artemis.UI.Screens.StartupWizard
|
||||||
{
|
{
|
||||||
public class StartupWizardViewModel : Conductor<Screen>.Collection.OneActive
|
public class StartupWizardViewModel : Conductor<Screen>.Collection.OneActive
|
||||||
{
|
{
|
||||||
|
private readonly IMessageService _messageService;
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private StepperController _stepperController;
|
private StepperController _stepperController;
|
||||||
|
|
||||||
public StartupWizardViewModel(ISettingsService settingsService,
|
public StartupWizardViewModel(ISettingsService settingsService,
|
||||||
|
IMessageService messageService,
|
||||||
WelcomeStepViewModel welcome,
|
WelcomeStepViewModel welcome,
|
||||||
DevicesStepViewModel devices,
|
DevicesStepViewModel devices,
|
||||||
LayoutStepViewModel layout,
|
LayoutStepViewModel layout,
|
||||||
@ -21,6 +25,7 @@ namespace Artemis.UI.Screens.StartupWizard
|
|||||||
FinishStepViewModel finish)
|
FinishStepViewModel finish)
|
||||||
{
|
{
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
|
_messageService = messageService;
|
||||||
Items.Add(welcome);
|
Items.Add(welcome);
|
||||||
Items.Add(devices);
|
Items.Add(devices);
|
||||||
Items.Add(layout);
|
Items.Add(layout);
|
||||||
@ -30,6 +35,8 @@ namespace Artemis.UI.Screens.StartupWizard
|
|||||||
ActiveItem = Items.First();
|
ActiveItem = Items.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ISnackbarMessageQueue MainMessageQueue { get; set; }
|
||||||
|
|
||||||
public void ActiveStepChanged(object sender, ActiveStepChangedEventArgs e)
|
public void ActiveStepChanged(object sender, ActiveStepChangedEventArgs e)
|
||||||
{
|
{
|
||||||
Stepper stepper = (Stepper) sender;
|
Stepper stepper = (Stepper) sender;
|
||||||
@ -40,22 +47,23 @@ namespace Artemis.UI.Screens.StartupWizard
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void SkipOrFinishWizard()
|
public void SkipOrFinishWizard()
|
||||||
{
|
|
||||||
RequestClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnClose()
|
|
||||||
{
|
{
|
||||||
PluginSetting<bool> setting = _settingsService.GetSetting("UI.SetupWizardCompleted", false);
|
PluginSetting<bool> setting = _settingsService.GetSetting("UI.SetupWizardCompleted", false);
|
||||||
setting.Value = true;
|
setting.Value = true;
|
||||||
setting.Save();
|
setting.Save();
|
||||||
|
|
||||||
base.OnClose();
|
RequestClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Continue()
|
public void Continue()
|
||||||
{
|
{
|
||||||
_stepperController.Continue();
|
_stepperController.Continue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnInitialActivate()
|
||||||
|
{
|
||||||
|
MainMessageQueue = _messageService.MainMessageQueue;
|
||||||
|
base.OnInitialActivate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -16,7 +16,7 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<TextBlock Grid.Row="0" Style="{StaticResource MaterialDesignBody1TextBlock}" TextWrapping="Wrap">
|
<TextBlock Grid.Row="0" Style="{StaticResource MaterialDesignBody1TextBlock}" TextWrapping="Wrap">
|
||||||
EnabledDevices are supported through the use of device providers. <LineBreak />
|
Devices are supported through the use of device providers. <LineBreak />
|
||||||
In the list below you can enable device providers for each brand you own by checking <Run Text="Feature enabled" FontWeight="Bold" />.
|
In the list below you can enable device providers for each brand you own by checking <Run Text="Feature enabled" FontWeight="Bold" />.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Artemis.Core;
|
||||||
using Artemis.Core.DeviceProviders;
|
using Artemis.Core.DeviceProviders;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
@ -27,11 +28,10 @@ namespace Artemis.UI.Screens.StartupWizard.Steps
|
|||||||
Items.Clear();
|
Items.Clear();
|
||||||
|
|
||||||
// _pluginManagementService.GetFeaturesOfType<>() will only give us enabled features so lets get all of them this way
|
// _pluginManagementService.GetFeaturesOfType<>() will only give us enabled features so lets get all of them this way
|
||||||
IEnumerable<DeviceProvider> features = _pluginManagementService.GetAllPlugins()
|
IEnumerable<PluginFeatureInfo> features = _pluginManagementService.GetAllPlugins()
|
||||||
.SelectMany(p => p.Features.Where(f => f is DeviceProvider))
|
.SelectMany(p => p.Features.Where(f => typeof(DeviceProvider).IsAssignableFrom(f.FeatureType)))
|
||||||
.Cast<DeviceProvider>()
|
|
||||||
.OrderBy(d => d.GetType().Name);
|
.OrderBy(d => d.GetType().Name);
|
||||||
Items.AddRange(features.Select(d => _settingsVmFactory.CreatePluginFeatureViewModel(d)));
|
Items.AddRange(features.Select(d => _settingsVmFactory.CreatePluginFeatureViewModel(d, true)));
|
||||||
|
|
||||||
base.OnActivate();
|
base.OnActivate();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ using Artemis.UI.Screens.Settings.Tabs.General;
|
|||||||
using Artemis.UI.Screens.Settings.Tabs.Plugins;
|
using Artemis.UI.Screens.Settings.Tabs.Plugins;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Artemis.UI.Utilities;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.StartupWizard.Steps
|
namespace Artemis.UI.Screens.StartupWizard.Steps
|
||||||
@ -34,7 +35,7 @@ namespace Artemis.UI.Screens.StartupWizard.Steps
|
|||||||
_settingsService.GetSetting("UI.AutoRun", false).Value = value;
|
_settingsService.GetSetting("UI.AutoRun", false).Value = value;
|
||||||
_settingsService.GetSetting("UI.AutoRun", false).Save();
|
_settingsService.GetSetting("UI.AutoRun", false).Save();
|
||||||
NotifyOfPropertyChange(nameof(StartWithWindows));
|
NotifyOfPropertyChange(nameof(StartWithWindows));
|
||||||
Task.Run(ApplyAutorun);
|
Task.Run(() => ApplyAutorun(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,23 +60,35 @@ namespace Artemis.UI.Screens.StartupWizard.Steps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyAutorun()
|
private void ApplyAutorun(bool recreate)
|
||||||
{
|
{
|
||||||
try
|
if (!StartWithWindows)
|
||||||
{
|
StartMinimized = false;
|
||||||
string autoRunFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), "Artemis.lnk");
|
|
||||||
string executableFile = Constants.ExecutablePath;
|
|
||||||
|
|
||||||
|
// Remove the old auto-run method of placing a shortcut in shell:startup
|
||||||
|
string autoRunFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), "Artemis.lnk");
|
||||||
if (File.Exists(autoRunFile))
|
if (File.Exists(autoRunFile))
|
||||||
File.Delete(autoRunFile);
|
File.Delete(autoRunFile);
|
||||||
if (StartWithWindows)
|
|
||||||
ShortcutUtilities.Create(autoRunFile, executableFile, "--autorun", new FileInfo(executableFile).DirectoryName, "Artemis", "", "");
|
// Local builds shouldn't auto-run, this is just annoying
|
||||||
else
|
// if (Constants.BuildInfo.IsLocalBuild)
|
||||||
StartMinimized = false;
|
// return;
|
||||||
|
|
||||||
|
// Create or remove the task if necessary
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool taskCreated = false;
|
||||||
|
if (!recreate)
|
||||||
|
taskCreated = SettingsUtilities.IsAutoRunTaskCreated();
|
||||||
|
|
||||||
|
if (StartWithWindows && !taskCreated)
|
||||||
|
SettingsUtilities.CreateAutoRunTask(TimeSpan.FromSeconds(15));
|
||||||
|
else if (!StartWithWindows && taskCreated)
|
||||||
|
SettingsUtilities.RemoveAutoRunTask();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_dialogService.ShowExceptionDialog("An exception occured while trying to apply the auto run setting", e);
|
Execute.PostToUIThread(() => _dialogService.ShowExceptionDialog("An exception occured while trying to apply the auto run setting", e));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,7 +59,7 @@ namespace Artemis.UI.Services
|
|||||||
|
|
||||||
#region Event handlers
|
#region Event handlers
|
||||||
|
|
||||||
private async void WindowServiceOnMainWindowOpened(object? sender, EventArgs e)
|
private async void WindowServiceOnMainWindowOpened(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
List<ArtemisDevice> devices = _rgbService.Devices.Where(device => DeviceNeedsLayout(device) && !_ignoredDevices.Contains(device)).ToList();
|
List<ArtemisDevice> devices = _rgbService.Devices.Where(device => DeviceNeedsLayout(device) && !_ignoredDevices.Contains(device)).ToList();
|
||||||
foreach (ArtemisDevice artemisDevice in devices)
|
foreach (ArtemisDevice artemisDevice in devices)
|
||||||
|
|||||||
@ -7,6 +7,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Timers;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Exceptions;
|
using Artemis.UI.Exceptions;
|
||||||
@ -23,7 +24,9 @@ namespace Artemis.UI.Services
|
|||||||
{
|
{
|
||||||
public class UpdateService : IUpdateService
|
public class UpdateService : IUpdateService
|
||||||
{
|
{
|
||||||
|
private const double UpdateCheckInterval = 3600000; // once per hour
|
||||||
private const string ApiUrl = "https://dev.azure.com/artemis-rgb/Artemis/_apis/";
|
private const string ApiUrl = "https://dev.azure.com/artemis-rgb/Artemis/_apis/";
|
||||||
|
|
||||||
private readonly PluginSetting<bool> _autoInstallUpdates;
|
private readonly PluginSetting<bool> _autoInstallUpdates;
|
||||||
private readonly PluginSetting<bool> _checkForUpdates;
|
private readonly PluginSetting<bool> _checkForUpdates;
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
@ -41,13 +44,20 @@ namespace Artemis.UI.Services
|
|||||||
|
|
||||||
_checkForUpdates = settingsService.GetSetting("UI.CheckForUpdates", true);
|
_checkForUpdates = settingsService.GetSetting("UI.CheckForUpdates", true);
|
||||||
_autoInstallUpdates = settingsService.GetSetting("UI.AutoInstallUpdates", false);
|
_autoInstallUpdates = settingsService.GetSetting("UI.AutoInstallUpdates", false);
|
||||||
|
|
||||||
_checkForUpdates.SettingChanged += CheckForUpdatesOnSettingChanged;
|
_checkForUpdates.SettingChanged += CheckForUpdatesOnSettingChanged;
|
||||||
|
|
||||||
|
Timer timer = new(UpdateCheckInterval);
|
||||||
|
timer.Elapsed += async (_, _) => await AutoUpdate();
|
||||||
|
timer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SuspendAutoUpdate { get; set; }
|
||||||
|
|
||||||
private async Task OfferUpdate(DevOpsBuild buildInfo)
|
private async Task OfferUpdate(DevOpsBuild buildInfo)
|
||||||
{
|
{
|
||||||
await _dialogService.ShowDialog<UpdateDialogViewModel>(new Dictionary<string, object> {{"buildInfo", buildInfo}});
|
object result = await _dialogService.ShowDialog<UpdateDialogViewModel>(new Dictionary<string, object> {{"buildInfo", buildInfo}});
|
||||||
|
if (result is bool resultBool && resultBool == false)
|
||||||
|
SuspendAutoUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateInstaller()
|
private async Task UpdateInstaller()
|
||||||
@ -73,11 +83,21 @@ namespace Artemis.UI.Services
|
|||||||
|
|
||||||
public async Task<bool> AutoUpdate()
|
public async Task<bool> AutoUpdate()
|
||||||
{
|
{
|
||||||
if (!_checkForUpdates.Value)
|
if (Constants.BuildInfo.IsLocalBuild)
|
||||||
|
return false;
|
||||||
|
if (!_checkForUpdates.Value || SuspendAutoUpdate)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
return await OfferUpdateIfFound();
|
return await OfferUpdateIfFound();
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Warning(e, "Auto update failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> OfferUpdateIfFound()
|
public async Task<bool> OfferUpdateIfFound()
|
||||||
{
|
{
|
||||||
@ -186,13 +206,13 @@ namespace Artemis.UI.Services
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.Warning(e, "GetBuildInfo: Failed to retrieve build info JSON");
|
_logger.Warning(e, "GetBuildInfo: Failed to retrieve build info JSON");
|
||||||
return null;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (FlurlHttpException e)
|
catch (FlurlHttpException e)
|
||||||
{
|
{
|
||||||
_logger.Warning("GetBuildInfo: Getting build info, request returned {statusCode}", e.StatusCode);
|
_logger.Warning("GetBuildInfo: Getting build info, request returned {statusCode}", e.StatusCode);
|
||||||
return null;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,16 +228,16 @@ namespace Artemis.UI.Services
|
|||||||
|
|
||||||
#region Event handlers
|
#region Event handlers
|
||||||
|
|
||||||
private void CheckForUpdatesOnSettingChanged(object sender, EventArgs e)
|
private async void CheckForUpdatesOnSettingChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
// Run an auto-update as soon as the setting gets changed to enabled
|
// Run an auto-update as soon as the setting gets changed to enabled
|
||||||
if (_checkForUpdates.Value)
|
if (_checkForUpdates.Value)
|
||||||
AutoUpdate();
|
await AutoUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WindowServiceOnMainWindowOpened(object sender, EventArgs e)
|
private async void WindowServiceOnMainWindowOpened(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
_logger.Information("Main window opened!");
|
await AutoUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -225,6 +245,8 @@ namespace Artemis.UI.Services
|
|||||||
|
|
||||||
public interface IUpdateService : IArtemisUIService
|
public interface IUpdateService : IArtemisUIService
|
||||||
{
|
{
|
||||||
|
bool SuspendAutoUpdate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If auto-update is enabled this will offer updates if found
|
/// If auto-update is enabled this will offer updates if found
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
95
src/Artemis.UI/Utilities/SettingsUtilities.cs
Normal file
95
src/Artemis.UI/Utilities/SettingsUtilities.cs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Properties;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Utilities
|
||||||
|
{
|
||||||
|
public static class SettingsUtilities
|
||||||
|
{
|
||||||
|
public static bool IsAutoRunTaskCreated()
|
||||||
|
{
|
||||||
|
Process schtasks = new()
|
||||||
|
{
|
||||||
|
StartInfo =
|
||||||
|
{
|
||||||
|
WindowStyle = ProcessWindowStyle.Hidden,
|
||||||
|
UseShellExecute = true,
|
||||||
|
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
|
||||||
|
Arguments = "/TN \"Artemis 2 autorun\""
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
schtasks.Start();
|
||||||
|
schtasks.WaitForExit();
|
||||||
|
return schtasks.ExitCode == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CreateAutoRunTask(TimeSpan autoRunDelay)
|
||||||
|
{
|
||||||
|
XDocument document = XDocument.Parse(Resources.artemis_autorun);
|
||||||
|
XElement task = document.Descendants().First();
|
||||||
|
|
||||||
|
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Date")
|
||||||
|
.SetValue(DateTime.Now);
|
||||||
|
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Author")
|
||||||
|
.SetValue(WindowsIdentity.GetCurrent().Name);
|
||||||
|
|
||||||
|
task.Descendants().First(d => d.Name.LocalName == "Triggers").Descendants().First(d => d.Name.LocalName == "LogonTrigger").Descendants().First(d => d.Name.LocalName == "Delay")
|
||||||
|
.SetValue(autoRunDelay);
|
||||||
|
|
||||||
|
task.Descendants().First(d => d.Name.LocalName == "Principals").Descendants().First(d => d.Name.LocalName == "Principal").Descendants().First(d => d.Name.LocalName == "UserId")
|
||||||
|
.SetValue(WindowsIdentity.GetCurrent().User.Value);
|
||||||
|
|
||||||
|
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "WorkingDirectory")
|
||||||
|
.SetValue(Constants.ApplicationFolder);
|
||||||
|
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "Command")
|
||||||
|
.SetValue("\"" + Constants.ExecutablePath + "\"");
|
||||||
|
|
||||||
|
string xmlPath = Path.GetTempFileName();
|
||||||
|
using (Stream fileStream = new FileStream(xmlPath, FileMode.Create))
|
||||||
|
{
|
||||||
|
document.Save(fileStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
Process schtasks = new()
|
||||||
|
{
|
||||||
|
StartInfo =
|
||||||
|
{
|
||||||
|
WindowStyle = ProcessWindowStyle.Hidden,
|
||||||
|
UseShellExecute = true,
|
||||||
|
Verb = "runas",
|
||||||
|
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
|
||||||
|
Arguments = $"/Create /XML \"{xmlPath}\" /tn \"Artemis 2 autorun\" /F"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
schtasks.Start();
|
||||||
|
schtasks.WaitForExit();
|
||||||
|
|
||||||
|
File.Delete(xmlPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveAutoRunTask()
|
||||||
|
{
|
||||||
|
Process schtasks = new()
|
||||||
|
{
|
||||||
|
StartInfo =
|
||||||
|
{
|
||||||
|
WindowStyle = ProcessWindowStyle.Hidden,
|
||||||
|
UseShellExecute = true,
|
||||||
|
Verb = "runas",
|
||||||
|
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
|
||||||
|
Arguments = "/Delete /TN \"Artemis 2 autorun\" /f"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
schtasks.Start();
|
||||||
|
schtasks.WaitForExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"BuildId": 442,
|
|
||||||
"BuildNumber": 20210130.3,
|
|
||||||
"SourceBranch": "refs/heads/master",
|
|
||||||
"SourceVersion": "b29ab064ae46d93141f31afaa8ee3ea9063c6263"
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user