mirror of
https://github.com/DarthAffe/RGB.NET.git
synced 2025-12-13 01:58:30 +00:00
commit
5a104eaf24
31
.github/workflows/ci.yml
vendored
31
.github/workflows/ci.yml
vendored
@ -11,16 +11,19 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-2022
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: |
|
||||
8.0.x
|
||||
7.0.x
|
||||
6.0.x
|
||||
- name: Git Semantic Version
|
||||
id: versioning
|
||||
uses: PaulHatch/semantic-version@v4.0.3
|
||||
@ -33,12 +36,6 @@ jobs:
|
||||
run: dotnet build --no-restore --configuration Release /p:Version=${{ steps.versioning.outputs.version }}
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal --configuration Release
|
||||
- name: Upload a Build Artifact NET5
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-NET5
|
||||
path: bin/net5.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload a Build Artifact NET6
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
@ -51,6 +48,12 @@ jobs:
|
||||
name: RGB.NET-NET7
|
||||
path: bin/net7.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload a Build Artifact NET8
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-NET8
|
||||
path: bin/net8.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload Nuget Build Artifact
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
@ -59,3 +62,11 @@ jobs:
|
||||
if-no-files-found: error
|
||||
- name: Nuget Push
|
||||
run: dotnet nuget push **\*.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_TOKEN }} --source https://api.nuget.org/v3/index.json
|
||||
- name: Symbols Push
|
||||
run: dotnet nuget push **\*.snupkg --skip-duplicate --api-key ${{ secrets.NUGET_TOKEN }} --source https://api.nuget.org/v3/index.json
|
||||
- name: Create Tag
|
||||
uses: mathieudutour/github-tag-action@v6.1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
custom_tag: ${{ steps.versioning.outputs.version }}
|
||||
tag_prefix: v
|
||||
|
||||
11
.github/workflows/pr_verify.yml
vendored
11
.github/workflows/pr_verify.yml
vendored
@ -7,14 +7,17 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-2022
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: |
|
||||
8.0.x
|
||||
7.0.x
|
||||
6.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
|
||||
25
.github/workflows/release.yml
vendored
25
.github/workflows/release.yml
vendored
@ -10,16 +10,19 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-2022
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: |
|
||||
8.0.x
|
||||
7.0.x
|
||||
6.0.x
|
||||
- name: Git Semantic Version
|
||||
id: versioning
|
||||
uses: PaulHatch/semantic-version@v4.0.3
|
||||
@ -32,12 +35,6 @@ jobs:
|
||||
run: dotnet build --no-restore --configuration Release /p:Version=${{ steps.versioning.outputs.version }}
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal --configuration Release
|
||||
- name: Upload a Build Artifact NET5
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-NET5
|
||||
path: bin/net5.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload a Build Artifact NET6
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
@ -50,6 +47,12 @@ jobs:
|
||||
name: RGB.NET-NET7
|
||||
path: bin/net7.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload a Build Artifact NET8
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-NET8
|
||||
path: bin/net8.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload Nuget Build Artifact
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
@ -61,6 +64,6 @@ jobs:
|
||||
with:
|
||||
tag_name: ${{ steps.versioning.outputs.version_tag }}
|
||||
generate_release_notes: true
|
||||
files: bin/net7.0/RGB.NET.*.dll
|
||||
files: bin/net8.0/RGB.NET.*.dll
|
||||
- name: Nuget Push
|
||||
run: dotnet nuget push **\*.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_TOKEN }} --source https://api.nuget.org/v3/index.json
|
||||
|
||||
@ -6,7 +6,7 @@ namespace RGB.NET.Core;
|
||||
/// <summary>
|
||||
/// Represents the default-behavior for the work with colors.
|
||||
/// </summary>
|
||||
public class DefaultColorBehavior : IColorBehavior
|
||||
public sealed class DefaultColorBehavior : IColorBehavior
|
||||
{
|
||||
#region Methods
|
||||
|
||||
@ -14,7 +14,7 @@ public class DefaultColorBehavior : IColorBehavior
|
||||
/// Converts the individual byte values of this <see cref="Color"/> to a human-readable string.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the individual byte values of this <see cref="Color"/>. For example "[A: 255, R: 255, G: 0, B: 0]".</returns>
|
||||
public virtual string ToString(in Color color) => $"[A: {color.GetA()}, R: {color.GetR()}, G: {color.GetG()}, B: {color.GetB()}]";
|
||||
public string ToString(in Color color) => $"[A: {color.GetA()}, R: {color.GetR()}, G: {color.GetG()}, B: {color.GetB()}]";
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
|
||||
@ -22,28 +22,31 @@ public class DefaultColorBehavior : IColorBehavior
|
||||
/// <param name="color">The color to test.</param>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
|
||||
public virtual bool Equals(in Color color, object? obj)
|
||||
{
|
||||
if (obj is not Color color2) return false;
|
||||
|
||||
return color.A.EqualsInTolerance(color2.A)
|
||||
&& color.R.EqualsInTolerance(color2.R)
|
||||
&& color.G.EqualsInTolerance(color2.G)
|
||||
&& color.B.EqualsInTolerance(color2.B);
|
||||
}
|
||||
public bool Equals(in Color color, object? obj) => obj is Color color2 && Equals(color, color2);
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
|
||||
/// </summary>
|
||||
/// <param name="color">The first color to test.</param>
|
||||
/// <param name="color2">The second color to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="color2" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
|
||||
public bool Equals(in Color color, in Color color2) => color.A.EqualsInTolerance(color2.A)
|
||||
&& color.R.EqualsInTolerance(color2.R)
|
||||
&& color.G.EqualsInTolerance(color2.G)
|
||||
&& color.B.EqualsInTolerance(color2.B);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Color" />.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
|
||||
public virtual int GetHashCode(in Color color) => HashCode.Combine(color.A, color.R, color.G, color.B);
|
||||
public int GetHashCode(in Color color) => HashCode.Combine(color.A, color.R, color.G, color.B);
|
||||
|
||||
/// <summary>
|
||||
/// Blends a <see cref="Color"/> over this color.
|
||||
/// </summary>
|
||||
/// <param name="baseColor">The <see cref="Color"/> to to blend over.</param>
|
||||
/// <param name="blendColor">The <see cref="Color"/> to blend.</param>
|
||||
public virtual Color Blend(in Color baseColor, in Color blendColor)
|
||||
public Color Blend(in Color baseColor, in Color blendColor)
|
||||
{
|
||||
if (blendColor.A.EqualsInTolerance(0)) return baseColor;
|
||||
|
||||
|
||||
@ -20,6 +20,14 @@ public interface IColorBehavior
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
|
||||
bool Equals(in Color color, object? obj);
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
|
||||
/// </summary>
|
||||
/// <param name="color">The first color to test.</param>
|
||||
/// <param name="color2">The second color to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="color2" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
|
||||
bool Equals(in Color color, in Color color2);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Color" />.
|
||||
/// </summary>
|
||||
|
||||
@ -11,7 +11,7 @@ namespace RGB.NET.Core;
|
||||
/// Represents an ARGB (alpha, red, green, blue) color.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
public readonly struct Color
|
||||
public readonly struct Color : IEquatable<Color>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
@ -196,6 +196,13 @@ public readonly struct Color
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object? obj) => Behavior.Equals(this, obj);
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified <see cref="Color" /> is equivalent to this <see cref="Color" />, as defined by the current <see cref="Behavior"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">The color to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="other" /> is equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
|
||||
public bool Equals(Color other) => Behavior.Equals(this, other);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Color" />, as defined by the current <see cref="Behavior"/>.
|
||||
/// </summary>
|
||||
|
||||
@ -11,7 +11,7 @@ public abstract class AbstractDecoratable<T> : AbstractBindable, IDecoratable<T>
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly List<T> _decorators = new();
|
||||
private readonly List<T> _decorators = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<T> Decorators { get; }
|
||||
|
||||
@ -29,7 +29,7 @@ public abstract class AbstractDecorator : AbstractBindable, IDecorator
|
||||
/// <summary>
|
||||
/// Gets a readonly-list of all <see cref="IDecoratable"/> this decorator is attached to.
|
||||
/// </summary>
|
||||
protected List<IDecoratable> DecoratedObjects { get; } = new();
|
||||
protected List<IDecoratable> DecoratedObjects { get; } = [];
|
||||
|
||||
#endregion
|
||||
|
||||
@ -46,14 +46,14 @@ public abstract class AbstractDecorator : AbstractBindable, IDecorator
|
||||
/// </summary>
|
||||
protected virtual void Detach()
|
||||
{
|
||||
List<IDecoratable> decoratables = new(DecoratedObjects);
|
||||
List<IDecoratable> decoratables = [..DecoratedObjects];
|
||||
foreach (IDecoratable decoratable in decoratables)
|
||||
{
|
||||
IEnumerable<Type> types = decoratable.GetType().GetInterfaces().Where(t => t.IsGenericType
|
||||
&& (t.Name == typeof(IDecoratable<>).Name)
|
||||
&& t.GenericTypeArguments[0].IsInstanceOfType(this));
|
||||
foreach (Type decoratableType in types)
|
||||
decoratableType.GetMethod(nameof(IDecoratable<IDecorator>.RemoveDecorator))?.Invoke(decoratable, new object[] { this });
|
||||
decoratableType.GetMethod(nameof(IDecoratable<IDecorator>.RemoveDecorator))?.Invoke(decoratable, [this]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,8 +6,7 @@ namespace RGB.NET.Core;
|
||||
/// <summary>
|
||||
/// Represents a basic decoratable.
|
||||
/// </summary>
|
||||
public interface IDecoratable : INotifyPropertyChanged
|
||||
{ }
|
||||
public interface IDecoratable : INotifyPropertyChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
|
||||
@ -4,5 +4,4 @@
|
||||
/// <summary>
|
||||
/// Represents a basic decorator decorating a <see cref="T:RGB.NET.Core.ILedGroup" />.
|
||||
/// </summary>
|
||||
public interface ILedGroupDecorator : IDecorator
|
||||
{ }
|
||||
public interface ILedGroupDecorator : IDecorator;
|
||||
@ -2,9 +2,12 @@
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
@ -50,7 +53,7 @@ public abstract class AbstractRGBDevice<TDeviceInfo> : Placeable, IRGBDevice<TDe
|
||||
/// <summary>
|
||||
/// Gets a dictionary containing all <see cref="Led"/> of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
protected Dictionary<LedId, Led> LedMapping { get; } = new();
|
||||
protected Dictionary<LedId, Led> LedMapping { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the update queue used to update this device.
|
||||
@ -84,6 +87,8 @@ public abstract class AbstractRGBDevice<TDeviceInfo> : Placeable, IRGBDevice<TDe
|
||||
{
|
||||
this.DeviceInfo = deviceInfo;
|
||||
this.UpdateQueue = updateQueue;
|
||||
|
||||
UpdateQueue.AddReferencingObject(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -97,12 +102,7 @@ public abstract class AbstractRGBDevice<TDeviceInfo> : Placeable, IRGBDevice<TDe
|
||||
DeviceUpdate();
|
||||
|
||||
// Send LEDs to SDK
|
||||
List<Led> ledsToUpdate = GetLedsToUpdate(flushLeds).ToList();
|
||||
|
||||
foreach (Led led in ledsToUpdate)
|
||||
led.Update();
|
||||
|
||||
UpdateLeds(ledsToUpdate);
|
||||
UpdateLeds(GetLedsToUpdate(flushLeds));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -110,7 +110,7 @@ public abstract class AbstractRGBDevice<TDeviceInfo> : Placeable, IRGBDevice<TDe
|
||||
/// </summary>
|
||||
/// <param name="flushLeds">Forces all LEDs to be treated as dirty.</param>
|
||||
/// <returns>The collection LEDs to update.</returns>
|
||||
protected virtual IEnumerable<Led> GetLedsToUpdate(bool flushLeds) => ((RequiresFlush || flushLeds) ? LedMapping.Values : LedMapping.Values.Where(x => x.IsDirty)).Where(led => led.RequestedColor?.A > 0);
|
||||
protected virtual IEnumerable<Led> GetLedsToUpdate(bool flushLeds) => ((RequiresFlush || flushLeds || UpdateQueue.RequiresFlush) ? LedMapping.Values : LedMapping.Values.Where(x => x.IsDirty)).Where(led => led.RequestedColor?.A > 0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerable of a custom data and color tuple for the specified leds.
|
||||
@ -121,45 +121,55 @@ public abstract class AbstractRGBDevice<TDeviceInfo> : Placeable, IRGBDevice<TDe
|
||||
/// </remarks>
|
||||
/// <param name="leds">The enumerable of leds to convert.</param>
|
||||
/// <returns>The enumerable of custom data and color tuples for the specified leds.</returns>
|
||||
protected virtual IEnumerable<(object key, Color color)> GetUpdateData(IEnumerable<Led> leds)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected (object key, Color color) GetUpdateData(Led led)
|
||||
{
|
||||
if (ColorCorrections.Count > 0)
|
||||
{
|
||||
foreach (Led led in leds)
|
||||
{
|
||||
Color color = led.Color;
|
||||
object key = led.CustomData ?? led.Id;
|
||||
Color color = led.Color;
|
||||
object key = led.CustomData ?? led.Id;
|
||||
|
||||
foreach (IColorCorrection colorCorrection in ColorCorrections)
|
||||
colorCorrection.ApplyTo(ref color);
|
||||
// ReSharper disable once ForCanBeConvertedToForeach - This causes an allocation that's not really needed here
|
||||
for (int i = 0; i < ColorCorrections.Count; i++)
|
||||
ColorCorrections[i].ApplyTo(ref color);
|
||||
|
||||
yield return (key, color);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Led led in leds)
|
||||
{
|
||||
Color color = led.Color;
|
||||
object key = led.CustomData ?? led.Id;
|
||||
|
||||
yield return (key, color);
|
||||
}
|
||||
}
|
||||
return (key, color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends all the updated <see cref="Led"/> to the device.
|
||||
/// </summary>
|
||||
protected virtual void UpdateLeds(IEnumerable<Led> ledsToUpdate) => UpdateQueue.SetData(GetUpdateData(ledsToUpdate));
|
||||
protected virtual void UpdateLeds(IEnumerable<Led> ledsToUpdate)
|
||||
{
|
||||
(object key, Color color)[] buffer = ArrayPool<(object, Color)>.Shared.Rent(LedMapping.Count);
|
||||
|
||||
int counter = 0;
|
||||
foreach (Led led in ledsToUpdate)
|
||||
{
|
||||
led.Update();
|
||||
|
||||
buffer[counter] = GetUpdateData(led);
|
||||
++counter;
|
||||
}
|
||||
|
||||
UpdateQueue.SetData(new ReadOnlySpan<(object, Color)>(buffer)[..counter]);
|
||||
|
||||
ArrayPool<(object, Color)>.Shared.Return(buffer);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Dispose()
|
||||
{
|
||||
try { UpdateQueue.Dispose(); } catch { /* :( */ }
|
||||
try
|
||||
{
|
||||
UpdateQueue.RemoveReferencingObject(this);
|
||||
if (!UpdateQueue.HasActiveReferences())
|
||||
UpdateQueue.Dispose();
|
||||
}
|
||||
catch { /* :( */ }
|
||||
try { LedMapping.Clear(); } catch { /* this really shouldn't happen */ }
|
||||
|
||||
IdGenerator.ResetCounter(GetType().Assembly);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -12,6 +12,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private bool _isDisposed = false;
|
||||
|
||||
private readonly double _defaultUpdateRateHardLimit;
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -20,14 +22,19 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
|
||||
/// <inheritdoc />
|
||||
public bool ThrowsExceptions { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of devices managed by this device-provider.
|
||||
/// </summary>
|
||||
protected List<IRGBDevice> InternalDevices { get; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<IRGBDevice> Devices { get; protected set; } = Enumerable.Empty<IRGBDevice>();
|
||||
public virtual IReadOnlyList<IRGBDevice> Devices => new ReadOnlyCollection<IRGBDevice>(InternalDevices);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dictionary containing the registered update triggers.
|
||||
/// Normally <see cref="UpdateTriggers"/> should be used to access them.
|
||||
/// </summary>
|
||||
protected Dictionary<int, IDeviceUpdateTrigger> UpdateTriggerMapping { get; } = new();
|
||||
protected Dictionary<int, IDeviceUpdateTrigger> UpdateTriggerMapping { get; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<(int id, IDeviceUpdateTrigger trigger)> UpdateTriggers => new ReadOnlyCollection<(int id, IDeviceUpdateTrigger trigger)>(UpdateTriggerMapping.Select(x => (x.Key, x.Value)).ToList());
|
||||
@ -39,6 +46,9 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<ExceptionEventArgs>? Exception;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<DevicesChangedEventArgs>? DevicesChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
@ -52,6 +62,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
|
||||
this._defaultUpdateRateHardLimit = defaultUpdateRateHardLimit;
|
||||
}
|
||||
|
||||
~AbstractRGBDeviceProvider() => Dispose(false);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
@ -59,6 +71,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
|
||||
/// <inheritdoc />
|
||||
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
|
||||
{
|
||||
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
|
||||
|
||||
ThrowsExceptions = throwExceptions;
|
||||
|
||||
try
|
||||
@ -67,7 +81,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
|
||||
|
||||
InitializeSDK();
|
||||
|
||||
Devices = new ReadOnlyCollection<IRGBDevice>(GetLoadedDevices(loadFilter).ToList());
|
||||
foreach (IRGBDevice device in GetLoadedDevices(loadFilter))
|
||||
AddDevice(device);
|
||||
|
||||
foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values)
|
||||
updateTrigger.Start();
|
||||
@ -99,7 +114,9 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
|
||||
/// <returns>The filtered collection of loaded devices.</returns>
|
||||
protected virtual IEnumerable<IRGBDevice> GetLoadedDevices(RGBDeviceType loadFilter)
|
||||
{
|
||||
List<IRGBDevice> devices = new();
|
||||
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
|
||||
|
||||
List<IRGBDevice> devices = [];
|
||||
foreach (IRGBDevice device in LoadDevices())
|
||||
{
|
||||
try
|
||||
@ -143,6 +160,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
|
||||
/// <returns>The update trigger mapped to the specified id.</returns>
|
||||
protected virtual IDeviceUpdateTrigger GetUpdateTrigger(int id = -1, double? updateRateHardLimit = null)
|
||||
{
|
||||
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
|
||||
|
||||
if (!UpdateTriggerMapping.TryGetValue(id, out IDeviceUpdateTrigger? updaeTrigger))
|
||||
UpdateTriggerMapping[id] = (updaeTrigger = CreateUpdateTrigger(id, updateRateHardLimit ?? _defaultUpdateRateHardLimit));
|
||||
|
||||
@ -162,23 +181,61 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
|
||||
/// </summary>
|
||||
protected virtual void Reset()
|
||||
{
|
||||
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
|
||||
|
||||
foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values)
|
||||
updateTrigger.Dispose();
|
||||
|
||||
foreach (IRGBDevice device in Devices)
|
||||
device.Dispose();
|
||||
|
||||
Devices = Enumerable.Empty<IRGBDevice>();
|
||||
List<IRGBDevice> devices = [..InternalDevices];
|
||||
foreach (IRGBDevice device in devices)
|
||||
RemoveDevice(device);
|
||||
|
||||
UpdateTriggerMapping.Clear();
|
||||
IsInitialized = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the provided device to the list of managed devices.
|
||||
/// </summary>
|
||||
/// <param name="device">The device to add.</param>
|
||||
/// <returns><c>true</c> if the device was added successfully; otherwise <c>false</c>.</returns>
|
||||
protected virtual bool AddDevice(IRGBDevice device)
|
||||
{
|
||||
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
|
||||
|
||||
if (InternalDevices.Contains(device)) return false;
|
||||
|
||||
InternalDevices.Add(device);
|
||||
try { OnDevicesChanged(DevicesChangedEventArgs.CreateDevicesAddedArgs(device)); } catch { /* we don't want to throw due to bad event handlers */ }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the provided device from the list of managed devices.
|
||||
/// </summary>
|
||||
/// <param name="device">The device to remove.</param>
|
||||
/// <returns><c>true</c> if the device was removed successfully; otherwise <c>false</c>.</returns>
|
||||
protected virtual bool RemoveDevice(IRGBDevice device)
|
||||
{
|
||||
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
|
||||
|
||||
if (!InternalDevices.Remove(device)) return false;
|
||||
|
||||
try { OnDevicesChanged(DevicesChangedEventArgs.CreateDevicesRemovedArgs(device)); } catch { /* we don't want to throw due to bad event handlers */ }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the <see cref="Exception"/>-event and throws the specified exception if <see cref="ThrowsExceptions"/> is true and it is not overriden in the event.
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception to throw.</param>
|
||||
/// <param name="isCritical">Indicates if the exception is critical for device provider to work correctly.</param>
|
||||
protected virtual void Throw(Exception ex, bool isCritical = false)
|
||||
public virtual void Throw(Exception ex, bool isCritical = false)
|
||||
{
|
||||
ExceptionEventArgs args = new(ex, isCritical, ThrowsExceptions);
|
||||
try { OnException(args); } catch { /* we don't want to throw due to bad event handlers */ }
|
||||
@ -188,18 +245,38 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws the <see cref="Exception"/> event.
|
||||
/// Throws the <see cref="Exception"/>.event.
|
||||
/// </summary>
|
||||
/// <param name="args">The parameters passed to the event.</param>
|
||||
protected virtual void OnException(ExceptionEventArgs args) => Exception?.Invoke(this, args);
|
||||
|
||||
/// <summary>
|
||||
/// Throws the <see cref="DevicesChanged"/>-event.
|
||||
/// </summary>
|
||||
/// <param name="args">The parameters passed to the event.</param>
|
||||
protected virtual void OnDevicesChanged(DevicesChangedEventArgs args) => DevicesChanged?.Invoke(this, args);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Dispose()
|
||||
public void Dispose()
|
||||
{
|
||||
Reset();
|
||||
if (_isDisposed) return;
|
||||
|
||||
try
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
catch { /* don't throw in dispose! */ }
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
_isDisposed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the object and frees all resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> if explicitely called through the Dispose-Method, <c>false</c> if called by the destructor.</param>
|
||||
protected virtual void Dispose(bool disposing) => Reset();
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -20,12 +20,16 @@ public interface IRGBDeviceProvider : IDisposable
|
||||
/// <summary>
|
||||
/// Indicates if exceptions in the device provider are thrown or silently ignored.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should only be set to <c>true</c> for debugging/development purposes.
|
||||
/// Production code should use the <see cref="Exception"/>-Event to handle exceptions.
|
||||
/// </remarks>
|
||||
bool ThrowsExceptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IRGBDevice"/> loaded by this <see cref="IRGBDeviceProvider"/>.
|
||||
/// </summary>
|
||||
IEnumerable<IRGBDevice> Devices { get; }
|
||||
IReadOnlyList<IRGBDevice> Devices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection <see cref="IDeviceUpdateTrigger"/> registered to this device provider.
|
||||
@ -41,6 +45,11 @@ public interface IRGBDeviceProvider : IDisposable
|
||||
/// </summary>
|
||||
event EventHandler<ExceptionEventArgs>? Exception;
|
||||
|
||||
/// <summary>
|
||||
/// Occures when the devices provided by this device provider changed.
|
||||
/// </summary>
|
||||
event EventHandler<DevicesChangedEventArgs>? DevicesChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
@ -93,6 +93,11 @@ public enum RGBDeviceType
|
||||
/// </summary>
|
||||
LedController = 1 << 15,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a game controller.
|
||||
/// </summary>
|
||||
GameController = 1 << 16,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a device where the type is not known or not present in the list.
|
||||
/// </summary>
|
||||
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a cooler-device
|
||||
/// </summary>
|
||||
public interface ICooler : IRGBDevice
|
||||
{ }
|
||||
public interface ICooler : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a DRAM-device
|
||||
/// </summary>
|
||||
public interface IDRAM : IRGBDevice
|
||||
{ }
|
||||
public interface IDRAM : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// represents a fan-device
|
||||
/// </summary>
|
||||
public interface IFan : IRGBDevice
|
||||
{ }
|
||||
public interface IFan : IRGBDevice;
|
||||
6
RGB.NET.Core/Devices/TypeInterfaces/IGameController.cs
Normal file
6
RGB.NET.Core/Devices/TypeInterfaces/IGameController.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a gamecontroller-device
|
||||
/// </summary>
|
||||
public interface IGameController: IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a graphics-card-device
|
||||
/// </summary>
|
||||
public interface IGraphicsCard : IRGBDevice
|
||||
{ }
|
||||
public interface IGraphicsCard : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a headset-device
|
||||
/// </summary>
|
||||
public interface IHeadset : IRGBDevice
|
||||
{ }
|
||||
public interface IHeadset : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a headset-stand-device
|
||||
/// </summary>
|
||||
public interface IHeadsetStand : IRGBDevice
|
||||
{ }
|
||||
public interface IHeadsetStand : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a keypad-device
|
||||
/// </summary>
|
||||
public interface IKeypad : IRGBDevice
|
||||
{ }
|
||||
public interface IKeypad : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a led-matrix-device
|
||||
/// </summary>
|
||||
public interface ILedMatrix : IRGBDevice
|
||||
{ }
|
||||
public interface ILedMatrix : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a led-stripe-device
|
||||
/// </summary>
|
||||
public interface ILedStripe : IRGBDevice
|
||||
{ }
|
||||
public interface ILedStripe : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a mainboard-device
|
||||
/// </summary>
|
||||
public interface IMainboard : IRGBDevice
|
||||
{ }
|
||||
public interface IMainboard : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a mouse-device
|
||||
/// </summary>
|
||||
public interface IMouse : IRGBDevice
|
||||
{ }
|
||||
public interface IMouse : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a mousepad-device
|
||||
/// </summary>
|
||||
public interface IMousepad : IRGBDevice
|
||||
{ }
|
||||
public interface IMousepad : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a speaker-device
|
||||
/// </summary>
|
||||
public interface ISpeaker : IRGBDevice
|
||||
{ }
|
||||
public interface ISpeaker : IRGBDevice;
|
||||
@ -3,5 +3,4 @@
|
||||
/// <summary>
|
||||
/// Represents a device with unkown or not specified type.
|
||||
/// </summary>
|
||||
public interface IUnknownDevice : IRGBDevice
|
||||
{ }
|
||||
public interface IUnknownDevice : IRGBDevice;
|
||||
27
RGB.NET.Core/Events/DevicesChangedEventArgs.cs
Normal file
27
RGB.NET.Core/Events/DevicesChangedEventArgs.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
public sealed class DevicesChangedEventArgs(IRGBDevice device, DevicesChangedEventArgs.DevicesChangedAction action)
|
||||
: EventArgs
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public IRGBDevice Device { get; } = device;
|
||||
public DevicesChangedAction Action { get; } = action;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
public static DevicesChangedEventArgs CreateDevicesAddedArgs(IRGBDevice addedDevice) => new(addedDevice, DevicesChangedAction.Added);
|
||||
public static DevicesChangedEventArgs CreateDevicesRemovedArgs(IRGBDevice removedDevice) => new(removedDevice, DevicesChangedAction.Removed);
|
||||
|
||||
#endregion
|
||||
|
||||
public enum DevicesChangedAction
|
||||
{
|
||||
Added,
|
||||
Removed
|
||||
}
|
||||
}
|
||||
@ -6,5 +6,4 @@ namespace RGB.NET.Core;
|
||||
/// <summary>
|
||||
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Updated" />-event.
|
||||
/// </summary>
|
||||
public class UpdatedEventArgs : EventArgs
|
||||
{ }
|
||||
public class UpdatedEventArgs : EventArgs;
|
||||
@ -26,7 +26,7 @@ public class UpdatingEventArgs : EventArgs
|
||||
/// <summary>
|
||||
/// Gets the custom-data provided by the trigger for this update.
|
||||
/// </summary>
|
||||
public CustomUpdateData CustomData { get; }
|
||||
public ICustomUpdateData CustomData { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
@ -39,7 +39,7 @@ public class UpdatingEventArgs : EventArgs
|
||||
/// <param name="deltaTime">The elapsed time (in seconds) since the last update.</param>
|
||||
/// <param name="trigger">The trigger causing this update.</param>
|
||||
/// <param name="customData">The custom-data provided by the trigger for this update.</param>
|
||||
public UpdatingEventArgs(double deltaTime, IUpdateTrigger? trigger, CustomUpdateData customData)
|
||||
public UpdatingEventArgs(double deltaTime, IUpdateTrigger? trigger, ICustomUpdateData customData)
|
||||
{
|
||||
this.DeltaTime = deltaTime;
|
||||
this.Trigger = trigger;
|
||||
|
||||
@ -151,12 +151,13 @@ public static class RectangleExtensions
|
||||
/// <returns>A array of <see cref="Point"/> containing the new locations of the corners of the original rectangle.</returns>
|
||||
public static Point[] Rotate(this in Rectangle rect, in Rotation rotation, in Point origin = new())
|
||||
{
|
||||
Point[] points = {
|
||||
rect.Location, // top left
|
||||
new(rect.Location.X + rect.Size.Width, rect.Location.Y), // top right
|
||||
new(rect.Location.X + rect.Size.Width, rect.Location.Y + rect.Size.Height), // bottom right
|
||||
new(rect.Location.X, rect.Location.Y + rect.Size.Height), // bottom right
|
||||
};
|
||||
Point[] points =
|
||||
[
|
||||
rect.Location, // top left
|
||||
new Point(rect.Location.X + rect.Size.Width, rect.Location.Y), // top right
|
||||
new Point(rect.Location.X + rect.Size.Width, rect.Location.Y + rect.Size.Height), // bottom right
|
||||
new Point(rect.Location.X, rect.Location.Y + rect.Size.Height), // bottom right
|
||||
];
|
||||
|
||||
float sin = MathF.Sin(rotation.Radians);
|
||||
float cos = MathF.Cos(rotation.Radians);
|
||||
|
||||
6
RGB.NET.Core/Extensions/ReferenceCountingExtension.cs
Normal file
6
RGB.NET.Core/Extensions/ReferenceCountingExtension.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
public static class ReferenceCountingExtension
|
||||
{
|
||||
public static bool HasActiveReferences(this IReferenceCounting target) => target.ActiveReferenceCount > 0;
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
@ -51,11 +53,23 @@ public abstract class AbstractLedGroup : AbstractDecoratable<ILedGroupDecorator>
|
||||
/// <inheritdoc />
|
||||
public virtual void OnDetach() { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IList<Led> ToList() => GetLeds().ToList();
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<Led> GetEnumerator() => GetLeds().GetEnumerator();
|
||||
|
||||
/// <inheritdoc />
|
||||
IDisposable? ILedGroup.ToListUnsafe(out IList<Led> leds) => ToListUnsafe(out leds);
|
||||
|
||||
protected virtual IDisposable? ToListUnsafe(out IList<Led> leds)
|
||||
{
|
||||
leds = ToList();
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
// ReSharper disable UnusedMemberInSuper.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
@ -39,4 +40,12 @@ public interface ILedGroup : IDecoratable<ILedGroupDecorator>, IEnumerable<Led>
|
||||
/// Called when the <see cref="ILedGroup"/> is detached from the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
void OnDetach();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list containing all <see cref="Led"/> in this group.
|
||||
/// </summary>
|
||||
/// <returns>A list containing all <see cref="Led"/> in this group.</returns>
|
||||
IList<Led> ToList();
|
||||
|
||||
internal IDisposable? ToListUnsafe(out IList<Led> leds);
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
@ -9,14 +11,16 @@ namespace RGB.NET.Core;
|
||||
/// <summary>
|
||||
/// Represents a ledgroup containing arbitrary <see cref="T:RGB.NET.Core.Led" />.
|
||||
/// </summary>
|
||||
public class ListLedGroup : AbstractLedGroup
|
||||
public sealed class ListLedGroup : AbstractLedGroup
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly ActionDisposable _unlockDisposable;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list containing the <see cref="Led"/> of this <see cref="ListLedGroup"/>.
|
||||
/// </summary>
|
||||
protected IList<Led> GroupLeds { get; } = new List<Led>();
|
||||
private readonly IList<Led> _groupLeds = new List<Led>();
|
||||
|
||||
#endregion
|
||||
|
||||
@ -29,7 +33,9 @@ public class ListLedGroup : AbstractLedGroup
|
||||
/// <param name="surface">Specifies the surface to attach this group to or <c>null</c> if the group should not be attached on creation.</param>
|
||||
public ListLedGroup(RGBSurface? surface)
|
||||
: base(surface)
|
||||
{ }
|
||||
{
|
||||
_unlockDisposable = new ActionDisposable(Unlock);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
@ -40,6 +46,8 @@ public class ListLedGroup : AbstractLedGroup
|
||||
public ListLedGroup(RGBSurface? surface, IEnumerable<Led> leds)
|
||||
: base(surface)
|
||||
{
|
||||
_unlockDisposable = new ActionDisposable(Unlock);
|
||||
|
||||
AddLeds(leds);
|
||||
}
|
||||
|
||||
@ -52,6 +60,8 @@ public class ListLedGroup : AbstractLedGroup
|
||||
public ListLedGroup(RGBSurface? surface, params Led[] leds)
|
||||
: base(surface)
|
||||
{
|
||||
_unlockDisposable = new ActionDisposable(Unlock);
|
||||
|
||||
AddLeds(leds);
|
||||
}
|
||||
|
||||
@ -71,10 +81,10 @@ public class ListLedGroup : AbstractLedGroup
|
||||
/// <param name="leds">The <see cref="Led"/> to add.</param>
|
||||
public void AddLeds(IEnumerable<Led> leds)
|
||||
{
|
||||
lock (GroupLeds)
|
||||
lock (_groupLeds)
|
||||
foreach (Led led in leds)
|
||||
if (!ContainsLed(led))
|
||||
GroupLeds.Add(led);
|
||||
_groupLeds.Add(led);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -89,9 +99,9 @@ public class ListLedGroup : AbstractLedGroup
|
||||
/// <param name="leds">The <see cref="Led"/> to remove.</param>
|
||||
public void RemoveLeds(IEnumerable<Led> leds)
|
||||
{
|
||||
lock (GroupLeds)
|
||||
lock (_groupLeds)
|
||||
foreach (Led led in leds)
|
||||
GroupLeds.Remove(led);
|
||||
_groupLeds.Remove(led);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -101,8 +111,8 @@ public class ListLedGroup : AbstractLedGroup
|
||||
/// <returns><c>true</c> if the LED is contained by this ledgroup; otherwise, <c>false</c>.</returns>
|
||||
public bool ContainsLed(Led led)
|
||||
{
|
||||
lock (GroupLeds)
|
||||
return GroupLeds.Contains(led);
|
||||
lock (_groupLeds)
|
||||
return _groupLeds.Contains(led);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -111,10 +121,10 @@ public class ListLedGroup : AbstractLedGroup
|
||||
/// <param name="groupToMerge">The ledgroup to merge.</param>
|
||||
public void MergeLeds(ILedGroup groupToMerge)
|
||||
{
|
||||
lock (GroupLeds)
|
||||
lock (_groupLeds)
|
||||
foreach (Led led in groupToMerge)
|
||||
if (!GroupLeds.Contains(led))
|
||||
GroupLeds.Add(led);
|
||||
if (!_groupLeds.Contains(led))
|
||||
_groupLeds.Add(led);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -122,11 +132,27 @@ public class ListLedGroup : AbstractLedGroup
|
||||
/// Gets a list containing the <see cref="T:RGB.NET.Core.Led" /> from this group.
|
||||
/// </summary>
|
||||
/// <returns>The list containing the <see cref="T:RGB.NET.Core.Led" />.</returns>
|
||||
protected override IEnumerable<Led> GetLeds()
|
||||
protected override IEnumerable<Led> GetLeds() => ToList();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets a list containing the <see cref="T:RGB.NET.Core.Led" /> from this group.
|
||||
/// </summary>
|
||||
/// <returns>The list containing the <see cref="T:RGB.NET.Core.Led" />.</returns>
|
||||
public override IList<Led> ToList()
|
||||
{
|
||||
lock (GroupLeds)
|
||||
return new List<Led>(GroupLeds);
|
||||
lock (_groupLeds)
|
||||
return new List<Led>(_groupLeds);
|
||||
}
|
||||
|
||||
protected override IDisposable ToListUnsafe(out IList<Led> leds)
|
||||
{
|
||||
Monitor.Enter(_groupLeds);
|
||||
leds = _groupLeds;
|
||||
return _unlockDisposable;
|
||||
}
|
||||
|
||||
private void Unlock() => Monitor.Exit(_groupLeds);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -46,7 +46,7 @@ public static class TimerHelper
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private static readonly HashSet<HighResolutionTimerDisposable> _timerLeases = new();
|
||||
private static readonly HashSet<HighResolutionTimerDisposable> _timerLeases = [];
|
||||
|
||||
#endregion
|
||||
|
||||
@ -143,7 +143,7 @@ public static class TimerHelper
|
||||
/// </summary>
|
||||
public static void DisposeAllHighResolutionTimerRequests()
|
||||
{
|
||||
List<HighResolutionTimerDisposable> timerLeases = new(_timerLeases);
|
||||
List<HighResolutionTimerDisposable> timerLeases = [.._timerLeases];
|
||||
foreach (HighResolutionTimerDisposable timer in timerLeases)
|
||||
timer.Dispose();
|
||||
}
|
||||
|
||||
@ -12,9 +12,9 @@ public static class IdGenerator
|
||||
#region Properties & Fields
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
private static readonly HashSet<string> _registeredIds = new();
|
||||
private static readonly Dictionary<Assembly, Dictionary<string, string>> _idMappings = new();
|
||||
private static readonly Dictionary<Assembly, Dictionary<string, int>> _counter = new();
|
||||
private static readonly HashSet<string> _registeredIds = [];
|
||||
private static readonly Dictionary<Assembly, Dictionary<string, string>> _idMappings = [];
|
||||
private static readonly Dictionary<Assembly, Dictionary<string, int>> _counter = [];
|
||||
// ReSharper restore InconsistentNaming
|
||||
|
||||
#endregion
|
||||
@ -33,8 +33,8 @@ public static class IdGenerator
|
||||
{
|
||||
if (!_idMappings.TryGetValue(callingAssembly, out Dictionary<string, string>? idMapping))
|
||||
{
|
||||
_idMappings.Add(callingAssembly, idMapping = new Dictionary<string, string>());
|
||||
_counter.Add(callingAssembly, new Dictionary<string, int>());
|
||||
_idMappings.Add(callingAssembly, idMapping = []);
|
||||
_counter.Add(callingAssembly, []);
|
||||
}
|
||||
|
||||
Dictionary<string, int> counterMapping = _counter[callingAssembly];
|
||||
@ -50,8 +50,7 @@ public static class IdGenerator
|
||||
idMapping.Add(id, mappedId);
|
||||
}
|
||||
|
||||
if (!counterMapping.ContainsKey(mappedId))
|
||||
counterMapping.Add(mappedId, 0);
|
||||
counterMapping.TryAdd(mappedId, 0);
|
||||
|
||||
int counter = ++counterMapping[mappedId];
|
||||
return counter <= 1 ? mappedId : $"{mappedId} ({counter})";
|
||||
|
||||
@ -9,7 +9,7 @@ namespace RGB.NET.Core;
|
||||
/// Represents a single LED of a RGB-device.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Id} {Color}")]
|
||||
public class Led : Placeable
|
||||
public sealed class Led : Placeable
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
|
||||
@ -372,6 +372,14 @@ public enum LedId
|
||||
Keyboard_Custom127 = 0x0000707F,
|
||||
Keyboard_Custom128 = 0x00007080,
|
||||
|
||||
Keyboard_IndicatorNumLock = 0x00008001,
|
||||
Keyboard_IndicatorCapsLock = 0x00008002,
|
||||
Keyboard_IndicatorScrollLock = 0x00008003,
|
||||
Keyboard_IndicatorWinLock = 0x00008004,
|
||||
Keyboard_IndicatorPower = 0x00008005,
|
||||
Keyboard_IndicatorMuted = 0x00008006,
|
||||
Keyboard_IndicatorMacro = 0x00008007,
|
||||
|
||||
/*### Mouse ###*/
|
||||
Mouse1 = 0x00100001,
|
||||
Mouse2 = 0x00100002,
|
||||
@ -6286,6 +6294,136 @@ public enum LedId
|
||||
Cooler127 = 0x00D0007F,
|
||||
Cooler128 = 0x00D00080,
|
||||
|
||||
/*### GameController ###*/
|
||||
GameController1 = 0x00E00001,
|
||||
GameController2 = 0x00E00002,
|
||||
GameController3 = 0x00E00003,
|
||||
GameController4 = 0x00E00004,
|
||||
GameController5 = 0x00E00005,
|
||||
GameController6 = 0x00E00006,
|
||||
GameController7 = 0x00E00007,
|
||||
GameController8 = 0x00E00008,
|
||||
GameController9 = 0x00E00009,
|
||||
GameController10 = 0x00E0000A,
|
||||
GameController11 = 0x00E0000B,
|
||||
GameController12 = 0x00E0000C,
|
||||
GameController13 = 0x00E0000D,
|
||||
GameController14 = 0x00E0000E,
|
||||
GameController15 = 0x00E0000F,
|
||||
GameController16 = 0x00E00010,
|
||||
GameController17 = 0x00E00011,
|
||||
GameController18 = 0x00E00012,
|
||||
GameController19 = 0x00E00013,
|
||||
GameController20 = 0x00E00014,
|
||||
GameController21 = 0x00E00015,
|
||||
GameController22 = 0x00E00016,
|
||||
GameController23 = 0x00E00017,
|
||||
GameController24 = 0x00E00018,
|
||||
GameController25 = 0x00E00019,
|
||||
GameController26 = 0x00E0001A,
|
||||
GameController27 = 0x00E0001B,
|
||||
GameController28 = 0x00E0001C,
|
||||
GameController29 = 0x00E0001D,
|
||||
GameController30 = 0x00E0001E,
|
||||
GameController31 = 0x00E0001F,
|
||||
GameController32 = 0x00E00020,
|
||||
GameController33 = 0x00E00021,
|
||||
GameController34 = 0x00E00022,
|
||||
GameController35 = 0x00E00023,
|
||||
GameController36 = 0x00E00024,
|
||||
GameController37 = 0x00E00025,
|
||||
GameController38 = 0x00E00026,
|
||||
GameController39 = 0x00E00027,
|
||||
GameController40 = 0x00E00028,
|
||||
GameController41 = 0x00E00029,
|
||||
GameController42 = 0x00E0002A,
|
||||
GameController43 = 0x00E0002B,
|
||||
GameController44 = 0x00E0002C,
|
||||
GameController45 = 0x00E0002D,
|
||||
GameController46 = 0x00E0002E,
|
||||
GameController47 = 0x00E0002F,
|
||||
GameController48 = 0x00E00030,
|
||||
GameController49 = 0x00E00031,
|
||||
GameController50 = 0x00E00032,
|
||||
GameController51 = 0x00E00033,
|
||||
GameController52 = 0x00E00034,
|
||||
GameController53 = 0x00E00035,
|
||||
GameController54 = 0x00E00036,
|
||||
GameController55 = 0x00E00037,
|
||||
GameController56 = 0x00E00038,
|
||||
GameController57 = 0x00E00039,
|
||||
GameController58 = 0x00E0003A,
|
||||
GameController59 = 0x00E0003B,
|
||||
GameController60 = 0x00E0003C,
|
||||
GameController61 = 0x00E0003D,
|
||||
GameController62 = 0x00E0003E,
|
||||
GameController63 = 0x00E0003F,
|
||||
GameController64 = 0x00E00040,
|
||||
GameController65 = 0x00E00041,
|
||||
GameController66 = 0x00E00042,
|
||||
GameController67 = 0x00E00043,
|
||||
GameController68 = 0x00E00044,
|
||||
GameController69 = 0x00E00045,
|
||||
GameController70 = 0x00E00046,
|
||||
GameController71 = 0x00E00047,
|
||||
GameController72 = 0x00E00048,
|
||||
GameController73 = 0x00E00049,
|
||||
GameController74 = 0x00E0004A,
|
||||
GameController75 = 0x00E0004B,
|
||||
GameController76 = 0x00E0004C,
|
||||
GameController77 = 0x00E0004D,
|
||||
GameController78 = 0x00E0004E,
|
||||
GameController79 = 0x00E0004F,
|
||||
GameController80 = 0x00E00050,
|
||||
GameController81 = 0x00E00051,
|
||||
GameController82 = 0x00E00052,
|
||||
GameController83 = 0x00E00053,
|
||||
GameController84 = 0x00E00054,
|
||||
GameController85 = 0x00E00055,
|
||||
GameController86 = 0x00E00056,
|
||||
GameController87 = 0x00E00057,
|
||||
GameController88 = 0x00E00058,
|
||||
GameController89 = 0x00E00059,
|
||||
GameController90 = 0x00E0005A,
|
||||
GameController91 = 0x00E0005B,
|
||||
GameController92 = 0x00E0005C,
|
||||
GameController93 = 0x00E0005D,
|
||||
GameController94 = 0x00E0005E,
|
||||
GameController95 = 0x00E0005F,
|
||||
GameController96 = 0x00E00060,
|
||||
GameController97 = 0x00E00061,
|
||||
GameController98 = 0x00E00062,
|
||||
GameController99 = 0x00E00063,
|
||||
GameController100 = 0x00E00064,
|
||||
GameController101 = 0x00E00065,
|
||||
GameController102 = 0x00E00066,
|
||||
GameController103 = 0x00E00067,
|
||||
GameController104 = 0x00E00068,
|
||||
GameController105 = 0x00E00069,
|
||||
GameController106 = 0x00E0006A,
|
||||
GameController107 = 0x00E0006B,
|
||||
GameController108 = 0x00E0006C,
|
||||
GameController109 = 0x00E0006D,
|
||||
GameController110 = 0x00E0006E,
|
||||
GameController111 = 0x00E0006F,
|
||||
GameController112 = 0x00E00070,
|
||||
GameController113 = 0x00E00071,
|
||||
GameController114 = 0x00E00072,
|
||||
GameController115 = 0x00E00073,
|
||||
GameController116 = 0x00E00074,
|
||||
GameController117 = 0x00E00075,
|
||||
GameController118 = 0x00E00076,
|
||||
GameController119 = 0x00E00077,
|
||||
GameController120 = 0x00E00078,
|
||||
GameController121 = 0x00E00079,
|
||||
GameController122 = 0x00E0007A,
|
||||
GameController123 = 0x00E0007B,
|
||||
GameController124 = 0x00E0007C,
|
||||
GameController125 = 0x00E0007D,
|
||||
GameController126 = 0x00E0007E,
|
||||
GameController127 = 0x00E0007F,
|
||||
GameController128 = 0x00E00080,
|
||||
|
||||
/*### Custom ###*/
|
||||
Custom1 = 0x0FE00001,
|
||||
Custom2 = 0x0FE00002,
|
||||
|
||||
@ -8,13 +8,19 @@ namespace RGB.NET.Core;
|
||||
/// Represents a mapping from <see cref="LedId"/> to a custom identifier.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The identifier the <see cref="LedId"/> is mapped to.</typeparam>
|
||||
public class LedMapping<T> : IEnumerable<(LedId ledId, T mapping)>
|
||||
public sealed class LedMapping<T> : IEnumerable<(LedId ledId, T mapping)>
|
||||
where T : notnull
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public static LedMapping<T> Empty { get; } = [];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly Dictionary<LedId, T> _mapping = new();
|
||||
private readonly Dictionary<T, LedId> _reverseMapping = new();
|
||||
private readonly Dictionary<LedId, T> _mapping = [];
|
||||
private readonly Dictionary<T, LedId> _reverseMapping = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of entries in this mapping.
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
@ -26,9 +27,9 @@ public abstract class AbstractBindable : IBindable
|
||||
/// <typeparam name="T">Type of the property.</typeparam>
|
||||
/// <param name="storage">Reference to the backing-filed.</param>
|
||||
/// <param name="value">Value to apply.</param>
|
||||
/// <returns><c>true</c> if the value needs to be updated; otherweise <c>false</c>.</returns>
|
||||
/// <returns><c>true</c> if the value needs to be updated; otherwise <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected virtual bool RequiresUpdate<T>(ref T storage, T value) => !Equals(storage, value);
|
||||
protected bool RequiresUpdate<T>(ref T storage, T value) => !EqualityComparer<T>.Default.Equals(storage, value);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the property already matches the desired value and updates it if not.
|
||||
@ -39,7 +40,7 @@ public abstract class AbstractBindable : IBindable
|
||||
/// <param name="propertyName">Name of the property used to notify listeners. This value is optional
|
||||
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute"/>.</param>
|
||||
/// <returns><c>true</c> if the value was changed, <c>false</c> if the existing value matched the desired value.</returns>
|
||||
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
if (!RequiresUpdate(ref storage, value)) return false;
|
||||
|
||||
@ -54,7 +55,7 @@ public abstract class AbstractBindable : IBindable
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property used to notify listeners. This value is optional
|
||||
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute"/>.</param>
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
#endregion
|
||||
|
||||
@ -5,6 +5,4 @@ namespace RGB.NET.Core;
|
||||
/// <summary>
|
||||
/// Represents a basic bindable class which notifies when a property value changes.
|
||||
/// </summary>
|
||||
public interface IBindable : INotifyPropertyChanged
|
||||
{
|
||||
}
|
||||
public interface IBindable : INotifyPropertyChanged;
|
||||
40
RGB.NET.Core/Misc/AbstractReferenceCounting.cs
Normal file
40
RGB.NET.Core/Misc/AbstractReferenceCounting.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
public abstract class AbstractReferenceCounting : IReferenceCounting
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly HashSet<object> _referencingObjects = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ActiveReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_referencingObjects)
|
||||
return _referencingObjects.Count;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddReferencingObject(object obj)
|
||||
{
|
||||
lock (_referencingObjects)
|
||||
_referencingObjects.Add(obj);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveReferencingObject(object obj)
|
||||
{
|
||||
lock (_referencingObjects)
|
||||
_referencingObjects.Remove(obj);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
12
RGB.NET.Core/Misc/ActionDisposable.cs
Normal file
12
RGB.NET.Core/Misc/ActionDisposable.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
public sealed class ActionDisposable(Action onDispose) : IDisposable
|
||||
{
|
||||
#region Methods
|
||||
|
||||
public void Dispose() => onDispose();
|
||||
|
||||
#endregion
|
||||
}
|
||||
21
RGB.NET.Core/Misc/IReferenceCounting.cs
Normal file
21
RGB.NET.Core/Misc/IReferenceCounting.cs
Normal file
@ -0,0 +1,21 @@
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
public interface IReferenceCounting
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the amount of currently registered referencing objects.
|
||||
/// </summary>
|
||||
public int ActiveReferenceCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given object to the list of referencing objects.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to add.</param>
|
||||
public void AddReferencingObject(object obj);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given object from the list of referencing objects.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to remove.</param>
|
||||
public void RemoveReferencingObject(object obj);
|
||||
}
|
||||
@ -211,7 +211,7 @@ public class Placeable : AbstractBindable, IPlaceable
|
||||
/// </summary>
|
||||
protected virtual void OnLocationChanged()
|
||||
{
|
||||
LocationChanged?.Invoke(this, new EventArgs());
|
||||
LocationChanged?.Invoke(this, EventArgs.Empty);
|
||||
UpdateActualPlaceableData();
|
||||
}
|
||||
|
||||
@ -220,7 +220,7 @@ public class Placeable : AbstractBindable, IPlaceable
|
||||
/// </summary>
|
||||
protected virtual void OnSizeChanged()
|
||||
{
|
||||
SizeChanged?.Invoke(this, new EventArgs());
|
||||
SizeChanged?.Invoke(this, EventArgs.Empty);
|
||||
UpdateActualPlaceableData();
|
||||
}
|
||||
|
||||
@ -229,7 +229,7 @@ public class Placeable : AbstractBindable, IPlaceable
|
||||
/// </summary>
|
||||
protected virtual void OnScaleChanged()
|
||||
{
|
||||
ScaleChanged?.Invoke(this, new EventArgs());
|
||||
ScaleChanged?.Invoke(this, EventArgs.Empty);
|
||||
UpdateActualPlaceableData();
|
||||
}
|
||||
|
||||
@ -238,24 +238,24 @@ public class Placeable : AbstractBindable, IPlaceable
|
||||
/// </summary>
|
||||
protected virtual void OnRotationChanged()
|
||||
{
|
||||
RotationChanged?.Invoke(this, new EventArgs());
|
||||
RotationChanged?.Invoke(this, EventArgs.Empty);
|
||||
UpdateActualPlaceableData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="ActualLocation"/> property was changed.
|
||||
/// </summary>
|
||||
protected virtual void OnActualLocationChanged() => ActualLocationChanged?.Invoke(this, new EventArgs());
|
||||
protected virtual void OnActualLocationChanged() => ActualLocationChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="ActualLocation"/> property was changed.
|
||||
/// </summary>
|
||||
protected virtual void OnActualSizeChanged() => ActualSizeChanged?.Invoke(this, new EventArgs());
|
||||
protected virtual void OnActualSizeChanged() => ActualSizeChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="Boundary"/> property was changed.
|
||||
/// </summary>
|
||||
protected virtual void OnBoundaryChanged() => BoundaryChanged?.Invoke(this, new EventArgs());
|
||||
protected virtual void OnBoundaryChanged() => BoundaryChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -10,7 +10,7 @@ namespace RGB.NET.Core;
|
||||
/// Represents a point consisting of a X- and a Y-position.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[X: {X}, Y: {Y}]")]
|
||||
public readonly struct Point
|
||||
public readonly struct Point : IEquatable<Point>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
@ -59,18 +59,20 @@ public readonly struct Point
|
||||
/// <returns>A string that contains the <see cref="X"/> and <see cref="Y"/> of this <see cref="Point"/>. For example "[X: 100, Y: 20]".</returns>
|
||||
public override string ToString() => $"[X: {X}, Y: {Y}]";
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified <see cref="Point" /> is equivalent to this <see cref="Point" />.
|
||||
/// </summary>
|
||||
/// <param name="other">The point to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="other" /> is equivalent to this <see cref="Point" />; otherwise, <c>false</c>.</returns>
|
||||
public bool Equals(Point other) => ((float.IsNaN(X) && float.IsNaN(other.X)) || X.EqualsInTolerance(other.X))
|
||||
&& ((float.IsNaN(Y) && float.IsNaN(other.Y)) || Y.EqualsInTolerance(other.Y));
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Point" /> and is equivalent to this <see cref="Point" />.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Point" /> equivalent to this <see cref="Point" />; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not Point comparePoint) return false;
|
||||
|
||||
return ((float.IsNaN(X) && float.IsNaN(comparePoint.X)) || X.EqualsInTolerance(comparePoint.X))
|
||||
&& ((float.IsNaN(Y) && float.IsNaN(comparePoint.Y)) || Y.EqualsInTolerance(comparePoint.Y));
|
||||
}
|
||||
public override bool Equals(object? obj) => obj is Point other && Equals(other);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Point" />.
|
||||
|
||||
@ -12,7 +12,7 @@ namespace RGB.NET.Core;
|
||||
/// Represents a rectangle defined by it's position and it's size.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[Location: {Location}, Size: {Size}]")]
|
||||
public readonly struct Rectangle
|
||||
public readonly struct Rectangle : IEquatable<Rectangle>
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
@ -57,7 +57,8 @@ public readonly struct Rectangle
|
||||
/// Initializes a new instance of the <see cref="Rectangle"/> class using the <see cref="Location"/>(0,0) and the specified <see cref="Core.Size"/>.
|
||||
/// </summary>
|
||||
/// <param name="size">The size of of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
public Rectangle(Size size) : this(new Point(), size)
|
||||
public Rectangle(Size size)
|
||||
: this(new Point(), size)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
@ -120,15 +121,13 @@ public readonly struct Rectangle
|
||||
public Rectangle(params Point[] points)
|
||||
: this(points.AsEnumerable())
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Rectangle" /> class using the specified list of <see cref="T:RGB.NET.Core.Point" />.
|
||||
/// The <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" /> is calculated to contain all points provided as parameters.
|
||||
/// </summary>
|
||||
/// <param name="points">The list of <see cref="T:RGB.NET.Core.Point" /> used to calculate the <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" />.</param>
|
||||
public Rectangle(IEnumerable<Point> points)
|
||||
: this()
|
||||
{
|
||||
bool hasPoint = false;
|
||||
float posX = float.MaxValue;
|
||||
@ -145,13 +144,37 @@ public readonly struct Rectangle
|
||||
posY2 = Math.Max(posY2, point.Y);
|
||||
}
|
||||
|
||||
(Point location, Size size) = hasPoint ? InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)) : InitializeFromPoints(new Point(0, 0), new Point(0, 0));
|
||||
(Point location, Size size) = hasPoint ? InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2))
|
||||
: InitializeFromPoints(new Point(0, 0), new Point(0, 0));
|
||||
|
||||
Location = location;
|
||||
Size = size;
|
||||
Center = new Point(Location.X + (Size.Width / 2.0f), Location.Y + (Size.Height / 2.0f));
|
||||
}
|
||||
|
||||
internal Rectangle(IList<Led> leds)
|
||||
{
|
||||
float posX = float.MaxValue;
|
||||
float posY = float.MaxValue;
|
||||
float posX2 = float.MinValue;
|
||||
float posY2 = float.MinValue;
|
||||
|
||||
// ReSharper disable once ForCanBeConvertedToForeach
|
||||
for (int i = 0; i < leds.Count; i++)
|
||||
{
|
||||
Rectangle rectangle = leds[i].AbsoluteBoundary;
|
||||
posX = Math.Min(posX, rectangle.Location.X);
|
||||
posY = Math.Min(posY, rectangle.Location.Y);
|
||||
posX2 = Math.Max(posX2, rectangle.Location.X + rectangle.Size.Width);
|
||||
posY2 = Math.Max(posY2, rectangle.Location.Y + rectangle.Size.Height);
|
||||
}
|
||||
|
||||
(Point location, Size size) = leds.Count > 0 ? InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)) : InitializeFromPoints(new Point(0, 0), new Point(0, 0));
|
||||
Location = location;
|
||||
Size = size;
|
||||
Center = new Point(Location.X + (Size.Width / 2.0f), Location.Y + (Size.Height / 2.0f));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
@ -172,35 +195,25 @@ public readonly struct Rectangle
|
||||
/// <returns>A string that contains the <see cref="Location"/> and <see cref="Size"/> of this <see cref="Rectangle"/>. For example "[Location: [X: 100, Y: 10], Size: [Width: 20, Height: [40]]".</returns>
|
||||
public override string ToString() => $"[Location: {Location}, Size: {Size}]";
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified <see cref="Rectangle" /> is equivalent to this <see cref="Rectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="other">The rectangle to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="other" /> is equivalent to this <see cref="Rectangle" />; otherwise, <c>false</c>.</returns>
|
||||
public bool Equals(Rectangle other) => (Location == other.Location) && (Size == other.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Rectangle" /> and is equivalent to this <see cref="Rectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Rectangle" /> equivalent to this <see cref="Rectangle" />; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not Rectangle compareRect)
|
||||
return false;
|
||||
|
||||
if (GetType() != compareRect.GetType())
|
||||
return false;
|
||||
|
||||
return (Location == compareRect.Location) && (Size == compareRect.Size);
|
||||
}
|
||||
public override bool Equals(object? obj) => obj is Rectangle other && Equals(other);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Rectangle" />.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Rectangle" />.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = Location.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Size.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
public override int GetHashCode() => HashCode.Combine(Location, Size);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ namespace RGB.NET.Core;
|
||||
/// Represents an angular rotation.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[{" + nameof(Degrees) + "}°]")]
|
||||
public readonly struct Rotation
|
||||
public readonly struct Rotation : IEquatable<Rotation>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
@ -9,7 +10,7 @@ namespace RGB.NET.Core;
|
||||
/// Represents a scaling.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[Horizontal: {Horizontal}, Vertical: {Vertical}]")]
|
||||
public readonly struct Scale
|
||||
public readonly struct Scale : IEquatable<Scale>
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
@ -49,6 +50,12 @@ public readonly struct Scale
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Horizontal"/> and <see cref="Vertical"/> value of this <see cref="Scale"/> to a human-readable string.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the <see cref="Horizontal"/> and <see cref="Vertical"/> value of this <see cref="Scale"/>. For example "[Horizontal: 1, Vertical: 0.5]".</returns>
|
||||
public override string ToString() => $"[Horizontal: {Horizontal}, Vertical: {Vertical}]\"";
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified <see cref="Scale"/> is equivalent to this <see cref="Scale" />.
|
||||
/// </summary>
|
||||
@ -67,7 +74,7 @@ public readonly struct Scale
|
||||
/// Returns a hash code for this <see cref="Scale" />.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Scale" />.</returns>
|
||||
public override int GetHashCode() { unchecked { return (Horizontal.GetHashCode() * 397) ^ Vertical.GetHashCode(); } }
|
||||
public override int GetHashCode() => HashCode.Combine(Horizontal, Vertical);
|
||||
|
||||
/// <summary>
|
||||
/// Deconstructs the scale into the horizontal and vertical value.
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
@ -9,7 +10,7 @@ namespace RGB.NET.Core;
|
||||
/// Represents a size consisting of a width and a height.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[Width: {Width}, Height: {Height}]")]
|
||||
public readonly struct Size
|
||||
public readonly struct Size : IEquatable<Size>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
@ -67,33 +68,26 @@ public readonly struct Size
|
||||
/// <returns>A string that contains the <see cref="Width"/> and <see cref="Height"/> of this <see cref="Size"/>. For example "[Width: 100, Height: 20]".</returns>
|
||||
public override string ToString() => $"[Width: {Width}, Height: {Height}]";
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified <see cref="Size" /> is equivalent to this <see cref="Size" />.
|
||||
/// </summary>
|
||||
/// <param name="other">The size to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="other" /> is equivalent to this <see cref="Size" />; otherwise, <c>false</c>.</returns>
|
||||
public bool Equals(Size other) => ((float.IsNaN(Width) && float.IsNaN(other.Width)) || Width.EqualsInTolerance(other.Width))
|
||||
&& ((float.IsNaN(Height) && float.IsNaN(other.Height)) || Height.EqualsInTolerance(other.Height));
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Size" /> and is equivalent to this <see cref="Size" />.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Size" /> equivalent to this <see cref="Size" />; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not Size size) return false;
|
||||
|
||||
(float width, float height) = size;
|
||||
return ((float.IsNaN(Width) && float.IsNaN(width)) || Width.EqualsInTolerance(width))
|
||||
&& ((float.IsNaN(Height) && float.IsNaN(height)) || Height.EqualsInTolerance(height));
|
||||
}
|
||||
public override bool Equals(object? obj) => obj is Size other && Equals(other);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Size" />.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Size" />.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = Width.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Height.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
public override int GetHashCode() => HashCode.Combine(Width, Height);
|
||||
|
||||
/// <summary>
|
||||
/// Deconstructs the size into the width and height value.
|
||||
|
||||
@ -17,7 +17,7 @@ surface.AlignDevices();
|
||||
surface.RegisterUpdateTrigger(new TimerUpdateTrigger());
|
||||
```
|
||||
|
||||
## Basis Rendering
|
||||
## Basic Rendering
|
||||
```csharp
|
||||
// Create a led-group containing all leds on the surface
|
||||
ILedGroup allLeds = new ListLedGroup(surface, surface.Leds);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net7.0;net6.0;net5.0</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<Optimize>false</Optimize>
|
||||
</PropertyGroup>
|
||||
@ -49,7 +49,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>$(NoWarn);CS1591;CS1572;CS1573</NoWarn>
|
||||
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
|
||||
<DefineConstants>RELEASE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helper/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ids/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=leds/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=misc/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=mvvm/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=positioning/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=rendering/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@ -22,7 +22,7 @@ public sealed class RGBSurface : AbstractBindable, IDisposable
|
||||
|
||||
private readonly IList<IRGBDevice> _devices = new List<IRGBDevice>();
|
||||
private readonly IList<IUpdateTrigger> _updateTriggers = new List<IUpdateTrigger>();
|
||||
private readonly List<ILedGroup> _ledGroups = new();
|
||||
private readonly List<ILedGroup> _ledGroups = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a readonly list containing all loaded <see cref="IRGBDevice"/>.
|
||||
@ -132,11 +132,12 @@ public sealed class RGBSurface : AbstractBindable, IDisposable
|
||||
/// Perform a full update for all devices. Updates only dirty <see cref="Led"/> by default, or all <see cref="Led"/>, if flushLeds is set to true.
|
||||
/// </summary>
|
||||
/// <param name="flushLeds">Specifies whether all <see cref="Led"/>, (including clean ones) should be updated.</param>
|
||||
public void Update(bool flushLeds = false) => Update(null, new CustomUpdateData((CustomUpdateDataIndex.FLUSH_LEDS, flushLeds)));
|
||||
//public void Update(bool flushLeds = false) => Update(null, new CustomUpdateData((CustomUpdateDataIndex.FLUSH_LEDS, flushLeds)));
|
||||
public void Update(bool flushLeds = false) => Update(null, flushLeds ? DefaultCustomUpdateData.FLUSH : DefaultCustomUpdateData.NO_FLUSH);
|
||||
|
||||
private void Update(object? updateTrigger, CustomUpdateData customData) => Update(updateTrigger as IUpdateTrigger, customData);
|
||||
private void Update(object? updateTrigger, ICustomUpdateData customData) => Update(updateTrigger as IUpdateTrigger, customData);
|
||||
|
||||
private void Update(IUpdateTrigger? updateTrigger, CustomUpdateData customData)
|
||||
private void Update(IUpdateTrigger? updateTrigger, ICustomUpdateData customData)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -149,19 +150,25 @@ public sealed class RGBSurface : AbstractBindable, IDisposable
|
||||
{
|
||||
OnUpdating(updateTrigger, customData);
|
||||
|
||||
// ReSharper disable ForCanBeConvertedToForeach - 'for' has a performance benefit (no enumerator allocation) here and since 'Update' is considered a hot path it's optimized
|
||||
if (render)
|
||||
lock (_ledGroups)
|
||||
{
|
||||
// Render brushes
|
||||
foreach (ILedGroup ledGroup in _ledGroups)
|
||||
try { Render(ledGroup); }
|
||||
for (int i = 0; i < _ledGroups.Count; i++)
|
||||
{
|
||||
try { Render(_ledGroups[i]); }
|
||||
catch (Exception ex) { OnException(ex); }
|
||||
}
|
||||
}
|
||||
|
||||
if (updateDevices)
|
||||
foreach (IRGBDevice device in _devices)
|
||||
try { device.Update(flushLeds); }
|
||||
for (int i = 0; i < _devices.Count; i++)
|
||||
{
|
||||
try { _devices[i].Update(flushLeds); }
|
||||
catch (Exception ex) { OnException(ex); }
|
||||
}
|
||||
// ReSharper restore ForCanBeConvertedToForeach
|
||||
|
||||
OnUpdated();
|
||||
}
|
||||
@ -177,7 +184,7 @@ public sealed class RGBSurface : AbstractBindable, IDisposable
|
||||
{
|
||||
List<IRGBDevice> devices;
|
||||
lock (Devices)
|
||||
devices = new List<IRGBDevice>(_devices);
|
||||
devices = [.._devices];
|
||||
|
||||
foreach (IRGBDevice device in devices)
|
||||
try { Detach(device); }
|
||||
@ -197,29 +204,31 @@ public sealed class RGBSurface : AbstractBindable, IDisposable
|
||||
/// <exception cref="ArgumentException">Thrown if the <see cref="IBrush.CalculationMode"/> of the Brush is not valid.</exception>
|
||||
private void Render(ILedGroup ledGroup)
|
||||
{
|
||||
IList<Led> leds = ledGroup.ToList();
|
||||
IBrush? brush = ledGroup.Brush;
|
||||
|
||||
if ((brush == null) || !brush.IsEnabled) return;
|
||||
|
||||
IEnumerable<(RenderTarget renderTarget, Color color)> render;
|
||||
switch (brush.CalculationMode)
|
||||
using (ledGroup.ToListUnsafe(out IList<Led> leds))
|
||||
{
|
||||
case RenderMode.Relative:
|
||||
Rectangle brushRectangle = new(leds.Select(led => led.AbsoluteBoundary));
|
||||
Point offset = new(-brushRectangle.Location.X, -brushRectangle.Location.Y);
|
||||
brushRectangle = brushRectangle.SetLocation(new Point(0, 0));
|
||||
render = brush.Render(brushRectangle, leds.Select(led => new RenderTarget(led, led.AbsoluteBoundary.Translate(offset))));
|
||||
break;
|
||||
case RenderMode.Absolute:
|
||||
render = brush.Render(Boundary, leds.Select(led => new RenderTarget(led, led.AbsoluteBoundary)));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"The CalculationMode '{brush.CalculationMode}' is not valid.");
|
||||
}
|
||||
IEnumerable<(RenderTarget renderTarget, Color color)> render;
|
||||
switch (brush.CalculationMode)
|
||||
{
|
||||
case RenderMode.Relative:
|
||||
Rectangle brushRectangle = new(leds);
|
||||
Point offset = new(-brushRectangle.Location.X, -brushRectangle.Location.Y);
|
||||
brushRectangle = brushRectangle.SetLocation(new Point(0, 0));
|
||||
render = brush.Render(brushRectangle, leds.Select(led => new RenderTarget(led, led.AbsoluteBoundary.Translate(offset))));
|
||||
break;
|
||||
case RenderMode.Absolute:
|
||||
render = brush.Render(Boundary, leds.Select(led => new RenderTarget(led, led.AbsoluteBoundary)));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"The CalculationMode '{brush.CalculationMode}' is not valid.");
|
||||
}
|
||||
|
||||
foreach ((RenderTarget renderTarget, Color c) in render)
|
||||
renderTarget.Led.Color = c;
|
||||
foreach ((RenderTarget renderTarget, Color c) in render)
|
||||
renderTarget.Led.Color = c;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -358,7 +367,7 @@ public sealed class RGBSurface : AbstractBindable, IDisposable
|
||||
/// <summary>
|
||||
/// Handles the needed event-calls before updating.
|
||||
/// </summary>
|
||||
private void OnUpdating(IUpdateTrigger? trigger, CustomUpdateData customData)
|
||||
private void OnUpdating(IUpdateTrigger? trigger, ICustomUpdateData customData)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@ -74,9 +74,13 @@ public abstract class AbstractBrush : AbstractDecoratable<IBrushDecorator>, IBru
|
||||
if (Decorators.Count == 0) return;
|
||||
|
||||
lock (Decorators)
|
||||
foreach (IBrushDecorator decorator in Decorators)
|
||||
// ReSharper disable once ForCanBeConvertedToForeach - Sadly this does not get optimized reliably and causes allocations if foreached
|
||||
for (int i = 0; i < Decorators.Count; i++)
|
||||
{
|
||||
IBrushDecorator decorator = Decorators[i];
|
||||
if (decorator.IsEnabled)
|
||||
decorator.ManipulateColor(rectangle, renderTarget, ref color);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -7,7 +7,7 @@ namespace RGB.NET.Core;
|
||||
/// <summary>
|
||||
/// Represents a brush drawing only a single color.
|
||||
/// </summary>
|
||||
public class SolidColorBrush : AbstractBrush
|
||||
public sealed class SolidColorBrush : AbstractBrush
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
@ -32,6 +32,8 @@ public class SolidColorBrush : AbstractBrush
|
||||
public SolidColorBrush(Color color)
|
||||
{
|
||||
this.Color = color;
|
||||
|
||||
CalculationMode = RenderMode.Absolute;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
/// <summary>
|
||||
/// Represents a brush drawing a texture.
|
||||
/// </summary>
|
||||
public class TextureBrush : AbstractBrush
|
||||
public sealed class TextureBrush : AbstractBrush
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
internal class EmptyTexture : ITexture
|
||||
internal sealed class EmptyTexture : ITexture
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
@ -12,16 +13,22 @@ namespace RGB.NET.Core;
|
||||
public abstract class PixelTexture<T> : ITexture
|
||||
where T : unmanaged
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int STACK_ALLOC_LIMIT = 1024;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly int _dataPerPixel;
|
||||
private readonly int _stride;
|
||||
/// <summary>
|
||||
/// Gets the underlying pixel data.
|
||||
/// </summary>
|
||||
protected abstract ReadOnlySpan<T> Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of data-entries per pixel.
|
||||
/// </summary>
|
||||
protected int DataPerPixel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stride of the data.
|
||||
/// </summary>
|
||||
protected int Stride { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sampler used to get the color of a region.
|
||||
@ -31,11 +38,6 @@ public abstract class PixelTexture<T> : ITexture
|
||||
/// <inheritdoc />
|
||||
public Size Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying pixel data.
|
||||
/// </summary>
|
||||
protected abstract ReadOnlySpan<T> Data { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Color this[in Point point]
|
||||
{
|
||||
@ -85,31 +87,12 @@ public abstract class PixelTexture<T> : ITexture
|
||||
if ((width == 0) || (height == 0)) return Color.Transparent;
|
||||
if ((width == 1) && (height == 1)) return GetColor(GetPixelData(x, y));
|
||||
|
||||
int bufferSize = width * height * _dataPerPixel;
|
||||
if (bufferSize <= STACK_ALLOC_LIMIT)
|
||||
{
|
||||
Span<T> buffer = stackalloc T[bufferSize];
|
||||
GetRegionData(x, y, width, height, buffer);
|
||||
SamplerInfo<T> samplerInfo = new(x, y, width, height, Stride, DataPerPixel, Data);
|
||||
|
||||
Span<T> pixelData = stackalloc T[_dataPerPixel];
|
||||
Sampler.Sample(new SamplerInfo<T>(width, height, buffer), pixelData);
|
||||
Span<T> pixelData = stackalloc T[DataPerPixel];
|
||||
Sampler.Sample(samplerInfo, pixelData);
|
||||
|
||||
return GetColor(pixelData);
|
||||
}
|
||||
else
|
||||
{
|
||||
T[] rent = ArrayPool<T>.Shared.Rent(bufferSize);
|
||||
|
||||
Span<T> buffer = new Span<T>(rent)[..bufferSize];
|
||||
GetRegionData(x, y, width, height, buffer);
|
||||
|
||||
Span<T> pixelData = stackalloc T[_dataPerPixel];
|
||||
Sampler.Sample(new SamplerInfo<T>(width, height, buffer), pixelData);
|
||||
|
||||
ArrayPool<T>.Shared.Return(rent);
|
||||
|
||||
return GetColor(pixelData);
|
||||
}
|
||||
return GetColor(pixelData);
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,8 +110,8 @@ public abstract class PixelTexture<T> : ITexture
|
||||
/// <param name="stride">The stride of the data or -1 if the width should be used.</param>
|
||||
public PixelTexture(int with, int height, int dataPerPixel, ISampler<T> sampler, int stride = -1)
|
||||
{
|
||||
this._stride = stride == -1 ? with : stride;
|
||||
this._dataPerPixel = dataPerPixel;
|
||||
this.Stride = stride == -1 ? with : stride;
|
||||
this.DataPerPixel = dataPerPixel;
|
||||
this.Sampler = sampler;
|
||||
|
||||
Size = new Size(with, height);
|
||||
@ -152,27 +135,7 @@ public abstract class PixelTexture<T> : ITexture
|
||||
/// <param name="y">The y-location.</param>
|
||||
/// <returns>The pixel-data on the specified location.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected virtual ReadOnlySpan<T> GetPixelData(int x, int y) => Data.Slice((y * _stride) + x, _dataPerPixel);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the pixel-data of the specified region to the passed buffer.
|
||||
/// </summary>
|
||||
/// <param name="x">The x-location of the region to get the data for.</param>
|
||||
/// <param name="y">The y-location of the region to get the data for.</param>
|
||||
/// <param name="width">The width of the region to get the data for.</param>
|
||||
/// <param name="height">The height of the region to get the data for.</param>
|
||||
/// <param name="buffer">The buffer to write the data to.</param>
|
||||
protected virtual void GetRegionData(int x, int y, int width, int height, in Span<T> buffer)
|
||||
{
|
||||
int dataWidth = width * _dataPerPixel;
|
||||
ReadOnlySpan<T> data = Data;
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
ReadOnlySpan<T> dataSlice = data.Slice((((y + i) * _stride) + x) * _dataPerPixel, dataWidth);
|
||||
Span<T> destination = buffer.Slice(i * dataWidth, dataWidth);
|
||||
dataSlice.CopyTo(destination);
|
||||
}
|
||||
}
|
||||
private ReadOnlySpan<T> GetPixelData(int x, int y) => Data.Slice((y * Stride) + x, DataPerPixel);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -225,6 +188,7 @@ public sealed class PixelTexture : PixelTexture<Color>
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override Color GetColor(in ReadOnlySpan<Color> pixel) => pixel[0];
|
||||
|
||||
#endregion
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
@ -10,7 +9,7 @@ namespace RGB.NET.Core;
|
||||
/// <remarks>
|
||||
/// Averages all components (A, R, G, B) of the colors separately which isn't ideal in cases where multiple different colors are combined.
|
||||
/// </remarks>
|
||||
public class AverageColorSampler : ISampler<Color>
|
||||
public sealed class AverageColorSampler : ISampler<Color>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
@ -30,20 +29,35 @@ public class AverageColorSampler : ISampler<Color>
|
||||
|
||||
float a = 0, r = 0, g = 0, b = 0;
|
||||
|
||||
if (Vector.IsHardwareAccelerated && (info.Data.Length >= Vector<float>.Count))
|
||||
if (Vector.IsHardwareAccelerated && (info.Height > 1) && (info.Width >= ELEMENTS_PER_VECTOR))
|
||||
{
|
||||
int chunks = info.Data.Length / ELEMENTS_PER_VECTOR;
|
||||
int missingElements = info.Data.Length - (chunks * ELEMENTS_PER_VECTOR);
|
||||
int chunks = info.Width / ELEMENTS_PER_VECTOR;
|
||||
int missingElements = info.Width - (chunks * ELEMENTS_PER_VECTOR);
|
||||
|
||||
Vector<float> sum = Vector<float>.Zero;
|
||||
|
||||
fixed (Color* colorPtr = &MemoryMarshal.GetReference(info.Data))
|
||||
for (int y = 0; y < info.Height; y++)
|
||||
{
|
||||
Color* current = colorPtr;
|
||||
for (int i = 0; i < chunks; i++)
|
||||
ReadOnlySpan<Color> data = info[y];
|
||||
|
||||
fixed (Color* colorPtr = data)
|
||||
{
|
||||
sum = Vector.Add(sum, *(Vector<float>*)current);
|
||||
current += ELEMENTS_PER_VECTOR;
|
||||
Color* current = colorPtr;
|
||||
for (int i = 0; i < chunks; i++)
|
||||
{
|
||||
sum = Vector.Add(sum, *(Vector<float>*)current);
|
||||
current += ELEMENTS_PER_VECTOR;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < missingElements; i++)
|
||||
{
|
||||
Color color = data[^(i + 1)];
|
||||
|
||||
a += color.A;
|
||||
r += color.R;
|
||||
g += color.G;
|
||||
b += color.B;
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,26 +68,17 @@ public class AverageColorSampler : ISampler<Color>
|
||||
g += sum[i + 2];
|
||||
b += sum[i + 3];
|
||||
}
|
||||
|
||||
for (int i = 0; i < missingElements; i++)
|
||||
{
|
||||
Color color = info.Data[^(i + 1)];
|
||||
|
||||
a += color.A;
|
||||
r += color.R;
|
||||
g += color.G;
|
||||
b += color.B;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Color color in info.Data)
|
||||
{
|
||||
a += color.A;
|
||||
r += color.R;
|
||||
g += color.G;
|
||||
b += color.B;
|
||||
}
|
||||
for (int y = 0; y < info.Height; y++)
|
||||
foreach (Color color in info[y])
|
||||
{
|
||||
a += color.A;
|
||||
r += color.R;
|
||||
g += color.G;
|
||||
b += color.B;
|
||||
}
|
||||
}
|
||||
|
||||
pixelData[0] = new Color(a / count, r / count, g / count, b / count);
|
||||
|
||||
@ -10,20 +10,29 @@ public readonly ref struct SamplerInfo<T>
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly ReadOnlySpan<T> _data;
|
||||
private readonly int _x;
|
||||
private readonly int _y;
|
||||
private readonly int _stride;
|
||||
private readonly int _dataPerPixel;
|
||||
private readonly int _dataWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of the region the data comes from.
|
||||
/// </summary>
|
||||
public int Width { get; }
|
||||
public readonly int Width;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of region the data comes from.
|
||||
/// </summary>
|
||||
public int Height { get; }
|
||||
public readonly int Height;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data to sample.
|
||||
/// Gets the data for the requested row.
|
||||
/// </summary>
|
||||
public ReadOnlySpan<T> Data { get; }
|
||||
/// <param name="row">The row to get the data for.</param>
|
||||
/// <returns>A readonly span containing the data of the row.</returns>
|
||||
public ReadOnlySpan<T> this[int row] => _data.Slice((((_y + row) * _stride) + _x) * _dataPerPixel, _dataWidth);
|
||||
|
||||
#endregion
|
||||
|
||||
@ -35,11 +44,17 @@ public readonly ref struct SamplerInfo<T>
|
||||
/// <param name="width">The width of the region the data comes from.</param>
|
||||
/// <param name="height">The height of region the data comes from.</param>
|
||||
/// <param name="data">The data to sample.</param>
|
||||
public SamplerInfo(int width, int height, ReadOnlySpan<T> data)
|
||||
public SamplerInfo(int x, int y, int width, int height, int stride, int dataPerPixel, in ReadOnlySpan<T> data)
|
||||
{
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._data = data;
|
||||
this._stride = stride;
|
||||
this._dataPerPixel = dataPerPixel;
|
||||
this.Width = width;
|
||||
this.Height = height;
|
||||
this.Data = data;
|
||||
|
||||
_dataWidth = width * dataPerPixel;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
@ -34,11 +35,24 @@ public static class CustomUpdateDataIndex
|
||||
/// <summary>
|
||||
/// Represents a set of custom data, each indexed by a string-key.
|
||||
/// </summary>
|
||||
public class CustomUpdateData
|
||||
public interface ICustomUpdateData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the value for a specific key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the value.</param>
|
||||
/// <returns>The value represented by the specified key.</returns>
|
||||
object? this[string key] { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a set of custom data, each indexed by a string-key.
|
||||
/// </summary>
|
||||
public sealed class CustomUpdateData : ICustomUpdateData
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private Dictionary<string, object?> _data = new();
|
||||
private readonly Dictionary<string, object?> _data = [];
|
||||
|
||||
#endregion
|
||||
|
||||
@ -51,8 +65,8 @@ public class CustomUpdateData
|
||||
/// <returns>The value represented by the specified key.</returns>
|
||||
public object? this[string key]
|
||||
{
|
||||
get => _data.TryGetValue(key.ToUpperInvariant(), out object? data) ? data : default;
|
||||
set => _data[key.ToUpperInvariant()] = value;
|
||||
get => _data.TryGetValue(key, out object? data) ? data : default;
|
||||
set => _data[key] = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -77,3 +91,39 @@ public class CustomUpdateData
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal sealed class DefaultCustomUpdateData : ICustomUpdateData
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public static readonly DefaultCustomUpdateData FLUSH = new(true);
|
||||
public static readonly DefaultCustomUpdateData NO_FLUSH = new(false);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly bool _flushLeds;
|
||||
|
||||
public object? this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.Equals(key, CustomUpdateDataIndex.FLUSH_LEDS, StringComparison.Ordinal))
|
||||
return _flushLeds;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private DefaultCustomUpdateData(bool flushLeds)
|
||||
{
|
||||
this._flushLeds = flushLeds;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -132,7 +133,7 @@ public class DeviceUpdateTrigger : AbstractUpdateTrigger, IDeviceUpdateTrigger
|
||||
/// <summary>
|
||||
/// Stops the trigger.
|
||||
/// </summary>
|
||||
public async void Stop()
|
||||
public virtual async void Stop()
|
||||
{
|
||||
if (!IsRunning) return;
|
||||
|
||||
@ -140,7 +141,9 @@ public class DeviceUpdateTrigger : AbstractUpdateTrigger, IDeviceUpdateTrigger
|
||||
|
||||
UpdateTokenSource?.Cancel();
|
||||
if (UpdateTask != null)
|
||||
await UpdateTask;
|
||||
try { await UpdateTask.ConfigureAwait(false); }
|
||||
catch (TaskCanceledException) { }
|
||||
catch (OperationCanceledException) { }
|
||||
|
||||
UpdateTask?.Dispose();
|
||||
UpdateTask = null;
|
||||
@ -156,11 +159,13 @@ public class DeviceUpdateTrigger : AbstractUpdateTrigger, IDeviceUpdateTrigger
|
||||
using (TimerHelper.RequestHighResolutionTimer())
|
||||
while (!UpdateToken.IsCancellationRequested)
|
||||
if (HasDataEvent.WaitOne(Timeout))
|
||||
LastUpdateTime = TimerHelper.Execute(() => OnUpdate(), UpdateFrequency * 1000);
|
||||
LastUpdateTime = TimerHelper.Execute(TimerExecute, UpdateFrequency * 1000);
|
||||
else if ((HeartbeatTimer > 0) && (LastUpdateTimestamp > 0) && (TimerHelper.GetElapsedTime(LastUpdateTimestamp) > HeartbeatTimer))
|
||||
OnUpdate(new CustomUpdateData().Heartbeat());
|
||||
}
|
||||
|
||||
private void TimerExecute() => OnUpdate();
|
||||
|
||||
protected override void OnUpdate(CustomUpdateData? updateData = null)
|
||||
{
|
||||
base.OnUpdate(updateData);
|
||||
@ -178,7 +183,12 @@ public class DeviceUpdateTrigger : AbstractUpdateTrigger, IDeviceUpdateTrigger
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose() => Stop();
|
||||
public override void Dispose()
|
||||
{
|
||||
Stop();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
@ -8,15 +7,20 @@ namespace RGB.NET.Core;
|
||||
/// </summary>
|
||||
/// <typeparam name="TIdentifier">The identifier used to identify the data processed by this queue.</typeparam>
|
||||
/// <typeparam name="TData">The type of the data processed by this queue.</typeparam>
|
||||
public interface IUpdateQueue<TIdentifier, TData> : IDisposable
|
||||
public interface IUpdateQueue<TIdentifier, TData> : IReferenceCounting, IDisposable
|
||||
where TIdentifier : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a bool indicating if the queue requires a flush of all data due to an internal error.
|
||||
/// </summary>
|
||||
bool RequiresFlush { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets or merges the provided data set in the current dataset and notifies the trigger that there is new data available.
|
||||
/// </summary>
|
||||
/// <param name="dataSet">The set of data.</param>
|
||||
// ReSharper disable once MemberCanBeProtected.Global
|
||||
void SetData(IEnumerable<(TIdentifier, TData)> dataSet);
|
||||
void SetData(ReadOnlySpan<(TIdentifier, TData)> dataSet);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the current data set.
|
||||
@ -27,5 +31,4 @@ public interface IUpdateQueue<TIdentifier, TData> : IDisposable
|
||||
/// <summary>
|
||||
/// Represents a generic update queue processing <see cref="Color"/>-data using <see cref="object"/>-identifiers.
|
||||
/// </summary>
|
||||
public interface IUpdateQueue : IUpdateQueue<object, Color>
|
||||
{ }
|
||||
public interface IUpdateQueue : IUpdateQueue<object, Color>;
|
||||
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
@ -10,14 +9,17 @@ namespace RGB.NET.Core;
|
||||
/// </summary>
|
||||
/// <typeparam name="TIdentifier">The type of the key used to identify some data.</typeparam>
|
||||
/// <typeparam name="TData">The type of the data.</typeparam>
|
||||
public abstract class UpdateQueue<TIdentifier, TData> : IUpdateQueue<TIdentifier, TData>
|
||||
public abstract class UpdateQueue<TIdentifier, TData> : AbstractReferenceCounting, IUpdateQueue<TIdentifier, TData>
|
||||
where TIdentifier : notnull
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly object _dataLock = new();
|
||||
private readonly IDeviceUpdateTrigger _updateTrigger;
|
||||
private readonly Dictionary<TIdentifier, TData> _currentDataSet = new();
|
||||
private readonly Dictionary<TIdentifier, TData> _currentDataSet = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool RequiresFlush { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@ -62,7 +64,7 @@ public abstract class UpdateQueue<TIdentifier, TData> : IUpdateQueue<TIdentifier
|
||||
_currentDataSet.Clear();
|
||||
}
|
||||
|
||||
Update(data);
|
||||
RequiresFlush = !Update(data);
|
||||
|
||||
ArrayPool<(TIdentifier, TData)>.Shared.Return(dataSet);
|
||||
}
|
||||
@ -78,17 +80,16 @@ public abstract class UpdateQueue<TIdentifier, TData> : IUpdateQueue<TIdentifier
|
||||
/// Performs the update this queue is responsible for.
|
||||
/// </summary>
|
||||
/// <param name="dataSet">The set of data that needs to be updated.</param>
|
||||
protected abstract void Update(in ReadOnlySpan<(TIdentifier key, TData color)> dataSet);
|
||||
protected abstract bool Update(in ReadOnlySpan<(TIdentifier key, TData color)> dataSet);
|
||||
|
||||
/// <summary>
|
||||
/// Sets or merges the provided data set in the current dataset and notifies the trigger that there is new data available.
|
||||
/// </summary>
|
||||
/// <param name="dataSet">The set of data.</param>
|
||||
// ReSharper disable once MemberCanBeProtected.Global
|
||||
public virtual void SetData(IEnumerable<(TIdentifier, TData)> dataSet)
|
||||
public virtual void SetData(ReadOnlySpan<(TIdentifier, TData)> data)
|
||||
{
|
||||
IList<(TIdentifier, TData)> data = dataSet.ToList();
|
||||
if (data.Count == 0) return;
|
||||
if (data.Length == 0) return;
|
||||
|
||||
lock (_dataLock)
|
||||
{
|
||||
|
||||
@ -83,12 +83,12 @@ public sealed class ManualUpdateTrigger : AbstractUpdateTrigger
|
||||
OnStartup();
|
||||
|
||||
while (!UpdateToken.IsCancellationRequested)
|
||||
{
|
||||
if (_mutex.WaitOne(100))
|
||||
LastUpdateTime = TimerHelper.Execute(() => OnUpdate(_customUpdateData));
|
||||
}
|
||||
LastUpdateTime = TimerHelper.Execute(TimerExecute);
|
||||
}
|
||||
|
||||
private void TimerExecute() => OnUpdate(_customUpdateData);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose() => Stop();
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ namespace RGB.NET.Core;
|
||||
/// <summary>
|
||||
/// Represents an update trigger that triggers in a set interval.
|
||||
/// </summary>
|
||||
public class TimerUpdateTrigger : AbstractUpdateTrigger
|
||||
public sealed class TimerUpdateTrigger : AbstractUpdateTrigger
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
@ -21,17 +21,17 @@ public class TimerUpdateTrigger : AbstractUpdateTrigger
|
||||
/// <summary>
|
||||
/// Gets or sets the update loop of this trigger.
|
||||
/// </summary>
|
||||
protected Task? UpdateTask { get; set; }
|
||||
private Task? _updateTask;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cancellation token source used to create the cancellation token checked by the <see cref="UpdateTask"/>.
|
||||
/// Gets or sets the cancellation token source used to create the cancellation token checked by the <see cref="_updateTask"/>.
|
||||
/// </summary>
|
||||
protected CancellationTokenSource? UpdateTokenSource { get; set; }
|
||||
private CancellationTokenSource? _updateTokenSource;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cancellation token checked by the <see cref="UpdateTask"/>.
|
||||
/// Gets or sets the cancellation token checked by the <see cref="_updateTask"/>.
|
||||
/// </summary>
|
||||
protected CancellationToken UpdateToken { get; set; }
|
||||
private CancellationToken _updateToken;
|
||||
|
||||
private double _updateFrequency = 1.0 / 30.0;
|
||||
/// <summary>
|
||||
@ -88,11 +88,11 @@ public class TimerUpdateTrigger : AbstractUpdateTrigger
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (UpdateTask == null)
|
||||
if (_updateTask == null)
|
||||
{
|
||||
UpdateTokenSource?.Dispose();
|
||||
UpdateTokenSource = new CancellationTokenSource();
|
||||
UpdateTask = Task.Factory.StartNew(UpdateLoop, (UpdateToken = UpdateTokenSource.Token), TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
_updateTokenSource?.Dispose();
|
||||
_updateTokenSource = new CancellationTokenSource();
|
||||
_updateTask = Task.Factory.StartNew(UpdateLoop, (_updateToken = _updateTokenSource.Token), TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,13 +104,13 @@ public class TimerUpdateTrigger : AbstractUpdateTrigger
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (UpdateTask != null)
|
||||
if (_updateTask != null)
|
||||
{
|
||||
UpdateTokenSource?.Cancel();
|
||||
_updateTokenSource?.Cancel();
|
||||
try
|
||||
{
|
||||
// ReSharper disable once MethodSupportsCancellation
|
||||
UpdateTask.Wait();
|
||||
_updateTask.Wait();
|
||||
}
|
||||
catch (AggregateException)
|
||||
{
|
||||
@ -118,8 +118,8 @@ public class TimerUpdateTrigger : AbstractUpdateTrigger
|
||||
}
|
||||
finally
|
||||
{
|
||||
UpdateTask.Dispose();
|
||||
UpdateTask = null;
|
||||
_updateTask.Dispose();
|
||||
_updateTask = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,16 +130,16 @@ public class TimerUpdateTrigger : AbstractUpdateTrigger
|
||||
OnStartup();
|
||||
|
||||
using (TimerHelper.RequestHighResolutionTimer())
|
||||
while (!UpdateToken.IsCancellationRequested)
|
||||
LastUpdateTime = TimerHelper.Execute(() => OnUpdate(_customUpdateData), UpdateFrequency * 1000);
|
||||
|
||||
while (!_updateToken.IsCancellationRequested)
|
||||
LastUpdateTime = TimerHelper.Execute(TimerExecute, UpdateFrequency * 1000);
|
||||
}
|
||||
|
||||
private void TimerExecute() => OnUpdate(_customUpdateData);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
Stop();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -12,15 +12,25 @@ namespace RGB.NET.Devices.Asus;
|
||||
/// <summary>
|
||||
/// Represents a device provider responsible for Cooler Master devices.
|
||||
/// </summary>
|
||||
public class AsusDeviceProvider : AbstractRGBDeviceProvider
|
||||
public sealed class AsusDeviceProvider : AbstractRGBDeviceProvider
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private static readonly object _lock = new();
|
||||
|
||||
private static AsusDeviceProvider? _instance;
|
||||
/// <summary>
|
||||
/// Gets the singleton <see cref="AsusDeviceProvider"/> instance.
|
||||
/// </summary>
|
||||
public static AsusDeviceProvider Instance => _instance ?? new AsusDeviceProvider();
|
||||
public static AsusDeviceProvider Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
return _instance ?? new AsusDeviceProvider();
|
||||
}
|
||||
}
|
||||
|
||||
private IAuraSdk2? _sdk;
|
||||
private IAuraSyncDeviceCollection? _devices; //HACK DarthAffe 05.04.2021: Due to some researches this might fix the access violation in the asus-sdk
|
||||
@ -35,8 +45,11 @@ public class AsusDeviceProvider : AbstractRGBDeviceProvider
|
||||
/// <exception cref="InvalidOperationException">Thrown if this constructor is called even if there is already an instance of this class.</exception>
|
||||
public AsusDeviceProvider()
|
||||
{
|
||||
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(AsusDeviceProvider)}");
|
||||
_instance = this;
|
||||
lock (_lock)
|
||||
{
|
||||
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(AsusDeviceProvider)}");
|
||||
_instance = this;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -60,6 +73,7 @@ public class AsusDeviceProvider : AbstractRGBDeviceProvider
|
||||
for (int i = 0; i < _devices.Count; i++)
|
||||
{
|
||||
IAuraSyncDevice device = _devices[i];
|
||||
|
||||
yield return (AsusDeviceType)device.Type switch
|
||||
{
|
||||
AsusDeviceType.MB_RGB => new AsusMainboardRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Mainboard, device, WMIHelper.GetMainboardInfo()?.model ?? device.Name), GetUpdateTrigger()),
|
||||
@ -68,26 +82,30 @@ public class AsusDeviceProvider : AbstractRGBDeviceProvider
|
||||
AsusDeviceType.HEADSET_RGB => new AsusHeadsetRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Headset, device), GetUpdateTrigger()),
|
||||
AsusDeviceType.DRAM_RGB => new AsusDramRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.DRAM, device), GetUpdateTrigger()),
|
||||
AsusDeviceType.KEYBOARD_RGB => new AsusKeyboardRGBDevice(new AsusKeyboardRGBDeviceInfo(device), LedMappings.KeyboardMapping, GetUpdateTrigger()),
|
||||
AsusDeviceType.KEYBOARD_5ZONE_RGB => new AsusKeyboardRGBDevice(new AsusKeyboardRGBDeviceInfo(device), null, GetUpdateTrigger()),
|
||||
AsusDeviceType.NB_KB_RGB => new AsusKeyboardRGBDevice(new AsusKeyboardRGBDeviceInfo(device), LedMappings.KeyboardMapping, GetUpdateTrigger()),
|
||||
AsusDeviceType.NB_KB_4ZONE_RGB => new AsusKeyboardRGBDevice(new AsusKeyboardRGBDeviceInfo(device), null, GetUpdateTrigger()),
|
||||
AsusDeviceType.MOUSE_RGB => new AsusMouseRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Mouse, device), GetUpdateTrigger()),
|
||||
_ => new AsusUnspecifiedRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Unknown, device), LedId.Custom1, GetUpdateTrigger())
|
||||
AsusDeviceType.TERMINAL_RGB => new AsusUnspecifiedRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.LedController, device), LedId.Custom1, GetUpdateTrigger()),
|
||||
_ => new AsusUnspecifiedRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Unknown, device), LedId.Unknown1, GetUpdateTrigger())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose();
|
||||
lock (_lock)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
try { _sdk?.ReleaseControl(0); }
|
||||
catch { /* at least we tried */ }
|
||||
try { _sdk?.ReleaseControl(0); }
|
||||
catch { /* at least we tried */ }
|
||||
|
||||
_devices = null;
|
||||
_sdk = null;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
_devices = null;
|
||||
_sdk = null;
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -16,9 +16,12 @@ internal enum AsusDeviceType : uint
|
||||
EXTERNAL_BLUE_RAY_RGB = 0x61000,
|
||||
DRAM_RGB = 0x70000,
|
||||
KEYBOARD_RGB = 0x80000,
|
||||
KEYBOARD_5ZONE_RGB = 0x80001,
|
||||
NB_KB_RGB = 0x81000,
|
||||
NB_KB_4ZONE_RGB = 0x81001,
|
||||
MOUSE_RGB = 0x90000,
|
||||
CHASSIS_RGB = 0xB0000,
|
||||
PROJECTOR_RGB = 0xC0000
|
||||
PROJECTOR_RGB = 0xC0000,
|
||||
WATERCOOLER_RGB = 0xD1000,
|
||||
TERMINAL_RGB = 0xE0000
|
||||
}
|
||||
@ -4,9 +4,9 @@ namespace RGB.NET.Devices.Asus;
|
||||
|
||||
/// <inheritdoc cref="AsusRGBDevice{TDeviceInfo}" />
|
||||
/// <summary>
|
||||
/// Represents a Asus headset.
|
||||
/// Represents a Asus device that is otherwise not handled by a more specific helper.
|
||||
/// </summary>
|
||||
public class AsusUnspecifiedRGBDevice : AsusRGBDevice<AsusRGBDeviceInfo>, IUnknownDevice
|
||||
public sealed class AsusUnspecifiedRGBDevice : AsusRGBDevice<AsusRGBDeviceInfo>, IUnknownDevice
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
@ -18,9 +18,9 @@ public class AsusUnspecifiedRGBDevice : AsusRGBDevice<AsusRGBDeviceInfo>, IUnkno
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Asus.AsusHeadsetRGBDevice" /> class.
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Asus.AsusUnspecifiedRGBDevice" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The specific information provided by Asus for the headset.</param>
|
||||
/// <param name="info">The specific information provided by Asus for the device.</param>
|
||||
/// <param name="baseLedId">The ledId of the first led of this device. All other leds are created by incrementing this base-id by 1.</param>
|
||||
/// <param name="updateTrigger">The update trigger used to update this device.</param>
|
||||
internal AsusUnspecifiedRGBDevice(AsusRGBDeviceInfo info, LedId baseLedId, IDeviceUpdateTrigger updateTrigger)
|
||||
|
||||
@ -8,7 +8,7 @@ namespace RGB.NET.Devices.Asus;
|
||||
/// <summary>
|
||||
/// Represents the update-queue performing updates for asus devices.
|
||||
/// </summary>
|
||||
public class AsusUpdateQueue : UpdateQueue
|
||||
public sealed class AsusUpdateQueue : UpdateQueue
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
@ -17,7 +17,7 @@ public class AsusUpdateQueue : UpdateQueue
|
||||
/// <summary>
|
||||
/// The device to be updated.
|
||||
/// </summary>
|
||||
protected IAuraSyncDevice Device { get; }
|
||||
private readonly IAuraSyncDevice _device;
|
||||
|
||||
#endregion
|
||||
|
||||
@ -31,7 +31,7 @@ public class AsusUpdateQueue : UpdateQueue
|
||||
public AsusUpdateQueue(IDeviceUpdateTrigger updateTrigger, IAuraSyncDevice device)
|
||||
: base(updateTrigger)
|
||||
{
|
||||
this.Device = device;
|
||||
this._device = device;
|
||||
|
||||
this._lights = new IAuraRgbLight[device.Lights.Count];
|
||||
for (int i = 0; i < device.Lights.Count; i++)
|
||||
@ -43,14 +43,14 @@ public class AsusUpdateQueue : UpdateQueue
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Update(in ReadOnlySpan<(object key, Color color)> dataSet)
|
||||
protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet)
|
||||
{
|
||||
try
|
||||
{
|
||||
if ((Device.Type == (uint)AsusDeviceType.KEYBOARD_RGB) || (Device.Type == (uint)AsusDeviceType.NB_KB_RGB))
|
||||
if ((_device.Type == (uint)AsusDeviceType.KEYBOARD_RGB) || (_device.Type == (uint)AsusDeviceType.NB_KB_RGB))
|
||||
{
|
||||
if (Device is not IAuraSyncKeyboard keyboard)
|
||||
return;
|
||||
if (_device is not IAuraSyncKeyboard keyboard)
|
||||
return true;
|
||||
|
||||
foreach ((object customData, Color value) in dataSet)
|
||||
{
|
||||
@ -87,12 +87,17 @@ public class AsusUpdateQueue : UpdateQueue
|
||||
}
|
||||
}
|
||||
|
||||
Device.Apply();
|
||||
}
|
||||
catch
|
||||
{ /* "The server threw an exception." seems to be a thing here ... */
|
||||
}
|
||||
}
|
||||
_device.Apply();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AsusDeviceProvider.Instance.Throw(ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -5,5 +5,4 @@ namespace RGB.NET.Devices.Asus;
|
||||
/// <summary>
|
||||
/// Represents a asus RGB-device.
|
||||
/// </summary>
|
||||
public interface IAsusRGBDevice : IRGBDevice
|
||||
{ }
|
||||
public interface IAsusRGBDevice : IRGBDevice;
|
||||
@ -6,7 +6,7 @@ namespace RGB.NET.Devices.Asus;
|
||||
/// <summary>
|
||||
/// Represents a Asus graphicsCard.
|
||||
/// </summary>
|
||||
public class AsusGraphicsCardRGBDevice : AsusRGBDevice<AsusRGBDeviceInfo>, IGraphicsCard
|
||||
public sealed class AsusGraphicsCardRGBDevice : AsusRGBDevice<AsusRGBDeviceInfo>, IGraphicsCard
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ namespace RGB.NET.Devices.Asus;
|
||||
/// <summary>
|
||||
/// Represents a Asus headset.
|
||||
/// </summary>
|
||||
public class AsusHeadsetRGBDevice : AsusRGBDevice<AsusRGBDeviceInfo>, IHeadset
|
||||
public sealed class AsusHeadsetRGBDevice : AsusRGBDevice<AsusRGBDeviceInfo>, IHeadset
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ internal static class WMIHelper
|
||||
if ((_systemModelInfo == null) && (_systemModelSearcher != null))
|
||||
foreach (ManagementBaseObject managementBaseObject in _systemModelSearcher.Get())
|
||||
{
|
||||
_systemModelInfo = managementBaseObject["Model"]?.ToString();
|
||||
_systemModelInfo = managementBaseObject["Model"].ToString();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ internal static class WMIHelper
|
||||
if (!_mainboardInfo.HasValue && (_mainboardSearcher != null))
|
||||
foreach (ManagementBaseObject managementBaseObject in _mainboardSearcher.Get())
|
||||
{
|
||||
_mainboardInfo = (managementBaseObject["Manufacturer"]?.ToString() ?? string.Empty, managementBaseObject["Product"]?.ToString() ?? string.Empty);
|
||||
_mainboardInfo = (managementBaseObject["Manufacturer"].ToString() ?? string.Empty, managementBaseObject["Product"].ToString() ?? string.Empty);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ internal static class WMIHelper
|
||||
if ((_graphicsCardInfo == null) && (_graphicsCardSearcher != null))
|
||||
foreach (ManagementBaseObject managementBaseObject in _graphicsCardSearcher.Get())
|
||||
{
|
||||
_graphicsCardInfo = managementBaseObject["Name"]?.ToString();
|
||||
_graphicsCardInfo = managementBaseObject["Name"].ToString();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -188,4 +188,49 @@ public static class LedMappings
|
||||
{ LedId.Keyboard_Custom59, 131 },
|
||||
{ LedId.Keyboard_Custom60, 133 },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A LED mapping containing extra lights for the ROG Strix G15 (2021)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// ASUS notebooks have extra lights under wide keys like space and backspace, these do not appear as keys on the device.
|
||||
/// Instead they only appear in the Lights enumerable, this mapping maps the matching keys to the index of these lights.
|
||||
/// There are also some keys which do not use the default key scan code mappings for LEDs, and instead rely on lights.
|
||||
/// </para>
|
||||
/// <para>You may add more of these by further populating <see cref="AsusKeyboardRGBDevice.ExtraLedMappings"/>.</para>
|
||||
/// </remarks>
|
||||
public static LedMapping<int> ROGStrixG15 { get; } = new()
|
||||
{
|
||||
{ LedId.Keyboard_Custom71, 4 }, //Mic Mute
|
||||
{ LedId.Keyboard_Custom72, 5 }, //Fan
|
||||
{ LedId.Keyboard_Custom73, 6 }, //ROG Logo
|
||||
//{ LedId.Keyboard_Function, 127 }, //commented out because adding a mapping fails if a mapping already exists for a key, even if it is incorrect for this device
|
||||
//use Keyboard_Custom36 in the default mapping to get the Fn key on this laptop
|
||||
|
||||
{ LedId.Keyboard_Custom52, 55 }, //backspace extra LEDs (x2) - these are named to match the appropriate LEDs in the previous ROG Zephyrus mapping
|
||||
{ LedId.Keyboard_Custom53, 57 },
|
||||
{ LedId.Keyboard_Custom54, 97 }, //enter extra LEDs (x2)
|
||||
{ LedId.Keyboard_Custom55, 99 },
|
||||
{ LedId.Keyboard_Custom56, 118 }, //right shift extra LEDs (x2)
|
||||
{ LedId.Keyboard_Custom57, 120 },
|
||||
{ LedId.Keyboard_Custom58, 130 }, //space bar extra LEDs (x3)
|
||||
{ LedId.Keyboard_Custom59, 131 }, //this one specifically is also exposed as Custom7 (AsusLedID.KEY_NOCONVERT) in the main map
|
||||
{ LedId.Keyboard_Custom60, 133 },
|
||||
|
||||
{ LedId.Keyboard_MediaVolumeDown, 2 },
|
||||
{ LedId.Keyboard_MediaVolumeUp, 3 },
|
||||
{ LedId.Keyboard_MediaPlay, 58 },
|
||||
{ LedId.Keyboard_MediaStop, 79 },
|
||||
{ LedId.Keyboard_MediaPreviousTrack, 100 },
|
||||
{ LedId.Keyboard_MediaNextTrack, 121 },
|
||||
|
||||
{ LedId.LedStripe1, 174 }, //front LED strip; yes, these are in reverse order, since the SDK exposes them from right to left
|
||||
{ LedId.LedStripe2, 173 },
|
||||
{ LedId.LedStripe3, 172 },
|
||||
{ LedId.LedStripe4, 171 },
|
||||
{ LedId.LedStripe5, 170 },
|
||||
{ LedId.LedStripe6, 169 },
|
||||
|
||||
};
|
||||
}
|
||||
@ -20,13 +20,13 @@ public record AsusKeyboardExtraMapping(Regex Regex, LedMapping<int> LedMapping);
|
||||
/// <summary>
|
||||
/// Represents a Asus keyboard.
|
||||
/// </summary>
|
||||
public class AsusKeyboardRGBDevice : AsusRGBDevice<AsusKeyboardRGBDeviceInfo>, IKeyboard
|
||||
public sealed class AsusKeyboardRGBDevice : AsusRGBDevice<AsusKeyboardRGBDeviceInfo>, IKeyboard
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly LedMapping<AsusLedId>? _ledMapping;
|
||||
private readonly Dictionary<LedId, AsusLedId> _ledAsusLed = new();
|
||||
private readonly Dictionary<LedId, int> _ledAsusLights = new();
|
||||
private readonly Dictionary<LedId, AsusLedId> _ledAsusLed = [];
|
||||
private readonly Dictionary<LedId, int> _ledAsusLights = [];
|
||||
|
||||
IKeyboardDeviceInfo IKeyboard.DeviceInfo => DeviceInfo;
|
||||
|
||||
@ -35,10 +35,11 @@ public class AsusKeyboardRGBDevice : AsusRGBDevice<AsusKeyboardRGBDeviceInfo>, I
|
||||
/// <para>Note: These LED mappings should be based on light indexes.</para>
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static readonly List<AsusKeyboardExtraMapping> ExtraLedMappings = new()
|
||||
{
|
||||
new AsusKeyboardExtraMapping(new Regex("(ROG Zephyrus Duo 15).*?"), LedMappings.ROGZephyrusDuo15)
|
||||
};
|
||||
public static readonly List<AsusKeyboardExtraMapping> ExtraLedMappings =
|
||||
[
|
||||
new AsusKeyboardExtraMapping(new Regex("(ROG Zephyrus Duo 15).*?"), LedMappings.ROGZephyrusDuo15),
|
||||
new AsusKeyboardExtraMapping(new Regex("(ROG Strix G513QM).*?"), LedMappings.ROGStrixG15)
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
@ -65,7 +66,7 @@ public class AsusKeyboardRGBDevice : AsusRGBDevice<AsusKeyboardRGBDeviceInfo>, I
|
||||
|
||||
private void InitializeLayout()
|
||||
{
|
||||
if (DeviceInfo.Device.Type != (uint)AsusDeviceType.NB_KB_4ZONE_RGB)
|
||||
if ((DeviceInfo.Device.Type != (uint)AsusDeviceType.NB_KB_4ZONE_RGB) && (DeviceInfo.Device.Type !=(uint)AsusDeviceType.KEYBOARD_5ZONE_RGB))
|
||||
{
|
||||
int pos = 0;
|
||||
int unknownLed = (int)LedId.Unknown1;
|
||||
|
||||
@ -7,7 +7,7 @@ namespace RGB.NET.Devices.Asus;
|
||||
/// <summary>
|
||||
/// Represents a generic information for a <see cref="T:RGB.NET.Devices.Asus.AsusKeyboardRGBDevice" />.
|
||||
/// </summary>
|
||||
public class AsusKeyboardRGBDeviceInfo : AsusRGBDeviceInfo, IKeyboardDeviceInfo
|
||||
public sealed class AsusKeyboardRGBDeviceInfo : AsusRGBDeviceInfo, IKeyboardDeviceInfo
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
@ -15,7 +15,7 @@ public class AsusKeyboardRGBDeviceInfo : AsusRGBDeviceInfo, IKeyboardDeviceInfo
|
||||
/// The ASUS SDK returns useless names for notebook keyboards, possibly for others as well.
|
||||
/// Keep a list of those and rely on <see cref="WMIHelper.GetSystemModelInfo()"/> to get the real model
|
||||
/// </summary>
|
||||
private static readonly List<string> GENERIC_DEVICE_NAMES = new() { "NotebookKeyboard" };
|
||||
private static readonly List<string> GENERIC_DEVICE_NAMES = ["NotebookKeyboard"];
|
||||
|
||||
/// <inheritdoc />
|
||||
public KeyboardLayoutType Layout => KeyboardLayoutType.Unknown;
|
||||
|
||||
@ -6,7 +6,7 @@ namespace RGB.NET.Devices.Asus;
|
||||
/// <summary>
|
||||
/// Represents a Asus mainboard.
|
||||
/// </summary>
|
||||
public class AsusMainboardRGBDevice : AsusRGBDevice<AsusRGBDeviceInfo>, IMainboard
|
||||
public sealed class AsusMainboardRGBDevice : AsusRGBDevice<AsusRGBDeviceInfo>, IMainboard
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ namespace RGB.NET.Devices.Asus;
|
||||
/// <summary>
|
||||
/// Represents a Asus mouse.
|
||||
/// </summary>
|
||||
public class AsusMouseRGBDevice : AsusRGBDevice<AsusRGBDeviceInfo>, IMouse
|
||||
public sealed class AsusMouseRGBDevice : AsusRGBDevice<AsusRGBDeviceInfo>, IMouse
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net7.0;net6.0;net5.0</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<Optimize>false</Optimize>
|
||||
</PropertyGroup>
|
||||
@ -48,7 +48,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>$(NoWarn);CS1591;CS1572;CS1573</NoWarn>
|
||||
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
|
||||
<DefineConstants>RELEASE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -57,21 +57,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Management" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_PackageFiles Include="$(OutputPath)\net6.0\Interop.AuraServiceLib.dll">
|
||||
<BuildAction>None</BuildAction>
|
||||
<PackagePath>lib\net6.0\</PackagePath>
|
||||
</_PackageFiles>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_PackageFiles Include="$(OutputPath)\net5.0\Interop.AuraServiceLib.dll">
|
||||
<BuildAction>None</BuildAction>
|
||||
<PackagePath>lib\net5.0\</PackagePath>
|
||||
</_PackageFiles>
|
||||
<PackageReference Include="System.Management" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -8,7 +8,7 @@ namespace RGB.NET.Devices.CoolerMaster;
|
||||
/// Specifies the <see cref="T:RGB.NET.Core.RGBDeviceType" /> of a field.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class DeviceTypeAttribute : Attribute
|
||||
public sealed class DeviceTypeAttribute : Attribute
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
|
||||
@ -13,27 +13,37 @@ namespace RGB.NET.Devices.CoolerMaster;
|
||||
/// <summary>
|
||||
/// Represents a device provider responsible for Cooler Master devices.
|
||||
/// </summary>
|
||||
public class CoolerMasterDeviceProvider : AbstractRGBDeviceProvider
|
||||
public sealed class CoolerMasterDeviceProvider : AbstractRGBDeviceProvider
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private static readonly object _lock = new();
|
||||
|
||||
private static CoolerMasterDeviceProvider? _instance;
|
||||
/// <summary>
|
||||
/// Gets the singleton <see cref="CoolerMasterDeviceProvider"/> instance.
|
||||
/// </summary>
|
||||
public static CoolerMasterDeviceProvider Instance => _instance ?? new CoolerMasterDeviceProvider();
|
||||
public static CoolerMasterDeviceProvider Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
return _instance ?? new CoolerMasterDeviceProvider();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a modifiable list of paths used to find the native SDK-dlls for x86 applications.
|
||||
/// The first match will be used.
|
||||
/// </summary>
|
||||
public static List<string> PossibleX86NativePaths { get; } = new() { "x86/CMSDK.dll" };
|
||||
public static List<string> PossibleX86NativePaths { get; } = ["x86/CMSDK.dll"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a modifiable list of paths used to find the native SDK-dlls for x64 applications.
|
||||
/// The first match will be used.
|
||||
/// </summary>
|
||||
public static List<string> PossibleX64NativePaths { get; } = new() { "x64/CMSDK.dll" };
|
||||
public static List<string> PossibleX64NativePaths { get; } = ["x64/CMSDK.dll"];
|
||||
|
||||
#endregion
|
||||
|
||||
@ -45,8 +55,11 @@ public class CoolerMasterDeviceProvider : AbstractRGBDeviceProvider
|
||||
/// <exception cref="InvalidOperationException">Thrown if this constructor is called even if there is already an instance of this class.</exception>
|
||||
public CoolerMasterDeviceProvider()
|
||||
{
|
||||
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CoolerMasterDeviceProvider)}");
|
||||
_instance = this;
|
||||
lock (_lock)
|
||||
{
|
||||
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CoolerMasterDeviceProvider)}");
|
||||
_instance = this;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -94,12 +107,17 @@ public class CoolerMasterDeviceProvider : AbstractRGBDeviceProvider
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose();
|
||||
lock (_lock)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
try { _CoolerMasterSDK.Reload(); }
|
||||
catch { /* Unlucky.. */ }
|
||||
try { _CoolerMasterSDK.Reload(); }
|
||||
catch { /* Unlucky.. */ }
|
||||
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
using System.ComponentModel;
|
||||
using RGB.NET.Core;
|
||||
|
||||
#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
namespace RGB.NET.Devices.CoolerMaster;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
namespace RGB.NET.Devices.CoolerMaster;
|
||||
|
||||
/// <summary>
|
||||
/// Contains a list of available effects.
|
||||
/// </summary>
|
||||
public enum CoolerMasterEffects
|
||||
{
|
||||
FullOn = 0,
|
||||
Breath = 1,
|
||||
BreathCycle = 2,
|
||||
Single = 3,
|
||||
Wave = 4,
|
||||
Ripple = 5,
|
||||
Cross = 6,
|
||||
Rain = 7,
|
||||
Star = 8,
|
||||
Snake = 9,
|
||||
Rec = 10,
|
||||
|
||||
Spectrum = 11,
|
||||
RapidFire = 12,
|
||||
Indicator = 13, //mouse Effect
|
||||
FireBall = 14,
|
||||
WaterRipple = 15,
|
||||
ReactivePunch = 16,
|
||||
Snowing = 17,
|
||||
HeartBeat = 18,
|
||||
ReactiveTornade = 19,
|
||||
|
||||
Multi1 = 0xE0,
|
||||
Multi2 = 0xE1,
|
||||
Multi3 = 0xE2,
|
||||
Multi4 = 0xE3,
|
||||
Off = 0xFE
|
||||
}
|
||||
@ -33,6 +33,8 @@ public abstract class CoolerMasterRGBDevice<TDeviceInfo> : AbstractRGBDevice<TDe
|
||||
_CoolerMasterSDK.EnableLedControl(false, DeviceInfo.DeviceIndex);
|
||||
|
||||
base.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -8,7 +8,7 @@ namespace RGB.NET.Devices.CoolerMaster;
|
||||
/// <summary>
|
||||
/// Represents the update-queue performing updates for cooler master devices.
|
||||
/// </summary>
|
||||
public class CoolerMasterUpdateQueue : UpdateQueue
|
||||
public sealed class CoolerMasterUpdateQueue : UpdateQueue
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
@ -37,15 +37,26 @@ public class CoolerMasterUpdateQueue : UpdateQueue
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Update(in ReadOnlySpan<(object key, Color color)> dataSet)
|
||||
protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet)
|
||||
{
|
||||
foreach ((object key, Color color) in dataSet)
|
||||
try
|
||||
{
|
||||
(int row, int column) = ((int, int))key;
|
||||
_deviceMatrix.KeyColor[row, column] = new _CoolerMasterKeyColor(color.GetR(), color.GetG(), color.GetB());
|
||||
foreach ((object key, Color color) in dataSet)
|
||||
{
|
||||
(int row, int column) = ((int, int))key;
|
||||
_deviceMatrix.KeyColor[row, column] = new _CoolerMasterKeyColor(color.GetR(), color.GetG(), color.GetB());
|
||||
}
|
||||
|
||||
_CoolerMasterSDK.SetAllLedColor(_deviceMatrix, _deviceIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CoolerMasterDeviceProvider.Instance.Throw(ex);
|
||||
}
|
||||
|
||||
_CoolerMasterSDK.SetAllLedColor(_deviceMatrix, _deviceIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -5,5 +5,4 @@ namespace RGB.NET.Devices.CoolerMaster;
|
||||
/// <summary>
|
||||
/// Represents a CoolerMaster RGB-device.
|
||||
/// </summary>
|
||||
public interface ICoolerMasterRGBDevice : IRGBDevice
|
||||
{ }
|
||||
public interface ICoolerMasterRGBDevice : IRGBDevice;
|
||||
@ -7,7 +7,7 @@ namespace RGB.NET.Devices.CoolerMaster;
|
||||
/// <summary>
|
||||
/// Represents a CoolerMaster keyboard.
|
||||
/// </summary>
|
||||
public class CoolerMasterKeyboardRGBDevice : CoolerMasterRGBDevice<CoolerMasterKeyboardRGBDeviceInfo>, IKeyboard
|
||||
public sealed class CoolerMasterKeyboardRGBDevice : CoolerMasterRGBDevice<CoolerMasterKeyboardRGBDeviceInfo>, IKeyboard
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ namespace RGB.NET.Devices.CoolerMaster;
|
||||
/// <summary>
|
||||
/// Represents a generic information for a <see cref="T:RGB.NET.Devices.CoolerMaster.CoolerMasterKeyboardRGBDevice" />.
|
||||
/// </summary>
|
||||
public class CoolerMasterKeyboardRGBDeviceInfo : CoolerMasterRGBDeviceInfo, IKeyboardDeviceInfo
|
||||
public sealed class CoolerMasterKeyboardRGBDeviceInfo : CoolerMasterRGBDeviceInfo, IKeyboardDeviceInfo
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ namespace RGB.NET.Devices.CoolerMaster;
|
||||
/// <summary>
|
||||
/// Represents a CoolerMaster mouse.
|
||||
/// </summary>
|
||||
public class CoolerMasterMouseRGBDevice : CoolerMasterRGBDevice<CoolerMasterMouseRGBDeviceInfo>, IMouse
|
||||
public sealed class CoolerMasterMouseRGBDevice : CoolerMasterRGBDevice<CoolerMasterMouseRGBDeviceInfo>, IMouse
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ namespace RGB.NET.Devices.CoolerMaster;
|
||||
/// <summary>
|
||||
/// Represents a generic information for a <see cref="T:RGB.NET.Devices.CoolerMaster.CoolerMasterMouseRGBDevice" />.
|
||||
/// </summary>
|
||||
public class CoolerMasterMouseRGBDeviceInfo : CoolerMasterRGBDeviceInfo
|
||||
public sealed class CoolerMasterMouseRGBDeviceInfo : CoolerMasterRGBDeviceInfo
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
|
||||
@ -16,14 +16,14 @@ internal static class _CoolerMasterSDK
|
||||
{
|
||||
#region Libary Management
|
||||
|
||||
private static IntPtr _handle = IntPtr.Zero;
|
||||
private static nint _handle = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the SDK.
|
||||
/// </summary>
|
||||
internal static void Reload()
|
||||
{
|
||||
if (_handle != IntPtr.Zero)
|
||||
if (_handle != 0)
|
||||
{
|
||||
foreach (CoolerMasterDevicesIndexes index in Enum.GetValues(typeof(CoolerMasterDevicesIndexes)))
|
||||
EnableLedControl(false, index);
|
||||
@ -34,7 +34,7 @@ internal static class _CoolerMasterSDK
|
||||
|
||||
private static void LoadCMSDK()
|
||||
{
|
||||
if (_handle != IntPtr.Zero) return;
|
||||
if (_handle != 0) return;
|
||||
|
||||
// HACK: Load library at runtime to support both, x86 and x64 with one managed dll
|
||||
List<string> possiblePathList = (Environment.Is64BitProcess ? CoolerMasterDeviceProvider.PossibleX64NativePaths : CoolerMasterDeviceProvider.PossibleX86NativePaths)
|
||||
@ -47,7 +47,7 @@ internal static class _CoolerMasterSDK
|
||||
#if NET6_0
|
||||
if (_handle == IntPtr.Zero) throw new RGBDeviceException($"CoolerMaster LoadLibrary failed with error code {Marshal.GetLastPInvokeError()}");
|
||||
#else
|
||||
if (_handle == IntPtr.Zero) throw new RGBDeviceException($"CoolerMaster LoadLibrary failed with error code {Marshal.GetLastWin32Error()}");
|
||||
if (_handle == 0) throw new RGBDeviceException($"CoolerMaster LoadLibrary failed with error code {Marshal.GetLastWin32Error()}");
|
||||
#endif
|
||||
|
||||
_getSDKVersionPointer = (GetSDKVersionPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_handle, "GetCM_SDK_DllVer"), typeof(GetSDKVersionPointer));
|
||||
@ -62,7 +62,7 @@ internal static class _CoolerMasterSDK
|
||||
|
||||
internal static void UnloadCMSDK()
|
||||
{
|
||||
if (_handle == IntPtr.Zero) return;
|
||||
if (_handle == 0) return;
|
||||
|
||||
_getSDKVersionPointer = null;
|
||||
_setControlDevicenPointer = null;
|
||||
@ -74,14 +74,14 @@ internal static class _CoolerMasterSDK
|
||||
_setAllLedColorPointer = null;
|
||||
|
||||
NativeLibrary.Free(_handle);
|
||||
_handle = IntPtr.Zero;
|
||||
_handle = 0;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr LoadLibrary(string dllToLoad);
|
||||
private static extern nint LoadLibrary(string dllToLoad);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
|
||||
private static extern IntPtr GetProcAddress(IntPtr dllHandle, string name);
|
||||
private static extern nint GetProcAddress(nint dllHandle, string name);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net7.0;net6.0;net5.0</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<Optimize>false</Optimize>
|
||||
</PropertyGroup>
|
||||
@ -48,7 +48,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>$(NoWarn);CS1591;CS1572;CS1573</NoWarn>
|
||||
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
|
||||
<DefineConstants>RELEASE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
34
RGB.NET.Devices.Corsair/Cooler/CorsairCoolerRGBDevice.cs
Normal file
34
RGB.NET.Devices.Corsair/Cooler/CorsairCoolerRGBDevice.cs
Normal file
@ -0,0 +1,34 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using RGB.NET.Core;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Devices.Corsair;
|
||||
|
||||
/// <inheritdoc cref="CorsairRGBDevice{TDeviceInfo}" />
|
||||
/// <summary>
|
||||
/// Represents a corsair cooler.
|
||||
/// </summary>
|
||||
public sealed class CorsairCoolerRGBDevice : CorsairRGBDevice<CorsairCoolerRGBDeviceInfo>, ICooler
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Corsair.CorsairCoolerRGBDevice" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The specific information provided by CUE for the cooler.</param>
|
||||
/// <param name="updateQueue">The queue used to update this device.</param>
|
||||
internal CorsairCoolerRGBDevice(CorsairCoolerRGBDeviceInfo info, CorsairDeviceUpdateQueue updateQueue)
|
||||
: base(info, updateQueue)
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
protected override LedMapping<CorsairLedId> CreateMapping(IEnumerable<CorsairLedId> ids) => LedMappings.CreateCoolerMapping(ids);
|
||||
|
||||
#endregion
|
||||
}
|
||||
28
RGB.NET.Devices.Corsair/Cooler/CorsairCoolerRGBDeviceInfo.cs
Normal file
28
RGB.NET.Devices.Corsair/Cooler/CorsairCoolerRGBDeviceInfo.cs
Normal file
@ -0,0 +1,28 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using RGB.NET.Core;
|
||||
using RGB.NET.Devices.Corsair.Native;
|
||||
|
||||
namespace RGB.NET.Devices.Corsair;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a generic information for a <see cref="T:RGB.NET.Devices.Corsair.CorsairCoolerRGBDevice" />.
|
||||
/// </summary>
|
||||
public sealed class CorsairCoolerRGBDeviceInfo : CorsairRGBDeviceInfo
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
internal CorsairCoolerRGBDeviceInfo(_CorsairDeviceInfo nativeInfo, int ledCount, int ledOffset)
|
||||
: base(RGBDeviceType.Cooler, nativeInfo, ledCount, ledOffset)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
internal CorsairCoolerRGBDeviceInfo(_CorsairDeviceInfo nativeInfo, int ledCount, int ledOffset, string modelName)
|
||||
: base(RGBDeviceType.Cooler, nativeInfo, ledCount, ledOffset, modelName)
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -3,8 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using RGB.NET.Core;
|
||||
using RGB.NET.Devices.Corsair.Native;
|
||||
|
||||
@ -14,37 +13,72 @@ namespace RGB.NET.Devices.Corsair;
|
||||
/// <summary>
|
||||
/// Represents a device provider responsible for corsair (CUE) devices.
|
||||
/// </summary>
|
||||
public class CorsairDeviceProvider : AbstractRGBDeviceProvider
|
||||
public sealed class CorsairDeviceProvider : AbstractRGBDeviceProvider
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private static readonly object _lock = new();
|
||||
|
||||
private static CorsairDeviceProvider? _instance;
|
||||
/// <summary>
|
||||
/// Gets the singleton <see cref="CorsairDeviceProvider"/> instance.
|
||||
/// </summary>
|
||||
public static CorsairDeviceProvider Instance => _instance ?? new CorsairDeviceProvider();
|
||||
public static CorsairDeviceProvider Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
return _instance ?? new CorsairDeviceProvider();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a modifiable list of paths used to find the native SDK-dlls for x86 applications.
|
||||
/// The first match will be used.
|
||||
/// </summary>
|
||||
public static List<string> PossibleX86NativePaths { get; } = new() { "x86/CUESDK.dll", "x86/CUESDK_2019.dll", "x86/CUESDK_2017.dll", "x86/CUESDK_2015.dll", "x86/CUESDK_2013.dll" };
|
||||
public static List<string> PossibleX86NativePaths { get; } = ["x86/iCUESDK.dll", "x86/CUESDK_2019.dll"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a modifiable list of paths used to find the native SDK-dlls for x64 applications.
|
||||
/// The first match will be used.
|
||||
/// </summary>
|
||||
public static List<string> PossibleX64NativePaths { get; } = new() { "x64/CUESDK.dll", "x64/CUESDK.x64_2019.dll", "x64/CUESDK.x64_2017.dll", "x64/CUESDK_2019.dll", "x64/CUESDK_2017.dll", "x64/CUESDK_2015.dll", "x64/CUESDK_2013.dll" };
|
||||
public static List<string> PossibleX64NativePaths { get; } = ["x64/iCUESDK.dll", "x64/iCUESDK.x64_2019.dll", "x64/CUESDK.dll", "x64/CUESDK.x64_2019.dll"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the protocol details for the current SDK-connection.
|
||||
/// Gets or sets the timeout used when connecting to the SDK.
|
||||
/// </summary>
|
||||
public CorsairProtocolDetails? ProtocolDetails { get; private set; }
|
||||
public static TimeSpan ConnectionTimeout { get; set; } = TimeSpan.FromMilliseconds(500);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last error documented by CUE.
|
||||
/// Gets or sets a bool indicating if exclusive request should be requested through the iCUE-SDK.
|
||||
/// </summary>
|
||||
public static CorsairError LastError => _CUESDK.CorsairGetLastError();
|
||||
public static bool ExclusiveAccess { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the details for the current SDK-session.
|
||||
/// </summary>
|
||||
public CorsairSessionDetails SessionDetails { get; private set; } = new();
|
||||
|
||||
private CorsairSessionState _sessionState = CorsairSessionState.Invalid;
|
||||
public CorsairSessionState SessionState
|
||||
{
|
||||
get => _sessionState;
|
||||
private set
|
||||
{
|
||||
_sessionState = value;
|
||||
|
||||
try { SessionStateChanged?.Invoke(this, SessionState); }
|
||||
catch { /* catch faulty event-handlers*/ }
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
// ReSharper disable once UnassignedField.Global
|
||||
public EventHandler<CorsairSessionState>? SessionStateChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
@ -56,35 +90,56 @@ public class CorsairDeviceProvider : AbstractRGBDeviceProvider
|
||||
/// <exception cref="InvalidOperationException">Thrown if this constructor is called even if there is already an instance of this class.</exception>
|
||||
public CorsairDeviceProvider()
|
||||
{
|
||||
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CorsairDeviceProvider)}");
|
||||
_instance = this;
|
||||
lock (_lock)
|
||||
{
|
||||
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CorsairDeviceProvider)}");
|
||||
_instance = this;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InitializeSDK()
|
||||
{
|
||||
_CUESDK.Reload();
|
||||
|
||||
ProtocolDetails = new CorsairProtocolDetails(_CUESDK.CorsairPerformProtocolHandshake());
|
||||
using ManualResetEventSlim waitEvent = new(false);
|
||||
|
||||
CorsairError error = LastError;
|
||||
if (error != CorsairError.Success)
|
||||
Throw(new CUEException(error), true);
|
||||
void OnInitializeSessionStateChanged(object? sender, CorsairSessionState state)
|
||||
{
|
||||
if (state == CorsairSessionState.Connected)
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
waitEvent.Set();
|
||||
}
|
||||
|
||||
if (ProtocolDetails.BreakingChanges)
|
||||
Throw(new RGBDeviceException("The SDK currently used isn't compatible with the installed version of CUE.\r\n"
|
||||
+ $"CUE-Version: {ProtocolDetails.ServerVersion} (Protocol {ProtocolDetails.ServerProtocolVersion})\r\n"
|
||||
+ $"SDK-Version: {ProtocolDetails.SdkVersion} (Protocol {ProtocolDetails.SdkProtocolVersion})"), true);
|
||||
try
|
||||
{
|
||||
_CUESDK.SessionStateChanged += OnSessionStateChanged;
|
||||
_CUESDK.SessionStateChanged += OnInitializeSessionStateChanged;
|
||||
|
||||
// DarthAffe 02.02.2021: 127 is iCUE
|
||||
if (!_CUESDK.CorsairSetLayerPriority(128))
|
||||
Throw(new CUEException(LastError));
|
||||
CorsairError errorCode = _CUESDK.CorsairConnect();
|
||||
if (errorCode != CorsairError.Success)
|
||||
Throw(new RGBDeviceException($"Failed to initialized Corsair-SDK. (ErrorCode: {errorCode})"));
|
||||
|
||||
if (!waitEvent.Wait(ConnectionTimeout))
|
||||
Throw(new RGBDeviceException($"Failed to initialized Corsair-SDK. (Timeout - Current connection state: {_CUESDK.SesionState})"));
|
||||
|
||||
_CUESDK.CorsairGetSessionDetails(out _CorsairSessionDetails? details);
|
||||
if (errorCode != CorsairError.Success)
|
||||
Throw(new RGBDeviceException($"Failed to get session details. (ErrorCode: {errorCode})"));
|
||||
|
||||
SessionDetails = new CorsairSessionDetails(details!);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_CUESDK.SessionStateChanged -= OnInitializeSessionStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSessionStateChanged(object? sender, CorsairSessionState state) => SessionState = state;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IEnumerable<IRGBDevice> LoadDevices()
|
||||
{
|
||||
@ -97,119 +152,190 @@ public class CorsairDeviceProvider : AbstractRGBDeviceProvider
|
||||
|
||||
private IEnumerable<ICorsairRGBDevice> LoadCorsairDevices()
|
||||
{
|
||||
int deviceCount = _CUESDK.CorsairGetDeviceCount();
|
||||
for (int i = 0; i < deviceCount; i++)
|
||||
CorsairError error = _CUESDK.CorsairGetDevices(new _CorsairDeviceFilter(CorsairDeviceType.All), out _CorsairDeviceInfo[] devices);
|
||||
if (error != CorsairError.Success)
|
||||
Throw(new RGBDeviceException($"Failed to load devices. (ErrorCode: {error})"));
|
||||
|
||||
foreach (_CorsairDeviceInfo device in devices)
|
||||
{
|
||||
_CorsairDeviceInfo nativeDeviceInfo = (_CorsairDeviceInfo)Marshal.PtrToStructure(_CUESDK.CorsairGetDeviceInfo(i), typeof(_CorsairDeviceInfo))!;
|
||||
if (!((CorsairDeviceCaps)nativeDeviceInfo.capsMask).HasFlag(CorsairDeviceCaps.Lighting))
|
||||
continue; // Everything that doesn't support lighting control is useless
|
||||
if (string.IsNullOrWhiteSpace(device.id)) continue;
|
||||
|
||||
CorsairDeviceUpdateQueue updateQueue = new(GetUpdateTrigger(), i);
|
||||
switch (nativeDeviceInfo.type)
|
||||
error = _CUESDK.CorsairRequestControl(device.id, ExclusiveAccess ? CorsairAccessLevel.ExclusiveLightingControl : CorsairAccessLevel.Shared);
|
||||
if (error != CorsairError.Success)
|
||||
Throw(new RGBDeviceException($"Failed to take control of device '{device.id}'. (ErrorCode: {error})"));
|
||||
|
||||
CorsairDeviceUpdateQueue updateQueue = new(GetUpdateTrigger(), device);
|
||||
|
||||
int channelLedCount = 0;
|
||||
for (int i = 0; i < device.channelCount; i++)
|
||||
{
|
||||
case CorsairDeviceType.Keyboard:
|
||||
yield return new CorsairKeyboardRGBDevice(new CorsairKeyboardRGBDeviceInfo(i, nativeDeviceInfo), updateQueue);
|
||||
break;
|
||||
Console.WriteLine($"Channel {i}/{device.channelCount}");
|
||||
channelLedCount += _CUESDK.ReadDevicePropertySimpleInt32(device.id!, CorsairDevicePropertyId.ChannelLedCount, (uint)i);
|
||||
}
|
||||
|
||||
case CorsairDeviceType.Mouse:
|
||||
yield return new CorsairMouseRGBDevice(new CorsairMouseRGBDeviceInfo(i, nativeDeviceInfo), updateQueue);
|
||||
break;
|
||||
int deviceLedCount = device.ledCount - channelLedCount;
|
||||
if (deviceLedCount > 0)
|
||||
switch (device.type)
|
||||
{
|
||||
case CorsairDeviceType.Keyboard:
|
||||
yield return new CorsairKeyboardRGBDevice(new CorsairKeyboardRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairDeviceType.Headset:
|
||||
yield return new CorsairHeadsetRGBDevice(new CorsairHeadsetRGBDeviceInfo(i, nativeDeviceInfo), updateQueue);
|
||||
break;
|
||||
case CorsairDeviceType.Mouse:
|
||||
yield return new CorsairMouseRGBDevice(new CorsairMouseRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairDeviceType.Mousepad:
|
||||
yield return new CorsairMousepadRGBDevice(new CorsairMousepadRGBDeviceInfo(i, nativeDeviceInfo), updateQueue);
|
||||
break;
|
||||
case CorsairDeviceType.Headset:
|
||||
yield return new CorsairHeadsetRGBDevice(new CorsairHeadsetRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairDeviceType.HeadsetStand:
|
||||
yield return new CorsairHeadsetStandRGBDevice(new CorsairHeadsetStandRGBDeviceInfo(i, nativeDeviceInfo), updateQueue);
|
||||
break;
|
||||
case CorsairDeviceType.Mousemat:
|
||||
yield return new CorsairMousepadRGBDevice(new CorsairMousepadRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairDeviceType.MemoryModule:
|
||||
yield return new CorsairMemoryRGBDevice(new CorsairMemoryRGBDeviceInfo(i, nativeDeviceInfo), updateQueue);
|
||||
break;
|
||||
case CorsairDeviceType.HeadsetStand:
|
||||
yield return new CorsairHeadsetStandRGBDevice(new CorsairHeadsetStandRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairDeviceType.Mainboard:
|
||||
yield return new CorsairMainboardRGBDevice(new CorsairMainboardRGBDeviceInfo(i, nativeDeviceInfo), updateQueue);
|
||||
break;
|
||||
case CorsairDeviceType.MemoryModule:
|
||||
yield return new CorsairMemoryRGBDevice(new CorsairMemoryRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairDeviceType.GraphicsCard:
|
||||
yield return new CorsairGraphicsCardRGBDevice(new CorsairGraphicsCardRGBDeviceInfo(i, nativeDeviceInfo), updateQueue);
|
||||
break;
|
||||
case CorsairDeviceType.Motherboard:
|
||||
yield return new CorsairMainboardRGBDevice(new CorsairMainboardRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairDeviceType.Touchbar:
|
||||
yield return new CorsairTouchbarRGBDevice(new CorsairTouchbarRGBDeviceInfo(i, nativeDeviceInfo), updateQueue);
|
||||
break;
|
||||
case CorsairDeviceType.GraphicsCard:
|
||||
yield return new CorsairGraphicsCardRGBDevice(new CorsairGraphicsCardRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairDeviceType.Cooler:
|
||||
case CorsairDeviceType.CommanderPro:
|
||||
case CorsairDeviceType.LightningNodePro:
|
||||
List<_CorsairChannelInfo> channels = GetChannels(nativeDeviceInfo).ToList();
|
||||
int channelsLedCount = channels.Sum(x => x.totalLedsCount);
|
||||
int deviceLedCount = nativeDeviceInfo.ledsCount - channelsLedCount;
|
||||
case CorsairDeviceType.Touchbar:
|
||||
yield return new CorsairTouchbarRGBDevice(new CorsairTouchbarRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
if (deviceLedCount > 0)
|
||||
yield return new CorsairCustomRGBDevice(new CorsairCustomRGBDeviceInfo(i, nativeDeviceInfo, deviceLedCount), updateQueue);
|
||||
case CorsairDeviceType.Cooler:
|
||||
yield return new CorsairCoolerRGBDevice(new CorsairCoolerRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
int ledOffset = deviceLedCount;
|
||||
foreach (_CorsairChannelInfo channelInfo in channels)
|
||||
case CorsairDeviceType.GameController:
|
||||
yield return new CorsairGameControllerRGBDevice(new CorsairGameControllerRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairDeviceType.FanLedController:
|
||||
case CorsairDeviceType.LedController:
|
||||
case CorsairDeviceType.Unknown:
|
||||
yield return new CorsairUnknownRGBDevice(new CorsairUnknownRGBDeviceInfo(device, deviceLedCount, 0), updateQueue);
|
||||
break;
|
||||
|
||||
default:
|
||||
Throw(new RGBDeviceException("Unknown Device-Type"));
|
||||
break;
|
||||
}
|
||||
|
||||
int offset = deviceLedCount;
|
||||
for (int i = 0; i < device.channelCount; i++)
|
||||
{
|
||||
int deviceCount = _CUESDK.ReadDevicePropertySimpleInt32(device.id!, CorsairDevicePropertyId.ChannelDeviceCount, (uint)i);
|
||||
if (deviceCount <= 0) continue; // DarthAffe 10.02.2023: There seem to be an issue in the SDK where it reports empty channels and fails when getting ledCounts and device types from them
|
||||
|
||||
int[] ledCounts = _CUESDK.ReadDevicePropertySimpleInt32Array(device.id!, CorsairDevicePropertyId.ChannelDeviceLedCountArray, (uint)i);
|
||||
int[] deviceTypes = _CUESDK.ReadDevicePropertySimpleInt32Array(device.id!, CorsairDevicePropertyId.ChannelDeviceTypeArray, (uint)i);
|
||||
|
||||
for (int j = 0; j < deviceCount; j++)
|
||||
{
|
||||
CorsairChannelDeviceType deviceType = (CorsairChannelDeviceType)deviceTypes[j];
|
||||
int ledCount = ledCounts[j];
|
||||
|
||||
switch (deviceType)
|
||||
{
|
||||
int channelDeviceInfoStructSize = Marshal.SizeOf(typeof(_CorsairChannelDeviceInfo));
|
||||
IntPtr channelDeviceInfoPtr = channelInfo.devices;
|
||||
for (int device = 0; (device < channelInfo.devicesCount) && (ledOffset < nativeDeviceInfo.ledsCount); device++)
|
||||
{
|
||||
_CorsairChannelDeviceInfo channelDeviceInfo = (_CorsairChannelDeviceInfo)Marshal.PtrToStructure(channelDeviceInfoPtr, typeof(_CorsairChannelDeviceInfo))!;
|
||||
case CorsairChannelDeviceType.FanHD:
|
||||
yield return new CorsairFanRGBDevice(new CorsairFanRGBDeviceInfo(device, ledCount, offset, "HD Fan"), updateQueue);
|
||||
break;
|
||||
|
||||
yield return new CorsairCustomRGBDevice(new CorsairCustomRGBDeviceInfo(i, nativeDeviceInfo, channelDeviceInfo, ledOffset), updateQueue);
|
||||
case CorsairChannelDeviceType.FanSP:
|
||||
yield return new CorsairFanRGBDevice(new CorsairFanRGBDeviceInfo(device, ledCount, offset, "SP Fan"), updateQueue);
|
||||
break;
|
||||
|
||||
ledOffset += channelDeviceInfo.deviceLedCount;
|
||||
channelDeviceInfoPtr = new IntPtr(channelDeviceInfoPtr.ToInt64() + channelDeviceInfoStructSize);
|
||||
}
|
||||
case CorsairChannelDeviceType.FanLL:
|
||||
yield return new CorsairFanRGBDevice(new CorsairFanRGBDeviceInfo(device, ledCount, offset, "LL Fan"), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairChannelDeviceType.FanML:
|
||||
yield return new CorsairFanRGBDevice(new CorsairFanRGBDeviceInfo(device, ledCount, offset, "ML Fan"), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairChannelDeviceType.FanQL:
|
||||
yield return new CorsairFanRGBDevice(new CorsairFanRGBDeviceInfo(device, ledCount, offset, "QL Fan"), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairChannelDeviceType.FanQX:
|
||||
yield return new CorsairFanRGBDevice(new CorsairFanRGBDeviceInfo(device, ledCount, offset, "QX Fan"), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairChannelDeviceType.EightLedSeriesFan:
|
||||
yield return new CorsairFanRGBDevice(new CorsairFanRGBDeviceInfo(device, ledCount, offset, "8-Led-Series Fan Fan"), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairChannelDeviceType.DAP:
|
||||
yield return new CorsairFanRGBDevice(new CorsairFanRGBDeviceInfo(device, ledCount, offset, "DAP Fan"), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairChannelDeviceType.Pump:
|
||||
yield return new CorsairCoolerRGBDevice(new CorsairCoolerRGBDeviceInfo(device, ledCount, offset, "Pump"), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairChannelDeviceType.WaterBlock:
|
||||
yield return new CorsairCoolerRGBDevice(new CorsairCoolerRGBDeviceInfo(device, ledCount, offset, "Water Block"), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairChannelDeviceType.Strip:
|
||||
string modelName = "LED Strip";
|
||||
|
||||
// LS100 Led Strips are reported as one big strip if configured in monitor mode in iCUE, 138 LEDs for dual monitor, 84 for single
|
||||
if ((device.model == "LS100 Starter Kit") && (ledCount == 138))
|
||||
modelName = "LS100 LED Strip (dual monitor)";
|
||||
else if ((device.model == "LS100 Starter Kit") && (ledCount == 84))
|
||||
modelName = "LS100 LED Strip (single monitor)";
|
||||
// Any other value means an "External LED Strip" in iCUE, these are reported per-strip, 15 for short strips, 27 for long
|
||||
else if ((device.model == "LS100 Starter Kit") && (ledCount == 15))
|
||||
modelName = "LS100 LED Strip (short)";
|
||||
else if ((device.model == "LS100 Starter Kit") && (ledCount == 27))
|
||||
modelName = "LS100 LED Strip (long)";
|
||||
|
||||
yield return new CorsairLedStripRGBDevice(new CorsairLedStripRGBDeviceInfo(device, ledCount, offset, modelName), updateQueue);
|
||||
break;
|
||||
|
||||
case CorsairChannelDeviceType.DRAM:
|
||||
yield return new CorsairMemoryRGBDevice(new CorsairMemoryRGBDeviceInfo(device, ledCount, offset, "DRAM"), updateQueue);
|
||||
break;
|
||||
|
||||
default:
|
||||
Throw(new RGBDeviceException("Unknown Device-Type"));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Throw(new RGBDeviceException("Unknown Device-Type"));
|
||||
break;
|
||||
offset += ledCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<_CorsairChannelInfo> GetChannels(_CorsairDeviceInfo deviceInfo)
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_CorsairChannelsInfo? channelsInfo = deviceInfo.channels;
|
||||
if (channelsInfo == null) yield break;
|
||||
|
||||
IntPtr channelInfoPtr = channelsInfo.channels;
|
||||
for (int channel = 0; channel < channelsInfo.channelsCount; channel++)
|
||||
lock (_lock)
|
||||
{
|
||||
yield return (_CorsairChannelInfo)Marshal.PtrToStructure(channelInfoPtr, typeof(_CorsairChannelInfo))!;
|
||||
base.Dispose(disposing);
|
||||
|
||||
int channelInfoStructSize = Marshal.SizeOf(typeof(_CorsairChannelInfo));
|
||||
channelInfoPtr = new IntPtr(channelInfoPtr.ToInt64() + channelInfoStructSize);
|
||||
try { _CUESDK.CorsairDisconnect(); }
|
||||
catch { /* at least we tried */ }
|
||||
|
||||
try { _CUESDK.UnloadCUESDK(); }
|
||||
catch { /* at least we tried */ }
|
||||
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Reset()
|
||||
{
|
||||
ProtocolDetails = null;
|
||||
|
||||
base.Reset();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
||||
try { _CUESDK.UnloadCUESDK(); }
|
||||
catch { /* at least we tried */ }
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user