From a12389f221614e4cdad65056a1a80e7a584b5a7c Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Tue, 11 Oct 2016 00:55:03 +0200 Subject: [PATCH] Updated Logitech SDK for #174 --- Artemis/Artemis/Artemis.csproj | 8 +-- .../Artemis/DeviceProviders/Logitech/G910.cs | 58 +++++++++++++++++- .../Logitech/LogitechKeyboard.cs | 3 - .../Logitech/Utilities/KeyboardNames.cs | 12 +++- .../Logitech/Utilities/LogitechGSDK.cs | 27 +++++++- .../Logitech/Utilities/OrionUtilities.cs | 1 + Artemis/Artemis/LogitechLedEnginesWrapper.dll | Bin 11264 -> 20088 bytes 7 files changed, 95 insertions(+), 14 deletions(-) diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj index 1a11ea0fd..9604e7fd8 100644 --- a/Artemis/Artemis/Artemis.csproj +++ b/Artemis/Artemis/Artemis.csproj @@ -650,8 +650,11 @@ - + + + Always + PreserveNewest @@ -837,9 +840,6 @@ - - PreserveNewest - diff --git a/Artemis/Artemis/DeviceProviders/Logitech/G910.cs b/Artemis/Artemis/DeviceProviders/Logitech/G910.cs index 97bfda191..957d7ebd9 100644 --- a/Artemis/Artemis/DeviceProviders/Logitech/G910.cs +++ b/Artemis/Artemis/DeviceProviders/Logitech/G910.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System; +using System.Drawing; +using System.Linq; using System.Windows; using System.Windows.Forms; using Artemis.DeviceProviders.Logitech.Utilities; @@ -16,8 +18,8 @@ namespace Artemis.DeviceProviders.Logitech "Please check your cables and updating the Logitech Gaming Software\n" + "A minimum version of 8.81.15 is required.\n\n" + "If needed, you can select a different keyboard in Artemis under settings."; - Height = 6; - Width = 21; + Height = 7; + Width = 22; PreviewSettings = new PreviewSettings(540, 154, new Thickness(25, -80, 0, 0), Resources.g910); } @@ -25,5 +27,55 @@ namespace Artemis.DeviceProviders.Logitech { return KeyMap.QwertyLayout.FirstOrDefault(k => k.KeyCode == keyCode); } + + /// + /// The G910 also updates the G-logo, G-badge and G-keys + /// + /// + public override void DrawBitmap(Bitmap bitmap) + { + using (var croppedBitmap = new Bitmap(21*4, 6*4)) + { + // Deal with non-standard DPI + croppedBitmap.SetResolution(96, 96); + // Don't forget that the image is upscaled 4 times + using (var g = Graphics.FromImage(croppedBitmap)) + { + g.DrawImage(bitmap, new Rectangle(0, 0, 84, 24), new Rectangle(4, 4, 84, 24), GraphicsUnit.Pixel); + } + + base.DrawBitmap(croppedBitmap); + } + + using (var resized = OrionUtilities.ResizeImage(bitmap, 22, 7)) + { + // Color the extra keys on the left side of the keyboard + SetLogitechColorFromCoordinates(resized, KeyboardNames.G_LOGO, 0, 1); + SetLogitechColorFromCoordinates(resized, KeyboardNames.G_1, 0, 2); + SetLogitechColorFromCoordinates(resized, KeyboardNames.G_2, 0, 3); + SetLogitechColorFromCoordinates(resized, KeyboardNames.G_3, 0, 4); + SetLogitechColorFromCoordinates(resized, KeyboardNames.G_4, 0, 5); + SetLogitechColorFromCoordinates(resized, KeyboardNames.G_5, 0, 6); + + // Color the extra keys on the top of the keyboard + SetLogitechColorFromCoordinates(resized, KeyboardNames.G_6, 3, 0); + SetLogitechColorFromCoordinates(resized, KeyboardNames.G_7, 4, 0); + SetLogitechColorFromCoordinates(resized, KeyboardNames.G_8, 5, 0); + SetLogitechColorFromCoordinates(resized, KeyboardNames.G_9, 6, 0); + + // Color the G-badge + SetLogitechColorFromCoordinates(resized, KeyboardNames.G_BADGE, 5, 6); + } + } + + private void SetLogitechColorFromCoordinates(Bitmap bitmap, KeyboardNames key, int x, int y) + { + var color = bitmap.GetPixel(x, y); + var rPer = (int) Math.Round(color.R/2.55); + var gPer = (int) Math.Round(color.G/2.55); + var bPer = (int) Math.Round(color.B/2.55); + + LogitechGSDK.LogiLedSetLightingForKeyWithKeyName(key, rPer, gPer, bPer); + } } } \ No newline at end of file diff --git a/Artemis/Artemis/DeviceProviders/Logitech/LogitechKeyboard.cs b/Artemis/Artemis/DeviceProviders/Logitech/LogitechKeyboard.cs index e04471a29..e5263ec7e 100644 --- a/Artemis/Artemis/DeviceProviders/Logitech/LogitechKeyboard.cs +++ b/Artemis/Artemis/DeviceProviders/Logitech/LogitechKeyboard.cs @@ -58,10 +58,7 @@ namespace Artemis.DeviceProviders.Logitech Thread.Sleep(200); LogitechGSDK.LogiLedSaveCurrentLighting(); - - // Disable keys we can't color LogitechGSDK.LogiLedSetTargetDevice(LogitechGSDK.LOGI_DEVICETYPE_PERKEY_RGB); - LogitechGSDK.LogiLedSetLighting(0, 0, 0); } public override void Disable() diff --git a/Artemis/Artemis/DeviceProviders/Logitech/Utilities/KeyboardNames.cs b/Artemis/Artemis/DeviceProviders/Logitech/Utilities/KeyboardNames.cs index 78f96b177..2d8758d5f 100644 --- a/Artemis/Artemis/DeviceProviders/Logitech/Utilities/KeyboardNames.cs +++ b/Artemis/Artemis/DeviceProviders/Logitech/Utilities/KeyboardNames.cs @@ -106,6 +106,16 @@ namespace Artemis.DeviceProviders.Logitech.Utilities ARROW_RIGHT = 0x14D, NUM_ZERO = 0x52, NUM_PERIOD = 0x53, - TEST = 0x1 + 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 } } \ No newline at end of file diff --git a/Artemis/Artemis/DeviceProviders/Logitech/Utilities/LogitechGSDK.cs b/Artemis/Artemis/DeviceProviders/Logitech/Utilities/LogitechGSDK.cs index 60f498b92..a5a81a558 100644 --- a/Artemis/Artemis/DeviceProviders/Logitech/Utilities/LogitechGSDK.cs +++ b/Artemis/Artemis/DeviceProviders/Logitech/Utilities/LogitechGSDK.cs @@ -1,6 +1,5 @@ using System.Runtime.InteropServices; - -// ReSharper disable InconsistentNaming +using System.Text; namespace Artemis.DeviceProviders.Logitech.Utilities { @@ -26,6 +25,25 @@ namespace Artemis.DeviceProviders.Logitech.Utilities [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] public static extern bool LogiLedInit(); + //Config option functions + [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] + public static extern bool LogiLedGetConfigOptionNumber([MarshalAs(UnmanagedType.LPWStr)] string configPath, + ref double defaultNumber); + + [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] + public static extern bool LogiLedGetConfigOptionBool([MarshalAs(UnmanagedType.LPWStr)] string configPath, + ref bool defaultRed); + + [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] + public static extern bool LogiLedGetConfigOptionColor([MarshalAs(UnmanagedType.LPWStr)] string configPath, + ref int defaultRed, ref int defaultGreen, ref int defaultBlue); + + [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] + public static extern bool LogiLedGetConfigOptionKeyInput([MarshalAs(UnmanagedType.LPWStr)] string configPath, + StringBuilder buffer, int bufsize); + + ///////////////////// + [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] public static extern bool LogiLedSetTargetDevice(int targetDevice); @@ -52,6 +70,9 @@ namespace Artemis.DeviceProviders.Logitech.Utilities [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] public static extern bool LogiLedStopEffects(); + [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] + public static extern bool LogiLedExcludeKeysFromBitmap(KeyboardNames[] keyList, int listCount); + [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] public static extern bool LogiLedSetLightingFromBitmap(byte[] bitmap); @@ -68,7 +89,7 @@ namespace Artemis.DeviceProviders.Logitech.Utilities int greenPercentage, int bluePercentage); [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] - public static extern bool LogiLedSetLightingForKeyWithKeyName(int keyCode, int redPercentage, + public static extern bool LogiLedSetLightingForKeyWithKeyName(KeyboardNames keyCode, int redPercentage, int greenPercentage, int bluePercentage); [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] diff --git a/Artemis/Artemis/DeviceProviders/Logitech/Utilities/OrionUtilities.cs b/Artemis/Artemis/DeviceProviders/Logitech/Utilities/OrionUtilities.cs index 048bd978e..370460b98 100644 --- a/Artemis/Artemis/DeviceProviders/Logitech/Utilities/OrionUtilities.cs +++ b/Artemis/Artemis/DeviceProviders/Logitech/Utilities/OrionUtilities.cs @@ -180,6 +180,7 @@ namespace Artemis.DeviceProviders.Logitech.Utilities remapped[firstTByte + j] = pixels[firstSByte + j]; } + b.Dispose(); return remapped; } diff --git a/Artemis/Artemis/LogitechLedEnginesWrapper.dll b/Artemis/Artemis/LogitechLedEnginesWrapper.dll index 37f959c2e807034f0476c82a72b47aa335917d91..fb707e78a9fc57566d267ecefbad8dbdeca0b5ec 100644 GIT binary patch literal 20088 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 delta 4252 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 W`!V|>#^`ipo!ze7k1Y78vi||$<@ECa