From fb6a94f8b219ce856b65d7d1700c60410624b90e Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sat, 15 May 2021 23:14:07 +0200 Subject: [PATCH] Improved DeviceName-generation to be more consistent after provider disposes. This addresses #198 --- RGB.NET.Core/Devices/AbstractRGBDevice.cs | 2 + RGB.NET.Core/Helper/DeviceHelper.cs | 26 ++------ RGB.NET.Core/Ids/IdGenerator.cs | 63 +++++++++++++++++++ RGB.NET.Core/RGB.NET.Core.csproj.DotSettings | 1 + RGB.NET.Core/RGBSurface.cs | 5 +- .../CorsairDeviceProvider.cs | 11 ++-- .../Custom/CorsairCustomRGBDeviceInfo.cs | 19 +++--- 7 files changed, 89 insertions(+), 38 deletions(-) create mode 100644 RGB.NET.Core/Ids/IdGenerator.cs diff --git a/RGB.NET.Core/Devices/AbstractRGBDevice.cs b/RGB.NET.Core/Devices/AbstractRGBDevice.cs index 0ff0471..4505c42 100644 --- a/RGB.NET.Core/Devices/AbstractRGBDevice.cs +++ b/RGB.NET.Core/Devices/AbstractRGBDevice.cs @@ -135,6 +135,8 @@ namespace RGB.NET.Core { try { UpdateQueue.Dispose(); } catch { /* :( */ } try { LedMapping.Clear(); } catch { /* this really shouldn't happen */ } + + IdGenerator.ResetCounter(GetType().Assembly); } /// diff --git a/RGB.NET.Core/Helper/DeviceHelper.cs b/RGB.NET.Core/Helper/DeviceHelper.cs index 41778bd..cb2f358 100644 --- a/RGB.NET.Core/Helper/DeviceHelper.cs +++ b/RGB.NET.Core/Helper/DeviceHelper.cs @@ -1,32 +1,14 @@ -using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; namespace RGB.NET.Core { public static class DeviceHelper { - #region Properties & Fields - - private static readonly Dictionary MODEL_COUNTER = new(); - - #endregion - #region Methods - public static string CreateDeviceName(string manufacturer, string model) => $"{manufacturer} {MakeUnique(model)}"; - - public static string MakeUnique(string model) - { - if (MODEL_COUNTER.TryGetValue(model, out int _)) - { - int counter = ++MODEL_COUNTER[model]; - return $"{model} {counter}"; - } - else - { - MODEL_COUNTER.Add(model, 1); - return model; - } - } + [MethodImpl(MethodImplOptions.NoInlining)] + public static string CreateDeviceName(string manufacturer, string model) => IdGenerator.MakeUnique(Assembly.GetCallingAssembly(), $"{manufacturer} {model}"); #endregion } diff --git a/RGB.NET.Core/Ids/IdGenerator.cs b/RGB.NET.Core/Ids/IdGenerator.cs new file mode 100644 index 0000000..cb68c41 --- /dev/null +++ b/RGB.NET.Core/Ids/IdGenerator.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace RGB.NET.Core +{ + public static class IdGenerator + { + #region Properties & Fields + + // ReSharper disable InconsistentNaming + private static readonly HashSet _registeredIds = new(); + private static readonly Dictionary> _idMappings = new(); + private static readonly Dictionary> _counter = new(); + // ReSharper restore InconsistentNaming + + #endregion + + #region Methods + + [MethodImpl(MethodImplOptions.NoInlining)] + public static string MakeUnique(string id) => MakeUnique(Assembly.GetCallingAssembly(), id); + + internal static string MakeUnique(Assembly callingAssembly, string id) + { + if (!_idMappings.TryGetValue(callingAssembly, out Dictionary? idMapping)) + { + _idMappings.Add(callingAssembly, idMapping = new Dictionary()); + _counter.Add(callingAssembly, new Dictionary()); + } + + Dictionary counterMapping = _counter[callingAssembly]; + + if (!idMapping.TryGetValue(id, out string? mappedId)) + { + mappedId = id; + int mappingCounter = 1; + while (_registeredIds.Contains(id)) + mappedId = $"{id} ({++mappingCounter})"; + + _registeredIds.Add(mappedId); + idMapping.Add(id, mappedId); + } + + if (!counterMapping.ContainsKey(mappedId)) + counterMapping.Add(mappedId, 0); + + int counter = ++counterMapping[mappedId]; + return counter <= 1 ? mappedId : $"{mappedId} ({counter})"; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ResetCounter() => ResetCounter(Assembly.GetCallingAssembly()); + + internal static void ResetCounter(Assembly callingAssembly) + { + if (_counter.TryGetValue(callingAssembly, out Dictionary? counter)) + counter.Clear(); + } + + #endregion + } +} diff --git a/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings b/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings index 59c26c0..9d38d8a 100644 --- a/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings +++ b/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings @@ -14,6 +14,7 @@ True True True + True True True True diff --git a/RGB.NET.Core/RGBSurface.cs b/RGB.NET.Core/RGBSurface.cs index 692e158..033d33e 100644 --- a/RGB.NET.Core/RGBSurface.cs +++ b/RGB.NET.Core/RGBSurface.cs @@ -265,7 +265,8 @@ namespace RGB.NET.Core { lock (_devices) { - if (device.Surface != null) throw new RGBSurfaceException($"The device '{device.DeviceInfo.Manufacturer} {device.DeviceInfo.Model}' is already attached to a surface."); + if (string.IsNullOrWhiteSpace(device.DeviceInfo.DeviceName)) throw new RGBDeviceException($"The device '{device.DeviceInfo.Manufacturer} {device.DeviceInfo.Model}' has no valid name."); + if (device.Surface != null) throw new RGBSurfaceException($"The device '{device.DeviceInfo.DeviceName}' is already attached to a surface."); device.Surface = this; device.BoundaryChanged += DeviceOnBoundaryChanged; @@ -279,7 +280,7 @@ namespace RGB.NET.Core { lock (_devices) { - if (!_devices.Contains(device)) throw new RGBSurfaceException($"The device '{device.DeviceInfo.Manufacturer} {device.DeviceInfo.Model}' is not attached to this surface."); + if (!_devices.Contains(device)) throw new RGBSurfaceException($"The device '{device.DeviceInfo.DeviceName}' is not attached to this surface."); device.BoundaryChanged -= DeviceOnBoundaryChanged; device.Surface = null; diff --git a/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs b/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs index 6456fe4..03dcdcd 100644 --- a/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs +++ b/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs @@ -99,12 +99,11 @@ namespace RGB.NET.Devices.Corsair for (int i = 0; i < deviceCount; i++) { _CorsairDeviceInfo nativeDeviceInfo = (_CorsairDeviceInfo)Marshal.PtrToStructure(_CUESDK.CorsairGetDeviceInfo(i), typeof(_CorsairDeviceInfo))!; - CorsairRGBDeviceInfo info = new(i, RGBDeviceType.Unknown, nativeDeviceInfo); - if (!info.CapsMask.HasFlag(CorsairDeviceCaps.Lighting)) + if (!((CorsairDeviceCaps)nativeDeviceInfo.capsMask).HasFlag(CorsairDeviceCaps.Lighting)) continue; // Everything that doesn't support lighting control is useless - CorsairDeviceUpdateQueue updateQueue = new(GetUpdateTrigger(), info.CorsairDeviceIndex); - switch (info.CorsairDeviceType) + CorsairDeviceUpdateQueue updateQueue = new(GetUpdateTrigger(), i); + switch (nativeDeviceInfo.type) { case CorsairDeviceType.Keyboard: yield return new CorsairKeyboardRGBDevice(new CorsairKeyboardRGBDeviceInfo(i, nativeDeviceInfo), updateQueue); @@ -149,7 +148,7 @@ namespace RGB.NET.Devices.Corsair for (int channel = 0; channel < channelsInfo.channelsCount; channel++) { - CorsairLedId referenceLed = GetChannelReferenceId(info.CorsairDeviceType, channel); + CorsairLedId referenceLed = GetChannelReferenceId(nativeDeviceInfo.type, channel); if (referenceLed == CorsairLedId.Invalid) continue; _CorsairChannelInfo channelInfo = (_CorsairChannelInfo)Marshal.PtrToStructure(channelInfoPtr, typeof(_CorsairChannelInfo))!; @@ -161,7 +160,7 @@ namespace RGB.NET.Devices.Corsair { _CorsairChannelDeviceInfo channelDeviceInfo = (_CorsairChannelDeviceInfo)Marshal.PtrToStructure(channelDeviceInfoPtr, typeof(_CorsairChannelDeviceInfo))!; - yield return new CorsairCustomRGBDevice(new CorsairCustomRGBDeviceInfo(info, nativeDeviceInfo, channelDeviceInfo, referenceLed, ledOffset), updateQueue); + yield return new CorsairCustomRGBDevice(new CorsairCustomRGBDeviceInfo(i, nativeDeviceInfo, channelDeviceInfo, referenceLed, ledOffset), updateQueue); referenceLed += channelDeviceInfo.deviceLedCount; ledOffset += channelDeviceInfo.deviceLedCount; diff --git a/RGB.NET.Devices.Corsair/Custom/CorsairCustomRGBDeviceInfo.cs b/RGB.NET.Devices.Corsair/Custom/CorsairCustomRGBDeviceInfo.cs index 3a5eb31..d6e222d 100644 --- a/RGB.NET.Devices.Corsair/Custom/CorsairCustomRGBDeviceInfo.cs +++ b/RGB.NET.Devices.Corsair/Custom/CorsairCustomRGBDeviceInfo.cs @@ -2,6 +2,8 @@ // ReSharper disable UnusedMember.Global using System; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using RGB.NET.Core; using RGB.NET.Devices.Corsair.Native; @@ -28,12 +30,13 @@ namespace RGB.NET.Devices.Corsair /// /// Internal constructor of managed . /// - /// The info describing the the . + /// The index of the . /// The native -struct /// The native representing this device. /// The id of the first led of this device. - internal CorsairCustomRGBDeviceInfo(CorsairRGBDeviceInfo info, _CorsairDeviceInfo nativeInfo, _CorsairChannelDeviceInfo channelDeviceInfo, CorsairLedId referenceCorsairLed, int ledOffset) - : base(info.CorsairDeviceIndex, GetDeviceType(channelDeviceInfo.type), nativeInfo, GetModelName(info, channelDeviceInfo)) + internal CorsairCustomRGBDeviceInfo(int deviceIndex, _CorsairDeviceInfo nativeInfo, _CorsairChannelDeviceInfo channelDeviceInfo, CorsairLedId referenceCorsairLed, int ledOffset) + : base(deviceIndex, GetDeviceType(channelDeviceInfo.type), nativeInfo, + GetModelName(nativeInfo.model == IntPtr.Zero ? string.Empty : Regex.Replace(Marshal.PtrToStringAnsi(nativeInfo.model) ?? string.Empty, " ?DEMO", string.Empty, RegexOptions.IgnoreCase), channelDeviceInfo)) { this.ReferenceCorsairLed = referenceCorsairLed; this.LedOffset = ledOffset; @@ -73,7 +76,7 @@ namespace RGB.NET.Devices.Corsair } } - private static string GetModelName(IRGBDeviceInfo info, _CorsairChannelDeviceInfo channelDeviceInfo) + private static string GetModelName(string model, _CorsairChannelDeviceInfo channelDeviceInfo) { switch (channelDeviceInfo.type) { @@ -100,14 +103,14 @@ namespace RGB.NET.Devices.Corsair case CorsairChannelDeviceType.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 ((info.Model == "LS100 Starter Kit") && (channelDeviceInfo.deviceLedCount == 138)) + if ((model == "LS100 Starter Kit") && (channelDeviceInfo.deviceLedCount == 138)) return "LS100 LED Strip (dual monitor)"; - else if ((info.Model == "LS100 Starter Kit") && (channelDeviceInfo.deviceLedCount == 84)) + else if ((model == "LS100 Starter Kit") && (channelDeviceInfo.deviceLedCount == 84)) return "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 ((info.Model == "LS100 Starter Kit") && (channelDeviceInfo.deviceLedCount == 15)) + else if ((model == "LS100 Starter Kit") && (channelDeviceInfo.deviceLedCount == 15)) return "LS100 LED Strip (short)"; - else if ((info.Model == "LS100 Starter Kit") && (channelDeviceInfo.deviceLedCount == 27)) + else if ((model == "LS100 Starter Kit") && (channelDeviceInfo.deviceLedCount == 27)) return "LS100 LED Strip (long)"; // Device model is "Commander Pro" for regular LED strips else