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 @@
+
+