From 84918f2d5b769155347917cdebc88278ac0454d1 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Sun, 22 Nov 2020 21:33:42 +0100 Subject: [PATCH] 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)