From 810db3619158dbcd76c11111f3ed8361906840e7 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Sun, 22 Nov 2020 00:01:55 +0100 Subject: [PATCH] Input - Added basic input stuff --- .../Artemis.Core.csproj.DotSettings | 1 + .../Models/Surface/ArtemisDevice.cs | 22 ++ .../Surface/ArtemisDeviceInputIdentifier.cs | 31 ++ src/Artemis.Core/Services/Input/InputKey.cs | 359 ++++++++++++++++++ .../Services/Input/InputKeyLedIdMap.cs | 185 +++++++++ .../Services/Input/InputProvider.cs | 49 +++ .../Input/InputProviderKeyboardEventArgs.cs | 37 ++ .../Services/Input/InputService.cs | 59 +++ .../Services/PluginManagementService.cs | 4 +- .../Entities/Surface/DeviceEntity.cs | 17 +- src/Artemis.UI/Artemis.UI.csproj | 1 + src/Artemis.UI/Bootstrapper.cs | 6 +- .../NativeWindowInputProvider.cs | 141 +++++++ src/Artemis.UI/InputProviders/SpongeWindow.cs | 26 ++ .../Services/RegistrationService.cs | 21 +- src/Artemis.UI/packages.lock.json | 9 + 16 files changed, 962 insertions(+), 6 deletions(-) create mode 100644 src/Artemis.Core/Models/Surface/ArtemisDeviceInputIdentifier.cs create mode 100644 src/Artemis.Core/Services/Input/InputKey.cs create mode 100644 src/Artemis.Core/Services/Input/InputKeyLedIdMap.cs create mode 100644 src/Artemis.Core/Services/Input/InputProvider.cs create mode 100644 src/Artemis.Core/Services/Input/InputProviderKeyboardEventArgs.cs create mode 100644 src/Artemis.Core/Services/Input/InputService.cs create mode 100644 src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs create mode 100644 src/Artemis.UI/InputProviders/SpongeWindow.cs diff --git a/src/Artemis.Core/Artemis.Core.csproj.DotSettings b/src/Artemis.Core/Artemis.Core.csproj.DotSettings index 006fb1134..914c8e232 100644 --- a/src/Artemis.Core/Artemis.Core.csproj.DotSettings +++ b/src/Artemis.Core/Artemis.Core.csproj.DotSettings @@ -55,6 +55,7 @@ True True True + True True True True diff --git a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs index 1bcb14540..8bffa761d 100644 --- a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs +++ b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Artemis.Core.DeviceProviders; @@ -22,6 +23,7 @@ namespace Artemis.Core RgbDevice = rgbDevice; DeviceProvider = deviceProvider; Surface = surface; + InputIdentifiers = new List(); DeviceEntity = new DeviceEntity(); _leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); @@ -38,6 +40,7 @@ namespace Artemis.Core RgbDevice = rgbDevice; DeviceProvider = deviceProvider; Surface = surface; + InputIdentifiers = new List(); DeviceEntity = deviceEntity; _leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); } @@ -84,6 +87,11 @@ namespace Artemis.Core private set => SetAndNotify(ref _leds, value); } + /// + /// Gets a list of input identifiers associated with this device + /// + public List InputIdentifiers { get; } + /// /// Gets or sets the X-position of the device /// @@ -174,6 +182,16 @@ namespace Artemis.Core { // Other properties are computed DeviceEntity.DeviceIdentifier = RgbDevice.GetDeviceIdentifier(); + + DeviceEntity.InputIdentifier.Clear(); + foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers) + { + DeviceEntity.InputIdentifier.Add(new DeviceInputIdentifierEntity + { + InputProvider = identifier.InputProvider, + Identifier = identifier.Identifier + }); + } } internal void ApplyToRgbDevice() @@ -186,6 +204,10 @@ namespace Artemis.Core RgbDevice.Location = new Point(1, 1); RgbDevice.Location = new Point(DeviceEntity.X, DeviceEntity.Y); + InputIdentifiers.Clear(); + foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifier) + InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier)); + CalculateRenderProperties(); OnDeviceUpdated(); } diff --git a/src/Artemis.Core/Models/Surface/ArtemisDeviceInputIdentifier.cs b/src/Artemis.Core/Models/Surface/ArtemisDeviceInputIdentifier.cs new file mode 100644 index 000000000..6af52fccb --- /dev/null +++ b/src/Artemis.Core/Models/Surface/ArtemisDeviceInputIdentifier.cs @@ -0,0 +1,31 @@ +using Artemis.Core.Services; + +namespace Artemis.Core +{ + /// + /// Represents a device input identifier used by a specific to identify the device + /// + public class ArtemisDeviceInputIdentifier + { + /// + /// Creates a new instance of the class + /// + /// The full type and namespace of the this identifier is used by + /// A value used to identify the device + public ArtemisDeviceInputIdentifier(string inputProvider, object identifier) + { + InputProvider = inputProvider; + Identifier = identifier; + } + + /// + /// Gets or sets the full type and namespace of the this identifier is used by + /// + public string InputProvider { get; set; } + + /// + /// Gets or sets a value used to identify the device + /// + public object Identifier { get; set; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/InputKey.cs b/src/Artemis.Core/Services/Input/InputKey.cs new file mode 100644 index 000000000..b40842b01 --- /dev/null +++ b/src/Artemis.Core/Services/Input/InputKey.cs @@ -0,0 +1,359 @@ +namespace Artemis.Core.Services +{ + /// Specifies the possible key values on a keyboard. + public enum InputKey + { + /// No key pressed. + None = 0, + /// The Cancel key. + Cancel = 1, + /// The Backspace key. + Back = 2, + /// The Tab key. + Tab = 3, + /// The Linefeed key. + LineFeed = 4, + /// The Clear key. + Clear = 5, + /// The Enter key. + Enter = 6, + /// The Return key. + Return = 6, + /// The Pause key. + Pause = 7, + /// The Caps Lock key. + CapsLock = 8, + /// The IME Hangul mode key. + HangulMode = 9, + /// The IME Junja mode key. + JunjaMode = 10, // 0x0000000A + /// The IME Final mode key. + FinalMode = 11, // 0x0000000B + /// The IME Hanja mode key. + HanjaMode = 12, // 0x0000000C + /// The ESC key. + Escape = 13, // 0x0000000D + /// The IME Convert key. + ImeConvert = 14, // 0x0000000E + /// The IME NonConvert key. + ImeNonConvert = 15, // 0x0000000F + /// The IME Accept key. + ImeAccept = 16, // 0x00000010 + /// The IME Mode change request. + ImeModeChange = 17, // 0x00000011 + /// The Spacebar key. + Space = 18, // 0x00000012 + /// The Page Up key. + PageUp = 19, // 0x00000013 + /// The Page Up key. + Prior = 19, // 0x00000013 + /// The Page Down key. + Next = 20, // 0x00000014 + /// The Page Down key. + PageDown = 20, // 0x00000014 + /// The End key. + End = 21, // 0x00000015 + /// The Home key. + Home = 22, // 0x00000016 + /// The Left Arrow key. + Left = 23, // 0x00000017 + /// The Up Arrow key. + Up = 24, // 0x00000018 + /// The Right Arrow key. + Right = 25, // 0x00000019 + /// The Down Arrow key. + Down = 26, // 0x0000001A + /// The Select key. + Select = 27, // 0x0000001B + /// The Print key. + Print = 28, // 0x0000001C + /// The Execute key. + Execute = 29, // 0x0000001D + /// The Print Screen key. + PrintScreen = 30, // 0x0000001E + /// The Insert key. + Insert = 31, // 0x0000001F + /// The Delete key. + Delete = 32, // 0x00000020 + /// The Help key. + Help = 33, // 0x00000021 + /// The 0 (zero) key. + D0 = 34, // 0x00000022 + /// The 1 (one) key. + D1 = 35, // 0x00000023 + /// The 2 key. + D2 = 36, // 0x00000024 + /// The 3 key. + D3 = 37, // 0x00000025 + /// The 4 key. + D4 = 38, // 0x00000026 + /// The 5 key. + D5 = 39, // 0x00000027 + /// The 6 key. + D6 = 40, // 0x00000028 + /// The 7 key. + D7 = 41, // 0x00000029 + /// The 8 key. + D8 = 42, // 0x0000002A + /// The 9 key. + D9 = 43, // 0x0000002B + /// The A key. + A = 44, // 0x0000002C + /// The B key. + B = 45, // 0x0000002D + /// The C key. + C = 46, // 0x0000002E + /// The D key. + D = 47, // 0x0000002F + /// The E key. + E = 48, // 0x00000030 + /// The F key. + F = 49, // 0x00000031 + /// The G key. + G = 50, // 0x00000032 + /// The H key. + H = 51, // 0x00000033 + /// The I key. + I = 52, // 0x00000034 + /// The J key. + J = 53, // 0x00000035 + /// The K key. + K = 54, // 0x00000036 + /// The L key. + L = 55, // 0x00000037 + /// The M key. + M = 56, // 0x00000038 + /// The N key. + N = 57, // 0x00000039 + /// The O key. + O = 58, // 0x0000003A + /// The P key. + P = 59, // 0x0000003B + /// The Q key. + Q = 60, // 0x0000003C + /// The R key. + R = 61, // 0x0000003D + /// The S key. + S = 62, // 0x0000003E + /// The T key. + T = 63, // 0x0000003F + /// The U key. + U = 64, // 0x00000040 + /// The V key. + V = 65, // 0x00000041 + /// The W key. + W = 66, // 0x00000042 + /// The X key. + X = 67, // 0x00000043 + /// The Y key. + Y = 68, // 0x00000044 + /// The Z key. + Z = 69, // 0x00000045 + /// The left Windows logo key (Microsoft Natural Keyboard). + LWin = 70, // 0x00000046 + /// The right Windows logo key (Microsoft Natural Keyboard). + RWin = 71, // 0x00000047 + /// The Application key (Microsoft Natural Keyboard). Also known as the Menu key, as it displays an application-specific context menu. + Apps = 72, // 0x00000048 + /// The Computer Sleep key. + Sleep = 73, // 0x00000049 + /// The 0 key on the numeric keypad. + NumPad0 = 74, // 0x0000004A + /// The 1 key on the numeric keypad. + NumPad1 = 75, // 0x0000004B + /// The 2 key on the numeric keypad. + NumPad2 = 76, // 0x0000004C + /// The 3 key on the numeric keypad. + NumPad3 = 77, // 0x0000004D + /// The 4 key on the numeric keypad. + NumPad4 = 78, // 0x0000004E + /// The 5 key on the numeric keypad. + NumPad5 = 79, // 0x0000004F + /// The 6 key on the numeric keypad. + NumPad6 = 80, // 0x00000050 + /// The 7 key on the numeric keypad. + NumPad7 = 81, // 0x00000051 + /// The 8 key on the numeric keypad. + NumPad8 = 82, // 0x00000052 + /// The 9 key on the numeric keypad. + NumPad9 = 83, // 0x00000053 + /// The Multiply key. + Multiply = 84, // 0x00000054 + /// The Add key. + Add = 85, // 0x00000055 + /// The Separator key. + Separator = 86, // 0x00000056 + /// The Subtract key. + Subtract = 87, // 0x00000057 + /// The Decimal key. + Decimal = 88, // 0x00000058 + /// The Divide key. + Divide = 89, // 0x00000059 + /// The F1 key. + F1 = 90, // 0x0000005A + /// The F2 key. + F2 = 91, // 0x0000005B + /// The F3 key. + F3 = 92, // 0x0000005C + /// The F4 key. + F4 = 93, // 0x0000005D + /// The F5 key. + F5 = 94, // 0x0000005E + /// The F6 key. + F6 = 95, // 0x0000005F + /// The F7 key. + F7 = 96, // 0x00000060 + /// The F8 key. + F8 = 97, // 0x00000061 + /// The F9 key. + F9 = 98, // 0x00000062 + /// The F10 key. + F10 = 99, // 0x00000063 + /// The F11 key. + F11 = 100, // 0x00000064 + /// The F12 key. + F12 = 101, // 0x00000065 + /// The F13 key. + F13 = 102, // 0x00000066 + /// The F14 key. + F14 = 103, // 0x00000067 + /// The F15 key. + F15 = 104, // 0x00000068 + /// The F16 key. + F16 = 105, // 0x00000069 + /// The F17 key. + F17 = 106, // 0x0000006A + /// The F18 key. + F18 = 107, // 0x0000006B + /// The F19 key. + F19 = 108, // 0x0000006C + /// The F20 key. + F20 = 109, // 0x0000006D + /// The F21 key. + F21 = 110, // 0x0000006E + /// The F22 key. + F22 = 111, // 0x0000006F + /// The F23 key. + F23 = 112, // 0x00000070 + /// The F24 key. + F24 = 113, // 0x00000071 + /// The Num Lock key. + NumLock = 114, // 0x00000072 + /// The Scroll Lock key. + Scroll = 115, // 0x00000073 + /// The left Shift key. + LeftShift = 116, // 0x00000074 + /// The right Shift key. + RightShift = 117, // 0x00000075 + /// The left CTRL key. + LeftCtrl = 118, // 0x00000076 + /// The right CTRL key. + RightCtrl = 119, // 0x00000077 + /// The left ALT key. + LeftAlt = 120, // 0x00000078 + /// The right ALT key. + RightAlt = 121, // 0x00000079 + /// The Browser Back key. + BrowserBack = 122, // 0x0000007A + /// The Browser Forward key. + BrowserForward = 123, // 0x0000007B + /// The Browser Refresh key. + BrowserRefresh = 124, // 0x0000007C + /// The Browser Stop key. + BrowserStop = 125, // 0x0000007D + /// The Browser Search key. + BrowserSearch = 126, // 0x0000007E + /// The Browser Favorites key. + BrowserFavorites = 127, // 0x0000007F + /// The Browser Home key. + BrowserHome = 128, // 0x00000080 + /// The Volume Mute key. + VolumeMute = 129, // 0x00000081 + /// The Volume Down key. + VolumeDown = 130, // 0x00000082 + /// The Volume Up key. + VolumeUp = 131, // 0x00000083 + /// The Media Next Track key. + MediaNextTrack = 132, // 0x00000084 + /// The Media Previous Track key. + MediaPreviousTrack = 133, // 0x00000085 + /// The Media Stop key. + MediaStop = 134, // 0x00000086 + /// The Media Play Pause key. + MediaPlayPause = 135, // 0x00000087 + /// The Launch Mail key. + LaunchMail = 136, // 0x00000088 + /// The Select Media key. + SelectMedia = 137, // 0x00000089 + /// The Launch Application1 key. + LaunchApplication1 = 138, // 0x0000008A + /// The Launch Application2 key. + LaunchApplication2 = 139, // 0x0000008B + /// The OEM Semicolon key. + OemSemicolon = 140, // 0x0000008C + /// The OEM Addition key. + OemPlus = 141, // 0x0000008D + /// The OEM Comma key. + OemComma = 142, // 0x0000008E + /// The OEM Minus key. + OemMinus = 143, // 0x0000008F + /// The OEM Period key. + OemPeriod = 144, // 0x00000091 + /// The OEM Question key. + OemQuestion = 145, // 0x00000092 + /// The OEM Tilde key. + OemTilde = 146, // 0x00000092 + /// The ABNT_C1 (Brazilian) key. + AbntC1 = 147, // 0x00000093 + /// The ABNT_C2 (Brazilian) key. + AbntC2 = 148, // 0x00000095 + /// The OEM Open Brackets key. + OemOpenBrackets = 149, // 0x00000096 + /// The OEM Pipe key. + OemPipe = 150, // 0x00000096 + /// The OEM Close Brackets key. + OemCloseBrackets = 151, // 0x00000097 + /// The OEM Quotes key. + OemQuotes = 152, // 0x00000098 + /// The OEM Backslash key. + OemBackslash = 154, // 0x0000009A + /// A special key masking the real key being processed by an IME. + ImeProcessed = 155, // 0x0000009B + /// A special key masking the real key being processed as a system key. + System = 156, // 0x0000009C + /// The OEM ATTN key. + OemAttn = 157, // 0x0000009D + /// The OEM FINISH key. + OemFinish = 158, // 0x0000009E + /// The OEM COPY key. + OemCopy = 159, // 0x0000009F + /// The OEM AUTO key. + OemAuto = 160, // 0x000000A0 + /// The OEM ENLW key. + OemEnlw = 161, // 0x000000A1 + /// The OEM BACKTAB key. + OemBackTab = 162, // 0x000000A2 + /// The ATTN key. + Attn = 163, // 0x000000A3 + /// The CRSEL key. + CrSel = 164, // 0x000000A4 + /// The EXSEL key. + ExSel = 165, // 0x000000A5 + /// The ERASE EOF key. + EraseEof = 166, // 0x000000A6 + /// The PLAY key. + Play = 167, // 0x000000A7 + /// The ZOOM key. + Zoom = 168, // 0x000000A8 + /// A constant reserved for future use. + NoName = 169, // 0x000000A9 + /// The PA1 key. + Pa1 = 170, // 0x000000AA + /// The OEM Clear key. + OemClear = 171, // 0x000000AB + /// The key is used with another key to create a single combined character. + DeadCharProcessed = 172, // 0x000000AC, + /// The NumPad enter key + NumPadEnter, + } +} diff --git a/src/Artemis.Core/Services/Input/InputKeyLedIdMap.cs b/src/Artemis.Core/Services/Input/InputKeyLedIdMap.cs new file mode 100644 index 000000000..bd53d2717 --- /dev/null +++ b/src/Artemis.Core/Services/Input/InputKeyLedIdMap.cs @@ -0,0 +1,185 @@ +using System.Collections.Generic; +using RGB.NET.Core; + +namespace Artemis.Core.Services +{ + internal static class InputKeyUtilities + { + internal static readonly Dictionary LedIdMap = new Dictionary() + { + {InputKey.None, LedId.Keyboard_Custom1}, + {InputKey.Cancel, LedId.Keyboard_Custom2}, + {InputKey.Back, LedId.Keyboard_Backspace}, + {InputKey.Tab, LedId.Keyboard_Tab}, + {InputKey.LineFeed, LedId.Keyboard_Custom3}, + {InputKey.Clear, LedId.Keyboard_Custom4}, + {InputKey.Enter, LedId.Keyboard_Enter}, + {InputKey.Pause, LedId.Keyboard_PauseBreak}, + {InputKey.CapsLock, LedId.Keyboard_CapsLock}, + {InputKey.HangulMode, LedId.Keyboard_Custom4}, + {InputKey.JunjaMode, LedId.Keyboard_Custom5}, + {InputKey.FinalMode, LedId.Keyboard_Custom6}, + {InputKey.HanjaMode, LedId.Keyboard_Custom7}, + {InputKey.Escape, LedId.Keyboard_Escape}, + {InputKey.ImeConvert, LedId.Keyboard_Custom8}, + {InputKey.ImeNonConvert, LedId.Keyboard_Custom9}, + {InputKey.ImeAccept, LedId.Keyboard_Custom10}, + {InputKey.ImeModeChange, LedId.Keyboard_Custom11}, + {InputKey.Space, LedId.Keyboard_Space}, + {InputKey.PageUp, LedId.Keyboard_PageUp}, + {InputKey.PageDown, LedId.Keyboard_PageDown}, + {InputKey.End, LedId.Keyboard_End}, + {InputKey.Home, LedId.Keyboard_Home}, + {InputKey.Left, LedId.Keyboard_ArrowLeft}, + {InputKey.Up, LedId.Keyboard_ArrowUp}, + {InputKey.Right, LedId.Keyboard_ArrowRight}, + {InputKey.Down, LedId.Keyboard_ArrowDown}, + {InputKey.Select, LedId.Keyboard_Custom12}, + {InputKey.Print, LedId.Keyboard_Custom13}, + {InputKey.Execute, LedId.Keyboard_Custom14}, + {InputKey.PrintScreen, LedId.Keyboard_PrintScreen}, + {InputKey.Insert, LedId.Keyboard_Insert}, + {InputKey.Delete, LedId.Keyboard_Delete}, + {InputKey.Help, LedId.Keyboard_Custom15}, + {InputKey.D0, LedId.Keyboard_0}, + {InputKey.D1, LedId.Keyboard_1}, + {InputKey.D2, LedId.Keyboard_2}, + {InputKey.D3, LedId.Keyboard_3}, + {InputKey.D4, LedId.Keyboard_4}, + {InputKey.D5, LedId.Keyboard_5}, + {InputKey.D6, LedId.Keyboard_6}, + {InputKey.D7, LedId.Keyboard_7}, + {InputKey.D8, LedId.Keyboard_8}, + {InputKey.D9, LedId.Keyboard_9}, + {InputKey.A, LedId.Keyboard_A}, + {InputKey.B, LedId.Keyboard_B}, + {InputKey.C, LedId.Keyboard_C}, + {InputKey.D, LedId.Keyboard_D}, + {InputKey.E, LedId.Keyboard_E}, + {InputKey.F, LedId.Keyboard_F}, + {InputKey.G, LedId.Keyboard_G}, + {InputKey.H, LedId.Keyboard_H}, + {InputKey.I, LedId.Keyboard_I}, + {InputKey.J, LedId.Keyboard_J}, + {InputKey.K, LedId.Keyboard_K}, + {InputKey.L, LedId.Keyboard_L}, + {InputKey.M, LedId.Keyboard_M}, + {InputKey.N, LedId.Keyboard_N}, + {InputKey.O, LedId.Keyboard_O}, + {InputKey.P, LedId.Keyboard_P}, + {InputKey.Q, LedId.Keyboard_Q}, + {InputKey.R, LedId.Keyboard_R}, + {InputKey.S, LedId.Keyboard_S}, + {InputKey.T, LedId.Keyboard_T}, + {InputKey.U, LedId.Keyboard_U}, + {InputKey.V, LedId.Keyboard_V}, + {InputKey.W, LedId.Keyboard_W}, + {InputKey.X, LedId.Keyboard_X}, + {InputKey.Y, LedId.Keyboard_Y}, + {InputKey.Z, LedId.Keyboard_Z}, + {InputKey.LWin, LedId.Keyboard_LeftGui}, + {InputKey.RWin, LedId.Keyboard_RightGui}, + {InputKey.Apps, LedId.Keyboard_Application}, + {InputKey.Sleep, LedId.Keyboard_Custom16}, + {InputKey.NumPad0, LedId.Keyboard_Num0}, + {InputKey.NumPad1, LedId.Keyboard_Num1}, + {InputKey.NumPad2, LedId.Keyboard_Num2}, + {InputKey.NumPad3, LedId.Keyboard_Num3}, + {InputKey.NumPad4, LedId.Keyboard_Num4}, + {InputKey.NumPad5, LedId.Keyboard_Num5}, + {InputKey.NumPad6, LedId.Keyboard_Num6}, + {InputKey.NumPad7, LedId.Keyboard_Num7}, + {InputKey.NumPad8, LedId.Keyboard_Num8}, + {InputKey.NumPad9, LedId.Keyboard_Num9}, + {InputKey.Multiply, LedId.Keyboard_NumAsterisk}, + {InputKey.Add, LedId.Keyboard_NumPlus}, + {InputKey.Separator, LedId.Keyboard_NumEnter}, // unverified + {InputKey.Subtract, LedId.Keyboard_NumMinus}, + {InputKey.Decimal, LedId.Keyboard_NumPeriodAndDelete}, + {InputKey.Divide, LedId.Keyboard_NumSlash}, + {InputKey.F1, LedId.Keyboard_F1}, + {InputKey.F2, LedId.Keyboard_F2}, + {InputKey.F3, LedId.Keyboard_F3}, + {InputKey.F4, LedId.Keyboard_F4}, + {InputKey.F5, LedId.Keyboard_F5}, + {InputKey.F6, LedId.Keyboard_F6}, + {InputKey.F7, LedId.Keyboard_F7}, + {InputKey.F8, LedId.Keyboard_F8}, + {InputKey.F9, LedId.Keyboard_F9}, + {InputKey.F10, LedId.Keyboard_F10}, + {InputKey.F11, LedId.Keyboard_F11}, + {InputKey.F12, LedId.Keyboard_F12}, + {InputKey.F13, LedId.Keyboard_Custom17}, + {InputKey.F14, LedId.Keyboard_Custom18}, + {InputKey.F15, LedId.Keyboard_Custom19}, + {InputKey.F16, LedId.Keyboard_Custom20}, + {InputKey.F17, LedId.Keyboard_Custom21}, + {InputKey.F18, LedId.Keyboard_Custom22}, + {InputKey.F19, LedId.Keyboard_Custom23}, + {InputKey.F20, LedId.Keyboard_Custom24}, + {InputKey.F21, LedId.Keyboard_Custom25}, + {InputKey.F22, LedId.Keyboard_Custom26}, + {InputKey.F23, LedId.Keyboard_Custom27}, + {InputKey.F24, LedId.Keyboard_Custom28}, + {InputKey.NumLock, LedId.Keyboard_NumLock}, + {InputKey.Scroll, LedId.Keyboard_ScrollLock}, + {InputKey.LeftShift, LedId.Keyboard_LeftShift}, + {InputKey.RightShift, LedId.Keyboard_RightShift}, + {InputKey.LeftCtrl, LedId.Keyboard_LeftCtrl}, + {InputKey.RightCtrl, LedId.Keyboard_RightCtrl}, + {InputKey.LeftAlt, LedId.Keyboard_LeftAlt}, + {InputKey.RightAlt, LedId.Keyboard_RightAlt}, + {InputKey.BrowserBack, LedId.Keyboard_Custom29}, + {InputKey.BrowserForward, LedId.Keyboard_Custom30}, + {InputKey.BrowserRefresh, LedId.Keyboard_Custom31}, + {InputKey.BrowserStop, LedId.Keyboard_Custom32}, + {InputKey.BrowserSearch, LedId.Keyboard_Custom33}, + {InputKey.BrowserFavorites, LedId.Keyboard_Custom34}, + {InputKey.BrowserHome, LedId.Keyboard_Custom35}, + {InputKey.VolumeMute, LedId.Keyboard_MediaMute}, + {InputKey.VolumeDown, LedId.Keyboard_MediaVolumeDown}, + {InputKey.VolumeUp, LedId.Keyboard_MediaVolumeUp}, + {InputKey.MediaNextTrack, LedId.Keyboard_MediaNextTrack}, + {InputKey.MediaPreviousTrack, LedId.Keyboard_MediaPreviousTrack}, + {InputKey.MediaStop, LedId.Keyboard_MediaStop}, + {InputKey.MediaPlayPause, LedId.Keyboard_MediaPlay}, + {InputKey.LaunchMail, LedId.Keyboard_Custom36}, + {InputKey.SelectMedia, LedId.Keyboard_Custom37}, + {InputKey.LaunchApplication1, LedId.Keyboard_Custom38}, + {InputKey.LaunchApplication2, LedId.Keyboard_Custom39}, + {InputKey.OemSemicolon, LedId.Keyboard_SemicolonAndColon}, + {InputKey.OemPlus, LedId.Keyboard_EqualsAndPlus}, + {InputKey.OemMinus, LedId.Keyboard_MinusAndUnderscore}, + {InputKey.OemComma, LedId.Keyboard_CommaAndLessThan}, + {InputKey.OemPeriod, LedId.Keyboard_PeriodAndBiggerThan}, + {InputKey.OemQuestion, LedId.Keyboard_SlashAndQuestionMark}, + {InputKey.OemTilde, LedId.Keyboard_GraveAccentAndTilde}, + {InputKey.AbntC1, LedId.Keyboard_Custom40}, + {InputKey.AbntC2, LedId.Keyboard_Custom41}, + {InputKey.OemOpenBrackets, LedId.Keyboard_BracketLeft}, + {InputKey.OemPipe, LedId.Keyboard_Backslash}, + {InputKey.OemCloseBrackets, LedId.Keyboard_BracketRight}, + {InputKey.OemQuotes, LedId.Keyboard_ApostropheAndDoubleQuote}, + {InputKey.OemBackslash, LedId.Keyboard_Custom42}, // unverified + {InputKey.ImeProcessed, LedId.Keyboard_Custom43}, + {InputKey.System, LedId.Keyboard_Custom44}, + {InputKey.OemAttn, LedId.Keyboard_Custom45}, + {InputKey.OemFinish, LedId.Keyboard_Custom46}, + {InputKey.OemCopy, LedId.Keyboard_Custom47}, + {InputKey.OemAuto, LedId.Keyboard_Custom48}, + {InputKey.OemEnlw, LedId.Keyboard_Custom49}, + {InputKey.OemBackTab, LedId.Keyboard_Custom50}, + {InputKey.Attn, LedId.Keyboard_Custom51}, + {InputKey.CrSel, LedId.Keyboard_Custom52}, + {InputKey.ExSel, LedId.Keyboard_Custom53}, + {InputKey.EraseEof, LedId.Keyboard_Custom54}, + {InputKey.Play, LedId.Keyboard_MediaPlay}, + {InputKey.Zoom, LedId.Keyboard_Custom55}, + {InputKey.NoName, LedId.Keyboard_Custom56}, + {InputKey.Pa1, LedId.Keyboard_Custom57}, + {InputKey.OemClear, LedId.Keyboard_Custom58}, + {InputKey.DeadCharProcessed, LedId.Keyboard_Custom59}, + {InputKey.NumPadEnter, LedId.Keyboard_NumEnter}, + }; + } +} diff --git a/src/Artemis.Core/Services/Input/InputProvider.cs b/src/Artemis.Core/Services/Input/InputProvider.cs new file mode 100644 index 000000000..07ddd71a6 --- /dev/null +++ b/src/Artemis.Core/Services/Input/InputProvider.cs @@ -0,0 +1,49 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Represents an interface for an input provider that provides and implementation for sending and receiving device + /// input + /// + public abstract class InputProvider : IDisposable + { + /// + /// Occurs when the input provided has received keyboard data + /// + public event EventHandler? KeyboardDataReceived; + + /// + /// Invokes the event + /// + protected virtual void OnKeyboardDataReceived(InputProviderKeyboardEventArgs e) + { + KeyboardDataReceived?.Invoke(this, e); + } + + #region IDisposable + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + } + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/InputProviderKeyboardEventArgs.cs b/src/Artemis.Core/Services/Input/InputProviderKeyboardEventArgs.cs new file mode 100644 index 000000000..a021d0e6e --- /dev/null +++ b/src/Artemis.Core/Services/Input/InputProviderKeyboardEventArgs.cs @@ -0,0 +1,37 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for input provider keyboard events + /// + public class InputProviderKeyboardEventArgs : EventArgs + { + /// + /// + /// The device that triggered the event + /// The key that triggered the event + /// Whether the key is pressed down + public InputProviderKeyboardEventArgs(ArtemisDevice device, InputKey key, bool isDown) + { + Device = device; + Key = key; + IsDown = isDown; + } + + /// + /// Gets the device that triggered the event + /// + public ArtemisDevice Device { get; } + + /// + /// Gets the key that triggered the event + /// + public InputKey Key { get; } + + /// + /// Gets whether the key is pressed down + /// + public bool IsDown { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/InputService.cs b/src/Artemis.Core/Services/Input/InputService.cs new file mode 100644 index 000000000..d342352ef --- /dev/null +++ b/src/Artemis.Core/Services/Input/InputService.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using RGB.NET.Core; +using Serilog; + +namespace Artemis.Core.Services +{ + internal class InputService : IInputService + { + private readonly ILogger _logger; + private readonly List _inputProviders; + + public InputService(ILogger logger) + { + _logger = logger; + _inputProviders = new List(); + } + + public void AddInputProvider(InputProvider inputProvider) + { + _inputProviders.Add(inputProvider); + inputProvider.KeyboardDataReceived += InputProviderOnKeyboardDataReceived; + } + + public void RemoveInputProvider(InputProvider inputProvider) + { + if (!_inputProviders.Contains(inputProvider)) + return; + + inputProvider.KeyboardDataReceived -= InputProviderOnKeyboardDataReceived; + } + + private void InputProviderOnKeyboardDataReceived(object? sender, InputProviderKeyboardEventArgs e) + { + if (!(sender is InputProvider inputProvider)) + return; + + bool foundLedId = InputKeyUtilities.LedIdMap.TryGetValue(e.Key, out LedId ledId); + _logger.Verbose("Received keyboard data: LED ID: {ledId}, key: {key}, is down: {isDown}, device: {device} ", ledId, e.Key, e.IsDown, e.Device); + } + } + + /// + /// A service that allows you to interact with keyboard and mice input events + /// + public interface IInputService : IArtemisService + { + /// + /// Adds an input provided + /// + /// The input provider the add + void AddInputProvider(InputProvider inputProvider); + + /// + /// Removes an input provided + /// + /// The input provider the remove + void RemoveInputProvider(InputProvider inputProvider); + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs index e9b401081..f0cafdff2 100644 --- a/src/Artemis.Core/Services/PluginManagementService.cs +++ b/src/Artemis.Core/Services/PluginManagementService.cs @@ -245,7 +245,7 @@ namespace Artemis.Core.Services // Load the entity and fall back on creating a new one Plugin plugin = new Plugin(pluginInfo, directory, _pluginRepository.GetPluginByGuid(pluginInfo.Guid)); OnPluginLoading(new PluginEventArgs(plugin)); - + // Locate the main assembly entry string? mainFile = plugin.ResolveRelativePath(plugin.Info.Main); if (!File.Exists(mainFile)) @@ -289,6 +289,8 @@ namespace Artemis.Core.Services // Create the Ninject child kernel and load the module plugin.Kernel = new ChildKernel(_kernel, new PluginModule(plugin)); + OnPluginEnabling(new PluginEventArgs(plugin)); + plugin.SetEnabled(true); // Get the Plugin feature from the main assembly and if there is only one, instantiate it diff --git a/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs b/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs index 6bc93a3ad..38712cf13 100644 --- a/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs +++ b/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs @@ -1,12 +1,27 @@ -namespace Artemis.Storage.Entities.Surface +using System.Collections.Generic; + +namespace Artemis.Storage.Entities.Surface { public class DeviceEntity { + public DeviceEntity() + { + InputIdentifier = new List(); + } + public string DeviceIdentifier { get; set; } public double X { get; set; } public double Y { get; set; } public double Rotation { get; set; } public double Scale { get; set; } public int ZIndex { get; set; } + + public List InputIdentifier { get; set; } + } + + public class DeviceInputIdentifierEntity + { + public string InputProvider { get; set; } + public object Identifier { get; set; } } } \ No newline at end of file diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index fa3a16690..00b623a35 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -142,6 +142,7 @@ + diff --git a/src/Artemis.UI/Bootstrapper.cs b/src/Artemis.UI/Bootstrapper.cs index 7f9496f63..0af16c76c 100644 --- a/src/Artemis.UI/Bootstrapper.cs +++ b/src/Artemis.UI/Bootstrapper.cs @@ -14,6 +14,7 @@ using Artemis.Core.Ninject; using Artemis.Core.Services; using Artemis.UI.Ninject; using Artemis.UI.Screens; +using Artemis.UI.Services; using Artemis.UI.Shared; using Artemis.UI.Shared.Services; using Artemis.UI.Stylet; @@ -42,6 +43,7 @@ namespace Artemis.UI ILogger logger = Kernel.Get(); IViewManager viewManager = Kernel.Get(); + IRegistrationService registrationService = Kernel.Get(); StartupArguments = Args.ToList(); CreateDataDirectory(logger); @@ -70,7 +72,7 @@ namespace Artemis.UI { if (StartupArguments.Contains("--autorun")) { - logger.Information("Sleeping for 15 seconds on auto run to allow applications like iCUE and LGS to start"); + logger.Information("Sleeping for 15 seconds on auto run to allow vendor applications required for SDKs to start"); await Task.Delay(TimeSpan.FromSeconds(15)); } @@ -83,6 +85,8 @@ namespace Artemis.UI throw; } }); + + registrationService.RegisterInputProvider(); } protected override void ConfigureIoC(IKernel kernel) diff --git a/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs b/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs new file mode 100644 index 000000000..41bb7dcd5 --- /dev/null +++ b/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using System.Windows.Input; +using Artemis.Core; +using Artemis.Core.Services; +using Linearstar.Windows.RawInput; +using Linearstar.Windows.RawInput.Native; +using RGB.NET.Core; + +namespace Artemis.UI.InputProviders +{ + public class NativeWindowInputProvider : InputProvider + { + private const int WM_INPUT = 0x00FF; + + private readonly ISurfaceService _surfaceService; + private List _keyboards; + private DateTime _lastMouseUpdate; + private List _mice; + private SpongeWindow _sponge; + + public NativeWindowInputProvider(ISurfaceService surfaceService) + { + _surfaceService = surfaceService; + + _sponge = new SpongeWindow(); + _sponge.WndProcCalled += SpongeOnWndProcCalled; + + RawInputDevice.RegisterDevice(HidUsageAndPage.Keyboard, RawInputDeviceFlags.ExInputSink | RawInputDeviceFlags.NoLegacy, _sponge.Handle); + RawInputDevice.RegisterDevice(HidUsageAndPage.Mouse, RawInputDeviceFlags.InputSink, _sponge.Handle); + + _surfaceService.ActiveSurfaceConfigurationSelected += SurfaceConfigurationChanged; + _surfaceService.SurfaceConfigurationUpdated += SurfaceConfigurationChanged; + GetDevices(surfaceService.ActiveSurface); + } + + #region IDisposable + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + _sponge?.DestroyHandle(); + _sponge = null; + + _surfaceService.ActiveSurfaceConfigurationSelected -= SurfaceConfigurationChanged; + _surfaceService.SurfaceConfigurationUpdated -= SurfaceConfigurationChanged; + } + + base.Dispose(disposing); + } + + #endregion + + private void SurfaceConfigurationChanged(object sender, SurfaceConfigurationEventArgs e) + { + GetDevices(e.Surface); + } + + private void GetDevices(ArtemisSurface surface) + { + _keyboards = surface.Devices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard).ToList(); + _mice = surface.Devices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse).ToList(); + } + + private void SpongeOnWndProcCalled(object sender, Message message) + { + if (message.Msg != WM_INPUT) + return; + + RawInputData data = RawInputData.FromHandle(message.LParam); + switch (data) + { + case RawInputMouseData mouse: + HandleMouseData(mouse); + break; + case RawInputKeyboardData keyboard: + HandleKeyboardData(keyboard); + break; + } + } + + private void HandleKeyboardData(RawInputKeyboardData data) + { + // Get the keyboard that submitted the data + ArtemisDevice match = _keyboards?.FirstOrDefault(); + if (match == null) + return; + + InputKey key = (InputKey) KeyInterop.KeyFromVirtualKey(data.Keyboard.VirutalKey); + + // Debug.WriteLine($"VK: {key} ({data.Keyboard.VirutalKey}), Flags: {data.Keyboard.Flags}, Scan code: {data.Keyboard.ScanCode}"); + + // Sometimes we get double hits and they resolve to None, ignore those + if (key == InputKey.None) + return; + + // Right alt triggers LeftCtrl with a different scan code for some reason, ignore those + if (key == InputKey.LeftCtrl && data.Keyboard.ScanCode == 56) + return; + + // Duplicate keys with different positions can be identified by the LeftKey flag (even though its set of the key that's physically on the right) + if (data.Keyboard.Flags == RawKeyboardFlags.LeftKey || data.Keyboard.Flags == (RawKeyboardFlags.LeftKey | RawKeyboardFlags.Up)) + { + if (key == InputKey.Enter) + key = InputKey.NumPadEnter; + if (key == InputKey.LeftCtrl) + key = InputKey.RightCtrl; + if (key == InputKey.LeftAlt) + key = InputKey.RightAlt; + } + + if (key == InputKey.LeftShift && data.Keyboard.ScanCode == 54) + key = InputKey.RightShift; + + bool isDown = data.Keyboard.Flags != RawKeyboardFlags.Up && + data.Keyboard.Flags != (RawKeyboardFlags.Up | RawKeyboardFlags.LeftKey) && + data.Keyboard.Flags != (RawKeyboardFlags.Up | RawKeyboardFlags.RightKey); + + OnKeyboardDataReceived(new InputProviderKeyboardEventArgs(match, key, isDown)); + } + + private void HandleMouseData(RawInputMouseData data) + { + // Only handle mouse movement 25 times per second + if (data.Mouse.Buttons == RawMouseButtonFlags.None) + if (DateTime.Now - _lastMouseUpdate < TimeSpan.FromMilliseconds(40)) + return; + + _lastMouseUpdate = DateTime.Now; + + // Get the keyboard that submitted the data + ArtemisDevice match = _mice?.FirstOrDefault(); + if (match == null) + return; + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/InputProviders/SpongeWindow.cs b/src/Artemis.UI/InputProviders/SpongeWindow.cs new file mode 100644 index 000000000..34898ffcd --- /dev/null +++ b/src/Artemis.UI/InputProviders/SpongeWindow.cs @@ -0,0 +1,26 @@ +using System; +using System.Windows.Forms; + +namespace Artemis.UI.InputProviders +{ + public sealed class SpongeWindow : NativeWindow + { + public SpongeWindow() + { + CreateHandle(new CreateParams()); + } + + public event EventHandler WndProcCalled; + + protected override void WndProc(ref Message m) + { + OnWndProcCalled(m); + base.WndProc(ref m); + } + + private void OnWndProcCalled(Message e) + { + WndProcCalled?.Invoke(this, e); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Services/RegistrationService.cs b/src/Artemis.UI/Services/RegistrationService.cs index cf4fc5ffc..de139a508 100644 --- a/src/Artemis.UI/Services/RegistrationService.cs +++ b/src/Artemis.UI/Services/RegistrationService.cs @@ -3,6 +3,7 @@ using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.DefaultTypes.DataModel.Display; using Artemis.UI.DefaultTypes.DataModel.Input; +using Artemis.UI.InputProviders; using Artemis.UI.Ninject; using Artemis.UI.PropertyInput; using Artemis.UI.Services.Interfaces; @@ -15,18 +16,26 @@ namespace Artemis.UI.Services private readonly IDataModelUIService _dataModelUIService; private readonly IProfileEditorService _profileEditorService; private readonly IPluginManagementService _pluginManagementService; + private readonly ISurfaceService _surfaceService; + private readonly IInputService _inputService; private bool _registeredBuiltInDataModelDisplays; private bool _registeredBuiltInDataModelInputs; private bool _registeredBuiltInPropertyEditors; - public RegistrationService(IDataModelUIService dataModelUIService, IProfileEditorService profileEditorService, IPluginManagementService pluginManagementService) + public RegistrationService(IDataModelUIService dataModelUIService, + IProfileEditorService profileEditorService, + IPluginManagementService pluginManagementService, + ISurfaceService surfaceService, + IInputService inputService) { _dataModelUIService = dataModelUIService; _profileEditorService = profileEditorService; _pluginManagementService = pluginManagementService; + _surfaceService = surfaceService; + _inputService = inputService; LoadPluginModules(); - pluginManagementService.PluginLoaded += PluginServiceOnPluginLoaded; + pluginManagementService.PluginEnabling += PluginServiceOnPluginEnabling; } public void RegisterBuiltInDataModelDisplays() @@ -74,7 +83,12 @@ namespace Artemis.UI.Services _registeredBuiltInPropertyEditors = true; } - private void PluginServiceOnPluginLoaded(object sender, PluginEventArgs e) + public void RegisterInputProvider() + { + _inputService.AddInputProvider(new NativeWindowInputProvider(_surfaceService)); + } + + private void PluginServiceOnPluginEnabling(object sender, PluginEventArgs e) { e.Plugin.Kernel.Load(new[] {new PluginUIModule(e.Plugin)}); } @@ -91,5 +105,6 @@ namespace Artemis.UI.Services void RegisterBuiltInDataModelDisplays(); void RegisterBuiltInDataModelInputs(); void RegisterBuiltInPropertyEditors(); + void RegisterInputProvider(); } } \ No newline at end of file diff --git a/src/Artemis.UI/packages.lock.json b/src/Artemis.UI/packages.lock.json index 8d7a7ed8d..bf637b63b 100644 --- a/src/Artemis.UI/packages.lock.json +++ b/src/Artemis.UI/packages.lock.json @@ -80,6 +80,15 @@ "Ninject.Extensions.Factory": "3.3.2" } }, + "RawInput.Sharp": { + "type": "Direct", + "requested": "[0.0.3, )", + "resolved": "0.0.3", + "contentHash": "X5EuJYuU0RWzOnA68cHmLEJ4czhgu0EsQj3o5RtYSdTNnTnXo/zVNAXQWPWbBXgoLsws1WTsT567yX0HKjS4aA==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, "Serilog": { "type": "Direct", "requested": "[2.9.0, )",