using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; using RGB.NET.Core; using RGB.NET.Devices.Wooting.Enum; using RGB.NET.Devices.Wooting.Generic; using RGB.NET.Devices.Wooting.Keyboard; using RGB.NET.Devices.Wooting.Keypad; using RGB.NET.Devices.Wooting.Native; namespace RGB.NET.Devices.Wooting; /// /// /// Represents a device provider responsible for Wooting devices. /// public sealed class WootingDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields // ReSharper disable once InconsistentNaming private static readonly Lock _lock = new(); private static WootingDeviceProvider? _instance; /// /// Gets the singleton instance. /// public static WootingDeviceProvider Instance { get { lock (_lock) return _instance ?? new WootingDeviceProvider(); } } /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x86 windows applications. /// The first match will be used. /// public static List PossibleX86NativePathsWindows { get; } = ["x86/wooting-rgb-sdk.dll"]; /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x64 windows applications. /// The first match will be used. /// public static List PossibleX64NativePathsWindows { get; } = ["x64/wooting-rgb-sdk64.dll"]; /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x64 linux applications. /// The first match will be used. /// public static List PossibleNativePathsLinux { get; } = ["x64/libwooting-rgb-sdk.so"]; /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x64 MacOS applications. /// The first match will be used. /// // ReSharper disable once InconsistentNaming public static List PossibleNativePathsMacOS { get; } = ["x64/libwooting-rgb-sdk.dylib"]; #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 WootingDeviceProvider() { lock (_lock) { if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(WootingDeviceProvider)}"); _instance = this; } } #endregion #region Methods /// protected override void InitializeSDK() { lock (_WootingSDK.SdkLock) { _WootingSDK.Reload(); } } /// protected override IEnumerable LoadDevices() { lock (_WootingSDK.SdkLock) { if (_WootingSDK.KeyboardConnected()) { for (byte i = 0; i < _WootingSDK.GetDeviceCount(); i++) { WootingNativeUpdateQueue updateQueue = new(GetUpdateTrigger(), i); _WootingSDK.SelectDevice(i); _WootingDeviceInfo nativeDeviceInfo = (_WootingDeviceInfo)Marshal.PtrToStructure(_WootingSDK.GetDeviceInfo(), typeof(_WootingDeviceInfo))!; //Uwu non-rgb returns zero here. if (nativeDeviceInfo.MaxLedIndex == 0) continue; KeyboardLayoutType layoutType = nativeDeviceInfo.LayoutType switch { WootingLayoutType.Unknown => KeyboardLayoutType.Unknown, WootingLayoutType.ANSI => KeyboardLayoutType.ANSI, WootingLayoutType.ISO => KeyboardLayoutType.ISO, WootingLayoutType.JIS => KeyboardLayoutType.JIS, WootingLayoutType.ANSI_SPLIT_SPACEBAR => KeyboardLayoutType.ANSI, WootingLayoutType.ISO_SPLIT_SPACEBAR => KeyboardLayoutType.ISO, _ => throw new ArgumentOutOfRangeException() }; //Note: we cannot change *any* of these here or the local database Artemis has will no longer match up and everything breaks. string model = nativeDeviceInfo.Model; string name = DeviceHelper.CreateDeviceName("Wooting", model); yield return nativeDeviceInfo.DeviceType switch { WootingDeviceType.Keypad3Keys => new WootingKeypadRGBDevice(nativeDeviceInfo.DeviceType, new(model, name), updateQueue), _ => new WootingKeyboardRGBDevice(nativeDeviceInfo.DeviceType, new(layoutType, model, name), updateQueue) }; } } } } /// protected override void Dispose(bool disposing) { lock (_lock) { base.Dispose(disposing); lock (_WootingSDK.SdkLock) { try { _WootingSDK.UnloadWootingSDK(); } catch { /* at least we tried */ } } _instance = null; } } #endregion }