diff --git a/src/Artemis.Core/Artemis.Core.csproj.DotSettings b/src/Artemis.Core/Artemis.Core.csproj.DotSettings index 006fb1134..0ff3e797c 100644 --- a/src/Artemis.Core/Artemis.Core.csproj.DotSettings +++ b/src/Artemis.Core/Artemis.Core.csproj.DotSettings @@ -55,6 +55,10 @@ True True True + 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..953fda3d2 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; @@ -23,6 +24,9 @@ namespace Artemis.Core DeviceProvider = deviceProvider; Surface = surface; DeviceEntity = new DeviceEntity(); + + InputIdentifiers = new List(); + _leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); Rotation = 0; @@ -39,6 +43,11 @@ namespace Artemis.Core DeviceProvider = deviceProvider; Surface = surface; 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(); } @@ -84,6 +93,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 +188,16 @@ namespace Artemis.Core { // Other properties are computed DeviceEntity.DeviceIdentifier = RgbDevice.GetDeviceIdentifier(); + + DeviceEntity.InputIdentifiers.Clear(); + foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers) + { + DeviceEntity.InputIdentifiers.Add(new DeviceInputIdentifierEntity + { + InputProvider = identifier.InputProvider, + Identifier = identifier.Identifier + }); + } } internal void ApplyToRgbDevice() @@ -186,6 +210,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.InputIdentifiers) + 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/Models/Surface/ArtemisLed.cs b/src/Artemis.Core/Models/Surface/ArtemisLed.cs index 4cc6d003b..e31594d40 100644 --- a/src/Artemis.Core/Models/Surface/ArtemisLed.cs +++ b/src/Artemis.Core/Models/Surface/ArtemisLed.cs @@ -62,5 +62,11 @@ namespace Artemis.Core (RgbLed.AbsoluteLedRectangle.Size.Height * Device.Surface.Scale).RoundToInt() ); } + + /// + public override string ToString() + { + return RgbLed.ToString(); + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Enums/InputDeviceType.cs b/src/Artemis.Core/Services/Input/Enums/InputDeviceType.cs new file mode 100644 index 000000000..3d7ef1149 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Enums/InputDeviceType.cs @@ -0,0 +1,23 @@ +namespace Artemis.Core.Services +{ + /// + /// Represents a device that provides input to the + /// + public enum InputDeviceType + { + /// + /// None + /// + None, + + /// + /// A keyboard + /// + Keyboard, + + /// + /// A mouse + /// + Mouse + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Enums/KeyboardKey.cs b/src/Artemis.Core/Services/Input/Enums/KeyboardKey.cs new file mode 100644 index 000000000..4f13f0846 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Enums/KeyboardKey.cs @@ -0,0 +1,537 @@ +namespace Artemis.Core.Services +{ + /// Specifies the possible key values on a keyboard. + 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. + /// + 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 + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKey.cs b/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKey.cs new file mode 100644 index 000000000..2ebde89e7 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKey.cs @@ -0,0 +1,26 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Specifies the set of modifier keys. + /// + [Flags] + public enum KeyboardModifierKey + { + /// 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/Events/ArtemisKeyboardKeyEventArgs.cs b/src/Artemis.Core/Services/Input/Events/ArtemisKeyboardKeyEventArgs.cs new file mode 100644 index 000000000..c4d626494 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/ArtemisKeyboardKeyEventArgs.cs @@ -0,0 +1,38 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for keyboard input events + /// + public class ArtemisKeyboardKeyEventArgs : EventArgs + { + internal ArtemisKeyboardKeyEventArgs(ArtemisDevice? device, ArtemisLed? led, KeyboardKey key, KeyboardModifierKey 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 KeyboardModifierKey Modifiers { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/Events/ArtemisKeyboardKeyUpDownEventArgs.cs b/src/Artemis.Core/Services/Input/Events/ArtemisKeyboardKeyUpDownEventArgs.cs new file mode 100644 index 000000000..c8ae71123 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/ArtemisKeyboardKeyUpDownEventArgs.cs @@ -0,0 +1,18 @@ +namespace Artemis.Core.Services +{ + /// + /// Contains data for keyboard input events + /// + public class ArtemisKeyboardKeyUpDownEventArgs : ArtemisKeyboardKeyEventArgs + { + internal ArtemisKeyboardKeyUpDownEventArgs(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/ArtemisMouseButtonEventArgs.cs b/src/Artemis.Core/Services/Input/Events/ArtemisMouseButtonEventArgs.cs new file mode 100644 index 000000000..a27aab606 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/ArtemisMouseButtonEventArgs.cs @@ -0,0 +1,32 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for mouse input events + /// + public class ArtemisMouseButtonEventArgs : EventArgs + { + internal ArtemisMouseButtonEventArgs(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/ArtemisMouseButtonUpDownEventArgs.cs b/src/Artemis.Core/Services/Input/Events/ArtemisMouseButtonUpDownEventArgs.cs new file mode 100644 index 000000000..7bc4aa872 --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/ArtemisMouseButtonUpDownEventArgs.cs @@ -0,0 +1,18 @@ +namespace Artemis.Core.Services +{ + /// + /// Contains data for mouse input events + /// + public class ArtemisMouseButtonUpDownEventArgs : ArtemisMouseButtonEventArgs + { + internal ArtemisMouseButtonUpDownEventArgs(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/ArtemisMouseMoveEventArgs.cs b/src/Artemis.Core/Services/Input/Events/ArtemisMouseMoveEventArgs.cs new file mode 100644 index 000000000..9ba03596e --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/ArtemisMouseMoveEventArgs.cs @@ -0,0 +1,51 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for mouse movement events + /// + public class ArtemisMouseMoveEventArgs : 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 ArtemisMouseMoveEventArgs(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/ArtemisMouseScrollEventArgs.cs b/src/Artemis.Core/Services/Input/Events/ArtemisMouseScrollEventArgs.cs new file mode 100644 index 000000000..a3308ae2e --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/ArtemisMouseScrollEventArgs.cs @@ -0,0 +1,57 @@ +using System; + +namespace Artemis.Core.Services +{ + /// + /// Contains data for mouse scroll events + /// + public class ArtemisMouseScrollEventArgs : EventArgs + { + /// + /// + /// The device that triggered the event + /// The direction in which was scrolled + /// The scroll delta (can positive or negative) + internal ArtemisMouseScrollEventArgs(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/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/InputProviderKeyboardEventArgs.cs b/src/Artemis.Core/Services/Input/Events/InputProviderKeyboardEventArgs.cs new file mode 100644 index 000000000..313cd593f --- /dev/null +++ b/src/Artemis.Core/Services/Input/Events/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, KeyboardKey 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 KeyboardKey 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/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..e0231af91 --- /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 in pixels (not necessarily tied to the specific device) + /// + public int CursorX { get; } + + /// + /// 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 in pixels + /// + public int DeltaX { get; } + + /// + /// Gets the movement delta in the vertical direction in pixels + /// + 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/InputKeyLedIdMap.cs b/src/Artemis.Core/Services/Input/InputKeyLedIdMap.cs new file mode 100644 index 000000000..dc485e69c --- /dev/null +++ b/src/Artemis.Core/Services/Input/InputKeyLedIdMap.cs @@ -0,0 +1,194 @@ +using System.Collections.Generic; +using RGB.NET.Core; + +namespace Artemis.Core.Services +{ + internal static class InputKeyUtilities + { + internal static readonly Dictionary KeyboardKeyLedIdMap = new Dictionary + { + {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 new file mode 100644 index 000000000..b71722309 --- /dev/null +++ b/src/Artemis.Core/Services/Input/InputProvider.cs @@ -0,0 +1,123 @@ +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; + + /// + /// 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 + /// + /// 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, new InputProviderKeyboardEventArgs(device, key, isDown)); + } + + /// + /// Invokes the event which the listens to as long + /// as this provider is registered + /// + /// 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, new InputProviderMouseButtonEventArgs(device, button, isDown)); + } + + /// + /// Invokes the event which the listens to as long + /// as this provider is registered + /// + /// 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, new InputProviderMouseScrollEventArgs(device, direction, delta)); + } + + /// + /// Invokes the event which the listens to as long as + /// this provider is registered + /// + /// 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, 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 + /// + /// 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, new InputProviderIdentifierEventArgs(identifier, deviceType)); + } + + #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/InputService.cs b/src/Artemis.Core/Services/Input/InputService.cs new file mode 100644 index 000000000..c3fb070ab --- /dev/null +++ b/src/Artemis.Core/Services/Input/InputService.cs @@ -0,0 +1,375 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using RGB.NET.Core; +using Serilog; + +namespace Artemis.Core.Services +{ + internal class InputService : IInputService + { + private readonly ILogger _logger; + private readonly ISurfaceService _surfaceService; + + public InputService(ILogger logger, ISurfaceService surfaceService) + { + _logger = logger; + _surfaceService = surfaceService; + + _surfaceService.ActiveSurfaceConfigurationSelected += SurfaceConfigurationChanged; + _surfaceService.SurfaceConfigurationUpdated += SurfaceConfigurationChanged; + BustIdentifierCache(); + } + + #region IDisposable + + /// + public void Dispose() + { + while (_inputProviders.Any()) + RemoveInputProvider(_inputProviders.First()); + } + + #endregion + + #region Providers + + private readonly List _inputProviders = new List(); + + public void AddInputProvider(InputProvider inputProvider) + { + inputProvider.IdentifierReceived += InputProviderOnIdentifierReceived; + inputProvider.KeyboardDataReceived += InputProviderOnKeyboardDataReceived; + inputProvider.MouseButtonDataReceived += InputProviderOnMouseButtonDataReceived; + inputProvider.MouseScrollDataReceived += InputProviderOnMouseScrollDataReceived; + inputProvider.MouseMoveDataReceived += InputProviderOnMouseMoveDataReceived; + _inputProviders.Add(inputProvider); + } + + public void RemoveInputProvider(InputProvider inputProvider) + { + if (!_inputProviders.Contains(inputProvider)) + return; + + _inputProviders.Remove(inputProvider); + inputProvider.IdentifierReceived -= InputProviderOnIdentifierReceived; + inputProvider.KeyboardDataReceived -= InputProviderOnKeyboardDataReceived; + inputProvider.MouseButtonDataReceived -= InputProviderOnMouseButtonDataReceived; + inputProvider.MouseScrollDataReceived -= InputProviderOnMouseScrollDataReceived; + inputProvider.MouseMoveDataReceived -= InputProviderOnMouseMoveDataReceived; + } + + #endregion + + #region Identification + + private readonly Dictionary, ArtemisDevice> _deviceCache = new Dictionary, ArtemisDevice>(); + private List _devices = new List(); + private ArtemisDevice? _cachedFallbackKeyboard; + private ArtemisDevice? _cachedFallbackMouse; + private ArtemisDevice? _identifyingDevice; + + 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); + } + + 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, InputDeviceType type) + { + 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 (type == InputDeviceType.None) + return null; + if (type == InputDeviceType.Keyboard) + { + if (_cachedFallbackKeyboard != null) + return _cachedFallbackKeyboard; + _cachedFallbackKeyboard = _surfaceService.ActiveSurface.Devices.FirstOrDefault(d => d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard); + return _cachedFallbackKeyboard; + } + + if (type == InputDeviceType.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, InputProviderIdentifierEventArgs e) + { + // 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)) + return; + + string providerName = inputProvider.GetType().FullName!; + + // Remove existing identification + _identifyingDevice.InputIdentifiers.RemoveAll(i => i.InputProvider == providerName); + _identifyingDevice.InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(providerName, e.Identifier)); + + StopIdentify(); + OnDeviceIdentified(); + } + + #endregion + + #region Keyboard + + private readonly Dictionary> _pressedKeys = new Dictionary>(); + private readonly Dictionary _keyboardModifier = new Dictionary(); + private KeyboardModifierKey _globalModifiers; + + private void InputProviderOnKeyboardDataReceived(object? sender, InputProviderKeyboardEventArgs e) + { + KeyboardModifierKey keyboardModifierKey = UpdateModifierKeys(e.Device, e.Key, e.IsDown); + + // if UpdatePressedKeys is true, the key is already pressed, then we skip this event + if (UpdatePressedKeys(e.Device, e.Key, e.IsDown)) + return; + + // 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 + ArtemisKeyboardKeyUpDownEventArgs eventArgs = new ArtemisKeyboardKeyUpDownEventArgs(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, keyboardModifierKey, e.Device); + } + + private bool UpdatePressedKeys(ArtemisDevice? device, KeyboardKey key, bool isDown) + { + if (device != null) + { + // Ensure the device is in the dictionary + _pressedKeys.TryAdd(device, new HashSet()); + // Get the hash set of the device + HashSet pressedDeviceKeys = _pressedKeys[device]; + // See if the key is already pressed + bool alreadyPressed = pressedDeviceKeys.Contains(key); + + // Prevent triggering already down keys again. When a key is held, we don't want to spam events like Windows does + if (isDown && alreadyPressed) + return true; + + // Either add or remove the key depending on its status + if (isDown && !alreadyPressed) + pressedDeviceKeys.Add(key); + else if (!isDown && alreadyPressed) + pressedDeviceKeys.Remove(key); + } + + return false; + } + + private KeyboardModifierKey UpdateModifierKeys(ArtemisDevice? device, KeyboardKey key, in bool isDown) + { + KeyboardModifierKey modifiers = _globalModifiers; + if (device != null) + _keyboardModifier.TryGetValue(device, out modifiers); + + if (key == KeyboardKey.LeftAlt || key == KeyboardKey.RightAlt) + { + if (isDown) + modifiers |= KeyboardModifierKey.Alt; + else + modifiers &= ~KeyboardModifierKey.Alt; + } + else if (key == KeyboardKey.LeftCtrl || key == KeyboardKey.RightCtrl) + { + if (isDown) + modifiers |= KeyboardModifierKey.Control; + else + modifiers &= ~KeyboardModifierKey.Control; + } + else if (key == KeyboardKey.LeftShift || key == KeyboardKey.RightShift) + { + if (isDown) + modifiers |= KeyboardModifierKey.Shift; + else + modifiers &= ~KeyboardModifierKey.Shift; + } + else if (key == KeyboardKey.LWin || key == KeyboardKey.RWin) + { + if (isDown) + modifiers |= KeyboardModifierKey.Windows; + else + modifiers &= ~KeyboardModifierKey.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); + 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 + ArtemisMouseButtonUpDownEventArgs eventArgs = new ArtemisMouseButtonUpDownEventArgs(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 ArtemisMouseScrollEventArgs(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 ArtemisMouseMoveEventArgs(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); + } + + #endregion + + #region Events + + public event EventHandler? KeyboardKeyUpDown; + 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(ArtemisKeyboardKeyUpDownEventArgs e) + { + KeyboardKeyUpDown?.Invoke(this, e); + } + + protected virtual void OnKeyboardKeyDown(ArtemisKeyboardKeyEventArgs e) + { + KeyboardKeyDown?.Invoke(this, e); + } + + protected virtual void OnKeyboardKeyUp(ArtemisKeyboardKeyEventArgs e) + { + KeyboardKeyUp?.Invoke(this, e); + } + + protected virtual void OnMouseButtonUpDown(ArtemisMouseButtonUpDownEventArgs e) + { + MouseButtonUpDown?.Invoke(this, e); + } + + protected virtual void OnMouseButtonDown(ArtemisMouseButtonEventArgs e) + { + MouseButtonDown?.Invoke(this, e); + } + + protected virtual void OnMouseButtonUp(ArtemisMouseButtonEventArgs e) + { + MouseButtonUp?.Invoke(this, e); + } + + protected virtual void OnMouseScroll(ArtemisMouseScrollEventArgs e) + { + MouseScroll?.Invoke(this, e); + } + + protected virtual void OnMouseMove(ArtemisMouseMoveEventArgs e) + { + MouseMove?.Invoke(this, e); + } + + protected virtual void OnDeviceIdentified() + { + DeviceIdentified?.Invoke(this, EventArgs.Empty); + } + + #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..df20fc14f --- /dev/null +++ b/src/Artemis.Core/Services/Input/Interfaces/IInputService.cs @@ -0,0 +1,100 @@ +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 - Must be a keyboard or mouse + 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 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 + /// + 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, InputDeviceType type); + + /// + /// Clears the identifier cache + /// + void BustIdentifierCache(); + + #endregion + } +} \ 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..dc2c481d3 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() + { + InputIdentifiers = 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 InputIdentifiers { 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.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/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..a0e8b4d39 --- /dev/null +++ b/src/Artemis.UI/InputProviders/NativeWindowInputProvider.cs @@ -0,0 +1,221 @@ +using System; +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 MouseButton = Artemis.Core.Services.MouseButton; + +namespace Artemis.UI.InputProviders +{ + public class NativeWindowInputProvider : InputProvider + { + private const int WM_INPUT = 0x00FF; + + private readonly IInputService _inputService; + private DateTime _lastMouseUpdate; + private SpongeWindow _sponge; + + public NativeWindowInputProvider(IInputService inputService) + { + _inputService = inputService; + + _sponge = new SpongeWindow(); + _sponge.WndProcCalled += SpongeOnWndProcCalled; + + RawInputDevice.RegisterDevice(HidUsageAndPage.Keyboard, RawInputDeviceFlags.InputSink, _sponge.Handle); + RawInputDevice.RegisterDevice(HidUsageAndPage.Mouse, RawInputDeviceFlags.InputSink, _sponge.Handle); + } + + #region IDisposable + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + _sponge?.DestroyHandle(); + _sponge = null; + } + + base.Dispose(disposing); + } + + #endregion + + 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(data, mouse); + break; + case RawInputKeyboardData keyboard: + HandleKeyboardData(data, keyboard); + break; + } + } + + #region Keyboard + + private void HandleKeyboardData(RawInputData data, RawInputKeyboardData keyboardData) + { + 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 == KeyboardKey.None) + return; + + // Right alt triggers LeftCtrl with a different scan code for some reason, ignore those + 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, InputDeviceType.Keyboard); + + ArtemisDevice device = null; + if (identifier != null) + 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)) + { + if (key == KeyboardKey.Enter) + key = KeyboardKey.NumPadEnter; + if (key == KeyboardKey.LeftCtrl) + key = KeyboardKey.RightCtrl; + if (key == KeyboardKey.LeftAlt) + key = KeyboardKey.RightAlt; + } + + if (key == KeyboardKey.LeftShift && keyboardData.Keyboard.ScanCode == 54) + key = KeyboardKey.RightShift; + + bool isDown = keyboardData.Keyboard.Flags != RawKeyboardFlags.Up && + keyboardData.Keyboard.Flags != (RawKeyboardFlags.Up | RawKeyboardFlags.LeftKey) && + keyboardData.Keyboard.Flags != (RawKeyboardFlags.Up | RawKeyboardFlags.RightKey); + + OnKeyboardDataReceived(device, key, isDown); + } + + #endregion + + #region Mouse + + private int _mouseDeltaX; + private int _mouseDeltaY; + + private void HandleMouseData(RawInputData data, RawInputMouseData mouseData) + { + // 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; + } + + ArtemisDevice device = null; + string identifier = data.Device?.DevicePath; + if (identifier != null) + 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}"); + + // Movement + if (mouseData.Mouse.Buttons == RawMouseButtonFlags.None) + { + Win32Point cursorPosition = GetCursorPosition(); + OnMouseMoveDataReceived(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, InputDeviceType.Mouse); + + // Scrolling + if (mouseData.Mouse.ButtonData != 0) + { + if (mouseData.Mouse.Buttons == RawMouseButtonFlags.MouseWheel) + OnMouseScrollDataReceived(device, MouseScrollDirection.Vertical, mouseData.Mouse.ButtonData); + else if (mouseData.Mouse.Buttons == RawMouseButtonFlags.MouseHorizontalWheel) + OnMouseScrollDataReceived(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(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/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/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/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 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/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 new file mode 100644 index 000000000..54349c2f4 --- /dev/null +++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputView.xaml @@ -0,0 +1,61 @@ + + + + + + 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..cf75ee65b --- /dev/null +++ b/src/Artemis.UI/Screens/SurfaceEditor/Dialogs/SurfaceDeviceDetectInputViewModel.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using Artemis.Core; +using Artemis.Core.Services; +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 ListLedGroup _ledGroup; + private readonly ISnackbarMessageQueue _mainMessageQueue; + + public SurfaceDeviceDetectInputViewModel(ArtemisDevice device, IInputService inputService, ISnackbarMessageQueue mainMessageQueue) + { + Device = device; + Title = $"{Device.RgbDevice.DeviceInfo.DeviceName} - Detect input"; + IsMouse = Device.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse; + + _inputService = inputService; + _mainMessageQueue = mainMessageQueue; + _inputService.IdentifyDevice(Device); + _inputService.DeviceIdentified += InputServiceOnDeviceIdentified; + + // Create a LED group way at the top + _ledGroup = new ListLedGroup(Device.Leds.Select(l => l.RgbLed)) + { + Brush = new SolidColorBrush(new Color(255, 255, 0)), + ZIndex = 999 + }; + } + + public ArtemisDevice Device { get; } + public string Title { get; } + public bool IsMouse { get; } + + public override void OnDialogClosed(object sender, DialogClosingEventArgs e) + { + base.OnDialogClosed(sender, e); + _inputService.DeviceIdentified -= InputServiceOnDeviceIdentified; + _ledGroup.Detach(); + } + + private void InputServiceOnDeviceIdentified(object sender, EventArgs e) + { + Session?.Close(true); + _mainMessageQueue.Enqueue($"{Device.RgbDevice.DeviceInfo.DeviceName} 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..233224940 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.xaml +++ b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.xaml @@ -144,6 +144,16 @@ + + + + + + diff --git a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorViewModel.cs index 8b69f6817..2d0d4d2f1 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 @@ -279,10 +286,19 @@ namespace Artemis.UI.Screens.SurfaceEditor public async Task ViewProperties(SurfaceDeviceViewModel surfaceDeviceViewModel) { - object madeChanges = await _dialogService.ShowDialog(new Dictionary - { - {"surfaceDeviceViewModel", surfaceDeviceViewModel} - }); + object madeChanges = await _dialogService.ShowDialog( + new Dictionary {{"device", surfaceDeviceViewModel.Device}} + ); + + if ((bool) madeChanges) + _surfaceService.UpdateSurfaceConfiguration(SelectedSurface, true); + } + + public async Task DetectInput(SurfaceDeviceViewModel surfaceDeviceViewModel) + { + object madeChanges = await _dialogService.ShowDialog( + new Dictionary {{"device", surfaceDeviceViewModel.Device}} + ); if ((bool) madeChanges) _surfaceService.UpdateSurfaceConfiguration(SelectedSurface, true); diff --git a/src/Artemis.UI/Screens/SurfaceEditor/Visualization/SurfaceDeviceViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/Visualization/SurfaceDeviceViewModel.cs index 20789c29b..8cb010792 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/Visualization/SurfaceDeviceViewModel.cs +++ b/src/Artemis.UI/Screens/SurfaceEditor/Visualization/SurfaceDeviceViewModel.cs @@ -2,7 +2,9 @@ using System.Windows; using System.Windows.Input; using Artemis.Core; +using RGB.NET.Core; using Stylet; +using Point = System.Windows.Point; namespace Artemis.UI.Screens.SurfaceEditor.Visualization { @@ -45,6 +47,9 @@ namespace Artemis.UI.Screens.SurfaceEditor.Visualization ? new Rect() : new Rect(Device.X, Device.Y, Device.RgbDevice.DeviceRectangle.Size.Width, Device.RgbDevice.DeviceRectangle.Size.Height); + public bool CanDetectInput => Device.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || + Device.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse; + public void StartMouseDrag(Point mouseStartPosition) { _dragOffsetX = Device.X - mouseStartPosition.X; diff --git a/src/Artemis.UI/Services/RegistrationService.cs b/src/Artemis.UI/Services/RegistrationService.cs index cf4fc5ffc..b1f75b0ad 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(_inputService)); + } + + 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, )",