mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 03:48:56 +00:00
Code style - Use file scoped namespaces
Code style - Ran code cleanup
This commit is contained in:
1 parent
e68e16df4d
commit
f6090dc296
691 files changed
+37301
-38119
No files matched your search
@@ -1,67 +1,66 @@
|
||||
using SkiaSharp;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class ColorCube
|
||||
{
|
||||
internal class ColorCube
|
||||
private readonly List<SKColor> _colors;
|
||||
|
||||
internal ColorCube(IEnumerable<SKColor> colors)
|
||||
{
|
||||
private readonly List<SKColor> _colors;
|
||||
|
||||
internal ColorCube(IEnumerable<SKColor> colors)
|
||||
if (colors.Count() < 2)
|
||||
{
|
||||
if (colors.Count() < 2)
|
||||
{
|
||||
_colors = colors.ToList();
|
||||
return;
|
||||
}
|
||||
|
||||
int redRange = colors.Max(c => c.Red) - colors.Min(c => c.Red);
|
||||
int greenRange = colors.Max(c => c.Green) - colors.Min(c => c.Green);
|
||||
int blueRange = colors.Max(c => c.Blue) - colors.Min(c => c.Blue);
|
||||
|
||||
if (redRange > greenRange && redRange > blueRange)
|
||||
_colors = colors.OrderBy(a => a.Red).ToList();
|
||||
else if (greenRange > blueRange)
|
||||
_colors = colors.OrderBy(a => a.Green).ToList();
|
||||
else
|
||||
_colors = colors.OrderBy(a => a.Blue).ToList();
|
||||
_colors = colors.ToList();
|
||||
return;
|
||||
}
|
||||
|
||||
internal bool TrySplit([NotNullWhen(returnValue: true)] out ColorCube? a, [NotNullWhen(returnValue: true)] out ColorCube? b)
|
||||
int redRange = colors.Max(c => c.Red) - colors.Min(c => c.Red);
|
||||
int greenRange = colors.Max(c => c.Green) - colors.Min(c => c.Green);
|
||||
int blueRange = colors.Max(c => c.Blue) - colors.Min(c => c.Blue);
|
||||
|
||||
if (redRange > greenRange && redRange > blueRange)
|
||||
_colors = colors.OrderBy(a => a.Red).ToList();
|
||||
else if (greenRange > blueRange)
|
||||
_colors = colors.OrderBy(a => a.Green).ToList();
|
||||
else
|
||||
_colors = colors.OrderBy(a => a.Blue).ToList();
|
||||
}
|
||||
|
||||
internal bool TrySplit([NotNullWhen(true)] out ColorCube? a, [NotNullWhen(true)] out ColorCube? b)
|
||||
{
|
||||
if (_colors.Count < 2)
|
||||
{
|
||||
if (_colors.Count < 2)
|
||||
{
|
||||
a = null;
|
||||
b = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
int median = _colors.Count / 2;
|
||||
|
||||
a = new ColorCube(_colors.GetRange(0, median));
|
||||
b = new ColorCube(_colors.GetRange(median, _colors.Count - median));
|
||||
|
||||
return true;
|
||||
a = null;
|
||||
b = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal SKColor GetAverageColor()
|
||||
int median = _colors.Count / 2;
|
||||
|
||||
a = new ColorCube(_colors.GetRange(0, median));
|
||||
b = new ColorCube(_colors.GetRange(median, _colors.Count - median));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal SKColor GetAverageColor()
|
||||
{
|
||||
int r = 0, g = 0, b = 0;
|
||||
|
||||
for (int i = 0; i < _colors.Count; i++)
|
||||
{
|
||||
int r = 0, g = 0, b = 0;
|
||||
|
||||
for (int i = 0; i < _colors.Count; i++)
|
||||
{
|
||||
r += _colors[i].Red;
|
||||
g += _colors[i].Green;
|
||||
b += _colors[i].Blue;
|
||||
}
|
||||
|
||||
return new SKColor(
|
||||
(byte)(r / _colors.Count),
|
||||
(byte)(g / _colors.Count),
|
||||
(byte)(b / _colors.Count)
|
||||
);
|
||||
r += _colors[i].Red;
|
||||
g += _colors[i].Green;
|
||||
b += _colors[i].Blue;
|
||||
}
|
||||
|
||||
return new SKColor(
|
||||
(byte) (r / _colors.Count),
|
||||
(byte) (g / _colors.Count),
|
||||
(byte) (b / _colors.Count)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,145 +3,145 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <inheritdoc />
|
||||
internal class ColorQuantizerService : IColorQuantizerService
|
||||
{
|
||||
/// <inheritdoc />
|
||||
internal class ColorQuantizerService : IColorQuantizerService
|
||||
private static float GetScore(SKColor color, ColorType type, bool ignoreLimits = false)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SKColor[] Quantize(IEnumerable<SKColor> colors, int amount)
|
||||
static float InvertDiff(float value, float target)
|
||||
{
|
||||
if ((amount & (amount - 1)) != 0)
|
||||
throw new ArgumentException("Must be power of two", nameof(amount));
|
||||
return 1 - Math.Abs(value - target);
|
||||
}
|
||||
|
||||
Queue<ColorCube> cubes = new(amount);
|
||||
cubes.Enqueue(new ColorCube(colors));
|
||||
color.ToHsl(out float _, out float saturation, out float luma);
|
||||
saturation /= 100f;
|
||||
luma /= 100f;
|
||||
|
||||
while (cubes.Count < amount)
|
||||
if (!ignoreLimits &&
|
||||
(saturation <= GetMinSaturation(type) || saturation >= GetMaxSaturation(type)
|
||||
|| luma <= GetMinLuma(type) || luma >= GetMaxLuma(type)))
|
||||
//if either saturation or luma falls outside the min-max, return the
|
||||
//lowest score possible unless we're ignoring these limits.
|
||||
return float.MinValue;
|
||||
|
||||
float totalValue = InvertDiff(saturation, GetTargetSaturation(type)) * weightSaturation +
|
||||
InvertDiff(luma, GetTargetLuma(type)) * weightLuma;
|
||||
|
||||
const float totalWeight = weightSaturation + weightLuma;
|
||||
|
||||
return totalValue / totalWeight;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public SKColor[] Quantize(IEnumerable<SKColor> colors, int amount)
|
||||
{
|
||||
if ((amount & (amount - 1)) != 0)
|
||||
throw new ArgumentException("Must be power of two", nameof(amount));
|
||||
|
||||
Queue<ColorCube> cubes = new(amount);
|
||||
cubes.Enqueue(new ColorCube(colors));
|
||||
|
||||
while (cubes.Count < amount)
|
||||
{
|
||||
ColorCube cube = cubes.Dequeue();
|
||||
if (cube.TrySplit(out ColorCube? a, out ColorCube? b))
|
||||
{
|
||||
ColorCube cube = cubes.Dequeue();
|
||||
if (cube.TrySplit(out ColorCube? a, out ColorCube? b))
|
||||
cubes.Enqueue(a);
|
||||
cubes.Enqueue(b);
|
||||
}
|
||||
}
|
||||
|
||||
return cubes.Select(c => c.GetAverageColor()).ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public SKColor FindColorVariation(IEnumerable<SKColor> colors, ColorType type, bool ignoreLimits = false)
|
||||
{
|
||||
float bestColorScore = 0;
|
||||
SKColor bestColor = SKColor.Empty;
|
||||
|
||||
foreach (SKColor clr in colors)
|
||||
{
|
||||
float score = GetScore(clr, type, ignoreLimits);
|
||||
if (score > bestColorScore)
|
||||
{
|
||||
bestColorScore = score;
|
||||
bestColor = clr;
|
||||
}
|
||||
}
|
||||
|
||||
return bestColor;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ColorSwatch FindAllColorVariations(IEnumerable<SKColor> colors, bool ignoreLimits = false)
|
||||
{
|
||||
SKColor bestVibrantColor = SKColor.Empty;
|
||||
SKColor bestLightVibrantColor = SKColor.Empty;
|
||||
SKColor bestDarkVibrantColor = SKColor.Empty;
|
||||
SKColor bestMutedColor = SKColor.Empty;
|
||||
SKColor bestLightMutedColor = SKColor.Empty;
|
||||
SKColor bestDarkMutedColor = SKColor.Empty;
|
||||
float bestVibrantScore = 0;
|
||||
float bestLightVibrantScore = 0;
|
||||
float bestDarkVibrantScore = 0;
|
||||
float bestMutedScore = 0;
|
||||
float bestLightMutedScore = 0;
|
||||
float bestDarkMutedScore = 0;
|
||||
|
||||
//ugly but at least we only loop through the enumerable once ¯\_(ツ)_/¯
|
||||
foreach (SKColor color in colors)
|
||||
{
|
||||
static void SetIfBetterScore(ref float bestScore, ref SKColor bestColor, SKColor newColor, ColorType type, bool ignoreLimits)
|
||||
{
|
||||
float newScore = GetScore(newColor, type, ignoreLimits);
|
||||
if (newScore > bestScore)
|
||||
{
|
||||
cubes.Enqueue(a);
|
||||
cubes.Enqueue(b);
|
||||
bestScore = newScore;
|
||||
bestColor = newColor;
|
||||
}
|
||||
}
|
||||
|
||||
return cubes.Select(c => c.GetAverageColor()).ToArray();
|
||||
SetIfBetterScore(ref bestVibrantScore, ref bestVibrantColor, color, ColorType.Vibrant, ignoreLimits);
|
||||
SetIfBetterScore(ref bestLightVibrantScore, ref bestLightVibrantColor, color, ColorType.LightVibrant, ignoreLimits);
|
||||
SetIfBetterScore(ref bestDarkVibrantScore, ref bestDarkVibrantColor, color, ColorType.DarkVibrant, ignoreLimits);
|
||||
SetIfBetterScore(ref bestMutedScore, ref bestMutedColor, color, ColorType.Muted, ignoreLimits);
|
||||
SetIfBetterScore(ref bestLightMutedScore, ref bestLightMutedColor, color, ColorType.LightMuted, ignoreLimits);
|
||||
SetIfBetterScore(ref bestDarkMutedScore, ref bestDarkMutedColor, color, ColorType.DarkMuted, ignoreLimits);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public SKColor FindColorVariation(IEnumerable<SKColor> colors, ColorType type, bool ignoreLimits = false)
|
||||
return new ColorSwatch
|
||||
{
|
||||
float bestColorScore = 0;
|
||||
SKColor bestColor = SKColor.Empty;
|
||||
Vibrant = bestVibrantColor,
|
||||
LightVibrant = bestLightVibrantColor,
|
||||
DarkVibrant = bestDarkVibrantColor,
|
||||
Muted = bestMutedColor,
|
||||
LightMuted = bestLightMutedColor,
|
||||
DarkMuted = bestDarkMutedColor
|
||||
};
|
||||
}
|
||||
|
||||
foreach (SKColor clr in colors)
|
||||
{
|
||||
float score = GetScore(clr, type, ignoreLimits);
|
||||
if (score > bestColorScore)
|
||||
{
|
||||
bestColorScore = score;
|
||||
bestColor = clr;
|
||||
}
|
||||
}
|
||||
#region Constants
|
||||
|
||||
return bestColor;
|
||||
}
|
||||
private const float targetDarkLuma = 0.26f;
|
||||
private const float maxDarkLuma = 0.45f;
|
||||
private const float minLightLuma = 0.55f;
|
||||
private const float targetLightLuma = 0.74f;
|
||||
private const float minNormalLuma = 0.3f;
|
||||
private const float targetNormalLuma = 0.5f;
|
||||
private const float maxNormalLuma = 0.7f;
|
||||
private const float targetMutesSaturation = 0.3f;
|
||||
private const float maxMutesSaturation = 0.3f;
|
||||
private const float targetVibrantSaturation = 1.0f;
|
||||
private const float minVibrantSaturation = 0.35f;
|
||||
private const float weightSaturation = 3f;
|
||||
private const float weightLuma = 5f;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ColorSwatch FindAllColorVariations(IEnumerable<SKColor> colors, bool ignoreLimits = false)
|
||||
{
|
||||
SKColor bestVibrantColor = SKColor.Empty;
|
||||
SKColor bestLightVibrantColor = SKColor.Empty;
|
||||
SKColor bestDarkVibrantColor = SKColor.Empty;
|
||||
SKColor bestMutedColor = SKColor.Empty;
|
||||
SKColor bestLightMutedColor = SKColor.Empty;
|
||||
SKColor bestDarkMutedColor = SKColor.Empty;
|
||||
float bestVibrantScore = 0;
|
||||
float bestLightVibrantScore = 0;
|
||||
float bestDarkVibrantScore = 0;
|
||||
float bestMutedScore = 0;
|
||||
float bestLightMutedScore = 0;
|
||||
float bestDarkMutedScore = 0;
|
||||
|
||||
//ugly but at least we only loop through the enumerable once ¯\_(ツ)_/¯
|
||||
foreach (SKColor color in colors)
|
||||
{
|
||||
static void SetIfBetterScore(ref float bestScore, ref SKColor bestColor, SKColor newColor, ColorType type, bool ignoreLimits)
|
||||
{
|
||||
float newScore = GetScore(newColor, type, ignoreLimits);
|
||||
if (newScore > bestScore)
|
||||
{
|
||||
bestScore = newScore;
|
||||
bestColor = newColor;
|
||||
}
|
||||
}
|
||||
|
||||
SetIfBetterScore(ref bestVibrantScore, ref bestVibrantColor, color, ColorType.Vibrant, ignoreLimits);
|
||||
SetIfBetterScore(ref bestLightVibrantScore, ref bestLightVibrantColor, color, ColorType.LightVibrant, ignoreLimits);
|
||||
SetIfBetterScore(ref bestDarkVibrantScore, ref bestDarkVibrantColor, color, ColorType.DarkVibrant, ignoreLimits);
|
||||
SetIfBetterScore(ref bestMutedScore, ref bestMutedColor, color, ColorType.Muted, ignoreLimits);
|
||||
SetIfBetterScore(ref bestLightMutedScore, ref bestLightMutedColor, color, ColorType.LightMuted, ignoreLimits);
|
||||
SetIfBetterScore(ref bestDarkMutedScore, ref bestDarkMutedColor, color, ColorType.DarkMuted, ignoreLimits);
|
||||
}
|
||||
|
||||
return new ColorSwatch
|
||||
{
|
||||
Vibrant = bestVibrantColor,
|
||||
LightVibrant = bestLightVibrantColor,
|
||||
DarkVibrant = bestDarkVibrantColor,
|
||||
Muted = bestMutedColor,
|
||||
LightMuted = bestLightMutedColor,
|
||||
DarkMuted = bestDarkMutedColor,
|
||||
};
|
||||
}
|
||||
|
||||
private static float GetScore(SKColor color, ColorType type, bool ignoreLimits = false)
|
||||
{
|
||||
static float InvertDiff(float value, float target)
|
||||
{
|
||||
return 1 - Math.Abs(value - target);
|
||||
}
|
||||
|
||||
color.ToHsl(out float _, out float saturation, out float luma);
|
||||
saturation /= 100f;
|
||||
luma /= 100f;
|
||||
|
||||
if (!ignoreLimits &&
|
||||
(saturation <= GetMinSaturation(type) || saturation >= GetMaxSaturation(type)
|
||||
|| luma <= GetMinLuma(type) || luma >= GetMaxLuma(type)))
|
||||
{
|
||||
//if either saturation or luma falls outside the min-max, return the
|
||||
//lowest score possible unless we're ignoring these limits.
|
||||
return float.MinValue;
|
||||
}
|
||||
|
||||
float totalValue = (InvertDiff(saturation, GetTargetSaturation(type)) * weightSaturation) +
|
||||
(InvertDiff(luma, GetTargetLuma(type)) * weightLuma);
|
||||
|
||||
const float totalWeight = weightSaturation + weightLuma;
|
||||
|
||||
return totalValue / totalWeight;
|
||||
}
|
||||
|
||||
#region Constants
|
||||
|
||||
private const float targetDarkLuma = 0.26f;
|
||||
private const float maxDarkLuma = 0.45f;
|
||||
private const float minLightLuma = 0.55f;
|
||||
private const float targetLightLuma = 0.74f;
|
||||
private const float minNormalLuma = 0.3f;
|
||||
private const float targetNormalLuma = 0.5f;
|
||||
private const float maxNormalLuma = 0.7f;
|
||||
private const float targetMutesSaturation = 0.3f;
|
||||
private const float maxMutesSaturation = 0.3f;
|
||||
private const float targetVibrantSaturation = 1.0f;
|
||||
private const float minVibrantSaturation = 0.35f;
|
||||
private const float weightSaturation = 3f;
|
||||
private const float weightLuma = 5f;
|
||||
|
||||
private static float GetTargetLuma(ColorType colorType) => colorType switch
|
||||
private static float GetTargetLuma(ColorType colorType)
|
||||
{
|
||||
return colorType switch
|
||||
{
|
||||
ColorType.Vibrant => targetNormalLuma,
|
||||
ColorType.LightVibrant => targetLightLuma,
|
||||
@@ -151,8 +151,11 @@ namespace Artemis.Core.Services
|
||||
ColorType.DarkMuted => targetDarkLuma,
|
||||
_ => throw new ArgumentException(nameof(colorType))
|
||||
};
|
||||
}
|
||||
|
||||
private static float GetMinLuma(ColorType colorType) => colorType switch
|
||||
private static float GetMinLuma(ColorType colorType)
|
||||
{
|
||||
return colorType switch
|
||||
{
|
||||
ColorType.Vibrant => minNormalLuma,
|
||||
ColorType.LightVibrant => minLightLuma,
|
||||
@@ -162,8 +165,11 @@ namespace Artemis.Core.Services
|
||||
ColorType.DarkMuted => 0,
|
||||
_ => throw new ArgumentException(nameof(colorType))
|
||||
};
|
||||
}
|
||||
|
||||
private static float GetMaxLuma(ColorType colorType) => colorType switch
|
||||
private static float GetMaxLuma(ColorType colorType)
|
||||
{
|
||||
return colorType switch
|
||||
{
|
||||
ColorType.Vibrant => maxNormalLuma,
|
||||
ColorType.LightVibrant => 1f,
|
||||
@@ -173,8 +179,11 @@ namespace Artemis.Core.Services
|
||||
ColorType.DarkMuted => maxDarkLuma,
|
||||
_ => throw new ArgumentException(nameof(colorType))
|
||||
};
|
||||
}
|
||||
|
||||
private static float GetTargetSaturation(ColorType colorType) => colorType switch
|
||||
private static float GetTargetSaturation(ColorType colorType)
|
||||
{
|
||||
return colorType switch
|
||||
{
|
||||
ColorType.Vibrant => targetVibrantSaturation,
|
||||
ColorType.LightVibrant => targetVibrantSaturation,
|
||||
@@ -184,8 +193,11 @@ namespace Artemis.Core.Services
|
||||
ColorType.DarkMuted => targetMutesSaturation,
|
||||
_ => throw new ArgumentException(nameof(colorType))
|
||||
};
|
||||
}
|
||||
|
||||
private static float GetMinSaturation(ColorType colorType) => colorType switch
|
||||
private static float GetMinSaturation(ColorType colorType)
|
||||
{
|
||||
return colorType switch
|
||||
{
|
||||
ColorType.Vibrant => minVibrantSaturation,
|
||||
ColorType.LightVibrant => minVibrantSaturation,
|
||||
@@ -195,8 +207,11 @@ namespace Artemis.Core.Services
|
||||
ColorType.DarkMuted => 0,
|
||||
_ => throw new ArgumentException(nameof(colorType))
|
||||
};
|
||||
}
|
||||
|
||||
private static float GetMaxSaturation(ColorType colorType) => colorType switch
|
||||
private static float GetMaxSaturation(ColorType colorType)
|
||||
{
|
||||
return colorType switch
|
||||
{
|
||||
ColorType.Vibrant => 1f,
|
||||
ColorType.LightVibrant => 1f,
|
||||
@@ -206,7 +221,7 @@ namespace Artemis.Core.Services
|
||||
ColorType.DarkMuted => maxMutesSaturation,
|
||||
_ => throw new ArgumentException(nameof(colorType))
|
||||
};
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,40 +1,39 @@
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Swatch containing the known useful color variations.
|
||||
/// </summary>
|
||||
public struct ColorSwatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Swatch containing the known useful color variations.
|
||||
/// The <see cref="ColorType.Vibrant" /> component.
|
||||
/// </summary>
|
||||
public struct ColorSwatch
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="ColorType.Vibrant"/> component.
|
||||
/// </summary>
|
||||
public SKColor Vibrant { get; init; }
|
||||
public SKColor Vibrant { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ColorType.LightVibrant"/> component.
|
||||
/// </summary>
|
||||
public SKColor LightVibrant { get; init; }
|
||||
/// <summary>
|
||||
/// The <see cref="ColorType.LightVibrant" /> component.
|
||||
/// </summary>
|
||||
public SKColor LightVibrant { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ColorType.DarkVibrant"/> component.
|
||||
/// </summary>
|
||||
public SKColor DarkVibrant { get; init; }
|
||||
/// <summary>
|
||||
/// The <see cref="ColorType.DarkVibrant" /> component.
|
||||
/// </summary>
|
||||
public SKColor DarkVibrant { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ColorType.Muted"/> component.
|
||||
/// </summary>
|
||||
public SKColor Muted { get; init; }
|
||||
/// <summary>
|
||||
/// The <see cref="ColorType.Muted" /> component.
|
||||
/// </summary>
|
||||
public SKColor Muted { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ColorType.LightMuted"/> component.
|
||||
/// </summary>
|
||||
public SKColor LightMuted { get; init; }
|
||||
/// <summary>
|
||||
/// The <see cref="ColorType.LightMuted" /> component.
|
||||
/// </summary>
|
||||
public SKColor LightMuted { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ColorType.DarkMuted"/> component.
|
||||
/// </summary>
|
||||
public SKColor DarkMuted { get; init; }
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The <see cref="ColorType.DarkMuted" /> component.
|
||||
/// </summary>
|
||||
public SKColor DarkMuted { get; init; }
|
||||
}
|
||||
@@ -1,38 +1,37 @@
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// The types of relevant colors in an image.
|
||||
/// </summary>
|
||||
public enum ColorType
|
||||
{
|
||||
/// <summary>
|
||||
/// The types of relevant colors in an image.
|
||||
/// Represents a saturated color.
|
||||
/// </summary>
|
||||
public enum ColorType
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a saturated color.
|
||||
/// </summary>
|
||||
Vibrant,
|
||||
Vibrant,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a saturated and light color.
|
||||
/// </summary>
|
||||
LightVibrant,
|
||||
/// <summary>
|
||||
/// Represents a saturated and light color.
|
||||
/// </summary>
|
||||
LightVibrant,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a saturated and dark color.
|
||||
/// </summary>
|
||||
DarkVibrant,
|
||||
/// <summary>
|
||||
/// Represents a saturated and dark color.
|
||||
/// </summary>
|
||||
DarkVibrant,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a desaturated color.
|
||||
/// </summary>
|
||||
Muted,
|
||||
/// <summary>
|
||||
/// Represents a desaturated color.
|
||||
/// </summary>
|
||||
Muted,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a desaturated and light color.
|
||||
/// </summary>
|
||||
LightMuted,
|
||||
/// <summary>
|
||||
/// Represents a desaturated and light color.
|
||||
/// </summary>
|
||||
LightMuted,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a desaturated and dark color.
|
||||
/// </summary>
|
||||
DarkMuted
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents a desaturated and dark color.
|
||||
/// </summary>
|
||||
DarkMuted
|
||||
}
|
||||
@@ -1,38 +1,46 @@
|
||||
using SkiaSharp;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service providing a pallette of colors in a bitmap based on vibrant.js
|
||||
/// </summary>
|
||||
public interface IColorQuantizerService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// A service providing a pallette of colors in a bitmap based on vibrant.js
|
||||
/// Reduces an <see cref="IEnumerable{SKColor}" /> to a given amount of relevant colors. Based on the Median Cut
|
||||
/// algorithm
|
||||
/// </summary>
|
||||
public interface IColorQuantizerService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Reduces an <see cref="IEnumerable{SKColor}"/> to a given amount of relevant colors. Based on the Median Cut algorithm
|
||||
/// </summary>
|
||||
/// <param name="colors">The colors to quantize.</param>
|
||||
/// <param name="amount">The number of colors that should be calculated. Must be a power of two.</param>
|
||||
/// <returns>The quantized colors.</returns>
|
||||
public SKColor[] Quantize(IEnumerable<SKColor> colors, int amount);
|
||||
/// <param name="colors">The colors to quantize.</param>
|
||||
/// <param name="amount">The number of colors that should be calculated. Must be a power of two.</param>
|
||||
/// <returns>The quantized colors.</returns>
|
||||
public SKColor[] Quantize(IEnumerable<SKColor> colors, int amount);
|
||||
|
||||
/// <summary>
|
||||
/// Finds colors with certain characteristics in a given <see cref="IEnumerable{SKColor}"/>.<para />
|
||||
/// Vibrant variants are more saturated, while Muted colors are less.<para />
|
||||
/// Light and Dark colors have higher and lower lightness values, respectively.
|
||||
/// </summary>
|
||||
/// <param name="colors">The colors to find the variations in</param>
|
||||
/// <param name="type">Which type of color to find</param>
|
||||
/// <param name="ignoreLimits">Ignore hard limits on whether a color is considered for each category. Result may be <see cref="SKColor.Empty"/> if this is false</param>
|
||||
/// <returns>The color found</returns>
|
||||
public SKColor FindColorVariation(IEnumerable<SKColor> colors, ColorType type, bool ignoreLimits = false);
|
||||
/// <summary>
|
||||
/// Finds colors with certain characteristics in a given <see cref="IEnumerable{SKColor}" />.
|
||||
/// <para />
|
||||
/// Vibrant variants are more saturated, while Muted colors are less.
|
||||
/// <para />
|
||||
/// Light and Dark colors have higher and lower lightness values, respectively.
|
||||
/// </summary>
|
||||
/// <param name="colors">The colors to find the variations in</param>
|
||||
/// <param name="type">Which type of color to find</param>
|
||||
/// <param name="ignoreLimits">
|
||||
/// Ignore hard limits on whether a color is considered for each category. Result may be
|
||||
/// <see cref="SKColor.Empty" /> if this is false
|
||||
/// </param>
|
||||
/// <returns>The color found</returns>
|
||||
public SKColor FindColorVariation(IEnumerable<SKColor> colors, ColorType type, bool ignoreLimits = false);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all the color variations available and returns a struct containing them all.
|
||||
/// </summary>
|
||||
/// <param name="colors">The colors to find the variations in</param>
|
||||
/// <param name="ignoreLimits">Ignore hard limits on whether a color is considered for each category. Some colors may be <see cref="SKColor.Empty"/> if this is false</param>
|
||||
/// <returns>A swatch containing all color variations</returns>
|
||||
public ColorSwatch FindAllColorVariations(IEnumerable<SKColor> colors, bool ignoreLimits = false);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Finds all the color variations available and returns a struct containing them all.
|
||||
/// </summary>
|
||||
/// <param name="colors">The colors to find the variations in</param>
|
||||
/// <param name="ignoreLimits">
|
||||
/// Ignore hard limits on whether a color is considered for each category. Some colors may be
|
||||
/// <see cref="SKColor.Empty" /> if this is false
|
||||
/// </param>
|
||||
/// <returns>A swatch containing all color variations</returns>
|
||||
public ColorSwatch FindAllColorVariations(IEnumerable<SKColor> colors, bool ignoreLimits = false);
|
||||
}
|
||||
@@ -1,47 +1,46 @@
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core.Services.Core
|
||||
namespace Artemis.Core.Services.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents build information related to the currently running Artemis build
|
||||
/// </summary>
|
||||
public class BuildInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents build information related to the currently running Artemis build
|
||||
/// Gets the unique ID of this build
|
||||
/// </summary>
|
||||
public class BuildInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique ID of this build
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public int BuildId { get; internal set; }
|
||||
[JsonProperty]
|
||||
public int BuildId { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the build number. This contains the date and the build count for that day.
|
||||
/// <para>Per example <c>20210108.4</c></para>
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public double BuildNumber { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the build number. This contains the date and the build count for that day.
|
||||
/// <para>Per example <c>20210108.4</c></para>
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public double BuildNumber { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the build number formatted as a string. This contains the date and the build count for that day.
|
||||
/// <para>Per example <c>20210108.4</c></para>
|
||||
/// </summary>
|
||||
public string BuildNumberDisplay => BuildNumber.ToString(CultureInfo.InvariantCulture);
|
||||
/// <summary>
|
||||
/// Gets the build number formatted as a string. This contains the date and the build count for that day.
|
||||
/// <para>Per example <c>20210108.4</c></para>
|
||||
/// </summary>
|
||||
public string BuildNumberDisplay => BuildNumber.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the branch of the triggering repo the build was created for.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string SourceBranch { get; internal set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets the branch of the triggering repo the build was created for.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string SourceBranch { get; internal set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the commit ID used to create this build
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string SourceVersion { get; internal set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets the commit ID used to create this build
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
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; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the current build is a local build
|
||||
/// </summary>
|
||||
public bool IsLocalBuild { get; internal set; }
|
||||
}
|
||||
@@ -13,232 +13,231 @@ using Serilog;
|
||||
using Serilog.Events;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides Artemis's core update loop
|
||||
/// </summary>
|
||||
internal class CoreService : ICoreService
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides Artemis's core update loop
|
||||
/// </summary>
|
||||
internal class CoreService : ICoreService
|
||||
internal static IKernel? Kernel;
|
||||
private readonly Stopwatch _frameStopWatch;
|
||||
private readonly ILogger _logger;
|
||||
private readonly PluginSetting<LogEventLevel> _loggingLevel;
|
||||
private readonly IModuleService _moduleService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IScriptingService _scriptingService;
|
||||
private readonly List<Exception> _updateExceptions = new();
|
||||
|
||||
private int _frames;
|
||||
private DateTime _lastExceptionLog;
|
||||
private DateTime _lastFrameRateSample;
|
||||
|
||||
// ReSharper disable UnusedParameter.Local
|
||||
public CoreService(IKernel kernel,
|
||||
ILogger logger,
|
||||
StorageMigrationService _1, // injected to ensure migration runs early
|
||||
ISettingsService settingsService,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IRgbService rgbService,
|
||||
IProfileService profileService,
|
||||
IModuleService moduleService,
|
||||
IScriptingService scriptingService,
|
||||
IProcessMonitorService _2)
|
||||
{
|
||||
internal static IKernel? Kernel;
|
||||
private readonly Stopwatch _frameStopWatch;
|
||||
private readonly ILogger _logger;
|
||||
private readonly PluginSetting<LogEventLevel> _loggingLevel;
|
||||
private readonly IModuleService _moduleService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IScriptingService _scriptingService;
|
||||
private readonly List<Exception> _updateExceptions = new();
|
||||
Kernel = kernel;
|
||||
Constants.CorePlugin.Kernel = kernel;
|
||||
|
||||
private int _frames;
|
||||
private DateTime _lastExceptionLog;
|
||||
private DateTime _lastFrameRateSample;
|
||||
_logger = logger;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_rgbService = rgbService;
|
||||
_profileService = profileService;
|
||||
_moduleService = moduleService;
|
||||
_scriptingService = scriptingService;
|
||||
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
|
||||
_frameStopWatch = new Stopwatch();
|
||||
StartupArguments = new List<string>();
|
||||
|
||||
// ReSharper disable UnusedParameter.Local
|
||||
public CoreService(IKernel kernel,
|
||||
ILogger logger,
|
||||
StorageMigrationService _1, // injected to ensure migration runs early
|
||||
ISettingsService settingsService,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IRgbService rgbService,
|
||||
IProfileService profileService,
|
||||
IModuleService moduleService,
|
||||
IScriptingService scriptingService,
|
||||
IProcessMonitorService _2)
|
||||
_rgbService.Surface.Updating += SurfaceOnUpdating;
|
||||
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
|
||||
}
|
||||
|
||||
// ReSharper restore UnusedParameter.Local
|
||||
|
||||
protected virtual void OnFrameRendering(FrameRenderingEventArgs e)
|
||||
{
|
||||
FrameRendering?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnFrameRendered(FrameRenderedEventArgs e)
|
||||
{
|
||||
FrameRendered?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void ApplyLoggingLevel()
|
||||
{
|
||||
string? argument = StartupArguments.FirstOrDefault(a => a.StartsWith("--logging"));
|
||||
if (argument != null)
|
||||
{
|
||||
Kernel = kernel;
|
||||
Constants.CorePlugin.Kernel = kernel;
|
||||
|
||||
_logger = logger;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_rgbService = rgbService;
|
||||
_profileService = profileService;
|
||||
_moduleService = moduleService;
|
||||
_scriptingService = scriptingService;
|
||||
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
|
||||
_frameStopWatch = new Stopwatch();
|
||||
StartupArguments = new List<string>();
|
||||
|
||||
_rgbService.Surface.Updating += SurfaceOnUpdating;
|
||||
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
|
||||
}
|
||||
|
||||
// ReSharper restore UnusedParameter.Local
|
||||
|
||||
protected virtual void OnFrameRendering(FrameRenderingEventArgs e)
|
||||
{
|
||||
FrameRendering?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnFrameRendered(FrameRenderedEventArgs e)
|
||||
{
|
||||
FrameRendered?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void ApplyLoggingLevel()
|
||||
{
|
||||
string? argument = StartupArguments.FirstOrDefault(a => a.StartsWith("--logging"));
|
||||
if (argument != null)
|
||||
// Parse the provided log level
|
||||
string[] parts = argument.Split('=');
|
||||
if (parts.Length == 2 && Enum.TryParse(typeof(LogEventLevel), parts[1], true, out object? logLevelArgument))
|
||||
{
|
||||
// Parse the provided log level
|
||||
string[] parts = argument.Split('=');
|
||||
if (parts.Length == 2 && Enum.TryParse(typeof(LogEventLevel), parts[1], true, out object? logLevelArgument))
|
||||
{
|
||||
_logger.Information("Setting logging level to {loggingLevel} from startup argument", (LogEventLevel) logLevelArgument!);
|
||||
LoggerProvider.LoggingLevelSwitch.MinimumLevel = (LogEventLevel) logLevelArgument;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Warning("Failed to set log level from startup argument {argument}", argument);
|
||||
_logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value);
|
||||
LoggerProvider.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value;
|
||||
}
|
||||
_logger.Information("Setting logging level to {loggingLevel} from startup argument", (LogEventLevel) logLevelArgument!);
|
||||
LoggerProvider.LoggingLevelSwitch.MinimumLevel = (LogEventLevel) logLevelArgument;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Warning("Failed to set log level from startup argument {argument}", argument);
|
||||
_logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value);
|
||||
LoggerProvider.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private void SurfaceOnUpdating(UpdatingEventArgs args)
|
||||
else
|
||||
{
|
||||
if (_rgbService.IsRenderPaused)
|
||||
return;
|
||||
|
||||
if (_rgbService.FlushLeds)
|
||||
{
|
||||
_rgbService.FlushLeds = false;
|
||||
_rgbService.Surface.Update(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_frameStopWatch.Restart();
|
||||
|
||||
foreach (GlobalScript script in _scriptingService.GlobalScripts)
|
||||
script.OnCoreUpdating(args.DeltaTime);
|
||||
|
||||
_moduleService.UpdateActiveModules(args.DeltaTime);
|
||||
SKTexture texture = _rgbService.OpenRender();
|
||||
SKCanvas canvas = texture.Surface.Canvas;
|
||||
canvas.Save();
|
||||
if (Math.Abs(texture.RenderScale - 1) > 0.001)
|
||||
canvas.Scale(texture.RenderScale);
|
||||
canvas.Clear(new SKColor(0, 0, 0));
|
||||
|
||||
if (!ProfileRenderingDisabled)
|
||||
{
|
||||
_profileService.UpdateProfiles(args.DeltaTime);
|
||||
_profileService.RenderProfiles(canvas);
|
||||
}
|
||||
|
||||
OnFrameRendering(new FrameRenderingEventArgs(canvas, args.DeltaTime, _rgbService.Surface));
|
||||
canvas.RestoreToCount(-1);
|
||||
canvas.Flush();
|
||||
|
||||
OnFrameRendered(new FrameRenderedEventArgs(texture, _rgbService.Surface));
|
||||
|
||||
foreach (GlobalScript script in _scriptingService.GlobalScripts)
|
||||
script.OnCoreUpdated(args.DeltaTime);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_updateExceptions.Add(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_rgbService.CloseRender();
|
||||
_frameStopWatch.Stop();
|
||||
_frames++;
|
||||
|
||||
if ((DateTime.Now - _lastFrameRateSample).TotalSeconds >= 1)
|
||||
{
|
||||
FrameRate = _frames;
|
||||
_frames = 0;
|
||||
_lastFrameRateSample = DateTime.Now;
|
||||
}
|
||||
|
||||
FrameTime = _frameStopWatch.Elapsed;
|
||||
|
||||
LogUpdateExceptions();
|
||||
}
|
||||
_logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value);
|
||||
LoggerProvider.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value;
|
||||
}
|
||||
|
||||
private void LogUpdateExceptions()
|
||||
{
|
||||
// Only log update exceptions every 10 seconds to avoid spamming the logs
|
||||
if (DateTime.Now - _lastExceptionLog < TimeSpan.FromSeconds(10))
|
||||
return;
|
||||
_lastExceptionLog = DateTime.Now;
|
||||
|
||||
if (!_updateExceptions.Any())
|
||||
return;
|
||||
|
||||
// Group by stack trace, that should gather up duplicate exceptions
|
||||
foreach (IGrouping<string?, Exception> exceptions in _updateExceptions.GroupBy(e => e.StackTrace))
|
||||
_logger.Warning(exceptions.First(), "Exception was thrown {count} times during update in the last 10 seconds", exceptions.Count());
|
||||
|
||||
// When logging is finished start with a fresh slate
|
||||
_updateExceptions.Clear();
|
||||
}
|
||||
|
||||
private void OnInitialized()
|
||||
{
|
||||
IsInitialized = true;
|
||||
Initialized?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public int FrameRate { get; private set; }
|
||||
public TimeSpan FrameTime { get; private set; }
|
||||
public bool ProfileRenderingDisabled { get; set; }
|
||||
public List<string> StartupArguments { get; set; }
|
||||
public bool IsElevated { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Dispose services
|
||||
_pluginManagementService.Dispose();
|
||||
}
|
||||
|
||||
public bool IsInitialized { get; set; }
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (IsInitialized)
|
||||
throw new ArtemisCoreException("Cannot initialize the core as it is already initialized.");
|
||||
|
||||
AssemblyInformationalVersionAttribute? versionAttribute = typeof(CoreService).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||
_logger.Information(
|
||||
"Initializing Artemis Core version {version}, build {buildNumber} branch {branch}.",
|
||||
versionAttribute?.InformationalVersion,
|
||||
Constants.BuildInfo.BuildNumber,
|
||||
Constants.BuildInfo.SourceBranch
|
||||
);
|
||||
_logger.Information("Startup arguments: {args}", StartupArguments);
|
||||
_logger.Information("Elevated permissions: {perms}", IsElevated);
|
||||
_logger.Information("Stopwatch high resolution: {perms}", Stopwatch.IsHighResolution);
|
||||
|
||||
ApplyLoggingLevel();
|
||||
|
||||
// Don't remove even if it looks useless
|
||||
// Just this line should prevent a certain someone from removing HidSharp as an unused dependency as well
|
||||
Version? hidSharpVersion = Assembly.GetAssembly(typeof(HidDevice))!.GetName().Version;
|
||||
_logger.Debug("Forcing plugins to use HidSharp {hidSharpVersion}", hidSharpVersion);
|
||||
|
||||
// Initialize the services
|
||||
_pluginManagementService.CopyBuiltInPlugins();
|
||||
_pluginManagementService.LoadPlugins(StartupArguments, IsElevated);
|
||||
|
||||
_rgbService.ApplyPreferredGraphicsContext(StartupArguments.Contains("--force-software-render"));
|
||||
_rgbService.SetRenderPaused(false);
|
||||
OnInitialized();
|
||||
}
|
||||
|
||||
public event EventHandler? Initialized;
|
||||
public event EventHandler<FrameRenderingEventArgs>? FrameRendering;
|
||||
public event EventHandler<FrameRenderedEventArgs>? FrameRendered;
|
||||
}
|
||||
|
||||
private void SurfaceOnUpdating(UpdatingEventArgs args)
|
||||
{
|
||||
if (_rgbService.IsRenderPaused)
|
||||
return;
|
||||
|
||||
if (_rgbService.FlushLeds)
|
||||
{
|
||||
_rgbService.FlushLeds = false;
|
||||
_rgbService.Surface.Update(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_frameStopWatch.Restart();
|
||||
|
||||
foreach (GlobalScript script in _scriptingService.GlobalScripts)
|
||||
script.OnCoreUpdating(args.DeltaTime);
|
||||
|
||||
_moduleService.UpdateActiveModules(args.DeltaTime);
|
||||
SKTexture texture = _rgbService.OpenRender();
|
||||
SKCanvas canvas = texture.Surface.Canvas;
|
||||
canvas.Save();
|
||||
if (Math.Abs(texture.RenderScale - 1) > 0.001)
|
||||
canvas.Scale(texture.RenderScale);
|
||||
canvas.Clear(new SKColor(0, 0, 0));
|
||||
|
||||
if (!ProfileRenderingDisabled)
|
||||
{
|
||||
_profileService.UpdateProfiles(args.DeltaTime);
|
||||
_profileService.RenderProfiles(canvas);
|
||||
}
|
||||
|
||||
OnFrameRendering(new FrameRenderingEventArgs(canvas, args.DeltaTime, _rgbService.Surface));
|
||||
canvas.RestoreToCount(-1);
|
||||
canvas.Flush();
|
||||
|
||||
OnFrameRendered(new FrameRenderedEventArgs(texture, _rgbService.Surface));
|
||||
|
||||
foreach (GlobalScript script in _scriptingService.GlobalScripts)
|
||||
script.OnCoreUpdated(args.DeltaTime);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_updateExceptions.Add(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_rgbService.CloseRender();
|
||||
_frameStopWatch.Stop();
|
||||
_frames++;
|
||||
|
||||
if ((DateTime.Now - _lastFrameRateSample).TotalSeconds >= 1)
|
||||
{
|
||||
FrameRate = _frames;
|
||||
_frames = 0;
|
||||
_lastFrameRateSample = DateTime.Now;
|
||||
}
|
||||
|
||||
FrameTime = _frameStopWatch.Elapsed;
|
||||
|
||||
LogUpdateExceptions();
|
||||
}
|
||||
}
|
||||
|
||||
private void LogUpdateExceptions()
|
||||
{
|
||||
// Only log update exceptions every 10 seconds to avoid spamming the logs
|
||||
if (DateTime.Now - _lastExceptionLog < TimeSpan.FromSeconds(10))
|
||||
return;
|
||||
_lastExceptionLog = DateTime.Now;
|
||||
|
||||
if (!_updateExceptions.Any())
|
||||
return;
|
||||
|
||||
// Group by stack trace, that should gather up duplicate exceptions
|
||||
foreach (IGrouping<string?, Exception> exceptions in _updateExceptions.GroupBy(e => e.StackTrace))
|
||||
_logger.Warning(exceptions.First(), "Exception was thrown {count} times during update in the last 10 seconds", exceptions.Count());
|
||||
|
||||
// When logging is finished start with a fresh slate
|
||||
_updateExceptions.Clear();
|
||||
}
|
||||
|
||||
private void OnInitialized()
|
||||
{
|
||||
IsInitialized = true;
|
||||
Initialized?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public int FrameRate { get; private set; }
|
||||
public TimeSpan FrameTime { get; private set; }
|
||||
public bool ProfileRenderingDisabled { get; set; }
|
||||
public List<string> StartupArguments { get; set; }
|
||||
public bool IsElevated { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Dispose services
|
||||
_pluginManagementService.Dispose();
|
||||
}
|
||||
|
||||
public bool IsInitialized { get; set; }
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (IsInitialized)
|
||||
throw new ArtemisCoreException("Cannot initialize the core as it is already initialized.");
|
||||
|
||||
AssemblyInformationalVersionAttribute? versionAttribute = typeof(CoreService).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||
_logger.Information(
|
||||
"Initializing Artemis Core version {version}, build {buildNumber} branch {branch}.",
|
||||
versionAttribute?.InformationalVersion,
|
||||
Constants.BuildInfo.BuildNumber,
|
||||
Constants.BuildInfo.SourceBranch
|
||||
);
|
||||
_logger.Information("Startup arguments: {args}", StartupArguments);
|
||||
_logger.Information("Elevated permissions: {perms}", IsElevated);
|
||||
_logger.Information("Stopwatch high resolution: {perms}", Stopwatch.IsHighResolution);
|
||||
|
||||
ApplyLoggingLevel();
|
||||
|
||||
// Don't remove even if it looks useless
|
||||
// Just this line should prevent a certain someone from removing HidSharp as an unused dependency as well
|
||||
Version? hidSharpVersion = Assembly.GetAssembly(typeof(HidDevice))!.GetName().Version;
|
||||
_logger.Debug("Forcing plugins to use HidSharp {hidSharpVersion}", hidSharpVersion);
|
||||
|
||||
// Initialize the services
|
||||
_pluginManagementService.CopyBuiltInPlugins();
|
||||
_pluginManagementService.LoadPlugins(StartupArguments, IsElevated);
|
||||
|
||||
_rgbService.ApplyPreferredGraphicsContext(StartupArguments.Contains("--force-software-render"));
|
||||
_rgbService.SetRenderPaused(false);
|
||||
OnInitialized();
|
||||
}
|
||||
|
||||
public event EventHandler? Initialized;
|
||||
public event EventHandler<FrameRenderingEventArgs>? FrameRendering;
|
||||
public event EventHandler<FrameRenderedEventArgs>? FrameRendered;
|
||||
}
|
||||
@@ -1,46 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class DeviceService : IDeviceService
|
||||
{
|
||||
internal class DeviceService : IDeviceService
|
||||
private readonly IRgbService _rgbService;
|
||||
|
||||
public DeviceService(IRgbService rgbService)
|
||||
{
|
||||
private readonly IRgbService _rgbService;
|
||||
_rgbService = rgbService;
|
||||
}
|
||||
|
||||
public DeviceService(IRgbService rgbService)
|
||||
private void BlinkDevice(ArtemisDevice device, int blinkCount)
|
||||
{
|
||||
// Create a LED group way at the top
|
||||
ListLedGroup ledGroup = new(_rgbService.Surface, device.Leds.Select(l => l.RgbLed))
|
||||
{
|
||||
_rgbService = rgbService;
|
||||
}
|
||||
Brush = new SolidColorBrush(new Color(255, 255, 255)),
|
||||
ZIndex = 999
|
||||
};
|
||||
|
||||
public void IdentifyDevice(ArtemisDevice device)
|
||||
// After 200ms, detach the LED group
|
||||
Task.Run(async () =>
|
||||
{
|
||||
BlinkDevice(device, 0);
|
||||
}
|
||||
await Task.Delay(200);
|
||||
ledGroup.Detach();
|
||||
|
||||
private void BlinkDevice(ArtemisDevice device, int blinkCount)
|
||||
{
|
||||
// Create a LED group way at the top
|
||||
ListLedGroup ledGroup = new(_rgbService.Surface, device.Leds.Select(l => l.RgbLed))
|
||||
{
|
||||
Brush = new SolidColorBrush(new Color(255, 255, 255)),
|
||||
ZIndex = 999
|
||||
};
|
||||
|
||||
// After 200ms, detach the LED group
|
||||
Task.Run(async () =>
|
||||
if (blinkCount < 5)
|
||||
{
|
||||
// After another 200ms, start over, repeat six times
|
||||
await Task.Delay(200);
|
||||
ledGroup.Detach();
|
||||
BlinkDevice(device, blinkCount + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (blinkCount < 5)
|
||||
{
|
||||
// After another 200ms, start over, repeat six times
|
||||
await Task.Delay(200);
|
||||
BlinkDevice(device, blinkCount + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
public void IdentifyDevice(ArtemisDevice device)
|
||||
{
|
||||
BlinkDevice(device, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a device that provides input to the <see cref="IInputService" />
|
||||
/// </summary>
|
||||
public enum InputDeviceType
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a device that provides input to the <see cref="IInputService" />
|
||||
/// None
|
||||
/// </summary>
|
||||
public enum InputDeviceType
|
||||
{
|
||||
/// <summary>
|
||||
/// None
|
||||
/// </summary>
|
||||
None,
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// A keyboard
|
||||
/// </summary>
|
||||
Keyboard,
|
||||
/// <summary>
|
||||
/// A keyboard
|
||||
/// </summary>
|
||||
Keyboard,
|
||||
|
||||
/// <summary>
|
||||
/// A mouse
|
||||
/// </summary>
|
||||
Mouse
|
||||
}
|
||||
/// <summary>
|
||||
/// A mouse
|
||||
/// </summary>
|
||||
Mouse
|
||||
}
|
||||
@@ -1,537 +1,536 @@
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>Specifies the possible key values on a keyboard.</summary>
|
||||
public enum KeyboardKey
|
||||
{
|
||||
/// <summary>Specifies the possible key values on a keyboard.</summary>
|
||||
public enum KeyboardKey
|
||||
{
|
||||
/// <summary>No key pressed.</summary>
|
||||
None = 0,
|
||||
/// <summary>No key pressed.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>The Cancel key.</summary>
|
||||
Cancel = 1,
|
||||
/// <summary>The Cancel key.</summary>
|
||||
Cancel = 1,
|
||||
|
||||
/// <summary>The Backspace key.</summary>
|
||||
Back = 2,
|
||||
/// <summary>The Backspace key.</summary>
|
||||
Back = 2,
|
||||
|
||||
/// <summary>The Tab key.</summary>
|
||||
Tab = 3,
|
||||
/// <summary>The Tab key.</summary>
|
||||
Tab = 3,
|
||||
|
||||
/// <summary>The Linefeed key.</summary>
|
||||
LineFeed = 4,
|
||||
/// <summary>The Linefeed key.</summary>
|
||||
LineFeed = 4,
|
||||
|
||||
/// <summary>The Clear key.</summary>
|
||||
Clear = 5,
|
||||
/// <summary>The Clear key.</summary>
|
||||
Clear = 5,
|
||||
|
||||
/// <summary>The Enter key.</summary>
|
||||
Enter = 6,
|
||||
/// <summary>The Enter key.</summary>
|
||||
Enter = 6,
|
||||
|
||||
/// <summary>The Return key.</summary>
|
||||
Return = 6,
|
||||
/// <summary>The Return key.</summary>
|
||||
Return = 6,
|
||||
|
||||
/// <summary>The Pause key.</summary>
|
||||
Pause = 7,
|
||||
/// <summary>The Pause key.</summary>
|
||||
Pause = 7,
|
||||
|
||||
/// <summary>The Caps Lock key.</summary>
|
||||
CapsLock = 8,
|
||||
/// <summary>The Caps Lock key.</summary>
|
||||
CapsLock = 8,
|
||||
|
||||
/// <summary>The IME Hangul mode key.</summary>
|
||||
HangulMode = 9,
|
||||
/// <summary>The IME Hangul mode key.</summary>
|
||||
HangulMode = 9,
|
||||
|
||||
/// <summary>The IME Junja mode key.</summary>
|
||||
JunjaMode = 10, // 0x0000000A
|
||||
/// <summary>The IME Junja mode key.</summary>
|
||||
JunjaMode = 10, // 0x0000000A
|
||||
|
||||
/// <summary>The IME Final mode key.</summary>
|
||||
FinalMode = 11, // 0x0000000B
|
||||
/// <summary>The IME Final mode key.</summary>
|
||||
FinalMode = 11, // 0x0000000B
|
||||
|
||||
/// <summary>The IME Hanja mode key.</summary>
|
||||
HanjaMode = 12, // 0x0000000C
|
||||
/// <summary>The IME Hanja mode key.</summary>
|
||||
HanjaMode = 12, // 0x0000000C
|
||||
|
||||
/// <summary>The ESC key.</summary>
|
||||
Escape = 13, // 0x0000000D
|
||||
/// <summary>The ESC key.</summary>
|
||||
Escape = 13, // 0x0000000D
|
||||
|
||||
/// <summary>The IME Convert key.</summary>
|
||||
ImeConvert = 14, // 0x0000000E
|
||||
/// <summary>The IME Convert key.</summary>
|
||||
ImeConvert = 14, // 0x0000000E
|
||||
|
||||
/// <summary>The IME NonConvert key.</summary>
|
||||
ImeNonConvert = 15, // 0x0000000F
|
||||
/// <summary>The IME NonConvert key.</summary>
|
||||
ImeNonConvert = 15, // 0x0000000F
|
||||
|
||||
/// <summary>The IME Accept key.</summary>
|
||||
ImeAccept = 16, // 0x00000010
|
||||
/// <summary>The IME Accept key.</summary>
|
||||
ImeAccept = 16, // 0x00000010
|
||||
|
||||
/// <summary>The IME Mode change request.</summary>
|
||||
ImeModeChange = 17, // 0x00000011
|
||||
/// <summary>The IME Mode change request.</summary>
|
||||
ImeModeChange = 17, // 0x00000011
|
||||
|
||||
/// <summary>The Spacebar key.</summary>
|
||||
Space = 18, // 0x00000012
|
||||
/// <summary>The Spacebar key.</summary>
|
||||
Space = 18, // 0x00000012
|
||||
|
||||
/// <summary>The Page Up key.</summary>
|
||||
PageUp = 19, // 0x00000013
|
||||
/// <summary>The Page Up key.</summary>
|
||||
PageUp = 19, // 0x00000013
|
||||
|
||||
/// <summary>The Page Up key.</summary>
|
||||
Prior = 19, // 0x00000013
|
||||
/// <summary>The Page Up key.</summary>
|
||||
Prior = 19, // 0x00000013
|
||||
|
||||
/// <summary>The Page Down key.</summary>
|
||||
Next = 20, // 0x00000014
|
||||
/// <summary>The Page Down key.</summary>
|
||||
Next = 20, // 0x00000014
|
||||
|
||||
/// <summary>The Page Down key.</summary>
|
||||
PageDown = 20, // 0x00000014
|
||||
/// <summary>The Page Down key.</summary>
|
||||
PageDown = 20, // 0x00000014
|
||||
|
||||
/// <summary>The End key.</summary>
|
||||
End = 21, // 0x00000015
|
||||
/// <summary>The End key.</summary>
|
||||
End = 21, // 0x00000015
|
||||
|
||||
/// <summary>The Home key.</summary>
|
||||
Home = 22, // 0x00000016
|
||||
/// <summary>The Home key.</summary>
|
||||
Home = 22, // 0x00000016
|
||||
|
||||
/// <summary>The Left Arrow key.</summary>
|
||||
Left = 23, // 0x00000017
|
||||
/// <summary>The Left Arrow key.</summary>
|
||||
Left = 23, // 0x00000017
|
||||
|
||||
/// <summary>The Up Arrow key.</summary>
|
||||
Up = 24, // 0x00000018
|
||||
/// <summary>The Up Arrow key.</summary>
|
||||
Up = 24, // 0x00000018
|
||||
|
||||
/// <summary>The Right Arrow key.</summary>
|
||||
Right = 25, // 0x00000019
|
||||
/// <summary>The Right Arrow key.</summary>
|
||||
Right = 25, // 0x00000019
|
||||
|
||||
/// <summary>The Down Arrow key.</summary>
|
||||
Down = 26, // 0x0000001A
|
||||
/// <summary>The Down Arrow key.</summary>
|
||||
Down = 26, // 0x0000001A
|
||||
|
||||
/// <summary>The Select key.</summary>
|
||||
Select = 27, // 0x0000001B
|
||||
/// <summary>The Select key.</summary>
|
||||
Select = 27, // 0x0000001B
|
||||
|
||||
/// <summary>The Print key.</summary>
|
||||
Print = 28, // 0x0000001C
|
||||
/// <summary>The Print key.</summary>
|
||||
Print = 28, // 0x0000001C
|
||||
|
||||
/// <summary>The Execute key.</summary>
|
||||
Execute = 29, // 0x0000001D
|
||||
/// <summary>The Execute key.</summary>
|
||||
Execute = 29, // 0x0000001D
|
||||
|
||||
/// <summary>The Print Screen key.</summary>
|
||||
PrintScreen = 30, // 0x0000001E
|
||||
/// <summary>The Print Screen key.</summary>
|
||||
PrintScreen = 30, // 0x0000001E
|
||||
|
||||
/// <summary>The Insert key.</summary>
|
||||
Insert = 31, // 0x0000001F
|
||||
/// <summary>The Insert key.</summary>
|
||||
Insert = 31, // 0x0000001F
|
||||
|
||||
/// <summary>The Delete key.</summary>
|
||||
Delete = 32, // 0x00000020
|
||||
/// <summary>The Delete key.</summary>
|
||||
Delete = 32, // 0x00000020
|
||||
|
||||
/// <summary>The Help key.</summary>
|
||||
Help = 33, // 0x00000021
|
||||
/// <summary>The Help key.</summary>
|
||||
Help = 33, // 0x00000021
|
||||
|
||||
/// <summary>The 0 (zero) key.</summary>
|
||||
D0 = 34, // 0x00000022
|
||||
/// <summary>The 0 (zero) key.</summary>
|
||||
D0 = 34, // 0x00000022
|
||||
|
||||
/// <summary>The 1 (one) key.</summary>
|
||||
D1 = 35, // 0x00000023
|
||||
/// <summary>The 1 (one) key.</summary>
|
||||
D1 = 35, // 0x00000023
|
||||
|
||||
/// <summary>The 2 key.</summary>
|
||||
D2 = 36, // 0x00000024
|
||||
/// <summary>The 2 key.</summary>
|
||||
D2 = 36, // 0x00000024
|
||||
|
||||
/// <summary>The 3 key.</summary>
|
||||
D3 = 37, // 0x00000025
|
||||
/// <summary>The 3 key.</summary>
|
||||
D3 = 37, // 0x00000025
|
||||
|
||||
/// <summary>The 4 key.</summary>
|
||||
D4 = 38, // 0x00000026
|
||||
/// <summary>The 4 key.</summary>
|
||||
D4 = 38, // 0x00000026
|
||||
|
||||
/// <summary>The 5 key.</summary>
|
||||
D5 = 39, // 0x00000027
|
||||
/// <summary>The 5 key.</summary>
|
||||
D5 = 39, // 0x00000027
|
||||
|
||||
/// <summary>The 6 key.</summary>
|
||||
D6 = 40, // 0x00000028
|
||||
/// <summary>The 6 key.</summary>
|
||||
D6 = 40, // 0x00000028
|
||||
|
||||
/// <summary>The 7 key.</summary>
|
||||
D7 = 41, // 0x00000029
|
||||
/// <summary>The 7 key.</summary>
|
||||
D7 = 41, // 0x00000029
|
||||
|
||||
/// <summary>The 8 key.</summary>
|
||||
D8 = 42, // 0x0000002A
|
||||
/// <summary>The 8 key.</summary>
|
||||
D8 = 42, // 0x0000002A
|
||||
|
||||
/// <summary>The 9 key.</summary>
|
||||
D9 = 43, // 0x0000002B
|
||||
/// <summary>The 9 key.</summary>
|
||||
D9 = 43, // 0x0000002B
|
||||
|
||||
/// <summary>The A key.</summary>
|
||||
A = 44, // 0x0000002C
|
||||
/// <summary>The A key.</summary>
|
||||
A = 44, // 0x0000002C
|
||||
|
||||
/// <summary>The B key.</summary>
|
||||
B = 45, // 0x0000002D
|
||||
/// <summary>The B key.</summary>
|
||||
B = 45, // 0x0000002D
|
||||
|
||||
/// <summary>The C key.</summary>
|
||||
C = 46, // 0x0000002E
|
||||
/// <summary>The C key.</summary>
|
||||
C = 46, // 0x0000002E
|
||||
|
||||
/// <summary>The D key.</summary>
|
||||
D = 47, // 0x0000002F
|
||||
/// <summary>The D key.</summary>
|
||||
D = 47, // 0x0000002F
|
||||
|
||||
/// <summary>The E key.</summary>
|
||||
E = 48, // 0x00000030
|
||||
/// <summary>The E key.</summary>
|
||||
E = 48, // 0x00000030
|
||||
|
||||
/// <summary>The F key.</summary>
|
||||
F = 49, // 0x00000031
|
||||
/// <summary>The F key.</summary>
|
||||
F = 49, // 0x00000031
|
||||
|
||||
/// <summary>The G key.</summary>
|
||||
G = 50, // 0x00000032
|
||||
/// <summary>The G key.</summary>
|
||||
G = 50, // 0x00000032
|
||||
|
||||
/// <summary>The H key.</summary>
|
||||
H = 51, // 0x00000033
|
||||
/// <summary>The H key.</summary>
|
||||
H = 51, // 0x00000033
|
||||
|
||||
/// <summary>The I key.</summary>
|
||||
I = 52, // 0x00000034
|
||||
/// <summary>The I key.</summary>
|
||||
I = 52, // 0x00000034
|
||||
|
||||
/// <summary>The J key.</summary>
|
||||
J = 53, // 0x00000035
|
||||
/// <summary>The J key.</summary>
|
||||
J = 53, // 0x00000035
|
||||
|
||||
/// <summary>The K key.</summary>
|
||||
K = 54, // 0x00000036
|
||||
/// <summary>The K key.</summary>
|
||||
K = 54, // 0x00000036
|
||||
|
||||
/// <summary>The L key.</summary>
|
||||
L = 55, // 0x00000037
|
||||
/// <summary>The L key.</summary>
|
||||
L = 55, // 0x00000037
|
||||
|
||||
/// <summary>The M key.</summary>
|
||||
M = 56, // 0x00000038
|
||||
/// <summary>The M key.</summary>
|
||||
M = 56, // 0x00000038
|
||||
|
||||
/// <summary>The N key.</summary>
|
||||
N = 57, // 0x00000039
|
||||
/// <summary>The N key.</summary>
|
||||
N = 57, // 0x00000039
|
||||
|
||||
/// <summary>The O key.</summary>
|
||||
O = 58, // 0x0000003A
|
||||
/// <summary>The O key.</summary>
|
||||
O = 58, // 0x0000003A
|
||||
|
||||
/// <summary>The P key.</summary>
|
||||
P = 59, // 0x0000003B
|
||||
/// <summary>The P key.</summary>
|
||||
P = 59, // 0x0000003B
|
||||
|
||||
/// <summary>The Q key.</summary>
|
||||
Q = 60, // 0x0000003C
|
||||
/// <summary>The Q key.</summary>
|
||||
Q = 60, // 0x0000003C
|
||||
|
||||
/// <summary>The R key.</summary>
|
||||
R = 61, // 0x0000003D
|
||||
/// <summary>The R key.</summary>
|
||||
R = 61, // 0x0000003D
|
||||
|
||||
/// <summary>The S key.</summary>
|
||||
S = 62, // 0x0000003E
|
||||
/// <summary>The S key.</summary>
|
||||
S = 62, // 0x0000003E
|
||||
|
||||
/// <summary>The T key.</summary>
|
||||
T = 63, // 0x0000003F
|
||||
/// <summary>The T key.</summary>
|
||||
T = 63, // 0x0000003F
|
||||
|
||||
/// <summary>The U key.</summary>
|
||||
U = 64, // 0x00000040
|
||||
/// <summary>The U key.</summary>
|
||||
U = 64, // 0x00000040
|
||||
|
||||
/// <summary>The V key.</summary>
|
||||
V = 65, // 0x00000041
|
||||
/// <summary>The V key.</summary>
|
||||
V = 65, // 0x00000041
|
||||
|
||||
/// <summary>The W key.</summary>
|
||||
W = 66, // 0x00000042
|
||||
/// <summary>The W key.</summary>
|
||||
W = 66, // 0x00000042
|
||||
|
||||
/// <summary>The X key.</summary>
|
||||
X = 67, // 0x00000043
|
||||
/// <summary>The X key.</summary>
|
||||
X = 67, // 0x00000043
|
||||
|
||||
/// <summary>The Y key.</summary>
|
||||
Y = 68, // 0x00000044
|
||||
/// <summary>The Y key.</summary>
|
||||
Y = 68, // 0x00000044
|
||||
|
||||
/// <summary>The Z key.</summary>
|
||||
Z = 69, // 0x00000045
|
||||
/// <summary>The Z key.</summary>
|
||||
Z = 69, // 0x00000045
|
||||
|
||||
/// <summary>The left Windows logo key (Microsoft Natural Keyboard).</summary>
|
||||
LWin = 70, // 0x00000046
|
||||
/// <summary>The left Windows logo key (Microsoft Natural Keyboard).</summary>
|
||||
LWin = 70, // 0x00000046
|
||||
|
||||
/// <summary>The right Windows logo key (Microsoft Natural Keyboard).</summary>
|
||||
RWin = 71, // 0x00000047
|
||||
/// <summary>The right Windows logo key (Microsoft Natural Keyboard).</summary>
|
||||
RWin = 71, // 0x00000047
|
||||
|
||||
/// <summary>
|
||||
/// The Application key (Microsoft Natural Keyboard). Also known as the Menu key, as it displays an
|
||||
/// application-specific context menu.
|
||||
/// </summary>
|
||||
Apps = 72, // 0x00000048
|
||||
/// <summary>
|
||||
/// The Application key (Microsoft Natural Keyboard). Also known as the Menu key, as it displays an
|
||||
/// application-specific context menu.
|
||||
/// </summary>
|
||||
Apps = 72, // 0x00000048
|
||||
|
||||
/// <summary>The Computer Sleep key.</summary>
|
||||
Sleep = 73, // 0x00000049
|
||||
/// <summary>The Computer Sleep key.</summary>
|
||||
Sleep = 73, // 0x00000049
|
||||
|
||||
/// <summary>The 0 key on the numeric keypad.</summary>
|
||||
NumPad0 = 74, // 0x0000004A
|
||||
/// <summary>The 0 key on the numeric keypad.</summary>
|
||||
NumPad0 = 74, // 0x0000004A
|
||||
|
||||
/// <summary>The 1 key on the numeric keypad.</summary>
|
||||
NumPad1 = 75, // 0x0000004B
|
||||
/// <summary>The 1 key on the numeric keypad.</summary>
|
||||
NumPad1 = 75, // 0x0000004B
|
||||
|
||||
/// <summary>The 2 key on the numeric keypad.</summary>
|
||||
NumPad2 = 76, // 0x0000004C
|
||||
/// <summary>The 2 key on the numeric keypad.</summary>
|
||||
NumPad2 = 76, // 0x0000004C
|
||||
|
||||
/// <summary>The 3 key on the numeric keypad.</summary>
|
||||
NumPad3 = 77, // 0x0000004D
|
||||
/// <summary>The 3 key on the numeric keypad.</summary>
|
||||
NumPad3 = 77, // 0x0000004D
|
||||
|
||||
/// <summary>The 4 key on the numeric keypad.</summary>
|
||||
NumPad4 = 78, // 0x0000004E
|
||||
/// <summary>The 4 key on the numeric keypad.</summary>
|
||||
NumPad4 = 78, // 0x0000004E
|
||||
|
||||
/// <summary>The 5 key on the numeric keypad.</summary>
|
||||
NumPad5 = 79, // 0x0000004F
|
||||
/// <summary>The 5 key on the numeric keypad.</summary>
|
||||
NumPad5 = 79, // 0x0000004F
|
||||
|
||||
/// <summary>The 6 key on the numeric keypad.</summary>
|
||||
NumPad6 = 80, // 0x00000050
|
||||
/// <summary>The 6 key on the numeric keypad.</summary>
|
||||
NumPad6 = 80, // 0x00000050
|
||||
|
||||
/// <summary>The 7 key on the numeric keypad.</summary>
|
||||
NumPad7 = 81, // 0x00000051
|
||||
/// <summary>The 7 key on the numeric keypad.</summary>
|
||||
NumPad7 = 81, // 0x00000051
|
||||
|
||||
/// <summary>The 8 key on the numeric keypad.</summary>
|
||||
NumPad8 = 82, // 0x00000052
|
||||
/// <summary>The 8 key on the numeric keypad.</summary>
|
||||
NumPad8 = 82, // 0x00000052
|
||||
|
||||
/// <summary>The 9 key on the numeric keypad.</summary>
|
||||
NumPad9 = 83, // 0x00000053
|
||||
/// <summary>The 9 key on the numeric keypad.</summary>
|
||||
NumPad9 = 83, // 0x00000053
|
||||
|
||||
/// <summary>The Multiply key.</summary>
|
||||
Multiply = 84, // 0x00000054
|
||||
/// <summary>The Multiply key.</summary>
|
||||
Multiply = 84, // 0x00000054
|
||||
|
||||
/// <summary>The Add key.</summary>
|
||||
Add = 85, // 0x00000055
|
||||
/// <summary>The Add key.</summary>
|
||||
Add = 85, // 0x00000055
|
||||
|
||||
/// <summary>The Separator key.</summary>
|
||||
Separator = 86, // 0x00000056
|
||||
/// <summary>The Separator key.</summary>
|
||||
Separator = 86, // 0x00000056
|
||||
|
||||
/// <summary>The Subtract key.</summary>
|
||||
Subtract = 87, // 0x00000057
|
||||
/// <summary>The Subtract key.</summary>
|
||||
Subtract = 87, // 0x00000057
|
||||
|
||||
/// <summary>The Decimal key.</summary>
|
||||
Decimal = 88, // 0x00000058
|
||||
/// <summary>The Decimal key.</summary>
|
||||
Decimal = 88, // 0x00000058
|
||||
|
||||
/// <summary>The Divide key.</summary>
|
||||
Divide = 89, // 0x00000059
|
||||
/// <summary>The Divide key.</summary>
|
||||
Divide = 89, // 0x00000059
|
||||
|
||||
/// <summary>The F1 key.</summary>
|
||||
F1 = 90, // 0x0000005A
|
||||
/// <summary>The F1 key.</summary>
|
||||
F1 = 90, // 0x0000005A
|
||||
|
||||
/// <summary>The F2 key.</summary>
|
||||
F2 = 91, // 0x0000005B
|
||||
/// <summary>The F2 key.</summary>
|
||||
F2 = 91, // 0x0000005B
|
||||
|
||||
/// <summary>The F3 key.</summary>
|
||||
F3 = 92, // 0x0000005C
|
||||
/// <summary>The F3 key.</summary>
|
||||
F3 = 92, // 0x0000005C
|
||||
|
||||
/// <summary>The F4 key.</summary>
|
||||
F4 = 93, // 0x0000005D
|
||||
/// <summary>The F4 key.</summary>
|
||||
F4 = 93, // 0x0000005D
|
||||
|
||||
/// <summary>The F5 key.</summary>
|
||||
F5 = 94, // 0x0000005E
|
||||
/// <summary>The F5 key.</summary>
|
||||
F5 = 94, // 0x0000005E
|
||||
|
||||
/// <summary>The F6 key.</summary>
|
||||
F6 = 95, // 0x0000005F
|
||||
/// <summary>The F6 key.</summary>
|
||||
F6 = 95, // 0x0000005F
|
||||
|
||||
/// <summary>The F7 key.</summary>
|
||||
F7 = 96, // 0x00000060
|
||||
/// <summary>The F7 key.</summary>
|
||||
F7 = 96, // 0x00000060
|
||||
|
||||
/// <summary>The F8 key.</summary>
|
||||
F8 = 97, // 0x00000061
|
||||
/// <summary>The F8 key.</summary>
|
||||
F8 = 97, // 0x00000061
|
||||
|
||||
/// <summary>The F9 key.</summary>
|
||||
F9 = 98, // 0x00000062
|
||||
/// <summary>The F9 key.</summary>
|
||||
F9 = 98, // 0x00000062
|
||||
|
||||
/// <summary>The F10 key.</summary>
|
||||
F10 = 99, // 0x00000063
|
||||
/// <summary>The F10 key.</summary>
|
||||
F10 = 99, // 0x00000063
|
||||
|
||||
/// <summary>The F11 key.</summary>
|
||||
F11 = 100, // 0x00000064
|
||||
/// <summary>The F11 key.</summary>
|
||||
F11 = 100, // 0x00000064
|
||||
|
||||
/// <summary>The F12 key.</summary>
|
||||
F12 = 101, // 0x00000065
|
||||
/// <summary>The F12 key.</summary>
|
||||
F12 = 101, // 0x00000065
|
||||
|
||||
/// <summary>The F13 key.</summary>
|
||||
F13 = 102, // 0x00000066
|
||||
/// <summary>The F13 key.</summary>
|
||||
F13 = 102, // 0x00000066
|
||||
|
||||
/// <summary>The F14 key.</summary>
|
||||
F14 = 103, // 0x00000067
|
||||
/// <summary>The F14 key.</summary>
|
||||
F14 = 103, // 0x00000067
|
||||
|
||||
/// <summary>The F15 key.</summary>
|
||||
F15 = 104, // 0x00000068
|
||||
/// <summary>The F15 key.</summary>
|
||||
F15 = 104, // 0x00000068
|
||||
|
||||
/// <summary>The F16 key.</summary>
|
||||
F16 = 105, // 0x00000069
|
||||
/// <summary>The F16 key.</summary>
|
||||
F16 = 105, // 0x00000069
|
||||
|
||||
/// <summary>The F17 key.</summary>
|
||||
F17 = 106, // 0x0000006A
|
||||
/// <summary>The F17 key.</summary>
|
||||
F17 = 106, // 0x0000006A
|
||||
|
||||
/// <summary>The F18 key.</summary>
|
||||
F18 = 107, // 0x0000006B
|
||||
/// <summary>The F18 key.</summary>
|
||||
F18 = 107, // 0x0000006B
|
||||
|
||||
/// <summary>The F19 key.</summary>
|
||||
F19 = 108, // 0x0000006C
|
||||
/// <summary>The F19 key.</summary>
|
||||
F19 = 108, // 0x0000006C
|
||||
|
||||
/// <summary>The F20 key.</summary>
|
||||
F20 = 109, // 0x0000006D
|
||||
/// <summary>The F20 key.</summary>
|
||||
F20 = 109, // 0x0000006D
|
||||
|
||||
/// <summary>The F21 key.</summary>
|
||||
F21 = 110, // 0x0000006E
|
||||
/// <summary>The F21 key.</summary>
|
||||
F21 = 110, // 0x0000006E
|
||||
|
||||
/// <summary>The F22 key.</summary>
|
||||
F22 = 111, // 0x0000006F
|
||||
/// <summary>The F22 key.</summary>
|
||||
F22 = 111, // 0x0000006F
|
||||
|
||||
/// <summary>The F23 key.</summary>
|
||||
F23 = 112, // 0x00000070
|
||||
/// <summary>The F23 key.</summary>
|
||||
F23 = 112, // 0x00000070
|
||||
|
||||
/// <summary>The F24 key.</summary>
|
||||
F24 = 113, // 0x00000071
|
||||
/// <summary>The F24 key.</summary>
|
||||
F24 = 113, // 0x00000071
|
||||
|
||||
/// <summary>The Num Lock key.</summary>
|
||||
NumLock = 114, // 0x00000072
|
||||
/// <summary>The Num Lock key.</summary>
|
||||
NumLock = 114, // 0x00000072
|
||||
|
||||
/// <summary>The Scroll Lock key.</summary>
|
||||
Scroll = 115, // 0x00000073
|
||||
/// <summary>The Scroll Lock key.</summary>
|
||||
Scroll = 115, // 0x00000073
|
||||
|
||||
/// <summary>The left Shift key.</summary>
|
||||
LeftShift = 116, // 0x00000074
|
||||
/// <summary>The left Shift key.</summary>
|
||||
LeftShift = 116, // 0x00000074
|
||||
|
||||
/// <summary>The right Shift key.</summary>
|
||||
RightShift = 117, // 0x00000075
|
||||
/// <summary>The right Shift key.</summary>
|
||||
RightShift = 117, // 0x00000075
|
||||
|
||||
/// <summary>The left CTRL key.</summary>
|
||||
LeftCtrl = 118, // 0x00000076
|
||||
/// <summary>The left CTRL key.</summary>
|
||||
LeftCtrl = 118, // 0x00000076
|
||||
|
||||
/// <summary>The right CTRL key.</summary>
|
||||
RightCtrl = 119, // 0x00000077
|
||||
/// <summary>The right CTRL key.</summary>
|
||||
RightCtrl = 119, // 0x00000077
|
||||
|
||||
/// <summary>The left ALT key.</summary>
|
||||
LeftAlt = 120, // 0x00000078
|
||||
/// <summary>The left ALT key.</summary>
|
||||
LeftAlt = 120, // 0x00000078
|
||||
|
||||
/// <summary>The right ALT key.</summary>
|
||||
RightAlt = 121, // 0x00000079
|
||||
/// <summary>The right ALT key.</summary>
|
||||
RightAlt = 121, // 0x00000079
|
||||
|
||||
/// <summary>The Browser Back key.</summary>
|
||||
BrowserBack = 122, // 0x0000007A
|
||||
/// <summary>The Browser Back key.</summary>
|
||||
BrowserBack = 122, // 0x0000007A
|
||||
|
||||
/// <summary>The Browser Forward key.</summary>
|
||||
BrowserForward = 123, // 0x0000007B
|
||||
/// <summary>The Browser Forward key.</summary>
|
||||
BrowserForward = 123, // 0x0000007B
|
||||
|
||||
/// <summary>The Browser Refresh key.</summary>
|
||||
BrowserRefresh = 124, // 0x0000007C
|
||||
/// <summary>The Browser Refresh key.</summary>
|
||||
BrowserRefresh = 124, // 0x0000007C
|
||||
|
||||
/// <summary>The Browser Stop key.</summary>
|
||||
BrowserStop = 125, // 0x0000007D
|
||||
/// <summary>The Browser Stop key.</summary>
|
||||
BrowserStop = 125, // 0x0000007D
|
||||
|
||||
/// <summary>The Browser Search key.</summary>
|
||||
BrowserSearch = 126, // 0x0000007E
|
||||
/// <summary>The Browser Search key.</summary>
|
||||
BrowserSearch = 126, // 0x0000007E
|
||||
|
||||
/// <summary>The Browser Favorites key.</summary>
|
||||
BrowserFavorites = 127, // 0x0000007F
|
||||
/// <summary>The Browser Favorites key.</summary>
|
||||
BrowserFavorites = 127, // 0x0000007F
|
||||
|
||||
/// <summary>The Browser Home key.</summary>
|
||||
BrowserHome = 128, // 0x00000080
|
||||
/// <summary>The Browser Home key.</summary>
|
||||
BrowserHome = 128, // 0x00000080
|
||||
|
||||
/// <summary>The Volume Mute key.</summary>
|
||||
VolumeMute = 129, // 0x00000081
|
||||
/// <summary>The Volume Mute key.</summary>
|
||||
VolumeMute = 129, // 0x00000081
|
||||
|
||||
/// <summary>The Volume Down key.</summary>
|
||||
VolumeDown = 130, // 0x00000082
|
||||
/// <summary>The Volume Down key.</summary>
|
||||
VolumeDown = 130, // 0x00000082
|
||||
|
||||
/// <summary>The Volume Up key.</summary>
|
||||
VolumeUp = 131, // 0x00000083
|
||||
/// <summary>The Volume Up key.</summary>
|
||||
VolumeUp = 131, // 0x00000083
|
||||
|
||||
/// <summary>The Media Next Track key.</summary>
|
||||
MediaNextTrack = 132, // 0x00000084
|
||||
/// <summary>The Media Next Track key.</summary>
|
||||
MediaNextTrack = 132, // 0x00000084
|
||||
|
||||
/// <summary>The Media Previous Track key.</summary>
|
||||
MediaPreviousTrack = 133, // 0x00000085
|
||||
/// <summary>The Media Previous Track key.</summary>
|
||||
MediaPreviousTrack = 133, // 0x00000085
|
||||
|
||||
/// <summary>The Media Stop key.</summary>
|
||||
MediaStop = 134, // 0x00000086
|
||||
/// <summary>The Media Stop key.</summary>
|
||||
MediaStop = 134, // 0x00000086
|
||||
|
||||
/// <summary>The Media Play Pause key.</summary>
|
||||
MediaPlayPause = 135, // 0x00000087
|
||||
/// <summary>The Media Play Pause key.</summary>
|
||||
MediaPlayPause = 135, // 0x00000087
|
||||
|
||||
/// <summary>The Launch Mail key.</summary>
|
||||
LaunchMail = 136, // 0x00000088
|
||||
/// <summary>The Launch Mail key.</summary>
|
||||
LaunchMail = 136, // 0x00000088
|
||||
|
||||
/// <summary>The Select Media key.</summary>
|
||||
SelectMedia = 137, // 0x00000089
|
||||
/// <summary>The Select Media key.</summary>
|
||||
SelectMedia = 137, // 0x00000089
|
||||
|
||||
/// <summary>The Launch Application1 key.</summary>
|
||||
LaunchApplication1 = 138, // 0x0000008A
|
||||
/// <summary>The Launch Application1 key.</summary>
|
||||
LaunchApplication1 = 138, // 0x0000008A
|
||||
|
||||
/// <summary>The Launch Application2 key.</summary>
|
||||
LaunchApplication2 = 139, // 0x0000008B
|
||||
/// <summary>The Launch Application2 key.</summary>
|
||||
LaunchApplication2 = 139, // 0x0000008B
|
||||
|
||||
/// <summary>The OEM Semicolon key.</summary>
|
||||
OemSemicolon = 140, // 0x0000008C
|
||||
/// <summary>The OEM Semicolon key.</summary>
|
||||
OemSemicolon = 140, // 0x0000008C
|
||||
|
||||
/// <summary>The OEM Addition key.</summary>
|
||||
OemPlus = 141, // 0x0000008D
|
||||
/// <summary>The OEM Addition key.</summary>
|
||||
OemPlus = 141, // 0x0000008D
|
||||
|
||||
/// <summary>The OEM Comma key.</summary>
|
||||
OemComma = 142, // 0x0000008E
|
||||
/// <summary>The OEM Comma key.</summary>
|
||||
OemComma = 142, // 0x0000008E
|
||||
|
||||
/// <summary>The OEM Minus key.</summary>
|
||||
OemMinus = 143, // 0x0000008F
|
||||
/// <summary>The OEM Minus key.</summary>
|
||||
OemMinus = 143, // 0x0000008F
|
||||
|
||||
/// <summary>The OEM Period key.</summary>
|
||||
OemPeriod = 144, // 0x00000091
|
||||
/// <summary>The OEM Period key.</summary>
|
||||
OemPeriod = 144, // 0x00000091
|
||||
|
||||
/// <summary>The OEM Question key.</summary>
|
||||
OemQuestion = 145, // 0x00000092
|
||||
/// <summary>The OEM Question key.</summary>
|
||||
OemQuestion = 145, // 0x00000092
|
||||
|
||||
/// <summary>The OEM Tilde key.</summary>
|
||||
OemTilde = 146, // 0x00000092
|
||||
/// <summary>The OEM Tilde key.</summary>
|
||||
OemTilde = 146, // 0x00000092
|
||||
|
||||
/// <summary>The ABNT_C1 (Brazilian) key.</summary>
|
||||
AbntC1 = 147, // 0x00000093
|
||||
/// <summary>The ABNT_C1 (Brazilian) key.</summary>
|
||||
AbntC1 = 147, // 0x00000093
|
||||
|
||||
/// <summary>The ABNT_C2 (Brazilian) key.</summary>
|
||||
AbntC2 = 148, // 0x00000095
|
||||
/// <summary>The ABNT_C2 (Brazilian) key.</summary>
|
||||
AbntC2 = 148, // 0x00000095
|
||||
|
||||
/// <summary>The OEM Open Brackets key.</summary>
|
||||
OemOpenBrackets = 149, // 0x00000096
|
||||
/// <summary>The OEM Open Brackets key.</summary>
|
||||
OemOpenBrackets = 149, // 0x00000096
|
||||
|
||||
/// <summary>The OEM Pipe key.</summary>
|
||||
OemPipe = 150, // 0x00000096
|
||||
/// <summary>The OEM Pipe key.</summary>
|
||||
OemPipe = 150, // 0x00000096
|
||||
|
||||
/// <summary>The OEM Close Brackets key.</summary>
|
||||
OemCloseBrackets = 151, // 0x00000097
|
||||
/// <summary>The OEM Close Brackets key.</summary>
|
||||
OemCloseBrackets = 151, // 0x00000097
|
||||
|
||||
/// <summary>The OEM Quotes key.</summary>
|
||||
OemQuotes = 152, // 0x00000098
|
||||
/// <summary>The OEM Quotes key.</summary>
|
||||
OemQuotes = 152, // 0x00000098
|
||||
|
||||
/// <summary>The OEM Backslash key.</summary>
|
||||
OemBackslash = 154, // 0x0000009A
|
||||
/// <summary>The OEM Backslash key.</summary>
|
||||
OemBackslash = 154, // 0x0000009A
|
||||
|
||||
/// <summary>A special key masking the real key being processed by an IME.</summary>
|
||||
ImeProcessed = 155, // 0x0000009B
|
||||
/// <summary>A special key masking the real key being processed by an IME.</summary>
|
||||
ImeProcessed = 155, // 0x0000009B
|
||||
|
||||
/// <summary>A special key masking the real key being processed as a system key.</summary>
|
||||
System = 156, // 0x0000009C
|
||||
/// <summary>A special key masking the real key being processed as a system key.</summary>
|
||||
System = 156, // 0x0000009C
|
||||
|
||||
/// <summary>The OEM ATTN key.</summary>
|
||||
OemAttn = 157, // 0x0000009D
|
||||
/// <summary>The OEM ATTN key.</summary>
|
||||
OemAttn = 157, // 0x0000009D
|
||||
|
||||
/// <summary>The OEM FINISH key.</summary>
|
||||
OemFinish = 158, // 0x0000009E
|
||||
/// <summary>The OEM FINISH key.</summary>
|
||||
OemFinish = 158, // 0x0000009E
|
||||
|
||||
/// <summary>The OEM COPY key.</summary>
|
||||
OemCopy = 159, // 0x0000009F
|
||||
/// <summary>The OEM COPY key.</summary>
|
||||
OemCopy = 159, // 0x0000009F
|
||||
|
||||
/// <summary>The OEM AUTO key.</summary>
|
||||
OemAuto = 160, // 0x000000A0
|
||||
/// <summary>The OEM AUTO key.</summary>
|
||||
OemAuto = 160, // 0x000000A0
|
||||
|
||||
/// <summary>The OEM ENLW key.</summary>
|
||||
OemEnlw = 161, // 0x000000A1
|
||||
/// <summary>The OEM ENLW key.</summary>
|
||||
OemEnlw = 161, // 0x000000A1
|
||||
|
||||
/// <summary>The OEM BACKTAB key.</summary>
|
||||
OemBackTab = 162, // 0x000000A2
|
||||
/// <summary>The OEM BACKTAB key.</summary>
|
||||
OemBackTab = 162, // 0x000000A2
|
||||
|
||||
/// <summary>The ATTN key.</summary>
|
||||
Attn = 163, // 0x000000A3
|
||||
/// <summary>The ATTN key.</summary>
|
||||
Attn = 163, // 0x000000A3
|
||||
|
||||
/// <summary>The CRSEL key.</summary>
|
||||
CrSel = 164, // 0x000000A4
|
||||
/// <summary>The CRSEL key.</summary>
|
||||
CrSel = 164, // 0x000000A4
|
||||
|
||||
/// <summary>The EXSEL key.</summary>
|
||||
ExSel = 165, // 0x000000A5
|
||||
/// <summary>The EXSEL key.</summary>
|
||||
ExSel = 165, // 0x000000A5
|
||||
|
||||
/// <summary>The ERASE EOF key.</summary>
|
||||
EraseEof = 166, // 0x000000A6
|
||||
/// <summary>The ERASE EOF key.</summary>
|
||||
EraseEof = 166, // 0x000000A6
|
||||
|
||||
/// <summary>The PLAY key.</summary>
|
||||
Play = 167, // 0x000000A7
|
||||
/// <summary>The PLAY key.</summary>
|
||||
Play = 167, // 0x000000A7
|
||||
|
||||
/// <summary>The ZOOM key.</summary>
|
||||
Zoom = 168, // 0x000000A8
|
||||
/// <summary>The ZOOM key.</summary>
|
||||
Zoom = 168, // 0x000000A8
|
||||
|
||||
/// <summary>A constant reserved for future use.</summary>
|
||||
NoName = 169, // 0x000000A9
|
||||
/// <summary>A constant reserved for future use.</summary>
|
||||
NoName = 169, // 0x000000A9
|
||||
|
||||
/// <summary>The PA1 key.</summary>
|
||||
Pa1 = 170, // 0x000000AA
|
||||
/// <summary>The PA1 key.</summary>
|
||||
Pa1 = 170, // 0x000000AA
|
||||
|
||||
/// <summary>The OEM Clear key.</summary>
|
||||
OemClear = 171, // 0x000000AB
|
||||
/// <summary>The OEM Clear key.</summary>
|
||||
OemClear = 171, // 0x000000AB
|
||||
|
||||
/// <summary>The key is used with another key to create a single combined character.</summary>
|
||||
DeadCharProcessed = 172, // 0x000000AC,
|
||||
/// <summary>The key is used with another key to create a single combined character.</summary>
|
||||
DeadCharProcessed = 172, // 0x000000AC,
|
||||
|
||||
/// <summary>The NumPad enter key</summary>
|
||||
NumPadEnter
|
||||
}
|
||||
/// <summary>The NumPad enter key</summary>
|
||||
NumPadEnter
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the set of modifier keys.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum KeyboardModifierKey
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the set of modifier keys.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum KeyboardModifierKey
|
||||
{
|
||||
/// <summary>No modifiers are pressed.</summary>
|
||||
[Description("None")]
|
||||
None = 0,
|
||||
/// <summary>No modifiers are pressed.</summary>
|
||||
[Description("None")] None = 0,
|
||||
|
||||
/// <summary>The ALT key.</summary>
|
||||
[Description("Alt")]
|
||||
Alt = 1,
|
||||
/// <summary>The ALT key.</summary>
|
||||
[Description("Alt")] Alt = 1,
|
||||
|
||||
/// <summary>The CTRL key.</summary>
|
||||
[Description("Ctrl")]
|
||||
Control = 2,
|
||||
/// <summary>The CTRL key.</summary>
|
||||
[Description("Ctrl")] Control = 2,
|
||||
|
||||
/// <summary>The SHIFT key.</summary>
|
||||
[Description("Shift")]
|
||||
Shift = 4,
|
||||
/// <summary>The SHIFT key.</summary>
|
||||
[Description("Shift")] Shift = 4,
|
||||
|
||||
/// <summary>The Windows logo key.</summary>
|
||||
[Description("Win")]
|
||||
Windows = 8
|
||||
}
|
||||
/// <summary>The Windows logo key.</summary>
|
||||
[Description("Win")] Windows = 8
|
||||
}
|
||||
@@ -1,33 +1,32 @@
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the buttons on a mouse
|
||||
/// </summary>
|
||||
public enum MouseButton
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the buttons on a mouse
|
||||
/// The left mouse button
|
||||
/// </summary>
|
||||
public enum MouseButton
|
||||
{
|
||||
/// <summary>
|
||||
/// The left mouse button
|
||||
/// </summary>
|
||||
Left,
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// The middle mouse button
|
||||
/// </summary>
|
||||
Middle,
|
||||
/// <summary>
|
||||
/// The middle mouse button
|
||||
/// </summary>
|
||||
Middle,
|
||||
|
||||
/// <summary>
|
||||
/// The right mouse button
|
||||
/// </summary>
|
||||
Right,
|
||||
/// <summary>
|
||||
/// The right mouse button
|
||||
/// </summary>
|
||||
Right,
|
||||
|
||||
/// <summary>
|
||||
/// Extra mouse button 4 (backwards)
|
||||
/// </summary>
|
||||
Button4,
|
||||
/// <summary>
|
||||
/// Extra mouse button 4 (backwards)
|
||||
/// </summary>
|
||||
Button4,
|
||||
|
||||
/// <summary>
|
||||
/// Extra mouse button 5 (forwards)
|
||||
/// </summary>
|
||||
Button5
|
||||
}
|
||||
/// <summary>
|
||||
/// Extra mouse button 5 (forwards)
|
||||
/// </summary>
|
||||
Button5
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies mouse scrolling directions
|
||||
/// </summary>
|
||||
public enum MouseScrollDirection
|
||||
{
|
||||
/// <summary>A vertical scroll direction</summary>
|
||||
Vertical,
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>A horizontal scroll direction</summary>
|
||||
Horizontal
|
||||
}
|
||||
/// <summary>
|
||||
/// Specifies mouse scrolling directions
|
||||
/// </summary>
|
||||
public enum MouseScrollDirection
|
||||
{
|
||||
/// <summary>A vertical scroll direction</summary>
|
||||
Vertical,
|
||||
|
||||
/// <summary>A horizontal scroll direction</summary>
|
||||
Horizontal
|
||||
}
|
||||
@@ -1,38 +1,37 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data for keyboard input events
|
||||
/// </summary>
|
||||
public class ArtemisKeyboardKeyEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for keyboard input events
|
||||
/// </summary>
|
||||
public class ArtemisKeyboardKeyEventArgs : EventArgs
|
||||
internal ArtemisKeyboardKeyEventArgs(ArtemisDevice? device, ArtemisLed? led, KeyboardKey key, KeyboardModifierKey modifiers)
|
||||
{
|
||||
internal ArtemisKeyboardKeyEventArgs(ArtemisDevice? device, ArtemisLed? led, KeyboardKey key, KeyboardModifierKey modifiers)
|
||||
{
|
||||
Device = device;
|
||||
Led = led;
|
||||
Key = key;
|
||||
Modifiers = modifiers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LED that corresponds to the pressed key
|
||||
/// </summary>
|
||||
public ArtemisLed? Led { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key that triggered the event
|
||||
/// </summary>
|
||||
public KeyboardKey Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the modifiers that are pressed
|
||||
/// </summary>
|
||||
public KeyboardModifierKey Modifiers { get; }
|
||||
Device = device;
|
||||
Led = led;
|
||||
Key = key;
|
||||
Modifiers = modifiers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LED that corresponds to the pressed key
|
||||
/// </summary>
|
||||
public ArtemisLed? Led { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key that triggered the event
|
||||
/// </summary>
|
||||
public KeyboardKey Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the modifiers that are pressed
|
||||
/// </summary>
|
||||
public KeyboardModifierKey Modifiers { get; }
|
||||
}
|
||||
@@ -1,18 +1,17 @@
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for keyboard input events
|
||||
/// </summary>
|
||||
public class ArtemisKeyboardKeyUpDownEventArgs : ArtemisKeyboardKeyEventArgs
|
||||
{
|
||||
internal ArtemisKeyboardKeyUpDownEventArgs(ArtemisDevice? device, ArtemisLed? led, KeyboardKey key, KeyboardModifierKey modifiers, bool isDown) : base(device, led, key, modifiers)
|
||||
{
|
||||
IsDown = isDown;
|
||||
}
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the key is being pressed down, if <see langword="false"/> the key is being released
|
||||
/// </summary>
|
||||
public bool IsDown { get; set; }
|
||||
/// <summary>
|
||||
/// Contains data for keyboard input events
|
||||
/// </summary>
|
||||
public class ArtemisKeyboardKeyUpDownEventArgs : ArtemisKeyboardKeyEventArgs
|
||||
{
|
||||
internal ArtemisKeyboardKeyUpDownEventArgs(ArtemisDevice? device, ArtemisLed? led, KeyboardKey key, KeyboardModifierKey modifiers, bool isDown) : base(device, led, key, modifiers)
|
||||
{
|
||||
IsDown = isDown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the key is being pressed down, if <see langword="false" /> the key is being released
|
||||
/// </summary>
|
||||
public bool IsDown { get; set; }
|
||||
}
|
||||
@@ -1,26 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data for keyboard input events
|
||||
/// </summary>
|
||||
public class ArtemisKeyboardToggleStatusArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for keyboard input events
|
||||
/// </summary>
|
||||
public class ArtemisKeyboardToggleStatusArgs : EventArgs
|
||||
internal ArtemisKeyboardToggleStatusArgs(KeyboardToggleStatus oldStatus, KeyboardToggleStatus newStatus)
|
||||
{
|
||||
internal ArtemisKeyboardToggleStatusArgs(KeyboardToggleStatus oldStatus, KeyboardToggleStatus newStatus)
|
||||
{
|
||||
OldStatus = oldStatus;
|
||||
NewStatus = newStatus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keyboard status before the change
|
||||
/// </summary>
|
||||
public KeyboardToggleStatus OldStatus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keyboard status after the change
|
||||
/// </summary>
|
||||
public KeyboardToggleStatus NewStatus { get; }
|
||||
OldStatus = oldStatus;
|
||||
NewStatus = newStatus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keyboard status before the change
|
||||
/// </summary>
|
||||
public KeyboardToggleStatus OldStatus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keyboard status after the change
|
||||
/// </summary>
|
||||
public KeyboardToggleStatus NewStatus { get; }
|
||||
}
|
||||
@@ -1,32 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data for mouse input events
|
||||
/// </summary>
|
||||
public class ArtemisMouseButtonEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for mouse input events
|
||||
/// </summary>
|
||||
public class ArtemisMouseButtonEventArgs : EventArgs
|
||||
internal ArtemisMouseButtonEventArgs(ArtemisDevice? device, ArtemisLed? led, MouseButton button)
|
||||
{
|
||||
internal ArtemisMouseButtonEventArgs(ArtemisDevice? device, ArtemisLed? led, MouseButton button)
|
||||
{
|
||||
Device = device;
|
||||
Led = led;
|
||||
Button = button;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LED that corresponds to the pressed key
|
||||
/// </summary>
|
||||
public ArtemisLed? Led { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the button that triggered the event
|
||||
/// </summary>
|
||||
public MouseButton Button { get; }
|
||||
Device = device;
|
||||
Led = led;
|
||||
Button = button;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LED that corresponds to the pressed key
|
||||
/// </summary>
|
||||
public ArtemisLed? Led { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the button that triggered the event
|
||||
/// </summary>
|
||||
public MouseButton Button { get; }
|
||||
}
|
||||
@@ -1,18 +1,17 @@
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for mouse input events
|
||||
/// </summary>
|
||||
public class ArtemisMouseButtonUpDownEventArgs : ArtemisMouseButtonEventArgs
|
||||
{
|
||||
internal ArtemisMouseButtonUpDownEventArgs(ArtemisDevice? device, ArtemisLed? led, MouseButton button, bool isDown) : base(device, led, button)
|
||||
{
|
||||
IsDown = isDown;
|
||||
}
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the button is being pressed down, if <see langword="false"/> the button is being released
|
||||
/// </summary>
|
||||
public bool IsDown { get; set; }
|
||||
/// <summary>
|
||||
/// Contains data for mouse input events
|
||||
/// </summary>
|
||||
public class ArtemisMouseButtonUpDownEventArgs : ArtemisMouseButtonEventArgs
|
||||
{
|
||||
internal ArtemisMouseButtonUpDownEventArgs(ArtemisDevice? device, ArtemisLed? led, MouseButton button, bool isDown) : base(device, led, button)
|
||||
{
|
||||
IsDown = isDown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the button is being pressed down, if <see langword="false" /> the button is being released
|
||||
/// </summary>
|
||||
public bool IsDown { get; set; }
|
||||
}
|
||||
@@ -1,51 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data for mouse movement events
|
||||
/// </summary>
|
||||
public class ArtemisMouseMoveEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for mouse movement events
|
||||
/// </summary>
|
||||
public class ArtemisMouseMoveEventArgs : EventArgs
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="cursorX">The X position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="cursorY">The Y position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="deltaX">The movement delta in the horizontal direction</param>
|
||||
/// <param name="deltaY">The movement delta in the vertical direction</param>
|
||||
internal ArtemisMouseMoveEventArgs(ArtemisDevice? device, int cursorX, int cursorY, int deltaX, int deltaY)
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="cursorX">The X position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="cursorY">The Y position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="deltaX">The movement delta in the horizontal direction</param>
|
||||
/// <param name="deltaY">The movement delta in the vertical direction</param>
|
||||
internal ArtemisMouseMoveEventArgs(ArtemisDevice? device, int cursorX, int cursorY, int deltaX, int deltaY)
|
||||
{
|
||||
Device = device;
|
||||
CursorX = cursorX;
|
||||
CursorY = cursorY;
|
||||
DeltaX = deltaX;
|
||||
DeltaY = deltaY;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X position of the mouse cursor (not necessarily tied to the specific device)
|
||||
/// </summary>
|
||||
public int CursorX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Y position of the mouse cursor (not necessarily tied to the specific device)
|
||||
/// </summary>
|
||||
public int CursorY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the movement delta in the horizontal direction
|
||||
/// </summary>
|
||||
public int DeltaX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the movement delta in the vertical direction
|
||||
/// </summary>
|
||||
public int DeltaY { get; }
|
||||
Device = device;
|
||||
CursorX = cursorX;
|
||||
CursorY = cursorY;
|
||||
DeltaX = deltaX;
|
||||
DeltaY = deltaY;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X position of the mouse cursor (not necessarily tied to the specific device)
|
||||
/// </summary>
|
||||
public int CursorX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Y position of the mouse cursor (not necessarily tied to the specific device)
|
||||
/// </summary>
|
||||
public int CursorY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the movement delta in the horizontal direction
|
||||
/// </summary>
|
||||
public int DeltaX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the movement delta in the vertical direction
|
||||
/// </summary>
|
||||
public int DeltaY { get; }
|
||||
}
|
||||
@@ -1,57 +1,56 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data for mouse scroll events
|
||||
/// </summary>
|
||||
public class ArtemisMouseScrollEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for mouse scroll events
|
||||
/// </summary>
|
||||
public class ArtemisMouseScrollEventArgs : EventArgs
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="direction">The direction in which was scrolled</param>
|
||||
/// <param name="delta">The scroll delta (can positive or negative)</param>
|
||||
internal ArtemisMouseScrollEventArgs(ArtemisDevice? device, MouseScrollDirection direction, int delta)
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="direction">The direction in which was scrolled</param>
|
||||
/// <param name="delta">The scroll delta (can positive or negative)</param>
|
||||
internal ArtemisMouseScrollEventArgs(ArtemisDevice? device, MouseScrollDirection direction, int delta)
|
||||
{
|
||||
Device = device;
|
||||
Direction = direction;
|
||||
Delta = delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the direction in which was scrolled
|
||||
/// </summary>
|
||||
public MouseScrollDirection Direction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scroll delta (can positive or negative)
|
||||
/// </summary>
|
||||
public int Delta { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the mouse scrolled up
|
||||
/// </summary>
|
||||
public bool IsScrollingUp => Direction == MouseScrollDirection.Vertical && Delta > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the mouse scrolled down
|
||||
/// </summary>
|
||||
public bool IsScrollingDown => Direction == MouseScrollDirection.Vertical && Delta < 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the mouse scrolled right
|
||||
/// </summary>
|
||||
public bool IsScrollingRight => Direction == MouseScrollDirection.Horizontal && Delta > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the mouse scrolled left
|
||||
/// </summary>
|
||||
public bool IsScrollingLeft => Direction == MouseScrollDirection.Horizontal && Delta < 0;
|
||||
Device = device;
|
||||
Direction = direction;
|
||||
Delta = delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the direction in which was scrolled
|
||||
/// </summary>
|
||||
public MouseScrollDirection Direction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scroll delta (can positive or negative)
|
||||
/// </summary>
|
||||
public int Delta { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the mouse scrolled up
|
||||
/// </summary>
|
||||
public bool IsScrollingUp => Direction == MouseScrollDirection.Vertical && Delta > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the mouse scrolled down
|
||||
/// </summary>
|
||||
public bool IsScrollingDown => Direction == MouseScrollDirection.Vertical && Delta < 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the mouse scrolled right
|
||||
/// </summary>
|
||||
public bool IsScrollingRight => Direction == MouseScrollDirection.Horizontal && Delta > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the mouse scrolled left
|
||||
/// </summary>
|
||||
public bool IsScrollingLeft => Direction == MouseScrollDirection.Horizontal && Delta < 0;
|
||||
}
|
||||
@@ -1,31 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data for input provider identifier events
|
||||
/// </summary>
|
||||
public class InputProviderIdentifierEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for input provider identifier events
|
||||
/// Creates a new instance of the <see cref="InputProviderIdentifierEventArgs" /> class
|
||||
/// </summary>
|
||||
public class InputProviderIdentifierEventArgs : EventArgs
|
||||
/// <param name="identifier">A value that can be used to identify this device</param>
|
||||
/// <param name="deviceType">The type of device this identifier belongs to</param>
|
||||
public InputProviderIdentifierEventArgs(object identifier, InputDeviceType deviceType)
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="InputProviderIdentifierEventArgs" /> class
|
||||
/// </summary>
|
||||
/// <param name="identifier">A value that can be used to identify this device</param>
|
||||
/// <param name="deviceType">The type of device this identifier belongs to</param>
|
||||
public InputProviderIdentifierEventArgs(object identifier, InputDeviceType deviceType)
|
||||
{
|
||||
Identifier = identifier;
|
||||
DeviceType = deviceType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that can be used to identify this device
|
||||
/// </summary>
|
||||
public object Identifier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of device this identifier belongs to
|
||||
/// </summary>
|
||||
public InputDeviceType DeviceType { get; }
|
||||
Identifier = identifier;
|
||||
DeviceType = deviceType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that can be used to identify this device
|
||||
/// </summary>
|
||||
public object Identifier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of device this identifier belongs to
|
||||
/// </summary>
|
||||
public InputDeviceType DeviceType { get; }
|
||||
}
|
||||
@@ -1,38 +1,37 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data for input provider keyboard events
|
||||
/// </summary>
|
||||
public class InputProviderKeyboardEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for input provider keyboard events
|
||||
/// Creates a new instance of the <see cref="InputProviderKeyboardEventArgs " /> class
|
||||
/// </summary>
|
||||
public class InputProviderKeyboardEventArgs : EventArgs
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="key">The key that triggered the event</param>
|
||||
/// <param name="isDown">Whether the key is pressed down</param>
|
||||
public InputProviderKeyboardEventArgs(ArtemisDevice? device, KeyboardKey key, bool isDown)
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="InputProviderKeyboardEventArgs " /> class
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="key">The key that triggered the event</param>
|
||||
/// <param name="isDown">Whether the key is pressed down</param>
|
||||
public InputProviderKeyboardEventArgs(ArtemisDevice? device, KeyboardKey key, bool isDown)
|
||||
{
|
||||
Device = device;
|
||||
Key = key;
|
||||
IsDown = isDown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key that triggered the event
|
||||
/// </summary>
|
||||
public KeyboardKey Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the key is pressed down
|
||||
/// </summary>
|
||||
public bool IsDown { get; }
|
||||
Device = device;
|
||||
Key = key;
|
||||
IsDown = isDown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key that triggered the event
|
||||
/// </summary>
|
||||
public KeyboardKey Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the key is pressed down
|
||||
/// </summary>
|
||||
public bool IsDown { get; }
|
||||
}
|
||||
@@ -1,24 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data for input provider keyboard status toggle events
|
||||
/// </summary>
|
||||
public class InputProviderKeyboardToggleEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for input provider keyboard status toggle events
|
||||
/// Creates a new instance of the <see cref="InputProviderKeyboardToggleEventArgs " /> class
|
||||
/// </summary>
|
||||
public class InputProviderKeyboardToggleEventArgs : EventArgs
|
||||
/// <param name="keyboardToggleStatus">The toggle status of the keyboard</param>
|
||||
public InputProviderKeyboardToggleEventArgs(KeyboardToggleStatus keyboardToggleStatus)
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="InputProviderKeyboardToggleEventArgs " /> class
|
||||
/// </summary>
|
||||
/// <param name="keyboardToggleStatus">The toggle status of the keyboard</param>
|
||||
public InputProviderKeyboardToggleEventArgs(KeyboardToggleStatus keyboardToggleStatus)
|
||||
{
|
||||
KeyboardToggleStatus = keyboardToggleStatus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the toggle status of the keyboard
|
||||
/// </summary>
|
||||
public KeyboardToggleStatus KeyboardToggleStatus { get; }
|
||||
KeyboardToggleStatus = keyboardToggleStatus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the toggle status of the keyboard
|
||||
/// </summary>
|
||||
public KeyboardToggleStatus KeyboardToggleStatus { get; }
|
||||
}
|
||||
@@ -1,37 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data for input provider mouse button events
|
||||
/// </summary>
|
||||
public class InputProviderMouseButtonEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for input provider mouse button events
|
||||
/// </summary>
|
||||
public class InputProviderMouseButtonEventArgs : EventArgs
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="button">The button that triggered the event</param>
|
||||
/// <param name="isDown">Whether the button is pressed down</param>
|
||||
public InputProviderMouseButtonEventArgs(ArtemisDevice? device, MouseButton button, bool isDown)
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="button">The button that triggered the event</param>
|
||||
/// <param name="isDown">Whether the button is pressed down</param>
|
||||
public InputProviderMouseButtonEventArgs(ArtemisDevice? device, MouseButton button, bool isDown)
|
||||
{
|
||||
Device = device;
|
||||
Button = button;
|
||||
IsDown = isDown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the button that triggered the event
|
||||
/// </summary>
|
||||
public MouseButton Button { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the button is pressed down
|
||||
/// </summary>
|
||||
public bool IsDown { get; }
|
||||
Device = device;
|
||||
Button = button;
|
||||
IsDown = isDown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the button that triggered the event
|
||||
/// </summary>
|
||||
public MouseButton Button { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the button is pressed down
|
||||
/// </summary>
|
||||
public bool IsDown { get; }
|
||||
}
|
||||
@@ -1,51 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data for input provider mouse movement events
|
||||
/// </summary>
|
||||
public class InputProviderMouseMoveEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for input provider mouse movement events
|
||||
/// </summary>
|
||||
public class InputProviderMouseMoveEventArgs : EventArgs
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="cursorX">The X position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="cursorY">The Y position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="deltaX">The movement delta in the horizontal direction</param>
|
||||
/// <param name="deltaY">The movement delta in the vertical direction</param>
|
||||
public InputProviderMouseMoveEventArgs(ArtemisDevice? device, int cursorX, int cursorY, int deltaX, int deltaY)
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="cursorX">The X position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="cursorY">The Y position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="deltaX">The movement delta in the horizontal direction</param>
|
||||
/// <param name="deltaY">The movement delta in the vertical direction</param>
|
||||
public InputProviderMouseMoveEventArgs(ArtemisDevice? device, int cursorX, int cursorY, int deltaX, int deltaY)
|
||||
{
|
||||
Device = device;
|
||||
CursorX = cursorX;
|
||||
CursorY = cursorY;
|
||||
DeltaX = deltaX;
|
||||
DeltaY = deltaY;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X position of the mouse cursor in pixels (not necessarily tied to the specific device)
|
||||
/// </summary>
|
||||
public int CursorX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Y position of the mouse cursor in pixels (not necessarily tied to the specific device)
|
||||
/// </summary>
|
||||
public int CursorY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the movement delta in the horizontal direction in pixels
|
||||
/// </summary>
|
||||
public int DeltaX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the movement delta in the vertical direction in pixels
|
||||
/// </summary>
|
||||
public int DeltaY { get; }
|
||||
Device = device;
|
||||
CursorX = cursorX;
|
||||
CursorY = cursorY;
|
||||
DeltaX = deltaX;
|
||||
DeltaY = deltaY;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X position of the mouse cursor in pixels (not necessarily tied to the specific device)
|
||||
/// </summary>
|
||||
public int CursorX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Y position of the mouse cursor in pixels (not necessarily tied to the specific device)
|
||||
/// </summary>
|
||||
public int CursorY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the movement delta in the horizontal direction in pixels
|
||||
/// </summary>
|
||||
public int DeltaX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the movement delta in the vertical direction in pixels
|
||||
/// </summary>
|
||||
public int DeltaY { get; }
|
||||
}
|
||||
@@ -1,37 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data for input provider mouse button events
|
||||
/// </summary>
|
||||
public class InputProviderMouseScrollEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for input provider mouse button events
|
||||
/// </summary>
|
||||
public class InputProviderMouseScrollEventArgs : EventArgs
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="direction">The direction in which was scrolled</param>
|
||||
/// <param name="delta">The scroll delta (can positive or negative)</param>
|
||||
public InputProviderMouseScrollEventArgs(ArtemisDevice? device, MouseScrollDirection direction, int delta)
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="direction">The direction in which was scrolled</param>
|
||||
/// <param name="delta">The scroll delta (can positive or negative)</param>
|
||||
public InputProviderMouseScrollEventArgs(ArtemisDevice? device, MouseScrollDirection direction, int delta)
|
||||
{
|
||||
Device = device;
|
||||
Direction = direction;
|
||||
Delta = delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the direction in which was scrolled
|
||||
/// </summary>
|
||||
public MouseScrollDirection Direction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scroll delta (can positive or negative)
|
||||
/// </summary>
|
||||
public int Delta { get; }
|
||||
Device = device;
|
||||
Direction = direction;
|
||||
Delta = delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device that triggered the event
|
||||
/// </summary>
|
||||
public ArtemisDevice? Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the direction in which was scrolled
|
||||
/// </summary>
|
||||
public MouseScrollDirection Direction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scroll delta (can positive or negative)
|
||||
/// </summary>
|
||||
public int Delta { get; }
|
||||
}
|
||||
@@ -1,203 +1,202 @@
|
||||
using System.Collections.Generic;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Utilities for mapping keys and buttons to LEDs
|
||||
/// </summary>
|
||||
public static class InputKeyUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Utilities for mapping keys and buttons to LEDs
|
||||
/// A dictionary of mappings between <see cref="KeyboardKey" /> and <see cref="LedId" />
|
||||
/// </summary>
|
||||
public static class InputKeyUtilities
|
||||
public static readonly Dictionary<KeyboardKey, LedId> KeyboardKeyLedIdMap = new()
|
||||
{
|
||||
/// <summary>
|
||||
/// A dictionary of mappings between <see cref="KeyboardKey" /> and <see cref="LedId" />
|
||||
/// </summary>
|
||||
public static readonly Dictionary<KeyboardKey, LedId> KeyboardKeyLedIdMap = new()
|
||||
{
|
||||
{KeyboardKey.None, LedId.Keyboard_Custom1},
|
||||
{KeyboardKey.Cancel, LedId.Keyboard_Custom2},
|
||||
{KeyboardKey.Back, LedId.Keyboard_Backspace},
|
||||
{KeyboardKey.Tab, LedId.Keyboard_Tab},
|
||||
{KeyboardKey.LineFeed, LedId.Keyboard_Custom3},
|
||||
{KeyboardKey.Clear, LedId.Keyboard_Custom4},
|
||||
{KeyboardKey.Enter, LedId.Keyboard_Enter},
|
||||
{KeyboardKey.Pause, LedId.Keyboard_PauseBreak},
|
||||
{KeyboardKey.CapsLock, LedId.Keyboard_CapsLock},
|
||||
{KeyboardKey.HangulMode, LedId.Keyboard_Custom4},
|
||||
{KeyboardKey.JunjaMode, LedId.Keyboard_Custom5},
|
||||
{KeyboardKey.FinalMode, LedId.Keyboard_Custom6},
|
||||
{KeyboardKey.HanjaMode, LedId.Keyboard_Custom7},
|
||||
{KeyboardKey.Escape, LedId.Keyboard_Escape},
|
||||
{KeyboardKey.ImeConvert, LedId.Keyboard_Custom8},
|
||||
{KeyboardKey.ImeNonConvert, LedId.Keyboard_Custom9},
|
||||
{KeyboardKey.ImeAccept, LedId.Keyboard_Custom10},
|
||||
{KeyboardKey.ImeModeChange, LedId.Keyboard_Custom11},
|
||||
{KeyboardKey.Space, LedId.Keyboard_Space},
|
||||
{KeyboardKey.PageUp, LedId.Keyboard_PageUp},
|
||||
{KeyboardKey.PageDown, LedId.Keyboard_PageDown},
|
||||
{KeyboardKey.End, LedId.Keyboard_End},
|
||||
{KeyboardKey.Home, LedId.Keyboard_Home},
|
||||
{KeyboardKey.Left, LedId.Keyboard_ArrowLeft},
|
||||
{KeyboardKey.Up, LedId.Keyboard_ArrowUp},
|
||||
{KeyboardKey.Right, LedId.Keyboard_ArrowRight},
|
||||
{KeyboardKey.Down, LedId.Keyboard_ArrowDown},
|
||||
{KeyboardKey.Select, LedId.Keyboard_Custom12},
|
||||
{KeyboardKey.Print, LedId.Keyboard_Custom13},
|
||||
{KeyboardKey.Execute, LedId.Keyboard_Custom14},
|
||||
{KeyboardKey.PrintScreen, LedId.Keyboard_PrintScreen},
|
||||
{KeyboardKey.Insert, LedId.Keyboard_Insert},
|
||||
{KeyboardKey.Delete, LedId.Keyboard_Delete},
|
||||
{KeyboardKey.Help, LedId.Keyboard_Custom15},
|
||||
{KeyboardKey.D0, LedId.Keyboard_0},
|
||||
{KeyboardKey.D1, LedId.Keyboard_1},
|
||||
{KeyboardKey.D2, LedId.Keyboard_2},
|
||||
{KeyboardKey.D3, LedId.Keyboard_3},
|
||||
{KeyboardKey.D4, LedId.Keyboard_4},
|
||||
{KeyboardKey.D5, LedId.Keyboard_5},
|
||||
{KeyboardKey.D6, LedId.Keyboard_6},
|
||||
{KeyboardKey.D7, LedId.Keyboard_7},
|
||||
{KeyboardKey.D8, LedId.Keyboard_8},
|
||||
{KeyboardKey.D9, LedId.Keyboard_9},
|
||||
{KeyboardKey.A, LedId.Keyboard_A},
|
||||
{KeyboardKey.B, LedId.Keyboard_B},
|
||||
{KeyboardKey.C, LedId.Keyboard_C},
|
||||
{KeyboardKey.D, LedId.Keyboard_D},
|
||||
{KeyboardKey.E, LedId.Keyboard_E},
|
||||
{KeyboardKey.F, LedId.Keyboard_F},
|
||||
{KeyboardKey.G, LedId.Keyboard_G},
|
||||
{KeyboardKey.H, LedId.Keyboard_H},
|
||||
{KeyboardKey.I, LedId.Keyboard_I},
|
||||
{KeyboardKey.J, LedId.Keyboard_J},
|
||||
{KeyboardKey.K, LedId.Keyboard_K},
|
||||
{KeyboardKey.L, LedId.Keyboard_L},
|
||||
{KeyboardKey.M, LedId.Keyboard_M},
|
||||
{KeyboardKey.N, LedId.Keyboard_N},
|
||||
{KeyboardKey.O, LedId.Keyboard_O},
|
||||
{KeyboardKey.P, LedId.Keyboard_P},
|
||||
{KeyboardKey.Q, LedId.Keyboard_Q},
|
||||
{KeyboardKey.R, LedId.Keyboard_R},
|
||||
{KeyboardKey.S, LedId.Keyboard_S},
|
||||
{KeyboardKey.T, LedId.Keyboard_T},
|
||||
{KeyboardKey.U, LedId.Keyboard_U},
|
||||
{KeyboardKey.V, LedId.Keyboard_V},
|
||||
{KeyboardKey.W, LedId.Keyboard_W},
|
||||
{KeyboardKey.X, LedId.Keyboard_X},
|
||||
{KeyboardKey.Y, LedId.Keyboard_Y},
|
||||
{KeyboardKey.Z, LedId.Keyboard_Z},
|
||||
{KeyboardKey.LWin, LedId.Keyboard_LeftGui},
|
||||
{KeyboardKey.RWin, LedId.Keyboard_RightGui},
|
||||
{KeyboardKey.Apps, LedId.Keyboard_Application},
|
||||
{KeyboardKey.Sleep, LedId.Keyboard_Custom16},
|
||||
{KeyboardKey.NumPad0, LedId.Keyboard_Num0},
|
||||
{KeyboardKey.NumPad1, LedId.Keyboard_Num1},
|
||||
{KeyboardKey.NumPad2, LedId.Keyboard_Num2},
|
||||
{KeyboardKey.NumPad3, LedId.Keyboard_Num3},
|
||||
{KeyboardKey.NumPad4, LedId.Keyboard_Num4},
|
||||
{KeyboardKey.NumPad5, LedId.Keyboard_Num5},
|
||||
{KeyboardKey.NumPad6, LedId.Keyboard_Num6},
|
||||
{KeyboardKey.NumPad7, LedId.Keyboard_Num7},
|
||||
{KeyboardKey.NumPad8, LedId.Keyboard_Num8},
|
||||
{KeyboardKey.NumPad9, LedId.Keyboard_Num9},
|
||||
{KeyboardKey.Multiply, LedId.Keyboard_NumAsterisk},
|
||||
{KeyboardKey.Add, LedId.Keyboard_NumPlus},
|
||||
{KeyboardKey.Separator, LedId.Keyboard_NumEnter}, // unverified
|
||||
{KeyboardKey.Subtract, LedId.Keyboard_NumMinus},
|
||||
{KeyboardKey.Decimal, LedId.Keyboard_NumPeriodAndDelete},
|
||||
{KeyboardKey.Divide, LedId.Keyboard_NumSlash},
|
||||
{KeyboardKey.F1, LedId.Keyboard_F1},
|
||||
{KeyboardKey.F2, LedId.Keyboard_F2},
|
||||
{KeyboardKey.F3, LedId.Keyboard_F3},
|
||||
{KeyboardKey.F4, LedId.Keyboard_F4},
|
||||
{KeyboardKey.F5, LedId.Keyboard_F5},
|
||||
{KeyboardKey.F6, LedId.Keyboard_F6},
|
||||
{KeyboardKey.F7, LedId.Keyboard_F7},
|
||||
{KeyboardKey.F8, LedId.Keyboard_F8},
|
||||
{KeyboardKey.F9, LedId.Keyboard_F9},
|
||||
{KeyboardKey.F10, LedId.Keyboard_F10},
|
||||
{KeyboardKey.F11, LedId.Keyboard_F11},
|
||||
{KeyboardKey.F12, LedId.Keyboard_F12},
|
||||
{KeyboardKey.F13, LedId.Keyboard_Custom17},
|
||||
{KeyboardKey.F14, LedId.Keyboard_Custom18},
|
||||
{KeyboardKey.F15, LedId.Keyboard_Custom19},
|
||||
{KeyboardKey.F16, LedId.Keyboard_Custom20},
|
||||
{KeyboardKey.F17, LedId.Keyboard_Custom21},
|
||||
{KeyboardKey.F18, LedId.Keyboard_Custom22},
|
||||
{KeyboardKey.F19, LedId.Keyboard_Custom23},
|
||||
{KeyboardKey.F20, LedId.Keyboard_Custom24},
|
||||
{KeyboardKey.F21, LedId.Keyboard_Custom25},
|
||||
{KeyboardKey.F22, LedId.Keyboard_Custom26},
|
||||
{KeyboardKey.F23, LedId.Keyboard_Custom27},
|
||||
{KeyboardKey.F24, LedId.Keyboard_Custom28},
|
||||
{KeyboardKey.NumLock, LedId.Keyboard_NumLock},
|
||||
{KeyboardKey.Scroll, LedId.Keyboard_ScrollLock},
|
||||
{KeyboardKey.LeftShift, LedId.Keyboard_LeftShift},
|
||||
{KeyboardKey.RightShift, LedId.Keyboard_RightShift},
|
||||
{KeyboardKey.LeftCtrl, LedId.Keyboard_LeftCtrl},
|
||||
{KeyboardKey.RightCtrl, LedId.Keyboard_RightCtrl},
|
||||
{KeyboardKey.LeftAlt, LedId.Keyboard_LeftAlt},
|
||||
{KeyboardKey.RightAlt, LedId.Keyboard_RightAlt},
|
||||
{KeyboardKey.BrowserBack, LedId.Keyboard_Custom29},
|
||||
{KeyboardKey.BrowserForward, LedId.Keyboard_Custom30},
|
||||
{KeyboardKey.BrowserRefresh, LedId.Keyboard_Custom31},
|
||||
{KeyboardKey.BrowserStop, LedId.Keyboard_Custom32},
|
||||
{KeyboardKey.BrowserSearch, LedId.Keyboard_Custom33},
|
||||
{KeyboardKey.BrowserFavorites, LedId.Keyboard_Custom34},
|
||||
{KeyboardKey.BrowserHome, LedId.Keyboard_Custom35},
|
||||
{KeyboardKey.VolumeMute, LedId.Keyboard_MediaMute},
|
||||
{KeyboardKey.VolumeDown, LedId.Keyboard_MediaVolumeDown},
|
||||
{KeyboardKey.VolumeUp, LedId.Keyboard_MediaVolumeUp},
|
||||
{KeyboardKey.MediaNextTrack, LedId.Keyboard_MediaNextTrack},
|
||||
{KeyboardKey.MediaPreviousTrack, LedId.Keyboard_MediaPreviousTrack},
|
||||
{KeyboardKey.MediaStop, LedId.Keyboard_MediaStop},
|
||||
{KeyboardKey.MediaPlayPause, LedId.Keyboard_MediaPlay},
|
||||
{KeyboardKey.LaunchMail, LedId.Keyboard_Custom36},
|
||||
{KeyboardKey.SelectMedia, LedId.Keyboard_Custom37},
|
||||
{KeyboardKey.LaunchApplication1, LedId.Keyboard_Custom38},
|
||||
{KeyboardKey.LaunchApplication2, LedId.Keyboard_Custom39},
|
||||
{KeyboardKey.OemSemicolon, LedId.Keyboard_SemicolonAndColon},
|
||||
{KeyboardKey.OemPlus, LedId.Keyboard_EqualsAndPlus},
|
||||
{KeyboardKey.OemMinus, LedId.Keyboard_MinusAndUnderscore},
|
||||
{KeyboardKey.OemComma, LedId.Keyboard_CommaAndLessThan},
|
||||
{KeyboardKey.OemPeriod, LedId.Keyboard_PeriodAndBiggerThan},
|
||||
{KeyboardKey.OemQuestion, LedId.Keyboard_SlashAndQuestionMark},
|
||||
{KeyboardKey.OemTilde, LedId.Keyboard_GraveAccentAndTilde},
|
||||
{KeyboardKey.AbntC1, LedId.Keyboard_Custom40},
|
||||
{KeyboardKey.AbntC2, LedId.Keyboard_Custom41},
|
||||
{KeyboardKey.OemOpenBrackets, LedId.Keyboard_BracketLeft},
|
||||
{KeyboardKey.OemPipe, LedId.Keyboard_Backslash},
|
||||
{KeyboardKey.OemCloseBrackets, LedId.Keyboard_BracketRight},
|
||||
{KeyboardKey.OemQuotes, LedId.Keyboard_ApostropheAndDoubleQuote},
|
||||
{KeyboardKey.OemBackslash, LedId.Keyboard_Custom42}, // unverified
|
||||
{KeyboardKey.ImeProcessed, LedId.Keyboard_Custom43},
|
||||
{KeyboardKey.System, LedId.Keyboard_Custom44},
|
||||
{KeyboardKey.OemAttn, LedId.Keyboard_Custom45},
|
||||
{KeyboardKey.OemFinish, LedId.Keyboard_Custom46},
|
||||
{KeyboardKey.OemCopy, LedId.Keyboard_Custom47},
|
||||
{KeyboardKey.OemAuto, LedId.Keyboard_Custom48},
|
||||
{KeyboardKey.OemEnlw, LedId.Keyboard_Custom49},
|
||||
{KeyboardKey.OemBackTab, LedId.Keyboard_Custom50},
|
||||
{KeyboardKey.Attn, LedId.Keyboard_Custom51},
|
||||
{KeyboardKey.CrSel, LedId.Keyboard_Custom52},
|
||||
{KeyboardKey.ExSel, LedId.Keyboard_Custom53},
|
||||
{KeyboardKey.EraseEof, LedId.Keyboard_Custom54},
|
||||
{KeyboardKey.Play, LedId.Keyboard_MediaPlay},
|
||||
{KeyboardKey.Zoom, LedId.Keyboard_Custom55},
|
||||
{KeyboardKey.NoName, LedId.Keyboard_Custom56},
|
||||
{KeyboardKey.Pa1, LedId.Keyboard_Custom57},
|
||||
{KeyboardKey.OemClear, LedId.Keyboard_Custom58},
|
||||
{KeyboardKey.DeadCharProcessed, LedId.Keyboard_Custom59},
|
||||
{KeyboardKey.NumPadEnter, LedId.Keyboard_NumEnter}
|
||||
};
|
||||
{KeyboardKey.None, LedId.Keyboard_Custom1},
|
||||
{KeyboardKey.Cancel, LedId.Keyboard_Custom2},
|
||||
{KeyboardKey.Back, LedId.Keyboard_Backspace},
|
||||
{KeyboardKey.Tab, LedId.Keyboard_Tab},
|
||||
{KeyboardKey.LineFeed, LedId.Keyboard_Custom3},
|
||||
{KeyboardKey.Clear, LedId.Keyboard_Custom4},
|
||||
{KeyboardKey.Enter, LedId.Keyboard_Enter},
|
||||
{KeyboardKey.Pause, LedId.Keyboard_PauseBreak},
|
||||
{KeyboardKey.CapsLock, LedId.Keyboard_CapsLock},
|
||||
{KeyboardKey.HangulMode, LedId.Keyboard_Custom4},
|
||||
{KeyboardKey.JunjaMode, LedId.Keyboard_Custom5},
|
||||
{KeyboardKey.FinalMode, LedId.Keyboard_Custom6},
|
||||
{KeyboardKey.HanjaMode, LedId.Keyboard_Custom7},
|
||||
{KeyboardKey.Escape, LedId.Keyboard_Escape},
|
||||
{KeyboardKey.ImeConvert, LedId.Keyboard_Custom8},
|
||||
{KeyboardKey.ImeNonConvert, LedId.Keyboard_Custom9},
|
||||
{KeyboardKey.ImeAccept, LedId.Keyboard_Custom10},
|
||||
{KeyboardKey.ImeModeChange, LedId.Keyboard_Custom11},
|
||||
{KeyboardKey.Space, LedId.Keyboard_Space},
|
||||
{KeyboardKey.PageUp, LedId.Keyboard_PageUp},
|
||||
{KeyboardKey.PageDown, LedId.Keyboard_PageDown},
|
||||
{KeyboardKey.End, LedId.Keyboard_End},
|
||||
{KeyboardKey.Home, LedId.Keyboard_Home},
|
||||
{KeyboardKey.Left, LedId.Keyboard_ArrowLeft},
|
||||
{KeyboardKey.Up, LedId.Keyboard_ArrowUp},
|
||||
{KeyboardKey.Right, LedId.Keyboard_ArrowRight},
|
||||
{KeyboardKey.Down, LedId.Keyboard_ArrowDown},
|
||||
{KeyboardKey.Select, LedId.Keyboard_Custom12},
|
||||
{KeyboardKey.Print, LedId.Keyboard_Custom13},
|
||||
{KeyboardKey.Execute, LedId.Keyboard_Custom14},
|
||||
{KeyboardKey.PrintScreen, LedId.Keyboard_PrintScreen},
|
||||
{KeyboardKey.Insert, LedId.Keyboard_Insert},
|
||||
{KeyboardKey.Delete, LedId.Keyboard_Delete},
|
||||
{KeyboardKey.Help, LedId.Keyboard_Custom15},
|
||||
{KeyboardKey.D0, LedId.Keyboard_0},
|
||||
{KeyboardKey.D1, LedId.Keyboard_1},
|
||||
{KeyboardKey.D2, LedId.Keyboard_2},
|
||||
{KeyboardKey.D3, LedId.Keyboard_3},
|
||||
{KeyboardKey.D4, LedId.Keyboard_4},
|
||||
{KeyboardKey.D5, LedId.Keyboard_5},
|
||||
{KeyboardKey.D6, LedId.Keyboard_6},
|
||||
{KeyboardKey.D7, LedId.Keyboard_7},
|
||||
{KeyboardKey.D8, LedId.Keyboard_8},
|
||||
{KeyboardKey.D9, LedId.Keyboard_9},
|
||||
{KeyboardKey.A, LedId.Keyboard_A},
|
||||
{KeyboardKey.B, LedId.Keyboard_B},
|
||||
{KeyboardKey.C, LedId.Keyboard_C},
|
||||
{KeyboardKey.D, LedId.Keyboard_D},
|
||||
{KeyboardKey.E, LedId.Keyboard_E},
|
||||
{KeyboardKey.F, LedId.Keyboard_F},
|
||||
{KeyboardKey.G, LedId.Keyboard_G},
|
||||
{KeyboardKey.H, LedId.Keyboard_H},
|
||||
{KeyboardKey.I, LedId.Keyboard_I},
|
||||
{KeyboardKey.J, LedId.Keyboard_J},
|
||||
{KeyboardKey.K, LedId.Keyboard_K},
|
||||
{KeyboardKey.L, LedId.Keyboard_L},
|
||||
{KeyboardKey.M, LedId.Keyboard_M},
|
||||
{KeyboardKey.N, LedId.Keyboard_N},
|
||||
{KeyboardKey.O, LedId.Keyboard_O},
|
||||
{KeyboardKey.P, LedId.Keyboard_P},
|
||||
{KeyboardKey.Q, LedId.Keyboard_Q},
|
||||
{KeyboardKey.R, LedId.Keyboard_R},
|
||||
{KeyboardKey.S, LedId.Keyboard_S},
|
||||
{KeyboardKey.T, LedId.Keyboard_T},
|
||||
{KeyboardKey.U, LedId.Keyboard_U},
|
||||
{KeyboardKey.V, LedId.Keyboard_V},
|
||||
{KeyboardKey.W, LedId.Keyboard_W},
|
||||
{KeyboardKey.X, LedId.Keyboard_X},
|
||||
{KeyboardKey.Y, LedId.Keyboard_Y},
|
||||
{KeyboardKey.Z, LedId.Keyboard_Z},
|
||||
{KeyboardKey.LWin, LedId.Keyboard_LeftGui},
|
||||
{KeyboardKey.RWin, LedId.Keyboard_RightGui},
|
||||
{KeyboardKey.Apps, LedId.Keyboard_Application},
|
||||
{KeyboardKey.Sleep, LedId.Keyboard_Custom16},
|
||||
{KeyboardKey.NumPad0, LedId.Keyboard_Num0},
|
||||
{KeyboardKey.NumPad1, LedId.Keyboard_Num1},
|
||||
{KeyboardKey.NumPad2, LedId.Keyboard_Num2},
|
||||
{KeyboardKey.NumPad3, LedId.Keyboard_Num3},
|
||||
{KeyboardKey.NumPad4, LedId.Keyboard_Num4},
|
||||
{KeyboardKey.NumPad5, LedId.Keyboard_Num5},
|
||||
{KeyboardKey.NumPad6, LedId.Keyboard_Num6},
|
||||
{KeyboardKey.NumPad7, LedId.Keyboard_Num7},
|
||||
{KeyboardKey.NumPad8, LedId.Keyboard_Num8},
|
||||
{KeyboardKey.NumPad9, LedId.Keyboard_Num9},
|
||||
{KeyboardKey.Multiply, LedId.Keyboard_NumAsterisk},
|
||||
{KeyboardKey.Add, LedId.Keyboard_NumPlus},
|
||||
{KeyboardKey.Separator, LedId.Keyboard_NumEnter}, // unverified
|
||||
{KeyboardKey.Subtract, LedId.Keyboard_NumMinus},
|
||||
{KeyboardKey.Decimal, LedId.Keyboard_NumPeriodAndDelete},
|
||||
{KeyboardKey.Divide, LedId.Keyboard_NumSlash},
|
||||
{KeyboardKey.F1, LedId.Keyboard_F1},
|
||||
{KeyboardKey.F2, LedId.Keyboard_F2},
|
||||
{KeyboardKey.F3, LedId.Keyboard_F3},
|
||||
{KeyboardKey.F4, LedId.Keyboard_F4},
|
||||
{KeyboardKey.F5, LedId.Keyboard_F5},
|
||||
{KeyboardKey.F6, LedId.Keyboard_F6},
|
||||
{KeyboardKey.F7, LedId.Keyboard_F7},
|
||||
{KeyboardKey.F8, LedId.Keyboard_F8},
|
||||
{KeyboardKey.F9, LedId.Keyboard_F9},
|
||||
{KeyboardKey.F10, LedId.Keyboard_F10},
|
||||
{KeyboardKey.F11, LedId.Keyboard_F11},
|
||||
{KeyboardKey.F12, LedId.Keyboard_F12},
|
||||
{KeyboardKey.F13, LedId.Keyboard_Custom17},
|
||||
{KeyboardKey.F14, LedId.Keyboard_Custom18},
|
||||
{KeyboardKey.F15, LedId.Keyboard_Custom19},
|
||||
{KeyboardKey.F16, LedId.Keyboard_Custom20},
|
||||
{KeyboardKey.F17, LedId.Keyboard_Custom21},
|
||||
{KeyboardKey.F18, LedId.Keyboard_Custom22},
|
||||
{KeyboardKey.F19, LedId.Keyboard_Custom23},
|
||||
{KeyboardKey.F20, LedId.Keyboard_Custom24},
|
||||
{KeyboardKey.F21, LedId.Keyboard_Custom25},
|
||||
{KeyboardKey.F22, LedId.Keyboard_Custom26},
|
||||
{KeyboardKey.F23, LedId.Keyboard_Custom27},
|
||||
{KeyboardKey.F24, LedId.Keyboard_Custom28},
|
||||
{KeyboardKey.NumLock, LedId.Keyboard_NumLock},
|
||||
{KeyboardKey.Scroll, LedId.Keyboard_ScrollLock},
|
||||
{KeyboardKey.LeftShift, LedId.Keyboard_LeftShift},
|
||||
{KeyboardKey.RightShift, LedId.Keyboard_RightShift},
|
||||
{KeyboardKey.LeftCtrl, LedId.Keyboard_LeftCtrl},
|
||||
{KeyboardKey.RightCtrl, LedId.Keyboard_RightCtrl},
|
||||
{KeyboardKey.LeftAlt, LedId.Keyboard_LeftAlt},
|
||||
{KeyboardKey.RightAlt, LedId.Keyboard_RightAlt},
|
||||
{KeyboardKey.BrowserBack, LedId.Keyboard_Custom29},
|
||||
{KeyboardKey.BrowserForward, LedId.Keyboard_Custom30},
|
||||
{KeyboardKey.BrowserRefresh, LedId.Keyboard_Custom31},
|
||||
{KeyboardKey.BrowserStop, LedId.Keyboard_Custom32},
|
||||
{KeyboardKey.BrowserSearch, LedId.Keyboard_Custom33},
|
||||
{KeyboardKey.BrowserFavorites, LedId.Keyboard_Custom34},
|
||||
{KeyboardKey.BrowserHome, LedId.Keyboard_Custom35},
|
||||
{KeyboardKey.VolumeMute, LedId.Keyboard_MediaMute},
|
||||
{KeyboardKey.VolumeDown, LedId.Keyboard_MediaVolumeDown},
|
||||
{KeyboardKey.VolumeUp, LedId.Keyboard_MediaVolumeUp},
|
||||
{KeyboardKey.MediaNextTrack, LedId.Keyboard_MediaNextTrack},
|
||||
{KeyboardKey.MediaPreviousTrack, LedId.Keyboard_MediaPreviousTrack},
|
||||
{KeyboardKey.MediaStop, LedId.Keyboard_MediaStop},
|
||||
{KeyboardKey.MediaPlayPause, LedId.Keyboard_MediaPlay},
|
||||
{KeyboardKey.LaunchMail, LedId.Keyboard_Custom36},
|
||||
{KeyboardKey.SelectMedia, LedId.Keyboard_Custom37},
|
||||
{KeyboardKey.LaunchApplication1, LedId.Keyboard_Custom38},
|
||||
{KeyboardKey.LaunchApplication2, LedId.Keyboard_Custom39},
|
||||
{KeyboardKey.OemSemicolon, LedId.Keyboard_SemicolonAndColon},
|
||||
{KeyboardKey.OemPlus, LedId.Keyboard_EqualsAndPlus},
|
||||
{KeyboardKey.OemMinus, LedId.Keyboard_MinusAndUnderscore},
|
||||
{KeyboardKey.OemComma, LedId.Keyboard_CommaAndLessThan},
|
||||
{KeyboardKey.OemPeriod, LedId.Keyboard_PeriodAndBiggerThan},
|
||||
{KeyboardKey.OemQuestion, LedId.Keyboard_SlashAndQuestionMark},
|
||||
{KeyboardKey.OemTilde, LedId.Keyboard_GraveAccentAndTilde},
|
||||
{KeyboardKey.AbntC1, LedId.Keyboard_Custom40},
|
||||
{KeyboardKey.AbntC2, LedId.Keyboard_Custom41},
|
||||
{KeyboardKey.OemOpenBrackets, LedId.Keyboard_BracketLeft},
|
||||
{KeyboardKey.OemPipe, LedId.Keyboard_Backslash},
|
||||
{KeyboardKey.OemCloseBrackets, LedId.Keyboard_BracketRight},
|
||||
{KeyboardKey.OemQuotes, LedId.Keyboard_ApostropheAndDoubleQuote},
|
||||
{KeyboardKey.OemBackslash, LedId.Keyboard_Custom42}, // unverified
|
||||
{KeyboardKey.ImeProcessed, LedId.Keyboard_Custom43},
|
||||
{KeyboardKey.System, LedId.Keyboard_Custom44},
|
||||
{KeyboardKey.OemAttn, LedId.Keyboard_Custom45},
|
||||
{KeyboardKey.OemFinish, LedId.Keyboard_Custom46},
|
||||
{KeyboardKey.OemCopy, LedId.Keyboard_Custom47},
|
||||
{KeyboardKey.OemAuto, LedId.Keyboard_Custom48},
|
||||
{KeyboardKey.OemEnlw, LedId.Keyboard_Custom49},
|
||||
{KeyboardKey.OemBackTab, LedId.Keyboard_Custom50},
|
||||
{KeyboardKey.Attn, LedId.Keyboard_Custom51},
|
||||
{KeyboardKey.CrSel, LedId.Keyboard_Custom52},
|
||||
{KeyboardKey.ExSel, LedId.Keyboard_Custom53},
|
||||
{KeyboardKey.EraseEof, LedId.Keyboard_Custom54},
|
||||
{KeyboardKey.Play, LedId.Keyboard_MediaPlay},
|
||||
{KeyboardKey.Zoom, LedId.Keyboard_Custom55},
|
||||
{KeyboardKey.NoName, LedId.Keyboard_Custom56},
|
||||
{KeyboardKey.Pa1, LedId.Keyboard_Custom57},
|
||||
{KeyboardKey.OemClear, LedId.Keyboard_Custom58},
|
||||
{KeyboardKey.DeadCharProcessed, LedId.Keyboard_Custom59},
|
||||
{KeyboardKey.NumPadEnter, LedId.Keyboard_NumEnter}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary of mappings between <see cref="MouseButton" /> and <see cref="LedId" />
|
||||
/// </summary>
|
||||
public static readonly Dictionary<MouseButton, LedId> MouseButtonLedIdMap = new()
|
||||
{
|
||||
{MouseButton.Left, LedId.Mouse1},
|
||||
{MouseButton.Middle, LedId.Mouse2},
|
||||
{MouseButton.Right, LedId.Mouse3},
|
||||
{MouseButton.Button4, LedId.Mouse4},
|
||||
{MouseButton.Button5, LedId.Mouse5}
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
/// A dictionary of mappings between <see cref="MouseButton" /> and <see cref="LedId" />
|
||||
/// </summary>
|
||||
public static readonly Dictionary<MouseButton, LedId> MouseButtonLedIdMap = new()
|
||||
{
|
||||
{MouseButton.Left, LedId.Mouse1},
|
||||
{MouseButton.Middle, LedId.Mouse2},
|
||||
{MouseButton.Right, LedId.Mouse3},
|
||||
{MouseButton.Button4, LedId.Mouse4},
|
||||
{MouseButton.Button5, LedId.Mouse5}
|
||||
};
|
||||
}
|
||||
@@ -1,147 +1,142 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an interface for an input provider that provides and implementation for sending and receiving device
|
||||
/// input
|
||||
/// </summary>
|
||||
public abstract class InputProvider : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an interface for an input provider that provides and implementation for sending and receiving device
|
||||
/// input
|
||||
/// Called when the input service requests a <see cref="KeyboardToggleStatusReceived" /> event
|
||||
/// </summary>
|
||||
public abstract class InputProvider : IDisposable
|
||||
public virtual void OnKeyboardToggleStatusRequested()
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the input service requests a <see cref="KeyboardToggleStatusReceived"/> event
|
||||
/// </summary>
|
||||
public virtual void OnKeyboardToggleStatusRequested()
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provided has received keyboard data
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderKeyboardEventArgs>? KeyboardDataReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provider has received new toggle data for keyboards
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderKeyboardToggleEventArgs>? KeyboardToggleStatusReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provided has received mouse button data
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderMouseButtonEventArgs>? MouseButtonDataReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provided has received mouse scroll data
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderMouseScrollEventArgs>? MouseScrollDataReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provided has received mouse move data
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderMouseMoveEventArgs>? MouseMoveDataReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provided received a device identifier
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderIdentifierEventArgs>? IdentifierReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="KeyboardDataReceived" /> event which the <see cref="IInputService" /> listens to as long as
|
||||
/// this provider is registered
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="key">The key that triggered the event</param>
|
||||
/// <param name="isDown">Whether the key is pressed down</param>
|
||||
protected virtual void OnKeyboardDataReceived(ArtemisDevice? device, KeyboardKey key, bool isDown)
|
||||
{
|
||||
KeyboardDataReceived?.Invoke(this, new InputProviderKeyboardEventArgs(device, key, isDown));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="MouseButtonDataReceived" /> event which the <see cref="IInputService" /> listens to as long
|
||||
/// as this provider is registered
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="button">The button that triggered the event</param>
|
||||
/// <param name="isDown">Whether the button is pressed down</param>
|
||||
protected virtual void OnMouseButtonDataReceived(ArtemisDevice? device, MouseButton button, bool isDown)
|
||||
{
|
||||
MouseButtonDataReceived?.Invoke(this, new InputProviderMouseButtonEventArgs(device, button, isDown));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="MouseScrollDataReceived" /> event which the <see cref="IInputService" /> listens to as long
|
||||
/// as this provider is registered
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="direction">The direction in which was scrolled</param>
|
||||
/// <param name="delta">The scroll delta (can positive or negative)</param>
|
||||
protected virtual void OnMouseScrollDataReceived(ArtemisDevice? device, MouseScrollDirection direction, int delta)
|
||||
{
|
||||
MouseScrollDataReceived?.Invoke(this, new InputProviderMouseScrollEventArgs(device, direction, delta));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="MouseMoveDataReceived" /> event which the <see cref="IInputService" /> listens to as long as
|
||||
/// this provider is registered
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="cursorX">The X position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="cursorY">The Y position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="deltaX">The movement delta in the horizontal direction</param>
|
||||
/// <param name="deltaY">The movement delta in the vertical direction</param>
|
||||
protected virtual void OnMouseMoveDataReceived(ArtemisDevice? device, int cursorX, int cursorY, int deltaX, int deltaY)
|
||||
{
|
||||
MouseMoveDataReceived?.Invoke(this, new InputProviderMouseMoveEventArgs(device, cursorX, cursorY, deltaX, deltaY));
|
||||
}
|
||||
|
||||
// TODO: Remove this as the core should handle this in GetDeviceByIdentifier
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="IdentifierReceived" /> event which the <see cref="IInputService" /> listens to as long as
|
||||
/// this provider is registered
|
||||
/// <para>Except for on mouse movement, call this whenever you have an identifier ready for the core to process</para>
|
||||
/// </summary>
|
||||
/// <param name="identifier">A value that can be used to identify this device</param>
|
||||
/// <param name="deviceType">The type of device this identifier belongs to</param>
|
||||
protected virtual void OnIdentifierReceived(object identifier, InputDeviceType deviceType)
|
||||
{
|
||||
IdentifierReceived?.Invoke(this, new InputProviderIdentifierEventArgs(identifier, deviceType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="KeyboardToggleStatusReceived" /> event which the <see cref="IInputService" /> listens to as
|
||||
/// long as
|
||||
/// this provider is registered
|
||||
/// </summary>
|
||||
/// <param name="keyboardToggleStatus">The toggle status of the keyboard</param>
|
||||
protected virtual void OnKeyboardToggleStatusReceived(KeyboardToggleStatus keyboardToggleStatus)
|
||||
{
|
||||
KeyboardToggleStatusReceived?.Invoke(this, new InputProviderKeyboardToggleEventArgs(keyboardToggleStatus));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provided has received keyboard data
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderKeyboardEventArgs>? KeyboardDataReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provider has received new toggle data for keyboards
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderKeyboardToggleEventArgs>? KeyboardToggleStatusReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provided has received mouse button data
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderMouseButtonEventArgs>? MouseButtonDataReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provided has received mouse scroll data
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderMouseScrollEventArgs>? MouseScrollDataReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provided has received mouse move data
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderMouseMoveEventArgs>? MouseMoveDataReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the input provided received a device identifier
|
||||
/// </summary>
|
||||
public event EventHandler<InputProviderIdentifierEventArgs>? IdentifierReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="KeyboardDataReceived" /> event which the <see cref="IInputService" /> listens to as long as
|
||||
/// this provider is registered
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="key">The key that triggered the event</param>
|
||||
/// <param name="isDown">Whether the key is pressed down</param>
|
||||
protected virtual void OnKeyboardDataReceived(ArtemisDevice? device, KeyboardKey key, bool isDown)
|
||||
{
|
||||
KeyboardDataReceived?.Invoke(this, new InputProviderKeyboardEventArgs(device, key, isDown));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="MouseButtonDataReceived" /> event which the <see cref="IInputService" /> listens to as long
|
||||
/// as this provider is registered
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="button">The button that triggered the event</param>
|
||||
/// <param name="isDown">Whether the button is pressed down</param>
|
||||
protected virtual void OnMouseButtonDataReceived(ArtemisDevice? device, MouseButton button, bool isDown)
|
||||
{
|
||||
MouseButtonDataReceived?.Invoke(this, new InputProviderMouseButtonEventArgs(device, button, isDown));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="MouseScrollDataReceived" /> event which the <see cref="IInputService" /> listens to as long
|
||||
/// as this provider is registered
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="direction">The direction in which was scrolled</param>
|
||||
/// <param name="delta">The scroll delta (can positive or negative)</param>
|
||||
protected virtual void OnMouseScrollDataReceived(ArtemisDevice? device, MouseScrollDirection direction, int delta)
|
||||
{
|
||||
MouseScrollDataReceived?.Invoke(this, new InputProviderMouseScrollEventArgs(device, direction, delta));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="MouseMoveDataReceived" /> event which the <see cref="IInputService" /> listens to as long as
|
||||
/// this provider is registered
|
||||
/// </summary>
|
||||
/// <param name="device">The device that triggered the event</param>
|
||||
/// <param name="cursorX">The X position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="cursorY">The Y position of the mouse cursor (not necessarily tied to the specific device)</param>
|
||||
/// <param name="deltaX">The movement delta in the horizontal direction</param>
|
||||
/// <param name="deltaY">The movement delta in the vertical direction</param>
|
||||
protected virtual void OnMouseMoveDataReceived(ArtemisDevice? device, int cursorX, int cursorY, int deltaX, int deltaY)
|
||||
{
|
||||
MouseMoveDataReceived?.Invoke(this, new InputProviderMouseMoveEventArgs(device, cursorX, cursorY, deltaX, deltaY));
|
||||
}
|
||||
|
||||
// TODO: Remove this as the core should handle this in GetDeviceByIdentifier
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="IdentifierReceived" /> event which the <see cref="IInputService" /> listens to as long as
|
||||
/// this provider is registered
|
||||
/// <para>Except for on mouse movement, call this whenever you have an identifier ready for the core to process</para>
|
||||
/// </summary>
|
||||
/// <param name="identifier">A value that can be used to identify this device</param>
|
||||
/// <param name="deviceType">The type of device this identifier belongs to</param>
|
||||
protected virtual void OnIdentifierReceived(object identifier, InputDeviceType deviceType)
|
||||
{
|
||||
IdentifierReceived?.Invoke(this, new InputProviderIdentifierEventArgs(identifier, deviceType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="KeyboardToggleStatusReceived" /> event which the <see cref="IInputService" /> listens to as
|
||||
/// long as
|
||||
/// this provider is registered
|
||||
/// </summary>
|
||||
/// <param name="keyboardToggleStatus">The toggle status of the keyboard</param>
|
||||
protected virtual void OnKeyboardToggleStatusReceived(KeyboardToggleStatus keyboardToggleStatus)
|
||||
{
|
||||
KeyboardToggleStatusReceived?.Invoke(this, new InputProviderKeyboardToggleEventArgs(keyboardToggleStatus));
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -4,434 +4,423 @@ using System.Linq;
|
||||
using RGB.NET.Core;
|
||||
using Serilog;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class InputService : IInputService
|
||||
{
|
||||
internal class InputService : IInputService
|
||||
private readonly ILogger _logger;
|
||||
private readonly IRgbService _rgbService;
|
||||
|
||||
public InputService(ILogger logger, IRgbService rgbService)
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IRgbService _rgbService;
|
||||
_logger = logger;
|
||||
_rgbService = rgbService;
|
||||
|
||||
public InputService(ILogger logger, IRgbService rgbService)
|
||||
{
|
||||
_logger = logger;
|
||||
_rgbService = rgbService;
|
||||
|
||||
BustIdentifierCache();
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
while (_inputProviders.Any())
|
||||
{
|
||||
InputProvider provider = _inputProviders.First();
|
||||
RemoveInputProvider(provider);
|
||||
provider.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Providers
|
||||
|
||||
private readonly List<InputProvider> _inputProviders = new();
|
||||
|
||||
public KeyboardToggleStatus KeyboardToggleStatus { get; private set; } = new(false, false, false);
|
||||
|
||||
public void AddInputProvider(InputProvider inputProvider)
|
||||
{
|
||||
inputProvider.IdentifierReceived += InputProviderOnIdentifierReceived;
|
||||
inputProvider.KeyboardDataReceived += InputProviderOnKeyboardDataReceived;
|
||||
inputProvider.KeyboardToggleStatusReceived += InputProviderOnKeyboardToggleStatusReceived;
|
||||
inputProvider.MouseButtonDataReceived += InputProviderOnMouseButtonDataReceived;
|
||||
inputProvider.MouseScrollDataReceived += InputProviderOnMouseScrollDataReceived;
|
||||
inputProvider.MouseMoveDataReceived += InputProviderOnMouseMoveDataReceived;
|
||||
_inputProviders.Add(inputProvider);
|
||||
|
||||
inputProvider.OnKeyboardToggleStatusRequested();
|
||||
}
|
||||
|
||||
|
||||
public void RemoveInputProvider(InputProvider inputProvider)
|
||||
{
|
||||
if (!_inputProviders.Contains(inputProvider))
|
||||
return;
|
||||
|
||||
_inputProviders.Remove(inputProvider);
|
||||
inputProvider.IdentifierReceived -= InputProviderOnIdentifierReceived;
|
||||
inputProvider.KeyboardDataReceived -= InputProviderOnKeyboardDataReceived;
|
||||
inputProvider.KeyboardToggleStatusReceived -= InputProviderOnKeyboardToggleStatusReceived;
|
||||
inputProvider.MouseButtonDataReceived -= InputProviderOnMouseButtonDataReceived;
|
||||
inputProvider.MouseScrollDataReceived -= InputProviderOnMouseScrollDataReceived;
|
||||
inputProvider.MouseMoveDataReceived -= InputProviderOnMouseMoveDataReceived;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Identification
|
||||
|
||||
private readonly Dictionary<Tuple<InputProvider, object>, ArtemisDevice> _deviceCache = new();
|
||||
private List<ArtemisDevice> _devices = new();
|
||||
private ArtemisDevice? _cachedFallbackKeyboard;
|
||||
private ArtemisDevice? _cachedFallbackMouse;
|
||||
private ArtemisDevice? _identifyingDevice;
|
||||
|
||||
public void IdentifyDevice(ArtemisDevice device)
|
||||
{
|
||||
if (device.DeviceType != RGBDeviceType.Keyboard && device.DeviceType != RGBDeviceType.Mouse)
|
||||
throw new ArtemisCoreException($"Cannot initialize input-identification for a device of type {device.DeviceType}. \r\n" +
|
||||
"Only keyboard and mouse is supported.");
|
||||
|
||||
_identifyingDevice = device;
|
||||
_logger.Debug("Start identifying device {device}", device);
|
||||
}
|
||||
|
||||
public void StopIdentify()
|
||||
{
|
||||
if (_identifyingDevice == null)
|
||||
return;
|
||||
|
||||
_logger.Debug("Stop identifying device {device}", _identifyingDevice);
|
||||
_identifyingDevice = null;
|
||||
_rgbService.SaveDevices();
|
||||
|
||||
BustIdentifierCache();
|
||||
}
|
||||
|
||||
// TODO: Move the OnIdentifierReceived logic into here and get rid of OnIdentifierReceived, this and OnIdentifierReceived are always called in combination with each other
|
||||
public ArtemisDevice? GetDeviceByIdentifier(InputProvider provider, object identifier, InputDeviceType type)
|
||||
{
|
||||
if (provider == null) throw new ArgumentNullException(nameof(provider));
|
||||
if (identifier == null) throw new ArgumentNullException(nameof(identifier));
|
||||
|
||||
// Try cache first
|
||||
ArtemisDevice? cacheMatch = GetDeviceFromCache(provider, identifier);
|
||||
if (cacheMatch != null)
|
||||
return cacheMatch;
|
||||
|
||||
string providerName = provider.GetType().FullName!;
|
||||
ArtemisDevice? match = _devices.FirstOrDefault(m => m.InputIdentifiers.Any(i => Equals(i.InputProvider, providerName) && Equals(i.Identifier, identifier)));
|
||||
|
||||
// If a match was found cache it to speed up the next event and return the match
|
||||
if (match != null)
|
||||
{
|
||||
AddDeviceToCache(match, provider, identifier);
|
||||
return match;
|
||||
}
|
||||
|
||||
// If there is no match, apply our fallback type
|
||||
if (type == InputDeviceType.None)
|
||||
return null;
|
||||
if (type == InputDeviceType.Keyboard)
|
||||
{
|
||||
if (_cachedFallbackKeyboard != null)
|
||||
return _cachedFallbackKeyboard;
|
||||
_cachedFallbackKeyboard = _rgbService.EnabledDevices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Keyboard);
|
||||
return _cachedFallbackKeyboard;
|
||||
}
|
||||
|
||||
if (type == InputDeviceType.Mouse)
|
||||
{
|
||||
if (_cachedFallbackMouse != null)
|
||||
return _cachedFallbackMouse;
|
||||
_cachedFallbackMouse = _rgbService.EnabledDevices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Mouse);
|
||||
return _cachedFallbackMouse;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void BustIdentifierCache()
|
||||
{
|
||||
_deviceCache.Clear();
|
||||
_cachedFallbackKeyboard = null;
|
||||
_cachedFallbackMouse = null;
|
||||
|
||||
_devices = _rgbService.EnabledDevices.Where(d => d.InputIdentifiers.Any()).ToList();
|
||||
}
|
||||
|
||||
private void AddDeviceToCache(ArtemisDevice match, InputProvider provider, object identifier)
|
||||
{
|
||||
_deviceCache.TryAdd(new Tuple<InputProvider, object>(provider, identifier), match);
|
||||
}
|
||||
|
||||
private ArtemisDevice? GetDeviceFromCache(InputProvider provider, object identifier)
|
||||
{
|
||||
_deviceCache.TryGetValue(new Tuple<InputProvider, object>(provider, identifier), out ArtemisDevice? device);
|
||||
return device;
|
||||
}
|
||||
|
||||
private void SurfaceConfigurationChanged(object? sender, SurfaceConfigurationEventArgs e)
|
||||
{
|
||||
BustIdentifierCache();
|
||||
}
|
||||
|
||||
private void InputProviderOnIdentifierReceived(object? sender, InputProviderIdentifierEventArgs e)
|
||||
{
|
||||
// Don't match if there is no device or if the device type differs from the event device type
|
||||
if (_identifyingDevice == null ||
|
||||
_identifyingDevice.DeviceType == RGBDeviceType.Keyboard && e.DeviceType == InputDeviceType.Mouse ||
|
||||
_identifyingDevice.DeviceType == RGBDeviceType.Mouse && e.DeviceType == InputDeviceType.Keyboard)
|
||||
return;
|
||||
if (!(sender is InputProvider inputProvider))
|
||||
return;
|
||||
|
||||
string providerName = inputProvider.GetType().FullName!;
|
||||
|
||||
// Remove existing identification
|
||||
_identifyingDevice.InputIdentifiers.RemoveAll(i => i.InputProvider == providerName);
|
||||
_identifyingDevice.InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(providerName, e.Identifier));
|
||||
|
||||
StopIdentify();
|
||||
OnDeviceIdentified();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Keyboard
|
||||
|
||||
private readonly Dictionary<ArtemisDevice, HashSet<KeyboardKey>> _pressedKeys = new();
|
||||
private readonly Dictionary<ArtemisDevice, KeyboardModifierKey> _keyboardModifier = new();
|
||||
private KeyboardModifierKey _globalModifiers;
|
||||
|
||||
private void InputProviderOnKeyboardDataReceived(object? sender, InputProviderKeyboardEventArgs e)
|
||||
{
|
||||
KeyboardModifierKey keyboardModifierKey = UpdateModifierKeys(e.Device, e.Key, e.IsDown);
|
||||
|
||||
// if UpdatePressedKeys is true, the key is already pressed, then we skip this event
|
||||
if (UpdatePressedKeys(e.Device, e.Key, e.IsDown))
|
||||
return;
|
||||
|
||||
// Get the LED
|
||||
bool foundLedId = InputKeyUtilities.KeyboardKeyLedIdMap.TryGetValue(e.Key, out LedId ledId);
|
||||
ArtemisLed? led = null;
|
||||
if (foundLedId && e.Device != null)
|
||||
led = e.Device.GetLed(ledId, true);
|
||||
|
||||
// Create the UpDown event args because it can be used for every event
|
||||
ArtemisKeyboardKeyUpDownEventArgs eventArgs = new(e.Device, led, e.Key, keyboardModifierKey, e.IsDown);
|
||||
OnKeyboardKeyUpDown(eventArgs);
|
||||
if (e.IsDown)
|
||||
OnKeyboardKeyDown(eventArgs);
|
||||
else
|
||||
OnKeyboardKeyUp(eventArgs);
|
||||
|
||||
// _logger.Verbose("Keyboard data: LED ID: {ledId}, key: {key}, is down: {isDown}, modifiers: {modifiers}, device: {device} ", ledId, e.Key, e.IsDown, keyboardModifierKey, e.Device);
|
||||
}
|
||||
|
||||
private void InputProviderOnKeyboardToggleStatusReceived(object? sender, InputProviderKeyboardToggleEventArgs e)
|
||||
{
|
||||
KeyboardToggleStatus old = KeyboardToggleStatus;
|
||||
if (KeyboardToggleStatus.CapsLock == e.KeyboardToggleStatus.CapsLock &&
|
||||
KeyboardToggleStatus.NumLock == e.KeyboardToggleStatus.NumLock &&
|
||||
KeyboardToggleStatus.ScrollLock == e.KeyboardToggleStatus.ScrollLock)
|
||||
return;
|
||||
|
||||
KeyboardToggleStatus = e.KeyboardToggleStatus;
|
||||
OnKeyboardToggleStatusChanged(new ArtemisKeyboardToggleStatusArgs(old, KeyboardToggleStatus));
|
||||
}
|
||||
|
||||
private bool UpdatePressedKeys(ArtemisDevice? device, KeyboardKey key, bool isDown)
|
||||
{
|
||||
if (device != null)
|
||||
{
|
||||
// Ensure the device is in the dictionary
|
||||
_pressedKeys.TryAdd(device, new HashSet<KeyboardKey>());
|
||||
// Get the hash set of the device
|
||||
HashSet<KeyboardKey> pressedDeviceKeys = _pressedKeys[device];
|
||||
// See if the key is already pressed
|
||||
bool alreadyPressed = pressedDeviceKeys.Contains(key);
|
||||
|
||||
// Prevent triggering already down keys again. When a key is held, we don't want to spam events like Windows does
|
||||
if (isDown && alreadyPressed)
|
||||
return true;
|
||||
|
||||
// Either add or remove the key depending on its status
|
||||
if (isDown && !alreadyPressed)
|
||||
pressedDeviceKeys.Add(key);
|
||||
else if (!isDown && alreadyPressed)
|
||||
pressedDeviceKeys.Remove(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private KeyboardModifierKey UpdateModifierKeys(ArtemisDevice? device, KeyboardKey key, in bool isDown)
|
||||
{
|
||||
KeyboardModifierKey modifiers = _globalModifiers;
|
||||
if (device != null)
|
||||
_keyboardModifier.TryGetValue(device, out modifiers);
|
||||
|
||||
if (key == KeyboardKey.LeftAlt || key == KeyboardKey.RightAlt)
|
||||
{
|
||||
if (isDown)
|
||||
modifiers |= KeyboardModifierKey.Alt;
|
||||
else
|
||||
modifiers &= ~KeyboardModifierKey.Alt;
|
||||
}
|
||||
else if (key == KeyboardKey.LeftCtrl || key == KeyboardKey.RightCtrl)
|
||||
{
|
||||
if (isDown)
|
||||
modifiers |= KeyboardModifierKey.Control;
|
||||
else
|
||||
modifiers &= ~KeyboardModifierKey.Control;
|
||||
}
|
||||
else if (key == KeyboardKey.LeftShift || key == KeyboardKey.RightShift)
|
||||
{
|
||||
if (isDown)
|
||||
modifiers |= KeyboardModifierKey.Shift;
|
||||
else
|
||||
modifiers &= ~KeyboardModifierKey.Shift;
|
||||
}
|
||||
else if (key == KeyboardKey.LWin || key == KeyboardKey.RWin)
|
||||
{
|
||||
if (isDown)
|
||||
modifiers |= KeyboardModifierKey.Windows;
|
||||
else
|
||||
modifiers &= ~KeyboardModifierKey.Windows;
|
||||
}
|
||||
|
||||
if (device != null)
|
||||
_keyboardModifier[device] = modifiers;
|
||||
else
|
||||
_globalModifiers = modifiers;
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
public void ReleaseAll()
|
||||
{
|
||||
foreach ((ArtemisDevice? device, HashSet<KeyboardKey>? keys) in _pressedKeys.ToList())
|
||||
{
|
||||
foreach (KeyboardKey keyboardKey in keys)
|
||||
{
|
||||
InputProviderOnKeyboardDataReceived(this, new InputProviderKeyboardEventArgs(device, keyboardKey, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsKeyDown(KeyboardKey key)
|
||||
{
|
||||
return _pressedKeys.Any(p => p.Value.Contains(key));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mouse
|
||||
|
||||
private readonly HashSet<MouseButton> _pressedButtons = new();
|
||||
|
||||
private void InputProviderOnMouseButtonDataReceived(object? sender, InputProviderMouseButtonEventArgs e)
|
||||
{
|
||||
bool foundLedId = InputKeyUtilities.MouseButtonLedIdMap.TryGetValue(e.Button, out LedId ledId);
|
||||
ArtemisLed? led = null;
|
||||
if (foundLedId && e.Device != null)
|
||||
led = e.Device.Leds.FirstOrDefault(l => l.RgbLed.Id == ledId);
|
||||
|
||||
// Create the UpDown event args because it can be used for every event
|
||||
ArtemisMouseButtonUpDownEventArgs eventArgs = new(e.Device, led, e.Button, e.IsDown);
|
||||
OnMouseButtonUpDown(eventArgs);
|
||||
if (e.IsDown)
|
||||
{
|
||||
if (!_pressedButtons.Contains(e.Button))
|
||||
_pressedButtons.Add(e.Button);
|
||||
OnMouseButtonDown(eventArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_pressedButtons.Contains(e.Button))
|
||||
_pressedButtons.Remove(e.Button);
|
||||
OnMouseButtonUp(eventArgs);
|
||||
}
|
||||
|
||||
// _logger.Verbose("Mouse button data: LED ID: {ledId}, button: {button}, is down: {isDown}, device: {device} ", ledId, e.Button, e.IsDown, e.Device);
|
||||
}
|
||||
|
||||
private void InputProviderOnMouseScrollDataReceived(object? sender, InputProviderMouseScrollEventArgs e)
|
||||
{
|
||||
OnMouseScroll(new ArtemisMouseScrollEventArgs(e.Device, e.Direction, e.Delta));
|
||||
// _logger.Verbose("Mouse scroll data: Direction: {direction}, delta: {delta}, device: {device} ", e.Direction, e.Delta, e.Device);
|
||||
}
|
||||
|
||||
private void InputProviderOnMouseMoveDataReceived(object? sender, InputProviderMouseMoveEventArgs e)
|
||||
{
|
||||
OnMouseMove(new ArtemisMouseMoveEventArgs(e.Device, e.CursorX, e.CursorY, e.DeltaX, e.DeltaY));
|
||||
// _logger.Verbose("Mouse move data: XY: {X},{Y} - delta XY: {deltaX},{deltaY} - device: {device} ", e.CursorX, e.CursorY, e.DeltaX, e.DeltaY, e.Device);
|
||||
}
|
||||
|
||||
public bool IsButtonDown(MouseButton button)
|
||||
{
|
||||
return _pressedButtons.Contains(button);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler<ArtemisKeyboardKeyUpDownEventArgs>? KeyboardKeyUpDown;
|
||||
public event EventHandler<ArtemisKeyboardKeyEventArgs>? KeyboardKeyDown;
|
||||
public event EventHandler<ArtemisKeyboardKeyEventArgs>? KeyboardKeyUp;
|
||||
public event EventHandler<ArtemisKeyboardToggleStatusArgs>? KeyboardToggleStatusChanged;
|
||||
|
||||
public event EventHandler<ArtemisMouseButtonUpDownEventArgs>? MouseButtonUpDown;
|
||||
public event EventHandler<ArtemisMouseButtonEventArgs>? MouseButtonDown;
|
||||
public event EventHandler<ArtemisMouseButtonEventArgs>? MouseButtonUp;
|
||||
public event EventHandler<ArtemisMouseScrollEventArgs>? MouseScroll;
|
||||
public event EventHandler<ArtemisMouseMoveEventArgs>? MouseMove;
|
||||
public event EventHandler? DeviceIdentified;
|
||||
|
||||
protected virtual void OnKeyboardKeyUpDown(ArtemisKeyboardKeyUpDownEventArgs e)
|
||||
{
|
||||
KeyboardKeyUpDown?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnKeyboardKeyDown(ArtemisKeyboardKeyEventArgs e)
|
||||
{
|
||||
KeyboardKeyDown?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnKeyboardKeyUp(ArtemisKeyboardKeyEventArgs e)
|
||||
{
|
||||
KeyboardKeyUp?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnKeyboardToggleStatusChanged(ArtemisKeyboardToggleStatusArgs e)
|
||||
{
|
||||
KeyboardToggleStatusChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnMouseButtonUpDown(ArtemisMouseButtonUpDownEventArgs e)
|
||||
{
|
||||
MouseButtonUpDown?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnMouseButtonDown(ArtemisMouseButtonEventArgs e)
|
||||
{
|
||||
MouseButtonDown?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnMouseButtonUp(ArtemisMouseButtonEventArgs e)
|
||||
{
|
||||
MouseButtonUp?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnMouseScroll(ArtemisMouseScrollEventArgs e)
|
||||
{
|
||||
MouseScroll?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnMouseMove(ArtemisMouseMoveEventArgs e)
|
||||
{
|
||||
MouseMove?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnDeviceIdentified()
|
||||
{
|
||||
DeviceIdentified?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
BustIdentifierCache();
|
||||
}
|
||||
|
||||
protected virtual void OnKeyboardKeyUpDown(ArtemisKeyboardKeyUpDownEventArgs e)
|
||||
{
|
||||
KeyboardKeyUpDown?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnKeyboardKeyDown(ArtemisKeyboardKeyEventArgs e)
|
||||
{
|
||||
KeyboardKeyDown?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnKeyboardKeyUp(ArtemisKeyboardKeyEventArgs e)
|
||||
{
|
||||
KeyboardKeyUp?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnKeyboardToggleStatusChanged(ArtemisKeyboardToggleStatusArgs e)
|
||||
{
|
||||
KeyboardToggleStatusChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnMouseButtonUpDown(ArtemisMouseButtonUpDownEventArgs e)
|
||||
{
|
||||
MouseButtonUpDown?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnMouseButtonDown(ArtemisMouseButtonEventArgs e)
|
||||
{
|
||||
MouseButtonDown?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnMouseButtonUp(ArtemisMouseButtonEventArgs e)
|
||||
{
|
||||
MouseButtonUp?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnMouseScroll(ArtemisMouseScrollEventArgs e)
|
||||
{
|
||||
MouseScroll?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnMouseMove(ArtemisMouseMoveEventArgs e)
|
||||
{
|
||||
MouseMove?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnDeviceIdentified()
|
||||
{
|
||||
DeviceIdentified?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
while (_inputProviders.Any())
|
||||
{
|
||||
InputProvider provider = _inputProviders.First();
|
||||
RemoveInputProvider(provider);
|
||||
provider.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<ArtemisKeyboardKeyUpDownEventArgs>? KeyboardKeyUpDown;
|
||||
public event EventHandler<ArtemisKeyboardKeyEventArgs>? KeyboardKeyDown;
|
||||
public event EventHandler<ArtemisKeyboardKeyEventArgs>? KeyboardKeyUp;
|
||||
public event EventHandler<ArtemisKeyboardToggleStatusArgs>? KeyboardToggleStatusChanged;
|
||||
|
||||
public event EventHandler<ArtemisMouseButtonUpDownEventArgs>? MouseButtonUpDown;
|
||||
public event EventHandler<ArtemisMouseButtonEventArgs>? MouseButtonDown;
|
||||
public event EventHandler<ArtemisMouseButtonEventArgs>? MouseButtonUp;
|
||||
public event EventHandler<ArtemisMouseScrollEventArgs>? MouseScroll;
|
||||
public event EventHandler<ArtemisMouseMoveEventArgs>? MouseMove;
|
||||
public event EventHandler? DeviceIdentified;
|
||||
|
||||
#region Providers
|
||||
|
||||
private readonly List<InputProvider> _inputProviders = new();
|
||||
|
||||
public KeyboardToggleStatus KeyboardToggleStatus { get; private set; } = new(false, false, false);
|
||||
|
||||
public void AddInputProvider(InputProvider inputProvider)
|
||||
{
|
||||
inputProvider.IdentifierReceived += InputProviderOnIdentifierReceived;
|
||||
inputProvider.KeyboardDataReceived += InputProviderOnKeyboardDataReceived;
|
||||
inputProvider.KeyboardToggleStatusReceived += InputProviderOnKeyboardToggleStatusReceived;
|
||||
inputProvider.MouseButtonDataReceived += InputProviderOnMouseButtonDataReceived;
|
||||
inputProvider.MouseScrollDataReceived += InputProviderOnMouseScrollDataReceived;
|
||||
inputProvider.MouseMoveDataReceived += InputProviderOnMouseMoveDataReceived;
|
||||
_inputProviders.Add(inputProvider);
|
||||
|
||||
inputProvider.OnKeyboardToggleStatusRequested();
|
||||
}
|
||||
|
||||
|
||||
public void RemoveInputProvider(InputProvider inputProvider)
|
||||
{
|
||||
if (!_inputProviders.Contains(inputProvider))
|
||||
return;
|
||||
|
||||
_inputProviders.Remove(inputProvider);
|
||||
inputProvider.IdentifierReceived -= InputProviderOnIdentifierReceived;
|
||||
inputProvider.KeyboardDataReceived -= InputProviderOnKeyboardDataReceived;
|
||||
inputProvider.KeyboardToggleStatusReceived -= InputProviderOnKeyboardToggleStatusReceived;
|
||||
inputProvider.MouseButtonDataReceived -= InputProviderOnMouseButtonDataReceived;
|
||||
inputProvider.MouseScrollDataReceived -= InputProviderOnMouseScrollDataReceived;
|
||||
inputProvider.MouseMoveDataReceived -= InputProviderOnMouseMoveDataReceived;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Identification
|
||||
|
||||
private readonly Dictionary<Tuple<InputProvider, object>, ArtemisDevice> _deviceCache = new();
|
||||
private List<ArtemisDevice> _devices = new();
|
||||
private ArtemisDevice? _cachedFallbackKeyboard;
|
||||
private ArtemisDevice? _cachedFallbackMouse;
|
||||
private ArtemisDevice? _identifyingDevice;
|
||||
|
||||
public void IdentifyDevice(ArtemisDevice device)
|
||||
{
|
||||
if (device.DeviceType != RGBDeviceType.Keyboard && device.DeviceType != RGBDeviceType.Mouse)
|
||||
throw new ArtemisCoreException($"Cannot initialize input-identification for a device of type {device.DeviceType}. \r\n" +
|
||||
"Only keyboard and mouse is supported.");
|
||||
|
||||
_identifyingDevice = device;
|
||||
_logger.Debug("Start identifying device {device}", device);
|
||||
}
|
||||
|
||||
public void StopIdentify()
|
||||
{
|
||||
if (_identifyingDevice == null)
|
||||
return;
|
||||
|
||||
_logger.Debug("Stop identifying device {device}", _identifyingDevice);
|
||||
_identifyingDevice = null;
|
||||
_rgbService.SaveDevices();
|
||||
|
||||
BustIdentifierCache();
|
||||
}
|
||||
|
||||
// TODO: Move the OnIdentifierReceived logic into here and get rid of OnIdentifierReceived, this and OnIdentifierReceived are always called in combination with each other
|
||||
public ArtemisDevice? GetDeviceByIdentifier(InputProvider provider, object identifier, InputDeviceType type)
|
||||
{
|
||||
if (provider == null) throw new ArgumentNullException(nameof(provider));
|
||||
if (identifier == null) throw new ArgumentNullException(nameof(identifier));
|
||||
|
||||
// Try cache first
|
||||
ArtemisDevice? cacheMatch = GetDeviceFromCache(provider, identifier);
|
||||
if (cacheMatch != null)
|
||||
return cacheMatch;
|
||||
|
||||
string providerName = provider.GetType().FullName!;
|
||||
ArtemisDevice? match = _devices.FirstOrDefault(m => m.InputIdentifiers.Any(i => Equals(i.InputProvider, providerName) && Equals(i.Identifier, identifier)));
|
||||
|
||||
// If a match was found cache it to speed up the next event and return the match
|
||||
if (match != null)
|
||||
{
|
||||
AddDeviceToCache(match, provider, identifier);
|
||||
return match;
|
||||
}
|
||||
|
||||
// If there is no match, apply our fallback type
|
||||
if (type == InputDeviceType.None)
|
||||
return null;
|
||||
if (type == InputDeviceType.Keyboard)
|
||||
{
|
||||
if (_cachedFallbackKeyboard != null)
|
||||
return _cachedFallbackKeyboard;
|
||||
_cachedFallbackKeyboard = _rgbService.EnabledDevices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Keyboard);
|
||||
return _cachedFallbackKeyboard;
|
||||
}
|
||||
|
||||
if (type == InputDeviceType.Mouse)
|
||||
{
|
||||
if (_cachedFallbackMouse != null)
|
||||
return _cachedFallbackMouse;
|
||||
_cachedFallbackMouse = _rgbService.EnabledDevices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Mouse);
|
||||
return _cachedFallbackMouse;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void BustIdentifierCache()
|
||||
{
|
||||
_deviceCache.Clear();
|
||||
_cachedFallbackKeyboard = null;
|
||||
_cachedFallbackMouse = null;
|
||||
|
||||
_devices = _rgbService.EnabledDevices.Where(d => d.InputIdentifiers.Any()).ToList();
|
||||
}
|
||||
|
||||
private void AddDeviceToCache(ArtemisDevice match, InputProvider provider, object identifier)
|
||||
{
|
||||
_deviceCache.TryAdd(new Tuple<InputProvider, object>(provider, identifier), match);
|
||||
}
|
||||
|
||||
private ArtemisDevice? GetDeviceFromCache(InputProvider provider, object identifier)
|
||||
{
|
||||
_deviceCache.TryGetValue(new Tuple<InputProvider, object>(provider, identifier), out ArtemisDevice? device);
|
||||
return device;
|
||||
}
|
||||
|
||||
private void SurfaceConfigurationChanged(object? sender, SurfaceConfigurationEventArgs e)
|
||||
{
|
||||
BustIdentifierCache();
|
||||
}
|
||||
|
||||
private void InputProviderOnIdentifierReceived(object? sender, InputProviderIdentifierEventArgs e)
|
||||
{
|
||||
// Don't match if there is no device or if the device type differs from the event device type
|
||||
if (_identifyingDevice == null ||
|
||||
(_identifyingDevice.DeviceType == RGBDeviceType.Keyboard && e.DeviceType == InputDeviceType.Mouse) ||
|
||||
(_identifyingDevice.DeviceType == RGBDeviceType.Mouse && e.DeviceType == InputDeviceType.Keyboard))
|
||||
return;
|
||||
if (!(sender is InputProvider inputProvider))
|
||||
return;
|
||||
|
||||
string providerName = inputProvider.GetType().FullName!;
|
||||
|
||||
// Remove existing identification
|
||||
_identifyingDevice.InputIdentifiers.RemoveAll(i => i.InputProvider == providerName);
|
||||
_identifyingDevice.InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(providerName, e.Identifier));
|
||||
|
||||
StopIdentify();
|
||||
OnDeviceIdentified();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Keyboard
|
||||
|
||||
private readonly Dictionary<ArtemisDevice, HashSet<KeyboardKey>> _pressedKeys = new();
|
||||
private readonly Dictionary<ArtemisDevice, KeyboardModifierKey> _keyboardModifier = new();
|
||||
private KeyboardModifierKey _globalModifiers;
|
||||
|
||||
private void InputProviderOnKeyboardDataReceived(object? sender, InputProviderKeyboardEventArgs e)
|
||||
{
|
||||
KeyboardModifierKey keyboardModifierKey = UpdateModifierKeys(e.Device, e.Key, e.IsDown);
|
||||
|
||||
// if UpdatePressedKeys is true, the key is already pressed, then we skip this event
|
||||
if (UpdatePressedKeys(e.Device, e.Key, e.IsDown))
|
||||
return;
|
||||
|
||||
// Get the LED
|
||||
bool foundLedId = InputKeyUtilities.KeyboardKeyLedIdMap.TryGetValue(e.Key, out LedId ledId);
|
||||
ArtemisLed? led = null;
|
||||
if (foundLedId && e.Device != null)
|
||||
led = e.Device.GetLed(ledId, true);
|
||||
|
||||
// Create the UpDown event args because it can be used for every event
|
||||
ArtemisKeyboardKeyUpDownEventArgs eventArgs = new(e.Device, led, e.Key, keyboardModifierKey, e.IsDown);
|
||||
OnKeyboardKeyUpDown(eventArgs);
|
||||
if (e.IsDown)
|
||||
OnKeyboardKeyDown(eventArgs);
|
||||
else
|
||||
OnKeyboardKeyUp(eventArgs);
|
||||
|
||||
// _logger.Verbose("Keyboard data: LED ID: {ledId}, key: {key}, is down: {isDown}, modifiers: {modifiers}, device: {device} ", ledId, e.Key, e.IsDown, keyboardModifierKey, e.Device);
|
||||
}
|
||||
|
||||
private void InputProviderOnKeyboardToggleStatusReceived(object? sender, InputProviderKeyboardToggleEventArgs e)
|
||||
{
|
||||
KeyboardToggleStatus old = KeyboardToggleStatus;
|
||||
if (KeyboardToggleStatus.CapsLock == e.KeyboardToggleStatus.CapsLock &&
|
||||
KeyboardToggleStatus.NumLock == e.KeyboardToggleStatus.NumLock &&
|
||||
KeyboardToggleStatus.ScrollLock == e.KeyboardToggleStatus.ScrollLock)
|
||||
return;
|
||||
|
||||
KeyboardToggleStatus = e.KeyboardToggleStatus;
|
||||
OnKeyboardToggleStatusChanged(new ArtemisKeyboardToggleStatusArgs(old, KeyboardToggleStatus));
|
||||
}
|
||||
|
||||
private bool UpdatePressedKeys(ArtemisDevice? device, KeyboardKey key, bool isDown)
|
||||
{
|
||||
if (device != null)
|
||||
{
|
||||
// Ensure the device is in the dictionary
|
||||
_pressedKeys.TryAdd(device, new HashSet<KeyboardKey>());
|
||||
// Get the hash set of the device
|
||||
HashSet<KeyboardKey> pressedDeviceKeys = _pressedKeys[device];
|
||||
// See if the key is already pressed
|
||||
bool alreadyPressed = pressedDeviceKeys.Contains(key);
|
||||
|
||||
// Prevent triggering already down keys again. When a key is held, we don't want to spam events like Windows does
|
||||
if (isDown && alreadyPressed)
|
||||
return true;
|
||||
|
||||
// Either add or remove the key depending on its status
|
||||
if (isDown && !alreadyPressed)
|
||||
pressedDeviceKeys.Add(key);
|
||||
else if (!isDown && alreadyPressed)
|
||||
pressedDeviceKeys.Remove(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private KeyboardModifierKey UpdateModifierKeys(ArtemisDevice? device, KeyboardKey key, in bool isDown)
|
||||
{
|
||||
KeyboardModifierKey modifiers = _globalModifiers;
|
||||
if (device != null)
|
||||
_keyboardModifier.TryGetValue(device, out modifiers);
|
||||
|
||||
if (key == KeyboardKey.LeftAlt || key == KeyboardKey.RightAlt)
|
||||
{
|
||||
if (isDown)
|
||||
modifiers |= KeyboardModifierKey.Alt;
|
||||
else
|
||||
modifiers &= ~KeyboardModifierKey.Alt;
|
||||
}
|
||||
else if (key == KeyboardKey.LeftCtrl || key == KeyboardKey.RightCtrl)
|
||||
{
|
||||
if (isDown)
|
||||
modifiers |= KeyboardModifierKey.Control;
|
||||
else
|
||||
modifiers &= ~KeyboardModifierKey.Control;
|
||||
}
|
||||
else if (key == KeyboardKey.LeftShift || key == KeyboardKey.RightShift)
|
||||
{
|
||||
if (isDown)
|
||||
modifiers |= KeyboardModifierKey.Shift;
|
||||
else
|
||||
modifiers &= ~KeyboardModifierKey.Shift;
|
||||
}
|
||||
else if (key == KeyboardKey.LWin || key == KeyboardKey.RWin)
|
||||
{
|
||||
if (isDown)
|
||||
modifiers |= KeyboardModifierKey.Windows;
|
||||
else
|
||||
modifiers &= ~KeyboardModifierKey.Windows;
|
||||
}
|
||||
|
||||
if (device != null)
|
||||
_keyboardModifier[device] = modifiers;
|
||||
else
|
||||
_globalModifiers = modifiers;
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
public void ReleaseAll()
|
||||
{
|
||||
foreach ((ArtemisDevice? device, HashSet<KeyboardKey>? keys) in _pressedKeys.ToList())
|
||||
{
|
||||
foreach (KeyboardKey keyboardKey in keys)
|
||||
InputProviderOnKeyboardDataReceived(this, new InputProviderKeyboardEventArgs(device, keyboardKey, false));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsKeyDown(KeyboardKey key)
|
||||
{
|
||||
return _pressedKeys.Any(p => p.Value.Contains(key));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mouse
|
||||
|
||||
private readonly HashSet<MouseButton> _pressedButtons = new();
|
||||
|
||||
private void InputProviderOnMouseButtonDataReceived(object? sender, InputProviderMouseButtonEventArgs e)
|
||||
{
|
||||
bool foundLedId = InputKeyUtilities.MouseButtonLedIdMap.TryGetValue(e.Button, out LedId ledId);
|
||||
ArtemisLed? led = null;
|
||||
if (foundLedId && e.Device != null)
|
||||
led = e.Device.Leds.FirstOrDefault(l => l.RgbLed.Id == ledId);
|
||||
|
||||
// Create the UpDown event args because it can be used for every event
|
||||
ArtemisMouseButtonUpDownEventArgs eventArgs = new(e.Device, led, e.Button, e.IsDown);
|
||||
OnMouseButtonUpDown(eventArgs);
|
||||
if (e.IsDown)
|
||||
{
|
||||
if (!_pressedButtons.Contains(e.Button))
|
||||
_pressedButtons.Add(e.Button);
|
||||
OnMouseButtonDown(eventArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_pressedButtons.Contains(e.Button))
|
||||
_pressedButtons.Remove(e.Button);
|
||||
OnMouseButtonUp(eventArgs);
|
||||
}
|
||||
|
||||
// _logger.Verbose("Mouse button data: LED ID: {ledId}, button: {button}, is down: {isDown}, device: {device} ", ledId, e.Button, e.IsDown, e.Device);
|
||||
}
|
||||
|
||||
private void InputProviderOnMouseScrollDataReceived(object? sender, InputProviderMouseScrollEventArgs e)
|
||||
{
|
||||
OnMouseScroll(new ArtemisMouseScrollEventArgs(e.Device, e.Direction, e.Delta));
|
||||
// _logger.Verbose("Mouse scroll data: Direction: {direction}, delta: {delta}, device: {device} ", e.Direction, e.Delta, e.Device);
|
||||
}
|
||||
|
||||
private void InputProviderOnMouseMoveDataReceived(object? sender, InputProviderMouseMoveEventArgs e)
|
||||
{
|
||||
OnMouseMove(new ArtemisMouseMoveEventArgs(e.Device, e.CursorX, e.CursorY, e.DeltaX, e.DeltaY));
|
||||
// _logger.Verbose("Mouse move data: XY: {X},{Y} - delta XY: {deltaX},{deltaY} - device: {device} ", e.CursorX, e.CursorY, e.DeltaX, e.DeltaY, e.Device);
|
||||
}
|
||||
|
||||
public bool IsButtonDown(MouseButton button)
|
||||
{
|
||||
return _pressedButtons.Contains(button);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,125 +1,124 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service that allows you to interact with keyboard and mice input events
|
||||
/// </summary>
|
||||
public interface IInputService : IArtemisService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// A service that allows you to interact with keyboard and mice input events
|
||||
/// Gets the current keyboard toggle status
|
||||
/// </summary>
|
||||
public interface IInputService : IArtemisService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current keyboard toggle status
|
||||
/// </summary>
|
||||
KeyboardToggleStatus KeyboardToggleStatus { get; }
|
||||
KeyboardToggleStatus KeyboardToggleStatus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds an input provided
|
||||
/// </summary>
|
||||
/// <param name="inputProvider">The input provider the add</param>
|
||||
void AddInputProvider(InputProvider inputProvider);
|
||||
/// <summary>
|
||||
/// Adds an input provided
|
||||
/// </summary>
|
||||
/// <param name="inputProvider">The input provider the add</param>
|
||||
void AddInputProvider(InputProvider inputProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an input provided
|
||||
/// </summary>
|
||||
/// <param name="inputProvider">The input provider the remove</param>
|
||||
void RemoveInputProvider(InputProvider inputProvider);
|
||||
/// <summary>
|
||||
/// Removes an input provided
|
||||
/// </summary>
|
||||
/// <param name="inputProvider">The input provider the remove</param>
|
||||
void RemoveInputProvider(InputProvider inputProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the provided <paramref name="device" /> by assigning the next received device identification to it
|
||||
/// </summary>
|
||||
/// <param name="device">The device to identify - Must be a keyboard or mouse</param>
|
||||
void IdentifyDevice(ArtemisDevice device);
|
||||
/// <summary>
|
||||
/// Identifies the provided <paramref name="device" /> by assigning the next received device identification to it
|
||||
/// </summary>
|
||||
/// <param name="device">The device to identify - Must be a keyboard or mouse</param>
|
||||
void IdentifyDevice(ArtemisDevice device);
|
||||
|
||||
/// <summary>
|
||||
/// Cancels identifying the device last passed to <see cref="IdentifyDevice" />
|
||||
/// </summary>
|
||||
void StopIdentify();
|
||||
/// <summary>
|
||||
/// Cancels identifying the device last passed to <see cref="IdentifyDevice" />
|
||||
/// </summary>
|
||||
void StopIdentify();
|
||||
|
||||
/// <summary>
|
||||
/// Flush all currently pressed buttons/keys
|
||||
/// </summary>
|
||||
void ReleaseAll();
|
||||
/// <summary>
|
||||
/// Flush all currently pressed buttons/keys
|
||||
/// </summary>
|
||||
void ReleaseAll();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided key is pressed by any device
|
||||
/// </summary>
|
||||
/// <param name="key">The key to check</param>
|
||||
/// <returns><see langword="true" /> if the key is pressed; otherwise <see langword="false" /></returns>
|
||||
bool IsKeyDown(KeyboardKey key);
|
||||
/// <summary>
|
||||
/// Determines whether the provided key is pressed by any device
|
||||
/// </summary>
|
||||
/// <param name="key">The key to check</param>
|
||||
/// <returns><see langword="true" /> if the key is pressed; otherwise <see langword="false" /></returns>
|
||||
bool IsKeyDown(KeyboardKey key);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the button key is pressed by any device
|
||||
/// </summary>
|
||||
/// <param name="button">The button to check</param>
|
||||
/// <returns><see langword="true" /> if the button is pressed; otherwise <see langword="false" /></returns>
|
||||
bool IsButtonDown(MouseButton button);
|
||||
/// <summary>
|
||||
/// Determines whether the button key is pressed by any device
|
||||
/// </summary>
|
||||
/// <param name="button">The button to check</param>
|
||||
/// <returns><see langword="true" /> if the button is pressed; otherwise <see langword="false" /></returns>
|
||||
bool IsButtonDown(MouseButton button);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a key on a keyboard was pressed or released
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisKeyboardKeyUpDownEventArgs> KeyboardKeyUpDown;
|
||||
/// <summary>
|
||||
/// Occurs whenever a key on a keyboard was pressed or released
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisKeyboardKeyUpDownEventArgs> KeyboardKeyUpDown;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a key on a keyboard was pressed
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisKeyboardKeyEventArgs> KeyboardKeyDown;
|
||||
/// <summary>
|
||||
/// Occurs whenever a key on a keyboard was pressed
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisKeyboardKeyEventArgs> KeyboardKeyDown;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a key on a keyboard was released
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisKeyboardKeyEventArgs> KeyboardKeyUp;
|
||||
/// <summary>
|
||||
/// Occurs whenever a key on a keyboard was released
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisKeyboardKeyEventArgs> KeyboardKeyUp;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a one or more of the keyboard toggle statuses changed
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisKeyboardToggleStatusArgs> KeyboardToggleStatusChanged;
|
||||
/// <summary>
|
||||
/// Occurs whenever a one or more of the keyboard toggle statuses changed
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisKeyboardToggleStatusArgs> KeyboardToggleStatusChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a button on a mouse was pressed or released
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisMouseButtonUpDownEventArgs> MouseButtonUpDown;
|
||||
/// <summary>
|
||||
/// Occurs whenever a button on a mouse was pressed or released
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisMouseButtonUpDownEventArgs> MouseButtonUpDown;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a button on a mouse was pressed
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisMouseButtonEventArgs> MouseButtonDown;
|
||||
/// <summary>
|
||||
/// Occurs whenever a button on a mouse was pressed
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisMouseButtonEventArgs> MouseButtonDown;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a button on a mouse was released
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisMouseButtonEventArgs> MouseButtonUp;
|
||||
/// <summary>
|
||||
/// Occurs whenever a button on a mouse was released
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisMouseButtonEventArgs> MouseButtonUp;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a the scroll wheel of a mouse is turned
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisMouseScrollEventArgs> MouseScroll;
|
||||
/// <summary>
|
||||
/// Occurs whenever a the scroll wheel of a mouse is turned
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisMouseScrollEventArgs> MouseScroll;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a a mouse is moved
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisMouseMoveEventArgs> MouseMove;
|
||||
/// <summary>
|
||||
/// Occurs whenever a a mouse is moved
|
||||
/// </summary>
|
||||
event EventHandler<ArtemisMouseMoveEventArgs> MouseMove;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a device has been identified after calling <see cref="IdentifyDevice" />
|
||||
/// </summary>
|
||||
public event EventHandler DeviceIdentified;
|
||||
/// <summary>
|
||||
/// Occurs when a device has been identified after calling <see cref="IdentifyDevice" />
|
||||
/// </summary>
|
||||
public event EventHandler DeviceIdentified;
|
||||
|
||||
#region Identification
|
||||
#region Identification
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to identify the device using the provided <paramref name="identifier" />
|
||||
/// </summary>
|
||||
/// <param name="provider">The input provider to identify the device for</param>
|
||||
/// <param name="identifier">The value to use to identify the device</param>
|
||||
/// <param name="type">A device type to fall back to if no match is found</param>
|
||||
/// <returns>If found, the Artemis device matching the provider and identifier</returns>
|
||||
ArtemisDevice? GetDeviceByIdentifier(InputProvider provider, object identifier, InputDeviceType type);
|
||||
/// <summary>
|
||||
/// Attempts to identify the device using the provided <paramref name="identifier" />
|
||||
/// </summary>
|
||||
/// <param name="provider">The input provider to identify the device for</param>
|
||||
/// <param name="identifier">The value to use to identify the device</param>
|
||||
/// <param name="type">A device type to fall back to if no match is found</param>
|
||||
/// <returns>If found, the Artemis device matching the provider and identifier</returns>
|
||||
ArtemisDevice? GetDeviceByIdentifier(InputProvider provider, object identifier, InputDeviceType type);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the identifier cache
|
||||
/// </summary>
|
||||
void BustIdentifierCache();
|
||||
/// <summary>
|
||||
/// Clears the identifier cache
|
||||
/// </summary>
|
||||
void BustIdentifierCache();
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -1,36 +1,35 @@
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the status of a keyboards special toggles
|
||||
/// </summary>
|
||||
public readonly struct KeyboardToggleStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the status of a keyboards special toggles
|
||||
/// Creates a new <see cref="KeyboardToggleStatus" />
|
||||
/// </summary>
|
||||
public readonly struct KeyboardToggleStatus
|
||||
/// <param name="numLock">A boolean indicating whether num lock is on</param>
|
||||
/// <param name="capsLock">A boolean indicating whether caps lock is on</param>
|
||||
/// <param name="scrollLock">A boolean indicating whether scroll lock is on</param>
|
||||
public KeyboardToggleStatus(bool numLock, bool capsLock, bool scrollLock)
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="KeyboardToggleStatus" />
|
||||
/// </summary>
|
||||
/// <param name="numLock">A boolean indicating whether num lock is on</param>
|
||||
/// <param name="capsLock">A boolean indicating whether caps lock is on</param>
|
||||
/// <param name="scrollLock">A boolean indicating whether scroll lock is on</param>
|
||||
public KeyboardToggleStatus(bool numLock, bool capsLock, bool scrollLock)
|
||||
{
|
||||
NumLock = numLock;
|
||||
CapsLock = capsLock;
|
||||
ScrollLock = scrollLock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether num lock is on
|
||||
/// </summary>
|
||||
public bool NumLock { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether caps lock is on
|
||||
/// </summary>
|
||||
public bool CapsLock { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether scroll lock is on
|
||||
/// </summary>
|
||||
public bool ScrollLock { get; }
|
||||
NumLock = numLock;
|
||||
CapsLock = capsLock;
|
||||
ScrollLock = scrollLock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether num lock is on
|
||||
/// </summary>
|
||||
public bool NumLock { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether caps lock is on
|
||||
/// </summary>
|
||||
public bool CapsLock { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether scroll lock is on
|
||||
/// </summary>
|
||||
public bool ScrollLock { get; }
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A singleton service
|
||||
/// </summary>
|
||||
public interface IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// A singleton service
|
||||
/// </summary>
|
||||
public interface IArtemisService
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,61 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service that initializes the Core and manages the render loop
|
||||
/// </summary>
|
||||
public interface ICoreService : IArtemisService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// A service that initializes the Core and manages the render loop
|
||||
/// Gets whether the or not the core has been initialized
|
||||
/// </summary>
|
||||
public interface ICoreService : IArtemisService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets whether the or not the core has been initialized
|
||||
/// </summary>
|
||||
bool IsInitialized { get; }
|
||||
bool IsInitialized { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The time the last frame took to render
|
||||
/// </summary>
|
||||
TimeSpan FrameTime { get; }
|
||||
/// <summary>
|
||||
/// The time the last frame took to render
|
||||
/// </summary>
|
||||
TimeSpan FrameTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of frames rendered each second
|
||||
/// </summary>
|
||||
public int FrameRate { get; }
|
||||
/// <summary>
|
||||
/// The amount of frames rendered each second
|
||||
/// </summary>
|
||||
public int FrameRate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether profiles are rendered each frame by calling their Render method
|
||||
/// </summary>
|
||||
bool ProfileRenderingDisabled { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets whether profiles are rendered each frame by calling their Render method
|
||||
/// </summary>
|
||||
bool ProfileRenderingDisabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of startup arguments
|
||||
/// </summary>
|
||||
List<string> StartupArguments { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a list of startup arguments
|
||||
/// </summary>
|
||||
List<string> StartupArguments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions)
|
||||
/// </summary>
|
||||
bool IsElevated { get; set; }
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions)
|
||||
/// </summary>
|
||||
bool IsElevated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the core, only call once
|
||||
/// </summary>
|
||||
void Initialize();
|
||||
/// <summary>
|
||||
/// Initializes the core, only call once
|
||||
/// </summary>
|
||||
void Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Occurs the core has finished initializing
|
||||
/// </summary>
|
||||
event EventHandler Initialized;
|
||||
/// <summary>
|
||||
/// Occurs the core has finished initializing
|
||||
/// </summary>
|
||||
event EventHandler Initialized;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a frame is rendering, after modules have rendered
|
||||
/// </summary>
|
||||
event EventHandler<FrameRenderingEventArgs> FrameRendering;
|
||||
/// <summary>
|
||||
/// Occurs whenever a frame is rendering, after modules have rendered
|
||||
/// </summary>
|
||||
event EventHandler<FrameRenderingEventArgs> FrameRendering;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a frame is finished rendering and the render pipeline is closed
|
||||
/// </summary>
|
||||
event EventHandler<FrameRenderedEventArgs> FrameRendered;
|
||||
}
|
||||
/// <summary>
|
||||
/// Occurs whenever a frame is finished rendering and the render pipeline is closed
|
||||
/// </summary>
|
||||
event EventHandler<FrameRenderedEventArgs> FrameRendered;
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service that allows you manage an <see cref="ArtemisDevice" />
|
||||
/// </summary>
|
||||
public interface IDeviceService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// A service that allows you manage an <see cref="ArtemisDevice"/>
|
||||
/// Identifies the device by making it blink white 5 times
|
||||
/// </summary>
|
||||
public interface IDeviceService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the device by making it blink white 5 times
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
void IdentifyDevice(ArtemisDevice device);
|
||||
}
|
||||
/// <param name="device"></param>
|
||||
void IdentifyDevice(ArtemisDevice device);
|
||||
}
|
||||
@@ -1,38 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.Modules;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service providing module activation functionality
|
||||
/// </summary>
|
||||
public interface IModuleService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// A service providing module activation functionality
|
||||
/// Updates all currently active modules
|
||||
/// </summary>
|
||||
public interface IModuleService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates all currently active modules
|
||||
/// </summary>
|
||||
/// <param name="deltaTime"></param>
|
||||
void UpdateActiveModules(double deltaTime);
|
||||
/// <param name="deltaTime"></param>
|
||||
void UpdateActiveModules(double deltaTime);
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates every enabled module's activation requirements and activates/deactivates modules accordingly
|
||||
/// </summary>
|
||||
void UpdateModuleActivation();
|
||||
/// <summary>
|
||||
/// Evaluates every enabled module's activation requirements and activates/deactivates modules accordingly
|
||||
/// </summary>
|
||||
void UpdateModuleActivation();
|
||||
|
||||
/// <summary>
|
||||
/// Overrides activation on the provided module and restores regular activation to any remaining modules
|
||||
/// </summary>
|
||||
void SetActivationOverride(Module? module);
|
||||
/// <summary>
|
||||
/// Overrides activation on the provided module and restores regular activation to any remaining modules
|
||||
/// </summary>
|
||||
void SetActivationOverride(Module? module);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a module is activated
|
||||
/// </summary>
|
||||
event EventHandler<ModuleEventArgs> ModuleActivated;
|
||||
/// <summary>
|
||||
/// Occurs whenever a module is activated
|
||||
/// </summary>
|
||||
event EventHandler<ModuleEventArgs> ModuleActivated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a module is deactivated
|
||||
/// </summary>
|
||||
event EventHandler<ModuleEventArgs> ModuleDeactivated;
|
||||
}
|
||||
/// <summary>
|
||||
/// Occurs whenever a module is deactivated
|
||||
/// </summary>
|
||||
event EventHandler<ModuleEventArgs> ModuleDeactivated;
|
||||
}
|
||||
@@ -5,200 +5,199 @@ using System.Reflection;
|
||||
using Artemis.Core.DeviceProviders;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service providing plugin management
|
||||
/// </summary>
|
||||
public interface IPluginManagementService : IArtemisService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// A service providing plugin management
|
||||
/// Indicates whether or not plugins are currently being loaded
|
||||
/// </summary>
|
||||
public interface IPluginManagementService : IArtemisService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether or not plugins are currently being loaded
|
||||
/// </summary>
|
||||
bool LoadingPlugins { get; }
|
||||
bool LoadingPlugins { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Copy built-in plugins from the executable directory to the plugins directory if the version is higher
|
||||
/// (higher or equal if compiled as debug)
|
||||
/// </summary>
|
||||
void CopyBuiltInPlugins();
|
||||
/// <summary>
|
||||
/// Copy built-in plugins from the executable directory to the plugins directory if the version is higher
|
||||
/// (higher or equal if compiled as debug)
|
||||
/// </summary>
|
||||
void CopyBuiltInPlugins();
|
||||
|
||||
/// <summary>
|
||||
/// Loads all installed plugins. If plugins already loaded this will reload them all
|
||||
/// </summary>
|
||||
void LoadPlugins(List<string> startupArguments, bool isElevated);
|
||||
/// <summary>
|
||||
/// Loads all installed plugins. If plugins already loaded this will reload them all
|
||||
/// </summary>
|
||||
void LoadPlugins(List<string> startupArguments, bool isElevated);
|
||||
|
||||
/// <summary>
|
||||
/// Unloads all installed plugins.
|
||||
/// </summary>
|
||||
void UnloadPlugins();
|
||||
/// <summary>
|
||||
/// Unloads all installed plugins.
|
||||
/// </summary>
|
||||
void UnloadPlugins();
|
||||
|
||||
/// <summary>
|
||||
/// Loads the plugin located in the provided <paramref name="directory" />
|
||||
/// </summary>
|
||||
/// <param name="directory">The directory where the plugin is located</param>
|
||||
Plugin? LoadPlugin(DirectoryInfo directory);
|
||||
/// <summary>
|
||||
/// Loads the plugin located in the provided <paramref name="directory" />
|
||||
/// </summary>
|
||||
/// <param name="directory">The directory where the plugin is located</param>
|
||||
Plugin? LoadPlugin(DirectoryInfo directory);
|
||||
|
||||
/// <summary>
|
||||
/// Enables the provided <paramref name="plugin" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to enable</param>
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
/// <param name="ignorePluginLock">
|
||||
/// Whether or not plugin lock files should be ignored. If set to <see langword="true" />,
|
||||
/// plugins with lock files will load successfully
|
||||
/// </param>
|
||||
void EnablePlugin(Plugin plugin, bool saveState, bool ignorePluginLock = false);
|
||||
/// <summary>
|
||||
/// Enables the provided <paramref name="plugin" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to enable</param>
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
/// <param name="ignorePluginLock">
|
||||
/// Whether or not plugin lock files should be ignored. If set to <see langword="true" />,
|
||||
/// plugins with lock files will load successfully
|
||||
/// </param>
|
||||
void EnablePlugin(Plugin plugin, bool saveState, bool ignorePluginLock = false);
|
||||
|
||||
/// <summary>
|
||||
/// Unloads the provided <paramref name="plugin" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to unload</param>
|
||||
void UnloadPlugin(Plugin plugin);
|
||||
/// <summary>
|
||||
/// Unloads the provided <paramref name="plugin" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to unload</param>
|
||||
void UnloadPlugin(Plugin plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Disables the provided <paramref name="plugin" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to disable</param>
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
void DisablePlugin(Plugin plugin, bool saveState);
|
||||
/// <summary>
|
||||
/// Disables the provided <paramref name="plugin" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to disable</param>
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
void DisablePlugin(Plugin plugin, bool saveState);
|
||||
|
||||
/// <summary>
|
||||
/// Imports the plugin contained in the provided ZIP file
|
||||
/// </summary>
|
||||
/// <param name="fileName">The full path to the ZIP file that contains the plugin</param>
|
||||
/// <returns>The resulting plugin</returns>
|
||||
Plugin? ImportPlugin(string fileName);
|
||||
/// <summary>
|
||||
/// Imports the plugin contained in the provided ZIP file
|
||||
/// </summary>
|
||||
/// <param name="fileName">The full path to the ZIP file that contains the plugin</param>
|
||||
/// <returns>The resulting plugin</returns>
|
||||
Plugin? ImportPlugin(string fileName);
|
||||
|
||||
/// <summary>
|
||||
/// Unloads and permanently removes the provided plugin
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to remove</param>
|
||||
/// <param name="removeSettings"></param>
|
||||
void RemovePlugin(Plugin plugin, bool removeSettings);
|
||||
/// <summary>
|
||||
/// Unloads and permanently removes the provided plugin
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to remove</param>
|
||||
/// <param name="removeSettings"></param>
|
||||
void RemovePlugin(Plugin plugin, bool removeSettings);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the settings of a disabled plugin
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin whose settings to remove</param>
|
||||
void RemovePluginSettings(Plugin plugin);
|
||||
/// <summary>
|
||||
/// Removes the settings of a disabled plugin
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin whose settings to remove</param>
|
||||
void RemovePluginSettings(Plugin plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Enables the provided plugin feature
|
||||
/// </summary>
|
||||
/// <param name="pluginFeature">The feature to enable</param>
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
/// <param name="isAutoEnable">If true, fails if there is a lock file present</param>
|
||||
void EnablePluginFeature(PluginFeature pluginFeature, bool saveState, bool isAutoEnable = false);
|
||||
/// <summary>
|
||||
/// Enables the provided plugin feature
|
||||
/// </summary>
|
||||
/// <param name="pluginFeature">The feature to enable</param>
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
/// <param name="isAutoEnable">If true, fails if there is a lock file present</param>
|
||||
void EnablePluginFeature(PluginFeature pluginFeature, bool saveState, bool isAutoEnable = false);
|
||||
|
||||
/// <summary>
|
||||
/// Disables the provided plugin feature
|
||||
/// </summary>
|
||||
/// <param name="pluginFeature">The feature to enable</param>
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
void DisablePluginFeature(PluginFeature pluginFeature, bool saveState);
|
||||
/// <summary>
|
||||
/// Disables the provided plugin feature
|
||||
/// </summary>
|
||||
/// <param name="pluginFeature">The feature to enable</param>
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
void DisablePluginFeature(PluginFeature pluginFeature, bool saveState);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin info of all loaded plugins
|
||||
/// </summary>
|
||||
/// <returns>A list containing all the plugin info</returns>
|
||||
List<Plugin> GetAllPlugins();
|
||||
/// <summary>
|
||||
/// Gets the plugin info of all loaded plugins
|
||||
/// </summary>
|
||||
/// <returns>A list containing all the plugin info</returns>
|
||||
List<Plugin> GetAllPlugins();
|
||||
|
||||
/// <summary>
|
||||
/// Finds all enabled <see cref="PluginFeature" /> instances of <typeparamref name="T" />
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Either <see cref="PluginFeature" /> or a plugin type implementing
|
||||
/// <see cref="PluginFeature" />
|
||||
/// </typeparam>
|
||||
/// <returns>Returns a list of feature instances of <typeparamref name="T" /></returns>
|
||||
List<T> GetFeaturesOfType<T>() where T : PluginFeature;
|
||||
/// <summary>
|
||||
/// Finds all enabled <see cref="PluginFeature" /> instances of <typeparamref name="T" />
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Either <see cref="PluginFeature" /> or a plugin type implementing
|
||||
/// <see cref="PluginFeature" />
|
||||
/// </typeparam>
|
||||
/// <returns>Returns a list of feature instances of <typeparamref name="T" /></returns>
|
||||
List<T> GetFeaturesOfType<T>() where T : PluginFeature;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin that provided the specified assembly
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
/// <returns></returns>
|
||||
Plugin? GetPluginByAssembly(Assembly? assembly);
|
||||
/// <summary>
|
||||
/// Gets the plugin that provided the specified assembly
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
/// <returns></returns>
|
||||
Plugin? GetPluginByAssembly(Assembly? assembly);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the plugin info of the current call stack
|
||||
/// </summary>
|
||||
/// <returns>If the current call stack contains a plugin, the plugin. Otherwise null</returns>
|
||||
Plugin? GetCallingPlugin();
|
||||
/// <summary>
|
||||
/// Returns the plugin info of the current call stack
|
||||
/// </summary>
|
||||
/// <returns>If the current call stack contains a plugin, the plugin. Otherwise null</returns>
|
||||
Plugin? GetCallingPlugin();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin that defined the specified device
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
/// <returns></returns>
|
||||
DeviceProvider GetDeviceProviderByDevice(IRGBDevice device);
|
||||
/// <summary>
|
||||
/// Gets the plugin that defined the specified device
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
/// <returns></returns>
|
||||
DeviceProvider GetDeviceProviderByDevice(IRGBDevice device);
|
||||
|
||||
/// <summary>
|
||||
/// Queues the provided plugin to be deleted the next time Artemis starts, before plugins are loaded
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to delete</param>
|
||||
void QueuePluginDeletion(Plugin plugin);
|
||||
/// <summary>
|
||||
/// Queues the provided plugin to be deleted the next time Artemis starts, before plugins are loaded
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to delete</param>
|
||||
void QueuePluginDeletion(Plugin plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the provided plugin for the deletion queue it was added to via <see cref="QueuePluginDeletion" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to dequeue</param>
|
||||
void DequeuePluginDeletion(Plugin plugin);
|
||||
/// <summary>
|
||||
/// Removes the provided plugin for the deletion queue it was added to via <see cref="QueuePluginDeletion" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to dequeue</param>
|
||||
void DequeuePluginDeletion(Plugin plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when built-in plugins are being loaded
|
||||
/// </summary>
|
||||
event EventHandler CopyingBuildInPlugins;
|
||||
/// <summary>
|
||||
/// Occurs when built-in plugins are being loaded
|
||||
/// </summary>
|
||||
event EventHandler CopyingBuildInPlugins;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin has started loading
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginLoading;
|
||||
/// <summary>
|
||||
/// Occurs when a plugin has started loading
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginLoading;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin has loaded
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginLoaded;
|
||||
/// <summary>
|
||||
/// Occurs when a plugin has loaded
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin has been unloaded
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginUnloaded;
|
||||
/// <summary>
|
||||
/// Occurs when a plugin has been unloaded
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginUnloaded;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin is being enabled
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginEnabling;
|
||||
/// <summary>
|
||||
/// Occurs when a plugin is being enabled
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginEnabling;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin has been enabled
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginEnabled;
|
||||
/// <summary>
|
||||
/// Occurs when a plugin has been enabled
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin has been disabled
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginDisabled;
|
||||
/// <summary>
|
||||
/// Occurs when a plugin has been disabled
|
||||
/// </summary>
|
||||
event EventHandler<PluginEventArgs> PluginDisabled;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin feature is being enabled
|
||||
/// </summary>
|
||||
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabling;
|
||||
/// <summary>
|
||||
/// Occurs when a plugin feature is being enabled
|
||||
/// </summary>
|
||||
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabling;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin feature has been enabled
|
||||
/// </summary>
|
||||
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabled;
|
||||
/// <summary>
|
||||
/// Occurs when a plugin feature has been enabled
|
||||
/// </summary>
|
||||
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin feature could not be enabled
|
||||
/// </summary>
|
||||
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnableFailed;
|
||||
/// <summary>
|
||||
/// Occurs when a plugin feature could not be enabled
|
||||
/// </summary>
|
||||
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnableFailed;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin feature has been disabled
|
||||
/// </summary>
|
||||
public event EventHandler<PluginFeatureEventArgs> PluginFeatureDisabled;
|
||||
}
|
||||
/// <summary>
|
||||
/// Occurs when a plugin feature has been disabled
|
||||
/// </summary>
|
||||
public event EventHandler<PluginFeatureEventArgs> PluginFeatureDisabled;
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// An interface for services provided by plugins.
|
||||
/// <para>
|
||||
/// Any service implementing this interface will be available inside the plugin as a singleton through dependency
|
||||
/// injection
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public interface IPluginService
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface for services provided by plugins.
|
||||
/// <para>
|
||||
/// Any service implementing this interface will be available inside the plugin as a singleton through dependency
|
||||
/// injection
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public interface IPluginService
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A singleton service that cannot be used by plugins
|
||||
/// </summary>
|
||||
public interface IProtectedArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// A singleton service that cannot be used by plugins
|
||||
/// </summary>
|
||||
public interface IProtectedArtemisService
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -3,167 +3,166 @@ using System.Collections.Generic;
|
||||
using Artemis.Core.SkiaSharp;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service that allows you to manage the <see cref="RGBSurface" /> and its contents
|
||||
/// </summary>
|
||||
public interface IRgbService : IArtemisService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// A service that allows you to manage the <see cref="RGBSurface" /> and its contents
|
||||
/// Gets a read-only collection containing all enabled devices
|
||||
/// </summary>
|
||||
public interface IRgbService : IArtemisService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a read-only collection containing all enabled devices
|
||||
/// </summary>
|
||||
IReadOnlyCollection<ArtemisDevice> EnabledDevices { get; }
|
||||
IReadOnlyCollection<ArtemisDevice> EnabledDevices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only collection containing all registered devices
|
||||
/// </summary>
|
||||
IReadOnlyCollection<ArtemisDevice> Devices { get; }
|
||||
/// <summary>
|
||||
/// Gets a read-only collection containing all registered devices
|
||||
/// </summary>
|
||||
IReadOnlyCollection<ArtemisDevice> Devices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary containing all <see cref="ArtemisLed" />s on the surface with their corresponding RGB.NET
|
||||
/// <see cref="Led" /> as key
|
||||
/// </summary>
|
||||
IReadOnlyDictionary<Led, ArtemisLed> LedMap { get; }
|
||||
/// <summary>
|
||||
/// Gets a dictionary containing all <see cref="ArtemisLed" />s on the surface with their corresponding RGB.NET
|
||||
/// <see cref="Led" /> as key
|
||||
/// </summary>
|
||||
IReadOnlyDictionary<Led, ArtemisLed> LedMap { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the RGB surface rendering is performed on
|
||||
/// </summary>
|
||||
RGBSurface Surface { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the RGB surface rendering is performed on
|
||||
/// </summary>
|
||||
RGBSurface Surface { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether rendering should be paused
|
||||
/// </summary>
|
||||
bool IsRenderPaused { get; }
|
||||
/// <summary>
|
||||
/// Gets or sets whether rendering should be paused
|
||||
/// </summary>
|
||||
bool IsRenderPaused { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the render pipeline is open
|
||||
/// </summary>
|
||||
bool RenderOpen { get; }
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the render pipeline is open
|
||||
/// </summary>
|
||||
bool RenderOpen { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether to flush the RGB.NET LEDs during next update
|
||||
/// </summary>
|
||||
bool FlushLeds { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether to flush the RGB.NET LEDs during next update
|
||||
/// </summary>
|
||||
bool FlushLeds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Opens the render pipeline
|
||||
/// </summary>
|
||||
SKTexture OpenRender();
|
||||
/// <summary>
|
||||
/// Opens the render pipeline
|
||||
/// </summary>
|
||||
SKTexture OpenRender();
|
||||
|
||||
/// <summary>
|
||||
/// Closes the render pipeline
|
||||
/// </summary>
|
||||
void CloseRender();
|
||||
/// <summary>
|
||||
/// Closes the render pipeline
|
||||
/// </summary>
|
||||
void CloseRender();
|
||||
|
||||
/// <summary>
|
||||
/// Applies the current value of the <c>Core.PreferredGraphicsContext</c> setting to the graphics context.
|
||||
/// </summary>
|
||||
/// <param name="forceSoftware">A boolean to indicate whether or not to force the graphics context to software mode.</param>
|
||||
void ApplyPreferredGraphicsContext(bool forceSoftware);
|
||||
/// <summary>
|
||||
/// Applies the current value of the <c>Core.PreferredGraphicsContext</c> setting to the graphics context.
|
||||
/// </summary>
|
||||
/// <param name="forceSoftware">A boolean to indicate whether or not to force the graphics context to software mode.</param>
|
||||
void ApplyPreferredGraphicsContext(bool forceSoftware);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the graphics context to the provided <paramref name="managedGraphicsContext"></paramref>.
|
||||
/// <para>Note: The old graphics context will be used until the next frame starts rendering and is disposed afterwards.</para>
|
||||
/// </summary>
|
||||
/// <param name="managedGraphicsContext">
|
||||
/// The new managed graphics context. If <see langword="null" />, software rendering
|
||||
/// is used.
|
||||
/// </param>
|
||||
void UpdateGraphicsContext(IManagedGraphicsContext? managedGraphicsContext);
|
||||
/// <summary>
|
||||
/// Updates the graphics context to the provided <paramref name="managedGraphicsContext"></paramref>.
|
||||
/// <para>Note: The old graphics context will be used until the next frame starts rendering and is disposed afterwards.</para>
|
||||
/// </summary>
|
||||
/// <param name="managedGraphicsContext">
|
||||
/// The new managed graphics context. If <see langword="null" />, software rendering
|
||||
/// is used.
|
||||
/// </param>
|
||||
void UpdateGraphicsContext(IManagedGraphicsContext? managedGraphicsContext);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given device provider to the <see cref="Surface" />
|
||||
/// </summary>
|
||||
/// <param name="deviceProvider"></param>
|
||||
void AddDeviceProvider(IRGBDeviceProvider deviceProvider);
|
||||
/// <summary>
|
||||
/// Adds the given device provider to the <see cref="Surface" />
|
||||
/// </summary>
|
||||
/// <param name="deviceProvider"></param>
|
||||
void AddDeviceProvider(IRGBDeviceProvider deviceProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given device provider from the <see cref="Surface" />
|
||||
/// </summary>
|
||||
/// <param name="deviceProvider"></param>
|
||||
void RemoveDeviceProvider(IRGBDeviceProvider deviceProvider);
|
||||
/// <summary>
|
||||
/// Removes the given device provider from the <see cref="Surface" />
|
||||
/// </summary>
|
||||
/// <param name="deviceProvider"></param>
|
||||
void RemoveDeviceProvider(IRGBDeviceProvider deviceProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Applies auto-arranging logic to the surface
|
||||
/// </summary>
|
||||
void AutoArrangeDevices();
|
||||
/// <summary>
|
||||
/// Applies auto-arranging logic to the surface
|
||||
/// </summary>
|
||||
void AutoArrangeDevices();
|
||||
|
||||
/// <summary>
|
||||
/// Applies the best available layout for the given <see cref="ArtemisDevice" />
|
||||
/// </summary>
|
||||
/// <param name="device">The device to apply the best available layout to</param>
|
||||
/// <returns>The layout that was applied to the device</returns>
|
||||
ArtemisLayout? ApplyBestDeviceLayout(ArtemisDevice device);
|
||||
/// <summary>
|
||||
/// Applies the best available layout for the given <see cref="ArtemisDevice" />
|
||||
/// </summary>
|
||||
/// <param name="device">The device to apply the best available layout to</param>
|
||||
/// <returns>The layout that was applied to the device</returns>
|
||||
ArtemisLayout? ApplyBestDeviceLayout(ArtemisDevice device);
|
||||
|
||||
/// <summary>
|
||||
/// Apples the provided <see cref="ArtemisLayout" /> to the provided <see cref="ArtemisDevice" />
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
/// <param name="layout"></param>
|
||||
void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout);
|
||||
/// <summary>
|
||||
/// Apples the provided <see cref="ArtemisLayout" /> to the provided <see cref="ArtemisDevice" />
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
/// <param name="layout"></param>
|
||||
void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the <see cref="ArtemisDevice" /> that corresponds the provided RGB.NET
|
||||
/// <see cref="IRGBDevice" />
|
||||
/// </summary>
|
||||
/// <param name="rgbDevice">
|
||||
/// The RGB.NET <see cref="IRGBDevice" /> to find the corresponding <see cref="ArtemisDevice" />
|
||||
/// for
|
||||
/// </param>
|
||||
/// <returns>If found, the corresponding <see cref="ArtemisDevice" />; otherwise <see langword="null" />.</returns>
|
||||
ArtemisDevice? GetDevice(IRGBDevice rgbDevice);
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the <see cref="ArtemisDevice" /> that corresponds the provided RGB.NET
|
||||
/// <see cref="IRGBDevice" />
|
||||
/// </summary>
|
||||
/// <param name="rgbDevice">
|
||||
/// The RGB.NET <see cref="IRGBDevice" /> to find the corresponding <see cref="ArtemisDevice" />
|
||||
/// for
|
||||
/// </param>
|
||||
/// <returns>If found, the corresponding <see cref="ArtemisDevice" />; otherwise <see langword="null" />.</returns>
|
||||
ArtemisDevice? GetDevice(IRGBDevice rgbDevice);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the <see cref="ArtemisLed" /> that corresponds the provided RGB.NET <see cref="Led" />
|
||||
/// </summary>
|
||||
/// <param name="led">The RGB.NET <see cref="Led" /> to find the corresponding <see cref="ArtemisLed" /> for </param>
|
||||
/// <returns>If found, the corresponding <see cref="ArtemisLed" />; otherwise <see langword="null" />.</returns>
|
||||
ArtemisLed? GetLed(Led led);
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the <see cref="ArtemisLed" /> that corresponds the provided RGB.NET <see cref="Led" />
|
||||
/// </summary>
|
||||
/// <param name="led">The RGB.NET <see cref="Led" /> to find the corresponding <see cref="ArtemisLed" /> for </param>
|
||||
/// <returns>If found, the corresponding <see cref="ArtemisLed" />; otherwise <see langword="null" />.</returns>
|
||||
ArtemisLed? GetLed(Led led);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the configuration of the provided device to persistent storage
|
||||
/// </summary>
|
||||
/// <param name="artemisDevice"></param>
|
||||
void SaveDevice(ArtemisDevice artemisDevice);
|
||||
/// <summary>
|
||||
/// Saves the configuration of the provided device to persistent storage
|
||||
/// </summary>
|
||||
/// <param name="artemisDevice"></param>
|
||||
void SaveDevice(ArtemisDevice artemisDevice);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the configuration of all current devices to persistent storage
|
||||
/// </summary>
|
||||
void SaveDevices();
|
||||
/// <summary>
|
||||
/// Saves the configuration of all current devices to persistent storage
|
||||
/// </summary>
|
||||
void SaveDevices();
|
||||
|
||||
/// <summary>
|
||||
/// Enables the provided device
|
||||
/// </summary>
|
||||
/// <param name="device">The device to enable</param>
|
||||
void EnableDevice(ArtemisDevice device);
|
||||
/// <summary>
|
||||
/// Enables the provided device
|
||||
/// </summary>
|
||||
/// <param name="device">The device to enable</param>
|
||||
void EnableDevice(ArtemisDevice device);
|
||||
|
||||
/// <summary>
|
||||
/// Disables the provided device
|
||||
/// </summary>
|
||||
/// <param name="device">The device to disable</param>
|
||||
void DisableDevice(ArtemisDevice device);
|
||||
/// <summary>
|
||||
/// Disables the provided device
|
||||
/// </summary>
|
||||
/// <param name="device">The device to disable</param>
|
||||
void DisableDevice(ArtemisDevice device);
|
||||
|
||||
/// <summary>
|
||||
/// Pauses or resumes rendering, method won't return until the current frame finished rendering
|
||||
/// </summary>
|
||||
/// <param name="paused"></param>
|
||||
/// <returns><see langword="true" /> if the pause state was changed; otherwise <see langword="false" />.</returns>
|
||||
bool SetRenderPaused(bool paused);
|
||||
/// <summary>
|
||||
/// Pauses or resumes rendering, method won't return until the current frame finished rendering
|
||||
/// </summary>
|
||||
/// <param name="paused"></param>
|
||||
/// <returns><see langword="true" /> if the pause state was changed; otherwise <see langword="false" />.</returns>
|
||||
bool SetRenderPaused(bool paused);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a single device was added
|
||||
/// </summary>
|
||||
event EventHandler<DeviceEventArgs> DeviceAdded;
|
||||
/// <summary>
|
||||
/// Occurs when a single device was added
|
||||
/// </summary>
|
||||
event EventHandler<DeviceEventArgs> DeviceAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a single device was removed
|
||||
/// </summary>
|
||||
event EventHandler<DeviceEventArgs> DeviceRemoved;
|
||||
/// <summary>
|
||||
/// Occurs when a single device was removed
|
||||
/// </summary>
|
||||
event EventHandler<DeviceEventArgs> DeviceRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the surface has had modifications to its LED collection
|
||||
/// </summary>
|
||||
event EventHandler LedsChanged;
|
||||
}
|
||||
/// <summary>
|
||||
/// Occurs when the surface has had modifications to its LED collection
|
||||
/// </summary>
|
||||
event EventHandler LedsChanged;
|
||||
}
|
||||
@@ -1,47 +1,46 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.Core.ScriptingProviders;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service that allows you to manage various types of <see cref="NodeScript" /> instances
|
||||
/// </summary>
|
||||
public interface IScriptingService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// A service that allows you to manage various types of <see cref="NodeScript" /> instances
|
||||
/// Gets a list of all available scripting providers
|
||||
/// </summary>
|
||||
public interface IScriptingService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a list of all available scripting providers
|
||||
/// </summary>
|
||||
ReadOnlyCollection<ScriptingProvider> ScriptingProviders { get; }
|
||||
ReadOnlyCollection<ScriptingProvider> ScriptingProviders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all currently active global scripts
|
||||
/// </summary>
|
||||
ReadOnlyCollection<GlobalScript> GlobalScripts { get; }
|
||||
/// <summary>
|
||||
/// Gets a list of all currently active global scripts
|
||||
/// </summary>
|
||||
ReadOnlyCollection<GlobalScript> GlobalScripts { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a script by the provided script configuration to the provided profile and instantiates it.
|
||||
/// </summary>
|
||||
/// <param name="scriptConfiguration">The script configuration whose script to add.</param>
|
||||
/// <param name="profile">The profile to add the script to.</param>
|
||||
ProfileScript AddScript(ScriptConfiguration scriptConfiguration, Profile profile);
|
||||
/// <summary>
|
||||
/// Adds a script by the provided script configuration to the provided profile and instantiates it.
|
||||
/// </summary>
|
||||
/// <param name="scriptConfiguration">The script configuration whose script to add.</param>
|
||||
/// <param name="profile">The profile to add the script to.</param>
|
||||
ProfileScript AddScript(ScriptConfiguration scriptConfiguration, Profile profile);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a script by the provided script configuration from the provided profile and disposes it.
|
||||
/// </summary>
|
||||
/// <param name="scriptConfiguration">The script configuration whose script to remove.</param>
|
||||
/// <param name="profile">The profile to remove the script from.</param>
|
||||
void RemoveScript(ScriptConfiguration scriptConfiguration, Profile profile);
|
||||
/// <summary>
|
||||
/// Removes a script by the provided script configuration from the provided profile and disposes it.
|
||||
/// </summary>
|
||||
/// <param name="scriptConfiguration">The script configuration whose script to remove.</param>
|
||||
/// <param name="profile">The profile to remove the script from.</param>
|
||||
void RemoveScript(ScriptConfiguration scriptConfiguration, Profile profile);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a script by the provided script configuration to the global collection and instantiates it.
|
||||
/// </summary>
|
||||
/// <param name="scriptConfiguration">The script configuration whose script to add.</param>
|
||||
GlobalScript AddScript(ScriptConfiguration scriptConfiguration);
|
||||
/// <summary>
|
||||
/// Adds a script by the provided script configuration to the global collection and instantiates it.
|
||||
/// </summary>
|
||||
/// <param name="scriptConfiguration">The script configuration whose script to add.</param>
|
||||
GlobalScript AddScript(ScriptConfiguration scriptConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a script by the provided script configuration from the global collection and disposes it.
|
||||
/// </summary>
|
||||
/// <param name="scriptConfiguration">The script configuration whose script to remove.</param>
|
||||
void RemoveScript(ScriptConfiguration scriptConfiguration);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes a script by the provided script configuration from the global collection and disposes it.
|
||||
/// </summary>
|
||||
/// <param name="scriptConfiguration">The script configuration whose script to remove.</param>
|
||||
void RemoveScript(ScriptConfiguration scriptConfiguration);
|
||||
}
|
||||
@@ -7,198 +7,198 @@ using Artemis.Core.Modules;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class ModuleService : IModuleService
|
||||
{
|
||||
internal class ModuleService : IModuleService
|
||||
private readonly Timer _activationUpdateTimer;
|
||||
private readonly ILogger _logger;
|
||||
private readonly List<Module> _modules;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly object _updateLock = new();
|
||||
|
||||
private Module? _activationOverride;
|
||||
|
||||
public ModuleService(ILogger logger, IPluginManagementService pluginManagementService, IProfileService profileService)
|
||||
{
|
||||
private readonly Timer _activationUpdateTimer;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly List<Module> _modules;
|
||||
private readonly object _updateLock = new();
|
||||
_logger = logger;
|
||||
_profileService = profileService;
|
||||
|
||||
private Module? _activationOverride;
|
||||
_activationUpdateTimer = new Timer(2000);
|
||||
_activationUpdateTimer.Start();
|
||||
_activationUpdateTimer.Elapsed += ActivationUpdateTimerOnElapsed;
|
||||
|
||||
public ModuleService(ILogger logger, IPluginManagementService pluginManagementService, IProfileService profileService)
|
||||
pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureEnabled;
|
||||
pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled;
|
||||
_modules = pluginManagementService.GetFeaturesOfType<Module>().ToList();
|
||||
foreach (Module module in _modules)
|
||||
ImportDefaultProfiles(module);
|
||||
}
|
||||
|
||||
protected virtual void OnModuleActivated(ModuleEventArgs e)
|
||||
{
|
||||
ModuleActivated?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnModuleDeactivated(ModuleEventArgs e)
|
||||
{
|
||||
ModuleDeactivated?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void OverrideActivate(Module module)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger = logger;
|
||||
_profileService = profileService;
|
||||
if (module.IsActivated)
|
||||
return;
|
||||
|
||||
_activationUpdateTimer = new Timer(2000);
|
||||
_activationUpdateTimer.Start();
|
||||
_activationUpdateTimer.Elapsed += ActivationUpdateTimerOnElapsed;
|
||||
// If activating while it should be deactivated, its an override
|
||||
bool shouldBeActivated = module.EvaluateActivationRequirements();
|
||||
module.Activate(!shouldBeActivated);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(new ArtemisPluginFeatureException(module, "Failed to activate module.", e), "Failed to activate module");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureEnabled;
|
||||
pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled;
|
||||
_modules = pluginManagementService.GetFeaturesOfType<Module>().ToList();
|
||||
foreach (Module module in _modules)
|
||||
private void OverrideDeactivate(Module module)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!module.IsActivated)
|
||||
return;
|
||||
|
||||
// If deactivating while it should be activated, its an override
|
||||
bool shouldBeActivated = module.EvaluateActivationRequirements();
|
||||
// No need to deactivate if it is not in an overridden state
|
||||
if (shouldBeActivated && !module.IsActivatedOverride)
|
||||
return;
|
||||
|
||||
module.Deactivate(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(new ArtemisPluginFeatureException(module, "Failed to deactivate module.", e), "Failed to deactivate module");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void ActivationUpdateTimerOnElapsed(object? sender, ElapsedEventArgs e)
|
||||
{
|
||||
UpdateModuleActivation();
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginFeatureEnabled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
lock (_updateLock)
|
||||
{
|
||||
if (e.PluginFeature is Module module && !_modules.Contains(module))
|
||||
{
|
||||
ImportDefaultProfiles(module);
|
||||
_modules.Add(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnModuleActivated(ModuleEventArgs e)
|
||||
private void PluginManagementServiceOnPluginFeatureDisabled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
lock (_updateLock)
|
||||
{
|
||||
ModuleActivated?.Invoke(this, e);
|
||||
if (e.PluginFeature is Module module)
|
||||
_modules.Remove(module);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnModuleDeactivated(ModuleEventArgs e)
|
||||
private void ImportDefaultProfiles(Module module)
|
||||
{
|
||||
try
|
||||
{
|
||||
ModuleDeactivated?.Invoke(this, e);
|
||||
}
|
||||
List<ProfileConfiguration> profileConfigurations = _profileService.ProfileCategories.SelectMany(c => c.ProfileConfigurations).ToList();
|
||||
foreach ((DefaultCategoryName categoryName, string profilePath) in module.DefaultProfilePaths)
|
||||
{
|
||||
ProfileConfigurationExportModel? profileConfigurationExportModel =
|
||||
JsonConvert.DeserializeObject<ProfileConfigurationExportModel>(File.ReadAllText(profilePath), IProfileService.ExportSettings);
|
||||
if (profileConfigurationExportModel?.ProfileEntity == null)
|
||||
throw new ArtemisCoreException($"Default profile at path {profilePath} contains no valid profile data");
|
||||
if (profileConfigurations.Any(p => p.Entity.ProfileId == profileConfigurationExportModel.ProfileEntity.Id))
|
||||
continue;
|
||||
|
||||
private void OverrideActivate(Module module)
|
||||
ProfileCategory category = _profileService.ProfileCategories.FirstOrDefault(c => c.Name == categoryName.ToString()) ??
|
||||
_profileService.CreateProfileCategory(categoryName.ToString());
|
||||
|
||||
_profileService.ImportProfile(category, profileConfigurationExportModel, false, true, null);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Warning(e, "Failed to import default profiles for module {module}", module);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateModuleActivation()
|
||||
{
|
||||
lock (_updateLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (module.IsActivated)
|
||||
return;
|
||||
|
||||
// If activating while it should be deactivated, its an override
|
||||
bool shouldBeActivated = module.EvaluateActivationRequirements();
|
||||
module.Activate(!shouldBeActivated);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(new ArtemisPluginFeatureException(module, "Failed to activate module.", e), "Failed to activate module");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void OverrideDeactivate(Module module)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!module.IsActivated)
|
||||
return;
|
||||
|
||||
// If deactivating while it should be activated, its an override
|
||||
bool shouldBeActivated = module.EvaluateActivationRequirements();
|
||||
// No need to deactivate if it is not in an overridden state
|
||||
if (shouldBeActivated && !module.IsActivatedOverride)
|
||||
return;
|
||||
|
||||
module.Deactivate(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(new ArtemisPluginFeatureException(module, "Failed to deactivate module.", e), "Failed to deactivate module");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void ActivationUpdateTimerOnElapsed(object? sender, ElapsedEventArgs e)
|
||||
{
|
||||
UpdateModuleActivation();
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginFeatureEnabled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
lock (_updateLock)
|
||||
{
|
||||
if (e.PluginFeature is Module module && !_modules.Contains(module))
|
||||
_activationUpdateTimer.Elapsed -= ActivationUpdateTimerOnElapsed;
|
||||
foreach (Module module in _modules)
|
||||
{
|
||||
ImportDefaultProfiles(module);
|
||||
_modules.Add(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginFeatureDisabled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
lock (_updateLock)
|
||||
{
|
||||
if (e.PluginFeature is Module module)
|
||||
_modules.Remove(module);
|
||||
}
|
||||
}
|
||||
|
||||
private void ImportDefaultProfiles(Module module)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<ProfileConfiguration> profileConfigurations = _profileService.ProfileCategories.SelectMany(c => c.ProfileConfigurations).ToList();
|
||||
foreach ((DefaultCategoryName categoryName, string profilePath) in module.DefaultProfilePaths)
|
||||
{
|
||||
ProfileConfigurationExportModel? profileConfigurationExportModel = JsonConvert.DeserializeObject<ProfileConfigurationExportModel>(File.ReadAllText(profilePath), IProfileService.ExportSettings);
|
||||
if (profileConfigurationExportModel?.ProfileEntity == null)
|
||||
throw new ArtemisCoreException($"Default profile at path {profilePath} contains no valid profile data");
|
||||
if (profileConfigurations.Any(p => p.Entity.ProfileId == profileConfigurationExportModel.ProfileEntity.Id))
|
||||
if (module.IsActivatedOverride)
|
||||
continue;
|
||||
|
||||
ProfileCategory category = _profileService.ProfileCategories.FirstOrDefault(c => c.Name == categoryName.ToString()) ??
|
||||
_profileService.CreateProfileCategory(categoryName.ToString());
|
||||
|
||||
_profileService.ImportProfile(category, profileConfigurationExportModel, false, true, null);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Warning(e, "Failed to import default profiles for module {module}", module);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateModuleActivation()
|
||||
{
|
||||
lock (_updateLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
_activationUpdateTimer.Elapsed -= ActivationUpdateTimerOnElapsed;
|
||||
foreach (Module module in _modules)
|
||||
if (module.IsAlwaysAvailable)
|
||||
{
|
||||
if (module.IsActivatedOverride)
|
||||
continue;
|
||||
module.Activate(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (module.IsAlwaysAvailable)
|
||||
{
|
||||
module.Activate(false);
|
||||
continue;
|
||||
}
|
||||
module.Profiler.StartMeasurement("EvaluateActivationRequirements");
|
||||
bool shouldBeActivated = module.IsEnabled && module.EvaluateActivationRequirements();
|
||||
module.Profiler.StopMeasurement("EvaluateActivationRequirements");
|
||||
|
||||
module.Profiler.StartMeasurement("EvaluateActivationRequirements");
|
||||
bool shouldBeActivated = module.IsEnabled && module.EvaluateActivationRequirements();
|
||||
module.Profiler.StopMeasurement("EvaluateActivationRequirements");
|
||||
|
||||
if (shouldBeActivated && !module.IsActivated)
|
||||
{
|
||||
module.Activate(false);
|
||||
OnModuleActivated(new ModuleEventArgs(module));
|
||||
}
|
||||
else if (!shouldBeActivated && module.IsActivated)
|
||||
{
|
||||
module.Deactivate(false);
|
||||
OnModuleDeactivated(new ModuleEventArgs(module));
|
||||
}
|
||||
if (shouldBeActivated && !module.IsActivated)
|
||||
{
|
||||
module.Activate(false);
|
||||
OnModuleActivated(new ModuleEventArgs(module));
|
||||
}
|
||||
else if (!shouldBeActivated && module.IsActivated)
|
||||
{
|
||||
module.Deactivate(false);
|
||||
OnModuleDeactivated(new ModuleEventArgs(module));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_activationUpdateTimer.Elapsed += ActivationUpdateTimerOnElapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetActivationOverride(Module? module)
|
||||
{
|
||||
lock (_updateLock)
|
||||
finally
|
||||
{
|
||||
if (_activationOverride != null)
|
||||
OverrideDeactivate(_activationOverride);
|
||||
_activationOverride = module;
|
||||
if (_activationOverride != null)
|
||||
OverrideActivate(_activationOverride);
|
||||
_activationUpdateTimer.Elapsed += ActivationUpdateTimerOnElapsed;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateActiveModules(double deltaTime)
|
||||
{
|
||||
lock (_updateLock)
|
||||
{
|
||||
foreach (Module module in _modules)
|
||||
module.InternalUpdate(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<ModuleEventArgs>? ModuleActivated;
|
||||
public event EventHandler<ModuleEventArgs>? ModuleDeactivated;
|
||||
}
|
||||
|
||||
public void SetActivationOverride(Module? module)
|
||||
{
|
||||
lock (_updateLock)
|
||||
{
|
||||
if (_activationOverride != null)
|
||||
OverrideDeactivate(_activationOverride);
|
||||
_activationOverride = module;
|
||||
if (_activationOverride != null)
|
||||
OverrideActivate(_activationOverride);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateActiveModules(double deltaTime)
|
||||
{
|
||||
lock (_updateLock)
|
||||
{
|
||||
foreach (Module module in _modules)
|
||||
module.InternalUpdate(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<ModuleEventArgs>? ModuleActivated;
|
||||
public event EventHandler<ModuleEventArgs>? ModuleDeactivated;
|
||||
}
|
||||
@@ -3,168 +3,166 @@ using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Artemis.Core.VisualScripting;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
using Newtonsoft.Json;
|
||||
using Ninject;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class NodeService : INodeService
|
||||
{
|
||||
internal class NodeService : INodeService
|
||||
#region Constants
|
||||
|
||||
private static readonly Type TYPE_NODE = typeof(INode);
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly IKernel _kernel;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public NodeService(IKernel kernel)
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private static readonly Type TYPE_NODE = typeof(INode);
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly IKernel _kernel;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public NodeService(IKernel kernel)
|
||||
{
|
||||
_kernel = kernel;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
public IEnumerable<NodeData> AvailableNodes => NodeTypeStore.GetAll();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public TypeColorRegistration GetTypeColorRegistration(Type type)
|
||||
{
|
||||
TypeColorRegistration? match = NodeTypeStore.GetColor(type);
|
||||
if (match != null)
|
||||
return match;
|
||||
|
||||
// Objects represent an input that can take any type, these are hardcoded white
|
||||
if (type == typeof(object))
|
||||
return new TypeColorRegistration(type, new SKColor(255, 255, 255, 255), Constants.CorePlugin);
|
||||
|
||||
// Come up with a random color based on the type name that should be the same each time
|
||||
MD5 md5Hasher = MD5.Create();
|
||||
byte[] hashed = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(type.FullName!));
|
||||
int hash = BitConverter.ToInt32(hashed, 0);
|
||||
|
||||
SKColor baseColor = SKColor.FromHsl(hash % 255, 50 + hash % 50, 50);
|
||||
return new TypeColorRegistration(type, baseColor, Constants.CorePlugin);
|
||||
}
|
||||
|
||||
public NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType)
|
||||
{
|
||||
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
|
||||
if (nodeType == null) throw new ArgumentNullException(nameof(nodeType));
|
||||
|
||||
if (!TYPE_NODE.IsAssignableFrom(nodeType)) throw new ArgumentException("Node has to be a base type of the Node-Type.", nameof(nodeType));
|
||||
|
||||
NodeAttribute? nodeAttribute = nodeType.GetCustomAttribute<NodeAttribute>();
|
||||
string name = nodeAttribute?.Name ?? nodeType.Name;
|
||||
string description = nodeAttribute?.Description ?? string.Empty;
|
||||
string category = nodeAttribute?.Category ?? string.Empty;
|
||||
|
||||
NodeData nodeData = new(plugin, nodeType, name, description, category, nodeAttribute?.InputType, nodeAttribute?.OutputType, (s, e) => CreateNode(s, e, nodeType));
|
||||
return NodeTypeStore.Add(nodeData);
|
||||
}
|
||||
|
||||
public TypeColorRegistration RegisterTypeColor(Plugin plugin, Type type, SKColor color)
|
||||
{
|
||||
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
|
||||
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||
|
||||
return NodeTypeStore.AddColor(type, color, plugin);
|
||||
}
|
||||
|
||||
public string ExportScript(NodeScript nodeScript)
|
||||
{
|
||||
nodeScript.Save();
|
||||
return JsonConvert.SerializeObject(nodeScript.Entity, IProfileService.ExportSettings);
|
||||
}
|
||||
|
||||
public void ImportScript(string json, NodeScript target)
|
||||
{
|
||||
NodeScriptEntity? entity = JsonConvert.DeserializeObject<NodeScriptEntity>(json);
|
||||
if (entity == null)
|
||||
throw new ArtemisCoreException("Failed to load node script");
|
||||
|
||||
target.LoadFromEntity(entity);
|
||||
}
|
||||
|
||||
private INode CreateNode(INodeScript script, NodeEntity? entity, Type nodeType)
|
||||
{
|
||||
INode node = _kernel.Get(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode");
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
node.X = entity.X;
|
||||
node.Y = entity.Y;
|
||||
try
|
||||
{
|
||||
if (node is Node nodeImplementation)
|
||||
nodeImplementation.DeserializeStorage(entity.Storage);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
node.Initialize(script);
|
||||
return node;
|
||||
}
|
||||
|
||||
#endregion
|
||||
_kernel = kernel;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
public IEnumerable<NodeData> AvailableNodes => NodeTypeStore.GetAll();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public TypeColorRegistration GetTypeColorRegistration(Type type)
|
||||
{
|
||||
TypeColorRegistration? match = NodeTypeStore.GetColor(type);
|
||||
if (match != null)
|
||||
return match;
|
||||
|
||||
// Objects represent an input that can take any type, these are hardcoded white
|
||||
if (type == typeof(object))
|
||||
return new TypeColorRegistration(type, new SKColor(255, 255, 255, 255), Constants.CorePlugin);
|
||||
|
||||
// Come up with a random color based on the type name that should be the same each time
|
||||
MD5 md5Hasher = MD5.Create();
|
||||
byte[] hashed = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(type.FullName!));
|
||||
int hash = BitConverter.ToInt32(hashed, 0);
|
||||
|
||||
SKColor baseColor = SKColor.FromHsl(hash % 255, 50 + hash % 50, 50);
|
||||
return new TypeColorRegistration(type, baseColor, Constants.CorePlugin);
|
||||
}
|
||||
|
||||
public NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType)
|
||||
{
|
||||
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
|
||||
if (nodeType == null) throw new ArgumentNullException(nameof(nodeType));
|
||||
|
||||
if (!TYPE_NODE.IsAssignableFrom(nodeType)) throw new ArgumentException("Node has to be a base type of the Node-Type.", nameof(nodeType));
|
||||
|
||||
NodeAttribute? nodeAttribute = nodeType.GetCustomAttribute<NodeAttribute>();
|
||||
string name = nodeAttribute?.Name ?? nodeType.Name;
|
||||
string description = nodeAttribute?.Description ?? string.Empty;
|
||||
string category = nodeAttribute?.Category ?? string.Empty;
|
||||
|
||||
NodeData nodeData = new(plugin, nodeType, name, description, category, nodeAttribute?.InputType, nodeAttribute?.OutputType, (s, e) => CreateNode(s, e, nodeType));
|
||||
return NodeTypeStore.Add(nodeData);
|
||||
}
|
||||
|
||||
public TypeColorRegistration RegisterTypeColor(Plugin plugin, Type type, SKColor color)
|
||||
{
|
||||
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
|
||||
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||
|
||||
return NodeTypeStore.AddColor(type, color, plugin);
|
||||
}
|
||||
|
||||
public string ExportScript(NodeScript nodeScript)
|
||||
{
|
||||
nodeScript.Save();
|
||||
return JsonConvert.SerializeObject(nodeScript.Entity, IProfileService.ExportSettings);
|
||||
}
|
||||
|
||||
public void ImportScript(string json, NodeScript target)
|
||||
{
|
||||
NodeScriptEntity? entity = JsonConvert.DeserializeObject<NodeScriptEntity>(json);
|
||||
if (entity == null)
|
||||
throw new ArtemisCoreException("Failed to load node script");
|
||||
|
||||
target.LoadFromEntity(entity);
|
||||
}
|
||||
|
||||
private INode CreateNode(INodeScript script, NodeEntity? entity, Type nodeType)
|
||||
{
|
||||
INode node = _kernel.Get(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode");
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
node.X = entity.X;
|
||||
node.Y = entity.Y;
|
||||
try
|
||||
{
|
||||
if (node is Node nodeImplementation)
|
||||
nodeImplementation.DeserializeStorage(entity.Storage);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
node.Initialize(script);
|
||||
return node;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A service that provides access to the node system
|
||||
/// </summary>
|
||||
public interface INodeService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all available nodes
|
||||
/// </summary>
|
||||
IEnumerable<NodeData> AvailableNodes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A service that provides access to the node system
|
||||
/// Gets the best matching registration for the provided type
|
||||
/// </summary>
|
||||
public interface INodeService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all available nodes
|
||||
/// </summary>
|
||||
IEnumerable<NodeData> AvailableNodes { get; }
|
||||
TypeColorRegistration GetTypeColorRegistration(Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the best matching registration for the provided type
|
||||
/// </summary>
|
||||
TypeColorRegistration GetTypeColorRegistration(Type type);
|
||||
/// <summary>
|
||||
/// Registers a node of the provided <paramref name="nodeType" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin the node belongs to</param>
|
||||
/// <param name="nodeType">The type of node to initialize</param>
|
||||
NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a node of the provided <paramref name="nodeType" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin the node belongs to</param>
|
||||
/// <param name="nodeType">The type of node to initialize</param>
|
||||
NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType);
|
||||
/// <summary>
|
||||
/// Registers a type with a provided color for use in the node editor
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin making the registration</param>
|
||||
/// <param name="type">The type to associate the color with</param>
|
||||
/// <param name="color">The color to display</param>
|
||||
TypeColorRegistration RegisterTypeColor(Plugin plugin, Type type, SKColor color);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a type with a provided color for use in the node editor
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin making the registration</param>
|
||||
/// <param name="type">The type to associate the color with</param>
|
||||
/// <param name="color">The color to display</param>
|
||||
TypeColorRegistration RegisterTypeColor(Plugin plugin, Type type, SKColor color);
|
||||
/// <summary>
|
||||
/// Exports the provided node script to JSON.
|
||||
/// </summary>
|
||||
/// <param name="nodeScript">The node script to export.</param>
|
||||
/// <returns>The resulting JSON.</returns>
|
||||
string ExportScript(NodeScript nodeScript);
|
||||
|
||||
/// <summary>
|
||||
/// Exports the provided node script to JSON.
|
||||
/// </summary>
|
||||
/// <param name="nodeScript">The node script to export.</param>
|
||||
/// <returns>The resulting JSON.</returns>
|
||||
string ExportScript(NodeScript nodeScript);
|
||||
|
||||
/// <summary>
|
||||
/// Imports the provided JSON onto the provided node script, overwriting any existing contents.
|
||||
/// </summary>
|
||||
/// <param name="json">The JSON to import.</param>
|
||||
/// <param name="target">The target node script whose contents to overwrite.</param>
|
||||
void ImportScript(string json, NodeScript target);
|
||||
}
|
||||
/// <summary>
|
||||
/// Imports the provided JSON onto the provided node script, overwriting any existing contents.
|
||||
/// </summary>
|
||||
/// <param name="json">The JSON to import.</param>
|
||||
/// <param name="target">The target node script whose contents to overwrite.</param>
|
||||
void ImportScript(string json, NodeScript target);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,21 +1,20 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for the ProcessMonitor process events
|
||||
/// </summary>
|
||||
public class ProcessEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the process related to the event
|
||||
/// </summary>
|
||||
public Process Process { get; }
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal ProcessEventArgs(Process process)
|
||||
{
|
||||
Process = process;
|
||||
}
|
||||
/// <summary>
|
||||
/// Contains data for the ProcessMonitor process events
|
||||
/// </summary>
|
||||
public class ProcessEventArgs : EventArgs
|
||||
{
|
||||
internal ProcessEventArgs(Process process)
|
||||
{
|
||||
Process = process;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the process related to the event
|
||||
/// </summary>
|
||||
public Process Process { get; }
|
||||
}
|
||||
@@ -1,30 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service that provides events for started and stopped processes and a list of all running processes.
|
||||
/// </summary>
|
||||
public interface IProcessMonitorService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// A service that provides events for started and stopped processes and a list of all running processes.
|
||||
/// Occurs when a process starts.
|
||||
/// </summary>
|
||||
public interface IProcessMonitorService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when a process starts.
|
||||
/// </summary>
|
||||
event EventHandler<ProcessEventArgs> ProcessStarted;
|
||||
event EventHandler<ProcessEventArgs> ProcessStarted;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a process stops.
|
||||
/// </summary>
|
||||
event EventHandler<ProcessEventArgs> ProcessStopped;
|
||||
/// <summary>
|
||||
/// Occurs when a process stops.
|
||||
/// </summary>
|
||||
event EventHandler<ProcessEventArgs> ProcessStopped;
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerable with the processes running on the system.
|
||||
/// </summary>
|
||||
IEnumerable<Process> GetRunningProcesses();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns an enumerable with the processes running on the system.
|
||||
/// </summary>
|
||||
IEnumerable<Process> GetRunningProcesses();
|
||||
}
|
||||
@@ -1,71 +1,70 @@
|
||||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Timers;
|
||||
using Artemis.Core.Modules;
|
||||
using Serilog;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class ProcessMonitorService : IProcessMonitorService
|
||||
{
|
||||
internal class ProcessMonitorService : IProcessMonitorService
|
||||
private readonly ProcessComparer _comparer;
|
||||
private readonly ILogger _logger;
|
||||
private Process[] _lastScannedProcesses;
|
||||
|
||||
public ProcessMonitorService(ILogger logger)
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly ProcessComparer _comparer;
|
||||
private Process[] _lastScannedProcesses;
|
||||
_logger = logger;
|
||||
_lastScannedProcesses = Process.GetProcesses();
|
||||
Timer processScanTimer = new(1000);
|
||||
processScanTimer.Elapsed += OnTimerElapsed;
|
||||
processScanTimer.Start();
|
||||
_comparer = new ProcessComparer();
|
||||
|
||||
public ProcessMonitorService(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_lastScannedProcesses = Process.GetProcesses();
|
||||
Timer processScanTimer = new(1000);
|
||||
processScanTimer.Elapsed += OnTimerElapsed;
|
||||
processScanTimer.Start();
|
||||
_comparer = new ProcessComparer();
|
||||
|
||||
ProcessActivationRequirement.ProcessMonitorService = this;
|
||||
}
|
||||
|
||||
public event EventHandler<ProcessEventArgs>? ProcessStarted;
|
||||
public event EventHandler<ProcessEventArgs>? ProcessStopped;
|
||||
|
||||
public IEnumerable<Process> GetRunningProcesses()
|
||||
{
|
||||
return _lastScannedProcesses;
|
||||
}
|
||||
|
||||
private void OnTimerElapsed(object? sender, ElapsedEventArgs e)
|
||||
{
|
||||
Process[] newProcesses = Process.GetProcesses();
|
||||
foreach (Process startedProcess in newProcesses.Except(_lastScannedProcesses, _comparer))
|
||||
{
|
||||
ProcessStarted?.Invoke(this, new ProcessEventArgs(startedProcess));
|
||||
_logger.Verbose("Started Process: {startedProcess}", startedProcess.ProcessName);
|
||||
}
|
||||
|
||||
foreach (Process stoppedProcess in _lastScannedProcesses.Except(newProcesses, _comparer))
|
||||
{
|
||||
ProcessStopped?.Invoke(this, new ProcessEventArgs(stoppedProcess));
|
||||
_logger.Verbose("Stopped Process: {stoppedProcess}", stoppedProcess.ProcessName);
|
||||
}
|
||||
|
||||
_lastScannedProcesses = newProcesses;
|
||||
}
|
||||
ProcessActivationRequirement.ProcessMonitorService = this;
|
||||
}
|
||||
|
||||
internal class ProcessComparer : IEqualityComparer<Process>
|
||||
private void OnTimerElapsed(object? sender, ElapsedEventArgs e)
|
||||
{
|
||||
public bool Equals(Process? x, Process? y)
|
||||
Process[] newProcesses = Process.GetProcesses();
|
||||
foreach (Process startedProcess in newProcesses.Except(_lastScannedProcesses, _comparer))
|
||||
{
|
||||
if (x == null && y == null) return true;
|
||||
if (x == null || y == null) return false;
|
||||
return x.Id == y.Id && x.ProcessName == y.ProcessName && x.SessionId == y.SessionId;
|
||||
ProcessStarted?.Invoke(this, new ProcessEventArgs(startedProcess));
|
||||
_logger.Verbose("Started Process: {startedProcess}", startedProcess.ProcessName);
|
||||
}
|
||||
|
||||
public int GetHashCode(Process? obj)
|
||||
foreach (Process stoppedProcess in _lastScannedProcesses.Except(newProcesses, _comparer))
|
||||
{
|
||||
if (obj == null) return 0;
|
||||
return obj.Id;
|
||||
ProcessStopped?.Invoke(this, new ProcessEventArgs(stoppedProcess));
|
||||
_logger.Verbose("Stopped Process: {stoppedProcess}", stoppedProcess.ProcessName);
|
||||
}
|
||||
|
||||
_lastScannedProcesses = newProcesses;
|
||||
}
|
||||
|
||||
public event EventHandler<ProcessEventArgs>? ProcessStarted;
|
||||
public event EventHandler<ProcessEventArgs>? ProcessStopped;
|
||||
|
||||
public IEnumerable<Process> GetRunningProcesses()
|
||||
{
|
||||
return _lastScannedProcesses;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ProcessComparer : IEqualityComparer<Process>
|
||||
{
|
||||
public bool Equals(Process? x, Process? y)
|
||||
{
|
||||
if (x == null && y == null) return true;
|
||||
if (x == null || y == null) return false;
|
||||
return x.Id == y.Id && x.ProcessName == y.ProcessName && x.SessionId == y.SessionId;
|
||||
}
|
||||
|
||||
public int GetHashCode(Process? obj)
|
||||
{
|
||||
if (obj == null) return 0;
|
||||
return obj.Id;
|
||||
}
|
||||
}
|
||||
@@ -3,66 +3,65 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Modules;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class DataModelService : IDataModelService
|
||||
{
|
||||
internal class DataModelService : IDataModelService
|
||||
public DataModelService(IPluginManagementService pluginManagementService)
|
||||
{
|
||||
public DataModelService(IPluginManagementService pluginManagementService)
|
||||
{
|
||||
// Add data models of already loaded plugins
|
||||
foreach (Module module in pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled))
|
||||
AddModuleDataModel(module);
|
||||
// Add data models of already loaded plugins
|
||||
foreach (Module module in pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled))
|
||||
AddModuleDataModel(module);
|
||||
|
||||
// Add data models of new plugins when they get enabled
|
||||
pluginManagementService.PluginFeatureEnabled += OnPluginFeatureEnabled;
|
||||
}
|
||||
// Add data models of new plugins when they get enabled
|
||||
pluginManagementService.PluginFeatureEnabled += OnPluginFeatureEnabled;
|
||||
}
|
||||
|
||||
private void OnPluginFeatureEnabled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
if (e.PluginFeature is Module module)
|
||||
AddModuleDataModel(module);
|
||||
}
|
||||
|
||||
private void AddModuleDataModel(Module module)
|
||||
{
|
||||
if (module.InternalDataModel == null)
|
||||
return;
|
||||
|
||||
if (module.InternalDataModel.DataModelDescription == null)
|
||||
throw new ArtemisPluginFeatureException(module, "Module overrides GetDataModelDescription but returned null");
|
||||
|
||||
module.InternalDataModel.IsExpansion = module.IsAlwaysAvailable;
|
||||
RegisterDataModel(module.InternalDataModel);
|
||||
}
|
||||
|
||||
|
||||
public DataModelRegistration RegisterDataModel(DataModel dataModel)
|
||||
{
|
||||
if (dataModel == null)
|
||||
throw new ArgumentNullException(nameof(dataModel));
|
||||
return DataModelStore.Add(dataModel);
|
||||
}
|
||||
public DataModelRegistration RegisterDataModel(DataModel dataModel)
|
||||
{
|
||||
if (dataModel == null)
|
||||
throw new ArgumentNullException(nameof(dataModel));
|
||||
return DataModelStore.Add(dataModel);
|
||||
}
|
||||
|
||||
public void RemoveDataModel(DataModelRegistration registration)
|
||||
{
|
||||
if (registration == null)
|
||||
throw new ArgumentNullException(nameof(registration));
|
||||
DataModelStore.Remove(registration);
|
||||
}
|
||||
public void RemoveDataModel(DataModelRegistration registration)
|
||||
{
|
||||
if (registration == null)
|
||||
throw new ArgumentNullException(nameof(registration));
|
||||
DataModelStore.Remove(registration);
|
||||
}
|
||||
|
||||
public List<DataModel> GetDataModels()
|
||||
{
|
||||
return DataModelStore.GetAll().Select(d => d.DataModel).ToList();
|
||||
}
|
||||
public List<DataModel> GetDataModels()
|
||||
{
|
||||
return DataModelStore.GetAll().Select(d => d.DataModel).ToList();
|
||||
}
|
||||
|
||||
public T? GetDataModel<T>() where T : DataModel
|
||||
{
|
||||
return (T?) DataModelStore.GetAll().FirstOrDefault(d => d.DataModel is T)?.DataModel;
|
||||
}
|
||||
public T? GetDataModel<T>() where T : DataModel
|
||||
{
|
||||
return (T?) DataModelStore.GetAll().FirstOrDefault(d => d.DataModel is T)?.DataModel;
|
||||
}
|
||||
|
||||
public DataModel? GetPluginDataModel(PluginFeature pluginFeature)
|
||||
{
|
||||
return DataModelStore.Get(pluginFeature.Id)?.DataModel;
|
||||
}
|
||||
|
||||
private void OnPluginFeatureEnabled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
if (e.PluginFeature is Module module)
|
||||
AddModuleDataModel(module);
|
||||
}
|
||||
|
||||
private void AddModuleDataModel(Module module)
|
||||
{
|
||||
if (module.InternalDataModel == null)
|
||||
return;
|
||||
|
||||
if (module.InternalDataModel.DataModelDescription == null)
|
||||
throw new ArtemisPluginFeatureException(module, "Module overrides GetDataModelDescription but returned null");
|
||||
|
||||
module.InternalDataModel.IsExpansion = module.IsAlwaysAvailable;
|
||||
RegisterDataModel(module.InternalDataModel);
|
||||
}
|
||||
public DataModel? GetPluginDataModel(PluginFeature pluginFeature)
|
||||
{
|
||||
return DataModelStore.Get(pluginFeature.Id)?.DataModel;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.Modules;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service that allows you to register and retrieve data models
|
||||
/// </summary>
|
||||
public interface IDataModelService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// A service that allows you to register and retrieve data models
|
||||
/// Add a data model to so that it is available to conditions and data bindings
|
||||
/// </summary>
|
||||
public interface IDataModelService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Add a data model to so that it is available to conditions and data bindings
|
||||
/// </summary>
|
||||
/// <param name="dataModel"></param>
|
||||
DataModelRegistration RegisterDataModel(DataModel dataModel);
|
||||
/// <param name="dataModel"></param>
|
||||
DataModelRegistration RegisterDataModel(DataModel dataModel);
|
||||
|
||||
/// <summary>
|
||||
/// Remove a previously added data model so that it is no longer available
|
||||
/// </summary>
|
||||
void RemoveDataModel(DataModelRegistration registration);
|
||||
/// <summary>
|
||||
/// Remove a previously added data model so that it is no longer available
|
||||
/// </summary>
|
||||
void RemoveDataModel(DataModelRegistration registration);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all registered data models
|
||||
/// </summary>
|
||||
List<DataModel> GetDataModels();
|
||||
/// <summary>
|
||||
/// Returns a list of all registered data models
|
||||
/// </summary>
|
||||
List<DataModel> GetDataModels();
|
||||
|
||||
/// <summary>
|
||||
/// If found, returns the registered data model of type <typeparamref name="T" />
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the data model to find</typeparam>
|
||||
T? GetDataModel<T>() where T : DataModel;
|
||||
/// <summary>
|
||||
/// If found, returns the registered data model of type <typeparamref name="T" />
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the data model to find</typeparam>
|
||||
T? GetDataModel<T>() where T : DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// If found, returns the data model of the provided plugin
|
||||
/// </summary>
|
||||
/// <param name="pluginFeature">The plugin to find the data model of</param>
|
||||
DataModel? GetPluginDataModel(PluginFeature pluginFeature);
|
||||
}
|
||||
/// <summary>
|
||||
/// If found, returns the data model of the provided plugin
|
||||
/// </summary>
|
||||
/// <param name="pluginFeature">The plugin to find the data model of</param>
|
||||
DataModel? GetPluginDataModel(PluginFeature pluginFeature);
|
||||
}
|
||||
@@ -1,37 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service that allows you to register and retrieve layer brushes
|
||||
/// </summary>
|
||||
public interface ILayerBrushService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// A service that allows you to register and retrieve layer brushes
|
||||
/// Add a layer brush descriptor so that it is available to layers
|
||||
/// </summary>
|
||||
public interface ILayerBrushService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Add a layer brush descriptor so that it is available to layers
|
||||
/// </summary>
|
||||
LayerBrushRegistration RegisterLayerBrush(LayerBrushDescriptor descriptor);
|
||||
LayerBrushRegistration RegisterLayerBrush(LayerBrushDescriptor descriptor);
|
||||
|
||||
/// <summary>
|
||||
/// Remove a previously added layer brush descriptor so that it is no longer available
|
||||
/// </summary>
|
||||
void RemoveLayerBrush(LayerBrushRegistration registration);
|
||||
/// <summary>
|
||||
/// Remove a previously added layer brush descriptor so that it is no longer available
|
||||
/// </summary>
|
||||
void RemoveLayerBrush(LayerBrushRegistration registration);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all registered layer brush descriptors
|
||||
/// </summary>
|
||||
List<LayerBrushDescriptor> GetLayerBrushes();
|
||||
/// <summary>
|
||||
/// Returns a list of all registered layer brush descriptors
|
||||
/// </summary>
|
||||
List<LayerBrushDescriptor> GetLayerBrushes();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the descriptor of the default layer brush
|
||||
/// </summary>
|
||||
LayerBrushDescriptor? GetDefaultLayerBrush();
|
||||
/// <summary>
|
||||
/// Returns the descriptor of the default layer brush
|
||||
/// </summary>
|
||||
LayerBrushDescriptor? GetDefaultLayerBrush();
|
||||
|
||||
/// <summary>
|
||||
/// Applies the configured default brush to the provided <paramref name="layer" />.
|
||||
/// </summary>
|
||||
/// <param name="layer">The layer to apply the default brush to.</param>
|
||||
void ApplyDefaultBrush(Layer layer);
|
||||
}
|
||||
/// <summary>
|
||||
/// Applies the configured default brush to the provided <paramref name="layer" />.
|
||||
/// </summary>
|
||||
/// <param name="layer">The layer to apply the default brush to.</param>
|
||||
void ApplyDefaultBrush(Layer layer);
|
||||
}
|
||||
@@ -1,27 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
using Artemis.Core.LayerEffects;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service that allows you to register and retrieve layer brushes
|
||||
/// </summary>
|
||||
public interface ILayerEffectService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// A service that allows you to register and retrieve layer brushes
|
||||
/// Add an effect descriptor so that it is available to profile elements
|
||||
/// </summary>
|
||||
public interface ILayerEffectService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Add an effect descriptor so that it is available to profile elements
|
||||
/// </summary>
|
||||
LayerEffectRegistration RegisterLayerEffect(LayerEffectDescriptor descriptor);
|
||||
LayerEffectRegistration RegisterLayerEffect(LayerEffectDescriptor descriptor);
|
||||
|
||||
/// <summary>
|
||||
/// Remove a previously added layer effect descriptor so that it is no longer available
|
||||
/// </summary>
|
||||
void RemoveLayerEffect(LayerEffectRegistration registration);
|
||||
/// <summary>
|
||||
/// Remove a previously added layer effect descriptor so that it is no longer available
|
||||
/// </summary>
|
||||
void RemoveLayerEffect(LayerEffectRegistration registration);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all registered layer effect descriptors
|
||||
/// </summary>
|
||||
List<LayerEffectDescriptor> GetLayerEffects();
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a list of all registered layer effect descriptors
|
||||
/// </summary>
|
||||
List<LayerEffectDescriptor> GetLayerEffects();
|
||||
}
|
||||
@@ -3,56 +3,55 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class LayerBrushService : ILayerBrushService
|
||||
{
|
||||
internal class LayerBrushService : ILayerBrushService
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
public LayerBrushService(ISettingsService settingsService)
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
_settingsService = settingsService;
|
||||
}
|
||||
|
||||
public LayerBrushService(ISettingsService settingsService)
|
||||
public LayerBrushRegistration RegisterLayerBrush(LayerBrushDescriptor descriptor)
|
||||
{
|
||||
if (descriptor == null)
|
||||
throw new ArgumentNullException(nameof(descriptor));
|
||||
|
||||
return LayerBrushStore.Add(descriptor);
|
||||
}
|
||||
|
||||
public void RemoveLayerBrush(LayerBrushRegistration registration)
|
||||
{
|
||||
if (registration == null)
|
||||
throw new ArgumentNullException(nameof(registration));
|
||||
LayerBrushStore.Remove(registration);
|
||||
}
|
||||
|
||||
public List<LayerBrushDescriptor> GetLayerBrushes()
|
||||
{
|
||||
return LayerBrushStore.GetAll().Select(r => r.LayerBrushDescriptor).ToList();
|
||||
}
|
||||
|
||||
public LayerBrushDescriptor? GetDefaultLayerBrush()
|
||||
{
|
||||
PluginSetting<LayerBrushReference> defaultReference = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
}
|
||||
LayerBrushProviderId = "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba",
|
||||
BrushType = "SolidBrush"
|
||||
});
|
||||
|
||||
public LayerBrushRegistration RegisterLayerBrush(LayerBrushDescriptor descriptor)
|
||||
{
|
||||
if (descriptor == null)
|
||||
throw new ArgumentNullException(nameof(descriptor));
|
||||
defaultReference.Value.LayerBrushProviderId ??= "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba";
|
||||
defaultReference.Value.BrushType ??= "SolidBrush";
|
||||
return LayerBrushStore.Get(defaultReference.Value.LayerBrushProviderId, defaultReference.Value.BrushType)?.LayerBrushDescriptor;
|
||||
}
|
||||
|
||||
return LayerBrushStore.Add(descriptor);
|
||||
}
|
||||
|
||||
public void RemoveLayerBrush(LayerBrushRegistration registration)
|
||||
{
|
||||
if (registration == null)
|
||||
throw new ArgumentNullException(nameof(registration));
|
||||
LayerBrushStore.Remove(registration);
|
||||
}
|
||||
|
||||
public List<LayerBrushDescriptor> GetLayerBrushes()
|
||||
{
|
||||
return LayerBrushStore.GetAll().Select(r => r.LayerBrushDescriptor).ToList();
|
||||
}
|
||||
|
||||
public LayerBrushDescriptor? GetDefaultLayerBrush()
|
||||
{
|
||||
PluginSetting<LayerBrushReference> defaultReference = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
|
||||
{
|
||||
LayerBrushProviderId = "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba",
|
||||
BrushType = "SolidBrush"
|
||||
});
|
||||
|
||||
defaultReference.Value.LayerBrushProviderId ??= "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba";
|
||||
defaultReference.Value.BrushType ??= "SolidBrush";
|
||||
return LayerBrushStore.Get(defaultReference.Value.LayerBrushProviderId, defaultReference.Value.BrushType)?.LayerBrushDescriptor;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ApplyDefaultBrush(Layer layer)
|
||||
{
|
||||
LayerBrushDescriptor? brush = GetDefaultLayerBrush();
|
||||
if (brush != null)
|
||||
layer.ChangeLayerBrush(brush.CreateInstance(layer, null));
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public void ApplyDefaultBrush(Layer layer)
|
||||
{
|
||||
LayerBrushDescriptor? brush = GetDefaultLayerBrush();
|
||||
if (brush != null)
|
||||
layer.ChangeLayerBrush(brush.CreateInstance(layer, null));
|
||||
}
|
||||
}
|
||||
@@ -3,28 +3,27 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core.LayerEffects;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class LayerEffectService : ILayerEffectService
|
||||
{
|
||||
internal class LayerEffectService : ILayerEffectService
|
||||
public LayerEffectRegistration RegisterLayerEffect(LayerEffectDescriptor descriptor)
|
||||
{
|
||||
public LayerEffectRegistration RegisterLayerEffect(LayerEffectDescriptor descriptor)
|
||||
{
|
||||
if (descriptor == null)
|
||||
throw new ArgumentNullException(nameof(descriptor));
|
||||
if (descriptor == null)
|
||||
throw new ArgumentNullException(nameof(descriptor));
|
||||
|
||||
return LayerEffectStore.Add(descriptor);
|
||||
}
|
||||
return LayerEffectStore.Add(descriptor);
|
||||
}
|
||||
|
||||
public void RemoveLayerEffect(LayerEffectRegistration registration)
|
||||
{
|
||||
if (registration == null)
|
||||
throw new ArgumentNullException(nameof(registration));
|
||||
LayerEffectStore.Remove(registration);
|
||||
}
|
||||
public void RemoveLayerEffect(LayerEffectRegistration registration)
|
||||
{
|
||||
if (registration == null)
|
||||
throw new ArgumentNullException(nameof(registration));
|
||||
LayerEffectStore.Remove(registration);
|
||||
}
|
||||
|
||||
public List<LayerEffectDescriptor> GetLayerEffects()
|
||||
{
|
||||
return LayerEffectStore.GetAll().Select(r => r.LayerEffectDescriptor).ToList();
|
||||
}
|
||||
public List<LayerEffectDescriptor> GetLayerEffects()
|
||||
{
|
||||
return LayerEffectStore.GetAll().Select(r => r.LayerEffectDescriptor).ToList();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -7,185 +7,180 @@ using Artemis.Core.ScriptingProviders;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class ScriptingService : IScriptingService
|
||||
{
|
||||
internal class ScriptingService : IScriptingService
|
||||
private readonly List<GlobalScript> _globalScripts;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly List<ScriptingProvider> _scriptingProviders;
|
||||
|
||||
public ScriptingService(IPluginManagementService pluginManagementService, IProfileService profileService)
|
||||
{
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly List<GlobalScript> _globalScripts;
|
||||
private readonly List<ScriptingProvider> _scriptingProviders;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_profileService = profileService;
|
||||
|
||||
public ScriptingService(IPluginManagementService pluginManagementService, IProfileService profileService)
|
||||
_pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureToggled;
|
||||
_pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureToggled;
|
||||
_scriptingProviders = _pluginManagementService.GetFeaturesOfType<ScriptingProvider>();
|
||||
_globalScripts = new List<GlobalScript>();
|
||||
|
||||
ScriptingProviders = new ReadOnlyCollection<ScriptingProvider>(_scriptingProviders);
|
||||
GlobalScripts = new ReadOnlyCollection<GlobalScript>(_globalScripts);
|
||||
|
||||
// No need to sub to Deactivated, scripts will deactivate themselves
|
||||
profileService.ProfileActivated += ProfileServiceOnProfileActivated;
|
||||
|
||||
foreach (ProfileConfiguration profileConfiguration in _profileService.ProfileConfigurations)
|
||||
{
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_profileService = profileService;
|
||||
if (profileConfiguration.Profile != null)
|
||||
InitializeProfileScripts(profileConfiguration.Profile);
|
||||
}
|
||||
}
|
||||
|
||||
_pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureToggled;
|
||||
_pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureToggled;
|
||||
_scriptingProviders = _pluginManagementService.GetFeaturesOfType<ScriptingProvider>();
|
||||
_globalScripts = new List<GlobalScript>();
|
||||
private GlobalScript CreateScriptInstance(ScriptConfiguration scriptConfiguration)
|
||||
{
|
||||
GlobalScript? script = null;
|
||||
try
|
||||
{
|
||||
if (scriptConfiguration.Script != null)
|
||||
throw new ArtemisCoreException("The provided script configuration already has an active script");
|
||||
|
||||
ScriptingProviders = new ReadOnlyCollection<ScriptingProvider>(_scriptingProviders);
|
||||
GlobalScripts = new ReadOnlyCollection<GlobalScript>(_globalScripts);
|
||||
ScriptingProvider? provider = _scriptingProviders.FirstOrDefault(p => p.Id == scriptConfiguration.ScriptingProviderId);
|
||||
if (provider == null)
|
||||
throw new ArtemisCoreException($"Can't create script instance as there is no matching scripting provider found for the script ({scriptConfiguration.ScriptingProviderId}).");
|
||||
|
||||
// No need to sub to Deactivated, scripts will deactivate themselves
|
||||
profileService.ProfileActivated += ProfileServiceOnProfileActivated;
|
||||
script = (GlobalScript) provider.Plugin.Kernel!.Get(
|
||||
provider.GlobalScriptType,
|
||||
CreateScriptConstructorArgument(provider.GlobalScriptType, scriptConfiguration)
|
||||
);
|
||||
|
||||
foreach (ProfileConfiguration profileConfiguration in _profileService.ProfileConfigurations)
|
||||
script.ScriptingProvider = provider;
|
||||
script.ScriptingService = this;
|
||||
scriptConfiguration.Script = script;
|
||||
provider.InternalScripts.Add(script);
|
||||
|
||||
return script;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
script?.Dispose();
|
||||
throw new ArtemisCoreException("Failed to initialize global script", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ProfileScript CreateScriptInstance(ScriptConfiguration scriptConfiguration, Profile profile)
|
||||
{
|
||||
ProfileScript? script = null;
|
||||
try
|
||||
{
|
||||
if (scriptConfiguration.Script != null)
|
||||
throw new ArtemisCoreException("The provided script configuration already has an active script");
|
||||
|
||||
ScriptingProvider? provider = _scriptingProviders.FirstOrDefault(p => p.Id == scriptConfiguration.ScriptingProviderId);
|
||||
if (provider == null)
|
||||
throw new ArtemisCoreException($"Can't create script instance as there is no matching scripting provider found for the script ({scriptConfiguration.ScriptingProviderId}).");
|
||||
|
||||
script = (ProfileScript) provider.Plugin.Kernel!.Get(
|
||||
provider.ProfileScriptType,
|
||||
CreateScriptConstructorArgument(provider.ProfileScriptType, profile),
|
||||
CreateScriptConstructorArgument(provider.ProfileScriptType, scriptConfiguration)
|
||||
);
|
||||
|
||||
script.ScriptingProvider = provider;
|
||||
scriptConfiguration.Script = script;
|
||||
provider.InternalScripts.Add(script);
|
||||
lock (profile)
|
||||
{
|
||||
if (profileConfiguration.Profile != null)
|
||||
InitializeProfileScripts(profileConfiguration.Profile);
|
||||
profile.AddScript(script);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ReadOnlyCollection<ScriptingProvider> ScriptingProviders { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ReadOnlyCollection<GlobalScript> GlobalScripts { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ProfileScript AddScript(ScriptConfiguration scriptConfiguration, Profile profile)
|
||||
{
|
||||
profile.AddScriptConfiguration(scriptConfiguration);
|
||||
return CreateScriptInstance(scriptConfiguration, profile);
|
||||
return script;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveScript(ScriptConfiguration scriptConfiguration, Profile profile)
|
||||
catch (Exception e)
|
||||
{
|
||||
profile.RemoveScriptConfiguration(scriptConfiguration);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public GlobalScript AddScript(ScriptConfiguration scriptConfiguration)
|
||||
{
|
||||
throw new NotImplementedException("Global scripts are not yet implemented.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveScript(ScriptConfiguration scriptConfiguration)
|
||||
{
|
||||
throw new NotImplementedException("Global scripts are not yet implemented.");
|
||||
}
|
||||
|
||||
private GlobalScript CreateScriptInstance(ScriptConfiguration scriptConfiguration)
|
||||
{
|
||||
GlobalScript? script = null;
|
||||
try
|
||||
// If something went wrong but the script was created, clean up as best we can
|
||||
if (script != null)
|
||||
{
|
||||
if (scriptConfiguration.Script != null)
|
||||
throw new ArtemisCoreException("The provided script configuration already has an active script");
|
||||
|
||||
ScriptingProvider? provider = _scriptingProviders.FirstOrDefault(p => p.Id == scriptConfiguration.ScriptingProviderId);
|
||||
if (provider == null)
|
||||
throw new ArtemisCoreException($"Can't create script instance as there is no matching scripting provider found for the script ({scriptConfiguration.ScriptingProviderId}).");
|
||||
|
||||
script = (GlobalScript) provider.Plugin.Kernel!.Get(
|
||||
provider.GlobalScriptType,
|
||||
CreateScriptConstructorArgument(provider.GlobalScriptType, scriptConfiguration)
|
||||
);
|
||||
|
||||
script.ScriptingProvider = provider;
|
||||
script.ScriptingService = this;
|
||||
scriptConfiguration.Script = script;
|
||||
provider.InternalScripts.Add(script);
|
||||
|
||||
return script;
|
||||
if (profile.Scripts.Contains(script))
|
||||
profile.RemoveScript(script);
|
||||
else
|
||||
script.Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
script?.Dispose();
|
||||
throw new ArtemisCoreException("Failed to initialize global script", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ProfileScript CreateScriptInstance(ScriptConfiguration scriptConfiguration, Profile profile)
|
||||
throw new ArtemisCoreException("Failed to initialize profile script", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ConstructorArgument CreateScriptConstructorArgument(Type scriptType, object value)
|
||||
{
|
||||
// Limit to one constructor, there's no need to have more and it complicates things anyway
|
||||
ConstructorInfo[] constructors = scriptType.GetConstructors();
|
||||
if (constructors.Length != 1)
|
||||
throw new ArtemisCoreException("Scripts must have exactly one constructor");
|
||||
|
||||
// Find the ScriptConfiguration parameter, it is required by the base constructor so its there for sure
|
||||
ParameterInfo configurationParameter = constructors.First().GetParameters().First(p => value.GetType().IsAssignableFrom(p.ParameterType));
|
||||
|
||||
if (configurationParameter.Name == null)
|
||||
throw new ArtemisCoreException($"Couldn't find a valid constructor argument on {scriptType.Name} with type {value.GetType().Name}");
|
||||
return new ConstructorArgument(configurationParameter.Name, value);
|
||||
}
|
||||
|
||||
private void InitializeProfileScripts(Profile profile)
|
||||
{
|
||||
// Initialize the scripts on the profile
|
||||
foreach (ScriptConfiguration scriptConfiguration in profile.ScriptConfigurations.Where(c => c.Script == null && _scriptingProviders.Any(p => p.Id == c.ScriptingProviderId)))
|
||||
CreateScriptInstance(scriptConfiguration, profile);
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginFeatureToggled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
_scriptingProviders.Clear();
|
||||
_scriptingProviders.AddRange(_pluginManagementService.GetFeaturesOfType<ScriptingProvider>());
|
||||
|
||||
foreach (ProfileConfiguration profileConfiguration in _profileService.ProfileConfigurations)
|
||||
{
|
||||
ProfileScript? script = null;
|
||||
try
|
||||
{
|
||||
if (scriptConfiguration.Script != null)
|
||||
throw new ArtemisCoreException("The provided script configuration already has an active script");
|
||||
|
||||
ScriptingProvider? provider = _scriptingProviders.FirstOrDefault(p => p.Id == scriptConfiguration.ScriptingProviderId);
|
||||
if (provider == null)
|
||||
throw new ArtemisCoreException($"Can't create script instance as there is no matching scripting provider found for the script ({scriptConfiguration.ScriptingProviderId}).");
|
||||
|
||||
script = (ProfileScript) provider.Plugin.Kernel!.Get(
|
||||
provider.ProfileScriptType,
|
||||
CreateScriptConstructorArgument(provider.ProfileScriptType, profile),
|
||||
CreateScriptConstructorArgument(provider.ProfileScriptType, scriptConfiguration)
|
||||
);
|
||||
|
||||
script.ScriptingProvider = provider;
|
||||
scriptConfiguration.Script = script;
|
||||
provider.InternalScripts.Add(script);
|
||||
lock (profile)
|
||||
{
|
||||
profile.AddScript(script);
|
||||
}
|
||||
|
||||
return script;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// If something went wrong but the script was created, clean up as best we can
|
||||
if (script != null)
|
||||
{
|
||||
if (profile.Scripts.Contains(script))
|
||||
profile.RemoveScript(script);
|
||||
else
|
||||
script.Dispose();
|
||||
}
|
||||
|
||||
throw new ArtemisCoreException("Failed to initialize profile script", e);
|
||||
}
|
||||
if (profileConfiguration.Profile != null)
|
||||
InitializeProfileScripts(profileConfiguration.Profile);
|
||||
}
|
||||
|
||||
private ConstructorArgument CreateScriptConstructorArgument(Type scriptType, object value)
|
||||
{
|
||||
// Limit to one constructor, there's no need to have more and it complicates things anyway
|
||||
ConstructorInfo[] constructors = scriptType.GetConstructors();
|
||||
if (constructors.Length != 1)
|
||||
throw new ArtemisCoreException("Scripts must have exactly one constructor");
|
||||
}
|
||||
|
||||
// Find the ScriptConfiguration parameter, it is required by the base constructor so its there for sure
|
||||
ParameterInfo configurationParameter = constructors.First().GetParameters().First(p => value.GetType().IsAssignableFrom(p.ParameterType));
|
||||
private void ProfileServiceOnProfileActivated(object? sender, ProfileConfigurationEventArgs e)
|
||||
{
|
||||
if (e.ProfileConfiguration.Profile != null)
|
||||
InitializeProfileScripts(e.ProfileConfiguration.Profile);
|
||||
}
|
||||
|
||||
if (configurationParameter.Name == null)
|
||||
throw new ArtemisCoreException($"Couldn't find a valid constructor argument on {scriptType.Name} with type {value.GetType().Name}");
|
||||
return new ConstructorArgument(configurationParameter.Name, value);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public ReadOnlyCollection<ScriptingProvider> ScriptingProviders { get; }
|
||||
|
||||
private void InitializeProfileScripts(Profile profile)
|
||||
{
|
||||
// Initialize the scripts on the profile
|
||||
foreach (ScriptConfiguration scriptConfiguration in profile.ScriptConfigurations.Where(c => c.Script == null && _scriptingProviders.Any(p => p.Id == c.ScriptingProviderId)))
|
||||
CreateScriptInstance(scriptConfiguration, profile);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public ReadOnlyCollection<GlobalScript> GlobalScripts { get; }
|
||||
|
||||
#region Event handlers
|
||||
/// <inheritdoc />
|
||||
public ProfileScript AddScript(ScriptConfiguration scriptConfiguration, Profile profile)
|
||||
{
|
||||
profile.AddScriptConfiguration(scriptConfiguration);
|
||||
return CreateScriptInstance(scriptConfiguration, profile);
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginFeatureToggled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
_scriptingProviders.Clear();
|
||||
_scriptingProviders.AddRange(_pluginManagementService.GetFeaturesOfType<ScriptingProvider>());
|
||||
/// <inheritdoc />
|
||||
public void RemoveScript(ScriptConfiguration scriptConfiguration, Profile profile)
|
||||
{
|
||||
profile.RemoveScriptConfiguration(scriptConfiguration);
|
||||
}
|
||||
|
||||
foreach (ProfileConfiguration profileConfiguration in _profileService.ProfileConfigurations)
|
||||
{
|
||||
if (profileConfiguration.Profile != null)
|
||||
InitializeProfileScripts(profileConfiguration.Profile);
|
||||
}
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public GlobalScript AddScript(ScriptConfiguration scriptConfiguration)
|
||||
{
|
||||
throw new NotImplementedException("Global scripts are not yet implemented.");
|
||||
}
|
||||
|
||||
private void ProfileServiceOnProfileActivated(object? sender, ProfileConfigurationEventArgs e)
|
||||
{
|
||||
if (e.ProfileConfiguration.Profile != null)
|
||||
InitializeProfileScripts(e.ProfileConfiguration.Profile);
|
||||
}
|
||||
|
||||
#endregion
|
||||
/// <inheritdoc />
|
||||
public void RemoveScript(ScriptConfiguration scriptConfiguration)
|
||||
{
|
||||
throw new NotImplementedException("Global scripts are not yet implemented.");
|
||||
}
|
||||
}
|
||||
@@ -1,47 +1,46 @@
|
||||
using Artemis.Storage.Repositories.Interfaces;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <inheritdoc />
|
||||
internal class SettingsService : ISettingsService
|
||||
{
|
||||
/// <inheritdoc />
|
||||
internal class SettingsService : ISettingsService
|
||||
private readonly PluginSettings _pluginSettings;
|
||||
|
||||
public SettingsService(IPluginRepository pluginRepository)
|
||||
{
|
||||
private readonly PluginSettings _pluginSettings;
|
||||
|
||||
public SettingsService(IPluginRepository pluginRepository)
|
||||
{
|
||||
_pluginSettings = new PluginSettings(Constants.CorePlugin, pluginRepository);
|
||||
}
|
||||
|
||||
public PluginSetting<T> GetSetting<T>(string name, T? defaultValue = default)
|
||||
{
|
||||
return _pluginSettings.GetSetting(name, defaultValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveAllSettings()
|
||||
{
|
||||
_pluginSettings.SaveAllSettings();
|
||||
}
|
||||
_pluginSettings = new PluginSettings(Constants.CorePlugin, pluginRepository);
|
||||
}
|
||||
|
||||
public PluginSetting<T> GetSetting<T>(string name, T? defaultValue = default)
|
||||
{
|
||||
return _pluginSettings.GetSetting(name, defaultValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveAllSettings()
|
||||
{
|
||||
_pluginSettings.SaveAllSettings();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>A wrapper around plugin settings for miscellaneous use outside plugins</para>
|
||||
/// <para>Do not inject into a plugin, for plugins inject <see cref="PluginSettings" /> instead.</para>
|
||||
/// </summary>
|
||||
public interface ISettingsService : IProtectedArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the setting with the provided name. If the setting does not exist yet, it is created.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the setting, can be any serializable type</typeparam>
|
||||
/// <param name="name">The name of the setting</param>
|
||||
/// <param name="defaultValue">The default value to use if the setting does not exist yet</param>
|
||||
/// <returns></returns>
|
||||
PluginSetting<T> GetSetting<T>(string name, T? defaultValue = default);
|
||||
|
||||
/// <summary>
|
||||
/// <para>A wrapper around plugin settings for miscellaneous use outside plugins</para>
|
||||
/// <para>Do not inject into a plugin, for plugins inject <see cref="PluginSettings" /> instead.</para>
|
||||
/// Saves all settings, obviously
|
||||
/// </summary>
|
||||
public interface ISettingsService : IProtectedArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the setting with the provided name. If the setting does not exist yet, it is created.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the setting, can be any serializable type</typeparam>
|
||||
/// <param name="name">The name of the setting</param>
|
||||
/// <param name="defaultValue">The default value to use if the setting does not exist yet</param>
|
||||
/// <returns></returns>
|
||||
PluginSetting<T> GetSetting<T>(string name, T? defaultValue = default);
|
||||
|
||||
/// <summary>
|
||||
/// Saves all settings, obviously
|
||||
/// </summary>
|
||||
void SaveAllSettings();
|
||||
}
|
||||
void SaveAllSettings();
|
||||
}
|
||||
@@ -3,159 +3,158 @@ using System.Collections.ObjectModel;
|
||||
using Newtonsoft.Json;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to profile storage and is responsible for activating default profiles
|
||||
/// </summary>
|
||||
public interface IProfileService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to profile storage and is responsible for activating default profiles
|
||||
/// Gets the JSON serializer settings used to import/export profiles
|
||||
/// </summary>
|
||||
public interface IProfileService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the JSON serializer settings used to import/export profiles
|
||||
/// </summary>
|
||||
public static JsonSerializerSettings ExportSettings { get; } = new() {TypeNameHandling = TypeNameHandling.All, Formatting = Formatting.Indented};
|
||||
public static JsonSerializerSettings ExportSettings { get; } = new() {TypeNameHandling = TypeNameHandling.All, Formatting = Formatting.Indented};
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection containing all the profile categories
|
||||
/// </summary>
|
||||
ReadOnlyCollection<ProfileCategory> ProfileCategories { get; }
|
||||
/// <summary>
|
||||
/// Gets a read only collection containing all the profile categories
|
||||
/// </summary>
|
||||
ReadOnlyCollection<ProfileCategory> ProfileCategories { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection containing all the profile configurations
|
||||
/// </summary>
|
||||
ReadOnlyCollection<ProfileConfiguration> ProfileConfigurations { get; }
|
||||
/// <summary>
|
||||
/// Gets a read only collection containing all the profile configurations
|
||||
/// </summary>
|
||||
ReadOnlyCollection<ProfileConfiguration> ProfileConfigurations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether hotkeys are enabled
|
||||
/// </summary>
|
||||
bool HotkeysEnabled { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether hotkeys are enabled
|
||||
/// </summary>
|
||||
bool HotkeysEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether rendering should only be done for profiles being edited
|
||||
/// </summary>
|
||||
bool RenderForEditor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the profile element to focus on while rendering for the editor
|
||||
/// </summary>
|
||||
ProfileElement? EditorFocus { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether rendering should only be done for profiles being edited
|
||||
/// </summary>
|
||||
bool RenderForEditor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Activates the profile of the given <see cref="ProfileConfiguration" /> with the currently active surface
|
||||
/// </summary>
|
||||
/// <param name="profileConfiguration">The profile configuration of the profile to activate</param>
|
||||
Profile ActivateProfile(ProfileConfiguration profileConfiguration);
|
||||
/// <summary>
|
||||
/// Gets or sets the profile element to focus on while rendering for the editor
|
||||
/// </summary>
|
||||
ProfileElement? EditorFocus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates the profile of the given <see cref="ProfileConfiguration" /> with the currently active surface
|
||||
/// </summary>
|
||||
/// <param name="profileConfiguration">The profile configuration of the profile to activate</param>
|
||||
void DeactivateProfile(ProfileConfiguration profileConfiguration);
|
||||
/// <summary>
|
||||
/// Activates the profile of the given <see cref="ProfileConfiguration" /> with the currently active surface
|
||||
/// </summary>
|
||||
/// <param name="profileConfiguration">The profile configuration of the profile to activate</param>
|
||||
Profile ActivateProfile(ProfileConfiguration profileConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Permanently deletes the profile of the given <see cref="ProfileConfiguration" />
|
||||
/// </summary>
|
||||
/// <param name="profileConfiguration">The profile configuration of the profile to delete</param>
|
||||
void DeleteProfile(ProfileConfiguration profileConfiguration);
|
||||
/// <summary>
|
||||
/// Deactivates the profile of the given <see cref="ProfileConfiguration" /> with the currently active surface
|
||||
/// </summary>
|
||||
/// <param name="profileConfiguration">The profile configuration of the profile to activate</param>
|
||||
void DeactivateProfile(ProfileConfiguration profileConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the provided <see cref="ProfileCategory" /> and it's <see cref="ProfileConfiguration" />s but not the
|
||||
/// <see cref="Profile" />s themselves
|
||||
/// </summary>
|
||||
/// <param name="profileCategory">The profile category to update</param>
|
||||
void SaveProfileCategory(ProfileCategory profileCategory);
|
||||
/// <summary>
|
||||
/// Permanently deletes the profile of the given <see cref="ProfileConfiguration" />
|
||||
/// </summary>
|
||||
/// <param name="profileConfiguration">The profile configuration of the profile to delete</param>
|
||||
void DeleteProfile(ProfileConfiguration profileConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new profile category and saves it to persistent storage
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the new profile category, must be unique</param>
|
||||
/// <returns>The newly created profile category</returns>
|
||||
ProfileCategory CreateProfileCategory(string name);
|
||||
/// <summary>
|
||||
/// Saves the provided <see cref="ProfileCategory" /> and it's <see cref="ProfileConfiguration" />s but not the
|
||||
/// <see cref="Profile" />s themselves
|
||||
/// </summary>
|
||||
/// <param name="profileCategory">The profile category to update</param>
|
||||
void SaveProfileCategory(ProfileCategory profileCategory);
|
||||
|
||||
/// <summary>
|
||||
/// Permanently deletes the provided profile category
|
||||
/// </summary>
|
||||
void DeleteProfileCategory(ProfileCategory profileCategory);
|
||||
/// <summary>
|
||||
/// Creates a new profile category and saves it to persistent storage
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the new profile category, must be unique</param>
|
||||
/// <returns>The newly created profile category</returns>
|
||||
ProfileCategory CreateProfileCategory(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new profile configuration and adds it to the provided <see cref="ProfileCategory" />
|
||||
/// </summary>
|
||||
/// <param name="category">The profile category to add the profile to</param>
|
||||
/// <param name="name">The name of the new profile configuration</param>
|
||||
/// <param name="icon">The icon of the new profile configuration</param>
|
||||
/// <returns>The newly created profile configuration</returns>
|
||||
ProfileConfiguration CreateProfileConfiguration(ProfileCategory category, string name, string icon);
|
||||
/// <summary>
|
||||
/// Permanently deletes the provided profile category
|
||||
/// </summary>
|
||||
void DeleteProfileCategory(ProfileCategory profileCategory);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the provided profile configuration from the <see cref="ProfileCategory" />
|
||||
/// </summary>
|
||||
/// <param name="profileConfiguration"></param>
|
||||
void RemoveProfileConfiguration(ProfileConfiguration profileConfiguration);
|
||||
/// <summary>
|
||||
/// Creates a new profile configuration and adds it to the provided <see cref="ProfileCategory" />
|
||||
/// </summary>
|
||||
/// <param name="category">The profile category to add the profile to</param>
|
||||
/// <param name="name">The name of the new profile configuration</param>
|
||||
/// <param name="icon">The icon of the new profile configuration</param>
|
||||
/// <returns>The newly created profile configuration</returns>
|
||||
ProfileConfiguration CreateProfileConfiguration(ProfileCategory category, string name, string icon);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the icon of this profile configuration if needed and puts it into <c>ProfileConfiguration.Icon.FileIcon</c>
|
||||
/// </summary>
|
||||
void LoadProfileConfigurationIcon(ProfileConfiguration profileConfiguration);
|
||||
/// <summary>
|
||||
/// Removes the provided profile configuration from the <see cref="ProfileCategory" />
|
||||
/// </summary>
|
||||
/// <param name="profileConfiguration"></param>
|
||||
void RemoveProfileConfiguration(ProfileConfiguration profileConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the current icon of this profile
|
||||
/// </summary>
|
||||
void SaveProfileConfigurationIcon(ProfileConfiguration profileConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the profile to persistent storage
|
||||
/// </summary>
|
||||
/// <param name="profile"></param>
|
||||
/// <param name="includeChildren"></param>
|
||||
void SaveProfile(Profile profile, bool includeChildren);
|
||||
|
||||
/// <summary>
|
||||
/// Exports the profile described in the given <see cref="ProfileConfiguration" /> into an export model
|
||||
/// </summary>
|
||||
/// <param name="profileConfiguration">The profile configuration of the profile to export</param>
|
||||
/// <returns>The resulting export model</returns>
|
||||
ProfileConfigurationExportModel ExportProfile(ProfileConfiguration profileConfiguration);
|
||||
/// <summary>
|
||||
/// Loads the icon of this profile configuration if needed and puts it into <c>ProfileConfiguration.Icon.FileIcon</c>
|
||||
/// </summary>
|
||||
void LoadProfileConfigurationIcon(ProfileConfiguration profileConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Imports the provided base64 encoded GZIPed JSON as a profile configuration
|
||||
/// </summary>
|
||||
/// <param name="category">The <see cref="ProfileCategory" /> in which to import the profile</param>
|
||||
/// <param name="exportModel">The model containing the profile to import</param>
|
||||
/// <param name="makeUnique">Whether or not to give the profile a new GUID, making it unique</param>
|
||||
/// <param name="markAsFreshImport">
|
||||
/// Whether or not to mark the profile as a fresh import, causing it to be adapted until
|
||||
/// any changes are made to it
|
||||
/// </param>
|
||||
/// <param name="nameAffix">Text to add after the name of the profile (separated by a dash)</param>
|
||||
/// <returns>The resulting profile configuration</returns>
|
||||
ProfileConfiguration ImportProfile(ProfileCategory category, ProfileConfigurationExportModel exportModel, bool makeUnique = true, bool markAsFreshImport = true,
|
||||
string? nameAffix = "imported");
|
||||
/// <summary>
|
||||
/// Saves the current icon of this profile
|
||||
/// </summary>
|
||||
void SaveProfileConfigurationIcon(ProfileConfiguration profileConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Adapts a given profile to the currently active devices
|
||||
/// </summary>
|
||||
/// <param name="profile">The profile to adapt</param>
|
||||
void AdaptProfile(Profile profile);
|
||||
/// <summary>
|
||||
/// Writes the profile to persistent storage
|
||||
/// </summary>
|
||||
/// <param name="profile"></param>
|
||||
/// <param name="includeChildren"></param>
|
||||
void SaveProfile(Profile profile, bool includeChildren);
|
||||
|
||||
/// <summary>
|
||||
/// Updates all currently active profiles
|
||||
/// </summary>
|
||||
void UpdateProfiles(double deltaTime);
|
||||
/// <summary>
|
||||
/// Exports the profile described in the given <see cref="ProfileConfiguration" /> into an export model
|
||||
/// </summary>
|
||||
/// <param name="profileConfiguration">The profile configuration of the profile to export</param>
|
||||
/// <returns>The resulting export model</returns>
|
||||
ProfileConfigurationExportModel ExportProfile(ProfileConfiguration profileConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Renders all currently active profiles
|
||||
/// </summary>
|
||||
/// <param name="canvas"></param>
|
||||
void RenderProfiles(SKCanvas canvas);
|
||||
/// <summary>
|
||||
/// Imports the provided base64 encoded GZIPed JSON as a profile configuration
|
||||
/// </summary>
|
||||
/// <param name="category">The <see cref="ProfileCategory" /> in which to import the profile</param>
|
||||
/// <param name="exportModel">The model containing the profile to import</param>
|
||||
/// <param name="makeUnique">Whether or not to give the profile a new GUID, making it unique</param>
|
||||
/// <param name="markAsFreshImport">
|
||||
/// Whether or not to mark the profile as a fresh import, causing it to be adapted until
|
||||
/// any changes are made to it
|
||||
/// </param>
|
||||
/// <param name="nameAffix">Text to add after the name of the profile (separated by a dash)</param>
|
||||
/// <returns>The resulting profile configuration</returns>
|
||||
ProfileConfiguration ImportProfile(ProfileCategory category, ProfileConfigurationExportModel exportModel, bool makeUnique = true, bool markAsFreshImport = true,
|
||||
string? nameAffix = "imported");
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a profile has been activated
|
||||
/// </summary>
|
||||
public event EventHandler<ProfileConfigurationEventArgs>? ProfileActivated;
|
||||
/// <summary>
|
||||
/// Adapts a given profile to the currently active devices
|
||||
/// </summary>
|
||||
/// <param name="profile">The profile to adapt</param>
|
||||
void AdaptProfile(Profile profile);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a profile has been deactivated
|
||||
/// </summary>
|
||||
public event EventHandler<ProfileConfigurationEventArgs>? ProfileDeactivated;
|
||||
}
|
||||
/// <summary>
|
||||
/// Updates all currently active profiles
|
||||
/// </summary>
|
||||
void UpdateProfiles(double deltaTime);
|
||||
|
||||
/// <summary>
|
||||
/// Renders all currently active profiles
|
||||
/// </summary>
|
||||
/// <param name="canvas"></param>
|
||||
void RenderProfiles(SKCanvas canvas);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a profile has been activated
|
||||
/// </summary>
|
||||
public event EventHandler<ProfileConfigurationEventArgs>? ProfileActivated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a profile has been deactivated
|
||||
/// </summary>
|
||||
public event EventHandler<ProfileConfigurationEventArgs>? ProfileDeactivated;
|
||||
}
|
||||
@@ -2,118 +2,113 @@
|
||||
using System.Linq;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.Core.Services.Models
|
||||
namespace Artemis.Core.Services.Models;
|
||||
|
||||
internal class SurfaceArrangement
|
||||
{
|
||||
internal class SurfaceArrangement
|
||||
public SurfaceArrangement()
|
||||
{
|
||||
public SurfaceArrangement()
|
||||
Types = new List<SurfaceArrangementType>();
|
||||
ArrangedDevices = new List<ArtemisDevice>();
|
||||
}
|
||||
|
||||
public List<SurfaceArrangementType> Types { get; }
|
||||
public List<ArtemisDevice> ArrangedDevices { get; }
|
||||
|
||||
public void Arrange(List<ArtemisDevice> devices)
|
||||
{
|
||||
ArrangedDevices.Clear();
|
||||
|
||||
// Not much to do here
|
||||
if (!devices.Any())
|
||||
return;
|
||||
|
||||
foreach (ArtemisDevice surfaceDevice in devices)
|
||||
{
|
||||
Types = new List<SurfaceArrangementType>();
|
||||
ArrangedDevices = new List<ArtemisDevice>();
|
||||
surfaceDevice.X = 0;
|
||||
surfaceDevice.Y = 0;
|
||||
surfaceDevice.ApplyToRgbDevice();
|
||||
}
|
||||
|
||||
public List<SurfaceArrangementType> Types { get; }
|
||||
public List<ArtemisDevice> ArrangedDevices { get; }
|
||||
|
||||
internal static SurfaceArrangement GetDefaultArrangement()
|
||||
{
|
||||
SurfaceArrangement arrangement = new();
|
||||
|
||||
SurfaceArrangementType keypad = arrangement.AddType(RGBDeviceType.Keypad, 1);
|
||||
keypad.AddConfiguration(new SurfaceArrangementConfiguration(null, HorizontalArrangementPosition.Equal, VerticalArrangementPosition.Equal, 20));
|
||||
|
||||
SurfaceArrangementType keyboard = arrangement.AddType(RGBDeviceType.Keyboard, 1);
|
||||
keyboard.AddConfiguration(new SurfaceArrangementConfiguration(keypad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 20));
|
||||
|
||||
SurfaceArrangementType mousepad = arrangement.AddType(RGBDeviceType.Mousepad, 1);
|
||||
mousepad.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10));
|
||||
|
||||
SurfaceArrangementType mouse = arrangement.AddType(RGBDeviceType.Mouse, 2);
|
||||
mouse.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
|
||||
mouse.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Center, 100));
|
||||
|
||||
SurfaceArrangementType headset = arrangement.AddType(RGBDeviceType.Headset, 1);
|
||||
headset.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Bottom, 100));
|
||||
|
||||
SurfaceArrangementType headsetStand = arrangement.AddType(RGBDeviceType.HeadsetStand, 1);
|
||||
headsetStand.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Top, 100));
|
||||
headsetStand.AddConfiguration(new SurfaceArrangementConfiguration(mouse, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Top, 100));
|
||||
headsetStand.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Top, 100));
|
||||
|
||||
SurfaceArrangementType mainboard = arrangement.AddType(RGBDeviceType.Mainboard, 1);
|
||||
mainboard.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 500));
|
||||
mainboard.AddConfiguration(new SurfaceArrangementConfiguration(mouse, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 500));
|
||||
mainboard.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 500));
|
||||
|
||||
SurfaceArrangementType cooler = arrangement.AddType(RGBDeviceType.Cooler, 2);
|
||||
cooler.AddConfiguration(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
|
||||
|
||||
SurfaceArrangementType dram = arrangement.AddType(RGBDeviceType.DRAM, 2);
|
||||
dram.AddConfiguration(new SurfaceArrangementConfiguration(cooler, HorizontalArrangementPosition.Left, VerticalArrangementPosition.Equal, 10));
|
||||
dram.AddConfiguration(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
|
||||
|
||||
SurfaceArrangementType graphicsCard = arrangement.AddType(RGBDeviceType.GraphicsCard, 2);
|
||||
graphicsCard.AddConfiguration(new SurfaceArrangementConfiguration(cooler, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 10));
|
||||
graphicsCard.AddConfiguration(new SurfaceArrangementConfiguration(dram, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 10));
|
||||
graphicsCard.AddConfiguration(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
|
||||
|
||||
SurfaceArrangementType fan = arrangement.AddType(RGBDeviceType.Fan, 2);
|
||||
fan.AddConfiguration(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 100));
|
||||
|
||||
SurfaceArrangementType ledStripe = arrangement.AddType(RGBDeviceType.LedStripe, 2);
|
||||
ledStripe.AddConfiguration(new SurfaceArrangementConfiguration(fan, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 100));
|
||||
|
||||
arrangement.AddType(RGBDeviceType.Speaker, 1);
|
||||
arrangement.AddType(RGBDeviceType.None, 1);
|
||||
|
||||
return arrangement;
|
||||
}
|
||||
|
||||
private SurfaceArrangementType AddType(RGBDeviceType type, int zIndex)
|
||||
{
|
||||
SurfaceArrangementType surfaceArrangementType = new(this, type, zIndex);
|
||||
Types.Add(surfaceArrangementType);
|
||||
return surfaceArrangementType;
|
||||
}
|
||||
|
||||
public void Arrange(List<ArtemisDevice> devices)
|
||||
{
|
||||
ArrangedDevices.Clear();
|
||||
|
||||
// Not much to do here
|
||||
if (!devices.Any())
|
||||
return;
|
||||
foreach (SurfaceArrangementType surfaceArrangementType in Types)
|
||||
surfaceArrangementType.Arrange(devices);
|
||||
|
||||
// See if we need to move the surface to keep X and Y values positive
|
||||
float x = devices.Min(d => d.RgbDevice.Location.X);
|
||||
float y = devices.Min(d => d.RgbDevice.Location.Y);
|
||||
if (x < 0)
|
||||
foreach (ArtemisDevice surfaceDevice in devices)
|
||||
{
|
||||
surfaceDevice.X = 0;
|
||||
surfaceDevice.Y = 0;
|
||||
surfaceDevice.X += x * -1;
|
||||
surfaceDevice.ApplyToRgbDevice();
|
||||
}
|
||||
|
||||
foreach (SurfaceArrangementType surfaceArrangementType in Types)
|
||||
surfaceArrangementType.Arrange(devices);
|
||||
|
||||
// See if we need to move the surface to keep X and Y values positive
|
||||
float x = devices.Min(d => d.RgbDevice.Location.X);
|
||||
float y = devices.Min(d => d.RgbDevice.Location.Y);
|
||||
if (x < 0)
|
||||
if (y < 0)
|
||||
foreach (ArtemisDevice surfaceDevice in devices)
|
||||
{
|
||||
foreach (ArtemisDevice surfaceDevice in devices)
|
||||
{
|
||||
surfaceDevice.X += x * -1;
|
||||
surfaceDevice.ApplyToRgbDevice();
|
||||
}
|
||||
surfaceDevice.Y += y * -1;
|
||||
surfaceDevice.ApplyToRgbDevice();
|
||||
}
|
||||
}
|
||||
|
||||
if (y < 0)
|
||||
{
|
||||
foreach (ArtemisDevice surfaceDevice in devices)
|
||||
{
|
||||
surfaceDevice.Y += y * -1;
|
||||
surfaceDevice.ApplyToRgbDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static SurfaceArrangement GetDefaultArrangement()
|
||||
{
|
||||
SurfaceArrangement arrangement = new();
|
||||
|
||||
SurfaceArrangementType keypad = arrangement.AddType(RGBDeviceType.Keypad, 1);
|
||||
keypad.AddConfiguration(new SurfaceArrangementConfiguration(null, HorizontalArrangementPosition.Equal, VerticalArrangementPosition.Equal, 20));
|
||||
|
||||
SurfaceArrangementType keyboard = arrangement.AddType(RGBDeviceType.Keyboard, 1);
|
||||
keyboard.AddConfiguration(new SurfaceArrangementConfiguration(keypad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 20));
|
||||
|
||||
SurfaceArrangementType mousepad = arrangement.AddType(RGBDeviceType.Mousepad, 1);
|
||||
mousepad.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10));
|
||||
|
||||
SurfaceArrangementType mouse = arrangement.AddType(RGBDeviceType.Mouse, 2);
|
||||
mouse.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
|
||||
mouse.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Center, 100));
|
||||
|
||||
SurfaceArrangementType headset = arrangement.AddType(RGBDeviceType.Headset, 1);
|
||||
headset.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Bottom, 100));
|
||||
|
||||
SurfaceArrangementType headsetStand = arrangement.AddType(RGBDeviceType.HeadsetStand, 1);
|
||||
headsetStand.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Top, 100));
|
||||
headsetStand.AddConfiguration(new SurfaceArrangementConfiguration(mouse, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Top, 100));
|
||||
headsetStand.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Top, 100));
|
||||
|
||||
SurfaceArrangementType mainboard = arrangement.AddType(RGBDeviceType.Mainboard, 1);
|
||||
mainboard.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 500));
|
||||
mainboard.AddConfiguration(new SurfaceArrangementConfiguration(mouse, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 500));
|
||||
mainboard.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 500));
|
||||
|
||||
SurfaceArrangementType cooler = arrangement.AddType(RGBDeviceType.Cooler, 2);
|
||||
cooler.AddConfiguration(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
|
||||
|
||||
SurfaceArrangementType dram = arrangement.AddType(RGBDeviceType.DRAM, 2);
|
||||
dram.AddConfiguration(new SurfaceArrangementConfiguration(cooler, HorizontalArrangementPosition.Left, VerticalArrangementPosition.Equal, 10));
|
||||
dram.AddConfiguration(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
|
||||
|
||||
SurfaceArrangementType graphicsCard = arrangement.AddType(RGBDeviceType.GraphicsCard, 2);
|
||||
graphicsCard.AddConfiguration(new SurfaceArrangementConfiguration(cooler, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 10));
|
||||
graphicsCard.AddConfiguration(new SurfaceArrangementConfiguration(dram, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 10));
|
||||
graphicsCard.AddConfiguration(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
|
||||
|
||||
SurfaceArrangementType fan = arrangement.AddType(RGBDeviceType.Fan, 2);
|
||||
fan.AddConfiguration(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 100));
|
||||
|
||||
SurfaceArrangementType ledStripe = arrangement.AddType(RGBDeviceType.LedStripe, 2);
|
||||
ledStripe.AddConfiguration(new SurfaceArrangementConfiguration(fan, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 100));
|
||||
|
||||
arrangement.AddType(RGBDeviceType.Speaker, 1);
|
||||
arrangement.AddType(RGBDeviceType.None, 1);
|
||||
|
||||
return arrangement;
|
||||
}
|
||||
|
||||
private SurfaceArrangementType AddType(RGBDeviceType type, int zIndex)
|
||||
{
|
||||
SurfaceArrangementType surfaceArrangementType = new(this, type, zIndex);
|
||||
Types.Add(surfaceArrangementType);
|
||||
return surfaceArrangementType;
|
||||
}
|
||||
}
|
||||
@@ -2,91 +2,90 @@
|
||||
using System.Linq;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.Core.Services.Models
|
||||
namespace Artemis.Core.Services.Models;
|
||||
|
||||
internal class SurfaceArrangementConfiguration
|
||||
{
|
||||
internal class SurfaceArrangementConfiguration
|
||||
public SurfaceArrangementConfiguration(SurfaceArrangementType? anchor, HorizontalArrangementPosition horizontalPosition, VerticalArrangementPosition verticalPosition,
|
||||
int margin)
|
||||
{
|
||||
public SurfaceArrangementConfiguration(SurfaceArrangementType? anchor, HorizontalArrangementPosition horizontalPosition, VerticalArrangementPosition verticalPosition,
|
||||
int margin)
|
||||
Anchor = anchor;
|
||||
HorizontalPosition = horizontalPosition;
|
||||
VerticalPosition = verticalPosition;
|
||||
|
||||
MarginLeft = margin;
|
||||
MarginTop = margin;
|
||||
MarginRight = margin;
|
||||
MarginBottom = margin;
|
||||
|
||||
SurfaceArrangement = null!;
|
||||
}
|
||||
|
||||
|
||||
public SurfaceArrangementType? Anchor { get; }
|
||||
public HorizontalArrangementPosition HorizontalPosition { get; }
|
||||
public VerticalArrangementPosition VerticalPosition { get; }
|
||||
|
||||
public int MarginLeft { get; }
|
||||
public int MarginTop { get; }
|
||||
public int MarginRight { get; }
|
||||
public int MarginBottom { get; }
|
||||
public SurfaceArrangement SurfaceArrangement { get; set; }
|
||||
|
||||
public bool Apply(List<ArtemisDevice> devices)
|
||||
{
|
||||
if (Anchor != null && !Anchor.HasDevices(devices))
|
||||
return false;
|
||||
|
||||
// Start at the edge of the anchor, if there is no anchor start at any device
|
||||
Point startPoint = Anchor?.GetEdge(HorizontalPosition, VerticalPosition) ??
|
||||
new SurfaceArrangementType(SurfaceArrangement, RGBDeviceType.All, 1).GetEdge(HorizontalPosition, VerticalPosition);
|
||||
|
||||
// Stack multiple devices of the same type vertically if they are wider than they are tall
|
||||
bool stackVertically = devices.Average(d => d.RgbDevice.Size.Width) >= devices.Average(d => d.RgbDevice.Size.Height);
|
||||
|
||||
ArtemisDevice? previous = null;
|
||||
foreach (ArtemisDevice artemisDevice in devices)
|
||||
{
|
||||
Anchor = anchor;
|
||||
HorizontalPosition = horizontalPosition;
|
||||
VerticalPosition = verticalPosition;
|
||||
|
||||
MarginLeft = margin;
|
||||
MarginTop = margin;
|
||||
MarginRight = margin;
|
||||
MarginBottom = margin;
|
||||
|
||||
SurfaceArrangement = null!;
|
||||
}
|
||||
|
||||
|
||||
public SurfaceArrangementType? Anchor { get; }
|
||||
public HorizontalArrangementPosition HorizontalPosition { get; }
|
||||
public VerticalArrangementPosition VerticalPosition { get; }
|
||||
|
||||
public int MarginLeft { get; }
|
||||
public int MarginTop { get; }
|
||||
public int MarginRight { get; }
|
||||
public int MarginBottom { get; }
|
||||
public SurfaceArrangement SurfaceArrangement { get; set; }
|
||||
|
||||
public bool Apply(List<ArtemisDevice> devices)
|
||||
{
|
||||
if (Anchor != null && !Anchor.HasDevices(devices))
|
||||
return false;
|
||||
|
||||
// Start at the edge of the anchor, if there is no anchor start at any device
|
||||
Point startPoint = Anchor?.GetEdge(HorizontalPosition, VerticalPosition) ??
|
||||
new SurfaceArrangementType(SurfaceArrangement, RGBDeviceType.All, 1).GetEdge(HorizontalPosition, VerticalPosition);
|
||||
|
||||
// Stack multiple devices of the same type vertically if they are wider than they are tall
|
||||
bool stackVertically = devices.Average(d => d.RgbDevice.Size.Width) >= devices.Average(d => d.RgbDevice.Size.Height);
|
||||
|
||||
ArtemisDevice? previous = null;
|
||||
foreach (ArtemisDevice artemisDevice in devices)
|
||||
if (previous != null)
|
||||
{
|
||||
if (previous != null)
|
||||
if (stackVertically)
|
||||
{
|
||||
if (stackVertically)
|
||||
{
|
||||
artemisDevice.X = previous.X;
|
||||
artemisDevice.Y = previous.RgbDevice.Location.Y + previous.RgbDevice.Size.Height + MarginTop / 2f;
|
||||
}
|
||||
else
|
||||
{
|
||||
artemisDevice.X = previous.RgbDevice.Location.X + previous.RgbDevice.Size.Width + MarginLeft / 2f;
|
||||
artemisDevice.Y = previous.Y;
|
||||
}
|
||||
artemisDevice.X = previous.X;
|
||||
artemisDevice.Y = previous.RgbDevice.Location.Y + previous.RgbDevice.Size.Height + MarginTop / 2f;
|
||||
}
|
||||
else
|
||||
{
|
||||
artemisDevice.X = HorizontalPosition switch
|
||||
{
|
||||
HorizontalArrangementPosition.Left => startPoint.X - artemisDevice.RgbDevice.Size.Width - MarginRight,
|
||||
HorizontalArrangementPosition.Right => startPoint.X + MarginLeft,
|
||||
HorizontalArrangementPosition.Center => startPoint.X - artemisDevice.RgbDevice.Size.Width / 2f,
|
||||
HorizontalArrangementPosition.Equal => startPoint.X,
|
||||
_ => artemisDevice.X
|
||||
};
|
||||
artemisDevice.Y = VerticalPosition switch
|
||||
{
|
||||
VerticalArrangementPosition.Top => startPoint.Y - artemisDevice.RgbDevice.Size.Height - MarginBottom,
|
||||
VerticalArrangementPosition.Bottom => startPoint.Y + MarginTop,
|
||||
VerticalArrangementPosition.Center => startPoint.Y - artemisDevice.RgbDevice.Size.Height / 2f,
|
||||
VerticalArrangementPosition.Equal => startPoint.Y,
|
||||
_ => artemisDevice.X
|
||||
};
|
||||
artemisDevice.X = previous.RgbDevice.Location.X + previous.RgbDevice.Size.Width + MarginLeft / 2f;
|
||||
artemisDevice.Y = previous.Y;
|
||||
}
|
||||
|
||||
artemisDevice.ApplyToRgbDevice();
|
||||
previous = artemisDevice;
|
||||
|
||||
SurfaceArrangement.ArrangedDevices.Add(artemisDevice);
|
||||
}
|
||||
else
|
||||
{
|
||||
artemisDevice.X = HorizontalPosition switch
|
||||
{
|
||||
HorizontalArrangementPosition.Left => startPoint.X - artemisDevice.RgbDevice.Size.Width - MarginRight,
|
||||
HorizontalArrangementPosition.Right => startPoint.X + MarginLeft,
|
||||
HorizontalArrangementPosition.Center => startPoint.X - artemisDevice.RgbDevice.Size.Width / 2f,
|
||||
HorizontalArrangementPosition.Equal => startPoint.X,
|
||||
_ => artemisDevice.X
|
||||
};
|
||||
artemisDevice.Y = VerticalPosition switch
|
||||
{
|
||||
VerticalArrangementPosition.Top => startPoint.Y - artemisDevice.RgbDevice.Size.Height - MarginBottom,
|
||||
VerticalArrangementPosition.Bottom => startPoint.Y + MarginTop,
|
||||
VerticalArrangementPosition.Center => startPoint.Y - artemisDevice.RgbDevice.Size.Height / 2f,
|
||||
VerticalArrangementPosition.Equal => startPoint.Y,
|
||||
_ => artemisDevice.X
|
||||
};
|
||||
}
|
||||
|
||||
return true;
|
||||
artemisDevice.ApplyToRgbDevice();
|
||||
previous = artemisDevice;
|
||||
|
||||
SurfaceArrangement.ArrangedDevices.Add(artemisDevice);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -3,105 +3,104 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.Core.Services.Models
|
||||
namespace Artemis.Core.Services.Models;
|
||||
|
||||
internal class SurfaceArrangementType
|
||||
{
|
||||
internal class SurfaceArrangementType
|
||||
public SurfaceArrangementType(SurfaceArrangement surfaceArrangement, RGBDeviceType deviceType, int zIndex)
|
||||
{
|
||||
public SurfaceArrangementType(SurfaceArrangement surfaceArrangement, RGBDeviceType deviceType, int zIndex)
|
||||
{
|
||||
SurfaceArrangement = surfaceArrangement;
|
||||
DeviceType = deviceType;
|
||||
ZIndex = zIndex;
|
||||
Configurations = new List<SurfaceArrangementConfiguration>();
|
||||
}
|
||||
SurfaceArrangement = surfaceArrangement;
|
||||
DeviceType = deviceType;
|
||||
ZIndex = zIndex;
|
||||
Configurations = new List<SurfaceArrangementConfiguration>();
|
||||
}
|
||||
|
||||
public SurfaceArrangement SurfaceArrangement { get; }
|
||||
public RGBDeviceType DeviceType { get; }
|
||||
public int ZIndex { get; }
|
||||
public List<SurfaceArrangementConfiguration> Configurations { get; }
|
||||
public SurfaceArrangementConfiguration? AppliedConfiguration { get; set; }
|
||||
public SurfaceArrangement SurfaceArrangement { get; }
|
||||
public RGBDeviceType DeviceType { get; }
|
||||
public int ZIndex { get; }
|
||||
public List<SurfaceArrangementConfiguration> Configurations { get; }
|
||||
public SurfaceArrangementConfiguration? AppliedConfiguration { get; set; }
|
||||
|
||||
public bool HasDevices(List<ArtemisDevice> devices)
|
||||
{
|
||||
return devices.Any(d => d.DeviceType == DeviceType);
|
||||
}
|
||||
public bool HasDevices(List<ArtemisDevice> devices)
|
||||
{
|
||||
return devices.Any(d => d.DeviceType == DeviceType);
|
||||
}
|
||||
|
||||
public void Arrange(List<ArtemisDevice> devices)
|
||||
public void Arrange(List<ArtemisDevice> devices)
|
||||
{
|
||||
devices = devices.Where(d => d.DeviceType == DeviceType).ToList();
|
||||
if (!devices.Any())
|
||||
return;
|
||||
|
||||
AppliedConfiguration = null;
|
||||
foreach (SurfaceArrangementConfiguration configuration in Configurations)
|
||||
{
|
||||
devices = devices.Where(d => d.DeviceType == DeviceType).ToList();
|
||||
if (!devices.Any())
|
||||
bool applied = configuration.Apply(devices);
|
||||
if (applied)
|
||||
{
|
||||
AppliedConfiguration = configuration;
|
||||
foreach (ArtemisDevice artemisDevice in devices)
|
||||
artemisDevice.ZIndex = ZIndex;
|
||||
return;
|
||||
|
||||
AppliedConfiguration = null;
|
||||
foreach (SurfaceArrangementConfiguration configuration in Configurations)
|
||||
{
|
||||
bool applied = configuration.Apply(devices);
|
||||
if (applied)
|
||||
{
|
||||
AppliedConfiguration = configuration;
|
||||
foreach (ArtemisDevice artemisDevice in devices)
|
||||
artemisDevice.ZIndex = ZIndex;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing applied fall back to just basing on whatever is the furthers to the right
|
||||
SurfaceArrangementConfiguration fallback = new(
|
||||
null,
|
||||
HorizontalArrangementPosition.Right,
|
||||
VerticalArrangementPosition.Equal,
|
||||
10
|
||||
) {SurfaceArrangement = SurfaceArrangement};
|
||||
fallback.Apply(devices);
|
||||
AppliedConfiguration = fallback;
|
||||
}
|
||||
|
||||
public Point GetEdge(HorizontalArrangementPosition horizontalPosition, VerticalArrangementPosition verticalPosition)
|
||||
{
|
||||
List<ArtemisDevice> devices = SurfaceArrangement.ArrangedDevices.Where(d => d.DeviceType == DeviceType || DeviceType == RGBDeviceType.All).ToList();
|
||||
if (!devices.Any())
|
||||
return new Point();
|
||||
|
||||
float x = horizontalPosition switch
|
||||
{
|
||||
HorizontalArrangementPosition.Left => devices.Min(d => d.RgbDevice.Location.X) - (AppliedConfiguration?.MarginLeft ?? 0.0f),
|
||||
HorizontalArrangementPosition.Right => devices.Max(d => d.RgbDevice.Location.X + d.RgbDevice.Size.Width) + (AppliedConfiguration?.MarginRight ?? 0.0f),
|
||||
HorizontalArrangementPosition.Center => devices.First().RgbDevice.Boundary.Center.X,
|
||||
HorizontalArrangementPosition.Equal => devices.First().RgbDevice.Location.X,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(horizontalPosition), horizontalPosition, null)
|
||||
};
|
||||
float y = verticalPosition switch
|
||||
{
|
||||
VerticalArrangementPosition.Top => devices.Min(d => d.RgbDevice.Location.Y) - (AppliedConfiguration?.MarginTop ?? 0.0f),
|
||||
VerticalArrangementPosition.Bottom => devices.Max(d => d.RgbDevice.Location.Y + d.RgbDevice.Size.Height) + (AppliedConfiguration?.MarginBottom ?? 0.0f),
|
||||
VerticalArrangementPosition.Center => devices.First().RgbDevice.Boundary.Center.Y,
|
||||
VerticalArrangementPosition.Equal => devices.First().RgbDevice.Location.Y,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(verticalPosition), verticalPosition, null)
|
||||
};
|
||||
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
public void AddConfiguration(SurfaceArrangementConfiguration surfaceArrangementConfiguration)
|
||||
{
|
||||
surfaceArrangementConfiguration.SurfaceArrangement = SurfaceArrangement;
|
||||
Configurations.Add(surfaceArrangementConfiguration);
|
||||
}
|
||||
// If nothing applied fall back to just basing on whatever is the furthers to the right
|
||||
SurfaceArrangementConfiguration fallback = new(
|
||||
null,
|
||||
HorizontalArrangementPosition.Right,
|
||||
VerticalArrangementPosition.Equal,
|
||||
10
|
||||
) {SurfaceArrangement = SurfaceArrangement};
|
||||
fallback.Apply(devices);
|
||||
AppliedConfiguration = fallback;
|
||||
}
|
||||
|
||||
internal enum HorizontalArrangementPosition
|
||||
public Point GetEdge(HorizontalArrangementPosition horizontalPosition, VerticalArrangementPosition verticalPosition)
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Equal,
|
||||
Center
|
||||
List<ArtemisDevice> devices = SurfaceArrangement.ArrangedDevices.Where(d => d.DeviceType == DeviceType || DeviceType == RGBDeviceType.All).ToList();
|
||||
if (!devices.Any())
|
||||
return new Point();
|
||||
|
||||
float x = horizontalPosition switch
|
||||
{
|
||||
HorizontalArrangementPosition.Left => devices.Min(d => d.RgbDevice.Location.X) - (AppliedConfiguration?.MarginLeft ?? 0.0f),
|
||||
HorizontalArrangementPosition.Right => devices.Max(d => d.RgbDevice.Location.X + d.RgbDevice.Size.Width) + (AppliedConfiguration?.MarginRight ?? 0.0f),
|
||||
HorizontalArrangementPosition.Center => devices.First().RgbDevice.Boundary.Center.X,
|
||||
HorizontalArrangementPosition.Equal => devices.First().RgbDevice.Location.X,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(horizontalPosition), horizontalPosition, null)
|
||||
};
|
||||
float y = verticalPosition switch
|
||||
{
|
||||
VerticalArrangementPosition.Top => devices.Min(d => d.RgbDevice.Location.Y) - (AppliedConfiguration?.MarginTop ?? 0.0f),
|
||||
VerticalArrangementPosition.Bottom => devices.Max(d => d.RgbDevice.Location.Y + d.RgbDevice.Size.Height) + (AppliedConfiguration?.MarginBottom ?? 0.0f),
|
||||
VerticalArrangementPosition.Center => devices.First().RgbDevice.Boundary.Center.Y,
|
||||
VerticalArrangementPosition.Equal => devices.First().RgbDevice.Location.Y,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(verticalPosition), verticalPosition, null)
|
||||
};
|
||||
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
internal enum VerticalArrangementPosition
|
||||
public void AddConfiguration(SurfaceArrangementConfiguration surfaceArrangementConfiguration)
|
||||
{
|
||||
Top,
|
||||
Bottom,
|
||||
Equal,
|
||||
Center
|
||||
surfaceArrangementConfiguration.SurfaceArrangement = SurfaceArrangement;
|
||||
Configurations.Add(surfaceArrangementConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
internal enum HorizontalArrangementPosition
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Equal,
|
||||
Center
|
||||
}
|
||||
|
||||
internal enum VerticalArrangementPosition
|
||||
{
|
||||
Top,
|
||||
Bottom,
|
||||
Equal,
|
||||
Center
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -5,54 +5,53 @@ using Artemis.Core.Modules;
|
||||
using EmbedIO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a plugin web endpoint receiving an object of type <typeparamref name="T" /> and returning any
|
||||
/// <see cref="object" /> or <see langword="null" />.
|
||||
/// <para>Note: Both will be deserialized and serialized respectively using JSON.</para>
|
||||
/// </summary>
|
||||
public class DataModelJsonPluginEndPoint<T> : PluginEndPoint where T : DataModel, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a plugin web endpoint receiving an object of type <typeparamref name="T" /> and returning any
|
||||
/// <see cref="object" /> or <see langword="null" />.
|
||||
/// <para>Note: Both will be deserialized and serialized respectively using JSON.</para>
|
||||
/// </summary>
|
||||
public class DataModelJsonPluginEndPoint<T> : PluginEndPoint where T : DataModel, new()
|
||||
private readonly Module<T> _module;
|
||||
|
||||
internal DataModelJsonPluginEndPoint(Module<T> module, string name, PluginsModule pluginsModule) : base(module, name, pluginsModule)
|
||||
{
|
||||
private readonly Module<T> _module;
|
||||
_module = module ?? throw new ArgumentNullException(nameof(module));
|
||||
|
||||
internal DataModelJsonPluginEndPoint(Module<T> module, string name, PluginsModule pluginsModule) : base(module, name, pluginsModule)
|
||||
{
|
||||
_module = module ?? throw new ArgumentNullException(nameof(module));
|
||||
|
||||
ThrowOnFail = true;
|
||||
Accepts = MimeType.Json;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the end point should throw an exception if deserializing the received JSON fails.
|
||||
/// If set to <see langword="false" /> malformed JSON is silently ignored; if set to <see langword="true" /> malformed
|
||||
/// JSON throws a <see cref="JsonException" />.
|
||||
/// </summary>
|
||||
public bool ThrowOnFail { get; set; }
|
||||
|
||||
#region Overrides of PluginEndPoint
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task ProcessRequest(IHttpContext context)
|
||||
{
|
||||
if (context.Request.HttpVerb != HttpVerbs.Post && context.Request.HttpVerb != HttpVerbs.Put)
|
||||
throw HttpException.MethodNotAllowed("This end point only accepts POST and PUT calls");
|
||||
|
||||
context.Response.ContentType = MimeType.Json;
|
||||
|
||||
using TextReader reader = context.OpenRequestText();
|
||||
try
|
||||
{
|
||||
JsonConvert.PopulateObject(await reader.ReadToEndAsync(), _module.DataModel);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
if (ThrowOnFail)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
ThrowOnFail = true;
|
||||
Accepts = MimeType.Json;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the end point should throw an exception if deserializing the received JSON fails.
|
||||
/// If set to <see langword="false" /> malformed JSON is silently ignored; if set to <see langword="true" /> malformed
|
||||
/// JSON throws a <see cref="JsonException" />.
|
||||
/// </summary>
|
||||
public bool ThrowOnFail { get; set; }
|
||||
|
||||
#region Overrides of PluginEndPoint
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task ProcessRequest(IHttpContext context)
|
||||
{
|
||||
if (context.Request.HttpVerb != HttpVerbs.Post && context.Request.HttpVerb != HttpVerbs.Put)
|
||||
throw HttpException.MethodNotAllowed("This end point only accepts POST and PUT calls");
|
||||
|
||||
context.Response.ContentType = MimeType.Json;
|
||||
|
||||
using TextReader reader = context.OpenRequestText();
|
||||
try
|
||||
{
|
||||
JsonConvert.PopulateObject(await reader.ReadToEndAsync(), _module.DataModel);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
if (ThrowOnFail)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+14
-15
@@ -1,20 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data about endpoint exception related events
|
||||
/// </summary>
|
||||
public class EndpointExceptionEventArgs : EventArgs
|
||||
{
|
||||
internal EndpointExceptionEventArgs(Exception exception)
|
||||
{
|
||||
Exception = exception;
|
||||
}
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exception that occurred
|
||||
/// </summary>
|
||||
public Exception Exception { get; }
|
||||
/// <summary>
|
||||
/// Provides data about endpoint exception related events
|
||||
/// </summary>
|
||||
public class EndpointExceptionEventArgs : EventArgs
|
||||
{
|
||||
internal EndpointExceptionEventArgs(Exception exception)
|
||||
{
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exception that occurred
|
||||
/// </summary>
|
||||
public Exception Exception { get; }
|
||||
}
|
||||
+14
-15
@@ -1,21 +1,20 @@
|
||||
using System;
|
||||
using EmbedIO;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data about endpoint request related events
|
||||
/// </summary>
|
||||
public class EndpointRequestEventArgs : EventArgs
|
||||
{
|
||||
internal EndpointRequestEventArgs(IHttpContext context)
|
||||
{
|
||||
Context = context;
|
||||
}
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP context of the request
|
||||
/// </summary>
|
||||
public IHttpContext Context { get; }
|
||||
/// <summary>
|
||||
/// Provides data about endpoint request related events
|
||||
/// </summary>
|
||||
public class EndpointRequestEventArgs : EventArgs
|
||||
{
|
||||
internal EndpointRequestEventArgs(IHttpContext context)
|
||||
{
|
||||
Context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP context of the request
|
||||
/// </summary>
|
||||
public IHttpContext Context { get; }
|
||||
}
|
||||
@@ -4,79 +4,78 @@ using System.Threading.Tasks;
|
||||
using EmbedIO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a plugin web endpoint receiving an object of type <typeparamref name="T" /> and returning any
|
||||
/// <see cref="object" /> or <see langword="null" />.
|
||||
/// <para>Note: Both will be deserialized and serialized respectively using JSON.</para>
|
||||
/// </summary>
|
||||
public class JsonPluginEndPoint<T> : PluginEndPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a plugin web endpoint receiving an object of type <typeparamref name="T" /> and returning any
|
||||
/// <see cref="object" /> or <see langword="null" />.
|
||||
/// <para>Note: Both will be deserialized and serialized respectively using JSON.</para>
|
||||
/// </summary>
|
||||
public class JsonPluginEndPoint<T> : PluginEndPoint
|
||||
private readonly Action<T>? _requestHandler;
|
||||
private readonly Func<T, object?>? _responseRequestHandler;
|
||||
|
||||
internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action<T> requestHandler) : base(pluginFeature, name, pluginsModule)
|
||||
{
|
||||
private readonly Action<T>? _requestHandler;
|
||||
private readonly Func<T, object?>? _responseRequestHandler;
|
||||
|
||||
internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action<T> requestHandler) : base(pluginFeature, name, pluginsModule)
|
||||
{
|
||||
_requestHandler = requestHandler;
|
||||
ThrowOnFail = true;
|
||||
Accepts = MimeType.Json;
|
||||
}
|
||||
|
||||
internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func<T, object?> responseRequestHandler) : base(pluginFeature, name, pluginsModule)
|
||||
{
|
||||
_responseRequestHandler = responseRequestHandler;
|
||||
ThrowOnFail = true;
|
||||
Accepts = MimeType.Json;
|
||||
Returns = MimeType.Json;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the end point should throw an exception if deserializing the received JSON fails.
|
||||
/// If set to <see langword="false" /> malformed JSON is silently ignored; if set to <see langword="true" /> malformed
|
||||
/// JSON throws a <see cref="JsonException" />.
|
||||
/// </summary>
|
||||
public bool ThrowOnFail { get; set; }
|
||||
|
||||
#region Overrides of PluginEndPoint
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task ProcessRequest(IHttpContext context)
|
||||
{
|
||||
if (context.Request.HttpVerb != HttpVerbs.Post && context.Request.HttpVerb != HttpVerbs.Put)
|
||||
throw HttpException.MethodNotAllowed("This end point only accepts POST and PUT calls");
|
||||
|
||||
context.Response.ContentType = MimeType.Json;
|
||||
|
||||
using TextReader reader = context.OpenRequestText();
|
||||
object? response = null;
|
||||
try
|
||||
{
|
||||
T? deserialized = JsonConvert.DeserializeObject<T>(await reader.ReadToEndAsync());
|
||||
if (deserialized == null)
|
||||
throw new JsonException("Deserialization returned null");
|
||||
|
||||
if (_requestHandler != null)
|
||||
{
|
||||
_requestHandler(deserialized);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_responseRequestHandler != null)
|
||||
response = _responseRequestHandler(deserialized);
|
||||
else
|
||||
throw new ArtemisCoreException("JSON plugin end point has no request handler");
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
if (ThrowOnFail)
|
||||
throw;
|
||||
}
|
||||
|
||||
await using TextWriter writer = context.OpenResponseText();
|
||||
await writer.WriteAsync(JsonConvert.SerializeObject(response));
|
||||
}
|
||||
|
||||
#endregion
|
||||
_requestHandler = requestHandler;
|
||||
ThrowOnFail = true;
|
||||
Accepts = MimeType.Json;
|
||||
}
|
||||
|
||||
internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func<T, object?> responseRequestHandler) : base(pluginFeature, name, pluginsModule)
|
||||
{
|
||||
_responseRequestHandler = responseRequestHandler;
|
||||
ThrowOnFail = true;
|
||||
Accepts = MimeType.Json;
|
||||
Returns = MimeType.Json;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the end point should throw an exception if deserializing the received JSON fails.
|
||||
/// If set to <see langword="false" /> malformed JSON is silently ignored; if set to <see langword="true" /> malformed
|
||||
/// JSON throws a <see cref="JsonException" />.
|
||||
/// </summary>
|
||||
public bool ThrowOnFail { get; set; }
|
||||
|
||||
#region Overrides of PluginEndPoint
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task ProcessRequest(IHttpContext context)
|
||||
{
|
||||
if (context.Request.HttpVerb != HttpVerbs.Post && context.Request.HttpVerb != HttpVerbs.Put)
|
||||
throw HttpException.MethodNotAllowed("This end point only accepts POST and PUT calls");
|
||||
|
||||
context.Response.ContentType = MimeType.Json;
|
||||
|
||||
using TextReader reader = context.OpenRequestText();
|
||||
object? response = null;
|
||||
try
|
||||
{
|
||||
T? deserialized = JsonConvert.DeserializeObject<T>(await reader.ReadToEndAsync());
|
||||
if (deserialized == null)
|
||||
throw new JsonException("Deserialization returned null");
|
||||
|
||||
if (_requestHandler != null)
|
||||
{
|
||||
_requestHandler(deserialized);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_responseRequestHandler != null)
|
||||
response = _responseRequestHandler(deserialized);
|
||||
else
|
||||
throw new ArtemisCoreException("JSON plugin end point has no request handler");
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
if (ThrowOnFail)
|
||||
throw;
|
||||
}
|
||||
|
||||
await using TextWriter writer = context.OpenResponseText();
|
||||
await writer.WriteAsync(JsonConvert.SerializeObject(response));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -3,120 +3,119 @@ using System.Threading.Tasks;
|
||||
using EmbedIO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a base type for plugin end points to be targeted by the <see cref="PluginsModule" />
|
||||
/// </summary>
|
||||
public abstract class PluginEndPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a base type for plugin end points to be targeted by the <see cref="PluginsModule" />
|
||||
/// </summary>
|
||||
public abstract class PluginEndPoint
|
||||
private readonly PluginsModule _pluginsModule;
|
||||
|
||||
internal PluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule)
|
||||
{
|
||||
private readonly PluginsModule _pluginsModule;
|
||||
_pluginsModule = pluginsModule;
|
||||
PluginFeature = pluginFeature;
|
||||
Name = name;
|
||||
|
||||
internal PluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule)
|
||||
PluginFeature.Disabled += OnDisabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the end point
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full URL of the end point
|
||||
/// </summary>
|
||||
public string Url => $"{_pluginsModule.ServerUrl?.TrimEnd('/')}{_pluginsModule.BaseRoute}{PluginFeature.Plugin.Guid}/{Name}";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin the end point is associated with
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public PluginFeature PluginFeature { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin info of the plugin the end point is associated with
|
||||
/// </summary>
|
||||
public PluginInfo PluginInfo => PluginFeature.Plugin.Info;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mime type of the input this end point accepts
|
||||
/// </summary>
|
||||
public string? Accepts { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mime type of the output this end point returns
|
||||
/// </summary>
|
||||
public string? Returns { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a request threw an unhandled exception
|
||||
/// </summary>
|
||||
public event EventHandler<EndpointExceptionEventArgs>? RequestException;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a request is about to be processed
|
||||
/// </summary>
|
||||
public event EventHandler<EndpointRequestEventArgs>? ProcessingRequest;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a request was processed
|
||||
/// </summary>
|
||||
public event EventHandler<EndpointRequestEventArgs>? ProcessedRequest;
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the end point has to process a request
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP context of the request</param>
|
||||
protected abstract Task ProcessRequest(IHttpContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="RequestException" /> event
|
||||
/// </summary>
|
||||
/// <param name="e">The exception that occurred during the request</param>
|
||||
protected virtual void OnRequestException(Exception e)
|
||||
{
|
||||
RequestException?.Invoke(this, new EndpointExceptionEventArgs(e));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ProcessingRequest" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnProcessingRequest(IHttpContext context)
|
||||
{
|
||||
ProcessingRequest?.Invoke(this, new EndpointRequestEventArgs(context));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ProcessedRequest" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnProcessedRequest(IHttpContext context)
|
||||
{
|
||||
ProcessedRequest?.Invoke(this, new EndpointRequestEventArgs(context));
|
||||
}
|
||||
|
||||
internal async Task InternalProcessRequest(IHttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
_pluginsModule = pluginsModule;
|
||||
PluginFeature = pluginFeature;
|
||||
Name = name;
|
||||
|
||||
PluginFeature.Disabled += OnDisabled;
|
||||
OnProcessingRequest(context);
|
||||
await ProcessRequest(context);
|
||||
OnProcessedRequest(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the end point
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full URL of the end point
|
||||
/// </summary>
|
||||
public string Url => $"{_pluginsModule.ServerUrl?.TrimEnd('/')}{_pluginsModule.BaseRoute}{PluginFeature.Plugin.Guid}/{Name}";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin the end point is associated with
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public PluginFeature PluginFeature { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin info of the plugin the end point is associated with
|
||||
/// </summary>
|
||||
public PluginInfo PluginInfo => PluginFeature.Plugin.Info;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mime type of the input this end point accepts
|
||||
/// </summary>
|
||||
public string? Accepts { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mime type of the output this end point returns
|
||||
/// </summary>
|
||||
public string? Returns { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a request threw an unhandled exception
|
||||
/// </summary>
|
||||
public event EventHandler<EndpointExceptionEventArgs>? RequestException;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a request is about to be processed
|
||||
/// </summary>
|
||||
public event EventHandler<EndpointRequestEventArgs>? ProcessingRequest;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a request was processed
|
||||
/// </summary>
|
||||
public event EventHandler<EndpointRequestEventArgs>? ProcessedRequest;
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the end point has to process a request
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP context of the request</param>
|
||||
protected abstract Task ProcessRequest(IHttpContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="RequestException" /> event
|
||||
/// </summary>
|
||||
/// <param name="e">The exception that occurred during the request</param>
|
||||
protected virtual void OnRequestException(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
RequestException?.Invoke(this, new EndpointExceptionEventArgs(e));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ProcessingRequest" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnProcessingRequest(IHttpContext context)
|
||||
{
|
||||
ProcessingRequest?.Invoke(this, new EndpointRequestEventArgs(context));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ProcessedRequest" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnProcessedRequest(IHttpContext context)
|
||||
{
|
||||
ProcessedRequest?.Invoke(this, new EndpointRequestEventArgs(context));
|
||||
}
|
||||
|
||||
internal async Task InternalProcessRequest(IHttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
OnProcessingRequest(context);
|
||||
await ProcessRequest(context);
|
||||
OnProcessedRequest(context);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
OnRequestException(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisabled(object? sender, EventArgs e)
|
||||
{
|
||||
PluginFeature.Disabled -= OnDisabled;
|
||||
_pluginsModule.RemovePluginEndPoint(this);
|
||||
OnRequestException(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisabled(object? sender, EventArgs e)
|
||||
{
|
||||
PluginFeature.Disabled -= OnDisabled;
|
||||
_pluginsModule.RemovePluginEndPoint(this);
|
||||
}
|
||||
}
|
||||
@@ -2,52 +2,51 @@
|
||||
using System.Threading.Tasks;
|
||||
using EmbedIO;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a plugin web endpoint that handles a raw <see cref="IHttpContext" />.
|
||||
/// <para>
|
||||
/// Note: This requires that you reference the EmbedIO
|
||||
/// <see href="https://www.nuget.org/packages/embedio">Nuget package</see>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class RawPluginEndPoint : PluginEndPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a plugin web endpoint that handles a raw <see cref="IHttpContext" />.
|
||||
/// <para>
|
||||
/// Note: This requires that you reference the EmbedIO
|
||||
/// <see href="https://www.nuget.org/packages/embedio">Nuget package</see>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class RawPluginEndPoint : PluginEndPoint
|
||||
/// <inheritdoc />
|
||||
internal RawPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func<IHttpContext, Task> requestHandler) : base(pluginFeature, name, pluginsModule)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
internal RawPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func<IHttpContext, Task> requestHandler) : base(pluginFeature, name, pluginsModule)
|
||||
{
|
||||
RequestHandler = requestHandler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler used to handle incoming requests to this endpoint
|
||||
/// </summary>
|
||||
public Func<IHttpContext, Task> RequestHandler { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mime type this plugin end point accepts
|
||||
/// </summary>
|
||||
public void SetAcceptType(string type)
|
||||
{
|
||||
Accepts = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mime type this plugin end point returns
|
||||
/// </summary>
|
||||
public void SetReturnType(string type)
|
||||
{
|
||||
Returns = type;
|
||||
}
|
||||
|
||||
#region Overrides of PluginEndPoint
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task ProcessRequest(IHttpContext context)
|
||||
{
|
||||
await RequestHandler(context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
RequestHandler = requestHandler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler used to handle incoming requests to this endpoint
|
||||
/// </summary>
|
||||
public Func<IHttpContext, Task> RequestHandler { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mime type this plugin end point accepts
|
||||
/// </summary>
|
||||
public void SetAcceptType(string type)
|
||||
{
|
||||
Accepts = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mime type this plugin end point returns
|
||||
/// </summary>
|
||||
public void SetReturnType(string type)
|
||||
{
|
||||
Returns = type;
|
||||
}
|
||||
|
||||
#region Overrides of PluginEndPoint
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task ProcessRequest(IHttpContext context)
|
||||
{
|
||||
await RequestHandler(context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -3,57 +3,56 @@ using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using EmbedIO;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a plugin web endpoint receiving an a <see cref="string" /> and returning a <see cref="string" /> or
|
||||
/// <see langword="null" />.
|
||||
/// </summary>
|
||||
public class StringPluginEndPoint : PluginEndPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a plugin web endpoint receiving an a <see cref="string" /> and returning a <see cref="string" /> or
|
||||
/// <see langword="null" />.
|
||||
/// </summary>
|
||||
public class StringPluginEndPoint : PluginEndPoint
|
||||
private readonly Action<string>? _requestHandler;
|
||||
private readonly Func<string, string?>? _responseRequestHandler;
|
||||
|
||||
internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action<string> requestHandler) : base(pluginFeature, name, pluginsModule)
|
||||
{
|
||||
private readonly Action<string>? _requestHandler;
|
||||
private readonly Func<string, string?>? _responseRequestHandler;
|
||||
|
||||
internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action<string> requestHandler) : base(pluginFeature, name, pluginsModule)
|
||||
{
|
||||
_requestHandler = requestHandler;
|
||||
Accepts = MimeType.PlainText;
|
||||
}
|
||||
|
||||
internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func<string, string?> requestHandler) : base(pluginFeature, name, pluginsModule)
|
||||
{
|
||||
_responseRequestHandler = requestHandler;
|
||||
Accepts = MimeType.PlainText;
|
||||
Returns = MimeType.PlainText;
|
||||
}
|
||||
|
||||
#region Overrides of PluginEndPoint
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task ProcessRequest(IHttpContext context)
|
||||
{
|
||||
if (context.Request.HttpVerb != HttpVerbs.Post && context.Request.HttpVerb != HttpVerbs.Put)
|
||||
throw HttpException.MethodNotAllowed("This end point only accepts POST and PUT calls");
|
||||
|
||||
context.Response.ContentType = MimeType.PlainText;
|
||||
|
||||
using TextReader reader = context.OpenRequestText();
|
||||
string? response;
|
||||
if (_requestHandler != null)
|
||||
{
|
||||
_requestHandler(await reader.ReadToEndAsync());
|
||||
return;
|
||||
}
|
||||
|
||||
if (_responseRequestHandler != null)
|
||||
response = _responseRequestHandler(await reader.ReadToEndAsync());
|
||||
else
|
||||
throw new ArtemisCoreException("String plugin end point has no request handler");
|
||||
|
||||
await using TextWriter writer = context.OpenResponseText();
|
||||
await writer.WriteAsync(response);
|
||||
}
|
||||
|
||||
#endregion
|
||||
_requestHandler = requestHandler;
|
||||
Accepts = MimeType.PlainText;
|
||||
}
|
||||
|
||||
internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func<string, string?> requestHandler) : base(pluginFeature, name, pluginsModule)
|
||||
{
|
||||
_responseRequestHandler = requestHandler;
|
||||
Accepts = MimeType.PlainText;
|
||||
Returns = MimeType.PlainText;
|
||||
}
|
||||
|
||||
#region Overrides of PluginEndPoint
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task ProcessRequest(IHttpContext context)
|
||||
{
|
||||
if (context.Request.HttpVerb != HttpVerbs.Post && context.Request.HttpVerb != HttpVerbs.Put)
|
||||
throw HttpException.MethodNotAllowed("This end point only accepts POST and PUT calls");
|
||||
|
||||
context.Response.ContentType = MimeType.PlainText;
|
||||
|
||||
using TextReader reader = context.OpenRequestText();
|
||||
string? response;
|
||||
if (_requestHandler != null)
|
||||
{
|
||||
_requestHandler(await reader.ReadToEndAsync());
|
||||
return;
|
||||
}
|
||||
|
||||
if (_responseRequestHandler != null)
|
||||
response = _responseRequestHandler(await reader.ReadToEndAsync());
|
||||
else
|
||||
throw new ArtemisCoreException("String plugin end point has no request handler");
|
||||
|
||||
await using TextWriter writer = context.OpenResponseText();
|
||||
await writer.WriteAsync(response);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -4,130 +4,129 @@ using Artemis.Core.Modules;
|
||||
using EmbedIO;
|
||||
using EmbedIO.WebApi;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A service that provides access to the local Artemis web server
|
||||
/// </summary>
|
||||
public interface IWebServerService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// A service that provides access to the local Artemis web server
|
||||
/// Gets the current instance of the web server, replaced when <see cref="WebServerStarting" /> occurs.
|
||||
/// </summary>
|
||||
public interface IWebServerService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current instance of the web server, replaced when <see cref="WebServerStarting" /> occurs.
|
||||
/// </summary>
|
||||
WebServer? Server { get; }
|
||||
WebServer? Server { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugins module containing all plugin end points
|
||||
/// </summary>
|
||||
PluginsModule PluginsModule { get; }
|
||||
/// <summary>
|
||||
/// Gets the plugins module containing all plugin end points
|
||||
/// </summary>
|
||||
PluginsModule PluginsModule { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature receiving an object of type <typeparamref name="T" />
|
||||
/// <para>Note: Object will be deserialized using JSON.</para>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to be received</typeparam>
|
||||
/// <param name="feature">The plugin feature the end point is associated with</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <param name="requestHandler"></param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
JsonPluginEndPoint<T> AddJsonEndPoint<T>(PluginFeature feature, string endPointName, Action<T> requestHandler);
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature receiving an object of type <typeparamref name="T" />
|
||||
/// <para>Note: Object will be deserialized using JSON.</para>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to be received</typeparam>
|
||||
/// <param name="feature">The plugin feature the end point is associated with</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <param name="requestHandler"></param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
JsonPluginEndPoint<T> AddJsonEndPoint<T>(PluginFeature feature, string endPointName, Action<T> requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature receiving an object of type <typeparamref name="T" /> and
|
||||
/// returning any <see cref="object" />.
|
||||
/// <para>Note: Both will be deserialized and serialized respectively using JSON.</para>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to be received</typeparam>
|
||||
/// <param name="feature">The plugin feature the end point is associated with</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <param name="requestHandler"></param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
JsonPluginEndPoint<T> AddResponsiveJsonEndPoint<T>(PluginFeature feature, string endPointName, Func<T, object?> requestHandler);
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature receiving an object of type <typeparamref name="T" /> and
|
||||
/// returning any <see cref="object" />.
|
||||
/// <para>Note: Both will be deserialized and serialized respectively using JSON.</para>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to be received</typeparam>
|
||||
/// <param name="feature">The plugin feature the end point is associated with</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <param name="requestHandler"></param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
JsonPluginEndPoint<T> AddResponsiveJsonEndPoint<T>(PluginFeature feature, string endPointName, Func<T, object?> requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new endpoint that directly maps received JSON to the data model of the provided <paramref name="module" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data model type of the module</typeparam>
|
||||
/// <param name="module">The module whose datamodel to apply the received JSON to</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel, new();
|
||||
/// <summary>
|
||||
/// Adds a new endpoint that directly maps received JSON to the data model of the provided <paramref name="module" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data model type of the module</typeparam>
|
||||
/// <param name="module">The module whose datamodel to apply the received JSON to</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel, new();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature receiving an a <see cref="string" />.
|
||||
/// </summary>
|
||||
/// <param name="feature">The plugin feature the end point is associated with</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <param name="requestHandler"></param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
StringPluginEndPoint AddStringEndPoint(PluginFeature feature, string endPointName, Action<string> requestHandler);
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature receiving an a <see cref="string" />.
|
||||
/// </summary>
|
||||
/// <param name="feature">The plugin feature the end point is associated with</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <param name="requestHandler"></param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
StringPluginEndPoint AddStringEndPoint(PluginFeature feature, string endPointName, Action<string> requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature receiving an a <see cref="string" /> and returning a
|
||||
/// <see cref="string" /> or <see langword="null" />.
|
||||
/// </summary>
|
||||
/// <param name="feature">The plugin feature the end point is associated with</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <param name="requestHandler"></param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
StringPluginEndPoint AddResponsiveStringEndPoint(PluginFeature feature, string endPointName, Func<string, string?> requestHandler);
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature receiving an a <see cref="string" /> and returning a
|
||||
/// <see cref="string" /> or <see langword="null" />.
|
||||
/// </summary>
|
||||
/// <param name="feature">The plugin feature the end point is associated with</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <param name="requestHandler"></param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
StringPluginEndPoint AddResponsiveStringEndPoint(PluginFeature feature, string endPointName, Func<string, string?> requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature that handles a raw <see cref="IHttpContext" />.
|
||||
/// <para>
|
||||
/// Note: This requires that you reference the EmbedIO
|
||||
/// <see href="https://www.nuget.org/packages/embedio">Nuget package</see>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="feature">The plugin feature the end point is associated with</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <param name="requestHandler"></param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
RawPluginEndPoint AddRawEndPoint(PluginFeature feature, string endPointName, Func<IHttpContext, Task> requestHandler);
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature that handles a raw <see cref="IHttpContext" />.
|
||||
/// <para>
|
||||
/// Note: This requires that you reference the EmbedIO
|
||||
/// <see href="https://www.nuget.org/packages/embedio">Nuget package</see>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="feature">The plugin feature the end point is associated with</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <param name="requestHandler"></param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
RawPluginEndPoint AddRawEndPoint(PluginFeature feature, string endPointName, Func<IHttpContext, Task> requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an existing endpoint
|
||||
/// </summary>
|
||||
/// <param name="endPoint">The end point to remove</param>
|
||||
void RemovePluginEndPoint(PluginEndPoint endPoint);
|
||||
/// <summary>
|
||||
/// Removes an existing endpoint
|
||||
/// </summary>
|
||||
/// <param name="endPoint">The end point to remove</param>
|
||||
void RemovePluginEndPoint(PluginEndPoint endPoint);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new Web API controller and restarts the web server
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
|
||||
void AddController<T>(PluginFeature feature) where T : WebApiController;
|
||||
/// <summary>
|
||||
/// Adds a new Web API controller and restarts the web server
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
|
||||
void AddController<T>(PluginFeature feature) where T : WebApiController;
|
||||
|
||||
/// <summary>
|
||||
/// Removes an existing Web API controller and restarts the web server
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
|
||||
void RemoveController<T>() where T : WebApiController;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new EmbedIO module and restarts the web server
|
||||
/// </summary>
|
||||
void AddModule(PluginFeature feature, Func<IWebModule> create);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a EmbedIO module and restarts the web server
|
||||
/// </summary>
|
||||
void RemoveModule(Func<IWebModule> create);
|
||||
/// <summary>
|
||||
/// Removes an existing Web API controller and restarts the web server
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
|
||||
void RemoveController<T>() where T : WebApiController;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new EmbedIO module and restarts the web server
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of module to add</typeparam>
|
||||
void AddModule<T>(PluginFeature feature) where T : IWebModule;
|
||||
/// <summary>
|
||||
/// Adds a new EmbedIO module and restarts the web server
|
||||
/// </summary>
|
||||
void AddModule(PluginFeature feature, Func<IWebModule> create);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a EmbedIO module and restarts the web server
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of module to remove</typeparam>
|
||||
void RemoveModule<T>() where T : IWebModule;
|
||||
/// <summary>
|
||||
/// Removes a EmbedIO module and restarts the web server
|
||||
/// </summary>
|
||||
void RemoveModule(Func<IWebModule> create);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the web server has been created and is about to start. This is the ideal place to add your own modules.
|
||||
/// </summary>
|
||||
event EventHandler? WebServerStarting;
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds a new EmbedIO module and restarts the web server
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of module to add</typeparam>
|
||||
void AddModule<T>(PluginFeature feature) where T : IWebModule;
|
||||
|
||||
/// <summary>
|
||||
/// Removes a EmbedIO module and restarts the web server
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of module to remove</typeparam>
|
||||
void RemoveModule<T>() where T : IWebModule;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the web server has been created and is about to start. This is the ideal place to add your own modules.
|
||||
/// </summary>
|
||||
event EventHandler? WebServerStarting;
|
||||
}
|
||||
@@ -3,90 +3,89 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using EmbedIO;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an EmbedIO web module used to process web requests and forward them to the right
|
||||
/// <see cref="PluginEndPoint" />.
|
||||
/// </summary>
|
||||
public class PluginsModule : WebModuleBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an EmbedIO web module used to process web requests and forward them to the right
|
||||
/// <see cref="PluginEndPoint" />.
|
||||
/// </summary>
|
||||
public class PluginsModule : WebModuleBase
|
||||
private readonly Dictionary<string, Dictionary<string, PluginEndPoint>> _pluginEndPoints;
|
||||
|
||||
internal PluginsModule(string baseRoute) : base(baseRoute)
|
||||
{
|
||||
private readonly Dictionary<string, Dictionary<string, PluginEndPoint>> _pluginEndPoints;
|
||||
|
||||
internal PluginsModule(string baseRoute) : base(baseRoute)
|
||||
{
|
||||
_pluginEndPoints = new Dictionary<string, Dictionary<string, PluginEndPoint>>();
|
||||
}
|
||||
|
||||
internal void AddPluginEndPoint(PluginEndPoint registration)
|
||||
{
|
||||
string id = registration.PluginFeature.Plugin.Guid.ToString();
|
||||
if (!_pluginEndPoints.TryGetValue(id, out Dictionary<string, PluginEndPoint>? registrations))
|
||||
{
|
||||
registrations = new Dictionary<string, PluginEndPoint>();
|
||||
_pluginEndPoints.Add(id, registrations);
|
||||
}
|
||||
|
||||
if (registrations.ContainsKey(registration.Name))
|
||||
throw new ArtemisPluginException(registration.PluginFeature.Plugin, $"Plugin already registered an endpoint at {registration.Name}.");
|
||||
registrations.Add(registration.Name, registration);
|
||||
}
|
||||
|
||||
internal void RemovePluginEndPoint(PluginEndPoint registration)
|
||||
{
|
||||
string id = registration.PluginFeature.Plugin.Guid.ToString();
|
||||
if (!_pluginEndPoints.TryGetValue(id, out Dictionary<string, PluginEndPoint>? registrations))
|
||||
return;
|
||||
if (!registrations.ContainsKey(registration.Name))
|
||||
return;
|
||||
registrations.Remove(registration.Name);
|
||||
}
|
||||
|
||||
#region Overrides of WebModuleBase
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnRequestAsync(IHttpContext context)
|
||||
{
|
||||
if (context.Route.SubPath == null)
|
||||
return;
|
||||
|
||||
// Split the sub path
|
||||
string[] pathParts = context.Route.SubPath.Substring(1).Split('/');
|
||||
// Expect a plugin ID and an endpoint
|
||||
if (pathParts.Length != 2)
|
||||
return;
|
||||
|
||||
// Find a matching plugin
|
||||
if (!_pluginEndPoints.TryGetValue(pathParts[0], out Dictionary<string, PluginEndPoint>? endPoints))
|
||||
throw HttpException.NotFound($"Found no plugin with ID {pathParts[0]}.");
|
||||
|
||||
// Find a matching endpoint
|
||||
if (!endPoints.TryGetValue(pathParts[1], out PluginEndPoint? endPoint))
|
||||
throw HttpException.NotFound($"Found no endpoint called {pathParts[1]} for plugin with ID {pathParts[0]}.");
|
||||
|
||||
// If Accept-Charset contains a wildcard, remove the header so we default to UTF8
|
||||
// This is a workaround for an EmbedIO ehh issue
|
||||
string? acceptCharset = context.Request.Headers["Accept-Charset"];
|
||||
if (acceptCharset != null && acceptCharset.Contains("*"))
|
||||
context.Request.Headers.Remove("Accept-Charset");
|
||||
|
||||
// It is up to the registration how the request is eventually handled, it might even set a response here
|
||||
await endPoint.InternalProcessRequest(context);
|
||||
|
||||
// No need to return ourselves, assume the request is fully handled by the end point
|
||||
context.SetHandled();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsFinalHandler => false;
|
||||
|
||||
internal string? ServerUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection containing all current plugin end points
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<PluginEndPoint> PluginEndPoints => new List<PluginEndPoint>(_pluginEndPoints.SelectMany(p => p.Value.Values));
|
||||
|
||||
#endregion
|
||||
_pluginEndPoints = new Dictionary<string, Dictionary<string, PluginEndPoint>>();
|
||||
}
|
||||
|
||||
internal void AddPluginEndPoint(PluginEndPoint registration)
|
||||
{
|
||||
string id = registration.PluginFeature.Plugin.Guid.ToString();
|
||||
if (!_pluginEndPoints.TryGetValue(id, out Dictionary<string, PluginEndPoint>? registrations))
|
||||
{
|
||||
registrations = new Dictionary<string, PluginEndPoint>();
|
||||
_pluginEndPoints.Add(id, registrations);
|
||||
}
|
||||
|
||||
if (registrations.ContainsKey(registration.Name))
|
||||
throw new ArtemisPluginException(registration.PluginFeature.Plugin, $"Plugin already registered an endpoint at {registration.Name}.");
|
||||
registrations.Add(registration.Name, registration);
|
||||
}
|
||||
|
||||
internal void RemovePluginEndPoint(PluginEndPoint registration)
|
||||
{
|
||||
string id = registration.PluginFeature.Plugin.Guid.ToString();
|
||||
if (!_pluginEndPoints.TryGetValue(id, out Dictionary<string, PluginEndPoint>? registrations))
|
||||
return;
|
||||
if (!registrations.ContainsKey(registration.Name))
|
||||
return;
|
||||
registrations.Remove(registration.Name);
|
||||
}
|
||||
|
||||
#region Overrides of WebModuleBase
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnRequestAsync(IHttpContext context)
|
||||
{
|
||||
if (context.Route.SubPath == null)
|
||||
return;
|
||||
|
||||
// Split the sub path
|
||||
string[] pathParts = context.Route.SubPath.Substring(1).Split('/');
|
||||
// Expect a plugin ID and an endpoint
|
||||
if (pathParts.Length != 2)
|
||||
return;
|
||||
|
||||
// Find a matching plugin
|
||||
if (!_pluginEndPoints.TryGetValue(pathParts[0], out Dictionary<string, PluginEndPoint>? endPoints))
|
||||
throw HttpException.NotFound($"Found no plugin with ID {pathParts[0]}.");
|
||||
|
||||
// Find a matching endpoint
|
||||
if (!endPoints.TryGetValue(pathParts[1], out PluginEndPoint? endPoint))
|
||||
throw HttpException.NotFound($"Found no endpoint called {pathParts[1]} for plugin with ID {pathParts[0]}.");
|
||||
|
||||
// If Accept-Charset contains a wildcard, remove the header so we default to UTF8
|
||||
// This is a workaround for an EmbedIO ehh issue
|
||||
string? acceptCharset = context.Request.Headers["Accept-Charset"];
|
||||
if (acceptCharset != null && acceptCharset.Contains("*"))
|
||||
context.Request.Headers.Remove("Accept-Charset");
|
||||
|
||||
// It is up to the registration how the request is eventually handled, it might even set a response here
|
||||
await endPoint.InternalProcessRequest(context);
|
||||
|
||||
// No need to return ourselves, assume the request is fully handled by the end point
|
||||
context.SetHandled();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsFinalHandler => false;
|
||||
|
||||
internal string? ServerUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection containing all current plugin end points
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<PluginEndPoint> PluginEndPoints => new List<PluginEndPoint>(_pluginEndPoints.SelectMany(p => p.Value.Values));
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -2,29 +2,28 @@
|
||||
using EmbedIO.WebApi;
|
||||
using Ninject;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class WebApiControllerRegistration<T> : WebApiControllerRegistration where T : WebApiController
|
||||
{
|
||||
internal class WebApiControllerRegistration<T> : WebApiControllerRegistration where T : WebApiController
|
||||
public WebApiControllerRegistration(PluginFeature feature) : base(feature, typeof(T))
|
||||
{
|
||||
public WebApiControllerRegistration(PluginFeature feature) : base(feature, typeof(T))
|
||||
{
|
||||
Factory = () => feature.Plugin.Kernel!.Get<T>();
|
||||
}
|
||||
|
||||
public Func<T> Factory { get; set; }
|
||||
public override object UntypedFactory => Factory;
|
||||
Factory = () => feature.Plugin.Kernel!.Get<T>();
|
||||
}
|
||||
|
||||
internal abstract class WebApiControllerRegistration
|
||||
{
|
||||
protected WebApiControllerRegistration(PluginFeature feature, Type controllerType)
|
||||
{
|
||||
Feature = feature;
|
||||
ControllerType = controllerType;
|
||||
}
|
||||
public Func<T> Factory { get; set; }
|
||||
public override object UntypedFactory => Factory;
|
||||
}
|
||||
|
||||
public abstract object UntypedFactory { get; }
|
||||
public Type ControllerType { get; set; }
|
||||
public PluginFeature Feature { get; }
|
||||
internal abstract class WebApiControllerRegistration
|
||||
{
|
||||
protected WebApiControllerRegistration(PluginFeature feature, Type controllerType)
|
||||
{
|
||||
Feature = feature;
|
||||
ControllerType = controllerType;
|
||||
}
|
||||
|
||||
public abstract object UntypedFactory { get; }
|
||||
public Type ControllerType { get; set; }
|
||||
public PluginFeature Feature { get; }
|
||||
}
|
||||
@@ -2,33 +2,32 @@
|
||||
using EmbedIO;
|
||||
using Ninject;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class WebModuleRegistration
|
||||
{
|
||||
internal class WebModuleRegistration
|
||||
public WebModuleRegistration(PluginFeature feature, Type webModuleType)
|
||||
{
|
||||
public PluginFeature Feature { get; }
|
||||
public Type? WebModuleType { get; }
|
||||
public Func<IWebModule>? Create { get; }
|
||||
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
|
||||
WebModuleType = webModuleType ?? throw new ArgumentNullException(nameof(webModuleType));
|
||||
}
|
||||
|
||||
public WebModuleRegistration(PluginFeature feature, Type webModuleType)
|
||||
{
|
||||
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
|
||||
WebModuleType = webModuleType ?? throw new ArgumentNullException(nameof(webModuleType));
|
||||
}
|
||||
public WebModuleRegistration(PluginFeature feature, Func<IWebModule> create)
|
||||
{
|
||||
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
|
||||
Create = create ?? throw new ArgumentNullException(nameof(create));
|
||||
}
|
||||
|
||||
public WebModuleRegistration(PluginFeature feature, Func<IWebModule> create)
|
||||
{
|
||||
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
|
||||
Create = create ?? throw new ArgumentNullException(nameof(create));
|
||||
}
|
||||
public PluginFeature Feature { get; }
|
||||
public Type? WebModuleType { get; }
|
||||
public Func<IWebModule>? Create { get; }
|
||||
|
||||
public IWebModule CreateInstance()
|
||||
{
|
||||
if (Create != null)
|
||||
return Create();
|
||||
if (WebModuleType != null)
|
||||
return (IWebModule) Feature.Plugin.Kernel!.Get(WebModuleType);
|
||||
throw new ArtemisCoreException("WebModuleRegistration doesn't have a create function nor a web module type :(");
|
||||
}
|
||||
public IWebModule CreateInstance()
|
||||
{
|
||||
if (Create != null)
|
||||
return Create();
|
||||
if (WebModuleType != null)
|
||||
return (IWebModule) Feature.Plugin.Kernel!.Get(WebModuleType);
|
||||
throw new ArtemisCoreException("WebModuleRegistration doesn't have a create function nor a web module type :(");
|
||||
}
|
||||
}
|
||||
@@ -10,283 +10,270 @@ using EmbedIO.WebApi;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class WebServerService : IWebServerService, IDisposable
|
||||
{
|
||||
internal class WebServerService : IWebServerService, IDisposable
|
||||
private readonly List<WebApiControllerRegistration> _controllers;
|
||||
private readonly ILogger _logger;
|
||||
private readonly List<WebModuleRegistration> _modules;
|
||||
private readonly PluginSetting<int> _webServerPortSetting;
|
||||
|
||||
public WebServerService(ILogger logger, ISettingsService settingsService, IPluginManagementService pluginManagementService)
|
||||
{
|
||||
private readonly List<WebApiControllerRegistration> _controllers;
|
||||
private readonly List<WebModuleRegistration> _modules;
|
||||
private readonly ILogger _logger;
|
||||
private readonly PluginSetting<int> _webServerPortSetting;
|
||||
_logger = logger;
|
||||
_controllers = new List<WebApiControllerRegistration>();
|
||||
_modules = new List<WebModuleRegistration>();
|
||||
|
||||
public WebServerService(ILogger logger, ISettingsService settingsService, IPluginManagementService pluginManagementService)
|
||||
{
|
||||
_logger = logger;
|
||||
_controllers = new List<WebApiControllerRegistration>();
|
||||
_modules = new List<WebModuleRegistration>();
|
||||
_webServerPortSetting = settingsService.GetSetting("WebServer.Port", 9696);
|
||||
_webServerPortSetting.SettingChanged += WebServerPortSettingOnSettingChanged;
|
||||
pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled;
|
||||
|
||||
_webServerPortSetting = settingsService.GetSetting("WebServer.Port", 9696);
|
||||
_webServerPortSetting.SettingChanged += WebServerPortSettingOnSettingChanged;
|
||||
pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled;
|
||||
|
||||
PluginsModule = new PluginsModule("/plugins");
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
public WebServer? Server { get; private set; }
|
||||
public PluginsModule PluginsModule { get; }
|
||||
|
||||
#region Web server managament
|
||||
|
||||
private WebServer CreateWebServer()
|
||||
{
|
||||
Server?.Dispose();
|
||||
Server = null;
|
||||
|
||||
WebApiModule apiModule = new("/", JsonNetSerializer);
|
||||
PluginsModule.ServerUrl = $"http://localhost:{_webServerPortSetting.Value}/";
|
||||
WebServer server = new WebServer(o => o.WithUrlPrefix($"http://*:{_webServerPortSetting.Value}/").WithMode(HttpListenerMode.EmbedIO))
|
||||
.WithLocalSessionManager()
|
||||
.WithModule(PluginsModule);
|
||||
|
||||
// Add registered modules
|
||||
foreach (WebModuleRegistration? webModule in _modules)
|
||||
server = server.WithModule(webModule.CreateInstance());
|
||||
|
||||
server = server
|
||||
.WithModule(apiModule)
|
||||
.HandleHttpException((context, exception) => HandleHttpExceptionJson(context, exception))
|
||||
.HandleUnhandledException(JsonExceptionHandlerCallback);
|
||||
|
||||
// Add registered controllers to the API module
|
||||
foreach (WebApiControllerRegistration registration in _controllers)
|
||||
apiModule.RegisterController(registration.ControllerType, (Func<WebApiController>) registration.UntypedFactory);
|
||||
|
||||
// Listen for state changes.
|
||||
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
|
||||
File.WriteAllText(Path.Combine(Constants.DataFolder, "webserver.txt"), PluginsModule.ServerUrl);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
private void StartWebServer()
|
||||
{
|
||||
Server = CreateWebServer();
|
||||
OnWebServerStarting();
|
||||
Server.Start();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Plugin endpoint management
|
||||
|
||||
public JsonPluginEndPoint<T> AddJsonEndPoint<T>(PluginFeature feature, string endPointName, Action<T> requestHandler)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
|
||||
JsonPluginEndPoint<T> endPoint = new(feature, endPointName, PluginsModule, requestHandler);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public JsonPluginEndPoint<T> AddResponsiveJsonEndPoint<T>(PluginFeature feature, string endPointName, Func<T, object?> requestHandler)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
|
||||
JsonPluginEndPoint<T> endPoint = new(feature, endPointName, PluginsModule, requestHandler);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public StringPluginEndPoint AddStringEndPoint(PluginFeature feature, string endPointName, Action<string> requestHandler)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
|
||||
StringPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public StringPluginEndPoint AddResponsiveStringEndPoint(PluginFeature feature, string endPointName, Func<string, string?> requestHandler)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
|
||||
StringPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public RawPluginEndPoint AddRawEndPoint(PluginFeature feature, string endPointName, Func<IHttpContext, Task> requestHandler)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
|
||||
RawPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel, new()
|
||||
{
|
||||
if (module == null) throw new ArgumentNullException(nameof(module));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
DataModelJsonPluginEndPoint<T> endPoint = new(module, endPointName, PluginsModule);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
private void HandleDataModelRequest<T>(Module<T> module, T value) where T : DataModel, new()
|
||||
{
|
||||
}
|
||||
|
||||
public void RemovePluginEndPoint(PluginEndPoint endPoint)
|
||||
{
|
||||
PluginsModule.RemovePluginEndPoint(endPoint);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controller management
|
||||
|
||||
public void AddController<T>(PluginFeature feature) where T : WebApiController
|
||||
{
|
||||
_controllers.Add(new WebApiControllerRegistration<T>(feature));
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
public void RemoveController<T>() where T : WebApiController
|
||||
{
|
||||
_controllers.RemoveAll(r => r.ControllerType == typeof(T));
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Module management
|
||||
|
||||
public void AddModule(PluginFeature feature, Func<IWebModule> create)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
|
||||
_modules.Add(new WebModuleRegistration(feature, create));
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
public void RemoveModule(Func<IWebModule> create)
|
||||
{
|
||||
_modules.RemoveAll(r => r.Create == create);
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
public void AddModule<T>(PluginFeature feature) where T : IWebModule
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (_modules.Any(r => r.WebModuleType == typeof(T)))
|
||||
return;
|
||||
|
||||
_modules.Add(new WebModuleRegistration(feature, typeof(T)));
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
public void RemoveModule<T>() where T : IWebModule
|
||||
{
|
||||
_modules.RemoveAll(r => r.WebModuleType == typeof(T));
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Handlers
|
||||
|
||||
private async Task JsonExceptionHandlerCallback(IHttpContext context, Exception exception)
|
||||
{
|
||||
context.Response.ContentType = MimeType.Json;
|
||||
await using TextWriter writer = context.OpenResponseText();
|
||||
|
||||
string response = JsonConvert.SerializeObject(new Dictionary<string, object?>()
|
||||
{
|
||||
{"StatusCode", context.Response.StatusCode},
|
||||
{"StackTrace", exception.StackTrace},
|
||||
{"Type", exception.GetType().FullName},
|
||||
{"Message", exception.Message},
|
||||
{"Data", exception.Data},
|
||||
{"InnerException", exception.InnerException},
|
||||
{"HelpLink", exception.HelpLink},
|
||||
{"Source", exception.Source},
|
||||
{"HResult", exception.HResult}
|
||||
});
|
||||
await writer.WriteAsync(response);
|
||||
}
|
||||
|
||||
private async Task JsonNetSerializer(IHttpContext context, object? data)
|
||||
{
|
||||
context.Response.ContentType = MimeType.Json;
|
||||
await using TextWriter writer = context.OpenResponseText();
|
||||
string json = JsonConvert.SerializeObject(data, new JsonSerializerSettings {PreserveReferencesHandling = PreserveReferencesHandling.Objects});
|
||||
await writer.WriteAsync(json);
|
||||
}
|
||||
|
||||
private async Task HandleHttpExceptionJson(IHttpContext context, IHttpException httpException)
|
||||
{
|
||||
await context.SendStringAsync(JsonConvert.SerializeObject(httpException, Formatting.Indented), MimeType.Json, Encoding.UTF8);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler? WebServerStarting;
|
||||
|
||||
protected virtual void OnWebServerStarting()
|
||||
{
|
||||
WebServerStarting?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void WebServerPortSettingOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginFeatureDisabled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
bool mustRestart = false;
|
||||
if (_controllers.Any(c => c.Feature == e.PluginFeature))
|
||||
{
|
||||
mustRestart = true;
|
||||
_controllers.RemoveAll(c => c.Feature == e.PluginFeature);
|
||||
}
|
||||
|
||||
if (_modules.Any(m => m.Feature == e.PluginFeature))
|
||||
{
|
||||
mustRestart = true;
|
||||
_modules.RemoveAll(m => m.Feature == e.PluginFeature);
|
||||
}
|
||||
|
||||
if (mustRestart)
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Server?.Dispose();
|
||||
_webServerPortSetting.SettingChanged -= WebServerPortSettingOnSettingChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
PluginsModule = new PluginsModule("/plugins");
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
protected virtual void OnWebServerStarting()
|
||||
{
|
||||
WebServerStarting?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void WebServerPortSettingOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginFeatureDisabled(object? sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
bool mustRestart = false;
|
||||
if (_controllers.Any(c => c.Feature == e.PluginFeature))
|
||||
{
|
||||
mustRestart = true;
|
||||
_controllers.RemoveAll(c => c.Feature == e.PluginFeature);
|
||||
}
|
||||
|
||||
if (_modules.Any(m => m.Feature == e.PluginFeature))
|
||||
{
|
||||
mustRestart = true;
|
||||
_modules.RemoveAll(m => m.Feature == e.PluginFeature);
|
||||
}
|
||||
|
||||
if (mustRestart)
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Server?.Dispose();
|
||||
_webServerPortSetting.SettingChanged -= WebServerPortSettingOnSettingChanged;
|
||||
}
|
||||
|
||||
public WebServer? Server { get; private set; }
|
||||
public PluginsModule PluginsModule { get; }
|
||||
|
||||
public event EventHandler? WebServerStarting;
|
||||
|
||||
#region Web server managament
|
||||
|
||||
private WebServer CreateWebServer()
|
||||
{
|
||||
Server?.Dispose();
|
||||
Server = null;
|
||||
|
||||
WebApiModule apiModule = new("/", JsonNetSerializer);
|
||||
PluginsModule.ServerUrl = $"http://localhost:{_webServerPortSetting.Value}/";
|
||||
WebServer server = new WebServer(o => o.WithUrlPrefix($"http://*:{_webServerPortSetting.Value}/").WithMode(HttpListenerMode.EmbedIO))
|
||||
.WithLocalSessionManager()
|
||||
.WithModule(PluginsModule);
|
||||
|
||||
// Add registered modules
|
||||
foreach (WebModuleRegistration? webModule in _modules)
|
||||
server = server.WithModule(webModule.CreateInstance());
|
||||
|
||||
server = server
|
||||
.WithModule(apiModule)
|
||||
.HandleHttpException((context, exception) => HandleHttpExceptionJson(context, exception))
|
||||
.HandleUnhandledException(JsonExceptionHandlerCallback);
|
||||
|
||||
// Add registered controllers to the API module
|
||||
foreach (WebApiControllerRegistration registration in _controllers)
|
||||
apiModule.RegisterController(registration.ControllerType, (Func<WebApiController>) registration.UntypedFactory);
|
||||
|
||||
// Listen for state changes.
|
||||
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
|
||||
File.WriteAllText(Path.Combine(Constants.DataFolder, "webserver.txt"), PluginsModule.ServerUrl);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
private void StartWebServer()
|
||||
{
|
||||
Server = CreateWebServer();
|
||||
OnWebServerStarting();
|
||||
Server.Start();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Plugin endpoint management
|
||||
|
||||
public JsonPluginEndPoint<T> AddJsonEndPoint<T>(PluginFeature feature, string endPointName, Action<T> requestHandler)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
|
||||
JsonPluginEndPoint<T> endPoint = new(feature, endPointName, PluginsModule, requestHandler);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public JsonPluginEndPoint<T> AddResponsiveJsonEndPoint<T>(PluginFeature feature, string endPointName, Func<T, object?> requestHandler)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
|
||||
JsonPluginEndPoint<T> endPoint = new(feature, endPointName, PluginsModule, requestHandler);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public StringPluginEndPoint AddStringEndPoint(PluginFeature feature, string endPointName, Action<string> requestHandler)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
|
||||
StringPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public StringPluginEndPoint AddResponsiveStringEndPoint(PluginFeature feature, string endPointName, Func<string, string?> requestHandler)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
|
||||
StringPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public RawPluginEndPoint AddRawEndPoint(PluginFeature feature, string endPointName, Func<IHttpContext, Task> requestHandler)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
|
||||
RawPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel, new()
|
||||
{
|
||||
if (module == null) throw new ArgumentNullException(nameof(module));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
DataModelJsonPluginEndPoint<T> endPoint = new(module, endPointName, PluginsModule);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
private void HandleDataModelRequest<T>(Module<T> module, T value) where T : DataModel, new()
|
||||
{
|
||||
}
|
||||
|
||||
public void RemovePluginEndPoint(PluginEndPoint endPoint)
|
||||
{
|
||||
PluginsModule.RemovePluginEndPoint(endPoint);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controller management
|
||||
|
||||
public void AddController<T>(PluginFeature feature) where T : WebApiController
|
||||
{
|
||||
_controllers.Add(new WebApiControllerRegistration<T>(feature));
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
public void RemoveController<T>() where T : WebApiController
|
||||
{
|
||||
_controllers.RemoveAll(r => r.ControllerType == typeof(T));
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Module management
|
||||
|
||||
public void AddModule(PluginFeature feature, Func<IWebModule> create)
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
|
||||
_modules.Add(new WebModuleRegistration(feature, create));
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
public void RemoveModule(Func<IWebModule> create)
|
||||
{
|
||||
_modules.RemoveAll(r => r.Create == create);
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
public void AddModule<T>(PluginFeature feature) where T : IWebModule
|
||||
{
|
||||
if (feature == null) throw new ArgumentNullException(nameof(feature));
|
||||
if (_modules.Any(r => r.WebModuleType == typeof(T)))
|
||||
return;
|
||||
|
||||
_modules.Add(new WebModuleRegistration(feature, typeof(T)));
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
public void RemoveModule<T>() where T : IWebModule
|
||||
{
|
||||
_modules.RemoveAll(r => r.WebModuleType == typeof(T));
|
||||
StartWebServer();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Handlers
|
||||
|
||||
private async Task JsonExceptionHandlerCallback(IHttpContext context, Exception exception)
|
||||
{
|
||||
context.Response.ContentType = MimeType.Json;
|
||||
await using TextWriter writer = context.OpenResponseText();
|
||||
|
||||
string response = JsonConvert.SerializeObject(new Dictionary<string, object?>
|
||||
{
|
||||
{"StatusCode", context.Response.StatusCode},
|
||||
{"StackTrace", exception.StackTrace},
|
||||
{"Type", exception.GetType().FullName},
|
||||
{"Message", exception.Message},
|
||||
{"Data", exception.Data},
|
||||
{"InnerException", exception.InnerException},
|
||||
{"HelpLink", exception.HelpLink},
|
||||
{"Source", exception.Source},
|
||||
{"HResult", exception.HResult}
|
||||
});
|
||||
await writer.WriteAsync(response);
|
||||
}
|
||||
|
||||
private async Task JsonNetSerializer(IHttpContext context, object? data)
|
||||
{
|
||||
context.Response.ContentType = MimeType.Json;
|
||||
await using TextWriter writer = context.OpenResponseText();
|
||||
string json = JsonConvert.SerializeObject(data, new JsonSerializerSettings {PreserveReferencesHandling = PreserveReferencesHandling.Objects});
|
||||
await writer.WriteAsync(json);
|
||||
}
|
||||
|
||||
private async Task HandleHttpExceptionJson(IHttpContext context, IHttpException httpException)
|
||||
{
|
||||
await context.SendStringAsync(JsonConvert.SerializeObject(httpException, Formatting.Indented), MimeType.Json, Encoding.UTF8);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in new issue
Block a user