#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
}