diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4091ba..5dd87e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,15 @@ name: RGB.NET-CI on: - push: - branches: [ Development ] - paths: - - '**.cs' - - '**.csproj' - - '**.yml' + workflow_dispatch: + inputs: + version: + description: 'version' + required: true + type: string + increment: + required: true + type: string jobs: build: @@ -14,48 +17,42 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4.1.1 with: fetch-depth: 0 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x 7.0.x 6.0.x - - name: Git Semantic Version - id: versioning - uses: PaulHatch/semantic-version@v4.0.3 - with: - short_tags: false - format: "${major}.${minor}.${patch}-prerelease.${increment}" - name: Restore dependencies run: dotnet restore - name: Build - run: dotnet build --no-restore --configuration Release /p:Version=${{ steps.versioning.outputs.version }} + run: dotnet build --no-restore --configuration Release /p:Version=${{ github.event.inputs.version }}-prerelease.${{ github.event.inputs.increment }} - name: Test run: dotnet test --no-build --verbosity normal --configuration Release - name: Upload a Build Artifact NET6 - uses: actions/upload-artifact@v2.2.4 + uses: actions/upload-artifact@v4.3.1 with: name: RGB.NET-NET6 path: bin/net6.0/RGB.NET.*.dll if-no-files-found: error - name: Upload a Build Artifact NET7 - uses: actions/upload-artifact@v2.2.4 + uses: actions/upload-artifact@v4.3.1 with: 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 + uses: actions/upload-artifact@v4.3.1 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 + uses: actions/upload-artifact@v4.3.1 with: name: RGB.NET-Nugets path: bin/*nupkg @@ -64,9 +61,3 @@ jobs: 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 diff --git a/.github/workflows/pr_verify.yml b/.github/workflows/pr_verify.yml index d556fa2..539b728 100644 --- a/.github/workflows/pr_verify.yml +++ b/.github/workflows/pr_verify.yml @@ -10,9 +10,9 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4.1.1 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f6e3df3..2504782 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,60 +1,54 @@ name: RGB.NET-Release on: - push: - branches: [ master ] - paths: - - '**.cs' - - '**.csproj' - - '**.yml' + workflow_dispatch: + inputs: + version: + description: 'version' + required: true + type: string jobs: build: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4.1.1 with: fetch-depth: 0 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x 7.0.x 6.0.x - - name: Git Semantic Version - id: versioning - uses: PaulHatch/semantic-version@v4.0.3 - with: - short_tags: false - format: "${major}.${minor}.${patch}" - name: Restore dependencies run: dotnet restore - name: Build - run: dotnet build --no-restore --configuration Release /p:Version=${{ steps.versioning.outputs.version }} + run: dotnet build --no-restore --configuration Release /p:Version=${{ github.event.inputs.version }} - name: Test run: dotnet test --no-build --verbosity normal --configuration Release - name: Upload a Build Artifact NET6 - uses: actions/upload-artifact@v2.2.4 + uses: actions/upload-artifact@v4.3.1 with: name: RGB.NET-NET6 path: bin/net6.0/RGB.NET.*.dll if-no-files-found: error - name: Upload a Build Artifact NET7 - uses: actions/upload-artifact@v2.2.4 + uses: actions/upload-artifact@v4.3.1 with: 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 + uses: actions/upload-artifact@v4.3.1 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 + uses: actions/upload-artifact@v4.3.1 with: name: RGB.NET-Nugets path: bin/*nupkg @@ -62,7 +56,7 @@ jobs: - name: Release uses: softprops/action-gh-release@v1 with: - tag_name: ${{ steps.versioning.outputs.version_tag }} + tag_name: v${{ github.event.inputs.version }} generate_release_notes: true files: bin/net8.0/RGB.NET.*.dll - name: Nuget Push diff --git a/RGB.NET.Core/Extensions/MathExtensions.cs b/RGB.NET.Core/Extensions/MathExtensions.cs index c3176b1..665df6a 100644 --- a/RGB.NET.Core/Extensions/MathExtensions.cs +++ b/RGB.NET.Core/Extensions/MathExtensions.cs @@ -91,9 +91,8 @@ public static class FloatExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte GetByteValueFromPercentage(this float percentage) { - if (float.IsNaN(percentage)) return 0; + if (float.IsNaN(percentage) || (percentage <= 0)) return 0; - percentage = percentage.Clamp(0, 1.0f); return (byte)(percentage >= 1.0f ? 255 : percentage * 256.0f); } diff --git a/RGB.NET.Core/Rendering/Textures/PixelTexture.cs b/RGB.NET.Core/Rendering/Textures/PixelTexture.cs index 170d2d5..5ad4459 100644 --- a/RGB.NET.Core/Rendering/Textures/PixelTexture.cs +++ b/RGB.NET.Core/Rendering/Textures/PixelTexture.cs @@ -126,7 +126,7 @@ public abstract class PixelTexture : ITexture /// /// The pixel-data to convert. /// The color represented by the specified pixel-data. - protected abstract Color GetColor(in ReadOnlySpan pixel); + protected abstract Color GetColor(ReadOnlySpan pixel); /// /// Gets the pixel-data at the specified location. @@ -189,7 +189,7 @@ public sealed class PixelTexture : PixelTexture /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override Color GetColor(in ReadOnlySpan pixel) => pixel[0]; + protected override Color GetColor(ReadOnlySpan pixel) => pixel[0]; #endregion } \ No newline at end of file diff --git a/RGB.NET.Core/Rendering/Textures/Sampler/AverageColorSampler.cs b/RGB.NET.Core/Rendering/Textures/Sampler/AverageColorSampler.cs index 9ba2cb0..f7e3258 100644 --- a/RGB.NET.Core/Rendering/Textures/Sampler/AverageColorSampler.cs +++ b/RGB.NET.Core/Rendering/Textures/Sampler/AverageColorSampler.cs @@ -22,7 +22,7 @@ public sealed class AverageColorSampler : ISampler #region Methods /// - public unsafe void Sample(in SamplerInfo info, in Span pixelData) + public unsafe void Sample(in SamplerInfo info, Span pixelData) { int count = info.Width * info.Height; if (count == 0) return; diff --git a/RGB.NET.Core/Rendering/Textures/Sampler/ISampler.cs b/RGB.NET.Core/Rendering/Textures/Sampler/ISampler.cs index 9a2c4f1..04fcfa0 100644 --- a/RGB.NET.Core/Rendering/Textures/Sampler/ISampler.cs +++ b/RGB.NET.Core/Rendering/Textures/Sampler/ISampler.cs @@ -13,5 +13,5 @@ public interface ISampler /// /// The information containing the data to sample. /// The buffer used to write the resulting pixel to. - void Sample(in SamplerInfo info, in Span pixelData); + void Sample(in SamplerInfo info, Span pixelData); } \ No newline at end of file diff --git a/RGB.NET.Core/Rendering/Textures/Sampler/SamplerInfo.cs b/RGB.NET.Core/Rendering/Textures/Sampler/SamplerInfo.cs index bf59c93..19ae76c 100644 --- a/RGB.NET.Core/Rendering/Textures/Sampler/SamplerInfo.cs +++ b/RGB.NET.Core/Rendering/Textures/Sampler/SamplerInfo.cs @@ -44,7 +44,7 @@ public readonly ref struct SamplerInfo /// The width of the region the data comes from. /// The height of region the data comes from. /// The data to sample. - public SamplerInfo(int x, int y, int width, int height, int stride, int dataPerPixel, in ReadOnlySpan data) + public SamplerInfo(int x, int y, int width, int height, int stride, int dataPerPixel, ReadOnlySpan data) { this._x = x; this._y = y; diff --git a/RGB.NET.Core/Update/Devices/UpdateQueue.cs b/RGB.NET.Core/Update/Devices/UpdateQueue.cs index 22ef7f9..7db3517 100644 --- a/RGB.NET.Core/Update/Devices/UpdateQueue.cs +++ b/RGB.NET.Core/Update/Devices/UpdateQueue.cs @@ -80,7 +80,7 @@ public abstract class UpdateQueue : AbstractReferenceCountin /// Performs the update this queue is responsible for. /// /// The set of data that needs to be updated. - protected abstract bool Update(in ReadOnlySpan<(TIdentifier key, TData color)> dataSet); + protected abstract bool Update(ReadOnlySpan<(TIdentifier key, TData color)> dataSet); /// /// Sets or merges the provided data set in the current dataset and notifies the trigger that there is new data available. diff --git a/RGB.NET.Devices.Asus/Generic/AsusUpdateQueue.cs b/RGB.NET.Devices.Asus/Generic/AsusUpdateQueue.cs index 2f40ec6..e6077a6 100644 --- a/RGB.NET.Devices.Asus/Generic/AsusUpdateQueue.cs +++ b/RGB.NET.Devices.Asus/Generic/AsusUpdateQueue.cs @@ -43,7 +43,7 @@ public sealed class AsusUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterUpdateQueue.cs b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterUpdateQueue.cs index 99e1428..f7506ce 100644 --- a/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterUpdateQueue.cs +++ b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterUpdateQueue.cs @@ -37,7 +37,7 @@ public sealed class CoolerMasterUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.Corsair/Generic/CorsairDeviceUpdateQueue.cs b/RGB.NET.Devices.Corsair/Generic/CorsairDeviceUpdateQueue.cs index be3a3bf..bd707a2 100644 --- a/RGB.NET.Devices.Corsair/Generic/CorsairDeviceUpdateQueue.cs +++ b/RGB.NET.Devices.Corsair/Generic/CorsairDeviceUpdateQueue.cs @@ -40,7 +40,7 @@ public sealed class CorsairDeviceUpdateQueue : UpdateQueue #region Methods /// - protected override unsafe bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override unsafe bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.Corsair/Native/_CUESDK.cs b/RGB.NET.Devices.Corsair/Native/_CUESDK.cs index 00d9f92..38324e3 100644 --- a/RGB.NET.Devices.Corsair/Native/_CUESDK.cs +++ b/RGB.NET.Devices.Corsair/Native/_CUESDK.cs @@ -47,6 +47,9 @@ internal static unsafe class _CUESDK #region Properties & Fields + // ReSharper disable once NotAccessedField.Local - This is important, the delegate can be collected if it's not stored! + private static readonly CorsairSessionStateChangedHandler SESSION_STATE_CHANGED_CALLBACK; + internal static bool IsConnected => SesionState == CorsairSessionState.Connected; internal static CorsairSessionState SesionState { get; private set; } @@ -58,6 +61,15 @@ internal static unsafe class _CUESDK #endregion + #region Constructors + + static _CUESDK() + { + SESSION_STATE_CHANGED_CALLBACK = CorsairSessionStateChangedCallback; + } + + #endregion + #region Methods private static void CorsairSessionStateChangedCallback(nint context, _CorsairSessionStateChanged eventdata) @@ -174,7 +186,7 @@ internal static unsafe class _CUESDK { if (_corsairConnectPtr == null) throw new RGBDeviceException("The Corsair-SDK is not initialized."); if (IsConnected) throw new RGBDeviceException("The Corsair-SDK is already connected."); - return _corsairConnectPtr(CorsairSessionStateChangedCallback, 0); + return _corsairConnectPtr(SESSION_STATE_CHANGED_CALLBACK, 0); } internal static CorsairError CorsairGetSessionDetails(out _CorsairSessionDetails? details) diff --git a/RGB.NET.Devices.Corsair_Legacy/Generic/CorsairDeviceUpdateQueue.cs b/RGB.NET.Devices.Corsair_Legacy/Generic/CorsairDeviceUpdateQueue.cs index a766911..5237353 100644 --- a/RGB.NET.Devices.Corsair_Legacy/Generic/CorsairDeviceUpdateQueue.cs +++ b/RGB.NET.Devices.Corsair_Legacy/Generic/CorsairDeviceUpdateQueue.cs @@ -35,7 +35,7 @@ public class CorsairDeviceUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs b/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs index 600a1ea..4fb1f43 100644 --- a/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs +++ b/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs @@ -59,7 +59,7 @@ public sealed class E131UpdateQueue : UpdateQueue } /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { @@ -99,5 +99,12 @@ public sealed class E131UpdateQueue : UpdateQueue return _sequenceNumber++; } + public override void Dispose() + { + base.Dispose(); + + _socket.Dispose(); + } + #endregion } \ No newline at end of file diff --git a/RGB.NET.Devices.Debug/DebugDeviceUpdateQueue.cs b/RGB.NET.Devices.Debug/DebugDeviceUpdateQueue.cs index df86acc..e228ea5 100644 --- a/RGB.NET.Devices.Debug/DebugDeviceUpdateQueue.cs +++ b/RGB.NET.Devices.Debug/DebugDeviceUpdateQueue.cs @@ -7,7 +7,7 @@ internal sealed class DebugDeviceUpdateQueue() : UpdateQueue(new DeviceUpdateTri { #region Methods - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) => true; + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) => true; #endregion } \ No newline at end of file diff --git a/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs b/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs index 22411f8..da892ba 100644 --- a/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs +++ b/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs @@ -277,6 +277,9 @@ public class LogitechDeviceProvider : AbstractRGBDeviceProvider try { _LogitechGSDK.LogiLedRestoreLighting(); } catch { /* at least we tried */ } + try { _LogitechGSDK.LogiLedShutdown(); } + catch { /* at least we tried */ } + try { _LogitechGSDK.UnloadLogitechGSDK(); } catch { /* at least we tried */ } diff --git a/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceUpdateQueue.cs b/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceUpdateQueue.cs index b780a28..655c9a7 100644 --- a/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceUpdateQueue.cs +++ b/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceUpdateQueue.cs @@ -25,7 +25,7 @@ public sealed class LogitechPerDeviceUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyUpdateQueue.cs b/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyUpdateQueue.cs index 7f1917f..7159b66 100644 --- a/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyUpdateQueue.cs +++ b/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyUpdateQueue.cs @@ -24,7 +24,7 @@ public sealed class LogitechPerKeyUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.Logitech/Zone/LogitechZoneUpdateQueue.cs b/RGB.NET.Devices.Logitech/Zone/LogitechZoneUpdateQueue.cs index c9a83a5..baf2253 100644 --- a/RGB.NET.Devices.Logitech/Zone/LogitechZoneUpdateQueue.cs +++ b/RGB.NET.Devices.Logitech/Zone/LogitechZoneUpdateQueue.cs @@ -33,7 +33,7 @@ public sealed class LogitechZoneUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.Msi/Generic/MsiDeviceUpdateQueue.cs b/RGB.NET.Devices.Msi/Generic/MsiDeviceUpdateQueue.cs index 0367029..0a64856 100644 --- a/RGB.NET.Devices.Msi/Generic/MsiDeviceUpdateQueue.cs +++ b/RGB.NET.Devices.Msi/Generic/MsiDeviceUpdateQueue.cs @@ -34,7 +34,7 @@ public sealed class MsiDeviceUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.Novation/Enum/NovationDevices.cs b/RGB.NET.Devices.Novation/Enum/NovationDevices.cs index 11ae6e0..5c8d19c 100644 --- a/RGB.NET.Devices.Novation/Enum/NovationDevices.cs +++ b/RGB.NET.Devices.Novation/Enum/NovationDevices.cs @@ -34,5 +34,10 @@ public enum NovationDevices [DeviceId("Launchpad Open")] [ColorCapability(NovationColorCapabilities.RGB)] [LedIdMapping(LedIdMappings.Pro)] - LaunchpadCustomFirmware + LaunchpadCustomFirmware, + + [DeviceId("LPMiniMK3")] + [ColorCapability(NovationColorCapabilities.RGB)] + [LedIdMapping(LedIdMappings.Current)] + LaunchpadMiniMK3, } \ No newline at end of file diff --git a/RGB.NET.Devices.Novation/Generic/MidiUpdateQueue.cs b/RGB.NET.Devices.Novation/Generic/MidiUpdateQueue.cs index 945c4ff..4cc8929 100644 --- a/RGB.NET.Devices.Novation/Generic/MidiUpdateQueue.cs +++ b/RGB.NET.Devices.Novation/Generic/MidiUpdateQueue.cs @@ -35,7 +35,7 @@ public abstract class MidiUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.Novation/NovationDeviceProvider.cs b/RGB.NET.Devices.Novation/NovationDeviceProvider.cs index 9a86f6f..e3c9b77 100644 --- a/RGB.NET.Devices.Novation/NovationDeviceProvider.cs +++ b/RGB.NET.Devices.Novation/NovationDeviceProvider.cs @@ -65,11 +65,10 @@ public sealed class NovationDeviceProvider : AbstractRGBDeviceProvider MidiOutCaps outCaps = OutputDeviceBase.GetDeviceCapabilities(index); if (outCaps.name == null) continue; - string deviceName = outCaps.name.ToUpperInvariant(); NovationDevices? deviceId = (NovationDevices?)Enum.GetValues(typeof(NovationDevices)) .Cast() .Where(x => x.GetDeviceId() != null) - .FirstOrDefault(x => deviceName.Contains(x.GetDeviceId()!.ToUpperInvariant())); + .FirstOrDefault(x => outCaps.name.Contains(x.GetDeviceId()!, StringComparison.InvariantCultureIgnoreCase)); if (deviceId == null) continue; diff --git a/RGB.NET.Devices.OpenRGB/Generic/LedMappings.cs b/RGB.NET.Devices.OpenRGB/Generic/LedMappings.cs index d6deb99..972bf12 100644 --- a/RGB.NET.Devices.OpenRGB/Generic/LedMappings.cs +++ b/RGB.NET.Devices.OpenRGB/Generic/LedMappings.cs @@ -141,5 +141,8 @@ internal static class LedMappings ["Key: G9"] = LedId.Keyboard_Programmable9, ["Lighting"] = LedId.Keyboard_Brightness, ["Game Mode"] = LedId.Keyboard_WinLock, + ["Num Lock Indicator"] = LedId.Keyboard_IndicatorNumLock, + ["Caps Lock Indicator"] = LedId.Keyboard_IndicatorCapsLock, + ["Scroll Lock Indicator"] = LedId.Keyboard_IndicatorScrollLock, }; } diff --git a/RGB.NET.Devices.OpenRGB/Generic/OpenRGBUpdateQueue.cs b/RGB.NET.Devices.OpenRGB/Generic/OpenRGBUpdateQueue.cs index 0537d11..43ddc0b 100644 --- a/RGB.NET.Devices.OpenRGB/Generic/OpenRGBUpdateQueue.cs +++ b/RGB.NET.Devices.OpenRGB/Generic/OpenRGBUpdateQueue.cs @@ -48,7 +48,7 @@ public sealed class OpenRGBUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.PicoPi/PicoPi/PicoPiBulkUpdateQueue.cs b/RGB.NET.Devices.PicoPi/PicoPi/PicoPiBulkUpdateQueue.cs index edd290b..3b211ae 100644 --- a/RGB.NET.Devices.PicoPi/PicoPi/PicoPiBulkUpdateQueue.cs +++ b/RGB.NET.Devices.PicoPi/PicoPi/PicoPiBulkUpdateQueue.cs @@ -44,7 +44,7 @@ public sealed class PicoPiBulkUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.PicoPi/PicoPi/PicoPiHIDUpdateQueue.cs b/RGB.NET.Devices.PicoPi/PicoPi/PicoPiHIDUpdateQueue.cs index 652ba80..cae4080 100644 --- a/RGB.NET.Devices.PicoPi/PicoPi/PicoPiHIDUpdateQueue.cs +++ b/RGB.NET.Devices.PicoPi/PicoPi/PicoPiHIDUpdateQueue.cs @@ -41,7 +41,7 @@ public sealed class PicoPiHIDUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.PicoPi/PicoPi/PicoPiSDK.cs b/RGB.NET.Devices.PicoPi/PicoPi/PicoPiSDK.cs index 2dcd6de..1e5c3ce 100644 --- a/RGB.NET.Devices.PicoPi/PicoPi/PicoPiSDK.cs +++ b/RGB.NET.Devices.PicoPi/PicoPi/PicoPiSDK.cs @@ -212,7 +212,7 @@ public sealed class PicoPiSDK : IDisposable /// /// The data to send. /// The channel to update. - public void SendHidUpdate(in Span buffer, int channel) + public void SendHidUpdate(Span buffer, int channel) { int chunks = buffer.Length / HID_OFFSET_MULTIPLIER; if ((chunks * HID_OFFSET_MULTIPLIER) < buffer.Length) chunks++; @@ -232,7 +232,7 @@ public sealed class PicoPiSDK : IDisposable /// The channel to update. /// The chunk id of the packet. (Required if packets are fragmented.) /// A value indicating if the device should update directly after receiving this packet. (If packets are fragmented this should only be true for the last chunk.) - public void SendHidUpdate(in Span data, int channel, int chunk, bool update) + public void SendHidUpdate(Span data, int channel, int chunk, bool update) { if (data.Length == 0) return; @@ -253,7 +253,7 @@ public sealed class PicoPiSDK : IDisposable /// /// The data packet to send. /// The channel to update. - public void SendBulkUpdate(in Span data, int channel) + public void SendBulkUpdate(Span data, int channel) { if ((data.Length == 0) || !IsBulkSupported) return; diff --git a/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkUpdateQueue.cs b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkUpdateQueue.cs index 17b7add..1b12a93 100644 --- a/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkUpdateQueue.cs +++ b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkUpdateQueue.cs @@ -25,7 +25,7 @@ public sealed class RazerChromaLinkUpdateQueue : RazerUpdateQueue #region Methods /// - protected override nint CreateEffectParams(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override nint CreateEffectParams(ReadOnlySpan<(object key, Color color)> dataSet) { _Color[] colors = new _Color[_Defines.CHROMALINK_MAX_LEDS]; diff --git a/RGB.NET.Devices.Razer/Enum/RazerEndpointType.cs b/RGB.NET.Devices.Razer/Enum/RazerEndpointType.cs index 01bb060..c48bfcd 100644 --- a/RGB.NET.Devices.Razer/Enum/RazerEndpointType.cs +++ b/RGB.NET.Devices.Razer/Enum/RazerEndpointType.cs @@ -1,42 +1,55 @@ -namespace RGB.NET.Devices.Razer; +using System; + +namespace RGB.NET.Devices.Razer; /// /// Represents a type of Razer SDK endpoint /// +[Flags] public enum RazerEndpointType { + /// + /// No endpoint + /// + None = 0, + /// /// The keyboard endpoint /// - Keyboard, + Keyboard = 1 << 0, /// /// The laptop keyboard endpoint, shares the endpoint but has a different LED layout /// - LaptopKeyboard, + LaptopKeyboard = 1 << 1, /// /// The mouse endpoint /// - Mouse, + Mouse = 1 << 2, /// /// The headset endpoint /// - Headset, + Headset = 1 << 3, /// /// The mousepad endpoint /// - Mousepad, + Mousepad = 1 << 4, /// /// The keypad endpoint /// - Keypad, + Keypad = 1 << 5, /// /// The Chroma Link endpoint /// - ChromaLink, -} \ No newline at end of file + ChromaLink = 1 << 6, + + /// + /// All endpoints + /// + All = ~None +} diff --git a/RGB.NET.Devices.Razer/Generic/LedMappings.cs b/RGB.NET.Devices.Razer/Generic/LedMappings.cs index d1a3dab..3b24f78 100644 --- a/RGB.NET.Devices.Razer/Generic/LedMappings.cs +++ b/RGB.NET.Devices.Razer/Generic/LedMappings.cs @@ -13,10 +13,17 @@ public static class LedMappings /// public static LedMapping Keyboard { get; } = new() { - //Row 0 is empty + #region Row 0 + + [LedId.LedStripe1] = (_Defines.KEYBOARD_MAX_COLUMN * 0) + 0, + [LedId.LedStripe16] = (_Defines.KEYBOARD_MAX_COLUMN * 0) + 23, + + #endregion #region Row 1 + [LedId.LedStripe2] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 0, + [LedId.Custom1] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 1, [LedId.Keyboard_Escape] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 2, [LedId.Keyboard_F1] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 4, [LedId.Keyboard_F2] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 5, @@ -33,12 +40,17 @@ public static class LedMappings [LedId.Keyboard_PrintScreen] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 16, [LedId.Keyboard_ScrollLock] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 17, [LedId.Keyboard_PauseBreak] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 18, - [LedId.Logo] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 21, + [LedId.Keyboard_MediaPreviousTrack] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 19, + [LedId.Keyboard_MediaPlay] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 20, + [LedId.Keyboard_MediaNextTrack] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 21, + [LedId.Keyboard_MediaMute] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 22, + [LedId.LedStripe15] = (_Defines.KEYBOARD_MAX_COLUMN * 1) + 23, #endregion #region Row 2 + [LedId.LedStripe3] = (_Defines.KEYBOARD_MAX_COLUMN * 2) + 0, [LedId.Keyboard_Programmable1] = (_Defines.KEYBOARD_MAX_COLUMN * 2) + 1, [LedId.Keyboard_GraveAccentAndTilde] = (_Defines.KEYBOARD_MAX_COLUMN * 2) + 2, [LedId.Keyboard_1] = (_Defines.KEYBOARD_MAX_COLUMN * 2) + 3, @@ -61,11 +73,13 @@ public static class LedMappings [LedId.Keyboard_NumSlash] = (_Defines.KEYBOARD_MAX_COLUMN * 2) + 20, [LedId.Keyboard_NumAsterisk] = (_Defines.KEYBOARD_MAX_COLUMN * 2) + 21, [LedId.Keyboard_NumMinus] = (_Defines.KEYBOARD_MAX_COLUMN * 2) + 22, + [LedId.LedStripe14] = (_Defines.KEYBOARD_MAX_COLUMN * 2) + 23, #endregion #region Row 3 + [LedId.LedStripe4] = (_Defines.KEYBOARD_MAX_COLUMN * 3) + 0, [LedId.Keyboard_Programmable2] = (_Defines.KEYBOARD_MAX_COLUMN * 3) + 1, [LedId.Keyboard_Tab] = (_Defines.KEYBOARD_MAX_COLUMN * 3) + 2, [LedId.Keyboard_Q] = (_Defines.KEYBOARD_MAX_COLUMN * 3) + 3, @@ -88,11 +102,13 @@ public static class LedMappings [LedId.Keyboard_Num8] = (_Defines.KEYBOARD_MAX_COLUMN * 3) + 20, [LedId.Keyboard_Num9] = (_Defines.KEYBOARD_MAX_COLUMN * 3) + 21, [LedId.Keyboard_NumPlus] = (_Defines.KEYBOARD_MAX_COLUMN * 3) + 22, + [LedId.LedStripe13] = (_Defines.KEYBOARD_MAX_COLUMN * 3) + 23, #endregion #region Row 4 + [LedId.LedStripe5] = (_Defines.KEYBOARD_MAX_COLUMN * 4) + 0, [LedId.Keyboard_Programmable3] = (_Defines.KEYBOARD_MAX_COLUMN * 4) + 1, [LedId.Keyboard_CapsLock] = (_Defines.KEYBOARD_MAX_COLUMN * 4) + 2, [LedId.Keyboard_A] = (_Defines.KEYBOARD_MAX_COLUMN * 4) + 3, @@ -111,11 +127,13 @@ public static class LedMappings [LedId.Keyboard_Num4] = (_Defines.KEYBOARD_MAX_COLUMN * 4) + 19, [LedId.Keyboard_Num5] = (_Defines.KEYBOARD_MAX_COLUMN * 4) + 20, [LedId.Keyboard_Num6] = (_Defines.KEYBOARD_MAX_COLUMN * 4) + 21, + [LedId.LedStripe12] = (_Defines.KEYBOARD_MAX_COLUMN * 4) + 23, #endregion #region Row 5 + [LedId.LedStripe6] = (_Defines.KEYBOARD_MAX_COLUMN * 5) + 0, [LedId.Keyboard_Programmable4] = (_Defines.KEYBOARD_MAX_COLUMN * 5) + 1, [LedId.Keyboard_LeftShift] = (_Defines.KEYBOARD_MAX_COLUMN * 5) + 2, [LedId.Keyboard_NonUsBackslash] = (_Defines.KEYBOARD_MAX_COLUMN * 5) + 3, @@ -135,11 +153,13 @@ public static class LedMappings [LedId.Keyboard_Num2] = (_Defines.KEYBOARD_MAX_COLUMN * 5) + 20, [LedId.Keyboard_Num3] = (_Defines.KEYBOARD_MAX_COLUMN * 5) + 21, [LedId.Keyboard_NumEnter] = (_Defines.KEYBOARD_MAX_COLUMN * 5) + 22, + [LedId.LedStripe11] = (_Defines.KEYBOARD_MAX_COLUMN * 5) + 23, #endregion #region Row 6 + [LedId.LedStripe7] = (_Defines.KEYBOARD_MAX_COLUMN * 6) + 0, [LedId.Keyboard_Programmable5] = (_Defines.KEYBOARD_MAX_COLUMN * 6) + 1, [LedId.Keyboard_LeftCtrl] = (_Defines.KEYBOARD_MAX_COLUMN * 6) + 2, [LedId.Keyboard_LeftGui] = (_Defines.KEYBOARD_MAX_COLUMN * 6) + 3, @@ -152,12 +172,18 @@ public static class LedMappings [LedId.Keyboard_ArrowLeft] = (_Defines.KEYBOARD_MAX_COLUMN * 6) + 16, [LedId.Keyboard_ArrowDown] = (_Defines.KEYBOARD_MAX_COLUMN * 6) + 17, [LedId.Keyboard_ArrowRight] = (_Defines.KEYBOARD_MAX_COLUMN * 6) + 18, + [LedId.LedStripe9] = (_Defines.KEYBOARD_MAX_COLUMN * 6) + 19, [LedId.Keyboard_Num0] = (_Defines.KEYBOARD_MAX_COLUMN * 6) + 20, [LedId.Keyboard_NumPeriodAndDelete] = (_Defines.KEYBOARD_MAX_COLUMN * 6) + 21, + [LedId.LedStripe10] = (_Defines.KEYBOARD_MAX_COLUMN * 6) + 23, #endregion - //Row 7 is also empty + #region Row 6 + + [LedId.LedStripe8] = (_Defines.KEYBOARD_MAX_COLUMN * 7) + 0, + + #endregion }; diff --git a/RGB.NET.Devices.Razer/Generic/RazerUpdateQueue.cs b/RGB.NET.Devices.Razer/Generic/RazerUpdateQueue.cs index 615e48b..f8fa417 100644 --- a/RGB.NET.Devices.Razer/Generic/RazerUpdateQueue.cs +++ b/RGB.NET.Devices.Razer/Generic/RazerUpdateQueue.cs @@ -30,7 +30,7 @@ public abstract class RazerUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { @@ -77,7 +77,7 @@ public abstract class RazerUpdateQueue : UpdateQueue /// /// The data to be updated. /// An pointing to the effect parameter struct. - protected abstract nint CreateEffectParams(in ReadOnlySpan<(object key, Color color)> dataSet); + protected abstract nint CreateEffectParams(ReadOnlySpan<(object key, Color color)> dataSet); #endregion } \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/Headset/RazerHeadsetUpdateQueue.cs b/RGB.NET.Devices.Razer/Headset/RazerHeadsetUpdateQueue.cs index 8ddbce3..e29f435 100644 --- a/RGB.NET.Devices.Razer/Headset/RazerHeadsetUpdateQueue.cs +++ b/RGB.NET.Devices.Razer/Headset/RazerHeadsetUpdateQueue.cs @@ -25,7 +25,7 @@ public sealed class RazerHeadsetUpdateQueue : RazerUpdateQueue #region Methods /// - protected override nint CreateEffectParams(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override nint CreateEffectParams(ReadOnlySpan<(object key, Color color)> dataSet) { _Color[] colors = new _Color[_Defines.HEADSET_MAX_LEDS]; diff --git a/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardUpdateQueue.cs b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardUpdateQueue.cs index 68e0f5f..4a709aa 100644 --- a/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardUpdateQueue.cs +++ b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardUpdateQueue.cs @@ -25,7 +25,7 @@ public sealed class RazerKeyboardUpdateQueue : RazerUpdateQueue #region Methods /// - protected override nint CreateEffectParams(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override nint CreateEffectParams(ReadOnlySpan<(object key, Color color)> dataSet) { _Color[] colors = new _Color[_Defines.KEYBOARD_MAX_LEDS]; diff --git a/RGB.NET.Devices.Razer/Keypad/RazerKeypadUpdateQueue.cs b/RGB.NET.Devices.Razer/Keypad/RazerKeypadUpdateQueue.cs index 7e2470e..e33487a 100644 --- a/RGB.NET.Devices.Razer/Keypad/RazerKeypadUpdateQueue.cs +++ b/RGB.NET.Devices.Razer/Keypad/RazerKeypadUpdateQueue.cs @@ -25,7 +25,7 @@ public sealed class RazerKeypadUpdateQueue : RazerUpdateQueue #region Methods /// - protected override nint CreateEffectParams(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override nint CreateEffectParams(ReadOnlySpan<(object key, Color color)> dataSet) { _Color[] colors = new _Color[_Defines.KEYPAD_MAX_LEDS]; diff --git a/RGB.NET.Devices.Razer/Mouse/RazerMouseUpdateQueue.cs b/RGB.NET.Devices.Razer/Mouse/RazerMouseUpdateQueue.cs index cbdde12..8e056c6 100644 --- a/RGB.NET.Devices.Razer/Mouse/RazerMouseUpdateQueue.cs +++ b/RGB.NET.Devices.Razer/Mouse/RazerMouseUpdateQueue.cs @@ -25,7 +25,7 @@ public sealed class RazerMouseUpdateQueue : RazerUpdateQueue #region Methods /// - protected override nint CreateEffectParams(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override nint CreateEffectParams(ReadOnlySpan<(object key, Color color)> dataSet) { _Color[] colors = new _Color[_Defines.MOUSE_MAX_LEDS]; diff --git a/RGB.NET.Devices.Razer/Mousepad/RazerMousepadUpdateQueue.cs b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadUpdateQueue.cs index 8f510e4..3b69487 100644 --- a/RGB.NET.Devices.Razer/Mousepad/RazerMousepadUpdateQueue.cs +++ b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadUpdateQueue.cs @@ -25,7 +25,7 @@ public sealed class RazerMousepadUpdateQueue : RazerUpdateQueue #region Methods /// - protected override nint CreateEffectParams(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override nint CreateEffectParams(ReadOnlySpan<(object key, Color color)> dataSet) { _Color[] colors = new _Color[_Defines.MOUSEPAD_MAX_LEDS]; diff --git a/RGB.NET.Devices.Razer/RazerDeviceProvider.cs b/RGB.NET.Devices.Razer/RazerDeviceProvider.cs index 28bd7da..6290d46 100644 --- a/RGB.NET.Devices.Razer/RazerDeviceProvider.cs +++ b/RGB.NET.Devices.Razer/RazerDeviceProvider.cs @@ -50,7 +50,7 @@ public sealed class RazerDeviceProvider : AbstractRGBDeviceProvider /// /// Forces to load the devices represented by the emulator even if they aren't reported to exist. /// - public bool LoadEmulatorDevices { get; set; } = false; + public RazerEndpointType LoadEmulatorDevices { get; set; } = RazerEndpointType.None; private const int VENDOR_ID = 0x1532; @@ -139,7 +139,7 @@ public sealed class RazerDeviceProvider : AbstractRGBDeviceProvider { 0x028B, RGBDeviceType.Keyboard, "Blade 17 (2022)", LedMappings.Keyboard, RazerEndpointType.Keyboard }, { 0x028C, RGBDeviceType.Keyboard, "Blade 14 (2022)", LedMappings.Keyboard, RazerEndpointType.Keyboard }, { 0x029F, RGBDeviceType.Keyboard, "Blade 16 (2023)", LedMappings.Blade, RazerEndpointType.Keyboard }, - { 0x028D, RGBDeviceType.Keyboard, "BlackWidow V4", LedMappings.Keyboard, RazerEndpointType.Keyboard }, + { 0x028D, RGBDeviceType.Keyboard, "BlackWidow V4 Pro", LedMappings.Keyboard, RazerEndpointType.Keyboard }, { 0x0290, RGBDeviceType.Keyboard, "DeathStalker V2 Pro", LedMappings.Keyboard, RazerEndpointType.Keyboard }, // Wireless { 0x0292, RGBDeviceType.Keyboard, "DeathStalker V2 Pro", LedMappings.Keyboard, RazerEndpointType.Keyboard }, // Wired { 0x0294, RGBDeviceType.Keyboard, "Ornata V3 X", LedMappings.Keyboard, RazerEndpointType.Keyboard }, @@ -149,6 +149,7 @@ public sealed class RazerDeviceProvider : AbstractRGBDeviceProvider { 0x02A0, RGBDeviceType.Keyboard, "Blade 18", LedMappings.Blade, RazerEndpointType.Keyboard }, { 0x02A1, RGBDeviceType.Keyboard, "Ornata V3", LedMappings.Keyboard, RazerEndpointType.Keyboard }, { 0x02A5, RGBDeviceType.Keyboard, "BlackWidow V4 75%", LedMappings.Keyboard, RazerEndpointType.Keyboard }, + { 0x02A7, RGBDeviceType.Keyboard, "Huntsman V3 Pro TKL", LedMappings.Keyboard, RazerEndpointType.Keyboard }, { 0x0A24, RGBDeviceType.Keyboard, "BlackWidow V3 TKL", LedMappings.Keyboard, RazerEndpointType.Keyboard }, // Mice @@ -216,6 +217,7 @@ public sealed class RazerDeviceProvider : AbstractRGBDeviceProvider { 0x008F, RGBDeviceType.Mouse, "Naga Pro", LedMappings.Mouse, RazerEndpointType.Mouse }, //this is via usb connection { 0x0090, RGBDeviceType.Mouse, "Naga Pro", LedMappings.Mouse, RazerEndpointType.Mouse }, //this is via bluetooth connection { 0x0091, RGBDeviceType.Mouse, "Viper 8khz", LedMappings.Mouse, RazerEndpointType.Mouse }, + { 0x0093, RGBDeviceType.Mouse, "Naga Classic", LedMappings.Mouse, RazerEndpointType.Mouse }, { 0x0094, RGBDeviceType.Mouse, "Orochi V2", LedMappings.Mouse, RazerEndpointType.Mouse }, { 0x0096, RGBDeviceType.Mouse, "Naga X", LedMappings.Mouse, RazerEndpointType.Mouse }, { 0x0099, RGBDeviceType.Mouse, "Basilisk v3", LedMappings.Mouse, RazerEndpointType.Mouse }, @@ -312,21 +314,26 @@ public sealed class RazerDeviceProvider : AbstractRGBDeviceProvider { DeviceDefinitions.LoadFilter = loadFilter; - IList devices = base.GetLoadedDevices(loadFilter).ToList(); + List devices = base.GetLoadedDevices(loadFilter).ToList(); - if (LoadEmulatorDevices) + if (LoadEmulatorDevices != RazerEndpointType.None) { - if (loadFilter.HasFlag(RGBDeviceType.Keyboard) && devices.All(d => d is not RazerKeyboardRGBDevice)) + if (loadFilter.HasFlag(RGBDeviceType.Keyboard) && (LoadEmulatorDevices.HasFlag(RazerEndpointType.Keyboard) || LoadEmulatorDevices.HasFlag(RazerEndpointType.LaptopKeyboard)) && devices.All(d => d is not RazerKeyboardRGBDevice)) devices.Add(new RazerKeyboardRGBDevice(new RazerKeyboardRGBDeviceInfo("Emulator Keyboard", RazerEndpointType.Keyboard), GetUpdateTrigger(), LedMappings.Keyboard)); - if (loadFilter.HasFlag(RGBDeviceType.Mouse) && devices.All(d => d is not RazerMouseRGBDevice)) + + if (loadFilter.HasFlag(RGBDeviceType.Mouse) && LoadEmulatorDevices.HasFlag(RazerEndpointType.Mouse) && devices.All(d => d is not RazerMouseRGBDevice)) devices.Add(new RazerMouseRGBDevice(new RazerRGBDeviceInfo(RGBDeviceType.Mouse, RazerEndpointType.Mouse, "Emulator Mouse"), GetUpdateTrigger(), LedMappings.Mouse)); - if (loadFilter.HasFlag(RGBDeviceType.Headset) && devices.All(d => d is not RazerHeadsetRGBDevice)) + + if (loadFilter.HasFlag(RGBDeviceType.Headset) && LoadEmulatorDevices.HasFlag(RazerEndpointType.Headset) && devices.All(d => d is not RazerHeadsetRGBDevice)) devices.Add(new RazerHeadsetRGBDevice(new RazerRGBDeviceInfo(RGBDeviceType.Headset, RazerEndpointType.Headset, "Emulator Headset"), GetUpdateTrigger())); - if (loadFilter.HasFlag(RGBDeviceType.Mousepad) && devices.All(d => d is not RazerMousepadRGBDevice)) + + if (loadFilter.HasFlag(RGBDeviceType.Mousepad) && LoadEmulatorDevices.HasFlag(RazerEndpointType.Mousepad) && devices.All(d => d is not RazerMousepadRGBDevice)) devices.Add(new RazerMousepadRGBDevice(new RazerRGBDeviceInfo(RGBDeviceType.Mousepad, RazerEndpointType.Mousepad, "Emulator Mousepad"), GetUpdateTrigger())); - if (loadFilter.HasFlag(RGBDeviceType.Keypad) && devices.All(d => d is not RazerMousepadRGBDevice)) + + if (loadFilter.HasFlag(RGBDeviceType.Keypad) && LoadEmulatorDevices.HasFlag(RazerEndpointType.Keypad) && devices.All(d => d is not RazerMousepadRGBDevice)) devices.Add(new RazerKeypadRGBDevice(new RazerRGBDeviceInfo(RGBDeviceType.Keypad, RazerEndpointType.Keypad, "Emulator Keypad"), GetUpdateTrigger())); - if (loadFilter.HasFlag(RGBDeviceType.Unknown) && devices.All(d => d is not RazerChromaLinkRGBDevice)) + + if (loadFilter.HasFlag(RGBDeviceType.Unknown) && LoadEmulatorDevices.HasFlag(RazerEndpointType.ChromaLink) && devices.All(d => d is not RazerChromaLinkRGBDevice)) devices.Add(new RazerChromaLinkRGBDevice(new RazerRGBDeviceInfo(RGBDeviceType.Unknown, RazerEndpointType.ChromaLink, "Emulator Chroma Link"), GetUpdateTrigger())); } diff --git a/RGB.NET.Devices.SteelSeries/Generic/LedMappings.cs b/RGB.NET.Devices.SteelSeries/Generic/LedMappings.cs index da55ac2..59310bd 100644 --- a/RGB.NET.Devices.SteelSeries/Generic/LedMappings.cs +++ b/RGB.NET.Devices.SteelSeries/Generic/LedMappings.cs @@ -315,6 +315,113 @@ public static class LedMappings { LedId.Keyboard_Custom1, SteelSeriesLedId.Power }, }; + /// + /// Gets the mapping for GE78HX keyboards. + /// + // ReSharper disable once InconsistentNaming + public static LedMapping KeyboardMSIGE78Mapping { get; } = new() + { + { LedId.Keyboard_Escape, SteelSeriesLedId.Escape }, + { LedId.Keyboard_F1, SteelSeriesLedId.F1 }, + { LedId.Keyboard_F2, SteelSeriesLedId.F2 }, + { LedId.Keyboard_F3, SteelSeriesLedId.F3 }, + { LedId.Keyboard_F4, SteelSeriesLedId.F4 }, + { LedId.Keyboard_F5, SteelSeriesLedId.F5 }, + { LedId.Keyboard_F6, SteelSeriesLedId.F6 }, + { LedId.Keyboard_F7, SteelSeriesLedId.F7 }, + { LedId.Keyboard_F8, SteelSeriesLedId.F8 }, + { LedId.Keyboard_F9, SteelSeriesLedId.F9 }, + { LedId.Keyboard_F10, SteelSeriesLedId.F10 }, + { LedId.Keyboard_F11, SteelSeriesLedId.F11 }, + { LedId.Keyboard_F12, SteelSeriesLedId.F12 }, + { LedId.Keyboard_PrintScreen, SteelSeriesLedId.PrintScreen }, + { LedId.Keyboard_Insert, SteelSeriesLedId.Insert }, + { LedId.Keyboard_Delete, SteelSeriesLedId.Delete }, + { LedId.Keyboard_GraveAccentAndTilde, SteelSeriesLedId.Backqoute }, + { LedId.Keyboard_1, SteelSeriesLedId.Keyboard1 }, + { LedId.Keyboard_2, SteelSeriesLedId.Keyboard2 }, + { LedId.Keyboard_3, SteelSeriesLedId.Keyboard3 }, + { LedId.Keyboard_4, SteelSeriesLedId.Keyboard4 }, + { LedId.Keyboard_5, SteelSeriesLedId.Keyboard5 }, + { LedId.Keyboard_6, SteelSeriesLedId.Keyboard6 }, + { LedId.Keyboard_7, SteelSeriesLedId.Keyboard7 }, + { LedId.Keyboard_8, SteelSeriesLedId.Keyboard8 }, + { LedId.Keyboard_9, SteelSeriesLedId.Keyboard9 }, + { LedId.Keyboard_0, SteelSeriesLedId.Keyboard0 }, + { LedId.Keyboard_MinusAndUnderscore, SteelSeriesLedId.Dash }, + { LedId.Keyboard_EqualsAndPlus, SteelSeriesLedId.Equal }, + { LedId.Keyboard_Backspace, SteelSeriesLedId.Backspace }, + { LedId.Keyboard_Tab, SteelSeriesLedId.Tab }, + { LedId.Keyboard_Q, SteelSeriesLedId.Q }, + { LedId.Keyboard_W, SteelSeriesLedId.W }, + { LedId.Keyboard_E, SteelSeriesLedId.E }, + { LedId.Keyboard_R, SteelSeriesLedId.R }, + { LedId.Keyboard_T, SteelSeriesLedId.T }, + { LedId.Keyboard_Y, SteelSeriesLedId.Y }, + { LedId.Keyboard_U, SteelSeriesLedId.U }, + { LedId.Keyboard_I, SteelSeriesLedId.I }, + { LedId.Keyboard_O, SteelSeriesLedId.O }, + { LedId.Keyboard_P, SteelSeriesLedId.P }, + { LedId.Keyboard_BracketLeft, SteelSeriesLedId.LBracket }, + { LedId.Keyboard_BracketRight, SteelSeriesLedId.RBracket }, + { LedId.Keyboard_Backslash, SteelSeriesLedId.Backslash }, + { LedId.Keyboard_CapsLock, SteelSeriesLedId.Caps }, + { LedId.Keyboard_A, SteelSeriesLedId.A }, + { LedId.Keyboard_S, SteelSeriesLedId.S }, + { LedId.Keyboard_D, SteelSeriesLedId.D }, + { LedId.Keyboard_F, SteelSeriesLedId.F }, + { LedId.Keyboard_G, SteelSeriesLedId.G }, + { LedId.Keyboard_H, SteelSeriesLedId.H }, + { LedId.Keyboard_J, SteelSeriesLedId.J }, + { LedId.Keyboard_K, SteelSeriesLedId.K }, + { LedId.Keyboard_L, SteelSeriesLedId.L }, + { LedId.Keyboard_SemicolonAndColon, SteelSeriesLedId.Semicolon }, + { LedId.Keyboard_ApostropheAndDoubleQuote, SteelSeriesLedId.Quote }, + { LedId.Keyboard_Enter, SteelSeriesLedId.Return }, + { LedId.Keyboard_LeftShift, SteelSeriesLedId.LShift }, + { LedId.Keyboard_Z, SteelSeriesLedId.Z }, + { LedId.Keyboard_X, SteelSeriesLedId.X }, + { LedId.Keyboard_C, SteelSeriesLedId.C }, + { LedId.Keyboard_V, SteelSeriesLedId.V }, + { LedId.Keyboard_B, SteelSeriesLedId.B }, + { LedId.Keyboard_N, SteelSeriesLedId.N }, + { LedId.Keyboard_M, SteelSeriesLedId.M }, + { LedId.Keyboard_CommaAndLessThan, SteelSeriesLedId.Comma }, + { LedId.Keyboard_PeriodAndBiggerThan, SteelSeriesLedId.Period }, + { LedId.Keyboard_SlashAndQuestionMark, SteelSeriesLedId.Slash }, + { LedId.Keyboard_RightShift, SteelSeriesLedId.RShift }, + { LedId.Keyboard_LeftCtrl, SteelSeriesLedId.LCtrl }, + { LedId.Keyboard_LeftGui, SteelSeriesLedId.LWin }, + { LedId.Keyboard_LeftAlt, SteelSeriesLedId.LAlt }, + { LedId.Keyboard_Space, SteelSeriesLedId.Spacebar }, + { LedId.Keyboard_RightAlt, SteelSeriesLedId.RAlt }, + { LedId.Keyboard_NonUsBackslash, SteelSeriesLedId.NonUsBackslash }, + { LedId.Keyboard_RightCtrl, SteelSeriesLedId.RCtrl }, + { LedId.Keyboard_Function, SteelSeriesLedId.Fn }, + { LedId.Keyboard_ArrowUp, SteelSeriesLedId.UpArrow }, + { LedId.Keyboard_ArrowLeft, SteelSeriesLedId.LeftArrow }, + { LedId.Keyboard_ArrowDown, SteelSeriesLedId.DownArrow }, + { LedId.Keyboard_ArrowRight, SteelSeriesLedId.RightArrow }, + { LedId.Keyboard_NumAsterisk, SteelSeriesLedId.KeypadTimes }, + { LedId.Keyboard_NumSlash, SteelSeriesLedId.KeypadDivide }, + { LedId.Logo, SteelSeriesLedId.Power }, + { LedId.Keyboard_NumPlus, SteelSeriesLedId.KeypadPlus }, + { LedId.Keyboard_NumMinus, SteelSeriesLedId.KeypadMinus }, + { LedId.Keyboard_NumLock, SteelSeriesLedId.KeypadNumLock }, + { LedId.Keyboard_Num7, SteelSeriesLedId.Keypad7 }, + { LedId.Keyboard_Num8, SteelSeriesLedId.Keypad8 }, + { LedId.Keyboard_Num9, SteelSeriesLedId.Keypad9 }, + { LedId.Keyboard_Num4, SteelSeriesLedId.Keypad4 }, + { LedId.Keyboard_Num5, SteelSeriesLedId.Keypad5 }, + { LedId.Keyboard_Num6, SteelSeriesLedId.Keypad6 }, + { LedId.Keyboard_Num1, SteelSeriesLedId.Keypad1 }, + { LedId.Keyboard_Num2, SteelSeriesLedId.Keypad2 }, + { LedId.Keyboard_Num3, SteelSeriesLedId.Keypad3 }, + { LedId.Keyboard_Num0, SteelSeriesLedId.Keypad0 }, + { LedId.Keyboard_NumPeriodAndDelete, SteelSeriesLedId.KeypadPeriod }, + { LedId.Keyboard_NumEnter, SteelSeriesLedId.KeypadEnter }, + }; + /// /// Gets the mapping for one-zone mice. /// diff --git a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs index c85f394..815e223 100644 --- a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs +++ b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs @@ -51,7 +51,7 @@ internal sealed class SteelSeriesDeviceUpdateQueue : UpdateQueue } /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs index 0bf1958..60b797c 100644 --- a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs +++ b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using RGB.NET.Core; using RGB.NET.Devices.SteelSeries.API; @@ -69,6 +69,7 @@ public sealed class SteelSeriesDeviceProvider : AbstractRGBDeviceProvider { 0x1852, RGBDeviceType.Mouse, "Aerox 5 Wireless", LedMappings.MouseThreeZone, SteelSeriesDeviceType.ThreeZone }, //Keyboards + { 0x161A, RGBDeviceType.Keyboard, "Apex 3", LedMappings.KeyboardMappingUk, SteelSeriesDeviceType.TenZone }, { 0x161C, RGBDeviceType.Keyboard, "Apex 5", LedMappings.KeyboardMappingUk, SteelSeriesDeviceType.PerKey }, { 0x1612, RGBDeviceType.Keyboard, "Apex 7", LedMappings.KeyboardMappingUk, SteelSeriesDeviceType.PerKey }, { 0x1618, RGBDeviceType.Keyboard, "Apex 7 TKL", LedMappings.KeyboardTklMappingUk, SteelSeriesDeviceType.PerKey }, @@ -76,7 +77,10 @@ public sealed class SteelSeriesDeviceProvider : AbstractRGBDeviceProvider { 0x1600, RGBDeviceType.Keyboard, "Apex M800", LedMappings.KeyboardMappingUk, SteelSeriesDeviceType.PerKey }, { 0x1610, RGBDeviceType.Keyboard, "Apex Pro", LedMappings.KeyboardMappingUk, SteelSeriesDeviceType.PerKey }, { 0x1614, RGBDeviceType.Keyboard, "Apex Pro TKL", LedMappings.KeyboardTklMappingUk, SteelSeriesDeviceType.PerKey }, - { 0x2036, RGBDeviceType.Keyboard, "MSI Notebook", LedMappings.KeyboardNotebookMappingUk, SteelSeriesDeviceType.PerKey }, + { 0x1630, RGBDeviceType.Keyboard, "Apex Pro TKL", LedMappings.KeyboardTklMappingUk, SteelSeriesDeviceType.PerKey }, // DarthAffe 27.05.2024: This could be a generic wireless connector + { 0x2036, RGBDeviceType.Keyboard, "MSI Notebook", LedMappings.KeyboardNotebookMappingUk, SteelSeriesDeviceType.PerKey }, + { 0x113A, RGBDeviceType.Keyboard, "MSI GE78HX", LedMappings.KeyboardMSIGE78Mapping, SteelSeriesDeviceType.PerKey }, + { 0x1122, RGBDeviceType.Keyboard, "MSI Notebook", LedMappings.KeyboardNotebookMappingUk, SteelSeriesDeviceType.PerKey }, //Headsets { 0x12AA, RGBDeviceType.Headset, "Arctis 5", LedMappings.HeadsetTwoZone, SteelSeriesDeviceType.TwoZone }, diff --git a/RGB.NET.Devices.WLED/API/WledAPI.cs b/RGB.NET.Devices.WLED/API/WledAPI.cs new file mode 100644 index 0000000..c67cda9 --- /dev/null +++ b/RGB.NET.Devices.WLED/API/WledAPI.cs @@ -0,0 +1,33 @@ +using System.Net.Http; +using System.Net.Http.Json; + +namespace RGB.NET.Devices.WLED; + +/// +/// Partial implementation of the WLED-JSON-API +/// +public static class WledAPI +{ + /// + /// Gets the data returned by the 'info' endpoint of the WLED-device. + /// + /// The address of the device to request data from. + /// The data returned by the WLED-device. + public static WledInfo? Info(string address) + { + if (string.IsNullOrEmpty(address)) return null; + + using HttpClient client = new(); + try + { + return client.Send(new HttpRequestMessage(HttpMethod.Get, $"http://{address}/json/info")) + .Content + .ReadFromJsonAsync() + .Result; + } + catch + { + return null; + } + } +} \ No newline at end of file diff --git a/RGB.NET.Devices.WLED/API/WledInfo.cs b/RGB.NET.Devices.WLED/API/WledInfo.cs new file mode 100644 index 0000000..ca92ab8 --- /dev/null +++ b/RGB.NET.Devices.WLED/API/WledInfo.cs @@ -0,0 +1,156 @@ +// ReSharper disable InconsistentNaming +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace RGB.NET.Devices.WLED; + +public class WledInfo +{ + [JsonPropertyName("ver")] + public string Version { get; set; } = ""; + + [JsonPropertyName("vid")] + public uint BuildId { get; set; } + + [JsonPropertyName("leds")] + public LedsInfo Leds { get; set; } = new(); + + [JsonPropertyName("str")] + public bool SyncReceive { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } = ""; + + [JsonPropertyName("udpport")] + public ushort UDPPort { get; set; } + + [JsonPropertyName("live")] + public bool IsLive { get; set; } + + [JsonPropertyName("liveseg")] + public short MainSegment { get; set; } + + [JsonPropertyName("lm")] + public string RealtimeDataSource { get; set; } = ""; + + [JsonPropertyName("lip")] + public string RealtimeDataSourceIpAddress { get; set; } = ""; + + [JsonPropertyName("ws")] + public byte ConnectedWebSocketCount { get; set; } + + [JsonPropertyName("fxcount")] + public byte EffectCount { get; set; } + + [JsonPropertyName("palcount")] + public short PaletteCount { get; set; } + + [JsonPropertyName("maps")] + public List LedMaps { get; set; } = []; + + [JsonPropertyName("wifi")] + public Wifi WifiInfo { get; set; } = new(); + + [JsonPropertyName("fs")] + public Fs FilesystemInfo { get; set; } = new(); + + [JsonPropertyName("ndc")] + public short DiscoveredDeviceCount { get; set; } + + [JsonPropertyName("arch")] + public string PlatformName { get; set; } = ""; + + [JsonPropertyName("core")] + public string ArduinoCoreVersion { get; set; } = ""; + + [JsonPropertyName("freeheap")] + public uint FreeHeap { get; set; } + + [JsonPropertyName("uptime")] + public uint Uptime { get; set; } + + [JsonPropertyName("time")] + public string Time { get; set; } = ""; + + [JsonPropertyName("brand")] + public string Brand { get; set; } = ""; + + [JsonPropertyName("product")] + public string Product { get; set; } = ""; + + [JsonPropertyName("ip")] + public string IpAddress { get; set; } = ""; + + + public class LedsInfo + { + [JsonPropertyName("count")] + public ushort Count { get; set; } + + [JsonPropertyName("pwr")] + public ushort PowerUsage { get; set; } + + [JsonPropertyName("fps")] + public ushort RefreshRate { get; set; } + + [JsonPropertyName("maxpwr")] + public ushort MaxPower { get; set; } + + [JsonPropertyName("maxseg")] + public byte MaxSegments { get; set; } + + [JsonPropertyName("matrix")] + public MatrixDims? Matrix { get; set; } + + [JsonPropertyName("seglc")] + public List SegmentLightCapabilities { get; set; } = []; + + [JsonPropertyName("lc")] + public byte CombinedSegmentLightCapabilities { get; set; } + } + + public class Map + { + [JsonPropertyName("id")] + public byte Id { get; set; } + + [JsonPropertyName("n")] + public string Name { get; set; } = ""; + } + + public class Wifi + { + [JsonPropertyName("bssid")] + public string BSSID { get; set; } = ""; + + [JsonPropertyName("rssi")] + public long RSSI { get; set; } + + [JsonPropertyName("signal")] + public byte SignalQuality { get; set; } + + [JsonPropertyName("channel")] + public int Channel { get; set; } + } + + public class Fs + { + [JsonPropertyName("u")] + public uint UsedSpace { get; set; } + + [JsonPropertyName("t")] + public uint TotalSpace { get; set; } + + [JsonPropertyName("pmt")] + public ulong LastPresetsJsonModificationTime { get; set; } + } + + public class MatrixDims + { + [JsonPropertyName("w")] + public ushort Width { get; set; } + + [JsonPropertyName("h")] + public ushort Height { get; set; } + } +} diff --git a/RGB.NET.Devices.WLED/Generic/IWLedRGBDevice.cs b/RGB.NET.Devices.WLED/Generic/IWLedRGBDevice.cs new file mode 100644 index 0000000..f875820 --- /dev/null +++ b/RGB.NET.Devices.WLED/Generic/IWLedRGBDevice.cs @@ -0,0 +1,8 @@ +using RGB.NET.Core; + +namespace RGB.NET.Devices.WLED; + +/// +/// Represents a WLED-device. +/// +internal interface IWledRGBDevice : IRGBDevice; \ No newline at end of file diff --git a/RGB.NET.Devices.WLED/Generic/IWledDeviceDefinition.cs b/RGB.NET.Devices.WLED/Generic/IWledDeviceDefinition.cs new file mode 100644 index 0000000..2ea5299 --- /dev/null +++ b/RGB.NET.Devices.WLED/Generic/IWledDeviceDefinition.cs @@ -0,0 +1,11 @@ +namespace RGB.NET.Devices.WLED; + +/// +/// Represents the data used to create a WLED-device. +/// +public interface IWledDeviceDefinition +{ + string Address { get; } + string? Manufacturer { get; } + string? Model { get; } +} \ No newline at end of file diff --git a/RGB.NET.Devices.WLED/Generic/WLedDeviceUpdateQueue.cs b/RGB.NET.Devices.WLED/Generic/WLedDeviceUpdateQueue.cs new file mode 100644 index 0000000..7ebeef7 --- /dev/null +++ b/RGB.NET.Devices.WLED/Generic/WLedDeviceUpdateQueue.cs @@ -0,0 +1,102 @@ +using System; +using System.Net.Sockets; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WLED; + +/// +/// +/// Represents the update-queue performing updates for WLED devices. +/// +internal sealed class WledDeviceUpdateQueue : UpdateQueue +{ + #region Properties & Fields + + /// + /// The UDP-Connection used to send data. + /// + private readonly UdpClient _socket; + + /// + /// The buffer the UDP-data is stored in. + /// + private byte[] _buffer; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The update trigger used by this queue. + /// The device type used to identify the device. + public WledDeviceUpdateQueue(IDeviceUpdateTrigger updateTrigger, string address, int port, int ledCount) + : base(updateTrigger) + { + _buffer = new byte[2 + (ledCount * 3)]; + _buffer[0] = 2; // protocol: DRGB + _buffer[1] = 2; // Timeout 2s + + _socket = new UdpClient(); + _socket.Connect(address, port); + } + + #endregion + + #region Methods + + /// + protected override void OnUpdate(object? sender, CustomUpdateData customData) + { + try + { + if (customData[CustomUpdateDataIndex.HEARTBEAT] as bool? ?? false) + Update(Array.Empty<(object key, Color color)>()); + else + base.OnUpdate(sender, customData); + } + catch (Exception ex) + { + WledDeviceProvider.Instance.Throw(ex); + } + } + + /// + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) + { + try + { + Span data = _buffer.AsSpan()[2..]; + foreach ((object key, Color color) in dataSet) + { + int ledIndex = (int)key; + int offset = (ledIndex * 3); + data[offset] = color.GetR(); + data[offset + 1] = color.GetG(); + data[offset + 2] = color.GetB(); + } + + _socket.Send(_buffer); + + return true; + } + catch (Exception ex) + { + WledDeviceProvider.Instance.Throw(ex); + } + + return false; + } + + /// + public override void Dispose() + { + base.Dispose(); + + _socket.Dispose(); + _buffer = []; + } + + #endregion +} \ No newline at end of file diff --git a/RGB.NET.Devices.WLED/Generic/WLedRGBDevice.cs b/RGB.NET.Devices.WLED/Generic/WLedRGBDevice.cs new file mode 100644 index 0000000..8178732 --- /dev/null +++ b/RGB.NET.Devices.WLED/Generic/WLedRGBDevice.cs @@ -0,0 +1,39 @@ +using RGB.NET.Core; + +namespace RGB.NET.Devices.WLED; + +/// +/// +/// +/// Represents a WLED-device. +/// +public sealed class WledRGBDevice : AbstractRGBDevice, IWledRGBDevice, ILedStripe +{ + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + internal WledRGBDevice(WledRGBDeviceInfo info, string address, IDeviceUpdateTrigger updateTrigger) + : base(info, new WledDeviceUpdateQueue(updateTrigger, address, info.Info.UDPPort, info.Info.Leds.Count)) + { + InitializeLayout(); + + RequiresFlush = true; + } + + #endregion + + #region Methods + + private void InitializeLayout() + { + for (int i = 0; i < DeviceInfo.Info.Leds.Count; i++) + AddLed(LedId.LedStripe1 + i, new Point(i * 10, 0), new Size(10, 10)); + } + + /// + protected override object GetLedCustomData(LedId ledId) => ledId - LedId.LedStripe1; + + #endregion +} \ No newline at end of file diff --git a/RGB.NET.Devices.WLED/Generic/WLedRGBDeviceInfo.cs b/RGB.NET.Devices.WLED/Generic/WLedRGBDeviceInfo.cs new file mode 100644 index 0000000..896ca94 --- /dev/null +++ b/RGB.NET.Devices.WLED/Generic/WLedRGBDeviceInfo.cs @@ -0,0 +1,52 @@ +using RGB.NET.Core; + +namespace RGB.NET.Devices.WLED; + +/// +/// +/// Represents a generic information for a WLED-. +/// +public sealed class WledRGBDeviceInfo : IRGBDeviceInfo +{ + #region Properties & Fields + + /// + public RGBDeviceType DeviceType => RGBDeviceType.LedStripe; + + /// + public string DeviceName { get; } + + /// + public string Manufacturer { get; } + + /// + public string Model { get; } + + /// + public object? LayoutMetadata { get; set; } + + /// + /// Gets some info returned by the WLED-device. + /// + public WledInfo Info { get; } + + #endregion + + #region Constructors + + /// + /// Internal constructor of managed . + /// + /// The manufacturer of the device. + /// The represented device model. + internal WledRGBDeviceInfo(WledInfo info, string? manufacturer, string? model) + { + this.Info = info; + this.Manufacturer = manufacturer ?? info.Brand; + this.Model = model ?? info.Name; + + DeviceName = DeviceHelper.CreateDeviceName(Manufacturer, Model); + } + + #endregion +} \ No newline at end of file diff --git a/RGB.NET.Devices.WLED/Generic/WledDeviceDefinition.cs b/RGB.NET.Devices.WLED/Generic/WledDeviceDefinition.cs new file mode 100644 index 0000000..7d70f28 --- /dev/null +++ b/RGB.NET.Devices.WLED/Generic/WledDeviceDefinition.cs @@ -0,0 +1,18 @@ +namespace RGB.NET.Devices.WLED; + +/// +public class WledDeviceDefinition(string address, string? manufacturer = null, string? model = null) : IWledDeviceDefinition +{ + #region Properties & Fields + + /// + public string Address { get; } = address; + + /// + public string? Manufacturer { get; } = manufacturer; + + /// + public string? Model { get; } = model; + + #endregion +} \ No newline at end of file diff --git a/RGB.NET.Devices.WLED/README.md b/RGB.NET.Devices.WLED/README.md new file mode 100644 index 0000000..8c17be2 --- /dev/null +++ b/RGB.NET.Devices.WLED/README.md @@ -0,0 +1,82 @@ +[RGB.NET](https://github.com/DarthAffe/RGB.NET) Device-Provider-Package for [WLED](https://kno.wled.ge/)-devices. + +## Usage +This provider does not load anything by default and requires additional configuration to work. + +```csharp +// Add as many WLED-devices as you like +WledDeviceProvider.Instance.AddDeviceDefinition(new WledDeviceDefinition("")); + +surface.Load(WledDeviceProvider.Instance); +``` + +You can also override the manufacturer and device model in the DeviceDefinition. + +# Required SDK +This provider does not require an additional SDK. + +UDP realtime needs to be enabled on the WLED device. + +# Automatic device discovery +Due to the requirement of an additional dependency and the requirement to be able to configure devices manually anywy automatic discovery is not part of the provider. + +Using the nuget `Tmds.MDns` you can use the following Helper to do this: + +```csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Tmds.MDns; + +namespace RGB.NET.Devices.WLED; + +public static class WledDiscoveryHelper +{ + /// + /// Searches for WLED devices and returns a list of devices found. + /// + /// The time the discovery is waiting for responses. Choose this as short as possible as the method is blocking + /// The maximum amount of devices that are expected in the network. The discovery will stop early if the given amount of devices is found. + /// A list of addresses and device-infos. + public static IEnumerable<(string address, WledInfo info)> DiscoverDevices(int waitFor = 500, int maxDevices = -1) + { + List<(string address, WledInfo info)> devices = []; + using ManualResetEventSlim waitEvent = new(false); + + int devicesToDetect = maxDevices <= 0 ? int.MaxValue : maxDevices; + + ServiceBrowser mdns = new(); + mdns.ServiceAdded += OnServiceAdded; + mdns.StartBrowse("_http._tcp"); + + waitEvent.Wait(TimeSpan.FromMilliseconds(waitFor)); + + mdns.StopBrowse(); + mdns.ServiceAdded -= OnServiceAdded; + + return devices; + + void OnServiceAdded(object? sender, ServiceAnnouncementEventArgs args) + { + string address = args.Announcement.Addresses.FirstOrDefault()?.ToString() ?? string.Empty; + if (!string.IsNullOrWhiteSpace(address)) + { + WledInfo? info = null; + try + { + info = WledAPI.Info(address); + } + catch { } + + if (info != null) + { + devices.Add((address, info)); + if (--devicesToDetect <= 0) + waitEvent.Set(); + } + } + } + } +} +``` \ No newline at end of file diff --git a/RGB.NET.Devices.WLED/RGB.NET.Devices.WLED.csproj b/RGB.NET.Devices.WLED/RGB.NET.Devices.WLED.csproj new file mode 100644 index 0000000..84ceafa --- /dev/null +++ b/RGB.NET.Devices.WLED/RGB.NET.Devices.WLED.csproj @@ -0,0 +1,62 @@ + + + net8.0;net7.0;net6.0 + latest + enable + + Darth Affe + Wyrez + en-US + en-US + RGB.NET.Devices.WLED + RGB.NET.Devices.WLED + RGB.NET.Devices.WLED + RGB.NET.Devices.WLED + RGB.NET.Devices.WLED + WLED-Device-Implementations of RGB.NET + WLED-Device-Implementations of RGB.NET, a C# (.NET) library for accessing various RGB-peripherals + Copyright © Darth Affe 2024 + Copyright © Darth Affe 2024 + icon.png + README.md + https://github.com/DarthAffe/RGB.NET + LGPL-2.1-only + Github + https://github.com/DarthAffe/RGB.NET + True + + + + 0.0.1 + 0.0.1 + 0.0.1 + + ..\bin\ + true + True + True + portable + snupkg + + + + TRACE;DEBUG + true + false + + + + true + $(NoWarn);CS1591;CS1572;CS1573 + RELEASE + + + + + + + + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.WLED/RGB.NET.Devices.WLED.csproj.DotSettings b/RGB.NET.Devices.WLED/RGB.NET.Devices.WLED.csproj.DotSettings new file mode 100644 index 0000000..e43e41f --- /dev/null +++ b/RGB.NET.Devices.WLED/RGB.NET.Devices.WLED.csproj.DotSettings @@ -0,0 +1,3 @@ + + True + True \ No newline at end of file diff --git a/RGB.NET.Devices.WLED/WLedDeviceProvider.cs b/RGB.NET.Devices.WLED/WLedDeviceProvider.cs new file mode 100644 index 0000000..978565e --- /dev/null +++ b/RGB.NET.Devices.WLED/WLedDeviceProvider.cs @@ -0,0 +1,113 @@ +// ReSharper disable MemberCanBePrivate.Global + +using System; +using System.Collections.Generic; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WLED; + +/// +/// +/// Represents a device provider responsible for WS2812B- and WS2811-Led-devices. +/// +// ReSharper disable once InconsistentNaming +// ReSharper disable once UnusedType.Global +public sealed class WledDeviceProvider : AbstractRGBDeviceProvider +{ + #region Constants + + private const int HEARTBEAT_TIMER = 250; + + #endregion + + #region Properties & Fields + + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + + private static WledDeviceProvider? _instance; + /// + /// Gets the singleton instance. + /// + public static WledDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new WledDeviceProvider(); + } + } + + /// + /// Gets a list of all defined device-definitions. + /// + public List DeviceDefinitions { get; } = []; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// Thrown if this constructor is called even if there is already an instance of this class. + public WledDeviceProvider() + { + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(WledDeviceProvider)}"); + _instance = this; + } + } + + #endregion + + #region Methods + + /// + /// Adds the specified to this device-provider. + /// + /// The to add. + public void AddDeviceDefinition(IWledDeviceDefinition deviceDefinition) => DeviceDefinitions.Add(deviceDefinition); + + /// + protected override void InitializeSDK() { } + + /// + protected override IEnumerable LoadDevices() + { + int i = 0; + foreach (IWledDeviceDefinition deviceDefinition in DeviceDefinitions) + { + IDeviceUpdateTrigger updateTrigger = GetUpdateTrigger(i++); + WledRGBDevice? device = CreateWledDevice(deviceDefinition, updateTrigger); + if (device != null) + yield return device; + } + } + + private static WledRGBDevice? CreateWledDevice(IWledDeviceDefinition deviceDefinition, IDeviceUpdateTrigger updateTrigger) + { + WledInfo? wledInfo = WledAPI.Info(deviceDefinition.Address); + if (wledInfo == null) return null; + + return new WledRGBDevice(new WledRGBDeviceInfo(wledInfo, deviceDefinition.Manufacturer, deviceDefinition.Model), deviceDefinition.Address, updateTrigger); + } + + protected override IDeviceUpdateTrigger CreateUpdateTrigger(int id, double updateRateHardLimit) => new DeviceUpdateTrigger(updateRateHardLimit) { HeartbeatTimer = HEARTBEAT_TIMER }; + + /// + protected override void Dispose(bool disposing) + { + lock (_lock) + { + base.Dispose(disposing); + + DeviceDefinitions.Clear(); + + _instance = null; + } + } + + #endregion +} \ No newline at end of file diff --git a/RGB.NET.Devices.WS281X/Generic/SerialPortUpdateQueue.cs b/RGB.NET.Devices.WS281X/Generic/SerialPortUpdateQueue.cs index f1aa16c..699218d 100644 --- a/RGB.NET.Devices.WS281X/Generic/SerialPortUpdateQueue.cs +++ b/RGB.NET.Devices.WS281X/Generic/SerialPortUpdateQueue.cs @@ -56,7 +56,7 @@ public abstract class SerialConnectionUpdateQueue : UpdateQueue } /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.WS281X/NodeMCU/NodeMCUWS2812USBUpdateQueue.cs b/RGB.NET.Devices.WS281X/NodeMCU/NodeMCUWS2812USBUpdateQueue.cs index 38a51f1..442c5b3 100644 --- a/RGB.NET.Devices.WS281X/NodeMCU/NodeMCUWS2812USBUpdateQueue.cs +++ b/RGB.NET.Devices.WS281X/NodeMCU/NodeMCUWS2812USBUpdateQueue.cs @@ -77,7 +77,7 @@ public sealed class NodeMCUWS2812USBUpdateQueue : UpdateQueue } /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Devices.Wooting/Generic/WootingUpdateQueue.cs b/RGB.NET.Devices.Wooting/Generic/WootingUpdateQueue.cs index 8a3012e..be942cf 100644 --- a/RGB.NET.Devices.Wooting/Generic/WootingUpdateQueue.cs +++ b/RGB.NET.Devices.Wooting/Generic/WootingUpdateQueue.cs @@ -33,7 +33,7 @@ public sealed class WootingUpdateQueue : UpdateQueue #region Methods /// - protected override bool Update(in ReadOnlySpan<(object key, Color color)> dataSet) + protected override bool Update(ReadOnlySpan<(object key, Color color)> dataSet) { try { diff --git a/RGB.NET.Presets/Textures/BytePixelTexture.cs b/RGB.NET.Presets/Textures/BytePixelTexture.cs index 103b2ae..176b54f 100644 --- a/RGB.NET.Presets/Textures/BytePixelTexture.cs +++ b/RGB.NET.Presets/Textures/BytePixelTexture.cs @@ -61,7 +61,7 @@ public sealed class BytePixelTexture : PixelTexture /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override Color GetColor(in ReadOnlySpan pixel) + protected override Color GetColor(ReadOnlySpan pixel) { return ColorFormat switch { diff --git a/RGB.NET.Presets/Textures/FloatPixelTexture.cs b/RGB.NET.Presets/Textures/FloatPixelTexture.cs index f60b177..4657ec3 100644 --- a/RGB.NET.Presets/Textures/FloatPixelTexture.cs +++ b/RGB.NET.Presets/Textures/FloatPixelTexture.cs @@ -61,7 +61,7 @@ public sealed class FloatPixelTexture : PixelTexture /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override Color GetColor(in ReadOnlySpan pixel) + protected override Color GetColor(ReadOnlySpan pixel) { return ColorFormat switch { diff --git a/RGB.NET.Presets/Textures/Sampler/AverageByteSampler.cs b/RGB.NET.Presets/Textures/Sampler/AverageByteSampler.cs index bf5d7c6..f9002c9 100644 --- a/RGB.NET.Presets/Textures/Sampler/AverageByteSampler.cs +++ b/RGB.NET.Presets/Textures/Sampler/AverageByteSampler.cs @@ -18,7 +18,7 @@ public sealed class AverageByteSampler : ISampler #region Methods /// - public unsafe void Sample(in SamplerInfo info, in Span pixelData) + public unsafe void Sample(in SamplerInfo info, Span pixelData) { int count = info.Width * info.Height; if (count == 0) return; diff --git a/RGB.NET.Presets/Textures/Sampler/AverageFloatSampler.cs b/RGB.NET.Presets/Textures/Sampler/AverageFloatSampler.cs index cea8f45..9cdc37b 100644 --- a/RGB.NET.Presets/Textures/Sampler/AverageFloatSampler.cs +++ b/RGB.NET.Presets/Textures/Sampler/AverageFloatSampler.cs @@ -12,7 +12,7 @@ public sealed class AverageFloatSampler : ISampler #region Methods /// - public unsafe void Sample(in SamplerInfo info, in Span pixelData) + public unsafe void Sample(in SamplerInfo info, Span pixelData) { int count = info.Width * info.Height; if (count == 0) return; diff --git a/RGB.NET.sln b/RGB.NET.sln index 257bf65..44b786e 100644 --- a/RGB.NET.sln +++ b/RGB.NET.sln @@ -49,6 +49,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Devices.OpenRGB", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Devices.Corsair_Legacy", "RGB.NET.Devices.Corsair_Legacy\RGB.NET.Devices.Corsair_Legacy.csproj", "{66AF690C-27A1-4097-AC53-57C0ED89E286}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Devices.WLED", "RGB.NET.Devices.WLED\RGB.NET.Devices.WLED.csproj", "{C533C5EA-66A8-4826-A814-80791E7593ED}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -139,6 +141,10 @@ Global {66AF690C-27A1-4097-AC53-57C0ED89E286}.Debug|Any CPU.Build.0 = Debug|Any CPU {66AF690C-27A1-4097-AC53-57C0ED89E286}.Release|Any CPU.ActiveCfg = Release|Any CPU {66AF690C-27A1-4097-AC53-57C0ED89E286}.Release|Any CPU.Build.0 = Release|Any CPU + {C533C5EA-66A8-4826-A814-80791E7593ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C533C5EA-66A8-4826-A814-80791E7593ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C533C5EA-66A8-4826-A814-80791E7593ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C533C5EA-66A8-4826-A814-80791E7593ED}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -161,6 +167,7 @@ Global {EDBA49D6-AE96-4E96-9E6A-30154D93BD5F} = {92D7C263-D4C9-4D26-93E2-93C1F9C2CD16} {F29A96E5-CDD0-469F-A871-A35A7519BC49} = {D13032C6-432E-4F43-8A32-071133C22B16} {66AF690C-27A1-4097-AC53-57C0ED89E286} = {D13032C6-432E-4F43-8A32-071133C22B16} + {C533C5EA-66A8-4826-A814-80791E7593ED} = {D13032C6-432E-4F43-8A32-071133C22B16} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7F222AD4-1F9E-4AAB-9D69-D62372D4C1BA}