diff --git a/Project-Aurora/Project-Aurora/Devices/XPG/XPGDevice.cs b/Project-Aurora/Project-Aurora/Devices/XPG/XPGDevice.cs new file mode 100644 index 000000000..16b463ad9 --- /dev/null +++ b/Project-Aurora/Project-Aurora/Devices/XPG/XPGDevice.cs @@ -0,0 +1,151 @@ +using Aurora; +using Aurora.Devices; +using Aurora.Devices.ScriptedDevice; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; + +using System.Runtime.InteropServices; // DllImport +using System.Diagnostics; // Stopwatch +using System.ComponentModel; // DoWorkEventArgs + +namespace Aurora.Devices.XPG +{ + + class XPGDevice : DefaultDevice + { + public override string DeviceName => "XPG"; + + private Stopwatch updateStopwatch = new Stopwatch(); + + + [DllImport("libxpgp_aurora", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Initialize")] + private static extern int cInitialize(); + [DllImport("libxpgp_aurora", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Reset")] + private static extern void cReset(); + [DllImport("libxpgp_aurora", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Shutdown")] + private static extern void cShutdown(); + [DllImport("libxpgp_aurora", CallingConvention = CallingConvention.Cdecl, EntryPoint = "UpdateDevice")] + private static extern int cUpdateDevice(byte[] array, int bytesize); + + private bool crashed = false; + private bool initialized = false; + public override bool IsInitialized => initialized && !crashed; + + protected override string DeviceInfo => crashed ? "Error" : "OK"; + + public override bool Initialize() + { + if (crashed) + { + return false; + } + if (!initialized) + { + int error = cInitialize(); + if (error > 0) // a proper error occured + { + crashed = true; // unlikely to work on the second try either + } + if (error == 0) // no error + device found + { + initialized = true; + } + } + return IsInitialized; + } + + public override void Reset() + { + updateStopwatch.Reset(); + if (IsInitialized) + { + cReset(); + } + } + + public override void Shutdown() + { + Reset(); + cShutdown(); + initialized = false; + } + + public override bool UpdateDevice(Dictionary keyColors, DoWorkEventArgs e, bool forced = false) + { + if (IsInitialized) + { + bool success = TryUpdateDevice(keyColors, forced); + if (!success) + { + this.Shutdown(); + } + return success; + } + return false; + } + + private const int colorcount = 108; // highest value we use is FN_Key (107) + private const int bytesize = 4 * colorcount; + private byte[] currentColors = new byte[bytesize]; + private byte[] spareBuffer = new byte[bytesize]; + private bool TryUpdateDevice(Dictionary keyColors, bool forced) + { + byte[] newColors = this.spareBuffer; + if (keyColors.ContainsKey(DeviceKeys.Peripheral)) + { + Color color = keyColors[DeviceKeys.Peripheral]; + for (int k = 0; k < colorcount; k++) + { + newColors[4 * k + 0] = color.R; + newColors[4 * k + 1] = color.G; + newColors[4 * k + 2] = color.B; + newColors[4 * k + 3] = color.A; + } + } + else + { + // start with current colors to support partial updates + Array.Copy(this.currentColors, newColors, bytesize); + } + foreach (KeyValuePair kvp in keyColors) + { + int k = (int)kvp.Key; + if (0 <= k && k < colorcount) + { + Color color = kvp.Value; + newColors[4 * k + 0] = color.R; + newColors[4 * k + 1] = color.G; + newColors[4 * k + 2] = color.B; + newColors[4 * k + 3] = color.A; + } + } + // Stopwatch is stopped. This is the first update after connecting. + // 'this.currentColors' doesn't hold any previous state. + if (!updateStopwatch.IsRunning) + { + forced = true; + } + // The device resets itself after 5s. Force an update before then. + if (updateStopwatch.ElapsedMilliseconds >= 4703) + { + forced = true; + } + // Do the thing! + if (forced || !Enumerable.SequenceEqual(this.currentColors, newColors)) + { + int error = cUpdateDevice(newColors, bytesize); + if (error != 0) // device has probably been physically disconnected + { + return false; + } + updateStopwatch.Restart(); + this.spareBuffer = this.currentColors; + this.currentColors = newColors; + } + return true; + } + } + +} diff --git a/Project-Aurora/Project-Aurora/Project-Aurora.csproj b/Project-Aurora/Project-Aurora/Project-Aurora.csproj index 5e686d109..3385ccf2b 100644 --- a/Project-Aurora/Project-Aurora/Project-Aurora.csproj +++ b/Project-Aurora/Project-Aurora/Project-Aurora.csproj @@ -138,6 +138,9 @@ PreserveNewest + + PreserveNewest + diff --git a/Project-Aurora/Project-Aurora/Settings/Configuration.cs b/Project-Aurora/Project-Aurora/Settings/Configuration.cs index 9c87116e2..148ff739b 100755 --- a/Project-Aurora/Project-Aurora/Settings/Configuration.cs +++ b/Project-Aurora/Project-Aurora/Settings/Configuration.cs @@ -308,6 +308,10 @@ public enum PreferredKeyboard [Description("HyperX Alloy Elite RGB")] HyperX_Alloy_Elite_RGB = 1400, + //XPG range is 1500-1599 + [Description("XPG Mage/Summoner")] + XPG_Summoner = 1500 + } public enum PreferredKeyboardLocalization diff --git a/Project-Aurora/Project-Aurora/Settings/KeyboardLayoutManager.cs b/Project-Aurora/Project-Aurora/Settings/KeyboardLayoutManager.cs index 80a9357d8..940924963 100755 --- a/Project-Aurora/Project-Aurora/Settings/KeyboardLayoutManager.cs +++ b/Project-Aurora/Project-Aurora/Settings/KeyboardLayoutManager.cs @@ -796,6 +796,8 @@ public void LoadBrand(PreferredKeyboard keyboard_preference = PreferredKeyboard. layoutConfigPath = Path.Combine(layoutsPath, "omen_four_zone.json"); else if (keyboard_preference == PreferredKeyboard.HyperX_Alloy_Elite_RGB) layoutConfigPath = Path.Combine(layoutsPath, "hyperx_alloy_elite_rgb.json"); + else if (keyboard_preference == PreferredKeyboard.XPG_Summoner) + layoutConfigPath = Path.Combine(layoutsPath, "xpg_summoner.json"); else { diff --git a/Project-Aurora/Project-Aurora/kb_layouts/Extra Features/xpg_summoner_right_features.json b/Project-Aurora/Project-Aurora/kb_layouts/Extra Features/xpg_summoner_right_features.json new file mode 100644 index 000000000..e48149054 --- /dev/null +++ b/Project-Aurora/Project-Aurora/kb_layouts/Extra Features/xpg_summoner_right_features.json @@ -0,0 +1,20 @@ +{ + "group_tag": "keyboard", + "origin_region": 2, + "grouped_keys": [ + { + "visualName": "\\", + "tag": 51, + "margin_left": -320, + "margin_top": 74, + "width": 47.0, + "height": 30.0, + "font_size": 9.0, + "enabled": true, + "absolute_location": true + } + ], + "key_conversion": { + 77: 51 + } +} \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/kb_layouts/xpg_summoner.json b/Project-Aurora/Project-Aurora/kb_layouts/xpg_summoner.json new file mode 100644 index 000000000..2b121c864 --- /dev/null +++ b/Project-Aurora/Project-Aurora/kb_layouts/xpg_summoner.json @@ -0,0 +1,68 @@ +{ + "keys_to_remove": [ + 51, 71, 77 + ], + "key_modifications": { + "50": { + "line_break": null, + "width": 30 + }, + "52": { + "line_break": null, + "margin_left": 67.0 + }, + "72": { + "visualName": null, + "tag": 72, + "line_break": null, + "margin_left": 7.0, + "margin_top": 0.0, + "width": 74.0, + "height": 30.0, + "font_size": 12.0, + "width_bits": 7, + "height_bits": 3, + "margin_left_bits": 0, + "margin_top_bits": 0, + "enabled": null + }, + "76": { + "visualName": null, + "tag": 76, + "line_break": null, + "margin_left": 0.0, + "margin_top": 0.0, + "width": 78.0, + "height": 30.0, + "font_size": 12.0, + "width_bits": 7, + "height_bits": 3, + "margin_left_bits": 0, + "margin_top_bits": 0, + "enabled": null + }, + "88": { + "tag": 88, + "line_break": null, + "width": 93 + }, + "99": { + "visualName": "FN", + "tag": 107, + "line_break": null, + "margin_left": null, + "margin_top": null, + "width": null, + "height": null, + "font_size": null, + "width_bits": null, + "height_bits": null, + "margin_left_bits": null, + "margin_top_bits": null, + "enabled": null + } + }, + "included_features": [ + "xpg_summoner_right_features.json" + ] +} diff --git a/Project-Aurora/Project-Aurora/libxpgp_aurora.dll b/Project-Aurora/Project-Aurora/libxpgp_aurora.dll new file mode 100644 index 000000000..5c0c7dee1 Binary files /dev/null and b/Project-Aurora/Project-Aurora/libxpgp_aurora.dll differ