// ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global using System; using System.Collections.Generic; using System.Threading; using RGB.NET.Core; using RGB.NET.Devices.Msi.Exceptions; using RGB.NET.Devices.Msi.Native; namespace RGB.NET.Devices.Msi; /// /// /// Represents a device provider responsible for MSI devices. /// public class MsiDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields // ReSharper disable once InconsistentNaming private static readonly Lock _lock = new(); private static MsiDeviceProvider? _instance; /// /// Gets the singleton instance. /// public static MsiDeviceProvider Instance { get { lock (_lock) return _instance ?? new MsiDeviceProvider(); } } /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x86 applications. /// The first match will be used. /// public static List PossibleX86NativePaths { get; } = ["x86/MysticLight_SDK.dll"]; /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x64 applications. /// The first match will be used. /// public static List PossibleX64NativePaths { get; } = ["x64/MysticLight_SDK.dll"]; #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 MsiDeviceProvider() { lock (_lock) { if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(MsiDeviceProvider)}"); _instance = this; } } #endregion #region Methods /// protected override void InitializeSDK() { _MsiSDK.Reload(); int errorCode; if ((errorCode = _MsiSDK.Initialize()) != 0) ThrowMsiError(errorCode, true); } /// protected override IEnumerable LoadDevices() { int errorCode; if ((errorCode = _MsiSDK.GetDeviceInfo(out string[] deviceTypes, out int[] ledCounts)) != 0) ThrowMsiError(errorCode, true); for (int i = 0; i < deviceTypes.Length; i++) { string deviceType = deviceTypes[i]; int ledCount = ledCounts[i]; if (deviceType.Equals("MSI_MB")) { //Hex3l: MSI_MB provide access to the motherboard "leds" where a led must be intended as a led header (JRGB, JRAINBOW etc..) (Tested on MSI X570 Unify) yield return new MsiMainboardRGBDevice(new MsiRGBDeviceInfo(RGBDeviceType.Mainboard, deviceType, "MSI", "Motherboard"), ledCount, GetUpdateTrigger()); } else if (deviceType.Equals("MSI_VGA")) { //Hex3l: Every led under MSI_VGA should be a different graphics card. Handling all the cards together seems a good way to avoid overlapping of leds //Hex3l: The led name is the name of the card (e.g. NVIDIA GeForce RTX 2080 Ti) we could provide it in device info. yield return new MsiGraphicsCardRGBDevice(new MsiRGBDeviceInfo(RGBDeviceType.GraphicsCard, deviceType, "MSI", "GraphicsCard"), ledCount, GetUpdateTrigger()); } else if (deviceType.Equals("MSI_MOUSE")) { //Hex3l: Every led under MSI_MOUSE should be a different mouse. Handling all the mouses together seems a good way to avoid overlapping of leds //Hex3l: The led name is the name of the mouse (e.g. msi CLUTCH GM11) we could provide it in device info. yield return new MsiMouseRGBDevice(new MsiRGBDeviceInfo(RGBDeviceType.Mouse, deviceType, "MSI", "Mouse"), ledCount, GetUpdateTrigger()); } } } private void ThrowMsiError(int errorCode, bool isCritical = false) => Throw(new MysticLightException(errorCode, _MsiSDK.GetErrorMessage(errorCode)), isCritical); /// protected override void Dispose(bool disposing) { lock (_lock) { base.Dispose(disposing); try { _MsiSDK.UnloadMsiSDK(); } catch { /* at least we tried */ } _instance = null; } } #endregion }