diff --git a/RGB.NET.Devices.Wooting/Enum/WootingDevicesIndexes.cs b/RGB.NET.Devices.Wooting/Enum/WootingDevicesIndexes.cs
new file mode 100644
index 0000000..c25afa4
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/Enum/WootingDevicesIndexes.cs
@@ -0,0 +1,21 @@
+// ReSharper disable InconsistentNaming
+// ReSharper disable UnusedMember.Global
+
+using System.ComponentModel;
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace RGB.NET.Devices.Wooting.Enum
+{
+ ///
+ /// Contains a list of available device-indexes.
+ ///
+ public enum WootingDevicesIndexes
+ {
+ [Description("Wooting One")]
+ WootingOne = 0,
+
+ [Description("Wooting Two")]
+ WootingTwo = 1
+ }
+}
diff --git a/RGB.NET.Devices.Wooting/Enum/WootingLogicalKeyboardLayout.cs b/RGB.NET.Devices.Wooting/Enum/WootingLogicalKeyboardLayout.cs
new file mode 100644
index 0000000..f21929d
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/Enum/WootingLogicalKeyboardLayout.cs
@@ -0,0 +1,16 @@
+// ReSharper disable InconsistentNaming
+// ReSharper disable UnusedMember.Global
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace RGB.NET.Devices.Wooting.Enum
+{
+ ///
+ /// Contains list of available logical layouts for cooler master keyboards.
+ ///
+ public enum WootingLogicalKeyboardLayout
+ {
+ US = 0,
+ EU = 1
+ };
+}
diff --git a/RGB.NET.Devices.Wooting/Enum/WootingPhysicalKeyboardLayout.cs b/RGB.NET.Devices.Wooting/Enum/WootingPhysicalKeyboardLayout.cs
new file mode 100644
index 0000000..2142d50
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/Enum/WootingPhysicalKeyboardLayout.cs
@@ -0,0 +1,16 @@
+// ReSharper disable InconsistentNaming
+// ReSharper disable UnusedMember.Global
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace RGB.NET.Devices.Wooting.Enum
+{
+ ///
+ /// Contains list of available physical layouts for Wooting keyboards.
+ ///
+ public enum WootingPhysicalKeyboardLayout
+ {
+ US = 0,
+ EU = 1
+ }
+}
diff --git a/RGB.NET.Devices.Wooting/Generic/IWootingRGBDevice.cs b/RGB.NET.Devices.Wooting/Generic/IWootingRGBDevice.cs
new file mode 100644
index 0000000..42c6995
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/Generic/IWootingRGBDevice.cs
@@ -0,0 +1,12 @@
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.Wooting.Generic
+{
+ ///
+ /// Represents a Wooting RGB-device.
+ ///
+ internal interface IWootingRGBDevice : IRGBDevice
+ {
+ void Initialize(UpdateQueue updateQueue);
+ }
+}
diff --git a/RGB.NET.Devices.Wooting/Generic/WootingRGBDevice.cs b/RGB.NET.Devices.Wooting/Generic/WootingRGBDevice.cs
new file mode 100644
index 0000000..6fd6af7
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/Generic/WootingRGBDevice.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using RGB.NET.Core;
+using RGB.NET.Devices.Wooting.Native;
+
+namespace RGB.NET.Devices.Wooting.Generic
+{
+ ///
+ ///
+ ///
+ /// Represents a Wooting-device
+ ///
+ public abstract class WootingRGBDevice : AbstractRGBDevice, IWootingRGBDevice
+ where TDeviceInfo : WootingRGBDeviceInfo
+ {
+ #region Properties & Fields
+
+ ///
+ ///
+ /// Gets information about the .
+ ///
+ public override TDeviceInfo DeviceInfo { get; }
+
+ ///
+ /// Gets or sets the update queue performing updates for this device.
+ ///
+ // ReSharper disable once MemberCanBePrivate.Global
+ protected UpdateQueue UpdateQueue { get; set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The generic information provided by Wooting for the device.
+ protected WootingRGBDevice(TDeviceInfo info)
+ {
+ this.DeviceInfo = info;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Initializes the device.
+ ///
+ public void Initialize(UpdateQueue updateQueue)
+ {
+ InitializeLayout();
+
+ if (Size == Size.Invalid)
+ {
+ Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle));
+ Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
+ }
+
+ UpdateQueue = updateQueue;
+ }
+
+ ///
+ /// Initializes the and of the device.
+ ///
+ protected abstract void InitializeLayout();
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Wooting/Generic/WootingRGBDeviceInfo.cs b/RGB.NET.Devices.Wooting/Generic/WootingRGBDeviceInfo.cs
new file mode 100644
index 0000000..bf91f8d
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/Generic/WootingRGBDeviceInfo.cs
@@ -0,0 +1,62 @@
+using System;
+using RGB.NET.Core;
+using RGB.NET.Devices.Wooting.Enum;
+using RGB.NET.Devices.Wooting.Helper;
+
+namespace RGB.NET.Devices.Wooting.Generic
+{
+ ///
+ ///
+ /// Represents a generic information for a Wooting-.
+ ///
+ public class WootingRGBDeviceInfo : IRGBDeviceInfo
+ {
+ #region Properties & Fields
+
+ ///
+ public RGBDeviceType DeviceType { get; }
+
+ ///
+ public string DeviceName { get; }
+
+ ///
+ public string Manufacturer => "Wooting";
+
+ ///
+ public string Model { get; }
+
+ ///
+ public Uri Image { get; set; }
+
+ ///
+ public RGBDeviceLighting Lighting => RGBDeviceLighting.Key;
+
+ ///
+ public bool SupportsSyncBack => false;
+
+ ///
+ /// Gets the of the .
+ ///
+ public WootingDevicesIndexes DeviceIndex { get; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Internal constructor of managed .
+ ///
+ /// The type of the .
+ /// The of the .
+ internal WootingRGBDeviceInfo(RGBDeviceType deviceType, WootingDevicesIndexes deviceIndex)
+ {
+ this.DeviceType = deviceType;
+ this.DeviceIndex = deviceIndex;
+
+ Model = deviceIndex.GetDescription();
+ DeviceName = $"{Manufacturer} {Model}";
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Wooting/Helper/EnumExtension.cs b/RGB.NET.Devices.Wooting/Helper/EnumExtension.cs
new file mode 100644
index 0000000..7c2e8b3
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/Helper/EnumExtension.cs
@@ -0,0 +1,41 @@
+using System;
+using System.ComponentModel;
+using System.Reflection;
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.Wooting.Helper
+{
+ ///
+ /// Offers some extensions and helper-methods for enum related things.
+ ///
+ internal static class EnumExtension
+ {
+ ///
+ /// Gets the value of the .
+ ///
+ /// The enum value to get the description from.
+ /// The generic enum-type
+ /// The value of the or the result of the source.
+ internal static string GetDescription(this T source)
+ where T : struct
+ {
+ return source.GetAttribute()?.Description ?? source.ToString();
+ }
+
+ ///
+ /// Gets the attribute of type T.
+ ///
+ /// The enum value to get the attribute from
+ /// The generic attribute type
+ /// The generic enum-type
+ /// The .
+ private static T GetAttribute(this TEnum source)
+ where T : Attribute
+ where TEnum : struct
+ {
+ FieldInfo fi = source.GetType().GetField(source.ToString());
+ T[] attributes = (T[])fi.GetCustomAttributes(typeof(T), false);
+ return attributes.Length > 0 ? attributes[0] : null;
+ }
+ }
+}
diff --git a/RGB.NET.Devices.Wooting/Keyboard/WootingKeyboardLedMappings.cs b/RGB.NET.Devices.Wooting/Keyboard/WootingKeyboardLedMappings.cs
new file mode 100644
index 0000000..ebc62d3
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/Keyboard/WootingKeyboardLedMappings.cs
@@ -0,0 +1,262 @@
+// ReSharper disable InconsistentNaming
+
+using System.Collections.Generic;
+using RGB.NET.Core;
+using RGB.NET.Devices.Wooting.Enum;
+
+namespace RGB.NET.Devices.Wooting.Keyboard
+{
+ ///
+ /// Contains all the hardware-id mappings for Wooting devices.
+ ///
+ internal static class WootingKeyboardLedMappings
+ {
+ #region Properties & Fields
+
+ #region Wooting One
+
+ private static readonly Dictionary WootingOne_US = new Dictionary
+ {
+ { LedId.Keyboard_Escape, (0,0) },
+ { LedId.Keyboard_F1, (0,2) },
+ { LedId.Keyboard_F2, (0,3) },
+ { LedId.Keyboard_F3, (0,4) },
+ { LedId.Keyboard_F4, (0,5) },
+ { LedId.Keyboard_F5, (0,6) },
+ { LedId.Keyboard_F6, (0,7) },
+ { LedId.Keyboard_F7, (0,8) },
+ { LedId.Keyboard_F8, (0,9) },
+ { LedId.Keyboard_F9, (0,10) },
+ { LedId.Keyboard_F10, (0,11) },
+ { LedId.Keyboard_F11, (0,12) },
+ { LedId.Keyboard_F12, (0,13) },
+ { LedId.Keyboard_PrintScreen, (0,14) },
+ { LedId.Keyboard_PauseBreak, (0,15) },
+ { LedId.Keyboard_Custom1, (0,20) }, // TODO: Make sure it is not 0,16
+
+ { LedId.Keyboard_GraveAccentAndTilde, (1,0) },
+ { LedId.Keyboard_1, (1,1) },
+ { LedId.Keyboard_2, (1,2) },
+ { LedId.Keyboard_3, (1,3) },
+ { LedId.Keyboard_4, (1,4) },
+ { LedId.Keyboard_5, (1,5) },
+ { LedId.Keyboard_6, (1,6) },
+ { LedId.Keyboard_7, (1,7) },
+ { LedId.Keyboard_8, (1,8) },
+ { LedId.Keyboard_9, (1,9) },
+ { LedId.Keyboard_0, (1,10) },
+ { LedId.Keyboard_MinusAndUnderscore, (1,11) },
+ { LedId.Keyboard_EqualsAndPlus, (1,12) },
+ { LedId.Keyboard_Backspace, (1,13) },
+ { LedId.Keyboard_Insert, (1,14) },
+ { LedId.Keyboard_Home, (1,15) },
+ { LedId.Keyboard_PageUp, (1,16) },
+
+ { LedId.Keyboard_Tab, (2,0) },
+ { LedId.Keyboard_Q, (2,1) },
+ { LedId.Keyboard_W, (2,2) },
+ { LedId.Keyboard_E, (2,3) },
+ { LedId.Keyboard_R, (2,4) },
+ { LedId.Keyboard_T, (2,5) },
+ { LedId.Keyboard_Y, (2,6) },
+ { LedId.Keyboard_U, (2,7) },
+ { LedId.Keyboard_I, (2,8) },
+ { LedId.Keyboard_O, (2,9) },
+ { LedId.Keyboard_P, (2,10) },
+ { LedId.Keyboard_BracketLeft, (2,11) },
+ { LedId.Keyboard_BracketRight, (2,12) },
+ { LedId.Keyboard_Backslash, (2,13) },
+ { LedId.Keyboard_Delete, (2,14) },
+ { LedId.Keyboard_End, (2,15) },
+ { LedId.Keyboard_PageDown, (2,16) },
+
+ { LedId.Keyboard_CapsLock, (3,0) },
+ { LedId.Keyboard_A, (3,1) },
+ { LedId.Keyboard_S, (3,2) },
+ { LedId.Keyboard_D, (3,3) },
+ { LedId.Keyboard_F, (3,4) },
+ { LedId.Keyboard_G, (3,5) },
+ { LedId.Keyboard_H, (3,6) },
+ { LedId.Keyboard_J, (3,7) },
+ { LedId.Keyboard_K, (3,8) },
+ { LedId.Keyboard_L, (3,9) },
+ { LedId.Keyboard_SemicolonAndColon, (3,10) },
+ { LedId.Keyboard_ApostropheAndDoubleQuote, (3,11) },
+ { LedId.Keyboard_Enter, (3,13) },
+
+ { LedId.Keyboard_LeftShift, (4,0) },
+ { LedId.Keyboard_NonUsBackslash, (4,1) },
+ { LedId.Keyboard_Z, (4,2) },
+ { LedId.Keyboard_X, (4,3) },
+ { LedId.Keyboard_C, (4,4) },
+ { LedId.Keyboard_V, (4,5) },
+ { LedId.Keyboard_B, (4,6) },
+ { LedId.Keyboard_N, (4,7) },
+ { LedId.Keyboard_M, (4,8) },
+ { LedId.Keyboard_CommaAndLessThan, (4,9) },
+ { LedId.Keyboard_PeriodAndBiggerThan, (4,10) },
+ { LedId.Keyboard_SlashAndQuestionMark, (4,11) },
+ { LedId.Keyboard_RightShift, (4,13) },
+ { LedId.Keyboard_ArrowUp, (4,15) },
+
+ { LedId.Keyboard_LeftCtrl, (5,0) },
+ { LedId.Keyboard_LeftGui, (5,1) },
+ { LedId.Keyboard_LeftAlt, (5,2) },
+ { LedId.Keyboard_Space, (5,6) },
+ { LedId.Keyboard_RightAlt, (5,10) },
+ { LedId.Keyboard_RightGui, (5,11) },
+ { LedId.Keyboard_Application, (5,12) },
+ { LedId.Keyboard_RightCtrl, (5,13) },
+ { LedId.Keyboard_ArrowLeft, (5,14) },
+ { LedId.Keyboard_ArrowDown, (5,15) },
+ { LedId.Keyboard_ArrowRight, (5,16) }
+ };
+
+ #endregion
+
+ #region Wooting Two
+
+ private static readonly Dictionary WootingTwo_US = new Dictionary
+ {
+ { LedId.Keyboard_Escape, (0,0) },
+ { LedId.Keyboard_F1, (0,2) },
+ { LedId.Keyboard_F2, (0,3) },
+ { LedId.Keyboard_F3, (0,4) },
+ { LedId.Keyboard_F4, (0,5) },
+ { LedId.Keyboard_F5, (0,6) },
+ { LedId.Keyboard_F6, (0,7) },
+ { LedId.Keyboard_F7, (0,8) },
+ { LedId.Keyboard_F8, (0,9) },
+ { LedId.Keyboard_F9, (0,10) },
+ { LedId.Keyboard_F10, (0,11) },
+ { LedId.Keyboard_F11, (0,12) },
+ { LedId.Keyboard_F12, (0,13) },
+ { LedId.Keyboard_PrintScreen, (0,14) },
+ { LedId.Keyboard_PauseBreak, (0,15) },
+ { LedId.Keyboard_ScrollLock, (0,16) },
+ { LedId.Keyboard_Custom1, (0,17) },
+ { LedId.Keyboard_Custom2, (0,18) },
+ { LedId.Keyboard_Custom3, (0,19) },
+ { LedId.Keyboard_Custom4, (0,20) },
+
+ { LedId.Keyboard_GraveAccentAndTilde, (1,0) },
+ { LedId.Keyboard_1, (1,1) },
+ { LedId.Keyboard_2, (1,2) },
+ { LedId.Keyboard_3, (1,3) },
+ { LedId.Keyboard_4, (1,4) },
+ { LedId.Keyboard_5, (1,5) },
+ { LedId.Keyboard_6, (1,6) },
+ { LedId.Keyboard_7, (1,7) },
+ { LedId.Keyboard_8, (1,8) },
+ { LedId.Keyboard_9, (1,9) },
+ { LedId.Keyboard_0, (1,10) },
+ { LedId.Keyboard_MinusAndUnderscore, (1,11) },
+ { LedId.Keyboard_EqualsAndPlus, (1,12) },
+ { LedId.Keyboard_Backspace, (1,13) },
+ { LedId.Keyboard_Insert, (1,14) },
+ { LedId.Keyboard_Home, (1,15) },
+ { LedId.Keyboard_PageUp, (1,16) },
+ { LedId.Keyboard_NumLock, (1,17) },
+ { LedId.Keyboard_NumSlash, (1,18) },
+ { LedId.Keyboard_NumAsterisk, (1,19) },
+ { LedId.Keyboard_NumMinus, (1,20) },
+
+ { LedId.Keyboard_Tab, (2,0) },
+ { LedId.Keyboard_Q, (2,1) },
+ { LedId.Keyboard_W, (2,2) },
+ { LedId.Keyboard_E, (2,3) },
+ { LedId.Keyboard_R, (2,4) },
+ { LedId.Keyboard_T, (2,5) },
+ { LedId.Keyboard_Y, (2,6) },
+ { LedId.Keyboard_U, (2,7) },
+ { LedId.Keyboard_I, (2,8) },
+ { LedId.Keyboard_O, (2,9) },
+ { LedId.Keyboard_P, (2,10) },
+ { LedId.Keyboard_BracketLeft, (2,11) },
+ { LedId.Keyboard_BracketRight, (2,12) },
+ { LedId.Keyboard_Backslash, (2,13) },
+ { LedId.Keyboard_Delete, (2,14) },
+ { LedId.Keyboard_End, (2,15) },
+ { LedId.Keyboard_PageDown, (2,16) },
+ { LedId.Keyboard_Num7, (2,17) },
+ { LedId.Keyboard_Num8, (2,18) },
+ { LedId.Keyboard_Num9, (2,19) },
+ { LedId.Keyboard_NumPlus, (2,20) },
+
+ { LedId.Keyboard_CapsLock, (3,0) },
+ { LedId.Keyboard_A, (3,1) },
+ { LedId.Keyboard_S, (3,2) },
+ { LedId.Keyboard_D, (3,3) },
+ { LedId.Keyboard_F, (3,4) },
+ { LedId.Keyboard_G, (3,5) },
+ { LedId.Keyboard_H, (3,6) },
+ { LedId.Keyboard_J, (3,7) },
+ { LedId.Keyboard_K, (3,8) },
+ { LedId.Keyboard_L, (3,9) },
+ { LedId.Keyboard_SemicolonAndColon, (3,10) },
+ { LedId.Keyboard_ApostropheAndDoubleQuote, (3,11) },
+ { LedId.Keyboard_Enter, (3,13) },
+ { LedId.Keyboard_Num4, (3,17) },
+ { LedId.Keyboard_Num5, (3,18) },
+ { LedId.Keyboard_Num6, (3,19) },
+
+ { LedId.Keyboard_LeftShift, (4,0) },
+ { LedId.Keyboard_NonUsBackslash, (4,1) },
+ { LedId.Keyboard_Z, (4,2) },
+ { LedId.Keyboard_X, (4,3) },
+ { LedId.Keyboard_C, (4,4) },
+ { LedId.Keyboard_V, (4,5) },
+ { LedId.Keyboard_B, (4,6) },
+ { LedId.Keyboard_N, (4,7) },
+ { LedId.Keyboard_M, (4,8) },
+ { LedId.Keyboard_CommaAndLessThan, (4,9) },
+ { LedId.Keyboard_PeriodAndBiggerThan, (4,10) },
+ { LedId.Keyboard_SlashAndQuestionMark, (4,11) },
+ { LedId.Keyboard_RightShift, (4,13) },
+ { LedId.Keyboard_ArrowUp, (4,15) },
+ { LedId.Keyboard_Num1, (4,17) },
+ { LedId.Keyboard_Num2, (4,18) },
+ { LedId.Keyboard_Num3, (4,19) },
+ { LedId.Keyboard_NumEnter, (4,20) },
+
+ { LedId.Keyboard_LeftCtrl, (5,0) },
+ { LedId.Keyboard_LeftGui, (5,1) },
+ { LedId.Keyboard_LeftAlt, (5,2) },
+ { LedId.Keyboard_Space, (5,6) },
+ { LedId.Keyboard_RightAlt, (5,10) },
+ { LedId.Keyboard_RightGui, (5,11) },
+ { LedId.Keyboard_Application, (5,12) },
+ { LedId.Keyboard_RightCtrl, (5,13) },
+ { LedId.Keyboard_ArrowLeft, (5,14) },
+ { LedId.Keyboard_ArrowDown, (5,15) },
+ { LedId.Keyboard_ArrowRight, (5,16) },
+ { LedId.Keyboard_Num0, (5,18) },
+ { LedId.Keyboard_NumPeriodAndDelete, (5,19) }
+ };
+
+ #endregion
+
+ ///
+ /// Contains all the hardware-id mappings for Wooting devices.
+ ///
+ public static readonly Dictionary>> Mapping =
+ new Dictionary>>
+ {
+ { WootingDevicesIndexes.WootingOne, new Dictionary>
+ {
+ { WootingPhysicalKeyboardLayout.US, WootingOne_US },
+ { WootingPhysicalKeyboardLayout.EU, WootingOne_US }
+ }
+ },
+
+ { WootingDevicesIndexes.WootingTwo, new Dictionary>
+ {
+ { WootingPhysicalKeyboardLayout.US, WootingTwo_US },
+ { WootingPhysicalKeyboardLayout.EU, WootingTwo_US }
+ }
+ }
+ };
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Wooting/Keyboard/WootingKeyboardRGBDevice.cs b/RGB.NET.Devices.Wooting/Keyboard/WootingKeyboardRGBDevice.cs
new file mode 100644
index 0000000..0b3334a
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/Keyboard/WootingKeyboardRGBDevice.cs
@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+using System.Linq;
+using RGB.NET.Core;
+using RGB.NET.Devices.Wooting.Generic;
+
+namespace RGB.NET.Devices.Wooting.Keyboard
+{
+ ///
+ ///
+ /// Represents a Wooting keyboard.
+ ///
+ public class WootingKeyboardRGBDevice : WootingRGBDevice
+ {
+ #region Constructors
+
+ ///
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The specific information provided by Wooting for the keyboard
+ internal WootingKeyboardRGBDevice(WootingKeyboardRGBDeviceInfo info)
+ : base(info)
+ { }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ protected override void InitializeLayout()
+ {
+
+ Dictionary mapping = WootingKeyboardLedMappings.Mapping[DeviceInfo.DeviceIndex][DeviceInfo.PhysicalLayout];
+
+ foreach (KeyValuePair led in mapping)
+ {
+ InitializeLed(led.Key, new Point(led.Value.column * 19, led.Value.row * 19), new Size(19,19));
+ }
+
+ string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper();
+ ApplyLayoutFromFile(PathHelper.GetAbsolutePath(this, $@"Layouts\Wooting\Keyboards\{model}", $"{DeviceInfo.PhysicalLayout.ToString().ToUpper()}.xml"),
+ DeviceInfo.LogicalLayout.ToString());
+ }
+
+ ///
+ protected override void UpdateLeds(IEnumerable ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0));
+
+ ///
+ protected override object CreateLedCustomData(LedId ledId) => WootingKeyboardLedMappings.Mapping[DeviceInfo.DeviceIndex][DeviceInfo.PhysicalLayout][ledId];
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Wooting/Keyboard/WootingKeyboardRGBDeviceInfo.cs b/RGB.NET.Devices.Wooting/Keyboard/WootingKeyboardRGBDeviceInfo.cs
new file mode 100644
index 0000000..f7806f0
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/Keyboard/WootingKeyboardRGBDeviceInfo.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Globalization;
+using RGB.NET.Core;
+using RGB.NET.Devices.Wooting.Enum;
+using RGB.NET.Devices.Wooting.Generic;
+
+namespace RGB.NET.Devices.Wooting.Keyboard
+{
+ ///
+ ///
+ /// Represents a generic information for a .
+ ///
+ public class WootingKeyboardRGBDeviceInfo : WootingRGBDeviceInfo
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets the of the .
+ ///
+ public WootingPhysicalKeyboardLayout PhysicalLayout { get; }
+
+ ///
+ /// Gets the of the .
+ ///
+ public WootingLogicalKeyboardLayout LogicalLayout { get; private set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ ///
+ /// Internal constructor of managed .
+ ///
+ /// The index of the .
+ /// The of the .
+ /// The of the layout this keyboard is using
+ internal WootingKeyboardRGBDeviceInfo(WootingDevicesIndexes deviceIndex, WootingPhysicalKeyboardLayout physicalKeyboardLayout, CultureInfo culture)
+ : base(RGBDeviceType.Keyboard, deviceIndex)
+ {
+ this.PhysicalLayout = physicalKeyboardLayout;
+
+ // For now just go for this
+ switch (physicalKeyboardLayout)
+ {
+ case WootingPhysicalKeyboardLayout.US:
+ this.LogicalLayout = WootingLogicalKeyboardLayout.US;
+ break;
+ case WootingPhysicalKeyboardLayout.EU:
+ this.LogicalLayout = WootingLogicalKeyboardLayout.EU;
+ break;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Wooting/Native/_WootingSDK.cs b/RGB.NET.Devices.Wooting/Native/_WootingSDK.cs
new file mode 100644
index 0000000..402f6f4
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/Native/_WootingSDK.cs
@@ -0,0 +1,123 @@
+// 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 System.Text;
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.Wooting.Native
+{
+ // ReSharper disable once InconsistentNaming
+ public class _WootingSDK
+ {
+ #region Library 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()
+ {
+ UnloadWootingSDK();
+ LoadWootingSDK();
+ }
+
+ private static void LoadWootingSDK()
+ {
+ if (_dllHandle != IntPtr.Zero) return;
+
+ // HACK: Load library at runtime to support both, x86 and x64 with one managed dll
+ List possiblePathList = Environment.Is64BitProcess ? WootingDeviceProvider.PossibleX64NativePaths : WootingDeviceProvider.PossibleX86NativePaths;
+ string dllPath = possiblePathList.FirstOrDefault(File.Exists);
+ if (dllPath == null) throw new RGBDeviceException($"Can't find the Wooting-SDK at one of the expected locations:\r\n '{string.Join("\r\n", possiblePathList.Select(Path.GetFullPath))}'");
+
+ SetDllDirectory(Path.GetDirectoryName(Path.GetFullPath(dllPath)));
+
+ _dllHandle = LoadLibrary(dllPath);
+
+ _isWootingOnePointer = (IsWootingOnePointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "wooting_rgb_kbd_is_wooting_one"), typeof(IsWootingOnePointer));
+ _isWootingTwoPointer = (IsWootingTwoPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "wooting_rgb_kbd_is_wooting_two"), typeof(IsWootingTwoPointer));
+ _keyboardConnectedPointer = (KeyboardConnectedPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "wooting_rgb_kbd_connected"), typeof(KeyboardConnectedPointer));
+ _resetPointer = (ResetPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "wooting_rgb_reset"), typeof(ResetPointer));
+ _arrayUpdateKeyboardPointer = (ArrayUpdateKeyboardPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "wooting_rgb_array_update_keyboard"), typeof(ArrayUpdateKeyboardPointer));
+ _arraySetSinglePointer = (ArraySetSinglePointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "wooting_rgb_array_set_single"), typeof(ArraySetSinglePointer));
+ }
+
+ private static void UnloadWootingSDK()
+ {
+ 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 bool SetDllDirectory(string lpPathName);
+
+ [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 IsWootingOnePointer _isWootingOnePointer;
+ private static IsWootingTwoPointer _isWootingTwoPointer;
+ private static KeyboardConnectedPointer _keyboardConnectedPointer;
+ private static ResetPointer _resetPointer;
+ private static ArrayUpdateKeyboardPointer _arrayUpdateKeyboardPointer;
+ private static ArraySetSinglePointer _arraySetSinglePointer;
+
+ #endregion
+
+ #region Delegates
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool IsWootingOnePointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool IsWootingTwoPointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool KeyboardConnectedPointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool ResetPointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool ArrayUpdateKeyboardPointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool ArraySetSinglePointer(byte row, byte column, byte red, byte green, byte blue);
+
+ #endregion
+
+ internal static bool IsWootingOne() => _isWootingOnePointer();
+ internal static bool IsWootingTwo() => _isWootingTwoPointer();
+ internal static bool KeyboardConnected() => _keyboardConnectedPointer();
+ internal static bool Reset() => _resetPointer();
+ internal static bool ArrayUpdateKeyboard() => _arrayUpdateKeyboardPointer();
+ internal static bool ArraySetSingle(byte row, byte column, byte red, byte green, byte blue) => _arraySetSinglePointer(row, column, red, green, blue);
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Wooting/RGB.NET.Devices.Wooting.csproj b/RGB.NET.Devices.Wooting/RGB.NET.Devices.Wooting.csproj
new file mode 100644
index 0000000..128556f
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/RGB.NET.Devices.Wooting.csproj
@@ -0,0 +1,68 @@
+
+
+ netstandard2.0;net45
+ win7-x86;win7-x64
+
+ Darth Affe
+ Wyrez
+ en-US
+ en-US
+ RGB.NET.Devices.Wooting
+ RGB.NET.Devices.Wooting
+ RGB.NET.Devices.Wooting
+ RGB.NET.Devices.Wooting
+ RGB.NET.Devices.Wooting
+ Wooting-Device-Implementations of RGB.NET
+ Wooting-Device-Implementations of RGB.NET, a C# (.NET) library for accessing various RGB-peripherals
+ Copyright © Wyrez 2017
+ Copyright © Wyrez 2017
+ http://lib.arge.be/icon.png
+ https://github.com/DarthAffe/RGB.NET
+ https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE
+ Github
+ https://github.com/DarthAffe/RGB.NET
+ False
+
+
+
+ 0.0.1
+ 0.0.1
+ 0.0.1
+
+ ..\bin\
+ true
+ True
+ True
+ latest
+
+
+
+ NETCORE;NETSTANDARD;NETSTANDARD2_0
+
+
+
+ NET45;NETFULL
+
+
+
+ $(DefineConstants);TRACE;DEBUG
+ true
+ full
+ false
+
+
+
+ pdbonly
+ true
+ $(NoWarn);CS1591;CS1572;CS1573
+ $(DefineConstants);RELEASE
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RGB.NET.Devices.Wooting/WootingDeviceProvider.cs b/RGB.NET.Devices.Wooting/WootingDeviceProvider.cs
new file mode 100644
index 0000000..417b967
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/WootingDeviceProvider.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using RGB.NET.Core;
+using RGB.NET.Devices.Wooting.Native;
+
+namespace RGB.NET.Devices.Wooting
+{
+ ///
+ ///
+ /// Represents a device provider responsible for Wooting devices.
+ ///
+ public class WootingDeviceProvider : IRGBDeviceProvider
+ {
+ #region Properties & Fields
+
+ private static WootingDeviceProvider _instance;
+ ///
+ /// Gets the singleton instance.
+ ///
+ public static WootingDeviceProvider Instance => _instance ?? new WootingDeviceProvider();
+
+ ///
+ /// 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/wooting-rgb-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; } = new List { "x64/wooting-rgb-sdk64.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 => _WootingSDK.LoadedArchitecture;
+
+ ///
+ ///
+ /// Gets whether the application has exclusive access to the SDK or not.
+ ///
+ public bool HasExclusiveAccess => false;
+
+ ///
+ public IEnumerable Devices { get; private set; }
+
+ #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()
+ {
+ if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(WootingDeviceProvider)}");
+ _instance = this;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Thrown if the SDK failed to initialize
+ public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool exclusiveAccessIfPossible = false, bool throwExceptions = false)
+ {
+ IsInitialized = false;
+
+ try
+ {
+ _WootingSDK.Reload();
+
+ IList devices = new List();
+ if (_WootingSDK.KeyboardConnected())
+ {
+ if (_WootingSDK.IsWootingOne())
+ {
+
+ }
+ else if (_WootingSDK.IsWootingTwo())
+ {
+
+ }
+ }
+ Devices = new ReadOnlyCollection(devices);
+ IsInitialized = true;
+ }
+ catch
+ {
+ if (throwExceptions) throw;
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ public void ResetDevices()
+ { }
+
+ ///
+ public void Dispose()
+ {
+ try { _WootingSDK.Reset(); }
+ catch { /* Unlucky.. */}
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Wooting/WootingDeviceProviderLoader.cs b/RGB.NET.Devices.Wooting/WootingDeviceProviderLoader.cs
new file mode 100644
index 0000000..5daf314
--- /dev/null
+++ b/RGB.NET.Devices.Wooting/WootingDeviceProviderLoader.cs
@@ -0,0 +1,24 @@
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.Wooting
+{
+ ///
+ /// Represents a device provider loaded used to dynamically load Wooting devices into an application.
+ ///
+ public class WootingDeviceProviderLoader : IRGBDeviceProviderLoader
+ {
+ #region Properties & Fields
+
+ ///
+ public bool RequiresInitialization => false;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public IRGBDeviceProvider GetDeviceProvider() => WootingDeviceProvider.Instance;
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.sln b/RGB.NET.sln
index e3edb01..2d2701e 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.27703.2035
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29424.173
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Devices", "Devices", "{D13032C6-432E-4F43-8A32-071133C22B16}"
EndProject
@@ -47,6 +47,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Core.Tests", "Tests
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Devices.Asus", "RGB.NET.Devices.Asus\RGB.NET.Devices.Asus.csproj", "{E0732B34-3F96-4DD9-AFD5-0E34B833AD6D}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Devices.Wooting", "RGB.NET.Devices.Wooting\RGB.NET.Devices.Wooting.csproj", "{DD46DB2D-85BE-4962-86AE-E38C9053A548}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -129,6 +131,10 @@ Global
{E0732B34-3F96-4DD9-AFD5-0E34B833AD6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0732B34-3F96-4DD9-AFD5-0E34B833AD6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0732B34-3F96-4DD9-AFD5-0E34B833AD6D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DD46DB2D-85BE-4962-86AE-E38C9053A548}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DD46DB2D-85BE-4962-86AE-E38C9053A548}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DD46DB2D-85BE-4962-86AE-E38C9053A548}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DD46DB2D-85BE-4962-86AE-E38C9053A548}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -152,6 +158,7 @@ Global
{FFDE4387-60F2-47B6-9704-3A57D02B8C64} = {D13032C6-432E-4F43-8A32-071133C22B16}
{A3FD5AD7-040A-47CA-A278-53493A25FF8A} = {92D7C263-D4C9-4D26-93E2-93C1F9C2CD16}
{E0732B34-3F96-4DD9-AFD5-0E34B833AD6D} = {D13032C6-432E-4F43-8A32-071133C22B16}
+ {DD46DB2D-85BE-4962-86AE-E38C9053A548} = {D13032C6-432E-4F43-8A32-071133C22B16}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7F222AD4-1F9E-4AAB-9D69-D62372D4C1BA}