diff --git a/RGB.NET.Devices.CoolerMaster/Attributes/DeviceTypeAttribute.cs b/RGB.NET.Devices.CoolerMaster/Attributes/DeviceTypeAttribute.cs
new file mode 100644
index 0000000..ae20a09
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Attributes/DeviceTypeAttribute.cs
@@ -0,0 +1,34 @@
+using System;
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.CoolerMaster.Attributes
+{
+ ///
+ /// Specifies the of a field.
+ ///
+ [AttributeUsage(AttributeTargets.Field)]
+ public class DeviceTypeAttribute : Attribute
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets the .
+ ///
+ public RGBDeviceType DeviceType { get; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Internal constructor of the class.
+ ///
+ /// The .
+ public DeviceTypeAttribute(RGBDeviceType deviceType)
+ {
+ this.DeviceType = deviceType;
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/CoolerMasterDeviceProvider.cs b/RGB.NET.Devices.CoolerMaster/CoolerMasterDeviceProvider.cs
new file mode 100644
index 0000000..5e354a7
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/CoolerMasterDeviceProvider.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Globalization;
+using RGB.NET.Core;
+using RGB.NET.Core.Exceptions;
+using RGB.NET.Devices.CoolerMaster.Helper;
+using RGB.NET.Devices.CoolerMaster.Native;
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Represents a device provider responsible for Cooler Master devices.
+ ///
+ public class CoolerMasterDeviceProvider : IRGBDeviceProvider
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets the singleton instance.
+ ///
+ public static CoolerMasterDeviceProvider Instance { get; } = new CoolerMasterDeviceProvider();
+
+ ///
+ /// 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 => _CoolerMasterSDK.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; }
+
+ ///
+ /// Gets or sets a function to get the culture for a specific device.
+ ///
+ public Func GetCulture { get; set; } = () => CultureHelper.GetCurrentCulture();
+
+ #endregion
+
+ #region Constructors
+
+ private CoolerMasterDeviceProvider()
+ { }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public bool Initialize(bool exclusiveAccessIfPossible = false, bool throwExceptions = false)
+ {
+ IsInitialized = false;
+
+ try
+ {
+ _CoolerMasterSDK.Reload();
+ if (_CoolerMasterSDK.GetSDKVersion() <= 0) return false;
+
+ IList devices = new List();
+
+ foreach (CoolerMasterDevicesIndexes index in Enum.GetValues(typeof(CoolerMasterDevicesIndexes)))
+ {
+ _CoolerMasterSDK.SetControlDevice(index);
+ if (_CoolerMasterSDK.IsDevicePlugged())
+ {
+ try
+ {
+ CoolerMasterRGBDevice device = null;
+ switch (index.GetDeviceType())
+ {
+ case RGBDeviceType.Keyboard:
+ CoolerMasterPhysicalKeyboardLayout physicalLayout = _CoolerMasterSDK.GetDeviceLayout();
+ device = new CoolerMasterKeyboardRGBDevice(new CoolerMasterKeyboardRGBDeviceInfo(index, physicalLayout, GetCulture()));
+ break;
+ default:
+ if (throwExceptions)
+ throw new RGBDeviceException("Unknown Device-Type");
+ else
+ continue;
+ }
+
+ _CoolerMasterSDK.EnableLedControl(true);
+
+ device.Initialize();
+ devices.Add(device);
+ }
+ catch
+ {
+ if (throwExceptions)
+ throw;
+ else
+ continue;
+ }
+ }
+ }
+
+ Devices = new ReadOnlyCollection(devices);
+ }
+ catch
+ {
+ if (throwExceptions)
+ throw;
+ else
+ return false;
+ }
+
+ IsInitialized = true;
+
+ return true;
+ }
+
+ ///
+ public void ResetDevices()
+ {
+ if (IsInitialized)
+ try
+ {
+ foreach (IRGBDevice device in Devices)
+ {
+ CoolerMasterRGBDeviceInfo deviceInfo = (CoolerMasterRGBDeviceInfo)device.DeviceInfo;
+ _CoolerMasterSDK.SetControlDevice(deviceInfo.DeviceIndex);
+ _CoolerMasterSDK.EnableLedControl(false);
+ _CoolerMasterSDK.EnableLedControl(true);
+ }
+ }
+ catch
+ {
+ // shit happens ...
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterDevicesIndexes.cs b/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterDevicesIndexes.cs
new file mode 100644
index 0000000..22c1e9c
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterDevicesIndexes.cs
@@ -0,0 +1,49 @@
+// ReSharper disable InconsistentNaming
+// ReSharper disable UnusedMember.Global
+
+using System.ComponentModel;
+using RGB.NET.Core;
+using RGB.NET.Devices.CoolerMaster.Attributes;
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Contains a list of available device-indexes.
+ ///
+ public enum CoolerMasterDevicesIndexes
+ {
+ [Description("MasterKeys Pro L")]
+ [DeviceType(RGBDeviceType.Keyboard)]
+ MasterKeys_L = 0,
+
+ [Description("MasterKeys ProS")]
+ [DeviceType(RGBDeviceType.Keyboard)]
+ MasterKeys_S = 1,
+
+ [Description("MasterKeys Pro L White")]
+ [DeviceType(RGBDeviceType.Keyboard)]
+ MasterKeys_L_White = 2,
+
+ [Description("MasterKeys Pro M White")]
+ [DeviceType(RGBDeviceType.Keyboard)]
+ MasterKeys_M_White = 3,
+
+ [Description("MasterMouse Pro L")]
+ [DeviceType(RGBDeviceType.Mouse)]
+ MasterMouse_L = 4,
+
+ [Description("MasterMouse S")]
+ [DeviceType(RGBDeviceType.Mouse)]
+ MasterMouse_S = 5,
+
+ [Description("MasterKeys Pro M")]
+ [DeviceType(RGBDeviceType.Keyboard)]
+ MasterKeys_M = 6,
+
+ [Description("MasterKeys Pro S White")]
+ [DeviceType(RGBDeviceType.Keyboard)]
+ MasterKeys_S_White = 7
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterEffects.cs b/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterEffects.cs
new file mode 100644
index 0000000..c40e3f7
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterEffects.cs
@@ -0,0 +1,35 @@
+// ReSharper disable InconsistentNaming
+// ReSharper disable UnusedMember.Global
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Contains a list of available effects.
+ ///
+ public enum CoolerMasterEffects
+ {
+ FullOn = 0,
+ Breath = 1,
+ BreathCycle = 2,
+ Single = 3,
+ Wave = 4,
+ Ripple = 5,
+ Cross = 6,
+ Rain = 7,
+ Star = 8,
+ Snake = 9,
+ Rec = 10,
+
+ Spectrum = 11,
+ RapidFire = 12,
+ Indicator = 13, //mouse Effect
+
+ Multi1 = 0xE0,
+ Multi2 = 0xE1,
+ Multi3 = 0xE2,
+ Multi4 = 0xE3,
+ Off = 0xFE
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterLedIds.cs b/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterLedIds.cs
new file mode 100644
index 0000000..072cfe0
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterLedIds.cs
@@ -0,0 +1,25 @@
+// ReSharper disable InconsistentNaming
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Contains list of all LEDs available for all CoolerMaster devices.
+ ///
+ public enum CoolerMasterLedIds
+ {
+ Invalid = 0,
+
+ A,
+ S,
+ D,
+ F,
+
+ Side1,
+ Side2,
+ Side3,
+ Back1,
+ Wheel
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterLogicalKeyboardLayout.cs b/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterLogicalKeyboardLayout.cs
new file mode 100644
index 0000000..30c8b5d
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterLogicalKeyboardLayout.cs
@@ -0,0 +1,15 @@
+// ReSharper disable InconsistentNaming
+// ReSharper disable UnusedMember.Global
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Contains list of available logical layouts for logitech keyboards.
+ ///
+ public enum CoolerMasterLogicalKeyboardLayout
+ {
+ DE
+ };
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterPhysicalKeyboardLayout.cs b/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterPhysicalKeyboardLayout.cs
new file mode 100644
index 0000000..5dd477c
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Enum/CoolerMasterPhysicalKeyboardLayout.cs
@@ -0,0 +1,17 @@
+// ReSharper disable InconsistentNaming
+// ReSharper disable UnusedMember.Global
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Contains list of available physical layouts for cooler master keyboards.
+ ///
+ public enum CoolerMasterPhysicalKeyboardLayout
+ {
+ UNINIT = 0,
+ US = 1,
+ EU = 2
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterLedId.cs b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterLedId.cs
new file mode 100644
index 0000000..6de5849
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterLedId.cs
@@ -0,0 +1,125 @@
+using System.Diagnostics;
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Represents a Id of a on a .
+ ///
+ [DebuggerDisplay("{" + nameof(LedId) + "}")]
+ public class CoolerMasterLedId : ILedId
+ {
+ #region Properties & Fields
+
+ internal readonly CoolerMasterLedIds LedId;
+
+ internal readonly int Row;
+ internal readonly int Column;
+
+ ///
+ public IRGBDevice Device { get; }
+
+ ///
+ public bool IsValid => LedId != CoolerMasterLedIds.Invalid;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The the belongs to.
+ /// The of the represented .
+ public CoolerMasterLedId(IRGBDevice device, CoolerMasterLedIds ledId)
+ {
+ this.Device = device;
+ this.LedId = ledId;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The the belongs to.
+ /// The of the represented .
+ /// The row in the mapping table of the device.
+ /// The column in the mapping table of the device.
+ public CoolerMasterLedId(IRGBDevice device, CoolerMasterLedIds ledId, int row, int column)
+ {
+ this.Device = device;
+ this.LedId = ledId;
+ this.Row = row;
+ this.Column = column;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Converts the Id of this to a human-readable string.
+ ///
+ /// A string that contains the Id of this . For example "Enter".
+ public override string ToString()
+ {
+ return LedId.ToString();
+ }
+
+ ///
+ /// Tests whether the specified object is a and is equivalent to this .
+ ///
+ /// The object to test.
+ /// true if is a equivalent to this ; otherwise, false.
+ public override bool Equals(object obj)
+ {
+ CoolerMasterLedId compareLedId = obj as CoolerMasterLedId;
+ if (ReferenceEquals(compareLedId, null))
+ return false;
+
+ if (ReferenceEquals(this, compareLedId))
+ return true;
+
+ if (GetType() != compareLedId.GetType())
+ return false;
+
+ return compareLedId.LedId == LedId;
+ }
+
+ ///
+ /// Returns a hash code for this .
+ ///
+ /// An integer value that specifies the hash code for this .
+ public override int GetHashCode()
+ {
+ return LedId.GetHashCode();
+ }
+
+ #endregion
+
+ #region Operators
+
+ ///
+ /// Returns a value that indicates whether two specified are equal.
+ ///
+ /// The first to compare.
+ /// The second to compare.
+ /// true if and are equal; otherwise, false.
+ public static bool operator ==(CoolerMasterLedId ledId1, CoolerMasterLedId ledId2)
+ {
+ return ReferenceEquals(ledId1, null) ? ReferenceEquals(ledId2, null) : ledId1.Equals(ledId2);
+ }
+
+ ///
+ /// Returns a value that indicates whether two specified are equal.
+ ///
+ /// The first to compare.
+ /// The second to compare.
+ /// true if and are not equal; otherwise, false.
+ public static bool operator !=(CoolerMasterLedId ledId1, CoolerMasterLedId ledId2)
+ {
+ return !(ledId1 == ledId2);
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterRGBDevice.cs b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterRGBDevice.cs
new file mode 100644
index 0000000..be2789c
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterRGBDevice.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using RGB.NET.Core;
+using RGB.NET.Core.Layout;
+using RGB.NET.Devices.CoolerMaster.Native;
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Represents a generic CoolerMaster-device. (keyboard, mouse, headset, mousmat).
+ ///
+ public abstract class CoolerMasterRGBDevice : AbstractRGBDevice
+ {
+ #region Properties & Fields
+ ///
+ /// Gets information about the .
+ ///
+ public override IRGBDeviceInfo DeviceInfo { get; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The generic information provided by CoolerMaster for the device.
+ protected CoolerMasterRGBDevice(IRGBDeviceInfo info)
+ {
+ this.DeviceInfo = info;
+ }
+
+ #endregion
+
+ #region Methods
+
+
+ ///
+ /// Initializes the device.
+ ///
+ internal void Initialize()
+ {
+ InitializeLayout();
+
+ if (InternalSize == null)
+ {
+ Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle));
+ InternalSize = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
+ }
+ }
+
+ ///
+ /// Initializes the and of the device.
+ ///
+ protected abstract void InitializeLayout();
+
+ ///
+ /// Applies the given layout.
+ ///
+ /// The file containing the layout.
+ /// The name of the layout used to get the images of the leds.
+ /// The path images for this device are collected in.
+ protected void ApplyLayoutFromFile(string layoutPath, string imageLayout, string imageBasePath)
+ {
+ DeviceLayout layout = DeviceLayout.Load(layoutPath);
+ if (layout != null)
+ {
+ LedImageLayout ledImageLayout = layout.LedImageLayouts.FirstOrDefault(x => string.Equals(x.Layout, imageLayout, StringComparison.OrdinalIgnoreCase));
+
+ InternalSize = new Size(layout.Width, layout.Height);
+
+ if (layout.Leds != null)
+ foreach (LedLayout layoutLed in layout.Leds)
+ {
+ CoolerMasterLedIds ledId;
+ if (Enum.TryParse(layoutLed.Id, true, out ledId))
+ {
+ Led led;
+ if (LedMapping.TryGetValue(new CoolerMasterLedId(this, ledId), out led))
+ {
+ led.LedRectangle.Location.X = layoutLed.X;
+ led.LedRectangle.Location.Y = layoutLed.Y;
+ led.LedRectangle.Size.Width = layoutLed.Width;
+ led.LedRectangle.Size.Height = layoutLed.Height;
+
+ led.Shape = layoutLed.Shape;
+ led.ShapeData = layoutLed.ShapeData;
+
+ LedImage image = ledImageLayout?.LedImages.FirstOrDefault(x => x.Id == layoutLed.Id);
+ led.Image = (!string.IsNullOrEmpty(image?.Image))
+ ? new Uri(Path.Combine(imageBasePath, image.Image), UriKind.Absolute)
+ : new Uri(Path.Combine(imageBasePath, "Missing.png"), UriKind.Absolute);
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ protected override void UpdateLeds(IEnumerable ledsToUpdate)
+ {
+ List leds = ledsToUpdate.Where(x => x.Color.A > 0).ToList();
+
+ if (leds.Count > 0)
+ {
+ _CoolerMasterSDK.SetControlDevice(((CoolerMasterRGBDeviceInfo)DeviceInfo).DeviceIndex);
+
+ foreach (Led led in leds)
+ {
+ CoolerMasterLedId ledId = (CoolerMasterLedId)led.Id;
+ _CoolerMasterSDK.SetLedColor(ledId.Row, ledId.Column, led.Color.R, led.Color.G, led.Color.B);
+ }
+
+ _CoolerMasterSDK.RefreshLed(false);
+ }
+ }
+
+ ///
+ public override void Dispose()
+ {
+ _CoolerMasterSDK.SetControlDevice(((CoolerMasterRGBDeviceInfo)DeviceInfo).DeviceIndex);
+ _CoolerMasterSDK.EnableLedControl(false);
+
+ base.Dispose();
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterRGBDeviceInfo.cs b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterRGBDeviceInfo.cs
new file mode 100644
index 0000000..54de789
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterRGBDeviceInfo.cs
@@ -0,0 +1,53 @@
+using System;
+using RGB.NET.Core;
+using RGB.NET.Devices.CoolerMaster.Helper;
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Represents a generic information for a Corsair-.
+ ///
+ public class CoolerMasterRGBDeviceInfo : IRGBDeviceInfo
+ {
+ #region Properties & Fields
+
+ ///
+ public RGBDeviceType DeviceType { get; }
+
+ ///
+ public string Manufacturer => "Cooler Master";
+
+ ///
+ public string Model { get; }
+
+ ///
+ public Uri Image { get; protected set; }
+
+ ///
+ public RGBDeviceLighting Lighting => RGBDeviceLighting.Key;
+
+ ///
+ /// Gets the of the .
+ ///
+ public CoolerMasterDevicesIndexes DeviceIndex { get; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Internal constructor of managed .
+ ///
+ /// The type of the .
+ /// The of the .
+ internal CoolerMasterRGBDeviceInfo(RGBDeviceType deviceType, CoolerMasterDevicesIndexes deviceIndex)
+ {
+ this.DeviceType = deviceType;
+ this.DeviceIndex = deviceIndex;
+
+ Model = deviceIndex.GetDescription();
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Helper/CultureHelper.cs b/RGB.NET.Devices.CoolerMaster/Helper/CultureHelper.cs
new file mode 100644
index 0000000..cd0bb4f
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Helper/CultureHelper.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Globalization;
+using System.Runtime.InteropServices;
+
+namespace RGB.NET.Devices.CoolerMaster.Helper
+{
+ ///
+ /// Offers some helper-methods for culture related things.
+ ///
+ internal static class CultureHelper
+ {
+ #region DLLImports
+
+ [DllImport("user32.dll")]
+ private static extern IntPtr GetKeyboardLayout(uint thread);
+
+ #endregion
+
+ #region Constructors
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Gets the current keyboard-layout from the OS.
+ ///
+ /// The current keyboard-layout
+ internal static CultureInfo GetCurrentCulture()
+ {
+ try
+ {
+ int keyboardLayout = GetKeyboardLayout(0).ToInt32() & 0xFFFF;
+ return new CultureInfo(keyboardLayout);
+ }
+ catch
+ {
+ return new CultureInfo(1033); // en-US on error.
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Helper/EnumExtension.cs b/RGB.NET.Devices.CoolerMaster/Helper/EnumExtension.cs
new file mode 100644
index 0000000..a9616cf
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Helper/EnumExtension.cs
@@ -0,0 +1,54 @@
+using System;
+using System.ComponentModel;
+using System.Reflection;
+using RGB.NET.Core;
+using RGB.NET.Devices.CoolerMaster.Attributes;
+
+namespace RGB.NET.Devices.CoolerMaster.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 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 RGBDeviceType GetDeviceType(this T source)
+ where T : struct
+ {
+ return source.GetAttribute()?.DeviceType ?? RGBDeviceType.Unknown;
+ }
+
+ ///
+ /// 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.CoolerMaster/Helper/PathHelper.cs b/RGB.NET.Devices.CoolerMaster/Helper/PathHelper.cs
new file mode 100644
index 0000000..213ab67
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Helper/PathHelper.cs
@@ -0,0 +1,24 @@
+using System.IO;
+using System.Reflection;
+
+namespace RGB.NET.Devices.CoolerMaster.Helper
+{
+ ///
+ /// Offers some helper-methods for file-path related things.
+ ///
+ internal static class PathHelper
+ {
+ ///
+ /// Returns an absolute path created from an relative path relatvie to the location of the executung assembly.
+ ///
+ /// The relative path to convert.
+ /// The absolute path.
+ internal static string GetAbsolutePath(string relativePath)
+ {
+ string assemblyLocation = Assembly.GetEntryAssembly()?.Location;
+ if (assemblyLocation == null) return relativePath;
+
+ return Path.Combine(Path.GetDirectoryName(assemblyLocation), relativePath);
+ }
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Keyboard/CoolerMasterKeyboardLedMappings.cs b/RGB.NET.Devices.CoolerMaster/Keyboard/CoolerMasterKeyboardLedMappings.cs
new file mode 100644
index 0000000..01256dc
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Keyboard/CoolerMasterKeyboardLedMappings.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Contains all the hardware-id mappings for CoolerMaster devices.
+ ///
+ internal static class CoolerMasterKeyboardLedMappings
+ {
+ #region Properties & Fields
+
+ private static readonly Dictionary> MasterKeysL_US = new Dictionary>
+ {
+ { CoolerMasterLedIds.A, new Tuple(3,1) },
+ { CoolerMasterLedIds.S, new Tuple(3,2) },
+ { CoolerMasterLedIds.D, new Tuple(3,3) },
+ { CoolerMasterLedIds.F, new Tuple(3,4) },
+ };
+
+ private static readonly Dictionary> MasterKeysL_EU = new Dictionary>
+ {
+ { CoolerMasterLedIds.A, new Tuple(3,1) },
+ { CoolerMasterLedIds.S, new Tuple(3,2) },
+ { CoolerMasterLedIds.D, new Tuple(3,3) },
+ { CoolerMasterLedIds.F, new Tuple(3,4) },
+ };
+
+ private static readonly Dictionary> MasterKeysM_US = new Dictionary>
+ {
+ { CoolerMasterLedIds.A, new Tuple(3,1) },
+ { CoolerMasterLedIds.S, new Tuple(3,2) },
+ { CoolerMasterLedIds.D, new Tuple(3,3) },
+ { CoolerMasterLedIds.F, new Tuple(3,4) },
+ };
+
+ private static readonly Dictionary> MasterKeysM_EU = new Dictionary>
+ {
+ { CoolerMasterLedIds.A, new Tuple(3,1) },
+ { CoolerMasterLedIds.S, new Tuple(3,2) },
+ { CoolerMasterLedIds.D, new Tuple(3,3) },
+ { CoolerMasterLedIds.F, new Tuple(3,4) },
+ };
+
+ private static readonly Dictionary> MasterKeysS_US = new Dictionary>
+ {
+ { CoolerMasterLedIds.A, new Tuple(3,1) },
+ { CoolerMasterLedIds.S, new Tuple(3,2) },
+ { CoolerMasterLedIds.D, new Tuple(3,3) },
+ { CoolerMasterLedIds.F, new Tuple(3,4) },
+ };
+
+ private static readonly Dictionary> MasterKeysS_EU = new Dictionary>
+ {
+ { CoolerMasterLedIds.A, new Tuple(3,1) },
+ { CoolerMasterLedIds.S, new Tuple(3,2) },
+ { CoolerMasterLedIds.D, new Tuple(3,3) },
+ { CoolerMasterLedIds.F, new Tuple(3,4) },
+ };
+
+ ///
+ /// Contains all the hardware-id mappings for CoolerMaster devices.
+ ///
+ // ReSharper disable once InconsistentNaming
+ public static readonly Dictionary>>> Mapping =
+ new Dictionary>>>
+ {
+ { CoolerMasterDevicesIndexes.MasterKeys_L, new Dictionary>>
+ {
+ { CoolerMasterPhysicalKeyboardLayout.US, MasterKeysL_US },
+ { CoolerMasterPhysicalKeyboardLayout.EU, MasterKeysL_EU }
+ }
+ },
+
+ { CoolerMasterDevicesIndexes.MasterKeys_M, new Dictionary>>
+ {
+ { CoolerMasterPhysicalKeyboardLayout.US, MasterKeysM_US },
+ { CoolerMasterPhysicalKeyboardLayout.EU, MasterKeysM_EU }
+ }
+ },
+
+ { CoolerMasterDevicesIndexes.MasterKeys_S, new Dictionary>>
+ {
+ { CoolerMasterPhysicalKeyboardLayout.US, MasterKeysS_US },
+ { CoolerMasterPhysicalKeyboardLayout.EU, MasterKeysS_EU }
+ }
+ },
+
+ { CoolerMasterDevicesIndexes.MasterKeys_L_White, new Dictionary>>
+ {
+ { CoolerMasterPhysicalKeyboardLayout.US, MasterKeysL_US },
+ { CoolerMasterPhysicalKeyboardLayout.EU, MasterKeysL_EU }
+ }
+ },
+
+ { CoolerMasterDevicesIndexes.MasterKeys_M_White, new Dictionary>>
+ {
+ { CoolerMasterPhysicalKeyboardLayout.US, MasterKeysM_US },
+ { CoolerMasterPhysicalKeyboardLayout.EU, MasterKeysM_EU }
+ }
+ },
+
+ { CoolerMasterDevicesIndexes.MasterKeys_S_White, new Dictionary>>
+ {
+ { CoolerMasterPhysicalKeyboardLayout.US, MasterKeysS_US },
+ { CoolerMasterPhysicalKeyboardLayout.EU, MasterKeysS_EU }
+ }
+ },
+ };
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Keyboard/CoolerMasterKeyboardRGBDevice.cs b/RGB.NET.Devices.CoolerMaster/Keyboard/CoolerMasterKeyboardRGBDevice.cs
new file mode 100644
index 0000000..9c4cd19
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Keyboard/CoolerMasterKeyboardRGBDevice.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using RGB.NET.Core;
+using RGB.NET.Devices.CoolerMaster.Helper;
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Represents a CoolerMaster keyboard.
+ ///
+ public class CoolerMasterKeyboardRGBDevice : CoolerMasterRGBDevice
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets information about the .
+ ///
+ public CoolerMasterKeyboardRGBDeviceInfo KeyboardDeviceInfo { get; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The specific information provided by CoolerMaster for the keyboard
+ internal CoolerMasterKeyboardRGBDevice(CoolerMasterKeyboardRGBDeviceInfo info)
+ : base(info)
+ {
+ this.KeyboardDeviceInfo = info;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ protected override void InitializeLayout()
+ {
+ Dictionary> mapping = CoolerMasterKeyboardLedMappings.Mapping[KeyboardDeviceInfo.DeviceIndex][KeyboardDeviceInfo.PhysicalLayout];
+
+ foreach (KeyValuePair> led in mapping)
+ InitializeLed(new CoolerMasterLedId(this, led.Key, led.Value.Item1, led.Value.Item2),
+ new Rectangle(led.Value.Item2 * 19, led.Value.Item1 * 19, 19, 19));
+
+ string model = KeyboardDeviceInfo.Model.Replace(" ", string.Empty).ToUpper();
+ ApplyLayoutFromFile(PathHelper.GetAbsolutePath(
+ $@"Layouts\CoolerMaster\Keyboards\{model}\{KeyboardDeviceInfo.PhysicalLayout.ToString().ToUpper()}.xml"),
+ KeyboardDeviceInfo.LogicalLayout.ToString(), PathHelper.GetAbsolutePath($@"Images\CoolerMaster\Keyboards"));
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Keyboard/CoolerMasterKeyboardRGBDeviceInfo.cs b/RGB.NET.Devices.CoolerMaster/Keyboard/CoolerMasterKeyboardRGBDeviceInfo.cs
new file mode 100644
index 0000000..f84e497
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Keyboard/CoolerMasterKeyboardRGBDeviceInfo.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Globalization;
+using RGB.NET.Core;
+using RGB.NET.Devices.CoolerMaster.Helper;
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Represents a generic information for a .
+ ///
+ public class CoolerMasterKeyboardRGBDeviceInfo : CoolerMasterRGBDeviceInfo
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets the of the .
+ ///
+ public CoolerMasterPhysicalKeyboardLayout PhysicalLayout { get; private set; }
+
+ ///
+ /// Gets the of the .
+ ///
+ public CoolerMasterLogicalKeyboardLayout 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 CoolerMasterKeyboardRGBDeviceInfo(CoolerMasterDevicesIndexes deviceIndex, CoolerMasterPhysicalKeyboardLayout physicalKeyboardLayout, CultureInfo culture)
+ : base(RGBDeviceType.Keyboard, deviceIndex)
+ {
+ this.PhysicalLayout = physicalKeyboardLayout;
+
+ SetLayouts(culture.KeyboardLayoutId);
+
+ Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Logitech\Keyboards\{Model.Replace(" ", string.Empty).ToUpper()}.png"), UriKind.Absolute);
+ }
+
+ private void SetLayouts(int keyboardLayoutId)
+ {
+ switch (keyboardLayoutId)
+ {
+ //TODO DarthAffe 02.04.2017: Check all available keyboards and there layout-ids
+ default:
+ LogicalLayout = CoolerMasterLogicalKeyboardLayout.DE;
+ break;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Mouse/CoolerMasterMouseLedMappings.cs b/RGB.NET.Devices.CoolerMaster/Mouse/CoolerMasterMouseLedMappings.cs
new file mode 100644
index 0000000..731d9d2
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Mouse/CoolerMasterMouseLedMappings.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+
+namespace RGB.NET.Devices.CoolerMaster
+{
+ ///
+ /// Contains all the hardware-id mappings for CoolerMaster devices.
+ ///
+ internal static class CoolerMasterMouseLedMappings
+ {
+ #region Properties & Fields
+
+ ///
+ /// Contains all the hardware-id mappings for CoolerMaster devices.
+ ///
+ // ReSharper disable once InconsistentNaming
+ public static readonly Dictionary>> Mapping =
+ new Dictionary>>
+ {
+ { CoolerMasterDevicesIndexes.MasterMouse_L, new Dictionary>
+ {
+ { CoolerMasterLedIds.Side1, new Tuple(0,0) },
+ { CoolerMasterLedIds.Side2, new Tuple(1,0) },
+ { CoolerMasterLedIds.Side3, new Tuple(2,0) },
+ { CoolerMasterLedIds.Back1, new Tuple(3,0) },
+ }
+ },
+
+ { CoolerMasterDevicesIndexes.MasterMouse_S, new Dictionary>
+ {
+ { CoolerMasterLedIds.Back1, new Tuple(0,0) },
+ { CoolerMasterLedIds.Wheel, new Tuple(1,0) },
+ { CoolerMasterLedIds.Side3, new Tuple(2,0) },
+ { CoolerMasterLedIds.Back1, new Tuple(3,0) },
+ }
+ },
+ };
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/Native/_CoolerMasterSDK.cs b/RGB.NET.Devices.CoolerMaster/Native/_CoolerMasterSDK.cs
new file mode 100644
index 0000000..151400a
--- /dev/null
+++ b/RGB.NET.Devices.CoolerMaster/Native/_CoolerMasterSDK.cs
@@ -0,0 +1,177 @@
+// ReSharper disable UnusedMethodReturnValue.Global
+// ReSharper disable UnusedMember.Global
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using RGB.NET.Core.Exceptions;
+
+namespace RGB.NET.Devices.CoolerMaster.Native
+{
+ // ReSharper disable once InconsistentNaming
+ internal static class _CoolerMasterSDK
+ {
+ #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()
+ {
+ UnloadCMSDK();
+ LoadCMSDK();
+ }
+
+ private static void LoadCMSDK()
+ {
+ if (_dllHandle != IntPtr.Zero) return;
+
+ // HACK: Load library at runtime to support both, x86 and x64 with one managed dll
+ string dllPath = (LoadedArchitecture = Environment.Is64BitProcess ? "x64" : "x86") + "/CMSDK.dll";
+ if (!File.Exists(dllPath))
+ throw new RGBDeviceException($"Can't find the CoolerMaster-SDK at the expected location '{Path.GetFullPath(dllPath)}'");
+
+ _dllHandle = LoadLibrary(dllPath);
+
+ _getSDKVersionPointer = (GetSDKVersionPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "GetCM_SDK_DllVer"), typeof(GetSDKVersionPointer));
+ _setControlDevicenPointer = (SetControlDevicePointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "SetControlDevice"), typeof(SetControlDevicePointer));
+ _isDevicePlugPointer = (IsDevicePlugPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "IsDevicePlug"), typeof(IsDevicePlugPointer));
+ _getDeviceLayoutPointer = (GetDeviceLayoutPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "GetDeviceLayout"), typeof(GetDeviceLayoutPointer));
+ _enableLedControlPointer = (EnableLedControlPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "EnableLedControl"), typeof(EnableLedControlPointer));
+ _refreshLedPointer = (RefreshLedPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "RefreshLed"), typeof(RefreshLedPointer));
+ _setLedColorPointer = (SetLedColorPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "SetLedColor"), typeof(SetLedColorPointer));
+ }
+
+ private static void UnloadCMSDK()
+ {
+ 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 GetSDKVersionPointer _getSDKVersionPointer;
+ private static SetControlDevicePointer _setControlDevicenPointer;
+ private static IsDevicePlugPointer _isDevicePlugPointer;
+ private static GetDeviceLayoutPointer _getDeviceLayoutPointer;
+ private static EnableLedControlPointer _enableLedControlPointer;
+ private static RefreshLedPointer _refreshLedPointer;
+ private static SetLedColorPointer _setLedColorPointer;
+
+ #endregion
+
+ #region Delegates
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate int GetSDKVersionPointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate void SetControlDevicePointer(CoolerMasterDevicesIndexes devicesIndexes);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ private delegate bool IsDevicePlugPointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate CoolerMasterPhysicalKeyboardLayout GetDeviceLayoutPointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ private delegate bool EnableLedControlPointer(bool value);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ private delegate bool RefreshLedPointer(bool autoRefresh);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ private delegate bool SetLedColorPointer(int row, int column, byte r, byte g, byte b);
+
+ #endregion
+
+ // ReSharper disable EventExceptionNotDocumented
+
+ ///
+ /// CM-SDK: Get SDK Dll's Version.
+ ///
+ internal static int GetSDKVersion()
+ {
+ return _getSDKVersionPointer();
+ }
+
+ ///
+ /// CM-SDK: set operating device
+ ///
+ internal static void SetControlDevice(CoolerMasterDevicesIndexes devicesIndexes)
+ {
+ _setControlDevicenPointer(devicesIndexes);
+ }
+
+ ///
+ /// CM-SDK: verify if the deviced is plugged in
+ ///
+ internal static bool IsDevicePlugged()
+ {
+ return _isDevicePlugPointer();
+ }
+
+ ///
+ /// CM-SDK: Obtain current device layout
+ ///
+ internal static CoolerMasterPhysicalKeyboardLayout GetDeviceLayout()
+ {
+ return _getDeviceLayoutPointer();
+ }
+
+ ///
+ /// CM-SDK: set control over device’s LED
+ ///
+ internal static bool EnableLedControl(bool value)
+ {
+ return _enableLedControlPointer(value);
+ }
+
+ ///
+ /// CM-SDK: Print out the lights setting from Buffer to LED
+ ///
+ internal static bool RefreshLed(bool autoRefresh)
+ {
+ return _refreshLedPointer(autoRefresh);
+ }
+
+ ///
+ /// CM-SDK: Set single Key LED color
+ ///
+ internal static bool SetLedColor(int row, int column, byte r, byte g, byte b)
+ {
+ return _setLedColorPointer(row, column, r, g, b);
+ }
+
+ // ReSharper restore EventExceptionNotDocumented
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.CoolerMaster/RGB.NET.Devices.CoolerMaster.csproj b/RGB.NET.Devices.CoolerMaster/RGB.NET.Devices.CoolerMaster.csproj
index 63ec803..ef465a0 100644
--- a/RGB.NET.Devices.CoolerMaster/RGB.NET.Devices.CoolerMaster.csproj
+++ b/RGB.NET.Devices.CoolerMaster/RGB.NET.Devices.CoolerMaster.csproj
@@ -47,6 +47,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -61,8 +79,8 @@
-
-
+
+