#pragma warning disable IDE1006 // Naming Styles // ReSharper disable UnusedMethodReturnValue.Global // ReSharper disable UnusedMember.Global using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using RGB.NET.Core; namespace RGB.NET.Devices.CorsairLegacy.Native; // ReSharper disable once InconsistentNaming internal static class _CUESDK { #region Libary Management private static nint _handle = 0; /// /// Reloads the SDK. /// internal static void Reload() { UnloadCUESDK(); LoadCUESDK(); } private static void LoadCUESDK() { if (_handle != 0) return; List possiblePathList = GetPossibleLibraryPaths().ToList(); string? dllPath = possiblePathList.FirstOrDefault(File.Exists); if (dllPath == null) throw new RGBDeviceException($"Can't find the CUE-SDK at one of the expected locations:\r\n '{string.Join("\r\n", possiblePathList.Select(Path.GetFullPath))}'"); if (!NativeLibrary.TryLoad(dllPath, out _handle)) #if NET6_0 throw new RGBDeviceException($"Corsair LoadLibrary failed with error code {Marshal.GetLastPInvokeError()}"); #else throw new RGBDeviceException($"Corsair LoadLibrary failed with error code {Marshal.GetLastWin32Error()}"); #endif if (!NativeLibrary.TryGetExport(_handle, "CorsairSetLedsColorsBufferByDeviceIndex", out _corsairSetLedsColorsBufferByDeviceIndexPointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairSetLedsColorsBufferByDeviceIndex'"); if (!NativeLibrary.TryGetExport(_handle, "CorsairSetLedsColorsFlushBuffer", out _corsairSetLedsColorsFlushBufferPointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairSetLedsColorsFlushBuffer'"); if (!NativeLibrary.TryGetExport(_handle, "CorsairGetLedsColorsByDeviceIndex", out _corsairGetLedsColorsByDeviceIndexPointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairGetLedsColorsByDeviceIndex'"); if (!NativeLibrary.TryGetExport(_handle, "CorsairSetLayerPriority", out _corsairSetLayerPriorityPointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairSetLayerPriority'"); if (!NativeLibrary.TryGetExport(_handle, "CorsairGetDeviceCount", out _corsairGetDeviceCountPointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairGetDeviceCount'"); if (!NativeLibrary.TryGetExport(_handle, "CorsairGetDeviceInfo", out _corsairGetDeviceInfoPointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairGetDeviceInfo'"); if (!NativeLibrary.TryGetExport(_handle, "CorsairGetLedIdForKeyName", out _corsairGetLedIdForKeyNamePointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairGetLedIdForKeyName'"); if (!NativeLibrary.TryGetExport(_handle, "CorsairGetLedPositionsByDeviceIndex", out _corsairGetLedPositionsByDeviceIndexPointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairGetLedPositionsByDeviceIndex'"); if (!NativeLibrary.TryGetExport(_handle, "CorsairRequestControl", out _corsairRequestControlPointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairRequestControl'"); if (!NativeLibrary.TryGetExport(_handle, "CorsairReleaseControl", out _corsairReleaseControlPointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairReleaseControl'"); if (!NativeLibrary.TryGetExport(_handle, "CorsairPerformProtocolHandshake", out _corsairPerformProtocolHandshakePointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairPerformProtocolHandshake'"); if (!NativeLibrary.TryGetExport(_handle, "CorsairGetLastError", out _corsairGetLastErrorPointer)) throw new RGBDeviceException("Failed to load Corsair function 'CorsairGetLastError'"); } private static IEnumerable GetPossibleLibraryPaths() { IEnumerable possibleLibraryPaths; if (OperatingSystem.IsWindows()) possibleLibraryPaths = Environment.Is64BitProcess ? CorsairLegacyDeviceProvider.PossibleX64NativePaths : CorsairLegacyDeviceProvider.PossibleX86NativePaths; else possibleLibraryPaths = []; return possibleLibraryPaths.Select(Environment.ExpandEnvironmentVariables); } internal static void UnloadCUESDK() { if (_handle == 0) return; _corsairSetLedsColorsBufferByDeviceIndexPointer = 0; _corsairSetLedsColorsFlushBufferPointer = 0; _corsairGetLedsColorsByDeviceIndexPointer = 0; _corsairSetLayerPriorityPointer = 0; _corsairGetDeviceCountPointer = 0; _corsairGetDeviceInfoPointer = 0; _corsairGetLedIdForKeyNamePointer = 0; _corsairGetLedPositionsByDeviceIndexPointer = 0; _corsairRequestControlPointer = 0; _corsairReleaseControlPointer = 0; _corsairPerformProtocolHandshakePointer = 0; _corsairGetLastErrorPointer = 0; NativeLibrary.Free(_handle); _handle = 0; } #endregion #region SDK-METHODS #region Pointers private static nint _corsairSetLedsColorsBufferByDeviceIndexPointer; private static nint _corsairSetLedsColorsFlushBufferPointer; private static nint _corsairGetLedsColorsByDeviceIndexPointer; private static nint _corsairSetLayerPriorityPointer; private static nint _corsairGetDeviceCountPointer; private static nint _corsairGetDeviceInfoPointer; private static nint _corsairGetLedIdForKeyNamePointer; private static nint _corsairGetLedPositionsByDeviceIndexPointer; private static nint _corsairRequestControlPointer; private static nint _corsairReleaseControlPointer; private static nint _corsairPerformProtocolHandshakePointer; private static nint _corsairGetLastErrorPointer; #endregion /// /// CUE-SDK: set specified LEDs to some colors. /// This function set LEDs colors in the buffer which is written to the devices via CorsairSetLedsColorsFlushBuffer or CorsairSetLedsColorsFlushBufferAsync. /// Typical usecase is next: CorsairSetLedsColorsFlushBuffer or CorsairSetLedsColorsFlushBufferAsync is called to write LEDs colors to the device /// and follows after one or more calls of CorsairSetLedsColorsBufferByDeviceIndex to set the LEDs buffer. /// This function does not take logical layout into account. /// internal static unsafe bool CorsairSetLedsColorsBufferByDeviceIndex(int deviceIndex, int size, nint ledsColors) => ((delegate* unmanaged[Cdecl])ThrowIfZero(_corsairSetLedsColorsBufferByDeviceIndexPointer))(deviceIndex, size, ledsColors); /// /// CUE-SDK: writes to the devices LEDs colors buffer which is previously filled by the CorsairSetLedsColorsBufferByDeviceIndex function. /// This function executes synchronously, if you are concerned about delays consider using CorsairSetLedsColorsFlushBufferAsync /// internal static unsafe bool CorsairSetLedsColorsFlushBuffer() => ((delegate* unmanaged[Cdecl])ThrowIfZero(_corsairSetLedsColorsFlushBufferPointer))(); /// /// CUE-SDK: get current color for the list of requested LEDs. /// The color should represent the actual state of the hardware LED, which could be a combination of SDK and/or CUE input. /// This function works for keyboard, mouse, mousemat, headset, headset stand and DIY-devices. /// internal static unsafe bool CorsairGetLedsColorsByDeviceIndex(int deviceIndex, int size, nint ledsColors) => ((delegate* unmanaged[Cdecl])ThrowIfZero(_corsairGetLedsColorsByDeviceIndexPointer))(deviceIndex, size, ledsColors); /// /// CUE-SDK: set layer priority for this shared client. /// By default CUE has priority of 127 and all shared clients have priority of 128 if they don’t call this function. /// Layers with higher priority value are shown on top of layers with lower priority. /// internal static unsafe bool CorsairSetLayerPriority(int priority) => ((delegate* unmanaged[Cdecl])ThrowIfZero(_corsairSetLayerPriorityPointer))(priority); /// /// CUE-SDK: returns number of connected Corsair devices that support lighting control. /// internal static unsafe int CorsairGetDeviceCount() => ((delegate* unmanaged[Cdecl])ThrowIfZero(_corsairGetDeviceCountPointer))(); /// /// CUE-SDK: returns information about device at provided index. /// internal static unsafe nint CorsairGetDeviceInfo(int deviceIndex) => ((delegate* unmanaged[Cdecl])ThrowIfZero(_corsairGetDeviceInfoPointer))(deviceIndex); /// /// CUE-SDK: provides list of keyboard or mousepad LEDs with their physical positions. /// internal static unsafe nint CorsairGetLedPositionsByDeviceIndex(int deviceIndex) => ((delegate* unmanaged[Cdecl])ThrowIfZero(_corsairGetLedPositionsByDeviceIndexPointer))(deviceIndex); /// /// CUE-SDK: retrieves led id for key name taking logical layout into account. /// internal static unsafe CorsairLedId CorsairGetLedIdForKeyName(char keyName) => ((delegate* unmanaged[Cdecl])ThrowIfZero(_corsairGetLedIdForKeyNamePointer))(keyName); /// /// CUE-SDK: requestes control using specified access mode. /// By default client has shared control over lighting so there is no need to call CorsairRequestControl unless client requires exclusive control. /// internal static unsafe bool CorsairRequestControl(CorsairAccessMode accessMode) => ((delegate* unmanaged[Cdecl])ThrowIfZero(_corsairRequestControlPointer))(accessMode); /// /// CUE-SDK: releases previously requested control for specified access mode. /// internal static unsafe bool CorsairReleaseControl(CorsairAccessMode accessMode) => ((delegate* unmanaged[Cdecl])ThrowIfZero(_corsairReleaseControlPointer))(accessMode); /// /// CUE-SDK: checks file and protocol version of CUE to understand which of SDK functions can be used with this version of CUE. /// internal static unsafe _CorsairProtocolDetails CorsairPerformProtocolHandshake() => ((delegate* unmanaged[Cdecl]<_CorsairProtocolDetails>)ThrowIfZero(_corsairPerformProtocolHandshakePointer))(); /// /// CUE-SDK: returns last error that occured while using any of Corsair* functions. /// internal static unsafe CorsairError CorsairGetLastError() => ((delegate* unmanaged[Cdecl])ThrowIfZero(_corsairGetLastErrorPointer))(); private static nint ThrowIfZero(nint ptr) { if (ptr == 0) throw new RGBDeviceException("The Corsair-SDK is not initialized."); return ptr; } #endregion }