diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/Artemis.Plugins.Devices.Wooting.csproj b/src/Devices/Artemis.Plugins.Devices.Wooting/Artemis.Plugins.Devices.Wooting.csproj
index 82d237fa..a5415103 100644
--- a/src/Devices/Artemis.Plugins.Devices.Wooting/Artemis.Plugins.Devices.Wooting.csproj
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/Artemis.Plugins.Devices.Wooting.csproj
@@ -7,18 +7,7 @@
-
-
-
-
-
-
-
-
-
-
-
@@ -33,4 +22,23 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+ ..\..\..\..\RGB.NET\bin\net9.0\RGB.NET.Devices.Wooting.dll
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Regular_Keys/1U_Blank.png b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Regular_Keys/1U_Blank.png
new file mode 100644
index 00000000..f34ccc23
Binary files /dev/null and b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Regular_Keys/1U_Blank.png differ
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Regular_Keys/1_25_Blank.png b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Regular_Keys/1_25_Blank.png
new file mode 100644
index 00000000..dbce9b82
Binary files /dev/null and b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Regular_Keys/1_25_Blank.png differ
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 60HE v2-ANSI.xml b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 60HE v2-ANSI.xml
new file mode 100644
index 00000000..e4d4b1b6
--- /dev/null
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 60HE v2-ANSI.xml
@@ -0,0 +1,499 @@
+
+
+ Wooting 60HE v2 - ANSI
+ Physical ANSI layout of the Wooting 60HE v2
+ BigBrainAFK
+ Keyboard
+ Wooting
+ Wooting 60HE
+ 302
+ 116
+
+
+ 11
+ 5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+
+
+ 11
+ +
+ 1.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.5
+
+
+
+
+
+
+
+ 11
+ +
+ 1.75
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2.25
+
+
+
+
+
+
+
+ 11
+ +
+ 2.25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2.75
+
+
+
+
+
+
+
+ 11
+ +
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ M0.319,-0.003 L0.32,0.991 L0.68,0.997 L0.68,0z
+ +-38
+ 6.25
+
+
+
+
+
+
+
+ +-38
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+
+ Wooting 60HE.png
+
+
\ No newline at end of file
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 60HE+-ANSI.xml b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 60HE+-ANSI.xml
new file mode 100644
index 00000000..a0e43086
--- /dev/null
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 60HE+-ANSI.xml
@@ -0,0 +1,499 @@
+
+
+ Wooting 60HE - ANSI
+ Physical ANSI layout of the Wooting 60HE
+ BigBrainAFK
+ Keyboard
+ Wooting
+ Wooting 60HE
+ 302
+ 116
+
+
+ 11
+ 5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+
+
+ 11
+ +
+ 1.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.5
+
+
+
+
+
+
+
+ 11
+ +
+ 1.75
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2.25
+
+
+
+
+
+
+
+ 11
+ +
+ 2.25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2.75
+
+
+
+
+
+
+
+ 11
+ +
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ M0.319,-0.003 L0.32,0.991 L0.68,0.997 L0.68,0z
+ +-38
+ 6.25
+
+
+
+
+
+
+
+ +-38
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+
+ Wooting 60HE.png
+
+
\ No newline at end of file
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 60HE+-ISO.xml b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 60HE+-ISO.xml
new file mode 100644
index 00000000..4f6d29d6
--- /dev/null
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 60HE+-ISO.xml
@@ -0,0 +1,507 @@
+
+
+ Wooting 60HE - ISO
+ Physical ISO layout of the Wooting 60HE
+ BigBrainAFK
+ Keyboard
+ Wooting
+ Wooting 60HE
+ 302
+ 116
+
+
+ 11
+ 5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+
+
+ 11
+ +
+ 1.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ M0,0 L0,0.5 L0.16666666666,0.5 L0.16666666666,1 L1,1 L1,0 Z
+ 1.5
+ 2
+
+
+
+
+
+
+
+ 11
+ +-19
+ 1.75
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 11
+ +
+ 1.25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2.75
+
+
+
+
+
+
+
+ 11
+ +
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ M0.319,-0.003 L0.32,0.991 L0.68,0.997 L0.68,0z
+ +-38
+ 6.25
+
+
+
+
+
+
+
+ +-38
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+
+ Wooting 60HE.png
+
+
\ No newline at end of file
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE-ANSI.xml b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE-ANSI.xml
new file mode 100644
index 00000000..199afc79
--- /dev/null
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE-ANSI.xml
@@ -0,0 +1,647 @@
+
+
+ Wooting 80HE-ANSI
+ Wooting 80HE-ANSI
+ Untamedx2
+ Keyboard
+
+ Wooting 80HE
+ 347
+ 147
+
+
+ 10
+ 16
+
+
+
+
+
+
+
+ +4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+ 41
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+ +
+ 1.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.5
+ 1
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+ +
+ 1.75
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2.25
+
+
+
+
+
+
+
+ 10
+ +
+ 2.25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2.75
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+ 10
+ +
+ 1.5
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.2
+
+
+ 1.2
+
+
+ 1.2
+
+
+ 1.2
+
+
+ 1.2
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ +9.75
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Wooting 80HE.png
+
+
\ No newline at end of file
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE-ISO.xml b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE-ISO.xml
new file mode 100644
index 00000000..53041807
--- /dev/null
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE-ISO.xml
@@ -0,0 +1,655 @@
+
+
+ Wooting 80HE-ISO
+ Wooting 80HE-ISO
+ Untamedx2
+ Keyboard
+
+ Wooting 80HE
+ 347
+ 147
+
+
+ 10
+ 16
+
+
+
+
+
+
+
+ +4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+ 41
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+ +
+ 1.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ M0.000,-0.000 L1.000,0.00 L1.000,1.000 L0.167,1.000 L0.167,0.475 L0.000,0.475z
+ 1.5
+ 2
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+ +
+ 1.75
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 10
+ +
+ 1.25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2.75
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+ 10
+ +
+ 1.5
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.2
+
+
+ 1.2
+
+
+ 1.2
+
+
+ 1.2
+
+
+ 1.2
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ +9.75
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Wooting 80HE.png
+
+
\ No newline at end of file
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE-JIS.xml b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE-JIS.xml
new file mode 100644
index 00000000..ce5ba79d
--- /dev/null
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE-JIS.xml
@@ -0,0 +1,671 @@
+
+
+ Wooting 80HE-JIS
+ Wooting 80HE-JIS
+ Untamedx2
+ Keyboard
+
+ Wooting 80HE
+ 347
+ 147
+
+
+ 10
+ 16
+
+
+
+
+
+
+
+ +4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+ 41
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+ +
+ 1.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ M0.000,-0.000 L1.000,0.00 L1.000,1.000 L0.167,1.000 L0.167,0.475 L0.000,0.475z
+ 1.5
+ 2
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+ +
+ 1.75
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 10
+ +
+ 2.25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.75
+
+
+
+
+
+
+
+ +5
+
+
+
+
+
+
+
+ 10
+ +
+ 1.25
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.5
+
+
+ 1.5
+
+
+ 1.5
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1.25
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ +9.75
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Wooting 80HE.png
+
+
\ No newline at end of file
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE.png b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE.png
new file mode 100644
index 00000000..e0eedc1d
Binary files /dev/null and b/src/Devices/Artemis.Plugins.Devices.Wooting/Layouts/Wooting/Keyboard/Wooting 80HE.png differ
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/Services/AnalogService/WootingAnalogLedMapping.cs b/src/Devices/Artemis.Plugins.Devices.Wooting/Services/AnalogService/WootingAnalogLedMapping.cs
index 5ff7e90e..67216441 100644
--- a/src/Devices/Artemis.Plugins.Devices.Wooting/Services/AnalogService/WootingAnalogLedMapping.cs
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/Services/AnalogService/WootingAnalogLedMapping.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
+using Artemis.Core.Services;
using RGB.NET.Core;
namespace Artemis.Plugins.Devices.Wooting.Services.AnalogService;
@@ -136,7 +137,7 @@ internal static class WootingAnalogLedMapping
FnKey = 0x409
DisableKey = 0x40A
FnLayerLock = 0x40B
- FnKey2 = 0x40C=
+ FnKey2 = 0x40C=
*/
[LedId.Keyboard_Custom1] = 1027,
[LedId.Keyboard_Custom2] = 1028,
@@ -146,4 +147,129 @@ internal static class WootingAnalogLedMapping
};
internal static Dictionary HidCodesReversed { get; } = HidCodes.ToDictionary(x => (short)x.Value, x => x.Key);
-}
+
+ internal static Dictionary InputKeyCodes { get; } = new()
+ {
+ [KeyboardKey.A] = 0x04,
+ [KeyboardKey.B] = 0x05, //US_B
+ [KeyboardKey.C] = 0x06, //US_C
+ [KeyboardKey.D] = 0x07, //US_D
+ [KeyboardKey.E] = 0x08, //US_E
+ [KeyboardKey.F] = 0x09, //US_F
+ [KeyboardKey.G] = 0x0a, //US_G
+ [KeyboardKey.H] = 0x0b, //US_H
+ [KeyboardKey.I] = 0x0c, //US_I
+ [KeyboardKey.J] = 0x0d, //US_J
+ [KeyboardKey.K] = 0x0e, //US_K
+ [KeyboardKey.L] = 0x0f, //US_L
+ [KeyboardKey.M] = 0x10, //US_M
+ [KeyboardKey.N] = 0x11, //US_N
+ [KeyboardKey.O] = 0x12, //US_O
+ [KeyboardKey.P] = 0x13, //US_P
+ [KeyboardKey.Q] = 0x14, //US_Q
+ [KeyboardKey.R] = 0x15, //US_R
+ [KeyboardKey.S] = 0x16, //US_S
+ [KeyboardKey.T] = 0x17, //US_T
+ [KeyboardKey.U] = 0x18, //US_U
+ [KeyboardKey.V] = 0x19, //US_V
+ [KeyboardKey.W] = 0x1a, //US_W
+ [KeyboardKey.X] = 0x1b, //US_X
+ [KeyboardKey.Y] = 0x1c, //US_Y
+ [KeyboardKey.Z] = 0x1d, //US_Z
+ [KeyboardKey.D1] = 0x1e, //DIGIT1
+ [KeyboardKey.D2] = 0x1f, //DIGIT2
+ [KeyboardKey.D3] = 0x20, //DIGIT3
+ [KeyboardKey.D4] = 0x21, //DIGIT4
+ [KeyboardKey.D5] = 0x22, //DIGIT5
+ [KeyboardKey.D6] = 0x23, //DIGIT6
+ [KeyboardKey.D7] = 0x24, //DIGIT7
+ [KeyboardKey.D8] = 0x25, //DIGIT8
+ [KeyboardKey.D9] = 0x26, //DIGIT9
+ [KeyboardKey.D0] = 0x27, //DIGIT0
+ [KeyboardKey.Enter] = 0x28, //ENTER
+ [KeyboardKey.Escape] = 0x29, //ESCAPE
+ [KeyboardKey.Backspace] = 0x2a, //BACKSPACE
+ [KeyboardKey.Tab] = 0x2b, //TAB
+ [KeyboardKey.Space] = 0x2c, //SPACE
+ [KeyboardKey.OemMinus] = 0x2d, //MINUS
+ [KeyboardKey.OemPlus] = 0x2e, //EQUAL
+ [KeyboardKey.OemOpenBrackets] = 0x2f, //BRACKET_LEFT
+ [KeyboardKey.OemCloseBrackets] = 0x30, //BRACKET_RIGHT
+ [KeyboardKey.OemBackslash] = 0x31, //BACKSLASH
+ [KeyboardKey.OemSemicolon] = 0x33, //SEMICOLON
+ [KeyboardKey.OemQuotes] = 0x34, //QUOTE
+ [KeyboardKey.OemTilde] = 0x35, //BACKQUOTE
+ [KeyboardKey.OemComma] = 0x36, //COMMA
+ [KeyboardKey.OemPeriod] = 0x37, //PERIOD
+ [KeyboardKey.OemQuestion] = 0x38, //SLASH
+ [KeyboardKey.CapsLock] = 0x39, //CAPS_LOCK
+ [KeyboardKey.F1] = 0x3a, //F1
+ [KeyboardKey.F2] = 0x3b, //F2
+ [KeyboardKey.F3] = 0x3c, //F3
+ [KeyboardKey.F4] = 0x3d, //F4
+ [KeyboardKey.F5] = 0x3e, //F5
+ [KeyboardKey.F6] = 0x3f, //F6
+ [KeyboardKey.F7] = 0x40, //F7
+ [KeyboardKey.F8] = 0x41, //F8
+ [KeyboardKey.F9] = 0x42, //F9
+ [KeyboardKey.F10] = 0x43, //F10
+ [KeyboardKey.F11] = 0x44, //F11
+ [KeyboardKey.F12] = 0x45, //F12
+ [KeyboardKey.PrintScreen] = 0x46, //PRINT_SCREEN
+ [KeyboardKey.ScrollLock] = 0x47, //SCROLL_LOCK
+ [KeyboardKey.PauseBreak] = 0x48, //PAUSE
+ [KeyboardKey.Insert] = 0x49, //INSERT
+ [KeyboardKey.Home] = 0x4a, //HOME
+ [KeyboardKey.PageUp] = 0x4b, //PAGE_UP
+ [KeyboardKey.Delete] = 0x4c, //DEL
+ [KeyboardKey.End] = 0x4d, //END
+ [KeyboardKey.PageDown] = 0x4e, //PAGE_DOWN
+ [KeyboardKey.ArrowRight] = 0x4f, //ARROW_RIGHT
+ [KeyboardKey.ArrowLeft] = 0x50, //ARROW_LEFT
+ [KeyboardKey.ArrowDown] = 0x51, //ARROW_DOWN
+ [KeyboardKey.ArrowUp] = 0x52, //ARROW_UP
+ [KeyboardKey.NumLock] = 0x53, //NUM_LOCK
+ [KeyboardKey.NumPadDivide] = 0x54, //NUMPAD_DIVIDE
+ [KeyboardKey.NumPadMultiply] = 0x55, //NUMPAD_MULTIPLY
+ [KeyboardKey.NumPadSubtract] = 0x56, //NUMPAD_SUBTRACT
+ [KeyboardKey.NumPadAdd] = 0x57, //NUMPAD_ADD
+ [KeyboardKey.NumPadEnter] = 0x58, //NUMPAD_ENTER
+ [KeyboardKey.NumPad1] = 0x59, //NUMPAD1
+ [KeyboardKey.NumPad2] = 0x5a, //NUMPAD2
+ [KeyboardKey.NumPad3] = 0x5b, //NUMPAD3
+ [KeyboardKey.NumPad4] = 0x5c, //NUMPAD4
+ [KeyboardKey.NumPad5] = 0x5d, //NUMPAD5
+ [KeyboardKey.NumPad6] = 0x5e, //NUMPAD6
+ [KeyboardKey.NumPad7] = 0x5f, //NUMPAD7
+ [KeyboardKey.NumPad8] = 0x60, //NUMPAD8
+ [KeyboardKey.NumPad9] = 0x61, //NUMPAD9
+ [KeyboardKey.NumPad0] = 0x62, //NUMPAD0
+ [KeyboardKey.NumPadDecimal] = 0x63, //NUMPAD_DECIMAL
+ [KeyboardKey.OemPipe] = 0x64, //INTL_BACKSLASH
+ [KeyboardKey.Application] = 0x65, //CONTEXT_MENU
+ [KeyboardKey.F13] = 0x68, //F13
+ [KeyboardKey.F14] = 0x69, //F14
+ [KeyboardKey.F15] = 0x6a, //F15
+ [KeyboardKey.F16] = 0x6b, //F16
+ [KeyboardKey.F17] = 0x6c, //F17
+ [KeyboardKey.F18] = 0x6d, //F18
+ [KeyboardKey.F19] = 0x6e, //F19
+ [KeyboardKey.F20] = 0x6f, //F20
+ [KeyboardKey.F21] = 0x70, //F21
+ [KeyboardKey.F22] = 0x71, //F22
+ [KeyboardKey.F23] = 0x72, //F23
+ [KeyboardKey.F24] = 0x73, //F24
+ [KeyboardKey.LeftCtrl] = 0xe0, //CONTROL_LEFT
+ [KeyboardKey.LeftShift] = 0xe1, //SHIFT_LEFT
+ [KeyboardKey.LeftAlt] = 0xe2, //ALT_LEFT
+ [KeyboardKey.LeftWin] = 0xe3, //META_LEFT
+ [KeyboardKey.RightCtrl] = 0xe4, //CONTROL_RIGHT
+ [KeyboardKey.RightShift] = 0xe5, //SHIFT_RIGHT
+ [KeyboardKey.RightAlt] = 0xe6, //ALT_RIGHT
+ [KeyboardKey.RightWin] = 0xe7, //META_RIGHT
+
+ //Mode = 1032,
+ };
+
+ internal static Dictionary InputKeyCodesReversed { get; } = InputKeyCodes.ToDictionary(x => x.Value, x => x.Key);
+}
\ No newline at end of file
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/WootingAnalogInputProvider.cs b/src/Devices/Artemis.Plugins.Devices.Wooting/WootingAnalogInputProvider.cs
new file mode 100644
index 00000000..c6fceba0
--- /dev/null
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/WootingAnalogInputProvider.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Artemis.Core;
+using Artemis.Core.Services;
+using Artemis.Plugins.Devices.Wooting.Services.AnalogService;
+using Grpc.Core;
+using Grpc.Net.Client;
+using Serilog;
+using WootingAnalogSdk;
+
+namespace Artemis.Plugins.Devices.Wooting;
+
+public class WootingAnalogInputProvider : InputProvider
+{
+ private readonly IDeviceService _deviceService;
+ private readonly GrpcChannel _channel;
+ private readonly AnalogSdkService.AnalogSdkServiceClient _client;
+ private readonly CancellationTokenSource _cts;
+ private readonly List _tasks;
+
+ public WootingAnalogInputProvider(IDeviceService deviceService)
+ {
+ _deviceService = deviceService;
+ _channel = GrpcChannel.ForAddress("http://127.0.0.1:50051");
+ _client = new AnalogSdkService.AnalogSdkServiceClient(_channel);
+ _cts = new CancellationTokenSource();
+ _tasks = new List();
+
+ foreach (AnalogGetDevicesResponse.Types.AnalogDevice deviceInfo in _client.GetConnectedDevices(new AnalogGetDevicesRequest()).Devices)
+ {
+ _tasks.Add(Task.Run(() => RunDevice(deviceInfo)));
+ }
+ }
+
+ private async Task RunDevice(AnalogGetDevicesResponse.Types.AnalogDevice deviceInfo)
+ {
+ AsyncServerStreamingCall stream = _client.AnalogStream(new AnalogStreamRequest { Deviceid = deviceInfo.Id });
+
+ Dictionary cache = new();
+
+ ArtemisDevice? device = null;
+
+ await foreach (AnalogStreamResponse response in stream.ResponseStream.ReadAllAsync(_cts.Token))
+ {
+ if (response.Data.Count == 0)
+ continue;
+
+ device ??= _deviceService.Devices.FirstOrDefault(x => x.Identifier.Contains(deviceInfo.SerialNumber, StringComparison.InvariantCultureIgnoreCase));
+
+ if (device == null)
+ continue;
+
+ foreach (AnalogEntry entry in response.Data)
+ {
+ if (!WootingAnalogLedMapping.InputKeyCodesReversed.TryGetValue((short)entry.Key, out KeyboardKey keyboardKey))
+ continue;
+
+ bool actuated = entry.Value > 0.5f;
+
+ if (!cache.TryGetValue(keyboardKey, out bool previouslyActuated))
+ {
+ previouslyActuated = false;
+ cache[keyboardKey] = actuated;
+ }
+
+ if (previouslyActuated == actuated)
+ continue;
+
+ cache[keyboardKey] = actuated;
+ OnKeyboardDataReceived(device, keyboardKey, actuated);
+ }
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _cts.Cancel();
+ _channel.ShutdownAsync().GetAwaiter().GetResult();
+
+ foreach (Task task in _tasks)
+ {
+ try
+ {
+ task.GetAwaiter().GetResult();
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Error while disposing WootingAnalogInputProvider task");
+ }
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+}
\ No newline at end of file
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/WootingDeviceProvider.cs b/src/Devices/Artemis.Plugins.Devices.Wooting/WootingDeviceProvider.cs
index 8e34d0be..6a2a0fb1 100644
--- a/src/Devices/Artemis.Plugins.Devices.Wooting/WootingDeviceProvider.cs
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/WootingDeviceProvider.cs
@@ -1,9 +1,10 @@
-using System.IO;
+using System;
+using Artemis.Core;
using Artemis.Core.DeviceProviders;
using Artemis.Core.Services;
using RGB.NET.Core;
using Serilog;
-using RGBDeviceProvider = RGB.NET.Devices.Wooting.WootingDeviceProvider;
+using RGBDeviceProvider = RGB.NET.Devices.Wooting.Grpc.WootingGrpcDeviceProvider;
namespace Artemis.Plugins.Devices.Wooting
{
@@ -13,20 +14,30 @@ public class WootingDeviceProvider : DeviceProvider
private readonly ILogger _logger;
private readonly IDeviceService _deviceService;
- public WootingDeviceProvider(ILogger logger, IDeviceService deviceService)
+ public WootingDeviceProvider(ILogger logger, IDeviceService deviceService, IInputService inputService)
{
_logger = logger;
_deviceService = deviceService;
CanDetectPhysicalLayout = true;
+ CanDetectLogicalLayout = true;
+ try
+ {
+ WootingAnalogInputProvider inputProvider = new(deviceService);
+ inputService.AddInputProvider(inputProvider);
+ }
+ catch (Exception e)
+ {
+ _logger.Error(e, "Failed to initialize Wooting analog input provider: {message}", e.Message);
+ }
}
-
+
public override RGBDeviceProvider RgbDeviceProvider => RGBDeviceProvider.Instance;
public override void Enable()
{
- RGBDeviceProvider.PossibleX64NativePathsWindows.Add(Path.Combine(Plugin.Directory.FullName, "x64", "wooting-rgb-sdk64.dll"));
- RGBDeviceProvider.PossibleX86NativePathsWindows.Add(Path.Combine(Plugin.Directory.FullName, "x86", "wooting-rgb-sdk.dll"));
- RGBDeviceProvider.PossibleNativePathsLinux.Add(Path.Combine(Plugin.Directory.FullName, "x64", "libwooting-rgb-sdk.so"));
+ // RGBDeviceProvider.PossibleX64NativePathsWindows.Add(Path.Combine(Plugin.Directory.FullName, "x64", "wooting-rgb-sdk64.dll"));
+ // RGBDeviceProvider.PossibleX86NativePathsWindows.Add(Path.Combine(Plugin.Directory.FullName, "x86", "wooting-rgb-sdk.dll"));
+ // RGBDeviceProvider.PossibleNativePathsLinux.Add(Path.Combine(Plugin.Directory.FullName, "x64", "libwooting-rgb-sdk.so"));
RgbDeviceProvider.Exception += Provider_OnException;
_deviceService.AddDeviceProvider(this);
@@ -39,6 +50,8 @@ public override void Disable()
RgbDeviceProvider.Dispose();
}
+ public override string GetLogicalLayout(IKeyboard keyboard) => "US";
+
private void Provider_OnException(object sender, ExceptionEventArgs args) => _logger.Debug(args.Exception, "Wooting Exception: {message}", args.Exception.Message);
}
}
\ No newline at end of file
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/plugin.json b/src/Devices/Artemis.Plugins.Devices.Wooting/plugin.json
index 15a29916..bca4c9ae 100644
--- a/src/Devices/Artemis.Plugins.Devices.Wooting/plugin.json
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/plugin.json
@@ -8,7 +8,7 @@
"HelpPage": "https://wiki.artemis-rgb.com/guides/user/devices/wooting",
"Icon": "Keyboard",
"Description": "Allows Artemis to control lighting on Wooting keyboards. Will eventually also expose analog key data.",
- "Version": "1.0.0.0",
+ "Version": "2.2.0.0",
"Main": "Artemis.Plugins.Devices.Wooting.dll",
"AutoEnableFeatures": false,
"Platforms":"Windows,Linux"
diff --git a/src/Devices/Artemis.Plugins.Devices.Wooting/wooting_analog_sdk.proto b/src/Devices/Artemis.Plugins.Devices.Wooting/wooting_analog_sdk.proto
new file mode 100644
index 00000000..4e29ee51
--- /dev/null
+++ b/src/Devices/Artemis.Plugins.Devices.Wooting/wooting_analog_sdk.proto
@@ -0,0 +1,36 @@
+syntax = "proto3";
+
+package wooting_analog_sdk;
+
+message AnalogGetDevicesRequest {}
+
+message AnalogGetDevicesResponse {
+ message AnalogDevice {
+ uint64 id = 1;
+ uint32 rows = 2;
+ uint32 columns = 3;
+ string model_name = 4;
+ string serial_number = 5;
+ }
+
+ repeated AnalogDevice devices = 1;
+}
+
+message AnalogStreamRequest {
+ uint64 deviceid = 1;
+}
+
+message AnalogEntry {
+ uint32 key = 1;
+ float value = 2;
+}
+
+message AnalogStreamResponse {
+ uint32 timestamp = 1;
+ repeated AnalogEntry data = 2;
+}
+
+service AnalogSdkService {
+ rpc GetConnectedDevices(AnalogGetDevicesRequest) returns (AnalogGetDevicesResponse);
+ rpc AnalogStream(AnalogStreamRequest) returns (stream AnalogStreamResponse);
+}
\ No newline at end of file