From 810db3619158dbcd76c11111f3ed8361906840e7 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Sun, 22 Nov 2020 00:01:55 +0100 Subject: [PATCH 1/6] 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, )", From 84918f2d5b769155347917cdebc88278ac0454d1 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Sun, 22 Nov 2020 21:33:42 +0100 Subject: [PATCH 2/6] Input - Added mouse support Input - Added device identification --- .../Artemis.Core.csproj.DotSettings | 3 + .../Input/Enums/InputFallbackDeviceType.cs | 23 ++ .../{InputKey.cs => Enums/KeyboardKey.cs} | 186 ++++++++- .../Input/Enums/KeyboardModifierKeys.cs | 26 ++ .../Services/Input/Enums/MouseButton.cs | 33 ++ .../Input/Enums/MouseScrollDirection.cs | 14 + .../InputProviderKeyboardEventArgs.cs | 8 +- .../InputProviderMouseButtonEventArgs.cs | 37 ++ .../Events/InputProviderMouseMoveEventArgs.cs | 51 +++ .../InputProviderMouseScrollEventArgs.cs | 37 ++ .../Input/Events/KeyboardEventArgs.cs | 54 +++ .../Services/Input/InputKeyLedIdMap.cs | 359 +++++++++--------- .../Services/Input/InputProvider.cs | 61 ++- .../Services/Input/InputService.cs | 280 ++++++++++++-- .../Input/Interfaces/IInputService.cs | 75 ++++ .../NativeWindowInputProvider.cs | 200 +++++++--- .../Screens/Splash/SplashViewModel.cs | 1 + .../Dialogs/SurfaceDeviceDetectInputView.xaml | 35 ++ .../SurfaceDeviceDetectInputViewModel.cs | 65 ++++ .../SurfaceEditor/SurfaceEditorView.xaml | 8 + .../SurfaceEditor/SurfaceEditorViewModel.cs | 22 +- .../Services/RegistrationService.cs | 2 +- 22 files changed, 1309 insertions(+), 271 deletions(-) create mode 100644 src/Artemis.Core/Services/Input/Enums/InputFallbackDeviceType.cs rename src/Artemis.Core/Services/Input/{InputKey.cs => Enums/KeyboardKey.cs} (97%) create mode 100644 src/Artemis.Core/Services/Input/Enums/KeyboardModifierKeys.cs create mode 100644 src/Artemis.Core/Services/Input/Enums/MouseButton.cs create mode 100644 src/Artemis.Core/Services/Input/Enums/MouseScrollDirection.cs rename src/Artemis.Core/Services/Input/{ => Events}/InputProviderKeyboardEventArgs.cs (77%) create mode 100644 src/Artemis.Core/Services/Input/Events/InputProviderMouseButtonEventArgs.cs create mode 100644 src/Artemis.Core/Services/Input/Events/InputProviderMouseMoveEventArgs.cs create mode 100644 src/Artemis.Core/Services/Input/Events/InputProviderMouseScrollEventArgs.cs create mode 100644 src/Artemis.Core/Services/Input/Events/KeyboardEventArgs.cs create mode 100644 src/Artemis.Core/Services/Input/Interfaces/IInputService.cs create mode 100644 src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputView.xaml create mode 100644 src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputViewModel.cs diff --git a/src/Artemis.Core/Artemis.Core.csproj.DotSettings b/src/Artemis.Core/Artemis.Core.csproj.DotSettings index 914c8e232..0ff3e797c 100644 --- a/src/Artemis.Core/Artemis.Core.csproj.DotSettings +++ b/src/Artemis.Core/Artemis.Core.csproj.DotSettings @@ -56,6 +56,9 @@ True True True + True + True + True True True True diff --git a/src/Artemis.Core/Services/Input/Enums/InputFallbackDeviceType.cs b/src/Artemis.Core/Services/Input/Enums/InputFallbackDeviceType.cs new file mode 100644 index 000000000..b1590f7b4 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Enums/InputFallbackDeviceType.cs @@ -0,0 +1,23 @@ +namespace Artemis.Core.Services +{ + /// + /// Represents a device that provides input to the + /// + public enum InputFallbackDeviceType + { + /// + /// None + /// + None, + + /// + /// A keyboard + /// + Keyboard, + + /// + /// A mouse + /// + Mouse + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/InputKey.cs b/src/Artemis.Core/Services/Input/Enums/KeyboardKey.cs similarity index 97% rename from src/Artemis.Core/Services/Input/InputKey.cs rename to src/Artemis.Core/Services/Input/Enums/KeyboardKey.cs index b40842b01..4f13f0846 100644 --- a/src/Artemis.Core/Services/Input/InputKey.cs +++ b/src/Artemis.Core/Services/Input/Enums/KeyboardKey.cs @@ -1,359 +1,537 @@ namespace Artemis.Core.Services { /// Specifies the possible key values on a keyboard. - public enum InputKey + public enum KeyboardKey { /// 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. + + /// + /// 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, + NumPadEnter } -} +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKeys.cs b/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKeys.cs new file mode 100644 index 000000000..4cc0c7f5d --- /dev/null +++ b/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKeys.cs @@ -0,0 +1,26 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Specifies the set of modifier keys. + /// + [Flags] + public enum KeyboardModifierKeys + { + /// No modifiers are pressed. + None = 0, + + /// The ALT key. + Alt = 1, + + /// The CTRL key. + Control = 2, + + /// The SHIFT key. + Shift = 4, + + /// The Windows logo key. + Windows = 8 + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Enums/MouseButton.cs b/src/Artemis.Core/Services/Input/Enums/MouseButton.cs new file mode 100644 index 000000000..c139de97d --- /dev/null +++ b/src/Artemis.Core/Services/Input/Enums/MouseButton.cs @@ -0,0 +1,33 @@ +namespace Artemis.Core.Services +{ + /// + /// Specifies the buttons on a mouse + /// + public enum MouseButton + { + /// + /// The left mouse button + /// + Left, + + /// + /// The middle mouse button + /// + Middle, + + /// + /// The right mouse button + /// + Right, + + /// + /// Extra mouse button 4 (backwards) + /// + Button4, + + /// + /// Extra mouse button 5 (forwards) + /// + Button5 + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Enums/MouseScrollDirection.cs b/src/Artemis.Core/Services/Input/Enums/MouseScrollDirection.cs new file mode 100644 index 000000000..b118ea60a --- /dev/null +++ b/src/Artemis.Core/Services/Input/Enums/MouseScrollDirection.cs @@ -0,0 +1,14 @@ +namespace Artemis.Core.Services +{ + /// + /// Specifies mouse scrolling directions + /// + public enum MouseScrollDirection + { + /// A vertical scroll direction + Vertical, + + /// A horizontal scroll direction + Horizontal + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/InputProviderKeyboardEventArgs.cs b/src/Artemis.Core/Services/Input/Events/InputProviderKeyboardEventArgs.cs similarity index 77% rename from src/Artemis.Core/Services/Input/InputProviderKeyboardEventArgs.cs rename to src/Artemis.Core/Services/Input/Events/InputProviderKeyboardEventArgs.cs index a021d0e6e..313cd593f 100644 --- a/src/Artemis.Core/Services/Input/InputProviderKeyboardEventArgs.cs +++ b/src/Artemis.Core/Services/Input/Events/InputProviderKeyboardEventArgs.cs @@ -3,7 +3,7 @@ namespace Artemis.Core.Services { /// - /// Contains data for input provider keyboard events + /// Contains data for input provider keyboard events /// public class InputProviderKeyboardEventArgs : EventArgs { @@ -12,7 +12,7 @@ namespace Artemis.Core.Services /// 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) + public InputProviderKeyboardEventArgs(ArtemisDevice? device, KeyboardKey key, bool isDown) { Device = device; Key = key; @@ -22,12 +22,12 @@ namespace Artemis.Core.Services /// /// Gets the device that triggered the event /// - public ArtemisDevice Device { get; } + public ArtemisDevice? Device { get; } /// /// Gets the key that triggered the event /// - public InputKey Key { get; } + public KeyboardKey Key { get; } /// /// Gets whether the key is pressed down diff --git a/src/Artemis.Core/Services/Input/Events/InputProviderMouseButtonEventArgs.cs b/src/Artemis.Core/Services/Input/Events/InputProviderMouseButtonEventArgs.cs new file mode 100644 index 000000000..c2f583843 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/InputProviderMouseButtonEventArgs.cs @@ -0,0 +1,37 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for input provider mouse button events + /// + public class InputProviderMouseButtonEventArgs : EventArgs + { + /// + /// + /// The device that triggered the event + /// The button that triggered the event + /// Whether the button is pressed down + public InputProviderMouseButtonEventArgs(ArtemisDevice? device, MouseButton button, bool isDown) + { + Device = device; + Button = button; + IsDown = isDown; + } + + /// + /// Gets the device that triggered the event + /// + public ArtemisDevice? Device { get; } + + /// + /// Gets the button that triggered the event + /// + public MouseButton Button { get; } + + /// + /// Gets whether the button is pressed down + /// + public bool IsDown { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Events/InputProviderMouseMoveEventArgs.cs b/src/Artemis.Core/Services/Input/Events/InputProviderMouseMoveEventArgs.cs new file mode 100644 index 000000000..ceeb8823d --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/InputProviderMouseMoveEventArgs.cs @@ -0,0 +1,51 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for input provider mouse movement events + /// + public class InputProviderMouseMoveEventArgs : EventArgs + { + /// + /// + /// The device that triggered the event + /// The X position of the mouse cursor (not necessarily tied to the specific device) + /// The Y position of the mouse cursor (not necessarily tied to the specific device) + /// The movement delta in the horizontal direction + /// The movement delta in the vertical direction + public InputProviderMouseMoveEventArgs(ArtemisDevice? device, int cursorX, int cursorY, int deltaX, int deltaY) + { + Device = device; + CursorX = cursorX; + CursorY = cursorY; + DeltaX = deltaX; + DeltaY = deltaY; + } + + /// + /// Gets the device that triggered the event + /// + public ArtemisDevice? Device { get; } + + /// + /// Gets the X position of the mouse cursor (not necessarily tied to the specific device) + /// + public int CursorX { get; } + + /// + /// Gets the Y position of the mouse cursor (not necessarily tied to the specific device) + /// + public int CursorY { get; } + + /// + /// Gets the movement delta in the horizontal direction + /// + public int DeltaX { get; } + + /// + /// Gets the movement delta in the vertical direction + /// + public int DeltaY { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Events/InputProviderMouseScrollEventArgs.cs b/src/Artemis.Core/Services/Input/Events/InputProviderMouseScrollEventArgs.cs new file mode 100644 index 000000000..db03376de --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/InputProviderMouseScrollEventArgs.cs @@ -0,0 +1,37 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for input provider mouse button events + /// + public class InputProviderMouseScrollEventArgs : EventArgs + { + /// + /// + /// The device that triggered the event + /// The direction in which was scrolled + /// The scroll delta (can positive or negative) + public InputProviderMouseScrollEventArgs(ArtemisDevice? device, MouseScrollDirection direction, int delta) + { + Device = device; + Direction = direction; + Delta = delta; + } + + /// + /// Gets the device that triggered the event + /// + public ArtemisDevice? Device { get; } + + /// + /// Gets the direction in which was scrolled + /// + public MouseScrollDirection Direction { get; } + + /// + /// Gets the scroll delta (can positive or negative) + /// + public int Delta { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Events/KeyboardEventArgs.cs b/src/Artemis.Core/Services/Input/Events/KeyboardEventArgs.cs new file mode 100644 index 000000000..3372555ed --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/KeyboardEventArgs.cs @@ -0,0 +1,54 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for keyboard input events + /// + public class KeyboardEventArgs : EventArgs + { + internal KeyboardEventArgs(ArtemisDevice? device, ArtemisLed? led, KeyboardKey key, KeyboardModifierKeys modifiers) + { + Device = device; + Led = led; + Key = key; + Modifiers = modifiers; + } + + /// + /// Gets the device that triggered the event + /// + public ArtemisDevice? Device { get; } + + /// + /// Gets the LED that corresponds to the pressed key + /// + public ArtemisLed? Led { get; } + + /// + /// Gets the key that triggered the event + /// + public KeyboardKey Key { get; } + + /// + /// Gets the modifiers that are pressed + /// + public KeyboardModifierKeys Modifiers { get; } + } + + /// + /// Contains data for keyboard input events + /// + public class KeyboardKeyUpDownEventArgs : KeyboardEventArgs + { + internal KeyboardKeyUpDownEventArgs(ArtemisDevice? device, ArtemisLed? led, KeyboardKey key, KeyboardModifierKeys modifiers, bool isDown) : base(device, led, key, modifiers) + { + IsDown = isDown; + } + + /// + /// Whether the key is being pressed down, if the key is being released + /// + public bool IsDown { get; set; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/InputKeyLedIdMap.cs b/src/Artemis.Core/Services/Input/InputKeyLedIdMap.cs index bd53d2717..dc485e69c 100644 --- a/src/Artemis.Core/Services/Input/InputKeyLedIdMap.cs +++ b/src/Artemis.Core/Services/Input/InputKeyLedIdMap.cs @@ -5,181 +5,190 @@ namespace Artemis.Core.Services { internal static class InputKeyUtilities { - internal static readonly Dictionary LedIdMap = new Dictionary() + internal static readonly Dictionary KeyboardKeyLedIdMap = 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}, + {KeyboardKey.None, LedId.Keyboard_Custom1}, + {KeyboardKey.Cancel, LedId.Keyboard_Custom2}, + {KeyboardKey.Back, LedId.Keyboard_Backspace}, + {KeyboardKey.Tab, LedId.Keyboard_Tab}, + {KeyboardKey.LineFeed, LedId.Keyboard_Custom3}, + {KeyboardKey.Clear, LedId.Keyboard_Custom4}, + {KeyboardKey.Enter, LedId.Keyboard_Enter}, + {KeyboardKey.Pause, LedId.Keyboard_PauseBreak}, + {KeyboardKey.CapsLock, LedId.Keyboard_CapsLock}, + {KeyboardKey.HangulMode, LedId.Keyboard_Custom4}, + {KeyboardKey.JunjaMode, LedId.Keyboard_Custom5}, + {KeyboardKey.FinalMode, LedId.Keyboard_Custom6}, + {KeyboardKey.HanjaMode, LedId.Keyboard_Custom7}, + {KeyboardKey.Escape, LedId.Keyboard_Escape}, + {KeyboardKey.ImeConvert, LedId.Keyboard_Custom8}, + {KeyboardKey.ImeNonConvert, LedId.Keyboard_Custom9}, + {KeyboardKey.ImeAccept, LedId.Keyboard_Custom10}, + {KeyboardKey.ImeModeChange, LedId.Keyboard_Custom11}, + {KeyboardKey.Space, LedId.Keyboard_Space}, + {KeyboardKey.PageUp, LedId.Keyboard_PageUp}, + {KeyboardKey.PageDown, LedId.Keyboard_PageDown}, + {KeyboardKey.End, LedId.Keyboard_End}, + {KeyboardKey.Home, LedId.Keyboard_Home}, + {KeyboardKey.Left, LedId.Keyboard_ArrowLeft}, + {KeyboardKey.Up, LedId.Keyboard_ArrowUp}, + {KeyboardKey.Right, LedId.Keyboard_ArrowRight}, + {KeyboardKey.Down, LedId.Keyboard_ArrowDown}, + {KeyboardKey.Select, LedId.Keyboard_Custom12}, + {KeyboardKey.Print, LedId.Keyboard_Custom13}, + {KeyboardKey.Execute, LedId.Keyboard_Custom14}, + {KeyboardKey.PrintScreen, LedId.Keyboard_PrintScreen}, + {KeyboardKey.Insert, LedId.Keyboard_Insert}, + {KeyboardKey.Delete, LedId.Keyboard_Delete}, + {KeyboardKey.Help, LedId.Keyboard_Custom15}, + {KeyboardKey.D0, LedId.Keyboard_0}, + {KeyboardKey.D1, LedId.Keyboard_1}, + {KeyboardKey.D2, LedId.Keyboard_2}, + {KeyboardKey.D3, LedId.Keyboard_3}, + {KeyboardKey.D4, LedId.Keyboard_4}, + {KeyboardKey.D5, LedId.Keyboard_5}, + {KeyboardKey.D6, LedId.Keyboard_6}, + {KeyboardKey.D7, LedId.Keyboard_7}, + {KeyboardKey.D8, LedId.Keyboard_8}, + {KeyboardKey.D9, LedId.Keyboard_9}, + {KeyboardKey.A, LedId.Keyboard_A}, + {KeyboardKey.B, LedId.Keyboard_B}, + {KeyboardKey.C, LedId.Keyboard_C}, + {KeyboardKey.D, LedId.Keyboard_D}, + {KeyboardKey.E, LedId.Keyboard_E}, + {KeyboardKey.F, LedId.Keyboard_F}, + {KeyboardKey.G, LedId.Keyboard_G}, + {KeyboardKey.H, LedId.Keyboard_H}, + {KeyboardKey.I, LedId.Keyboard_I}, + {KeyboardKey.J, LedId.Keyboard_J}, + {KeyboardKey.K, LedId.Keyboard_K}, + {KeyboardKey.L, LedId.Keyboard_L}, + {KeyboardKey.M, LedId.Keyboard_M}, + {KeyboardKey.N, LedId.Keyboard_N}, + {KeyboardKey.O, LedId.Keyboard_O}, + {KeyboardKey.P, LedId.Keyboard_P}, + {KeyboardKey.Q, LedId.Keyboard_Q}, + {KeyboardKey.R, LedId.Keyboard_R}, + {KeyboardKey.S, LedId.Keyboard_S}, + {KeyboardKey.T, LedId.Keyboard_T}, + {KeyboardKey.U, LedId.Keyboard_U}, + {KeyboardKey.V, LedId.Keyboard_V}, + {KeyboardKey.W, LedId.Keyboard_W}, + {KeyboardKey.X, LedId.Keyboard_X}, + {KeyboardKey.Y, LedId.Keyboard_Y}, + {KeyboardKey.Z, LedId.Keyboard_Z}, + {KeyboardKey.LWin, LedId.Keyboard_LeftGui}, + {KeyboardKey.RWin, LedId.Keyboard_RightGui}, + {KeyboardKey.Apps, LedId.Keyboard_Application}, + {KeyboardKey.Sleep, LedId.Keyboard_Custom16}, + {KeyboardKey.NumPad0, LedId.Keyboard_Num0}, + {KeyboardKey.NumPad1, LedId.Keyboard_Num1}, + {KeyboardKey.NumPad2, LedId.Keyboard_Num2}, + {KeyboardKey.NumPad3, LedId.Keyboard_Num3}, + {KeyboardKey.NumPad4, LedId.Keyboard_Num4}, + {KeyboardKey.NumPad5, LedId.Keyboard_Num5}, + {KeyboardKey.NumPad6, LedId.Keyboard_Num6}, + {KeyboardKey.NumPad7, LedId.Keyboard_Num7}, + {KeyboardKey.NumPad8, LedId.Keyboard_Num8}, + {KeyboardKey.NumPad9, LedId.Keyboard_Num9}, + {KeyboardKey.Multiply, LedId.Keyboard_NumAsterisk}, + {KeyboardKey.Add, LedId.Keyboard_NumPlus}, + {KeyboardKey.Separator, LedId.Keyboard_NumEnter}, // unverified + {KeyboardKey.Subtract, LedId.Keyboard_NumMinus}, + {KeyboardKey.Decimal, LedId.Keyboard_NumPeriodAndDelete}, + {KeyboardKey.Divide, LedId.Keyboard_NumSlash}, + {KeyboardKey.F1, LedId.Keyboard_F1}, + {KeyboardKey.F2, LedId.Keyboard_F2}, + {KeyboardKey.F3, LedId.Keyboard_F3}, + {KeyboardKey.F4, LedId.Keyboard_F4}, + {KeyboardKey.F5, LedId.Keyboard_F5}, + {KeyboardKey.F6, LedId.Keyboard_F6}, + {KeyboardKey.F7, LedId.Keyboard_F7}, + {KeyboardKey.F8, LedId.Keyboard_F8}, + {KeyboardKey.F9, LedId.Keyboard_F9}, + {KeyboardKey.F10, LedId.Keyboard_F10}, + {KeyboardKey.F11, LedId.Keyboard_F11}, + {KeyboardKey.F12, LedId.Keyboard_F12}, + {KeyboardKey.F13, LedId.Keyboard_Custom17}, + {KeyboardKey.F14, LedId.Keyboard_Custom18}, + {KeyboardKey.F15, LedId.Keyboard_Custom19}, + {KeyboardKey.F16, LedId.Keyboard_Custom20}, + {KeyboardKey.F17, LedId.Keyboard_Custom21}, + {KeyboardKey.F18, LedId.Keyboard_Custom22}, + {KeyboardKey.F19, LedId.Keyboard_Custom23}, + {KeyboardKey.F20, LedId.Keyboard_Custom24}, + {KeyboardKey.F21, LedId.Keyboard_Custom25}, + {KeyboardKey.F22, LedId.Keyboard_Custom26}, + {KeyboardKey.F23, LedId.Keyboard_Custom27}, + {KeyboardKey.F24, LedId.Keyboard_Custom28}, + {KeyboardKey.NumLock, LedId.Keyboard_NumLock}, + {KeyboardKey.Scroll, LedId.Keyboard_ScrollLock}, + {KeyboardKey.LeftShift, LedId.Keyboard_LeftShift}, + {KeyboardKey.RightShift, LedId.Keyboard_RightShift}, + {KeyboardKey.LeftCtrl, LedId.Keyboard_LeftCtrl}, + {KeyboardKey.RightCtrl, LedId.Keyboard_RightCtrl}, + {KeyboardKey.LeftAlt, LedId.Keyboard_LeftAlt}, + {KeyboardKey.RightAlt, LedId.Keyboard_RightAlt}, + {KeyboardKey.BrowserBack, LedId.Keyboard_Custom29}, + {KeyboardKey.BrowserForward, LedId.Keyboard_Custom30}, + {KeyboardKey.BrowserRefresh, LedId.Keyboard_Custom31}, + {KeyboardKey.BrowserStop, LedId.Keyboard_Custom32}, + {KeyboardKey.BrowserSearch, LedId.Keyboard_Custom33}, + {KeyboardKey.BrowserFavorites, LedId.Keyboard_Custom34}, + {KeyboardKey.BrowserHome, LedId.Keyboard_Custom35}, + {KeyboardKey.VolumeMute, LedId.Keyboard_MediaMute}, + {KeyboardKey.VolumeDown, LedId.Keyboard_MediaVolumeDown}, + {KeyboardKey.VolumeUp, LedId.Keyboard_MediaVolumeUp}, + {KeyboardKey.MediaNextTrack, LedId.Keyboard_MediaNextTrack}, + {KeyboardKey.MediaPreviousTrack, LedId.Keyboard_MediaPreviousTrack}, + {KeyboardKey.MediaStop, LedId.Keyboard_MediaStop}, + {KeyboardKey.MediaPlayPause, LedId.Keyboard_MediaPlay}, + {KeyboardKey.LaunchMail, LedId.Keyboard_Custom36}, + {KeyboardKey.SelectMedia, LedId.Keyboard_Custom37}, + {KeyboardKey.LaunchApplication1, LedId.Keyboard_Custom38}, + {KeyboardKey.LaunchApplication2, LedId.Keyboard_Custom39}, + {KeyboardKey.OemSemicolon, LedId.Keyboard_SemicolonAndColon}, + {KeyboardKey.OemPlus, LedId.Keyboard_EqualsAndPlus}, + {KeyboardKey.OemMinus, LedId.Keyboard_MinusAndUnderscore}, + {KeyboardKey.OemComma, LedId.Keyboard_CommaAndLessThan}, + {KeyboardKey.OemPeriod, LedId.Keyboard_PeriodAndBiggerThan}, + {KeyboardKey.OemQuestion, LedId.Keyboard_SlashAndQuestionMark}, + {KeyboardKey.OemTilde, LedId.Keyboard_GraveAccentAndTilde}, + {KeyboardKey.AbntC1, LedId.Keyboard_Custom40}, + {KeyboardKey.AbntC2, LedId.Keyboard_Custom41}, + {KeyboardKey.OemOpenBrackets, LedId.Keyboard_BracketLeft}, + {KeyboardKey.OemPipe, LedId.Keyboard_Backslash}, + {KeyboardKey.OemCloseBrackets, LedId.Keyboard_BracketRight}, + {KeyboardKey.OemQuotes, LedId.Keyboard_ApostropheAndDoubleQuote}, + {KeyboardKey.OemBackslash, LedId.Keyboard_Custom42}, // unverified + {KeyboardKey.ImeProcessed, LedId.Keyboard_Custom43}, + {KeyboardKey.System, LedId.Keyboard_Custom44}, + {KeyboardKey.OemAttn, LedId.Keyboard_Custom45}, + {KeyboardKey.OemFinish, LedId.Keyboard_Custom46}, + {KeyboardKey.OemCopy, LedId.Keyboard_Custom47}, + {KeyboardKey.OemAuto, LedId.Keyboard_Custom48}, + {KeyboardKey.OemEnlw, LedId.Keyboard_Custom49}, + {KeyboardKey.OemBackTab, LedId.Keyboard_Custom50}, + {KeyboardKey.Attn, LedId.Keyboard_Custom51}, + {KeyboardKey.CrSel, LedId.Keyboard_Custom52}, + {KeyboardKey.ExSel, LedId.Keyboard_Custom53}, + {KeyboardKey.EraseEof, LedId.Keyboard_Custom54}, + {KeyboardKey.Play, LedId.Keyboard_MediaPlay}, + {KeyboardKey.Zoom, LedId.Keyboard_Custom55}, + {KeyboardKey.NoName, LedId.Keyboard_Custom56}, + {KeyboardKey.Pa1, LedId.Keyboard_Custom57}, + {KeyboardKey.OemClear, LedId.Keyboard_Custom58}, + {KeyboardKey.DeadCharProcessed, LedId.Keyboard_Custom59}, + {KeyboardKey.NumPadEnter, LedId.Keyboard_NumEnter} + }; + + internal static readonly Dictionary MouseButtonLedIdMap = new Dictionary + { + {MouseButton.Left, LedId.Mouse1}, + {MouseButton.Middle, LedId.Mouse2}, + {MouseButton.Right, LedId.Mouse3}, + {MouseButton.Button4, LedId.Mouse4}, + {MouseButton.Button5, LedId.Mouse5} }; } -} +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/InputProvider.cs b/src/Artemis.Core/Services/Input/InputProvider.cs index 07ddd71a6..12246f2a9 100644 --- a/src/Artemis.Core/Services/Input/InputProvider.cs +++ b/src/Artemis.Core/Services/Input/InputProvider.cs @@ -12,15 +12,72 @@ namespace Artemis.Core.Services /// Occurs when the input provided has received keyboard data /// public event EventHandler? KeyboardDataReceived; - + /// - /// Invokes the event + /// Occurs when the input provided has received mouse button data + /// + public event EventHandler? MouseButtonDataReceived; + + /// + /// Occurs when the input provided has received mouse scroll data + /// + public event EventHandler? MouseScrollDataReceived; + + /// + /// Occurs when the input provided has received mouse move data + /// + public event EventHandler? MouseMoveDataReceived; + + /// + /// Occurs when the input provided received a device identifier + /// + public event EventHandler? IdentifierReceived; + + /// + /// Invokes the event which the listens to as long as + /// this provider is registered /// protected virtual void OnKeyboardDataReceived(InputProviderKeyboardEventArgs e) { KeyboardDataReceived?.Invoke(this, e); } + /// + /// Invokes the event which the listens to as long + /// as this provider is registered + /// + protected virtual void OnMouseButtonDataReceived(InputProviderMouseButtonEventArgs e) + { + MouseButtonDataReceived?.Invoke(this, e); + } + + /// + /// Invokes the event which the listens to as long + /// as this provider is registered + /// + protected virtual void OnMouseScrollDataReceived(InputProviderMouseScrollEventArgs e) + { + MouseScrollDataReceived?.Invoke(this, e); + } + + /// + /// Invokes the event which the listens to as long as + /// this provider is registered + /// + protected virtual void OnMouseMoveDataReceived(InputProviderMouseMoveEventArgs e) + { + MouseMoveDataReceived?.Invoke(this, e); + } + + /// + /// Invokes the event which the listens to as long as + /// this provider is registered + /// + protected virtual void OnIdentifierReceived(object identifier) + { + IdentifierReceived?.Invoke(this, identifier); + } + #region IDisposable /// diff --git a/src/Artemis.Core/Services/Input/InputService.cs b/src/Artemis.Core/Services/Input/InputService.cs index d342352ef..a1e87bc19 100644 --- a/src/Artemis.Core/Services/Input/InputService.cs +++ b/src/Artemis.Core/Services/Input/InputService.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using RGB.NET.Core; using Serilog; @@ -7,18 +9,31 @@ namespace Artemis.Core.Services internal class InputService : IInputService { private readonly ILogger _logger; + private readonly ISurfaceService _surfaceService; private readonly List _inputProviders; - public InputService(ILogger logger) + public InputService(ILogger logger, ISurfaceService surfaceService) { _logger = logger; + _surfaceService = surfaceService; _inputProviders = new List(); + _keyboardModifier = new Dictionary(); + _deviceCache = new Dictionary, ArtemisDevice>(); + _devices = new List(); + + _surfaceService.ActiveSurfaceConfigurationSelected += SurfaceConfigurationChanged; + _surfaceService.SurfaceConfigurationUpdated += SurfaceConfigurationChanged; + BustIdentifierCache(); } public void AddInputProvider(InputProvider inputProvider) { - _inputProviders.Add(inputProvider); + inputProvider.IdentifierReceived += InputProviderOnIdentifierReceived; inputProvider.KeyboardDataReceived += InputProviderOnKeyboardDataReceived; + inputProvider.MouseButtonDataReceived += InputProviderOnMouseButtonDataReceived; + inputProvider.MouseScrollDataReceived += InputProviderOnMouseScrollDataReceived; + inputProvider.MouseMoveDataReceived += InputProviderOnMouseMoveDataReceived; + _inputProviders.Add(inputProvider); } public void RemoveInputProvider(InputProvider inputProvider) @@ -26,34 +41,253 @@ namespace Artemis.Core.Services if (!_inputProviders.Contains(inputProvider)) return; + _inputProviders.Remove(inputProvider); + inputProvider.IdentifierReceived -= InputProviderOnIdentifierReceived; inputProvider.KeyboardDataReceived -= InputProviderOnKeyboardDataReceived; + inputProvider.MouseButtonDataReceived -= InputProviderOnMouseButtonDataReceived; + inputProvider.MouseScrollDataReceived -= InputProviderOnMouseScrollDataReceived; + inputProvider.MouseMoveDataReceived -= InputProviderOnMouseMoveDataReceived; } - private void InputProviderOnKeyboardDataReceived(object? sender, InputProviderKeyboardEventArgs e) + #region Identification + + private readonly Dictionary, ArtemisDevice> _deviceCache; + private List _devices; + private ArtemisDevice? _cachedFallbackKeyboard; + private ArtemisDevice? _cachedFallbackMouse; + private ArtemisDevice? _identifyingDevice; + + public void IdentifyDevice(ArtemisDevice device) { - if (!(sender is InputProvider inputProvider)) + _identifyingDevice = device; + _logger.Debug("Start identifying device {device}", device); + } + + public void StopIdentify() + { + _logger.Debug("Stop identifying device {device}", _identifyingDevice); + + _identifyingDevice = null; + _surfaceService.UpdateSurfaceConfiguration(_surfaceService.ActiveSurface, true); + + BustIdentifierCache(); + } + + public ArtemisDevice? GetDeviceByIdentifier(InputProvider provider, object identifier, InputFallbackDeviceType fallbackType) + { + if (provider == null) throw new ArgumentNullException(nameof(provider)); + if (identifier == null) throw new ArgumentNullException(nameof(identifier)); + + // Try cache first + ArtemisDevice? cacheMatch = GetDeviceFromCache(provider, identifier); + if (cacheMatch != null) + return cacheMatch; + + string providerName = provider.GetType().FullName!; + ArtemisDevice? match = _devices.FirstOrDefault(m => m.InputIdentifiers.Any(i => Equals(i.InputProvider,providerName) && Equals(i.Identifier, identifier))); + + // If a match was found cache it to speed up the next event and return the match + if (match != null) + { + AddDeviceToCache(match, provider, identifier); + return match; + } + + // If there is no match, apply our fallback type + if (fallbackType == InputFallbackDeviceType.None) + return null; + if (fallbackType == InputFallbackDeviceType.Keyboard) + { + if (_cachedFallbackKeyboard != null) + return _cachedFallbackKeyboard; + _cachedFallbackKeyboard = _surfaceService.ActiveSurface.Devices.FirstOrDefault(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard); + return _cachedFallbackKeyboard; + } + + if (fallbackType == InputFallbackDeviceType.Mouse) + { + if (_cachedFallbackMouse != null) + return _cachedFallbackMouse; + _cachedFallbackMouse = _surfaceService.ActiveSurface.Devices.FirstOrDefault(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse); + return _cachedFallbackMouse; + } + + return null; + } + + public void BustIdentifierCache() + { + _deviceCache.Clear(); + _cachedFallbackKeyboard = null; + _cachedFallbackMouse = null; + + _devices = _surfaceService.ActiveSurface.Devices.Where(d => d.InputIdentifiers.Any()).ToList(); + } + + private void AddDeviceToCache(ArtemisDevice match, InputProvider provider, object identifier) + { + _deviceCache.TryAdd(new Tuple(provider, identifier), match); + } + + private ArtemisDevice? GetDeviceFromCache(InputProvider provider, object identifier) + { + _deviceCache.TryGetValue(new Tuple(provider, identifier), out ArtemisDevice? device); + return device; + } + + private void SurfaceConfigurationChanged(object? sender, SurfaceConfigurationEventArgs e) + { + BustIdentifierCache(); + } + + private void InputProviderOnIdentifierReceived(object? sender, object identifier) + { + if (_identifyingDevice == null) + return; + 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); + string providerName = inputProvider.GetType().FullName!; + + // Remove existing identification + _identifyingDevice.InputIdentifiers.RemoveAll(i => i.InputProvider == providerName); + _identifyingDevice.InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(providerName, identifier)); + + StopIdentify(); + OnDeviceIdentified(); } - } - /// - /// 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); + #endregion - /// - /// Removes an input provided - /// - /// The input provider the remove - void RemoveInputProvider(InputProvider inputProvider); + #region Keyboard + + private readonly Dictionary _keyboardModifier; + private KeyboardModifierKeys _globalModifiers; + + private void InputProviderOnKeyboardDataReceived(object? sender, InputProviderKeyboardEventArgs e) + { + KeyboardModifierKeys keyboardModifierKeys = UpdateModifierKeys(e.Device, e.Key, e.IsDown); + + // Get the LED - TODO: leverage a lookup + bool foundLedId = InputKeyUtilities.KeyboardKeyLedIdMap.TryGetValue(e.Key, out LedId ledId); + ArtemisLed? led = null; + if (foundLedId && e.Device != null) + led = e.Device.Leds.FirstOrDefault(l => l.RgbLed.Id == ledId); + + // Create the UpDown event args because it can be used for every event + KeyboardKeyUpDownEventArgs eventArgs = new KeyboardKeyUpDownEventArgs(e.Device, led, e.Key, keyboardModifierKeys, e.IsDown); + OnKeyboardKeyUpDown(eventArgs); + if (e.IsDown) + OnKeyboardKeyDown(eventArgs); + else + OnKeyboardKeyUp(eventArgs); + + _logger.Verbose("Keyboard data: LED ID: {ledId}, key: {key}, is down: {isDown}, modifiers: {modifiers}, device: {device} ", ledId, e.Key, e.IsDown, keyboardModifierKeys, e.Device); + } + + private KeyboardModifierKeys UpdateModifierKeys(ArtemisDevice? device, KeyboardKey key, in bool isDown) + { + KeyboardModifierKeys modifiers = _globalModifiers; + if (device != null) + _keyboardModifier.TryGetValue(device, out modifiers); + + if (key == KeyboardKey.LeftAlt || key == KeyboardKey.RightAlt) + { + if (isDown) + modifiers = modifiers | KeyboardModifierKeys.Alt; + else + modifiers = modifiers & ~KeyboardModifierKeys.Alt; + } + else if (key == KeyboardKey.LeftCtrl || key == KeyboardKey.RightCtrl) + { + if (isDown) + modifiers = modifiers | KeyboardModifierKeys.Control; + else + modifiers = modifiers & ~KeyboardModifierKeys.Control; + } + else if (key == KeyboardKey.LeftShift || key == KeyboardKey.RightShift) + { + if (isDown) + modifiers = modifiers | KeyboardModifierKeys.Shift; + else + modifiers = modifiers & ~KeyboardModifierKeys.Shift; + } + else if (key == KeyboardKey.LWin || key == KeyboardKey.RWin) + { + if (isDown) + modifiers = modifiers | KeyboardModifierKeys.Windows; + else + modifiers = modifiers & ~KeyboardModifierKeys.Windows; + } + + if (device != null) + _keyboardModifier[device] = modifiers; + else + _globalModifiers = modifiers; + + return modifiers; + } + + #endregion + + #region Mouse + + private void InputProviderOnMouseButtonDataReceived(object? sender, InputProviderMouseButtonEventArgs e) + { + bool foundLedId = InputKeyUtilities.MouseButtonLedIdMap.TryGetValue(e.Button, out LedId ledId); + // _logger.Verbose("Mouse button data: LED ID: {ledId}, button: {button}, is down: {isDown}, device: {device} ", ledId, e.Button, e.IsDown, e.Device); + } + + private void InputProviderOnMouseScrollDataReceived(object? sender, InputProviderMouseScrollEventArgs e) + { + // _logger.Verbose("Mouse scroll data: Direction: {direction}, delta: {delta}, device: {device} ", e.Direction, e.Delta, e.Device); + } + + private void InputProviderOnMouseMoveDataReceived(object? sender, InputProviderMouseMoveEventArgs e) + { + // _logger.Verbose("Mouse move data: XY: {X},{Y} - delta XY: {deltaX},{deltaY} - device: {device} ", e.CursorX, e.CursorY, e.DeltaX, e.DeltaY, e.Device); + } + + #endregion + + #region Events + + public event EventHandler? KeyboardKeyUpDown; + public event EventHandler? KeyboardKeyDown; + public event EventHandler? KeyboardKeyUp; + public event EventHandler? DeviceIdentified; + + protected virtual void OnKeyboardKeyUpDown(KeyboardKeyUpDownEventArgs e) + { + KeyboardKeyUpDown?.Invoke(this, e); + } + + protected virtual void OnKeyboardKeyDown(KeyboardEventArgs e) + { + KeyboardKeyDown?.Invoke(this, e); + } + + protected virtual void OnKeyboardKeyUp(KeyboardEventArgs e) + { + KeyboardKeyUp?.Invoke(this, e); + } + + protected virtual void OnDeviceIdentified() + { + DeviceIdentified?.Invoke(this, EventArgs.Empty); + } + + #endregion + + #region IDisposable + + /// + public void Dispose() + { + while (_inputProviders.Any()) + RemoveInputProvider(_inputProviders.First()); + } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Interfaces/IInputService.cs b/src/Artemis.Core/Services/Input/Interfaces/IInputService.cs new file mode 100644 index 000000000..8b0897edf --- /dev/null +++ b/src/Artemis.Core/Services/Input/Interfaces/IInputService.cs @@ -0,0 +1,75 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// A service that allows you to interact with keyboard and mice input events + /// + public interface IInputService : IArtemisService, IDisposable + { + /// + /// 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); + + /// + /// Identifies the provided by assigning the next received device identification to it + /// + /// The device to identify + void IdentifyDevice(ArtemisDevice device); + + /// + /// Cancels identifying the device last passed to + /// + void StopIdentify(); + + #region Events + + /// + /// Occurs whenever a key on a keyboard was pressed or released + /// + event EventHandler KeyboardKeyUpDown; + + /// + /// Occurs whenever a key on a keyboard was pressed + /// + event EventHandler KeyboardKeyDown; + + /// + /// Occurs whenever a key on a keyboard was released + /// + event EventHandler KeyboardKeyUp; + + /// + /// Occurs when a device has been identified after calling + /// + public event EventHandler DeviceIdentified; + + #endregion + + #region Identification + + /// + /// Attempts to identify the device using the provided + /// + /// The input provider to identify the device for + /// The value to use to identify the device + /// A device type to fall back to if no match is found + /// If found, the Artemis device matching the provider and identifier + ArtemisDevice? GetDeviceByIdentifier(InputProvider provider, object identifier, InputFallbackDeviceType fallbackType); + + /// + /// Clears the identifier cache + /// + void BustIdentifierCache(); + + #endregion + } +} \ No newline at end of file diff --git a/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs b/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs index 41bb7dcd5..11412fe59 100644 --- a/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs +++ b/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs @@ -1,13 +1,12 @@ using System; -using System.Collections.Generic; -using System.Linq; +using System.Runtime.InteropServices; 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; +using MouseButton = Artemis.Core.Services.MouseButton; namespace Artemis.UI.InputProviders { @@ -15,25 +14,19 @@ namespace Artemis.UI.InputProviders { private const int WM_INPUT = 0x00FF; - private readonly ISurfaceService _surfaceService; - private List _keyboards; + private readonly IInputService _inputService; private DateTime _lastMouseUpdate; - private List _mice; private SpongeWindow _sponge; - public NativeWindowInputProvider(ISurfaceService surfaceService) + public NativeWindowInputProvider(IInputService inputService) { - _surfaceService = surfaceService; + _inputService = inputService; _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 @@ -45,9 +38,6 @@ namespace Artemis.UI.InputProviders { _sponge?.DestroyHandle(); _sponge = null; - - _surfaceService.ActiveSurfaceConfigurationSelected -= SurfaceConfigurationChanged; - _surfaceService.SurfaceConfigurationUpdated -= SurfaceConfigurationChanged; } base.Dispose(disposing); @@ -55,17 +45,6 @@ namespace Artemis.UI.InputProviders #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) @@ -75,67 +54,168 @@ namespace Artemis.UI.InputProviders switch (data) { case RawInputMouseData mouse: - HandleMouseData(mouse); + HandleMouseData(data, mouse); break; case RawInputKeyboardData keyboard: - HandleKeyboardData(keyboard); + HandleKeyboardData(data, keyboard); break; } } - private void HandleKeyboardData(RawInputKeyboardData data) + #region Keyboard + + private void HandleKeyboardData(RawInputData data, RawInputKeyboardData keyboardData) { - // 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}"); - + KeyboardKey key = (KeyboardKey) KeyInterop.KeyFromVirtualKey(keyboardData.Keyboard.VirutalKey); + // Debug.WriteLine($"VK: {key} ({keyboardData.Keyboard.VirutalKey}), Flags: {keyboardData.Keyboard.Flags}, Scan code: {keyboardData.Keyboard.ScanCode}"); + // Sometimes we get double hits and they resolve to None, ignore those - if (key == InputKey.None) + if (key == KeyboardKey.None) return; // Right alt triggers LeftCtrl with a different scan code for some reason, ignore those - if (key == InputKey.LeftCtrl && data.Keyboard.ScanCode == 56) + if (key == KeyboardKey.LeftCtrl && keyboardData.Keyboard.ScanCode == 56) return; + string identifier = data.Device?.DevicePath; + + // Let the core know there is an identifier so it can store new identifications if applicable + if (identifier != null) + OnIdentifierReceived(identifier); + + ArtemisDevice device = null; + if (identifier != null) + device = _inputService.GetDeviceByIdentifier(this, identifier, InputFallbackDeviceType.Keyboard); + // 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 (keyboardData.Keyboard.Flags == RawKeyboardFlags.LeftKey || keyboardData.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 == KeyboardKey.Enter) + key = KeyboardKey.NumPadEnter; + if (key == KeyboardKey.LeftCtrl) + key = KeyboardKey.RightCtrl; + if (key == KeyboardKey.LeftAlt) + key = KeyboardKey.RightAlt; } - if (key == InputKey.LeftShift && data.Keyboard.ScanCode == 54) - key = InputKey.RightShift; + if (key == KeyboardKey.LeftShift && keyboardData.Keyboard.ScanCode == 54) + key = KeyboardKey.RightShift; - bool isDown = data.Keyboard.Flags != RawKeyboardFlags.Up && - data.Keyboard.Flags != (RawKeyboardFlags.Up | RawKeyboardFlags.LeftKey) && - data.Keyboard.Flags != (RawKeyboardFlags.Up | RawKeyboardFlags.RightKey); + bool isDown = keyboardData.Keyboard.Flags != RawKeyboardFlags.Up && + keyboardData.Keyboard.Flags != (RawKeyboardFlags.Up | RawKeyboardFlags.LeftKey) && + keyboardData.Keyboard.Flags != (RawKeyboardFlags.Up | RawKeyboardFlags.RightKey); - OnKeyboardDataReceived(new InputProviderKeyboardEventArgs(match, key, isDown)); + OnKeyboardDataReceived(new InputProviderKeyboardEventArgs(device, key, isDown)); } - private void HandleMouseData(RawInputMouseData data) + #endregion + + #region Mouse + + private int _mouseDeltaX; + private int _mouseDeltaY; + + private void HandleMouseData(RawInputData data, RawInputMouseData mouseData) { - // Only handle mouse movement 25 times per second - if (data.Mouse.Buttons == RawMouseButtonFlags.None) + // Only submit mouse movement 25 times per second but increment the delta + // This can create a small inaccuracy of course, but Artemis is not a shooter :') + if (mouseData.Mouse.Buttons == RawMouseButtonFlags.None) + { + _mouseDeltaX += mouseData.Mouse.LastX; + _mouseDeltaY += mouseData.Mouse.LastY; if (DateTime.Now - _lastMouseUpdate < TimeSpan.FromMilliseconds(40)) return; + } - _lastMouseUpdate = DateTime.Now; + ArtemisDevice device = null; + string identifier = data.Device?.DevicePath; + if (identifier != null) + device = _inputService.GetDeviceByIdentifier(this, identifier, InputFallbackDeviceType.Mouse); - // Get the keyboard that submitted the data - ArtemisDevice match = _mice?.FirstOrDefault(); - if (match == null) + // Debug.WriteLine($"Buttons: {data.Mouse.Buttons}, Data: {data.Mouse.ButtonData}, Flags: {data.Mouse.Flags}, XY: {data.Mouse.LastX},{data.Mouse.LastY}"); + + // Movement + if (mouseData.Mouse.Buttons == RawMouseButtonFlags.None) + { + Win32Point cursorPosition = GetCursorPosition(); + OnMouseMoveDataReceived(new InputProviderMouseMoveEventArgs(device, cursorPosition.X, cursorPosition.Y, _mouseDeltaX, _mouseDeltaY)); + _mouseDeltaX = 0; + _mouseDeltaY = 0; + _lastMouseUpdate = DateTime.Now; return; + } + + // Now we know its not movement, let the core know there is an identifier so it can store new identifications if applicable + if (identifier != null) + OnIdentifierReceived(identifier); + + // Scrolling + if (mouseData.Mouse.ButtonData != 0) + { + if (mouseData.Mouse.Buttons == RawMouseButtonFlags.MouseWheel) + OnMouseScrollDataReceived(new InputProviderMouseScrollEventArgs(device, MouseScrollDirection.Vertical, mouseData.Mouse.ButtonData)); + else if (mouseData.Mouse.Buttons == RawMouseButtonFlags.MouseHorizontalWheel) + OnMouseScrollDataReceived(new InputProviderMouseScrollEventArgs(device, MouseScrollDirection.Horizontal, mouseData.Mouse.ButtonData)); + return; + } + + // Button presses + MouseButton button = MouseButton.Left; + bool isDown = false; + + // Left + if (DetermineMouseButton(mouseData, RawMouseButtonFlags.LeftButtonDown, RawMouseButtonFlags.LeftButtonUp, ref isDown)) + button = MouseButton.Left; + // Middle + else if (DetermineMouseButton(mouseData, RawMouseButtonFlags.MiddleButtonDown, RawMouseButtonFlags.MiddleButtonUp, ref isDown)) + button = MouseButton.Middle; + // Right + else if (DetermineMouseButton(mouseData, RawMouseButtonFlags.RightButtonDown, RawMouseButtonFlags.RightButtonUp, ref isDown)) + button = MouseButton.Right; + // Button 4 + else if (DetermineMouseButton(mouseData, RawMouseButtonFlags.Button4Down, RawMouseButtonFlags.Button4Up, ref isDown)) + button = MouseButton.Button4; + else if (DetermineMouseButton(mouseData, RawMouseButtonFlags.Button5Down, RawMouseButtonFlags.Button5Up, ref isDown)) + button = MouseButton.Button5; + + OnMouseButtonDataReceived(new InputProviderMouseButtonEventArgs(device, button, isDown)); } + + private bool DetermineMouseButton(RawInputMouseData data, RawMouseButtonFlags downButton, RawMouseButtonFlags upButton, ref bool isDown) + { + if (data.Mouse.Buttons == downButton || data.Mouse.Buttons == upButton) + { + isDown = data.Mouse.Buttons == downButton; + return true; + } + + isDown = false; + return false; + } + + #endregion + + #region Native + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GetCursorPos(ref Win32Point pt); + + [StructLayout(LayoutKind.Sequential)] + private struct Win32Point + { + public readonly int X; + public readonly int Y; + } + + private static Win32Point GetCursorPosition() + { + Win32Point w32Mouse = new Win32Point(); + GetCursorPos(ref w32Mouse); + + return w32Mouse; + } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Splash/SplashViewModel.cs b/src/Artemis.UI/Screens/Splash/SplashViewModel.cs index a3c4ef364..f80543a79 100644 --- a/src/Artemis.UI/Screens/Splash/SplashViewModel.cs +++ b/src/Artemis.UI/Screens/Splash/SplashViewModel.cs @@ -5,6 +5,7 @@ using Artemis.Core.Services; using Humanizer; using MaterialDesignExtensions.Controls; using Stylet; +using MouseButton = System.Windows.Input.MouseButton; namespace Artemis.UI.Screens.Splash { diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputView.xaml b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputView.xaml new file mode 100644 index 000000000..16e93aca1 --- /dev/null +++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputView.xaml @@ -0,0 +1,35 @@ + + + + + + Press a button/key on your + that is currently showing a yellow color. + + + + + + This will teach Artemis to associate button/key presses with this specific device. + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputViewModel.cs new file mode 100644 index 000000000..17e5794be --- /dev/null +++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputViewModel.cs @@ -0,0 +1,65 @@ +using System; +using System.Linq; +using Artemis.Core.Services; +using Artemis.UI.Screens.SurfaceEditor.Visualization; +using Artemis.UI.Shared.Services; +using MaterialDesignThemes.Wpf; +using RGB.NET.Brushes; +using RGB.NET.Core; +using RGB.NET.Groups; + +namespace Artemis.UI.Screens.SurfaceEditor.Dialogs +{ + public class SurfaceDeviceDetectInputViewModel : DialogViewModelBase + { + private readonly IInputService _inputService; + private readonly ISnackbarMessageQueue _mainMessageQueue; + private string _title; + private ListLedGroup _ledGroup; + + public SurfaceDeviceDetectInputViewModel(SurfaceDeviceViewModel surfaceDeviceViewModel, IInputService inputService, ISnackbarMessageQueue mainMessageQueue) + { + SurfaceDeviceViewModel = surfaceDeviceViewModel; + Title = $"{SurfaceDeviceViewModel.Device.RgbDevice.DeviceInfo.DeviceName} - Detect input"; + + // Create a LED group way at the top + _ledGroup = new ListLedGroup(SurfaceDeviceViewModel.Device.Leds.Select(l => l.RgbLed)) + { + Brush = new SolidColorBrush(new Color(255, 255, 0)), + ZIndex = 999 + }; + + + _inputService = inputService; + _mainMessageQueue = mainMessageQueue; + _inputService.IdentifyDevice(surfaceDeviceViewModel.Device); + _inputService.DeviceIdentified += InputServiceOnDeviceIdentified; + } + + public SurfaceDeviceViewModel SurfaceDeviceViewModel { get; } + + public string Title + { + get => _title; + set => SetAndNotify(ref _title, value); + } + + public override void OnDialogClosed(object sender, DialogClosingEventArgs e) + { + base.OnDialogClosed(sender, e); + _inputService.DeviceIdentified -= InputServiceOnDeviceIdentified; + _ledGroup.Detach(); + } + + public void Cancel() + { + Session?.Close(false); + } + + private void InputServiceOnDeviceIdentified(object sender, EventArgs e) + { + Session?.Close(true); + _mainMessageQueue.Enqueue("Device identified."); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.xaml b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.xaml index ded46140a..b8e52fbe7 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.xaml +++ b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.xaml @@ -144,6 +144,14 @@ + + + + + diff --git a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs index 8b69f6817..619acab06 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs +++ b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs @@ -15,12 +15,14 @@ using Artemis.UI.Screens.SurfaceEditor.Visualization; using Artemis.UI.Shared; using Artemis.UI.Shared.Services; using Stylet; +using MouseButton = System.Windows.Input.MouseButton; namespace Artemis.UI.Screens.SurfaceEditor { public class SurfaceEditorViewModel : Screen, IMainScreenViewModel { private readonly IDeviceService _deviceService; + private readonly IInputService _inputService; private readonly IDialogService _dialogService; private readonly IRgbService _rgbService; private readonly ISettingsService _settingsService; @@ -33,8 +35,12 @@ namespace Artemis.UI.Screens.SurfaceEditor private ObservableCollection _surfaceConfigurations; private PluginSetting _surfaceListWidth; - public SurfaceEditorViewModel(IRgbService rgbService, ISurfaceService surfaceService, IDialogService dialogService, ISettingsService settingsService, - IDeviceService deviceService) + public SurfaceEditorViewModel(IRgbService rgbService, + ISurfaceService surfaceService, + IDialogService dialogService, + ISettingsService settingsService, + IDeviceService deviceService, + IInputService inputService) { DisplayName = "Surface Editor"; @@ -49,6 +55,7 @@ namespace Artemis.UI.Screens.SurfaceEditor _dialogService = dialogService; _settingsService = settingsService; _deviceService = deviceService; + _inputService = inputService; } public ObservableCollection Devices @@ -288,6 +295,17 @@ namespace Artemis.UI.Screens.SurfaceEditor _surfaceService.UpdateSurfaceConfiguration(SelectedSurface, true); } + public async Task DetectInput(SurfaceDeviceViewModel surfaceDeviceViewModel) + { + object madeChanges = await _dialogService.ShowDialog(new Dictionary + { + {"surfaceDeviceViewModel", surfaceDeviceViewModel} + }); + + if ((bool)madeChanges) + _surfaceService.UpdateSurfaceConfiguration(SelectedSurface, true); + } + #endregion #region Selection diff --git a/src/Artemis.UI/Services/RegistrationService.cs b/src/Artemis.UI/Services/RegistrationService.cs index de139a508..b1f75b0ad 100644 --- a/src/Artemis.UI/Services/RegistrationService.cs +++ b/src/Artemis.UI/Services/RegistrationService.cs @@ -85,7 +85,7 @@ namespace Artemis.UI.Services public void RegisterInputProvider() { - _inputService.AddInputProvider(new NativeWindowInputProvider(_surfaceService)); + _inputService.AddInputProvider(new NativeWindowInputProvider(_inputService)); } private void PluginServiceOnPluginEnabling(object sender, PluginEventArgs e) From 6ee5cbd09c5a232db9795620e98e19600ddd258b Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Sun, 22 Nov 2020 22:21:21 +0100 Subject: [PATCH 3/6] Input - Fixed NativeInputProvider preventing input in the WPF windows --- src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs b/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs index 11412fe59..8d8c79264 100644 --- a/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs +++ b/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs @@ -25,7 +25,7 @@ namespace Artemis.UI.InputProviders _sponge = new SpongeWindow(); _sponge.WndProcCalled += SpongeOnWndProcCalled; - RawInputDevice.RegisterDevice(HidUsageAndPage.Keyboard, RawInputDeviceFlags.ExInputSink | RawInputDeviceFlags.NoLegacy, _sponge.Handle); + RawInputDevice.RegisterDevice(HidUsageAndPage.Keyboard, RawInputDeviceFlags.InputSink, _sponge.Handle); RawInputDevice.RegisterDevice(HidUsageAndPage.Mouse, RawInputDeviceFlags.InputSink, _sponge.Handle); } From f1f0abfec5366652eb5f77f53e7866b5c6ec3cec Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Mon, 23 Nov 2020 19:41:48 +0100 Subject: [PATCH 4/6] Input - Polished up UI Input - Added events to service --- .../Models/Surface/ArtemisDevice.cs | 16 +- ...llbackDeviceType.cs => InputDeviceType.cs} | 2 +- ...ModifierKeys.cs => KeyboardModifierKey.cs} | 2 +- .../InputProviderIdentifierEventArgs.cs | 31 ++++ .../Events/InputProviderMouseMoveEventArgs.cs | 8 +- ...rdEventArgs.cs => KeyboardKeyEventArgs.cs} | 22 +-- .../Events/KeyboardKeyUpDownEventArgs.cs | 18 +++ .../Input/Events/MouseButtonEventArgs.cs | 32 ++++ .../Events/MouseButtonUpDownEventArgs.cs | 18 +++ .../Input/Events/MouseMoveEventArgs.cs | 51 +++++++ .../Input/Events/MouseScrollEventArgs.cs | 57 +++++++ .../Services/Input/InputProvider.cs | 39 +++-- .../Services/Input/InputService.cs | 140 ++++++++++++------ .../Input/Interfaces/IInputService.cs | 35 ++++- .../Entities/Surface/DeviceEntity.cs | 4 +- .../Repositories/SurfaceRepository.cs | 3 +- .../Shared/DataModelListViewModel.cs | 1 + .../Screens/Dialogs/ConfirmDialogViewModel.cs | 6 - .../GradientEditor/GradientEditorViewModel.cs | 5 +- .../Services/Dialog/DialogViewModelBase.cs | 9 ++ .../NativeWindowInputProvider.cs | 18 +-- .../Dialogs/ProfileCreateViewModel.cs | 5 - .../Dialogs/ProfileExportViewModel.cs | 5 - .../Dialogs/ProfileImportViewModel.cs | 5 - .../ProfileEditor/Dialogs/RenameViewModel.cs | 5 - .../LayerPropertiesViewModel.cs | 1 + .../Dialogs/TimelineSegmentDialogViewModel.cs | 5 - .../Visualization/ProfileViewModel.cs | 1 + .../Tools/SelectionToolViewModel.cs | 1 + src/Artemis.UI/Screens/RootViewModel.cs | 1 + .../Tabs/Devices/DeviceSettingsView.xaml | 27 +++- .../Tabs/Devices/DeviceSettingsViewModel.cs | 38 ++++- .../Screens/Splash/SplashViewModel.cs | 1 + .../Dialogs/SurfaceCreateViewModel.cs | 5 - .../Dialogs/SurfaceDeviceConfigViewModel.cs | 34 ++--- .../Dialogs/SurfaceDeviceDetectInputView.xaml | 30 +++- .../SurfaceDeviceDetectInputViewModel.cs | 42 ++---- .../SurfaceEditor/SurfaceEditorView.xaml | 2 + .../SurfaceEditor/SurfaceEditorViewModel.cs | 25 ++-- .../Visualization/SurfaceDeviceViewModel.cs | 5 + 40 files changed, 552 insertions(+), 203 deletions(-) rename src/Artemis.Core/Services/Input/Enums/{InputFallbackDeviceType.cs => InputDeviceType.cs} (91%) rename src/Artemis.Core/Services/Input/Enums/{KeyboardModifierKeys.cs => KeyboardModifierKey.cs} (93%) create mode 100644 src/Artemis.Core/Services/Input/Events/InputProviderIdentifierEventArgs.cs rename src/Artemis.Core/Services/Input/Events/{KeyboardEventArgs.cs => KeyboardKeyEventArgs.cs} (50%) create mode 100644 src/Artemis.Core/Services/Input/Events/KeyboardKeyUpDownEventArgs.cs create mode 100644 src/Artemis.Core/Services/Input/Events/MouseButtonEventArgs.cs create mode 100644 src/Artemis.Core/Services/Input/Events/MouseButtonUpDownEventArgs.cs create mode 100644 src/Artemis.Core/Services/Input/Events/MouseMoveEventArgs.cs create mode 100644 src/Artemis.Core/Services/Input/Events/MouseScrollEventArgs.cs diff --git a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs index 8bffa761d..953fda3d2 100644 --- a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs +++ b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs @@ -23,8 +23,10 @@ namespace Artemis.Core RgbDevice = rgbDevice; DeviceProvider = deviceProvider; Surface = surface; - InputIdentifiers = new List(); DeviceEntity = new DeviceEntity(); + + InputIdentifiers = new List(); + _leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); Rotation = 0; @@ -40,8 +42,12 @@ namespace Artemis.Core RgbDevice = rgbDevice; DeviceProvider = deviceProvider; Surface = surface; - InputIdentifiers = new List(); DeviceEntity = deviceEntity; + + InputIdentifiers = new List(); + foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers) + InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier)); + _leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); } @@ -183,10 +189,10 @@ namespace Artemis.Core // Other properties are computed DeviceEntity.DeviceIdentifier = RgbDevice.GetDeviceIdentifier(); - DeviceEntity.InputIdentifier.Clear(); + DeviceEntity.InputIdentifiers.Clear(); foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers) { - DeviceEntity.InputIdentifier.Add(new DeviceInputIdentifierEntity + DeviceEntity.InputIdentifiers.Add(new DeviceInputIdentifierEntity { InputProvider = identifier.InputProvider, Identifier = identifier.Identifier @@ -205,7 +211,7 @@ namespace Artemis.Core RgbDevice.Location = new Point(DeviceEntity.X, DeviceEntity.Y); InputIdentifiers.Clear(); - foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifier) + foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers) InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier)); CalculateRenderProperties(); diff --git a/src/Artemis.Core/Services/Input/Enums/InputFallbackDeviceType.cs b/src/Artemis.Core/Services/Input/Enums/InputDeviceType.cs similarity index 91% rename from src/Artemis.Core/Services/Input/Enums/InputFallbackDeviceType.cs rename to src/Artemis.Core/Services/Input/Enums/InputDeviceType.cs index b1590f7b4..3d7ef1149 100644 --- a/src/Artemis.Core/Services/Input/Enums/InputFallbackDeviceType.cs +++ b/src/Artemis.Core/Services/Input/Enums/InputDeviceType.cs @@ -3,7 +3,7 @@ /// /// Represents a device that provides input to the /// - public enum InputFallbackDeviceType + public enum InputDeviceType { /// /// None diff --git a/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKeys.cs b/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKey.cs similarity index 93% rename from src/Artemis.Core/Services/Input/Enums/KeyboardModifierKeys.cs rename to src/Artemis.Core/Services/Input/Enums/KeyboardModifierKey.cs index 4cc0c7f5d..2ebde89e7 100644 --- a/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKeys.cs +++ b/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKey.cs @@ -6,7 +6,7 @@ namespace Artemis.Core.Services /// Specifies the set of modifier keys. /// [Flags] - public enum KeyboardModifierKeys + public enum KeyboardModifierKey { /// No modifiers are pressed. None = 0, diff --git a/src/Artemis.Core/Services/Input/Events/InputProviderIdentifierEventArgs.cs b/src/Artemis.Core/Services/Input/Events/InputProviderIdentifierEventArgs.cs new file mode 100644 index 000000000..277778a8b --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/InputProviderIdentifierEventArgs.cs @@ -0,0 +1,31 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for input provider identifier events + /// + public class InputProviderIdentifierEventArgs : EventArgs + { + /// + /// Creates a new instance of the class + /// + /// A value that can be used to identify this device + /// The type of device this identifier belongs to + public InputProviderIdentifierEventArgs(object identifier, InputDeviceType deviceType) + { + Identifier = identifier; + DeviceType = deviceType; + } + + /// + /// Gets a value that can be used to identify this device + /// + public object Identifier { get; } + + /// + /// Gets the type of device this identifier belongs to + /// + public InputDeviceType DeviceType { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Events/InputProviderMouseMoveEventArgs.cs b/src/Artemis.Core/Services/Input/Events/InputProviderMouseMoveEventArgs.cs index ceeb8823d..e0231af91 100644 --- a/src/Artemis.Core/Services/Input/Events/InputProviderMouseMoveEventArgs.cs +++ b/src/Artemis.Core/Services/Input/Events/InputProviderMouseMoveEventArgs.cs @@ -29,22 +29,22 @@ namespace Artemis.Core.Services public ArtemisDevice? Device { get; } /// - /// Gets the X position of the mouse cursor (not necessarily tied to the specific device) + /// Gets the X position of the mouse cursor in pixels (not necessarily tied to the specific device) /// public int CursorX { get; } /// - /// Gets the Y position of the mouse cursor (not necessarily tied to the specific device) + /// Gets the Y position of the mouse cursor in pixels (not necessarily tied to the specific device) /// public int CursorY { get; } /// - /// Gets the movement delta in the horizontal direction + /// Gets the movement delta in the horizontal direction in pixels /// public int DeltaX { get; } /// - /// Gets the movement delta in the vertical direction + /// Gets the movement delta in the vertical direction in pixels /// public int DeltaY { get; } } diff --git a/src/Artemis.Core/Services/Input/Events/KeyboardEventArgs.cs b/src/Artemis.Core/Services/Input/Events/KeyboardKeyEventArgs.cs similarity index 50% rename from src/Artemis.Core/Services/Input/Events/KeyboardEventArgs.cs rename to src/Artemis.Core/Services/Input/Events/KeyboardKeyEventArgs.cs index 3372555ed..2a53e4d78 100644 --- a/src/Artemis.Core/Services/Input/Events/KeyboardEventArgs.cs +++ b/src/Artemis.Core/Services/Input/Events/KeyboardKeyEventArgs.cs @@ -5,9 +5,9 @@ namespace Artemis.Core.Services /// /// Contains data for keyboard input events /// - public class KeyboardEventArgs : EventArgs + public class KeyboardKeyEventArgs : EventArgs { - internal KeyboardEventArgs(ArtemisDevice? device, ArtemisLed? led, KeyboardKey key, KeyboardModifierKeys modifiers) + internal KeyboardKeyEventArgs(ArtemisDevice? device, ArtemisLed? led, KeyboardKey key, KeyboardModifierKey modifiers) { Device = device; Led = led; @@ -33,22 +33,6 @@ namespace Artemis.Core.Services /// /// Gets the modifiers that are pressed /// - public KeyboardModifierKeys Modifiers { get; } - } - - /// - /// Contains data for keyboard input events - /// - public class KeyboardKeyUpDownEventArgs : KeyboardEventArgs - { - internal KeyboardKeyUpDownEventArgs(ArtemisDevice? device, ArtemisLed? led, KeyboardKey key, KeyboardModifierKeys modifiers, bool isDown) : base(device, led, key, modifiers) - { - IsDown = isDown; - } - - /// - /// Whether the key is being pressed down, if the key is being released - /// - public bool IsDown { get; set; } + public KeyboardModifierKey Modifiers { get; } } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Events/KeyboardKeyUpDownEventArgs.cs b/src/Artemis.Core/Services/Input/Events/KeyboardKeyUpDownEventArgs.cs new file mode 100644 index 000000000..8e3168e5c --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/KeyboardKeyUpDownEventArgs.cs @@ -0,0 +1,18 @@ +namespace Artemis.Core.Services +{ + /// + /// Contains data for keyboard input events + /// + public class KeyboardKeyUpDownEventArgs : KeyboardKeyEventArgs + { + internal KeyboardKeyUpDownEventArgs(ArtemisDevice? device, ArtemisLed? led, KeyboardKey key, KeyboardModifierKey modifiers, bool isDown) : base(device, led, key, modifiers) + { + IsDown = isDown; + } + + /// + /// Whether the key is being pressed down, if the key is being released + /// + public bool IsDown { get; set; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Events/MouseButtonEventArgs.cs b/src/Artemis.Core/Services/Input/Events/MouseButtonEventArgs.cs new file mode 100644 index 000000000..050589822 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/MouseButtonEventArgs.cs @@ -0,0 +1,32 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for mouse input events + /// + public class MouseButtonEventArgs : EventArgs + { + internal MouseButtonEventArgs(ArtemisDevice? device, ArtemisLed? led, MouseButton button) + { + Device = device; + Led = led; + Button = button; + } + + /// + /// Gets the device that triggered the event + /// + public ArtemisDevice? Device { get; } + + /// + /// Gets the LED that corresponds to the pressed key + /// + public ArtemisLed? Led { get; } + + /// + /// Gets the button that triggered the event + /// + public MouseButton Button { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Events/MouseButtonUpDownEventArgs.cs b/src/Artemis.Core/Services/Input/Events/MouseButtonUpDownEventArgs.cs new file mode 100644 index 000000000..0b045bf03 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/MouseButtonUpDownEventArgs.cs @@ -0,0 +1,18 @@ +namespace Artemis.Core.Services +{ + /// + /// Contains data for mouse input events + /// + public class MouseButtonUpDownEventArgs : MouseButtonEventArgs + { + internal MouseButtonUpDownEventArgs(ArtemisDevice? device, ArtemisLed? led, MouseButton button, bool isDown) : base(device, led, button) + { + IsDown = isDown; + } + + /// + /// Whether the button is being pressed down, if the button is being released + /// + public bool IsDown { get; set; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Events/MouseMoveEventArgs.cs b/src/Artemis.Core/Services/Input/Events/MouseMoveEventArgs.cs new file mode 100644 index 000000000..fa2c582b6 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/MouseMoveEventArgs.cs @@ -0,0 +1,51 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for mouse movement events + /// + public class MouseMoveEventArgs : EventArgs + { + /// + /// + /// The device that triggered the event + /// The X position of the mouse cursor (not necessarily tied to the specific device) + /// The Y position of the mouse cursor (not necessarily tied to the specific device) + /// The movement delta in the horizontal direction + /// The movement delta in the vertical direction + internal MouseMoveEventArgs(ArtemisDevice? device, int cursorX, int cursorY, int deltaX, int deltaY) + { + Device = device; + CursorX = cursorX; + CursorY = cursorY; + DeltaX = deltaX; + DeltaY = deltaY; + } + + /// + /// Gets the device that triggered the event + /// + public ArtemisDevice? Device { get; } + + /// + /// Gets the X position of the mouse cursor (not necessarily tied to the specific device) + /// + public int CursorX { get; } + + /// + /// Gets the Y position of the mouse cursor (not necessarily tied to the specific device) + /// + public int CursorY { get; } + + /// + /// Gets the movement delta in the horizontal direction + /// + public int DeltaX { get; } + + /// + /// Gets the movement delta in the vertical direction + /// + public int DeltaY { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Events/MouseScrollEventArgs.cs b/src/Artemis.Core/Services/Input/Events/MouseScrollEventArgs.cs new file mode 100644 index 000000000..50373b6e7 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/MouseScrollEventArgs.cs @@ -0,0 +1,57 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for mouse scroll events + /// + public class MouseScrollEventArgs : EventArgs + { + /// + /// + /// The device that triggered the event + /// The direction in which was scrolled + /// The scroll delta (can positive or negative) + internal MouseScrollEventArgs(ArtemisDevice? device, MouseScrollDirection direction, int delta) + { + Device = device; + Direction = direction; + Delta = delta; + } + + /// + /// Gets the device that triggered the event + /// + public ArtemisDevice? Device { get; } + + /// + /// Gets the direction in which was scrolled + /// + public MouseScrollDirection Direction { get; } + + /// + /// Gets the scroll delta (can positive or negative) + /// + public int Delta { get; } + + /// + /// Gets a boolean indicating whether the mouse scrolled up + /// + public bool IsScrollingUp => Direction == MouseScrollDirection.Vertical && Delta > 0; + + /// + /// Gets a boolean indicating whether the mouse scrolled down + /// + public bool IsScrollingDown => Direction == MouseScrollDirection.Vertical && Delta < 0; + + /// + /// Gets a boolean indicating whether the mouse scrolled right + /// + public bool IsScrollingRight => Direction == MouseScrollDirection.Horizontal && Delta > 0; + + /// + /// Gets a boolean indicating whether the mouse scrolled left + /// + public bool IsScrollingLeft => Direction == MouseScrollDirection.Horizontal && Delta < 0; + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/InputProvider.cs b/src/Artemis.Core/Services/Input/InputProvider.cs index 12246f2a9..b71722309 100644 --- a/src/Artemis.Core/Services/Input/InputProvider.cs +++ b/src/Artemis.Core/Services/Input/InputProvider.cs @@ -31,51 +31,68 @@ namespace Artemis.Core.Services /// /// Occurs when the input provided received a device identifier /// - public event EventHandler? IdentifierReceived; + public event EventHandler? IdentifierReceived; /// /// Invokes the event which the listens to as long as /// this provider is registered /// - protected virtual void OnKeyboardDataReceived(InputProviderKeyboardEventArgs e) + /// The device that triggered the event + /// The key that triggered the event + /// Whether the key is pressed down + protected virtual void OnKeyboardDataReceived(ArtemisDevice? device, KeyboardKey key, bool isDown) { - KeyboardDataReceived?.Invoke(this, e); + KeyboardDataReceived?.Invoke(this, new InputProviderKeyboardEventArgs(device, key, isDown)); } /// /// Invokes the event which the listens to as long /// as this provider is registered /// - protected virtual void OnMouseButtonDataReceived(InputProviderMouseButtonEventArgs e) + /// The device that triggered the event + /// The button that triggered the event + /// Whether the button is pressed down + protected virtual void OnMouseButtonDataReceived(ArtemisDevice? device, MouseButton button, bool isDown) { - MouseButtonDataReceived?.Invoke(this, e); + MouseButtonDataReceived?.Invoke(this, new InputProviderMouseButtonEventArgs(device, button, isDown)); } /// /// Invokes the event which the listens to as long /// as this provider is registered /// - protected virtual void OnMouseScrollDataReceived(InputProviderMouseScrollEventArgs e) + /// The device that triggered the event + /// The direction in which was scrolled + /// The scroll delta (can positive or negative) + protected virtual void OnMouseScrollDataReceived(ArtemisDevice? device, MouseScrollDirection direction, int delta) { - MouseScrollDataReceived?.Invoke(this, e); + MouseScrollDataReceived?.Invoke(this, new InputProviderMouseScrollEventArgs(device, direction, delta)); } /// /// Invokes the event which the listens to as long as /// this provider is registered /// - protected virtual void OnMouseMoveDataReceived(InputProviderMouseMoveEventArgs e) + /// The device that triggered the event + /// The X position of the mouse cursor (not necessarily tied to the specific device) + /// The Y position of the mouse cursor (not necessarily tied to the specific device) + /// The movement delta in the horizontal direction + /// The movement delta in the vertical direction + protected virtual void OnMouseMoveDataReceived(ArtemisDevice? device, int cursorX, int cursorY, int deltaX, int deltaY) { - MouseMoveDataReceived?.Invoke(this, e); + MouseMoveDataReceived?.Invoke(this, new InputProviderMouseMoveEventArgs(device, cursorX, cursorY, deltaX, deltaY)); } /// /// Invokes the event which the listens to as long as /// this provider is registered + /// Except for on mouse movement, call this whenever you have an identifier ready for the core to process /// - protected virtual void OnIdentifierReceived(object identifier) + /// A value that can be used to identify this device + /// The type of device this identifier belongs to + protected virtual void OnIdentifierReceived(object identifier, InputDeviceType deviceType) { - IdentifierReceived?.Invoke(this, identifier); + IdentifierReceived?.Invoke(this, new InputProviderIdentifierEventArgs(identifier, deviceType)); } #region IDisposable diff --git a/src/Artemis.Core/Services/Input/InputService.cs b/src/Artemis.Core/Services/Input/InputService.cs index a1e87bc19..4bc608fb8 100644 --- a/src/Artemis.Core/Services/Input/InputService.cs +++ b/src/Artemis.Core/Services/Input/InputService.cs @@ -10,14 +10,13 @@ namespace Artemis.Core.Services { private readonly ILogger _logger; private readonly ISurfaceService _surfaceService; - private readonly List _inputProviders; public InputService(ILogger logger, ISurfaceService surfaceService) { _logger = logger; _surfaceService = surfaceService; _inputProviders = new List(); - _keyboardModifier = new Dictionary(); + _keyboardModifier = new Dictionary(); _deviceCache = new Dictionary, ArtemisDevice>(); _devices = new List(); @@ -26,6 +25,21 @@ namespace Artemis.Core.Services BustIdentifierCache(); } + #region IDisposable + + /// + public void Dispose() + { + while (_inputProviders.Any()) + RemoveInputProvider(_inputProviders.First()); + } + + #endregion + + #region Providers + + private readonly List _inputProviders; + public void AddInputProvider(InputProvider inputProvider) { inputProvider.IdentifierReceived += InputProviderOnIdentifierReceived; @@ -49,6 +63,8 @@ namespace Artemis.Core.Services inputProvider.MouseMoveDataReceived -= InputProviderOnMouseMoveDataReceived; } + #endregion + #region Identification private readonly Dictionary, ArtemisDevice> _deviceCache; @@ -59,6 +75,10 @@ namespace Artemis.Core.Services public void IdentifyDevice(ArtemisDevice device) { + if (device.RgbDevice.DeviceInfo.DeviceType != RGBDeviceType.Keyboard && device.RgbDevice.DeviceInfo.DeviceType != RGBDeviceType.Mouse) + throw new ArtemisCoreException($"Cannot initialize input-identification for a device of type {device.RgbDevice.DeviceInfo.DeviceType}. \r\n" + + "Only keyboard and mouse is supported."); + _identifyingDevice = device; _logger.Debug("Start identifying device {device}", device); } @@ -73,7 +93,7 @@ namespace Artemis.Core.Services BustIdentifierCache(); } - public ArtemisDevice? GetDeviceByIdentifier(InputProvider provider, object identifier, InputFallbackDeviceType fallbackType) + public ArtemisDevice? GetDeviceByIdentifier(InputProvider provider, object identifier, InputDeviceType type) { if (provider == null) throw new ArgumentNullException(nameof(provider)); if (identifier == null) throw new ArgumentNullException(nameof(identifier)); @@ -84,7 +104,7 @@ namespace Artemis.Core.Services return cacheMatch; string providerName = provider.GetType().FullName!; - ArtemisDevice? match = _devices.FirstOrDefault(m => m.InputIdentifiers.Any(i => Equals(i.InputProvider,providerName) && Equals(i.Identifier, identifier))); + ArtemisDevice? match = _devices.FirstOrDefault(m => m.InputIdentifiers.Any(i => Equals(i.InputProvider, providerName) && Equals(i.Identifier, identifier))); // If a match was found cache it to speed up the next event and return the match if (match != null) @@ -94,9 +114,9 @@ namespace Artemis.Core.Services } // If there is no match, apply our fallback type - if (fallbackType == InputFallbackDeviceType.None) + if (type == InputDeviceType.None) return null; - if (fallbackType == InputFallbackDeviceType.Keyboard) + if (type == InputDeviceType.Keyboard) { if (_cachedFallbackKeyboard != null) return _cachedFallbackKeyboard; @@ -104,7 +124,7 @@ namespace Artemis.Core.Services return _cachedFallbackKeyboard; } - if (fallbackType == InputFallbackDeviceType.Mouse) + if (type == InputDeviceType.Mouse) { if (_cachedFallbackMouse != null) return _cachedFallbackMouse; @@ -140,18 +160,21 @@ namespace Artemis.Core.Services BustIdentifierCache(); } - private void InputProviderOnIdentifierReceived(object? sender, object identifier) + private void InputProviderOnIdentifierReceived(object? sender, InputProviderIdentifierEventArgs e) { - if (_identifyingDevice == null) + // Don't match if there is no device or if the device type differs from the event device type + if (_identifyingDevice == null || + _identifyingDevice.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard && e.DeviceType == InputDeviceType.Mouse || + _identifyingDevice.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse && e.DeviceType == InputDeviceType.Keyboard) return; - if (!(sender is InputProvider inputProvider)) + if (!(sender is InputProvider inputProvider)) return; string providerName = inputProvider.GetType().FullName!; // Remove existing identification _identifyingDevice.InputIdentifiers.RemoveAll(i => i.InputProvider == providerName); - _identifyingDevice.InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(providerName, identifier)); + _identifyingDevice.InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(providerName, e.Identifier)); StopIdentify(); OnDeviceIdentified(); @@ -161,12 +184,12 @@ namespace Artemis.Core.Services #region Keyboard - private readonly Dictionary _keyboardModifier; - private KeyboardModifierKeys _globalModifiers; - + private readonly Dictionary _keyboardModifier; + private KeyboardModifierKey _globalModifiers; + private void InputProviderOnKeyboardDataReceived(object? sender, InputProviderKeyboardEventArgs e) { - KeyboardModifierKeys keyboardModifierKeys = UpdateModifierKeys(e.Device, e.Key, e.IsDown); + KeyboardModifierKey keyboardModifierKey = UpdateModifierKeys(e.Device, e.Key, e.IsDown); // Get the LED - TODO: leverage a lookup bool foundLedId = InputKeyUtilities.KeyboardKeyLedIdMap.TryGetValue(e.Key, out LedId ledId); @@ -175,49 +198,49 @@ namespace Artemis.Core.Services led = e.Device.Leds.FirstOrDefault(l => l.RgbLed.Id == ledId); // Create the UpDown event args because it can be used for every event - KeyboardKeyUpDownEventArgs eventArgs = new KeyboardKeyUpDownEventArgs(e.Device, led, e.Key, keyboardModifierKeys, e.IsDown); + KeyboardKeyUpDownEventArgs eventArgs = new KeyboardKeyUpDownEventArgs(e.Device, led, e.Key, keyboardModifierKey, e.IsDown); OnKeyboardKeyUpDown(eventArgs); if (e.IsDown) OnKeyboardKeyDown(eventArgs); else OnKeyboardKeyUp(eventArgs); - _logger.Verbose("Keyboard data: LED ID: {ledId}, key: {key}, is down: {isDown}, modifiers: {modifiers}, device: {device} ", ledId, e.Key, e.IsDown, keyboardModifierKeys, e.Device); + // _logger.Verbose("Keyboard data: LED ID: {ledId}, key: {key}, is down: {isDown}, modifiers: {modifiers}, device: {device} ", ledId, e.Key, e.IsDown, keyboardModifierKey, e.Device); } - private KeyboardModifierKeys UpdateModifierKeys(ArtemisDevice? device, KeyboardKey key, in bool isDown) + private KeyboardModifierKey UpdateModifierKeys(ArtemisDevice? device, KeyboardKey key, in bool isDown) { - KeyboardModifierKeys modifiers = _globalModifiers; + KeyboardModifierKey modifiers = _globalModifiers; if (device != null) _keyboardModifier.TryGetValue(device, out modifiers); if (key == KeyboardKey.LeftAlt || key == KeyboardKey.RightAlt) { if (isDown) - modifiers = modifiers | KeyboardModifierKeys.Alt; + modifiers |= KeyboardModifierKey.Alt; else - modifiers = modifiers & ~KeyboardModifierKeys.Alt; + modifiers &= ~KeyboardModifierKey.Alt; } else if (key == KeyboardKey.LeftCtrl || key == KeyboardKey.RightCtrl) { if (isDown) - modifiers = modifiers | KeyboardModifierKeys.Control; + modifiers |= KeyboardModifierKey.Control; else - modifiers = modifiers & ~KeyboardModifierKeys.Control; + modifiers &= ~KeyboardModifierKey.Control; } else if (key == KeyboardKey.LeftShift || key == KeyboardKey.RightShift) { if (isDown) - modifiers = modifiers | KeyboardModifierKeys.Shift; + modifiers |= KeyboardModifierKey.Shift; else - modifiers = modifiers & ~KeyboardModifierKeys.Shift; + modifiers &= ~KeyboardModifierKey.Shift; } else if (key == KeyboardKey.LWin || key == KeyboardKey.RWin) { if (isDown) - modifiers = modifiers | KeyboardModifierKeys.Windows; + modifiers |= KeyboardModifierKey.Windows; else - modifiers = modifiers & ~KeyboardModifierKeys.Windows; + modifiers &= ~KeyboardModifierKey.Windows; } if (device != null) @@ -235,16 +258,30 @@ namespace Artemis.Core.Services private void InputProviderOnMouseButtonDataReceived(object? sender, InputProviderMouseButtonEventArgs e) { bool foundLedId = InputKeyUtilities.MouseButtonLedIdMap.TryGetValue(e.Button, out LedId ledId); + ArtemisLed? led = null; + if (foundLedId && e.Device != null) + led = e.Device.Leds.FirstOrDefault(l => l.RgbLed.Id == ledId); + + // Create the UpDown event args because it can be used for every event + MouseButtonUpDownEventArgs eventArgs = new MouseButtonUpDownEventArgs(e.Device, led, e.Button, e.IsDown); + OnMouseButtonUpDown(eventArgs); + if (e.IsDown) + OnMouseButtonDown(eventArgs); + else + OnMouseButtonUp(eventArgs); + // _logger.Verbose("Mouse button data: LED ID: {ledId}, button: {button}, is down: {isDown}, device: {device} ", ledId, e.Button, e.IsDown, e.Device); } private void InputProviderOnMouseScrollDataReceived(object? sender, InputProviderMouseScrollEventArgs e) { + OnMouseScroll(new MouseScrollEventArgs(e.Device, e.Direction, e.Delta)); // _logger.Verbose("Mouse scroll data: Direction: {direction}, delta: {delta}, device: {device} ", e.Direction, e.Delta, e.Device); } private void InputProviderOnMouseMoveDataReceived(object? sender, InputProviderMouseMoveEventArgs e) { + OnMouseMove(new MouseMoveEventArgs(e.Device, e.CursorX, e.CursorY, e.DeltaX, e.DeltaY)); // _logger.Verbose("Mouse move data: XY: {X},{Y} - delta XY: {deltaX},{deltaY} - device: {device} ", e.CursorX, e.CursorY, e.DeltaX, e.DeltaY, e.Device); } @@ -253,8 +290,13 @@ namespace Artemis.Core.Services #region Events public event EventHandler? KeyboardKeyUpDown; - public event EventHandler? KeyboardKeyDown; - public event EventHandler? KeyboardKeyUp; + public event EventHandler? KeyboardKeyDown; + public event EventHandler? KeyboardKeyUp; + public event EventHandler? MouseButtonUpDown; + public event EventHandler? MouseButtonDown; + public event EventHandler? MouseButtonUp; + public event EventHandler? MouseScroll; + public event EventHandler? MouseMove; public event EventHandler? DeviceIdentified; protected virtual void OnKeyboardKeyUpDown(KeyboardKeyUpDownEventArgs e) @@ -262,32 +304,46 @@ namespace Artemis.Core.Services KeyboardKeyUpDown?.Invoke(this, e); } - protected virtual void OnKeyboardKeyDown(KeyboardEventArgs e) + protected virtual void OnKeyboardKeyDown(KeyboardKeyEventArgs e) { KeyboardKeyDown?.Invoke(this, e); } - protected virtual void OnKeyboardKeyUp(KeyboardEventArgs e) + protected virtual void OnKeyboardKeyUp(KeyboardKeyEventArgs e) { KeyboardKeyUp?.Invoke(this, e); } + protected virtual void OnMouseButtonUpDown(MouseButtonUpDownEventArgs e) + { + MouseButtonUpDown?.Invoke(this, e); + } + + protected virtual void OnMouseButtonDown(MouseButtonEventArgs e) + { + MouseButtonDown?.Invoke(this, e); + } + + protected virtual void OnMouseButtonUp(MouseButtonEventArgs e) + { + MouseButtonUp?.Invoke(this, e); + } + + protected virtual void OnMouseScroll(MouseScrollEventArgs e) + { + MouseScroll?.Invoke(this, e); + } + + protected virtual void OnMouseMove(MouseMoveEventArgs e) + { + MouseMove?.Invoke(this, e); + } + protected virtual void OnDeviceIdentified() { DeviceIdentified?.Invoke(this, EventArgs.Empty); } #endregion - - #region IDisposable - - /// - public void Dispose() - { - while (_inputProviders.Any()) - RemoveInputProvider(_inputProviders.First()); - } - - #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Interfaces/IInputService.cs b/src/Artemis.Core/Services/Input/Interfaces/IInputService.cs index 8b0897edf..12e01601c 100644 --- a/src/Artemis.Core/Services/Input/Interfaces/IInputService.cs +++ b/src/Artemis.Core/Services/Input/Interfaces/IInputService.cs @@ -22,7 +22,7 @@ namespace Artemis.Core.Services /// /// Identifies the provided by assigning the next received device identification to it /// - /// The device to identify + /// The device to identify - Must be a keyboard or mouse void IdentifyDevice(ArtemisDevice device); /// @@ -40,12 +40,37 @@ namespace Artemis.Core.Services /// /// Occurs whenever a key on a keyboard was pressed /// - event EventHandler KeyboardKeyDown; + event EventHandler KeyboardKeyDown; /// /// Occurs whenever a key on a keyboard was released /// - event EventHandler KeyboardKeyUp; + event EventHandler KeyboardKeyUp; + + /// + /// Occurs whenever a button on a mouse was pressed or released + /// + event EventHandler MouseButtonUpDown; + + /// + /// Occurs whenever a button on a mouse was pressed + /// + event EventHandler MouseButtonDown; + + /// + /// Occurs whenever a button on a mouse was released + /// + event EventHandler MouseButtonUp; + + /// + /// Occurs whenever a the scroll wheel of a mouse is turned + /// + event EventHandler MouseScroll; + + /// + /// Occurs whenever a a mouse is moved + /// + event EventHandler MouseMove; /// /// Occurs when a device has been identified after calling @@ -61,9 +86,9 @@ namespace Artemis.Core.Services /// /// The input provider to identify the device for /// The value to use to identify the device - /// A device type to fall back to if no match is found + /// A device type to fall back to if no match is found /// If found, the Artemis device matching the provider and identifier - ArtemisDevice? GetDeviceByIdentifier(InputProvider provider, object identifier, InputFallbackDeviceType fallbackType); + ArtemisDevice? GetDeviceByIdentifier(InputProvider provider, object identifier, InputDeviceType type); /// /// Clears the identifier cache diff --git a/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs b/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs index 38712cf13..dc2c481d3 100644 --- a/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs +++ b/src/Artemis.Storage/Entities/Surface/DeviceEntity.cs @@ -6,7 +6,7 @@ namespace Artemis.Storage.Entities.Surface { public DeviceEntity() { - InputIdentifier = new List(); + InputIdentifiers = new List(); } public string DeviceIdentifier { get; set; } @@ -16,7 +16,7 @@ namespace Artemis.Storage.Entities.Surface public double Scale { get; set; } public int ZIndex { get; set; } - public List InputIdentifier { get; set; } + public List InputIdentifiers { get; set; } } public class DeviceInputIdentifierEntity diff --git a/src/Artemis.Storage/Repositories/SurfaceRepository.cs b/src/Artemis.Storage/Repositories/SurfaceRepository.cs index 85f5c6cbd..46b8b9e6b 100644 --- a/src/Artemis.Storage/Repositories/SurfaceRepository.cs +++ b/src/Artemis.Storage/Repositories/SurfaceRepository.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Artemis.Storage.Entities.Surface; using Artemis.Storage.Repositories.Interfaces; using LiteDB; @@ -32,7 +33,7 @@ namespace Artemis.Storage.Repositories public List GetAll() { - return _repository.Query().Include(s => s.DeviceEntities).ToList(); + return _repository.Query().Include(s => s.DeviceEntities.Select(de => de.InputIdentifiers)).ToList(); } public void Save(SurfaceEntity surfaceEntity) diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs index d590df5ca..22eeb33a1 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Windows.Documents; using Artemis.Core; using Artemis.Core.DataModelExpansions; using Artemis.UI.Shared.Services; diff --git a/src/Artemis.UI.Shared/Screens/Dialogs/ConfirmDialogViewModel.cs b/src/Artemis.UI.Shared/Screens/Dialogs/ConfirmDialogViewModel.cs index 4d65fe81c..d53436ae0 100644 --- a/src/Artemis.UI.Shared/Screens/Dialogs/ConfirmDialogViewModel.cs +++ b/src/Artemis.UI.Shared/Screens/Dialogs/ConfirmDialogViewModel.cs @@ -22,11 +22,5 @@ namespace Artemis.UI.Shared.Screens.Dialogs if (Session != null && !Session.IsEnded) Session.Close(true); } - - public void Cancel() - { - if (Session != null && !Session.IsEnded) - Session.Close(false); - } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorViewModel.cs b/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorViewModel.cs index c2cec49dd..d95ee175d 100644 --- a/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorViewModel.cs +++ b/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorViewModel.cs @@ -91,15 +91,14 @@ namespace Artemis.UI.Shared.Screens.GradientEditor Session.Close(true); } - public void Cancel() + public override void Cancel() { // Restore the saved state ColorGradient.Stops.Clear(); ColorGradient.Stops.AddRange(_originalStops); ColorGradient.OnColorValuesUpdated(); - if (Session != null && !Session.IsEnded) - Session.Close(false); + base.Cancel(); } private void UpdateColorStopViewModels(object? sender, PropertyChangedEventArgs e) diff --git a/src/Artemis.UI.Shared/Services/Dialog/DialogViewModelBase.cs b/src/Artemis.UI.Shared/Services/Dialog/DialogViewModelBase.cs index 5a93b3767..3cdddba3b 100644 --- a/src/Artemis.UI.Shared/Services/Dialog/DialogViewModelBase.cs +++ b/src/Artemis.UI.Shared/Services/Dialog/DialogViewModelBase.cs @@ -49,6 +49,15 @@ namespace Artemis.UI.Shared.Services { } + /// + /// If not yet closed, closes the current session passing + /// + public virtual void Cancel() + { + if (Session != null && !Session.IsEnded) + Session.Close(false); + } + internal void OnDialogOpened(object sender, DialogOpenedEventArgs e) { Session = e.Session; diff --git a/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs b/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs index 8d8c79264..a0e8b4d39 100644 --- a/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs +++ b/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs @@ -81,11 +81,11 @@ namespace Artemis.UI.InputProviders // Let the core know there is an identifier so it can store new identifications if applicable if (identifier != null) - OnIdentifierReceived(identifier); + OnIdentifierReceived(identifier, InputDeviceType.Keyboard); ArtemisDevice device = null; if (identifier != null) - device = _inputService.GetDeviceByIdentifier(this, identifier, InputFallbackDeviceType.Keyboard); + device = _inputService.GetDeviceByIdentifier(this, identifier, InputDeviceType.Keyboard); // 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 (keyboardData.Keyboard.Flags == RawKeyboardFlags.LeftKey || keyboardData.Keyboard.Flags == (RawKeyboardFlags.LeftKey | RawKeyboardFlags.Up)) @@ -105,7 +105,7 @@ namespace Artemis.UI.InputProviders keyboardData.Keyboard.Flags != (RawKeyboardFlags.Up | RawKeyboardFlags.LeftKey) && keyboardData.Keyboard.Flags != (RawKeyboardFlags.Up | RawKeyboardFlags.RightKey); - OnKeyboardDataReceived(new InputProviderKeyboardEventArgs(device, key, isDown)); + OnKeyboardDataReceived(device, key, isDown); } #endregion @@ -130,7 +130,7 @@ namespace Artemis.UI.InputProviders ArtemisDevice device = null; string identifier = data.Device?.DevicePath; if (identifier != null) - device = _inputService.GetDeviceByIdentifier(this, identifier, InputFallbackDeviceType.Mouse); + device = _inputService.GetDeviceByIdentifier(this, identifier, InputDeviceType.Mouse); // Debug.WriteLine($"Buttons: {data.Mouse.Buttons}, Data: {data.Mouse.ButtonData}, Flags: {data.Mouse.Flags}, XY: {data.Mouse.LastX},{data.Mouse.LastY}"); @@ -138,7 +138,7 @@ namespace Artemis.UI.InputProviders if (mouseData.Mouse.Buttons == RawMouseButtonFlags.None) { Win32Point cursorPosition = GetCursorPosition(); - OnMouseMoveDataReceived(new InputProviderMouseMoveEventArgs(device, cursorPosition.X, cursorPosition.Y, _mouseDeltaX, _mouseDeltaY)); + OnMouseMoveDataReceived(device, cursorPosition.X, cursorPosition.Y, _mouseDeltaX, _mouseDeltaY); _mouseDeltaX = 0; _mouseDeltaY = 0; _lastMouseUpdate = DateTime.Now; @@ -147,15 +147,15 @@ namespace Artemis.UI.InputProviders // Now we know its not movement, let the core know there is an identifier so it can store new identifications if applicable if (identifier != null) - OnIdentifierReceived(identifier); + OnIdentifierReceived(identifier, InputDeviceType.Mouse); // Scrolling if (mouseData.Mouse.ButtonData != 0) { if (mouseData.Mouse.Buttons == RawMouseButtonFlags.MouseWheel) - OnMouseScrollDataReceived(new InputProviderMouseScrollEventArgs(device, MouseScrollDirection.Vertical, mouseData.Mouse.ButtonData)); + OnMouseScrollDataReceived(device, MouseScrollDirection.Vertical, mouseData.Mouse.ButtonData); else if (mouseData.Mouse.Buttons == RawMouseButtonFlags.MouseHorizontalWheel) - OnMouseScrollDataReceived(new InputProviderMouseScrollEventArgs(device, MouseScrollDirection.Horizontal, mouseData.Mouse.ButtonData)); + OnMouseScrollDataReceived(device, MouseScrollDirection.Horizontal, mouseData.Mouse.ButtonData); return; } @@ -178,7 +178,7 @@ namespace Artemis.UI.InputProviders else if (DetermineMouseButton(mouseData, RawMouseButtonFlags.Button5Down, RawMouseButtonFlags.Button5Up, ref isDown)) button = MouseButton.Button5; - OnMouseButtonDataReceived(new InputProviderMouseButtonEventArgs(device, button, isDown)); + OnMouseButtonDataReceived(device, button, isDown); } private bool DetermineMouseButton(RawInputMouseData data, RawMouseButtonFlags downButton, RawMouseButtonFlags upButton, ref bool isDown) diff --git a/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileCreateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileCreateViewModel.cs index 3f38e24a4..75be9f097 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileCreateViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileCreateViewModel.cs @@ -28,11 +28,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Dialogs Session.Close(ProfileName); } - - public void Cancel() - { - Session.Close(); - } } public class ProfileCreateViewModelValidator : AbstractValidator diff --git a/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileExportViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileExportViewModel.cs index 4e7a82817..a85b85046 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileExportViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileExportViewModel.cs @@ -30,10 +30,5 @@ namespace Artemis.UI.Screens.ProfileEditor.Dialogs Session.Close(); } - - public void Cancel() - { - Session.Close(); - } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileImportViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileImportViewModel.cs index 3c824de7e..cab90345a 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileImportViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Dialogs/ProfileImportViewModel.cs @@ -37,10 +37,5 @@ namespace Artemis.UI.Screens.ProfileEditor.Dialogs _mainMessageQueue.Enqueue("Profile imported."); Session.Close(descriptor); } - - public void Cancel() - { - Session.Close(); - } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Dialogs/RenameViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Dialogs/RenameViewModel.cs index f970097de..214513c10 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Dialogs/RenameViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Dialogs/RenameViewModel.cs @@ -32,11 +32,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Dialogs Session.Close(ElementName); } - - public void Cancel() - { - Session.Close(); - } } public class ProfileElementRenameViewModelValidator : AbstractValidator diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs index f687f8eb9..a4df1b62b 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs @@ -19,6 +19,7 @@ using Artemis.UI.Shared.Services; using GongSolutions.Wpf.DragDrop; using Stylet; using static Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree.TreeGroupViewModel.LayerPropertyGroupType; +using MouseButtonEventArgs = System.Windows.Input.MouseButtonEventArgs; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties { diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Dialogs/TimelineSegmentDialogViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Dialogs/TimelineSegmentDialogViewModel.cs index d6575636e..fb4959405 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Dialogs/TimelineSegmentDialogViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Dialogs/TimelineSegmentDialogViewModel.cs @@ -38,11 +38,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Dialogs Segment.UpdateLength(TimelineSegmentDialogViewModelValidator.CreateTime(InputValue)); Session.Close(); } - - public void Cancel() - { - Session.Close(); - } } public class TimelineSegmentDialogViewModelValidator : AbstractValidator diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs index 780aac675..724831cd2 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs @@ -12,6 +12,7 @@ using Artemis.UI.Screens.ProfileEditor.Visualization.Tools; using Artemis.UI.Screens.Shared; using Artemis.UI.Shared.Services; using Stylet; +using MouseButtonEventArgs = System.Windows.Input.MouseButtonEventArgs; namespace Artemis.UI.Screens.ProfileEditor.Visualization { diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs index cf09e35ec..479169ea6 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs @@ -8,6 +8,7 @@ using Artemis.Core.LayerBrushes; using Artemis.Core.Services; using Artemis.UI.Properties; using Artemis.UI.Shared.Services; +using MouseButtonEventArgs = System.Windows.Input.MouseButtonEventArgs; namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools { diff --git a/src/Artemis.UI/Screens/RootViewModel.cs b/src/Artemis.UI/Screens/RootViewModel.cs index c021e1542..a8a5e4146 100644 --- a/src/Artemis.UI/Screens/RootViewModel.cs +++ b/src/Artemis.UI/Screens/RootViewModel.cs @@ -17,6 +17,7 @@ using Artemis.UI.Utilities; using MaterialDesignExtensions.Controls; using MaterialDesignThemes.Wpf; using Stylet; +using MouseButtonEventArgs = System.Windows.Input.MouseButtonEventArgs; namespace Artemis.UI.Screens { diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsView.xaml b/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsView.xaml index 46c27a1e5..d452a4fd0 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsView.xaml +++ b/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsView.xaml @@ -54,8 +54,31 @@ - + + + + diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsViewModel.cs index e2b2333ba..42f2121e1 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/Devices/DeviceSettingsViewModel.cs @@ -1,10 +1,15 @@ using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Threading.Tasks; using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.Ninject.Factories; +using Artemis.UI.Screens.SurfaceEditor.Dialogs; +using Artemis.UI.Screens.SurfaceEditor.Visualization; using Artemis.UI.Shared.Services; using Humanizer; +using RGB.NET.Core; using Stylet; namespace Artemis.UI.Screens.Settings.Tabs.Devices @@ -12,18 +17,24 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices public class DeviceSettingsViewModel : PropertyChangedBase { private readonly IDeviceDebugVmFactory _deviceDebugVmFactory; + private readonly ISurfaceService _surfaceService; private readonly IDeviceService _deviceService; private readonly IDialogService _dialogService; private readonly IWindowManager _windowManager; private bool _isDeviceEnabled; - public DeviceSettingsViewModel(ArtemisDevice device, IDeviceService deviceService, IDialogService dialogService, - IWindowManager windowManager, IDeviceDebugVmFactory deviceDebugVmFactory) + public DeviceSettingsViewModel(ArtemisDevice device, + IDeviceService deviceService, + IDialogService dialogService, + IWindowManager windowManager, + IDeviceDebugVmFactory deviceDebugVmFactory, + ISurfaceService surfaceService) { _deviceService = deviceService; _dialogService = dialogService; _windowManager = windowManager; _deviceDebugVmFactory = deviceDebugVmFactory; + _surfaceService = surfaceService; Device = device; Type = Device.RgbDevice.DeviceInfo.DeviceType.ToString().Humanize(); @@ -40,6 +51,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices public string Name { get; } public string Manufacturer { get; } + public bool CanDetectInput => Device.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || + Device.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse; + public bool IsDeviceEnabled { get => _isDeviceEnabled; @@ -67,5 +81,25 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices _dialogService.ShowExceptionDialog("Welp, we couldn't open the device's plugin folder for you", e); } } + + public async Task DetectInput() + { + object madeChanges = await _dialogService.ShowDialog( + new Dictionary { { "device", Device } } + ); + + if ((bool)madeChanges) + _surfaceService.UpdateSurfaceConfiguration(_surfaceService.ActiveSurface, true); + } + + public async Task ViewProperties() + { + object madeChanges = await _dialogService.ShowDialog( + new Dictionary {{"device", Device}} + ); + + if ((bool) madeChanges) + _surfaceService.UpdateSurfaceConfiguration(_surfaceService.ActiveSurface, true); + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Splash/SplashViewModel.cs b/src/Artemis.UI/Screens/Splash/SplashViewModel.cs index f80543a79..81ad13825 100644 --- a/src/Artemis.UI/Screens/Splash/SplashViewModel.cs +++ b/src/Artemis.UI/Screens/Splash/SplashViewModel.cs @@ -6,6 +6,7 @@ using Humanizer; using MaterialDesignExtensions.Controls; using Stylet; using MouseButton = System.Windows.Input.MouseButton; +using MouseButtonEventArgs = System.Windows.Input.MouseButtonEventArgs; namespace Artemis.UI.Screens.Splash { diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceCreateViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceCreateViewModel.cs index c92ebc24b..92c03e995 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceCreateViewModel.cs +++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceCreateViewModel.cs @@ -27,10 +27,5 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs Session.Close(SurfaceName); } - - public void Cancel() - { - Session.Close(); - } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs index 8553748d8..bd74d1076 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs +++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceConfigViewModel.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; +using Artemis.Core; using Artemis.Core.Services; -using Artemis.UI.Screens.SurfaceEditor.Visualization; using Artemis.UI.Shared.Services; using Stylet; @@ -15,20 +15,21 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs private int _x; private int _y; - public SurfaceDeviceConfigViewModel(SurfaceDeviceViewModel surfaceDeviceViewModel, ICoreService coreService, IModelValidator validator) - : base(validator) + public SurfaceDeviceConfigViewModel(ArtemisDevice device, ICoreService coreService, IModelValidator validator) : base(validator) { _coreService = coreService; - SurfaceDeviceViewModel = surfaceDeviceViewModel; - Title = $"{SurfaceDeviceViewModel.Device.RgbDevice.DeviceInfo.DeviceName} - Properties"; - X = (int) SurfaceDeviceViewModel.Device.X; - Y = (int) SurfaceDeviceViewModel.Device.Y; - Scale = SurfaceDeviceViewModel.Device.Scale; - Rotation = (int) SurfaceDeviceViewModel.Device.Rotation; + Device = device; + Title = $"{Device.RgbDevice.DeviceInfo.DeviceName} - Properties"; + + X = (int) Device.X; + Y = (int) Device.Y; + Scale = Device.Scale; + Rotation = (int) Device.Rotation; } - public SurfaceDeviceViewModel SurfaceDeviceViewModel { get; } + public ArtemisDevice Device { get; } + public string Title { @@ -69,19 +70,14 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs _coreService.ModuleRenderingDisabled = true; await Task.Delay(100); - SurfaceDeviceViewModel.Device.X = X; - SurfaceDeviceViewModel.Device.Y = Y; - SurfaceDeviceViewModel.Device.Scale = Scale; - SurfaceDeviceViewModel.Device.Rotation = Rotation; + Device.X = X; + Device.Y = Y; + Device.Scale = Scale; + Device.Rotation = Rotation; _coreService.ModuleRenderingDisabled = false; Session.Close(true); } - - public void Cancel() - { - Session.Close(false); - } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputView.xaml b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputView.xaml index 16e93aca1..54349c2f4 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputView.xaml +++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputView.xaml @@ -17,13 +17,39 @@ that is currently showing a yellow color. - + + This will teach Artemis to associate button/key presses with this specific device. - + +