diff --git a/NuGet/Metapackages/RGB.NET.Devices.nuspec b/NuGet/Metapackages/RGB.NET.Devices.nuspec index 2d578be..35e6773 100644 --- a/NuGet/Metapackages/RGB.NET.Devices.nuspec +++ b/NuGet/Metapackages/RGB.NET.Devices.nuspec @@ -21,6 +21,7 @@ + \ No newline at end of file diff --git a/NuGet/RGB.NET.Devices.Razer.nuspec b/NuGet/RGB.NET.Devices.Razer.nuspec new file mode 100644 index 0000000..eb3e065 --- /dev/null +++ b/NuGet/RGB.NET.Devices.Razer.nuspec @@ -0,0 +1,32 @@ + + + + RGB.NET.Devices.Razer + RGB.NET.Devices.Razer + 0.0.1 + Darth Affe + Darth Affe + https://github.com/DarthAffe/RGB.NET + https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE + true + Razer-Device-Implementations of RGB.NET + + Razer-Device-Implementations of RGB.NET, a C# (.NET) library for accessing various RGB-peripherals + Copyright © Wyrez 2017 + en-US + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDevice.cs b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDevice.cs new file mode 100644 index 0000000..36c6741 --- /dev/null +++ b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDevice.cs @@ -0,0 +1,63 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer chroma link. + /// + public class RazerChromaLinkRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the chroma link. + internal RazerChromaLinkRGBDevice(RazerChromaLinkRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + $@"Layouts\Razer\ChromaLink\{model}.xml"), null, PathHelper.GetAbsolutePath(@"Images\Razer\ChromaLink")); + + if (LedMapping.Count == 0) + for (int i = 0; i < _Defines.CHROMALINK_MAX_LEDS; i++) + InitializeLed(new RazerLedId(this, i), new Rectangle(i * 11, 0, 10, 10)); + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.CHROMALINK_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _ChromaLinkCustomEffect effectParams = new _ChromaLinkCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDeviceInfo.cs new file mode 100644 index 0000000..34746d5 --- /dev/null +++ b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDeviceInfo.cs @@ -0,0 +1,32 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerChromaLinkRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + internal RazerChromaLinkRGBDeviceInfo(Guid deviceId, string model) + : base(deviceId, RGBDeviceType.LedStripe, model) + { + string modelName = Model.Replace(" ", string.Empty).ToUpper(); + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\ChromaLinks\{modelName}.png"), UriKind.Absolute); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Enum/DeviceType.cs b/RGB.NET.Devices.Razer/Enum/DeviceType.cs new file mode 100644 index 0000000..d262656 --- /dev/null +++ b/RGB.NET.Devices.Razer/Enum/DeviceType.cs @@ -0,0 +1,13 @@ +namespace RGB.NET.Devices.Razer +{ + internal enum DeviceType + { + Keyboard = 1, + Mouse = 2, + Headset = 3, + Mousepad = 4, + Keypad = 5, + System = 6, + Invalid + } +} diff --git a/RGB.NET.Devices.Razer/Enum/RazerError.cs b/RGB.NET.Devices.Razer/Enum/RazerError.cs new file mode 100644 index 0000000..06321a7 --- /dev/null +++ b/RGB.NET.Devices.Razer/Enum/RazerError.cs @@ -0,0 +1,93 @@ +namespace RGB.NET.Devices.Razer +{ + /// + /// Razer-SDK: Error codes for Chroma SDK. If the error is not defined here, refer to WinError.h from the Windows SDK. + /// + public enum RazerError : long + { + /// + /// Razer-SDK: Invalid. + /// + Invalid = -1, + + /// + /// Razer-SDK: Success. + /// + Success = 0, + + /// + /// Razer-SDK: Access denied. + /// + AccessDenied = 5, + + /// + /// Razer-SDK: Invalid handle. + /// + InvalidHandle = 6, + + /// + /// Razer-SDK: Not supported. + /// + NotSupported = 50, + + /// + /// Razer-SDK: Invalid parameter. + /// + InvalidParameter = 87, + + /// + /// Razer-SDK: The service has not been started. + /// + ServiceNotActive = 1062, + + /// + /// Razer-SDK: Cannot start more than one instance of the specified program. + /// + SingleInstanceApp = 1152, + + /// + /// Razer-SDK: Device not connected. + /// + DeviceNotConnected = 1167, + + /// + /// Razer-SDK: Element not found. + /// + NotFound = 1168, + + /// + /// Razer-SDK: Request aborted. + /// + RequestAborted = 1235, + + /// + /// Razer-SDK: An attempt was made to perform an initialization operation when initialization has already been completed. + /// + AlreadyInitialized = 1247, + + /// + /// Razer-SDK: Resource not available or disabled. + /// + ResourceDisabled = 4309, + + /// + /// Razer-SDK: Device not available or supported. + /// + DeviceNotAvailable = 4319, + + /// + /// Razer-SDK: The group or resource is not in the correct state to perform the requested operation. + /// + NotValidState = 5023, + + /// + /// Razer-SDK: No more items. + /// + NoMoreItems = 259, + + /// + /// Razer-SDK: General failure. + /// + Failed = 2147500037 + } +} diff --git a/RGB.NET.Devices.Razer/Enum/RazerLogicalKeyboardLayout.cs b/RGB.NET.Devices.Razer/Enum/RazerLogicalKeyboardLayout.cs new file mode 100644 index 0000000..1ec9382 --- /dev/null +++ b/RGB.NET.Devices.Razer/Enum/RazerLogicalKeyboardLayout.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.Razer +{ + /// + /// Contains list of available logical layouts for razer keyboards. + /// + public enum RazerLogicalKeyboardLayout + { + TODO + }; +} diff --git a/RGB.NET.Devices.Razer/Enum/RazerPhysicalKeyboardLayout.cs b/RGB.NET.Devices.Razer/Enum/RazerPhysicalKeyboardLayout.cs new file mode 100644 index 0000000..090a2db --- /dev/null +++ b/RGB.NET.Devices.Razer/Enum/RazerPhysicalKeyboardLayout.cs @@ -0,0 +1,15 @@ +// ReSharper disable UnusedMember.Global +// ReSharper disable InconsistentNaming + +#pragma warning disable 1591 // Missing XML comment for publicly visible type or member + +namespace RGB.NET.Devices.Razer +{ + /// + /// Contains list of available physical layouts for razer keyboards. + /// + public enum RazerPhysicalKeyboardLayout + { + TODO + } +} diff --git a/RGB.NET.Devices.Razer/Exceptions/RazerException.cs b/RGB.NET.Devices.Razer/Exceptions/RazerException.cs new file mode 100644 index 0000000..edd6bac --- /dev/null +++ b/RGB.NET.Devices.Razer/Exceptions/RazerException.cs @@ -0,0 +1,37 @@ +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable MemberCanBePrivate.Global + +using System; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents an exception thrown by the Razer-SDK. + /// + public class RazerException : ApplicationException + { + #region Properties & Fields + + /// + /// Gets the error code provided by the SDK. + /// + public RazerError ErrorCode { get; } + + #endregion + + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The error code provided by the SDK. + public RazerException(RazerError errorCode) + { + this.ErrorCode = errorCode; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Generic/Devices.cs b/RGB.NET.Devices.Razer/Generic/Devices.cs new file mode 100644 index 0000000..a167aa9 --- /dev/null +++ b/RGB.NET.Devices.Razer/Generic/Devices.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +namespace RGB.NET.Devices.Razer +{ + internal class Devices + { + public static readonly List<(Guid guid, string model)> KEYBOARDS = new List<(Guid guid, string model)> + { + (new Guid("2EA1BB63-CA28-428D-9F06-196B88330BBB"), "Blackwidow Chroma"), + (new Guid("ED1C1B82-BFBE-418F-B49D-D03F05B149DF"), "Razer Blackwidow Chroma Tournament Edition"), + (new Guid("18C5AD9B-4326-4828-92C4-2669A66D2283"), "Razer Deathstalker "), + (new Guid("872AB2A9-7959-4478-9FED-15F6186E72E4"), "Overwatch Keyboard"), + (new Guid("5AF60076-ADE9-43D4-B574-52599293B554"), "Razer Blackwidow X Chroma"), + (new Guid("2D84DD51-3290-4AAC-9A89-D8AFDE38B57C"), "Razer Blackwidow X TE Chroma"), + (new Guid("803378C1-CC48-4970-8539-D828CC1D420A"), "Razer Omata Chroma"), + (new Guid("C83BDFE8-E7FC-40E0-99DB-872E23F19891"), "Razer Blade Stealth"), + (new Guid("F2BEDFAF-A0FE-4651-9D41-B6CE603A3DDD"), "Razer Blade"), + (new Guid("A73AC338-F0E5-4BF7-91AE-DD1F7E1737A5"), "Razer Blade Pro"), + (new Guid("608E743F-B402-44BD-A7A6-7AA9F574ECF4"), "Razer Blackwidow Chroma v2") + }; + + public static readonly List<(Guid guid, string model)> MICE = new List<(Guid guid, string model)> + { + (new Guid("7EC00450-E0EE-4289-89D5-0D879C19061A"), "Razer Mamba Chroma Tournament Edition"), + (new Guid("AEC50D91-B1F1-452F-8E16-7B73F376FDF3"), "Razer Deathadder Chroma "), + (new Guid("FF8A5929-4512-4257-8D59-C647BF9935D0"), "Razer Diamondback"), + (new Guid("D527CBDC-EB0A-483A-9E89-66D50463EC6C"), "Razer Mamba"), + (new Guid("D714C50B-7158-4368-B99C-601ACB985E98"), "Razer Naga Epic"), + (new Guid("F1876328-6CA4-46AE-BE04-BE812B414433"), "Razer Naga"), + (new Guid("52C15681-4ECE-4DD9-8A52-A1418459EB34"), "Razer Orochi Chroma"), + (new Guid("195D70F5-F285-4CFF-99F2-B8C0E9658DB4"), "Razer Naga Hex Chroma"), + (new Guid("77834867-3237-4A9F-AD77-4A46C4183003"), "Razer DeathAdder Elite Chroma") + }; + + public static readonly List<(Guid guid, string model)> HEADSETS = new List<(Guid guid, string model)> + { + (new Guid("DF3164D7-5408-4A0E-8A7F-A7412F26BEBF"), "Razer ManO'War"), + (new Guid("CD1E09A5-D5E6-4A6C-A93B-E6D9BF1D2092"), "Razer Kraken 7.1 Chroma"), + (new Guid("7FB8A36E-9E74-4BB3-8C86-CAC7F7891EBD"), "Razer Kraken 7.1 Chroma Refresh") + }; + + public static readonly List<(Guid guid, string model)> MOUSEMATS = new List<(Guid guid, string model)> + { + (new Guid("80F95A94-73D2-48CA-AE9A-0986789A9AF2"), "Razer Firefly") + }; + + public static readonly List<(Guid guid, string model)> KEYPADS = new List<(Guid guid, string model)> + { + (new Guid("9D24B0AB-0162-466C-9640-7A924AA4D9FD"), "Razer Orbweaver"), + (new Guid("00F0545C-E180-4AD1-8E8A-419061CE505E"), "Razer Tartarus") + }; + + public static readonly List<(Guid guid, string model)> CHROMALINKS = new List<(Guid guid, string model)> + { + (new Guid("0201203B-62F3-4C50-83DD-598BABD208E0"), "Core Chroma"), + (new Guid("35F6F18D-1AE5-436C-A575-AB44A127903A"), "Lenovo Y900"), + (new Guid("47DB1FA7-6B9B-4EE6-B6F4-4071A3B2053B"), "Lenovo Y27") + }; + } +} diff --git a/RGB.NET.Devices.Razer/Generic/IRazerRGBDevice.cs b/RGB.NET.Devices.Razer/Generic/IRazerRGBDevice.cs new file mode 100644 index 0000000..879537f --- /dev/null +++ b/RGB.NET.Devices.Razer/Generic/IRazerRGBDevice.cs @@ -0,0 +1,13 @@ +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// Represents a razer RGB-device. + /// + internal interface IRazerRGBDevice : IRGBDevice + { + void Initialize(); + void Reset(); + } +} diff --git a/RGB.NET.Devices.Razer/Generic/RazerLedId.cs b/RGB.NET.Devices.Razer/Generic/RazerLedId.cs new file mode 100644 index 0000000..d8fc56d --- /dev/null +++ b/RGB.NET.Devices.Razer/Generic/RazerLedId.cs @@ -0,0 +1,94 @@ +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a Id of a on a . + /// + public class RazerLedId : ILedId + { + #region Properties & Fields + + internal int Index { get; } + + /// + public IRGBDevice Device { get; } + + /// + public bool IsValid => true; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The the belongs to. + /// The index representing the led-location in the grid. + public RazerLedId(IRGBDevice device, int index) + { + this.Device = device; + this.Index = index; + } + + #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() => Index.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) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (!(obj is RazerLedId other)) return false; + + return (Index == other.Index) && Equals(Device, other.Device); + } + + /// + /// Returns a hash code for this . + /// + /// An integer value that specifies the hash code for this . + public override int GetHashCode() + { + unchecked + { + return (Index * 397) ^ (Device != null ? Device.GetHashCode() : 0); + } + } + + #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 ==(RazerLedId ledId1, RazerLedId ledId2) => 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 !=(RazerLedId ledId1, RazerLedId ledId2) => !(ledId1 == ledId2); + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Generic/RazerRGBDevice.cs b/RGB.NET.Devices.Razer/Generic/RazerRGBDevice.cs new file mode 100644 index 0000000..66cd55c --- /dev/null +++ b/RGB.NET.Devices.Razer/Generic/RazerRGBDevice.cs @@ -0,0 +1,143 @@ +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.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// + /// Represents a generic razer-device. (keyboard, mouse, headset, mousepad). + /// + public abstract class RazerRGBDevice : AbstractRGBDevice, IRazerRGBDevice + where TDeviceInfo : RazerRGBDeviceInfo + { + #region Properties & Fields + + private Guid? _lastEffect; + + /// + /// + /// Gets information about the . + /// + public override TDeviceInfo DeviceInfo { get; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The generic information provided by razer for the device. + protected RazerRGBDevice(TDeviceInfo info) + { + this.DeviceInfo = info; + } + + #endregion + + #region Methods + + /// + /// Initializes the device. + /// + public void Initialize() + { + 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); + } + } + + /// + /// 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)); + + Size = new Size(layout.Width, layout.Height); + + if (layout.Leds != null) + foreach (LedLayout layoutLed in layout.Leds) + { + if (Enum.TryParse(layoutLed.Id, true, out int ledIndex)) + { + if (LedMapping.TryGetValue(new RazerLedId(this, ledIndex), out Led led)) + { + led.LedRectangle.Location = new Point(layoutLed.X, layoutLed.Y); + led.LedRectangle.Size = new Size(layoutLed.Width, 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) return; + + IntPtr effectParams = CreateEffectParams(leds); + Guid effectId = Guid.NewGuid(); + _RazerSDK.CreateEffect(DeviceInfo.DeviceId, _Defines.EFFECT_ID, effectParams, ref effectId); + + _RazerSDK.SetEffect(effectId); + + if (_lastEffect.HasValue) + _RazerSDK.DeleteEffect(_lastEffect.Value); + + _lastEffect = effectId; + } + + /// + /// Creates the device-specific effect parameters for the led-update. + /// + /// The leds to be updated. + /// An pointing to the effect parameter struct. + protected abstract IntPtr CreateEffectParams(IEnumerable leds); + + /// + /// Resets the device. + /// + public void Reset() + { + if (_lastEffect.HasValue) + { + _RazerSDK.DeleteEffect(_lastEffect.Value); + _lastEffect = null; + } + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Generic/RazerRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Generic/RazerRGBDeviceInfo.cs new file mode 100644 index 0000000..84b307e --- /dev/null +++ b/RGB.NET.Devices.Razer/Generic/RazerRGBDeviceInfo.cs @@ -0,0 +1,56 @@ +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a Razer-. + /// + public class RazerRGBDeviceInfo : IRGBDeviceInfo + { + #region Properties & Fields + + /// + /// Gets the Id of the . + /// + public Guid DeviceId { get; } + + /// + public RGBDeviceType DeviceType { get; } + + /// + public string Manufacturer => "Razer"; + + /// + public string Model { get; } + + /// + public Uri Image { get; protected set; } + + /// + public bool SupportsSyncBack => false; + + /// + public RGBDeviceLighting Lighting => RGBDeviceLighting.Key; + + #endregion + + #region Constructors + + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The type of the . + /// The model of the . + internal RazerRGBDeviceInfo(Guid deviceId, RGBDeviceType deviceType, string model) + { + this.DeviceId = deviceId; + this.DeviceType = deviceType; + this.Model = model; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDevice.cs b/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDevice.cs new file mode 100644 index 0000000..a62cb07 --- /dev/null +++ b/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDevice.cs @@ -0,0 +1,63 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer headset. + /// + public class RazerHeadsetRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the headset. + internal RazerHeadsetRGBDevice(RazerHeadsetRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + $@"Layouts\Razer\Headset\{model}.xml"), null, PathHelper.GetAbsolutePath(@"Images\Razer\Headset")); + + if (LedMapping.Count == 0) + for (int i = 0; i < _Defines.HEADSET_MAX_LEDS; i++) + InitializeLed(new RazerLedId(this, i), new Rectangle(i * 11, 0, 10, 10)); + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.HEADSET_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _HeadsetCustomEffect effectParams = new _HeadsetCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDeviceInfo.cs new file mode 100644 index 0000000..34604cb --- /dev/null +++ b/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDeviceInfo.cs @@ -0,0 +1,32 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerHeadsetRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + internal RazerHeadsetRGBDeviceInfo(Guid deviceId, string model) + : base(deviceId, RGBDeviceType.Headset, model) + { + string modelName = Model.Replace(" ", string.Empty).ToUpper(); + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\Headsets\{modelName}.png"), UriKind.Absolute); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDevice.cs b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDevice.cs new file mode 100644 index 0000000..51a7fae --- /dev/null +++ b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDevice.cs @@ -0,0 +1,67 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer keyboard. + /// + public class RazerKeyboardRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the keyboard. + internal RazerKeyboardRGBDevice(RazerKeyboardRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + //string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + //ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + // $@"Layouts\Razer\Keyboards\{model}\{DeviceInfo.PhysicalLayout.ToString().ToUpper()}.xml"), + // DeviceInfo.LogicalLayout.ToString(), PathHelper.GetAbsolutePath(@"Images\Razer\Keyboards")); + + if (LedMapping.Count == 0) + { + for (int i = 0; i < _Defines.KEYBOARD_MAX_ROW; i++) + for (int j = 0; j < _Defines.KEYBOARD_MAX_COLUMN; j++) + InitializeLed(new RazerLedId(this, (i * _Defines.KEYBOARD_MAX_COLUMN) + j), new Rectangle(j * 20, i * 20, 19, 19)); + } + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.KEYBOARD_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _KeyboardCustomEffect effectParams = new _KeyboardCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDeviceInfo.cs new file mode 100644 index 0000000..d9173fc --- /dev/null +++ b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDeviceInfo.cs @@ -0,0 +1,66 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Globalization; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerKeyboardRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Properties & Fields + + /// + /// Gets the physical layout of the keyboard. + /// + public RazerPhysicalKeyboardLayout PhysicalLayout { get; private set; } + + /// + /// Gets the logical layout of the keyboard as set in CUE settings. + /// + public RazerLogicalKeyboardLayout LogicalLayout { get; private set; } + + #endregion + + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + /// The of the layout this keyboard is using. + internal RazerKeyboardRGBDeviceInfo(Guid deviceId, string model, CultureInfo culture) + : base(deviceId, RGBDeviceType.Keyboard, model) + { + SetLayouts(culture.KeyboardLayoutId); + + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\Keyboards\{Model.Replace(" ", string.Empty).ToUpper()}.png"), + UriKind.Absolute); + } + + #endregion + + #region Methods + + private void SetLayouts(int keyboardLayoutId) + { + switch (keyboardLayoutId) + { + //TODO DarthAffe 07.10.2017: Implement + default: + PhysicalLayout = RazerPhysicalKeyboardLayout.TODO; + LogicalLayout = RazerLogicalKeyboardLayout.TODO; + break; + } + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDevice.cs b/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDevice.cs new file mode 100644 index 0000000..2c528ee --- /dev/null +++ b/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDevice.cs @@ -0,0 +1,66 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer keypad. + /// + public class RazerKeypadRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the keypad. + internal RazerKeypadRGBDevice(RazerKeypadRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + $@"Layouts\Razer\Keypad\{model}.xml"), null, PathHelper.GetAbsolutePath(@"Images\Razer\Keypad")); + + if (LedMapping.Count == 0) + { + for (int i = 0; i < _Defines.KEYPAD_MAX_ROW; i++) + for (int j = 0; j < _Defines.KEYPAD_MAX_COLUMN; j++) + InitializeLed(new RazerLedId(this, (i * _Defines.KEYPAD_MAX_COLUMN) + j), new Rectangle(j * 20, i * 20, 19, 19)); + } + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.KEYPAD_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _KeypadCustomEffect effectParams = new _KeypadCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDeviceInfo.cs new file mode 100644 index 0000000..a41ccc5 --- /dev/null +++ b/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDeviceInfo.cs @@ -0,0 +1,32 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerKeypadRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + internal RazerKeypadRGBDeviceInfo(Guid deviceId, string model) + : base(deviceId, RGBDeviceType.Keypad, model) + { + string modelName = Model.Replace(" ", string.Empty).ToUpper(); + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\Keypads\{modelName}.png"), UriKind.Absolute); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Layouts/DeviceLayout.xsd b/RGB.NET.Devices.Razer/Layouts/DeviceLayout.xsd new file mode 100644 index 0000000..92690b3 --- /dev/null +++ b/RGB.NET.Devices.Razer/Layouts/DeviceLayout.xsd @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDevice.cs b/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDevice.cs new file mode 100644 index 0000000..944d00c --- /dev/null +++ b/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDevice.cs @@ -0,0 +1,66 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer mouse. + /// + public class RazerMouseRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the mouse. + internal RazerMouseRGBDevice(RazerMouseRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + $@"Layouts\Razer\Mice\{model}.xml"), null, PathHelper.GetAbsolutePath(@"Images\Razer\Mice")); + + if (LedMapping.Count == 0) + { + for (int i = 0; i < _Defines.MOUSE_MAX_ROW; i++) + for (int j = 0; j < _Defines.MOUSE_MAX_COLUMN; j++) + InitializeLed(new RazerLedId(this, (i * _Defines.MOUSE_MAX_COLUMN) + j), new Rectangle(j * 11, i * 11, 10, 10)); + } + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.MOUSE_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _MouseCustomEffect effectParams = new _MouseCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDeviceInfo.cs new file mode 100644 index 0000000..72782d6 --- /dev/null +++ b/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDeviceInfo.cs @@ -0,0 +1,32 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerMouseRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + internal RazerMouseRGBDeviceInfo(Guid deviceId, string model) + : base(deviceId, RGBDeviceType.Mouse, model) + { + string modelName = Model.Replace(" ", string.Empty).ToUpper(); + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\Mice\{modelName}.png"), UriKind.Absolute); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDevice.cs b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDevice.cs new file mode 100644 index 0000000..e42b97d --- /dev/null +++ b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDevice.cs @@ -0,0 +1,63 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer mousepad. + /// + public class RazerMousepadRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the mousepad. + internal RazerMousepadRGBDevice(RazerMousepadRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + $@"Layouts\Razer\Mousepad\{model}.xml"), null, PathHelper.GetAbsolutePath(@"Images\Razer\Mousepad")); + + if (LedMapping.Count == 0) + for (int i = 0; i < _Defines.MOUSEPAD_MAX_LEDS; i++) + InitializeLed(new RazerLedId(this, i), new Rectangle(i * 11, 0, 10, 10)); + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.MOUSEPAD_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _MousepadCustomEffect effectParams = new _MousepadCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDeviceInfo.cs new file mode 100644 index 0000000..b21d49a --- /dev/null +++ b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDeviceInfo.cs @@ -0,0 +1,32 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerMousepadRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + internal RazerMousepadRGBDeviceInfo(Guid deviceId, string model) + : base(deviceId, RGBDeviceType.Mousepad, model) + { + string modelName = Model.Replace(" ", string.Empty).ToUpper(); + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\Mousepads\{modelName}.png"), UriKind.Absolute); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Native/_ChromaLinkCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_ChromaLinkCustomEffect.cs new file mode 100644 index 0000000..267dbe0 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_ChromaLinkCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _ChromaLinkCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.CHROMALINK_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_Color.cs b/RGB.NET.Devices.Razer/Native/_Color.cs new file mode 100644 index 0000000..c3c5794 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_Color.cs @@ -0,0 +1,30 @@ +#pragma warning disable 169 // Field 'x' is never used +#pragma warning disable 414 // Field 'x' is assigned but its value never used +#pragma warning disable 649 // Field 'x' is never assigned +// ReSharper disable MemberCanBePrivate.Global + +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + // ReSharper disable once InconsistentNaming + [StructLayout(LayoutKind.Sequential, Size = sizeof(uint))] + internal struct _Color + { + #region Properties & Fields + + public uint Color; + + #endregion + + #region Constructors + + public _Color(byte red, byte green, byte blue) + : this() + { + Color = red + ((uint)green << 8) + ((uint)blue << 16); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Native/_Defines.cs b/RGB.NET.Devices.Razer/Native/_Defines.cs new file mode 100644 index 0000000..2c0c724 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_Defines.cs @@ -0,0 +1,25 @@ +namespace RGB.NET.Devices.Razer.Native +{ + internal static class _Defines + { + internal const int EFFECT_ID = 7; + + internal const int KEYBOARD_MAX_ROW = 6; + internal const int KEYBOARD_MAX_COLUMN = 22; + internal const int KEYBOARD_MAX_LEDS = KEYBOARD_MAX_ROW * KEYBOARD_MAX_COLUMN; + + internal const int MOUSE_MAX_ROW = 9; + internal const int MOUSE_MAX_COLUMN = 7; + internal const int MOUSE_MAX_LEDS = KEYBOARD_MAX_ROW * KEYBOARD_MAX_COLUMN; + + internal const int HEADSET_MAX_LEDS = 5; + + internal const int MOUSEPAD_MAX_LEDS = 15; + + internal const int KEYPAD_MAX_ROW = 4; + internal const int KEYPAD_MAX_COLUMN = 5; + internal const int KEYPAD_MAX_LEDS = KEYPAD_MAX_ROW * KEYPAD_MAX_COLUMN; + + internal const int CHROMALINK_MAX_LEDS = 5; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_DeviceInfo.cs b/RGB.NET.Devices.Razer/Native/_DeviceInfo.cs new file mode 100644 index 0000000..0b465c3 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_DeviceInfo.cs @@ -0,0 +1,27 @@ +#pragma warning disable 169 // Field 'x' is never used +#pragma warning disable 414 // Field 'x' is assigned but its value never used +#pragma warning disable 649 // Field 'x' is never assigned +// ReSharper disable MemberCanBePrivate.Global + +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + // ReSharper disable once InconsistentNaming + /// + /// Razer-SDK: Device info. + /// + [StructLayout(LayoutKind.Sequential)] + internal struct _DeviceInfo + { + /// + /// Razer-SDK: Device types. + /// + internal DeviceType Type; + + /// + /// Razer-SDK: Number of devices connected. + /// + internal int Connected; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_HeadsetCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_HeadsetCustomEffect.cs new file mode 100644 index 0000000..d3bcdb3 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_HeadsetCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _HeadsetCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.HEADSET_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_KeyboardCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_KeyboardCustomEffect.cs new file mode 100644 index 0000000..5e9294f --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_KeyboardCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _KeyboardCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.KEYBOARD_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_KeypadCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_KeypadCustomEffect.cs new file mode 100644 index 0000000..a08a175 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_KeypadCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _KeypadCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.KEYPAD_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_MouseCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_MouseCustomEffect.cs new file mode 100644 index 0000000..a5e1599 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_MouseCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _MouseCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.MOUSE_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_MousepadCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_MousepadCustomEffect.cs new file mode 100644 index 0000000..01368bf --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_MousepadCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _MousepadCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.MOUSEPAD_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_RazerSDK.cs b/RGB.NET.Devices.Razer/Native/_RazerSDK.cs new file mode 100644 index 0000000..af6310b --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_RazerSDK.cs @@ -0,0 +1,146 @@ +// ReSharper disable UnusedMethodReturnValue.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using RGB.NET.Core.Exceptions; + +namespace RGB.NET.Devices.Razer.Native +{ + // ReSharper disable once InconsistentNaming + internal static class _RazerSDK + { + #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() + { + UnloadRazerSDK(); + LoadRazerSDK(); + } + + private static void LoadRazerSDK() + { + if (_dllHandle != IntPtr.Zero) return; + + // HACK: Load library at runtime to support both, x86 and x64 with one managed dll + List possiblePathList = Environment.Is64BitProcess ? RazerDeviceProvider.PossibleX64NativePaths : RazerDeviceProvider.PossibleX86NativePaths; + string dllPath = possiblePathList.FirstOrDefault(File.Exists); + if (dllPath == null) throw new RGBDeviceException($"Can't find the Razer-SDK at one of the expected locations:\r\n '{string.Join("\r\n", possiblePathList.Select(Path.GetFullPath))}'"); + + _dllHandle = LoadLibrary(dllPath); + + _initPointer = (InitPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "Init"), typeof(InitPointer)); + _unInitPointer = (UnInitPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "UnInit"), typeof(UnInitPointer)); + _queryDevicePointer = (QueryDevicePointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "QueryDevice"), typeof(QueryDevicePointer)); + _createEffectPointer = (CreateEffectPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "CreateEffect"), typeof(CreateEffectPointer)); + _setEffectPointer = (SetEffectPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "SetEffect"), typeof(SetEffectPointer)); + _deleteEffectPointer = (DeleteEffectPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "DeleteEffect"), typeof(DeleteEffectPointer)); + } + + private static void UnloadRazerSDK() + { + if (_dllHandle == IntPtr.Zero) return; + + // ReSharper disable once EmptyEmbeddedStatement - DarthAffe 09.11.2017: 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 InitPointer _initPointer; + private static UnInitPointer _unInitPointer; + private static QueryDevicePointer _queryDevicePointer; + private static CreateEffectPointer _createEffectPointer; + private static SetEffectPointer _setEffectPointer; + private static DeleteEffectPointer _deleteEffectPointer; + + #endregion + + #region Delegates + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError InitPointer(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError UnInitPointer(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError QueryDevicePointer(Guid deviceId, IntPtr deviceInfo); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError CreateEffectPointer(Guid deviceId, int effectType, IntPtr param, ref Guid effectId); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError SetEffectPointer(Guid effectId); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError DeleteEffectPointer(Guid effectId); + + #endregion + + // ReSharper disable EventExceptionNotDocumented + + /// + /// Razer-SDK: Initialize Chroma SDK. + /// + internal static RazerError Init() => _initPointer(); + + /// + /// Razer-SDK: UnInitialize Chroma SDK. + /// + internal static RazerError UnInit() => _unInitPointer(); + + /// + /// Razer-SDK: Query for device information. + /// + internal static RazerError QueryDevice(Guid deviceId, out _DeviceInfo deviceInfo) + { + int structSize = Marshal.SizeOf(typeof(_DeviceInfo)); + IntPtr deviceInfoPtr = Marshal.AllocHGlobal(structSize); + + RazerError error = _queryDevicePointer(deviceId, deviceInfoPtr); + + deviceInfo = (_DeviceInfo)Marshal.PtrToStructure(deviceInfoPtr, typeof(_DeviceInfo)); + Marshal.FreeHGlobal(deviceInfoPtr); + + return error; + } + + internal static RazerError CreateEffect(Guid deviceId, int effectType, IntPtr param, ref Guid effectId) => _createEffectPointer(deviceId, effectType, param, ref effectId); + + internal static RazerError SetEffect(Guid effectId) => _setEffectPointer(effectId); + + internal static RazerError DeleteEffect(Guid effectId) => _deleteEffectPointer(effectId); + + // ReSharper restore EventExceptionNotDocumented + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Properties/AssemblyInfo.cs b/RGB.NET.Devices.Razer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..220a7ac --- /dev/null +++ b/RGB.NET.Devices.Razer/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RGB.NET.Devices.Razer")] +[assembly: AssemblyDescription("Razer-Device-Implementations of RGB.NET")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Wyrez")] +[assembly: AssemblyProduct("RGB.NET.Devices.Razer")] +[assembly: AssemblyCopyright("Copyright © Wyrez 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("24ff4acb-d679-4b2d-86d4-50ab6c02d816")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj b/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj new file mode 100644 index 0000000..11919f7 --- /dev/null +++ b/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj @@ -0,0 +1,105 @@ + + + + + Debug + AnyCPU + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816} + Library + Properties + RGB.NET.Devices.Razer + RGB.NET.Devices.Razer + v4.5 + 512 + + + true + full + false + ..\bin\ + DEBUG;TRACE + prompt + 4 + ..\bin\RGB.NET.Devices.Razer.xml + + + pdbonly + true + ..\bin\ + TRACE + prompt + 4 + ..\bin\RGB.NET.Devices.Razer.xml + + + + + + ..\packages\System.ValueTuple.4.3.1\lib\netstandard1.0\System.ValueTuple.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {5a4f9a75-75fe-47cd-90e5-914d5b20d232} + RGB.NET.Core + + + + + + + + + Designer + + + + + + + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj.DotSettings b/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj.DotSettings new file mode 100644 index 0000000..1e5aa14 --- /dev/null +++ b/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj.DotSettings @@ -0,0 +1,11 @@ + + True + True + True + True + True + True + True + True + True + True \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/RazerDeviceProvider.cs b/RGB.NET.Devices.Razer/RazerDeviceProvider.cs new file mode 100644 index 0000000..1114dd0 --- /dev/null +++ b/RGB.NET.Devices.Razer/RazerDeviceProvider.cs @@ -0,0 +1,218 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Linq; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a device provider responsible for razer devices. + /// + public class RazerDeviceProvider : IRGBDeviceProvider + { + #region Properties & Fields + + private static RazerDeviceProvider _instance; + /// + /// Gets the singleton instance. + /// + public static RazerDeviceProvider Instance => _instance ?? new RazerDeviceProvider(); + + /// + /// 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/RzChromaSDK.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/RzChromaSDK.dll", "x64/RzChromaSDK64.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 => _RazerSDK.LoadedArchitecture; + + /// + /// + /// Gets whether the application has exclusive access to the SDK or not. + /// + public bool HasExclusiveAccess => false; + + /// + 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; + + /// + /// Forces to load the devices represented by the emulator even if they aren't reported to exist. + /// + public bool LoadEmulatorDevices { get; set; } = false; + + #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 RazerDeviceProvider() + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(RazerDeviceProvider)}"); + _instance = this; + } + + #endregion + + #region Methods + + /// + public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool exclusiveAccessIfPossible = false, bool throwExceptions = false) + { + if (IsInitialized) + TryUnInit(); + + IsInitialized = false; + + try + { + _RazerSDK.Reload(); + + RazerError error; + if ((error = _RazerSDK.Init()) != RazerError.Success) + ThrowRazerError(error); + + IList devices = new List(); + + if (loadFilter.HasFlag(RGBDeviceType.Keyboard)) + foreach ((Guid guid, string model) in Razer.Devices.KEYBOARDS) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.KEYBOARDS.FirstOrDefault().guid != guid))) continue; + + RazerKeyboardRGBDevice device = new RazerKeyboardRGBDevice(new RazerKeyboardRGBDeviceInfo(guid, model, GetCulture())); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + if (loadFilter.HasFlag(RGBDeviceType.Mouse)) + foreach ((Guid guid, string model) in Razer.Devices.MICE) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.MICE.FirstOrDefault().guid != guid))) continue; + + RazerMouseRGBDevice device = new RazerMouseRGBDevice(new RazerMouseRGBDeviceInfo(guid, model)); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + if (loadFilter.HasFlag(RGBDeviceType.Headset)) + foreach ((Guid guid, string model) in Razer.Devices.HEADSETS) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.HEADSETS.FirstOrDefault().guid != guid))) continue; + + RazerHeadsetRGBDevice device = new RazerHeadsetRGBDevice(new RazerHeadsetRGBDeviceInfo(guid, model)); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + if (loadFilter.HasFlag(RGBDeviceType.Mousepad)) + foreach ((Guid guid, string model) in Razer.Devices.MOUSEMATS) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.MOUSEMATS.FirstOrDefault().guid != guid))) continue; + + RazerMousepadRGBDevice device = new RazerMousepadRGBDevice(new RazerMousepadRGBDeviceInfo(guid, model)); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + if (loadFilter.HasFlag(RGBDeviceType.Keypad)) + foreach ((Guid guid, string model) in Razer.Devices.KEYPADS) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.KEYPADS.FirstOrDefault().guid != guid))) continue; + + RazerKeypadRGBDevice device = new RazerKeypadRGBDevice(new RazerKeypadRGBDeviceInfo(guid, model)); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + if (loadFilter.HasFlag(RGBDeviceType.Keyboard)) + foreach ((Guid guid, string model) in Razer.Devices.CHROMALINKS) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.CHROMALINKS.FirstOrDefault().guid != guid))) continue; + + RazerChromaLinkRGBDevice device = new RazerChromaLinkRGBDevice(new RazerChromaLinkRGBDeviceInfo(guid, model)); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + Devices = new ReadOnlyCollection(devices); + IsInitialized = true; + } + catch + { + TryUnInit(); + if (throwExceptions) throw; + return false; + } + + return true; + } + + /// + public void ResetDevices() + { + foreach (IRGBDevice device in Devices) + ((IRazerRGBDevice)device).Reset(); + } + + private void ThrowRazerError(RazerError errorCode) => throw new RazerException(errorCode); + + private void TryUnInit() + { + try + { + _RazerSDK.UnInit(); + } + catch { /* We tried our best */ } + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/libs/x64/RzChromaSDK.dll b/RGB.NET.Devices.Razer/libs/x64/RzChromaSDK.dll new file mode 100644 index 0000000..0ec6585 Binary files /dev/null and b/RGB.NET.Devices.Razer/libs/x64/RzChromaSDK.dll differ diff --git a/RGB.NET.Devices.Razer/libs/x86/RzChromaSDK.dll b/RGB.NET.Devices.Razer/libs/x86/RzChromaSDK.dll new file mode 100644 index 0000000..fa948a2 Binary files /dev/null and b/RGB.NET.Devices.Razer/libs/x86/RzChromaSDK.dll differ diff --git a/RGB.NET.Devices.Razer/packages.config b/RGB.NET.Devices.Razer/packages.config new file mode 100644 index 0000000..d352211 --- /dev/null +++ b/RGB.NET.Devices.Razer/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/targets/RGB.NET.Devices.Razer.targets b/RGB.NET.Devices.Razer/targets/RGB.NET.Devices.Razer.targets new file mode 100644 index 0000000..6338df1 --- /dev/null +++ b/RGB.NET.Devices.Razer/targets/RGB.NET.Devices.Razer.targets @@ -0,0 +1,150 @@ + + + + + + + + + + + False + + + False + + + + + + + + %(RecursiveDir)%(FileName)%(Extension) + Always + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bin\%(RecursiveDir)%(Filename)%(Extension) + + + + + + + + + + + + + + + + + + + + + + + $(PostBuildEventDependsOn); + CopyRazerSDKFiles; + + + $(BuildDependsOn); + CopyRazerSDKFiles; + + + $(CleanDependsOn); + CleanRazerSDKFiles; + + + + + + + + CollectRazerSDKFiles; + $(PipelineCollectFilesPhaseDependsOn); + + + diff --git a/RGB.NET.sln b/RGB.NET.sln index 5944be8..1869c69 100644 --- a/RGB.NET.sln +++ b/RGB.NET.sln @@ -34,12 +34,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{06416566 NuGet\RGB.NET.Devices.Logitech.nuspec = NuGet\RGB.NET.Devices.Logitech.nuspec NuGet\RGB.NET.Devices.Msi.nuspec = NuGet\RGB.NET.Devices.Msi.nuspec NuGet\RGB.NET.Devices.Novation.nuspec = NuGet\RGB.NET.Devices.Novation.nuspec - NuGet\RGB.NET.Devices.nuspec = NuGet\RGB.NET.Devices.nuspec + NuGet\RGB.NET.Devices.Razer.nuspec = NuGet\RGB.NET.Devices.Razer.nuspec NuGet\RGB.NET.Groups.nuspec = NuGet\RGB.NET.Groups.nuspec NuGet\RGB.NET.Input.Corsair.nuspec = NuGet\RGB.NET.Input.Corsair.nuspec NuGet\RGB.NET.Input.nuspec = NuGet\RGB.NET.Input.nuspec - NuGet\RGB.NET.nuspec = NuGet\RGB.NET.nuspec - NuGet\RGB.NET.Presets.nuspec = NuGet\RGB.NET.Presets.nuspec NuGet\RGB.NET.WPF.nuspec = NuGet\RGB.NET.WPF.nuspec EndProjectSection EndProject @@ -70,6 +68,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Metapackages", "Metapackage NuGet\Metapackages\RGB.NET.Presets.nuspec = NuGet\Metapackages\RGB.NET.Presets.nuspec EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RGB.NET.Devices.Razer", "RGB.NET.Devices.Razer\RGB.NET.Devices.Razer.csproj", "{24FF4ACB-D679-4B2D-86D4-50AB6C02D816}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -128,6 +128,10 @@ Global {4EFD77C7-FDB4-4C6B-970C-0EF66D24BE09}.Debug|Any CPU.Build.0 = Debug|Any CPU {4EFD77C7-FDB4-4C6B-970C-0EF66D24BE09}.Release|Any CPU.ActiveCfg = Release|Any CPU {4EFD77C7-FDB4-4C6B-970C-0EF66D24BE09}.Release|Any CPU.Build.0 = Release|Any CPU + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -146,6 +150,7 @@ Global {4F2F3FBD-A1E4-4968-A2AD-0514959E5E59} = {33D5E279-1C4E-4AB6-9D1E-6D18109A6C25} {4EFD77C7-FDB4-4C6B-970C-0EF66D24BE09} = {33D5E279-1C4E-4AB6-9D1E-6D18109A6C25} {C191D5BE-E1B2-47E4-AB39-D954B277188C} = {06416566-481F-4571-80EE-7BB05B1E0299} + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816} = {33D5E279-1C4E-4AB6-9D1E-6D18109A6C25} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CDECA6C7-8D18-4AF3-94F7-C70A69B8571B}