diff --git a/NuGet/RGB.NET.Devices.Logitech.nuspec b/NuGet/RGB.NET.Devices.Logitech.nuspec
new file mode 100644
index 0000000..7c3c9eb
--- /dev/null
+++ b/NuGet/RGB.NET.Devices.Logitech.nuspec
@@ -0,0 +1,31 @@
+
+
+
+ RGB.NET.Devices.Logitech
+ CUE.NET.Devices.Logitech
+ 1.0.0.0
+ Darth Affe
+ Darth Affe
+ https://github.com/DarthAffe/RGB.NET
+ https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE
+ true
+ Logitech-Device-Implementations of RGB.NET
+
+ Logitech-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.Logitech/Enum/LogitechDeviceCaps.cs b/RGB.NET.Devices.Logitech/Enum/LogitechDeviceCaps.cs
new file mode 100644
index 0000000..0d72242
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Enum/LogitechDeviceCaps.cs
@@ -0,0 +1,18 @@
+// ReSharper disable InconsistentNaming
+
+using System;
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace RGB.NET.Devices.Logitech
+{
+ [Flags]
+ public enum LogitechDeviceCaps
+ {
+ None = 0,
+ Monochrome = 1 << 0,
+ DeviceRGB = 1 << 1,
+ PerKeyRGB = 1 << 2,
+ All = Monochrome | DeviceRGB | PerKeyRGB
+ }
+}
diff --git a/RGB.NET.Devices.Logitech/Enum/LogitechLedIds.cs b/RGB.NET.Devices.Logitech/Enum/LogitechLedIds.cs
new file mode 100644
index 0000000..d82f868
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Enum/LogitechLedIds.cs
@@ -0,0 +1,129 @@
+// ReSharper disable InconsistentNaming
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace RGB.NET.Devices.Logitech
+{
+ ///
+ /// Contains list of all LEDs available for all logitech devices.
+ ///
+ public enum LogitechLedIds
+ {
+ Invalid = 0,
+ ESC = 0x01,
+ F1 = 0x3b,
+ F2 = 0x3c,
+ F3 = 0x3d,
+ F4 = 0x3e,
+ F5 = 0x3f,
+ F6 = 0x40,
+ F7 = 0x41,
+ F8 = 0x42,
+ F9 = 0x43,
+ F10 = 0x44,
+ F11 = 0x57,
+ F12 = 0x58,
+ PRINT_SCREEN = 0x137,
+ SCROLL_LOCK = 0x46,
+ PAUSE_BREAK = 0x45,
+ TILDE = 0x29,
+ ONE = 0x02,
+ TWO = 0x03,
+ THREE = 0x04,
+ FOUR = 0x05,
+ FIVE = 0x06,
+ SIX = 0x07,
+ SEVEN = 0x08,
+ EIGHT = 0x09,
+ NINE = 0x0A,
+ ZERO = 0x0B,
+ MINUS = 0x0C,
+ EQUALS = 0x0D,
+ BACKSPACE = 0x0E,
+ INSERT = 0x152,
+ HOME = 0x147,
+ PAGE_UP = 0x149,
+ NUM_LOCK = 0x145,
+ NUM_SLASH = 0x135,
+ NUM_ASTERISK = 0x37,
+ NUM_MINUS = 0x4A,
+ TAB = 0x0F,
+ Q = 0x10,
+ W = 0x11,
+ E = 0x12,
+ R = 0x13,
+ T = 0x14,
+ Y = 0x15,
+ U = 0x16,
+ I = 0x17,
+ O = 0x18,
+ P = 0x19,
+ OPEN_BRACKET = 0x1A,
+ CLOSE_BRACKET = 0x1B,
+ BACKSLASH = 0x2B,
+ KEYBOARD_DELETE = 0x153,
+ END = 0x14F,
+ PAGE_DOWN = 0x151,
+ NUM_SEVEN = 0x47,
+ NUM_EIGHT = 0x48,
+ NUM_NINE = 0x49,
+ NUM_PLUS = 0x4E,
+ CAPS_LOCK = 0x3A,
+ A = 0x1E,
+ S = 0x1F,
+ D = 0x20,
+ F = 0x21,
+ G = 0x22,
+ H = 0x23,
+ J = 0x24,
+ K = 0x25,
+ L = 0x26,
+ SEMICOLON = 0x27,
+ APOSTROPHE = 0x28,
+ ENTER = 0x1C,
+ NUM_FOUR = 0x4B,
+ NUM_FIVE = 0x4C,
+ NUM_SIX = 0x4D,
+ LEFT_SHIFT = 0x2A,
+ Z = 0x2C,
+ X = 0x2D,
+ C = 0x2E,
+ V = 0x2F,
+ B = 0x30,
+ N = 0x31,
+ M = 0x32,
+ COMMA = 0x33,
+ PERIOD = 0x34,
+ FORWARD_SLASH = 0x35,
+ RIGHT_SHIFT = 0x36,
+ ARROW_UP = 0x148,
+ NUM_ONE = 0x4F,
+ NUM_TWO = 0x50,
+ NUM_THREE = 0x51,
+ NUM_ENTER = 0x11C,
+ LEFT_CONTROL = 0x1D,
+ LEFT_WINDOWS = 0x15B,
+ LEFT_ALT = 0x38,
+ SPACE = 0x39,
+ RIGHT_ALT = 0x138,
+ RIGHT_WINDOWS = 0x15C,
+ APPLICATION_SELECT = 0x15D,
+ RIGHT_CONTROL = 0x11D,
+ ARROW_LEFT = 0x14B,
+ ARROW_DOWN = 0x150,
+ ARROW_RIGHT = 0x14D,
+ NUM_ZERO = 0x52,
+ NUM_PERIOD = 0x53,
+ G_1 = 0xFFF1,
+ G_2 = 0xFFF2,
+ G_3 = 0xFFF3,
+ G_4 = 0xFFF4,
+ G_5 = 0xFFF5,
+ G_6 = 0xFFF6,
+ G_7 = 0xFFF7,
+ G_8 = 0xFFF8,
+ G_9 = 0xFFF9,
+ G_LOGO = 0xFFFF1,
+ G_BADGE = 0xFFFF2,
+ }
+}
diff --git a/RGB.NET.Devices.Logitech/Enum/LogitechLogicalKeyboardLayout.cs b/RGB.NET.Devices.Logitech/Enum/LogitechLogicalKeyboardLayout.cs
new file mode 100644
index 0000000..808c12d
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Enum/LogitechLogicalKeyboardLayout.cs
@@ -0,0 +1,32 @@
+// ReSharper disable InconsistentNaming
+// ReSharper disable UnusedMember.Global
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace RGB.NET.Devices.Logitech
+{
+ ///
+ /// Contains list of available logical layouts for logitech keyboards.
+ ///
+ public enum LogitechLogicalKeyboardLayout
+ {
+ US_Int = 1,
+ NA = 2,
+ EU = 3,
+ UK = 4,
+ BE = 5,
+ BR = 6,
+ CH = 7,
+ CN = 8,
+ DE = 9,
+ ES = 10,
+ FR = 11,
+ IT = 12,
+ ND = 13,
+ RU = 14,
+ JP = 15,
+ KR = 16,
+ TW = 17,
+ MEX = 18
+ };
+}
diff --git a/RGB.NET.Devices.Logitech/Enum/LogitechPhysicalKeyboardLayout.cs b/RGB.NET.Devices.Logitech/Enum/LogitechPhysicalKeyboardLayout.cs
new file mode 100644
index 0000000..95e608e
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Enum/LogitechPhysicalKeyboardLayout.cs
@@ -0,0 +1,36 @@
+// ReSharper disable UnusedMember.Global
+// ReSharper disable InconsistentNaming
+
+namespace RGB.NET.Devices.Logitech
+{
+ ///
+ /// Contains list of available physical layouts for logitech keyboards.
+ ///
+ public enum LogitechPhysicalKeyboardLayout
+ {
+ ///
+ /// US-Keyboard
+ ///
+ US = 1,
+
+ ///
+ /// UK-Keyboard
+ ///
+ UK = 2,
+
+ ///
+ /// BR-Keyboard
+ ///
+ BR = 3,
+
+ ///
+ /// JP-Keyboard
+ ///
+ JP = 4,
+
+ ///
+ /// KR-Keyboard
+ ///
+ KR = 5
+ }
+}
diff --git a/RGB.NET.Devices.Logitech/Generic/LogitechLedId.cs b/RGB.NET.Devices.Logitech/Generic/LogitechLedId.cs
new file mode 100644
index 0000000..58a27ed
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Generic/LogitechLedId.cs
@@ -0,0 +1,107 @@
+using System.Diagnostics;
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.Logitech
+{
+ ///
+ /// Represents a Id of a on a .
+ ///
+ [DebuggerDisplay("{" + nameof(LedId) + "}")]
+ public class LogitechLedId : ILedId
+ {
+ #region Properties & Fields
+
+ internal readonly LogitechLedIds LedId;
+
+ ///
+ public IRGBDevice Device { get; }
+
+ ///
+ public bool IsValid => LedId != LogitechLedIds.Invalid;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The the belongs to.
+ /// The of the represented .
+ public LogitechLedId(IRGBDevice device, LogitechLedIds ledId)
+ {
+ this.Device = device;
+ this.LedId = ledId;
+ }
+
+ #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)
+ {
+ LogitechLedId compareLedId = obj as LogitechLedId;
+ 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 ==(LogitechLedId ledId1, LogitechLedId 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 !=(LogitechLedId ledId1, LogitechLedId ledId2)
+ {
+ return !(ledId1 == ledId2);
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Logitech/Generic/LogitechRGBDevice.cs b/RGB.NET.Devices.Logitech/Generic/LogitechRGBDevice.cs
new file mode 100644
index 0000000..8336467
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Generic/LogitechRGBDevice.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using RGB.NET.Core;
+using RGB.NET.Core.Layout;
+using RGB.NET.Devices.Logitech.Native;
+
+namespace RGB.NET.Devices.Logitech
+{
+ ///
+ /// Represents a generic Logitech-device. (keyboard, mouse, headset, mousmat).
+ ///
+ public abstract class LogitechRGBDevice : 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 Logitech for the device.
+ protected LogitechRGBDevice(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.
+ protected void ApplyLayoutFromFile(string layoutPath)
+ {
+ DeviceLayout layout = DeviceLayout.Load(layoutPath);
+ if (layout != null)
+ {
+ InternalSize = new Size(layout.Width, layout.Height);
+
+ if (layout.Leds != null)
+ foreach (LedLayout layoutLed in layout.Leds)
+ {
+ LogitechLedIds ledId;
+ if (Enum.TryParse(layoutLed.Id, true, out ledId))
+ {
+ LogitechLedId id = new LogitechLedId(this, ledId);
+ Led led;
+ if (!LedMapping.TryGetValue(id, out led))
+ led = InitializeLed(id, new Rectangle());
+
+ 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;
+ }
+ }
+ }
+ }
+
+ ///
+ protected override void UpdateLeds(IEnumerable ledsToUpdate)
+ {
+ List leds = ledsToUpdate.Where(x => x.Color.A > 0).ToList();
+
+ foreach (Led led in leds)
+ _LogitechGSDK.LogiLedSetLightingForKeyWithKeyName((int)((LogitechLedId)led.Id).LedId,
+ (int)Math.Round(led.Color.RPercent * 100),
+ (int)Math.Round(led.Color.GPercent * 100),
+ (int)Math.Round(led.Color.BPercent * 100));
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Logitech/Generic/LogitechRGBDeviceInfo.cs b/RGB.NET.Devices.Logitech/Generic/LogitechRGBDeviceInfo.cs
new file mode 100644
index 0000000..9876581
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Generic/LogitechRGBDeviceInfo.cs
@@ -0,0 +1,64 @@
+using System;
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.Logitech
+{
+ ///
+ /// Represents a generic information for a Logitech-.
+ ///
+ public class LogitechRGBDeviceInfo : IRGBDeviceInfo
+ {
+ #region Properties & Fields
+
+ ///
+ public RGBDeviceType DeviceType { get; }
+
+ ///
+ public string Manufacturer => "Logitech";
+
+ ///
+ public string Model { get; }
+
+ ///
+ public Uri Image { get; protected set; }
+
+ ///
+ public RGBDeviceLighting Lighting
+ {
+ get
+ {
+ if (DeviceCaps.HasFlag(LogitechDeviceCaps.PerKeyRGB))
+ return RGBDeviceLighting.Key;
+
+ if (DeviceCaps.HasFlag(LogitechDeviceCaps.DeviceRGB))
+ return RGBDeviceLighting.Keyboard;
+
+ return RGBDeviceLighting.None;
+ }
+ }
+
+ ///
+ /// Gets a flag that describes device capabilities. ()
+ ///
+ public LogitechDeviceCaps DeviceCaps { get; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Internal constructor of managed .
+ ///
+ /// The type of the .
+ /// The represented device model.
+ /// The lighting-capabilities of the device.
+ internal LogitechRGBDeviceInfo(RGBDeviceType deviceType, string model, LogitechDeviceCaps deviceCaps)
+ {
+ this.DeviceType = deviceType;
+ this.Model = model;
+ this.DeviceCaps = deviceCaps;
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Logitech/HID/DeviceChecker.cs b/RGB.NET.Devices.Logitech/HID/DeviceChecker.cs
new file mode 100644
index 0000000..8a753f1
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/HID/DeviceChecker.cs
@@ -0,0 +1,57 @@
+using System.Collections.Generic;
+using HidSharp;
+
+namespace RGB.NET.Devices.Logitech.HID
+{
+ //TODO DarthAffe 04.02.2017: Rewrite this once the SDK supports per-device lighting to get all the devices connected.
+ internal static class DeviceChecker
+ {
+ #region Constants
+
+ //TODO DarthAffe 04.02.2017: Add IDs
+ private const int VENDOR_ID = 0x0;
+ private const int G910_ID = 0x0;
+ private const int G810_ID = 0x0;
+
+ #endregion
+
+ #region Properties & Fields
+
+ public static string ConnectedDeviceModel
+ {
+ get
+ {
+ if (IsG910Connected) return "G910";
+ if (IsG810Connected) return "G810";
+ return null;
+ }
+ }
+
+ public static bool IsDeviceConnected => IsG910Connected || IsG810Connected;
+ public static bool IsG910Connected { get; private set; }
+ public static bool IsG810Connected { get; private set; }
+
+ #endregion
+
+ #region Methods
+
+ internal static void LoadDeviceList()
+ {
+ IsG910Connected = false;
+ IsG810Connected = false;
+
+ HidDeviceLoader loader = new HidDeviceLoader();
+ IEnumerable devices = loader.GetDevices();
+ foreach (HidDevice hidDevice in devices)
+ if (hidDevice.VendorID == VENDOR_ID)
+ {
+ if (hidDevice.ProductID == G910_ID)
+ IsG910Connected = true;
+ else if (hidDevice.ProductID == G810_ID)
+ IsG810Connected = true;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Logitech/Helper/CultureHelper.cs b/RGB.NET.Devices.Logitech/Helper/CultureHelper.cs
new file mode 100644
index 0000000..ee5e787
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Helper/CultureHelper.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Globalization;
+using System.Runtime.InteropServices;
+
+namespace RGB.NET.Devices.Logitech
+{
+ ///
+ /// Offers some helper-methods for culture related things.
+ ///
+ public static class CultureHelper
+ {
+ #region DLLImports
+
+ [DllImport("user32.dll")]
+ static extern IntPtr GetKeyboardLayout(uint thread);
+
+ #endregion
+
+ #region Constructors
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Gets the current keyboard-layout from the OS.
+ ///
+ /// The current keyboard-layout
+ public 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.Logitech/Keyboard/LogitechKeyboardRGBDevice.cs b/RGB.NET.Devices.Logitech/Keyboard/LogitechKeyboardRGBDevice.cs
new file mode 100644
index 0000000..bb9e35e
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Keyboard/LogitechKeyboardRGBDevice.cs
@@ -0,0 +1,49 @@
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable UnusedMember.Global
+
+using System;
+using System.IO;
+using System.Reflection;
+
+namespace RGB.NET.Devices.Logitech
+{
+ ///
+ /// Represents a logitech keyboard.
+ ///
+ public class LogitechKeyboardRGBDevice : LogitechRGBDevice
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets information about the .
+ ///
+ public LogitechKeyboardRGBDeviceInfo KeyboardDeviceInfo { get; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The specific information provided by CUE for the keyboard
+ internal LogitechKeyboardRGBDevice(LogitechKeyboardRGBDeviceInfo info)
+ : base(info)
+ {
+ this.KeyboardDeviceInfo = info;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ protected override void InitializeLayout()
+ {
+ ApplyLayoutFromFile(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
+ $@"Layouts\Logitech\Keyboards\{KeyboardDeviceInfo.Model.Replace(" ", string.Empty).ToUpper()}\{KeyboardDeviceInfo.PhysicalLayout.ToString().ToUpper()}.xml"));
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Logitech/Keyboard/LogitechKeyboardRGBDeviceInfo.cs b/RGB.NET.Devices.Logitech/Keyboard/LogitechKeyboardRGBDeviceInfo.cs
new file mode 100644
index 0000000..1a9d00b
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Keyboard/LogitechKeyboardRGBDeviceInfo.cs
@@ -0,0 +1,63 @@
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable UnusedMember.Global
+
+using System;
+using System.Globalization;
+using System.IO;
+using System.Reflection;
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.Logitech
+{
+ ///
+ /// Represents a generic information for a .
+ ///
+ public class LogitechKeyboardRGBDeviceInfo : LogitechRGBDeviceInfo
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets the physical layout of the keyboard.
+ ///
+ public LogitechPhysicalKeyboardLayout PhysicalLayout { get; private set; }
+
+ ///
+ /// Gets the logical layout of the keyboard.
+ ///
+ public LogitechLogicalKeyboardLayout LogicalLayout { get; private set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Internal constructor of managed .
+ ///
+ /// The type of the .
+ /// The represented device model.
+ /// The lighting-capabilities of the device.
+ /// The of the layout this keyboard is using
+ internal LogitechKeyboardRGBDeviceInfo(RGBDeviceType deviceType, string model, LogitechDeviceCaps deviceCaps, CultureInfo culture)
+ : base(deviceType, model, deviceCaps)
+ {
+ SetLayouts(culture.KeyboardLayoutId);
+
+ Image = new Uri(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
+ $@"Images\Logitech\Keyboards\{Model.Replace(" ", string.Empty).ToUpper()}\{LogicalLayout.ToString().ToUpper()}.png"), UriKind.Absolute);
+ }
+
+ private void SetLayouts(int keyboardLayoutId)
+ {
+ switch (keyboardLayoutId)
+ {
+ //TODO DarthAffe 04.02.2017: Check all available keyboards and there layout-ids
+ default:
+ PhysicalLayout = LogitechPhysicalKeyboardLayout.US;
+ LogicalLayout = LogitechLogicalKeyboardLayout.NA;
+ break;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs b/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs
new file mode 100644
index 0000000..c9c3939
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Globalization;
+using RGB.NET.Core;
+using RGB.NET.Devices.Logitech.HID;
+using RGB.NET.Devices.Logitech.Native;
+
+namespace RGB.NET.Devices.Logitech
+{
+ ///
+ /// Represents a device provider responsible for logitech devices.
+ ///
+ public class LogitechDeviceProvider : IRGBDeviceProvider
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets the singleton instance.
+ ///
+ public static LogitechDeviceProvider Instance { get; } = new LogitechDeviceProvider();
+
+ ///
+ public bool IsInitialized { get; private set; }
+
+ ///
+ /// Gets the loaded architecture (x64/x86).
+ ///
+ public string LoadedArchitecture => _LogitechGSDK.LoadedArchitecture;
+
+ ///
+ public IEnumerable Devices { get; private set; }
+
+ ///
+ public bool HasExclusiveAccess => false; // Exclusive access isn't possible for logitech devices.
+
+ ///
+ /// Gets or sets a function to get the culture for a specific device.
+ ///
+ public Func GetCulture { get; set; } = () => CultureHelper.GetCurrentCulture();
+
+ #endregion
+
+ #region Constructors
+
+ private LogitechDeviceProvider()
+ { }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public bool Initialize(bool exclusiveAccessIfPossible = false, bool throwExceptions = false)
+ {
+ try
+ {
+ if (IsInitialized)
+ _LogitechGSDK.LogiLedRestoreLighting();
+ }
+ catch
+ { /* At least we tried ... */ }
+
+ IsInitialized = false;
+
+ try
+ {
+ _LogitechGSDK.Reload();
+ if (!_LogitechGSDK.LogiLedInit()) return false;
+
+ _LogitechGSDK.LogiLedSaveCurrentLighting();
+ _LogitechGSDK.LogiLedSetTargetDevice(LogitechDeviceCaps.PerKeyRGB);
+
+ IList devices = new List();
+
+ DeviceChecker.LoadDeviceList();
+ if (DeviceChecker.IsDeviceConnected)
+ {
+ LogitechRGBDevice device = new LogitechKeyboardRGBDevice(new LogitechKeyboardRGBDeviceInfo(
+ RGBDeviceType.Keyboard, DeviceChecker.ConnectedDeviceModel, LogitechDeviceCaps.PerKeyRGB, GetCulture()));
+ devices.Add(device);
+
+ try
+ {
+ device.Initialize();
+ }
+ catch
+ {
+ if (throwExceptions)
+ throw;
+ return false;
+ }
+ }
+ Devices = new ReadOnlyCollection(devices);
+ }
+ catch
+ {
+ if (throwExceptions)
+ throw;
+ else
+ return false;
+ }
+
+ IsInitialized = true;
+
+ return true;
+ }
+
+ ///
+ public void ResetDevices()
+ {
+ _LogitechGSDK.LogiLedRestoreLighting();
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Logitech/Native/_LogitechGSDK.cs b/RGB.NET.Devices.Logitech/Native/_LogitechGSDK.cs
new file mode 100644
index 0000000..e93e2cc
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Native/_LogitechGSDK.cs
@@ -0,0 +1,166 @@
+// ReSharper disable UnusedMethodReturnValue.Global
+// ReSharper disable UnusedMember.Global
+// ReSharper disable MemberCanBePrivate.Global
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using RGB.NET.Core.Exceptions;
+
+namespace RGB.NET.Devices.Logitech.Native
+{
+ // ReSharper disable once InconsistentNaming
+ internal class _LogitechGSDK
+ {
+ #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()
+ {
+ UnloadLogitechGSDK();
+ LoadLogitechGSDK();
+ }
+
+ private static void LoadLogitechGSDK()
+ {
+ 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") + "/LogitechLedEnginesWrapper.dll";
+ if (!File.Exists(dllPath))
+ throw new RGBDeviceException($"Can't find the Logitech-SDK at the expected location '{Path.GetFullPath(dllPath)}'");
+
+ _dllHandle = LoadLibrary(dllPath);
+
+ _logiLedInitPointer = (LogiLedInitPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "LogiLedInit"), typeof(LogiLedInitPointer));
+ _logiLedShutdownPointer = (LogiLedShutdownPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "LogiLedShutdown"), typeof(LogiLedShutdownPointer));
+ _logiLedSetTargetDevicePointer = (LogiLedSetTargetDevicePointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "LogiLedSetTargetDevice"), typeof(LogiLedSetTargetDevicePointer));
+ _logiLedGetSdkVersionPointer = (LogiLedGetSdkVersionPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "LogiLedGetSdkVersion"), typeof(LogiLedGetSdkVersionPointer));
+ _lgiLedSaveCurrentLightingPointer = (LogiLedSaveCurrentLightingPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "LogiLedSaveCurrentLighting"), typeof(LogiLedSaveCurrentLightingPointer));
+ _logiLedRestoreLightingPointer = (LogiLedRestoreLightingPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "LogiLedRestoreLighting"), typeof(LogiLedRestoreLightingPointer));
+ _logiLedSetLightingForKeyWithKeyNamePointer = (LogiLedSetLightingForKeyWithKeyNamePointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "LogiLedSetLightingForKeyWithKeyName"), typeof(LogiLedSetLightingForKeyWithKeyNamePointer));
+ }
+
+ private static void UnloadLogitechGSDK()
+ {
+ if (_dllHandle == IntPtr.Zero) return;
+
+ LogiLedShutdown();
+
+ // 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 LogiLedInitPointer _logiLedInitPointer;
+ private static LogiLedShutdownPointer _logiLedShutdownPointer;
+ private static LogiLedSetTargetDevicePointer _logiLedSetTargetDevicePointer;
+ private static LogiLedGetSdkVersionPointer _logiLedGetSdkVersionPointer;
+ private static LogiLedSaveCurrentLightingPointer _lgiLedSaveCurrentLightingPointer;
+ private static LogiLedRestoreLightingPointer _logiLedRestoreLightingPointer;
+ private static LogiLedSetLightingForKeyWithKeyNamePointer _logiLedSetLightingForKeyWithKeyNamePointer;
+
+ #endregion
+
+ #region Delegates
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool LogiLedInitPointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate void LogiLedShutdownPointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool LogiLedSetTargetDevicePointer(int targetDevice);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool LogiLedGetSdkVersionPointer(ref int majorNum, ref int minorNum, ref int buildNum);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool LogiLedSaveCurrentLightingPointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool LogiLedRestoreLightingPointer();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate bool LogiLedSetLightingForKeyWithKeyNamePointer(int keyCode, int redPercentage, int greenPercentage, int bluePercentage);
+
+ #endregion
+
+ // ReSharper disable EventExceptionNotDocumented
+
+ internal static bool LogiLedInit()
+ {
+ return _logiLedInitPointer();
+ }
+
+ internal static void LogiLedShutdown()
+ {
+ _logiLedShutdownPointer();
+ }
+
+ internal static bool LogiLedSetTargetDevice(LogitechDeviceCaps targetDevice)
+ {
+ return _logiLedSetTargetDevicePointer((int)targetDevice);
+ }
+
+ internal static string LogiLedGetSdkVersion()
+ {
+ int major = 0;
+ int minor = 0;
+ int build = 0;
+ LogiLedGetSdkVersion(ref major, ref minor, ref build);
+
+ return $"{major}.{minor}.{build}";
+ }
+
+ internal static bool LogiLedGetSdkVersion(ref int majorNum, ref int minorNum, ref int buildNum)
+ {
+ return _logiLedGetSdkVersionPointer(ref majorNum, ref minorNum, ref buildNum);
+ }
+
+ internal static bool LogiLedSaveCurrentLighting()
+ {
+ return _lgiLedSaveCurrentLightingPointer();
+ }
+
+ internal static bool LogiLedRestoreLighting()
+ {
+ return _logiLedRestoreLightingPointer();
+ }
+
+ internal static bool LogiLedSetLightingForKeyWithKeyName(int keyCode,
+ int redPercentage, int greenPercentage, int bluePercentage)
+ {
+ return _logiLedSetLightingForKeyWithKeyNamePointer(keyCode, redPercentage, greenPercentage, bluePercentage);
+ }
+
+ // ReSharper restore EventExceptionNotDocumented
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.Logitech/Properties/AssemblyInfo.cs b/RGB.NET.Devices.Logitech/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..d3238fd
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+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.Logitech")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("RGB.NET.Devices.Logitech")]
+[assembly: AssemblyCopyright("Copyright © 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("e7b2f174-fcc6-4fc7-9970-3138b5f4c921")]
+
+// 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.Logitech/RGB.NET.Devices.Logitech.csproj b/RGB.NET.Devices.Logitech/RGB.NET.Devices.Logitech.csproj
new file mode 100644
index 0000000..01fa985
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/RGB.NET.Devices.Logitech.csproj
@@ -0,0 +1,88 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {E7B2F174-FCC6-4FC7-9970-3138B5F4C921}
+ Library
+ Properties
+ RGB.NET.Devices.Logitech
+ RGB.NET.Devices.Logitech
+ v4.5
+ 512
+
+
+ true
+ full
+ false
+ ..\bin\
+ DEBUG;TRACE
+ prompt
+ 4
+ ..\bin\RGB.NET.Devices.Logitech.XML
+
+
+ pdbonly
+ true
+ ..\bin\
+ TRACE
+ prompt
+ 4
+ ..\bin\RGB.NET.Devices.Logitech.XML
+
+
+
+ ..\packages\HidSharp.1.5\lib\net35\HidSharp.dll
+ True
+
+
+ ..\packages\RGB.NET.Core.1.0.0\lib\net45\RGB.NET.Core.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RGB.NET.Devices.Logitech/RGB.NET.Devices.Logitech.csproj.DotSettings b/RGB.NET.Devices.Logitech/RGB.NET.Devices.Logitech.csproj.DotSettings
new file mode 100644
index 0000000..8babca3
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/RGB.NET.Devices.Logitech.csproj.DotSettings
@@ -0,0 +1,5 @@
+
+ True
+ True
+ True
+ True
\ No newline at end of file
diff --git a/RGB.NET.Devices.Logitech/libs/x64/LogitechLedEnginesWrapper.dll b/RGB.NET.Devices.Logitech/libs/x64/LogitechLedEnginesWrapper.dll
new file mode 100644
index 0000000..fb707e7
Binary files /dev/null and b/RGB.NET.Devices.Logitech/libs/x64/LogitechLedEnginesWrapper.dll differ
diff --git a/RGB.NET.Devices.Logitech/libs/x86/LogitechLedEnginesWrapper.dll b/RGB.NET.Devices.Logitech/libs/x86/LogitechLedEnginesWrapper.dll
new file mode 100644
index 0000000..a33fa40
Binary files /dev/null and b/RGB.NET.Devices.Logitech/libs/x86/LogitechLedEnginesWrapper.dll differ
diff --git a/RGB.NET.Devices.Logitech/packages.config b/RGB.NET.Devices.Logitech/packages.config
new file mode 100644
index 0000000..32c0f53
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/RGB.NET.Devices.Logitech/targets/RGB.NET.Devices.Logitech.targets b/RGB.NET.Devices.Logitech/targets/RGB.NET.Devices.Logitech.targets
new file mode 100644
index 0000000..decacc5
--- /dev/null
+++ b/RGB.NET.Devices.Logitech/targets/RGB.NET.Devices.Logitech.targets
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+ False
+
+
+ False
+
+
+
+
+
+
+
+ %(RecursiveDir)%(FileName)%(Extension)
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ bin\%(RecursiveDir)%(Filename)%(Extension)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(PostBuildEventDependsOn);
+ CopyLogitechSDKFiles;
+
+
+ $(BuildDependsOn);
+ CopyLogitechSDKFiles;
+
+
+ $(CleanDependsOn);
+ CleanLogitechSDKFiles;
+
+
+
+
+
+
+
+ CollectLogitechSDKFiles;
+ $(PipelineCollectFilesPhaseDependsOn);
+
+
+
diff --git a/RGB.NET.sln b/RGB.NET.sln
index 3aca62e..94a8642 100644
--- a/RGB.NET.sln
+++ b/RGB.NET.sln
@@ -31,6 +31,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{06416566
NuGet\RGB.NET.Brushes.nuspec = NuGet\RGB.NET.Brushes.nuspec
NuGet\RGB.NET.Core.nuspec = NuGet\RGB.NET.Core.nuspec
NuGet\RGB.NET.Devices.Corsair.nuspec = NuGet\RGB.NET.Devices.Corsair.nuspec
+ NuGet\RGB.NET.Devices.Logitech.nuspec = NuGet\RGB.NET.Devices.Logitech.nuspec
NuGet\RGB.NET.Effects.nuspec = NuGet\RGB.NET.Effects.nuspec
NuGet\RGB.NET.Groups.nuspec = NuGet\RGB.NET.Groups.nuspec
NuGet\RGB.NET.Input.Corsair.nuspec = NuGet\RGB.NET.Input.Corsair.nuspec
@@ -42,6 +43,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UI", "UI", "{BD7C9994-1747-
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RGB.NET.WPF", "RGB.NET.WPF\RGB.NET.WPF.csproj", "{8D6C4FE6-0046-4E98-876F-4C0B87249989}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RGB.NET.Devices.Logitech", "RGB.NET.Devices.Logitech\RGB.NET.Devices.Logitech.csproj", "{E7B2F174-FCC6-4FC7-9970-3138B5F4C921}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -80,6 +83,10 @@ Global
{8D6C4FE6-0046-4E98-876F-4C0B87249989}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D6C4FE6-0046-4E98-876F-4C0B87249989}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D6C4FE6-0046-4E98-876F-4C0B87249989}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E7B2F174-FCC6-4FC7-9970-3138B5F4C921}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E7B2F174-FCC6-4FC7-9970-3138B5F4C921}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E7B2F174-FCC6-4FC7-9970-3138B5F4C921}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E7B2F174-FCC6-4FC7-9970-3138B5F4C921}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -92,5 +99,6 @@ Global
{F905C418-76BB-4BA6-88AB-0793BC2681D3} = {C854766D-9C1D-474B-B5B8-249DF4A9E552}
{2A39F859-AAD0-4C16-94F8-78057820B376} = {FFBCAF88-6646-43EC-9F24-2D719D3779C8}
{8D6C4FE6-0046-4E98-876F-4C0B87249989} = {BD7C9994-1747-4595-9C21-298E8FDCB657}
+ {E7B2F174-FCC6-4FC7-9970-3138B5F4C921} = {33D5E279-1C4E-4AB6-9D1E-6D18109A6C25}
EndGlobalSection
EndGlobal