diff --git a/RGB.NET.Devices.Roccat/Native/_ROCCATSDK.cs b/RGB.NET.Devices.Roccat/Native/_ROCCATSDK.cs new file mode 100644 index 0000000..aa59118 --- /dev/null +++ b/RGB.NET.Devices.Roccat/Native/_ROCCATSDK.cs @@ -0,0 +1,163 @@ +// 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.Roccat.Native +{ + // ReSharper disable once InconsistentNaming + internal static class _RoccatSDK + { + #region Libary Management + + private static IntPtr _dllHandle = IntPtr.Zero; + + /// + /// Gets the loaded architecture (x64/x86). + /// + internal static string LoadedArchitecture { get; private set; } + + /// + /// Reloads the SDK. + /// + internal static void Reload() + { + UnloadCUESDK(); + LoadCUESDK(); + } + + private static void LoadCUESDK() + { + if (_dllHandle != IntPtr.Zero) return; + + // HACK: Load library at runtime to support both, x86 and x64 with one managed dll + List possiblePathList = Environment.Is64BitProcess ? RoccatDeviceProvider.PossibleX64NativePaths : RoccatDeviceProvider.PossibleX86NativePaths; + 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))}'"); + + _dllHandle = LoadLibrary(dllPath); + + _initSDKPointer = (InitSDKPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "InitSDK"), typeof(InitSDKPointer)); + _unloadSDKPointer = (UnloadSDKPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "UnloadSDK"), typeof(UnloadSDKPointer)); + _initRyosTalkPointer = (InitRyosTalkPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "init_ryos_talk"), typeof(InitRyosTalkPointer)); + _restoreLedRGBPointer = (RestoreLedRGBPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "RestoreLEDRGB"), typeof(RestoreLedRGBPointer)); + _setRyosKbSDKModePointer = (SetRyosKbSDKModePointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "set_ryos_kb_SDKmode"), typeof(SetRyosKbSDKModePointer)); + _turnOffAllLedsPointer = (TurnOffAllLedsPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "turn_off_all_LEDS"), typeof(TurnOffAllLedsPointer)); + _turnOnAllLedsPointer = (TurnOnAllLedsPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "turn_on_all_LEDS"), typeof(TurnOnAllLedsPointer)); + _setLedOnPointer = (SetLedOnPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "set_LED_on"), typeof(SetLedOnPointer)); + _setLedOffPointer = (SetLedOffPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "set_LED_off"), typeof(SetLedOffPointer)); + _setAllLedsPointer = (SetAllLedsPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "Set_all_LEDS"), typeof(SetAllLedsPointer)); + _allKeyblinkingPointer = (AllKeyblinkingPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "All_Key_Blinking"), typeof(AllKeyblinkingPointer)); + _setLedRGBPointer = (SetLedRGBPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "Set_LED_RGB"), typeof(SetLedRGBPointer)); + _setAllLedSfxPointer = (SetAllLedSfxPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "Set_all_LEDSFX"), typeof(SetAllLedSfxPointer)); + } + + private static void UnloadCUESDK() + { + if (_dllHandle == IntPtr.Zero) return; + + // ReSharper disable once EmptyEmbeddedStatement - DarthAffe 20.02.2016: We might need to reduce the internal reference counter more than once to set the library free + while (FreeLibrary(_dllHandle)) ; + _dllHandle = IntPtr.Zero; + } + + [DllImport("kernel32.dll")] + private static extern IntPtr LoadLibrary(string dllToLoad); + + [DllImport("kernel32.dll")] + private static extern bool FreeLibrary(IntPtr dllHandle); + + [DllImport("kernel32.dll")] + private static extern IntPtr GetProcAddress(IntPtr dllHandle, string name); + + #endregion + + #region SDK-METHODS + + #region Pointers + + private static InitSDKPointer _initSDKPointer; + private static UnloadSDKPointer _unloadSDKPointer; + private static InitRyosTalkPointer _initRyosTalkPointer; + private static RestoreLedRGBPointer _restoreLedRGBPointer; + private static SetRyosKbSDKModePointer _setRyosKbSDKModePointer; + private static TurnOffAllLedsPointer _turnOffAllLedsPointer; + private static TurnOnAllLedsPointer _turnOnAllLedsPointer; + private static SetLedOnPointer _setLedOnPointer; + private static SetLedOffPointer _setLedOffPointer; + private static SetAllLedsPointer _setAllLedsPointer; + private static AllKeyblinkingPointer _allKeyblinkingPointer; + private static SetLedRGBPointer _setLedRGBPointer; + private static SetAllLedSfxPointer _setAllLedSfxPointer; + + #endregion + + #region Delegates + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate IntPtr InitSDKPointer(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void UnloadSDKPointer(IntPtr handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate bool InitRyosTalkPointer(IntPtr handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void RestoreLedRGBPointer(IntPtr handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate bool SetRyosKbSDKModePointer(IntPtr handle, bool state); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void TurnOffAllLedsPointer(IntPtr handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void TurnOnAllLedsPointer(IntPtr handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void SetLedOnPointer(IntPtr handle, byte position); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void SetLedOffPointer(IntPtr handle, byte position); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void SetAllLedsPointer(IntPtr handle, byte led, byte country); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void AllKeyblinkingPointer(IntPtr handle, int delayTime, int loopTime); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void SetLedRGBPointer(IntPtr handle, byte zone, byte effect, byte speed, byte r, byte g, byte b); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void SetAllLedSfxPointer(IntPtr handle, byte ledOnOff, byte r, byte g, byte b, byte layout); + + #endregion + + // ReSharper disable EventExceptionNotDocumented + + internal static IntPtr InitSDK() => _initSDKPointer(); + internal static void UnloadSDK(IntPtr handle) => _unloadSDKPointer(handle); + internal static bool InitRyosTalk(IntPtr handle) => _initRyosTalkPointer(handle); + internal static void RestoreLedRGB(IntPtr handle) => _restoreLedRGBPointer(handle); + internal static bool SetRyosKbSDKMode(IntPtr handle, bool state) => _setRyosKbSDKModePointer(handle, state); + internal static void TurnOffAllLeds(IntPtr handle) => _turnOffAllLedsPointer(handle); + internal static void TurnOnAllLeds(IntPtr handle) => _turnOnAllLedsPointer(handle); + internal static void SetLedOn(IntPtr handle, byte position) => _setLedOnPointer(handle, position); + internal static void SetLedOff(IntPtr handle, byte position) => _setLedOffPointer(handle, position); + internal static void SetAllLeds(IntPtr handle, byte led, byte country) => _setAllLedsPointer(handle, led, country); + internal static void AllKeyblinking(IntPtr handle, int delayTime, int loopTime) => _allKeyblinkingPointer(handle, delayTime, loopTime); + internal static void SetLedRGB(IntPtr handle, byte zone, byte effect, byte speed, byte r, byte g, byte b) => _setLedRGBPointer(handle, zone, effect, speed, r, g, b); + internal static void SetAllLedSfx(IntPtr handle, byte ledOnOff, byte r, byte g, byte b, byte layout) => _setAllLedSfxPointer(handle, ledOnOff, r, g, b, layout); + + // ReSharper restore EventExceptionNotDocumented + + #endregion + } +} diff --git a/RGB.NET.Devices.Roccat/Properties/AssemblyInfo.cs b/RGB.NET.Devices.Roccat/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..adf4d9a --- /dev/null +++ b/RGB.NET.Devices.Roccat/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RGB.NET.Devices.Roccat")] +[assembly: AssemblyDescription("Roccat-Device-Implementations of RGB.NET")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Wyrez")] +[assembly: AssemblyProduct("RGB.NET.Devices.Roccat")] +[assembly: AssemblyCopyright("Copyright © Wyrez 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("01803e4a-36b8-4675-afb3-7c8968ac4a13")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RGB.NET.Devices.Roccat/RGB.NET.Devices.Roccat.csproj b/RGB.NET.Devices.Roccat/RGB.NET.Devices.Roccat.csproj new file mode 100644 index 0000000..33aaed8 --- /dev/null +++ b/RGB.NET.Devices.Roccat/RGB.NET.Devices.Roccat.csproj @@ -0,0 +1,66 @@ + + + + + Debug + AnyCPU + {01803E4A-36B8-4675-AFB3-7C8968AC4A13} + Library + Properties + RGB.NET.Devices.Roccat + RGB.NET.Devices.Roccat + v4.5 + 512 + + + true + full + false + ..\bin\ + DEBUG;TRACE + prompt + 4 + ..\bin\RGB.NET.Devices.Roccat.xml + + + pdbonly + true + ..\bin\ + TRACE + prompt + 4 + ..\bin\RGB.NET.Devices.Roccat.xml + + + + + + ..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll + + + + + + + + + + + + + + + + + + + {5a4f9a75-75fe-47cd-90e5-914d5b20d232} + RGB.NET.Core + + + + + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.Roccat/RoccatDeviceProvider.cs b/RGB.NET.Devices.Roccat/RoccatDeviceProvider.cs new file mode 100644 index 0000000..391a0da --- /dev/null +++ b/RGB.NET.Devices.Roccat/RoccatDeviceProvider.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using RGB.NET.Core; +using RGB.NET.Devices.Roccat.Native; + +namespace RGB.NET.Devices.Roccat +{ + /// + /// + /// Represents a device provider responsible for roccat (TalkFX)devices. + /// + public class RoccatDeviceProvider : IRGBDeviceProvider + { + #region Properties & Fields + + private static RoccatDeviceProvider _instance; + /// + /// Gets the singleton instance. + /// + public static RoccatDeviceProvider Instance => _instance ?? new RoccatDeviceProvider(); + + /// + /// 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; } = new List { "x86/RoccatTalkSDKWrapper.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; } = new List { "x64/RoccatTalkSDKWrapper.dll" }; + + /// + /// + /// Indicates if the SDK is initialized and ready to use. + /// + public bool IsInitialized { get; private set; } + + /// + /// Gets the loaded architecture (x64/x86). + /// + public string LoadedArchitecture => _RoccatSDK.LoadedArchitecture; + + /// + /// + /// Gets whether the application has exclusive access to the SDK or not. + /// + public bool HasExclusiveAccess { get; private set; } + + /// + public IEnumerable Devices { get; private set; } + + private IntPtr _sdkHandle; + + #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 RoccatDeviceProvider() + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(RoccatDeviceProvider)}"); + _instance = this; + } + + #endregion + + #region Methods + + /// + /// Thrown if the SDK is already initialized or if the SDK is not compatible to CUE. + public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool exclusiveAccessIfPossible = false, bool throwExceptions = false) + { + IsInitialized = false; + + try + { + _sdkHandle = _RoccatSDK.InitSDK(); + + IList devices = new List(); + + Devices = new ReadOnlyCollection(devices); + IsInitialized = true; + } + catch + { + if (throwExceptions) throw; + return false; + } + + return true; + } + + /// + public void ResetDevices() + { } + + /// + public void Dispose() + { + if (_sdkHandle != IntPtr.Zero) + { + try { _RoccatSDK.RestoreLedRGB(_sdkHandle); } + catch { /* We tried our best */} + + try { _RoccatSDK.SetRyosKbSDKMode(_sdkHandle, false); } + catch { /* We tried our best */} + + try { _RoccatSDK.UnloadSDK(_sdkHandle); } + catch { /* We tried our best */} + } + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Roccat/packages.config b/RGB.NET.Devices.Roccat/packages.config new file mode 100644 index 0000000..a59695c --- /dev/null +++ b/RGB.NET.Devices.Roccat/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/RGB.NET.sln b/RGB.NET.sln index 2231348..19fb685 100644 --- a/RGB.NET.sln +++ b/RGB.NET.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2010 +VisualStudioVersion = 15.0.27130.2024 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RGB.NET.Core", "RGB.NET.Core\RGB.NET.Core.csproj", "{5A4F9A75-75FE-47CD-90E5-914D5B20D232}" EndProject @@ -68,6 +68,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RGB.NET.Devices.Razer", "RG EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RGB.NET.Devices.Debug", "RGB.NET.Devices.Debug\RGB.NET.Devices.Debug.csproj", "{D013040E-1931-4F0D-9CCA-0F4AE74A507E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RGB.NET.Devices.Roccat", "RGB.NET.Devices.Roccat\RGB.NET.Devices.Roccat.csproj", "{01803E4A-36B8-4675-AFB3-7C8968AC4A13}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -130,6 +132,10 @@ Global {D013040E-1931-4F0D-9CCA-0F4AE74A507E}.Debug|Any CPU.Build.0 = Debug|Any CPU {D013040E-1931-4F0D-9CCA-0F4AE74A507E}.Release|Any CPU.ActiveCfg = Release|Any CPU {D013040E-1931-4F0D-9CCA-0F4AE74A507E}.Release|Any CPU.Build.0 = Release|Any CPU + {01803E4A-36B8-4675-AFB3-7C8968AC4A13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01803E4A-36B8-4675-AFB3-7C8968AC4A13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01803E4A-36B8-4675-AFB3-7C8968AC4A13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01803E4A-36B8-4675-AFB3-7C8968AC4A13}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -149,6 +155,7 @@ Global {C191D5BE-E1B2-47E4-AB39-D954B277188C} = {06416566-481F-4571-80EE-7BB05B1E0299} {24FF4ACB-D679-4B2D-86D4-50AB6C02D816} = {33D5E279-1C4E-4AB6-9D1E-6D18109A6C25} {D013040E-1931-4F0D-9CCA-0F4AE74A507E} = {33D5E279-1C4E-4AB6-9D1E-6D18109A6C25} + {01803E4A-36B8-4675-AFB3-7C8968AC4A13} = {33D5E279-1C4E-4AB6-9D1E-6D18109A6C25} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CDECA6C7-8D18-4AF3-94F7-C70A69B8571B}