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 @@ - - + +