summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-12-30 18:22:41 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-12-30 18:22:41 +0100
commitba9f12810f7dc4969ac175f6e959d5fe6407747d (patch)
treef4039e46985d56bac471bd8568f96b159a92b2ae
parentfeat(src): Make usable (diff)
downloadqmk_layout-ba9f12810f7dc4969ac175f6e959d5fe6407747d.zip
feat(treewide): Migrate the Unicode handling to a custom c program, that works via rawhid
-rw-r--r--flake.nix12
-rw-r--r--package.nix2
-rw-r--r--rust/format/output6
-rw-r--r--rust/format/output26
-rw-r--r--rust/qmk-hid-com/.gitignore3
-rw-r--r--rust/qmk-hid-com/Cargo.lock357
-rw-r--r--rust/qmk-hid-com/Cargo.toml26
-rw-r--r--rust/qmk-hid-com/contrib/lib/udev/rules.d/099-allow-hid.rules1
-rw-r--r--rust/qmk-hid-com/package.nix25
-rw-r--r--rust/qmk-hid-com/src/cli.rs40
-rw-r--r--rust/qmk-hid-com/src/hid/mod.rs44
-rw-r--r--rust/qmk-hid-com/src/main.rs107
-rw-r--r--rust/qmk-hid-com/src/search.rs51
-rwxr-xr-xrust/qmk-hid-com/src_c/build.sh18
-rw-r--r--rust/qmk-hid-com/src_c/meson.build51
-rw-r--r--rust/qmk-hid-com/src_c/protocol/meson.build37
-rw-r--r--rust/qmk-hid-com/src_c/protocol/virtual-keyboard-unstable-v1.xml113
-rw-r--r--rust/qmk-hid-com/src_c/src/error.c20
-rw-r--r--rust/qmk-hid-com/src_c/src/error.h1
-rw-r--r--rust/qmk-hid-com/src_c/src/hid/hid.c83
-rw-r--r--rust/qmk-hid-com/src_c/src/hid/hid.h5
-rw-r--r--rust/qmk-hid-com/src_c/src/keyboard/keyboard.c135
-rw-r--r--rust/qmk-hid-com/src_c/src/keyboard/keyboard.h17
-rw-r--r--rust/qmk-hid-com/src_c/src/keyboard/virtual-keyboard-unstable-v1-client-protocol.h280
-rw-r--r--rust/qmk-hid-com/src_c/src/main.c28
-rw-r--r--src/keymaps/soispha/config.h3
-rw-r--r--src/keymaps/soispha/hid/hid.c47
-rw-r--r--src/keymaps/soispha/hid/hid.h9
-rw-r--r--src/keymaps/soispha/keymap.c9
-rw-r--r--src/keymaps/soispha/layout/keymap.h2
-rw-r--r--src/keymaps/soispha/layout/layout.h1
-rw-r--r--src/keymaps/soispha/macros.h2
-rw-r--r--src/keymaps/soispha/rules.mk8
33 files changed, 1531 insertions, 18 deletions
diff --git a/flake.nix b/flake.nix
index 33ff880..4e472d3 100644
--- a/flake.nix
+++ b/flake.nix
@@ -54,6 +54,18 @@
       formatter = treefmtEval.config.build.wrapper;
 
       devShells.default = pkgs.mkShell {
+        nativeBuildInputs = [
+          pkgs.pkg-config
+
+          pkgs.wayland-scanner
+          pkgs.meson
+          pkgs.ninja
+        ];
+        buildInputs = [
+          pkgs.libudev-zero
+          pkgs.kdePackages.wayland.dev
+          pkgs.hidapi
+        ];
         packages = with pkgs; [
           format_layer
 
diff --git a/package.nix b/package.nix
index b5607a5..7db364e 100644
--- a/package.nix
+++ b/package.nix
@@ -30,7 +30,7 @@
   '';
 
   # NOTE: Change this _EVERY_ time you actually use a new keymap. <2024-12-29>
-  version = "25";
+  version = "26";
 in
   stdenv.mkDerivation
   {
diff --git a/rust/format/output b/rust/format/output
deleted file mode 100644
index 95f3cdd..0000000
--- a/rust/format/output
+++ /dev/null
@@ -1,6 +0,0 @@
-KK_TP,         KK_TP,              KK_TP,             KK_TP,      KK_TP,       KK_TP,      KK_TP,           KK_TP, RV_SPAWN_NHEKO,  RV_SPAWN_NEORG_FIREFOX, RV_SPAWN_KEEPASSXC, RV_SPAWN_SIGNAL, KK_TP, KK_TP,
-KK_TP,         RV_SCREEN_SHOT,     RV_PAUSE,          RV_TOGGLE_MUSIC,           RV_RUN,               KK_TP,      KK_TP,           KK_TP, RV_TOGGLE_FLOAT, RV_TOGGLE_FULLSCREEN,   RV_ZOOM_VIEW,       KK_TP,  RV_EXIT,        KK_TP,
-KK_TP,         KK_TP,              KK_TP,             RV_MOVE_VIEW_PREVIOUS_TAG, RV_MOVE_PREVIOUS_TAG, RV_MOVE_OUTPUT_NEXT, KK_TP,           KK_TP, RV_VIEW_CLOSE,   RV_VIEW_NEXT,           RV_SWAP_NEXT,       RV_SWAP_PREV,    RV_VIEW_PREV,   KK_TP,
-KK_TP,         KK_TP,              KK_TP,             KK_TP,            KK_TP,       KK_TP,                                                KK_TP,  RV_SEND_OUTPUT,         KK_TP,     KK_TP,  KK_TP, KK_TP,
-KK_TP,         KK_TP,              KK_TP,             KK_TP,            KK_TP,       KK_TP,                                                KK_TP,  KK_TP,         KK_TP,     KK_TP,  KK_TP, KK_TP,
-KK_TP,         KK_TP,              KK_TP,             KK_TP,            KK_TP,       KK_TP
diff --git a/rust/format/output2 b/rust/format/output2
deleted file mode 100644
index edc3a82..0000000
--- a/rust/format/output2
+++ /dev/null
@@ -1,6 +0,0 @@
-KC_EQL,  KC_1,    KC_2,    KC_3,    KC_4,    KC_5,    KC_LEFT,           KC_RGHT, KC_6,    KC_7,    KC_8,    KC_9,    KC_0,    KC_MINS,
-KC_DEL,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,    TG(SYMB),         TG(SYMB), KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,    KC_BSLS,
-KC_BSPC, KC_A,    KC_S,    KC_D,    KC_F,    KC_G,    KC_HYPR,           KC_MEH,  KC_H,    KC_J,    KC_K,    KC_L,    LT(MDIA,KC_SCLN), LGUI_T(KC_QUOT),
-KC_LSFT, LCTL_T(KC_Z), KC_X, KC_C,    KC_V,    KC_B,                                KC_N,    KC_M,    KC_COMM, KC_DOT,  RCTL_T(KC_SLSH), KC_RSFT,
-LT(SYMB,KC_GRV), WEBUSB_PAIR, A(KC_LSFT), KC_LEFT, KC_RGHT,  LALT_T(KC_APP),    RCTL_T(KC_ESC),   KC_UP,   KC_DOWN, KC_LBRC, KC_RBRC, MO(SYMB),
-KC_SPC,  KC_BSPC, KC_LGUI,           KC_LALT,  KC_TAB,  KC_ENT
diff --git a/rust/qmk-hid-com/.gitignore b/rust/qmk-hid-com/.gitignore
new file mode 100644
index 0000000..ed14267
--- /dev/null
+++ b/rust/qmk-hid-com/.gitignore
@@ -0,0 +1,3 @@
+# build
+/target
+/result
diff --git a/rust/qmk-hid-com/Cargo.lock b/rust/qmk-hid-com/Cargo.lock
new file mode 100644
index 0000000..e763b63
--- /dev/null
+++ b/rust/qmk-hid-com/Cargo.lock
@@ -0,0 +1,357 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "anstream"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
+
+[[package]]
+name = "cc"
+version = "1.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.5.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hidapi"
+version = "2.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03b876ecf37e86b359573c16c8366bc3eba52b689884a0fc42ba3f67203d2a8b"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "libc",
+ "pkg-config",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "libc"
+version = "0.2.169"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "qmk-hid-com"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "hidapi",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/rust/qmk-hid-com/Cargo.toml b/rust/qmk-hid-com/Cargo.toml
new file mode 100644
index 0000000..20cc11f
--- /dev/null
+++ b/rust/qmk-hid-com/Cargo.toml
@@ -0,0 +1,26 @@
+# Qmk Hid Com - A small middelware between a qmk keyboard and a wayland virtual
+# keyboard. For unicode input
+#
+# Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+#
+# This file is part of Qmk Hid Com.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/agpl.txt>.
+
+[package]
+name = "qmk-hid-com"
+description = "A small middelware between a qmk keyboard and a wayland virtual keyboard. For unicode input"
+version = "0.1.0"
+edition = "2021"
+license = "AGPL-3.0-or-later"
+homepage = ""
+repository = "https://git.foss-syndicate.org/bpeetz/tools/qmk-hid-com"
+
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+[dependencies]
+anyhow = "1.0.95"
+clap = { version = "4.5.23", features = ["derive"] }
+hidapi = "2.6.3"
diff --git a/rust/qmk-hid-com/contrib/lib/udev/rules.d/099-allow-hid.rules b/rust/qmk-hid-com/contrib/lib/udev/rules.d/099-allow-hid.rules
new file mode 100644
index 0000000..c4fc74b
--- /dev/null
+++ b/rust/qmk-hid-com/contrib/lib/udev/rules.d/099-allow-hid.rules
@@ -0,0 +1 @@
+KERNEL=="hidraw4", SUBSYSTEM=="hidraw", ATTRS{idProduct}=="1969", ATTRS{idVendor}=="3297", TAG+="uaccess"
diff --git a/rust/qmk-hid-com/package.nix b/rust/qmk-hid-com/package.nix
new file mode 100644
index 0000000..157e92e
--- /dev/null
+++ b/rust/qmk-hid-com/package.nix
@@ -0,0 +1,25 @@
+{
+  rustPlatform,
+  lib,
+}:
+rustPlatform.buildRustPackage {
+  pname = "qmk-hid-com";
+  version = "1.0.0";
+
+  src = lib.cleanSourceWith {
+    src = lib.cleanSource ./.;
+    filter = name: type:
+      (type == "directory")
+      || (builtins.elem (builtins.baseNameOf name) [
+        "Cargo.toml"
+        "Cargo.lock"
+      ])
+      || (lib.strings.hasSuffix ".rs" (builtins.baseNameOf name));
+  };
+
+  doCheck = true;
+
+  cargoLock = {
+    lockFile = ./Cargo.lock;
+  };
+}
diff --git a/rust/qmk-hid-com/src/cli.rs b/rust/qmk-hid-com/src/cli.rs
new file mode 100644
index 0000000..afef1a9
--- /dev/null
+++ b/rust/qmk-hid-com/src/cli.rs
@@ -0,0 +1,40 @@
+use clap::{command, Parser, Subcommand};
+
+#[derive(Parser, Debug)]
+pub struct CliArgs {
+    #[clap(subcommand)]
+    pub command: Command,
+}
+#[derive(Clone, Debug, Subcommand)]
+pub enum Command {
+    /// List all devices, where the manufacturer string contains the search key
+    Search {
+        /// The part that must be contained in the manufacturer name
+        vendor_name: String,
+    },
+
+    /// Talk to the device specified by the [`vendor_id`]  and [`product_id`].
+    Send {
+        usage_page: u16,
+        usage_id: u16,
+        message: u8,
+    },
+
+    /// Talk to the device specified by the [`vendor_id`]  and [`product_id`].
+    Monitor { usage_page: u16, usage_id: u16 },
+
+    Inform {
+        #[command(subcommand)]
+        command: Inform,
+    },
+
+    ArrInform { values: Vec<u8> },
+}
+
+#[derive(Subcommand, Clone, Debug)]
+pub enum Inform {
+    Hex { val: String },
+    Dec { val: i64 },
+    Bin { val: String },
+    Char { val: char },
+}
diff --git a/rust/qmk-hid-com/src/hid/mod.rs b/rust/qmk-hid-com/src/hid/mod.rs
new file mode 100644
index 0000000..b80c835
--- /dev/null
+++ b/rust/qmk-hid-com/src/hid/mod.rs
@@ -0,0 +1,44 @@
+use hidapi::{HidApi, HidDevice};
+
+fn find_device(api: &HidApi, usage_id: u16, usage_page: u16) -> anyhow::Result<HidDevice> {
+    Ok({
+        let devs = api
+            .device_list()
+            .filter(|dev| dev.usage() == usage_id && dev.usage_page() == usage_page)
+            .collect::<Vec<_>>();
+
+        assert_eq!(
+            devs.len(),
+            1,
+            "Only one device should have such a usage:usage_page value"
+        );
+
+        api.open(devs[0].vendor_id(), devs[0].product_id())?
+    })
+}
+
+pub fn monitor(api: &HidApi, usage_id: u16, usage_page: u16) -> anyhow::Result<()> {
+    let device = find_device(api, usage_id, usage_page)?;
+
+    loop {
+        // Read data from device
+        let mut buf = [0u8; 4];
+        let res = device.read(&mut buf[..]).unwrap();
+
+        let value = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
+
+        println!("Read: {:?} ({} bytes) -> {value:x}", &buf[..res], res);
+    }
+}
+pub fn send(api: &HidApi, usage_id: u16, usage_page: u16, message: u8) -> anyhow::Result<()> {
+    let device = find_device(api, usage_id, usage_page)?;
+
+    // Write data to device
+    let mut buf = [0u8; 33];
+    buf[1] = message;
+
+    let res = device.write(&buf)?;
+    println!("Wrote: {:#?} byte(s)", res);
+
+    Ok(())
+}
diff --git a/rust/qmk-hid-com/src/main.rs b/rust/qmk-hid-com/src/main.rs
new file mode 100644
index 0000000..ebaa91b
--- /dev/null
+++ b/rust/qmk-hid-com/src/main.rs
@@ -0,0 +1,107 @@
+// Qmk Hid Com - A small middelware between a qmk keyboard and a wayland virtual
+// keyboard. For unicode input
+//
+// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This file is part of Qmk Hid Com.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/agpl.txt>.
+
+use clap::Parser;
+use cli::{CliArgs, Command, Inform};
+
+mod cli;
+mod hid;
+mod search;
+
+fn main() -> anyhow::Result<()> {
+    let args = CliArgs::parse();
+    let api = hidapi::HidApi::new().unwrap();
+
+    match args.command {
+        Command::Search { vendor_name } => search::search(&api, vendor_name),
+        Command::Monitor {
+            usage_id,
+            usage_page,
+        } => hid::monitor(&api, usage_id, usage_page),
+        Command::Send {
+            usage_id,
+            usage_page,
+            message,
+        } => hid::send(&api, usage_id, usage_page, message),
+
+        Command::Inform { command } => {
+            let (decimal, _) = match command {
+                Inform::Hex { val } => (i64::from_str_radix(&val.to_lowercase(), 16)?, val),
+                Inform::Dec { val } => (val, val.to_string()),
+                Inform::Bin { val } => (i64::from_str_radix(&val.to_lowercase(), 2)?, val),
+                Inform::Char { val } => ((val as u32) as i64, val.to_string()),
+            };
+
+            let character = char::from_u32(decimal as u32).unwrap();
+
+            let binary = {
+                decimal
+                    .to_ne_bytes()
+                    .map(|sm| format!("{:<08b}", sm))
+                    .join(" ")
+            };
+
+            let split = {
+                let first;
+                let second;
+                let third;
+                let fourth;
+
+                if cfg!(target_endian = "big") {
+                    let decimal: u32 = decimal as u32;
+                    let decimal = decimal.to_be();
+
+                    dbg!(decimal
+                        .to_be_bytes()
+                        .map(|sm| format!("{:<08b}", sm))
+                        .join(" "));
+
+                    fourth = decimal as u8;
+                    third = (decimal >> 8) as u8;
+                    second = (decimal >> 16) as u8;
+                    first = (decimal >> 24) as u8;
+                } else {
+                    first = decimal as u8;
+                    second = (decimal >> 8) as u8;
+                    third = (decimal >> 16) as u8;
+                    fourth = (decimal >> 24) as u8;
+                }
+                [first, second, third, fourth]
+            };
+
+            println!(
+                "dec: {}, hex: {:x}, char: {:#?}, bin: {}, split: {:?}/[{}] ({})",
+                decimal,
+                decimal,
+                character,
+                binary,
+                split,
+                split
+                    .iter()
+                    .map(|val| format!("{:x}", val))
+                    .collect::<Vec<_>>()
+                    .join(", "),
+                i32::from_ne_bytes(split)
+            );
+            Ok(())
+        }
+        Command::ArrInform { values } => {
+            assert_eq!(values.len(), 4);
+            let arr = [values[0], values[1], values[2], values[3]];
+
+            let output = u32::from_le_bytes(arr);
+
+            println!("{:?} -> {output}", arr);
+
+            Ok(())
+        }
+    }
+}
diff --git a/rust/qmk-hid-com/src/search.rs b/rust/qmk-hid-com/src/search.rs
new file mode 100644
index 0000000..3c954ba
--- /dev/null
+++ b/rust/qmk-hid-com/src/search.rs
@@ -0,0 +1,51 @@
+use hidapi::HidApi;
+
+pub fn search(api: &HidApi, vendor_name: String) -> anyhow::Result<()> {
+    for device in api.device_list() {
+        if let Some(string) = device.manufacturer_string() {
+            if string.to_lowercase().contains(&vendor_name.to_lowercase()) {
+                println!(
+                    "Device matches: {} (VID) - {} (PID) {}:{} - at {} by {} via {:#?}",
+                    device.vendor_id(),
+                    device.product_id(),
+                    device.usage_page(),
+                    device.usage(),
+                    device.path().to_str()?,
+                    string,
+                    device.bus_type(),
+                );
+
+                println!(
+                    "  {} (Interface {}):",
+                    match device.product_string() {
+                        Some(s) => s,
+                        _ => "<COULD NOT FETCH>",
+                    },
+                    device.interface_number()
+                );
+                let mut descriptor = vec![0u8; 2048];
+                match device
+                    .open_device(api)
+                    .and_then(|dev| dev.get_report_descriptor(&mut descriptor))
+                {
+                    Ok(length) => {
+                        println!("    {} (descriptor)", {
+                            &descriptor[..length]
+                                .iter()
+                                .map(|val| val.to_string())
+                                .collect::<String>()
+                        })
+                    }
+                    Err(err) => println!("    Failed to retrieve descriptor ({:?})", err),
+                }
+                println!();
+            }
+        } else {
+            eprintln!(
+                "Device without manufacturer string ({})",
+                device.product_id()
+            );
+        }
+    }
+    Ok(())
+}
diff --git a/rust/qmk-hid-com/src_c/build.sh b/rust/qmk-hid-com/src_c/build.sh
new file mode 100755
index 0000000..de615f8
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/build.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env sh
+
+# Source: https://mesonbuild.com/Builtin-options.html
+# Use gcc's wrappers to support lto
+CC="gcc" AR="gcc-ar" RANLIB="gcc-ranlib" meson setup build_release \
+    -Dbuildtype=release \
+    -Ddebug=false \
+    -Doptimization=3 \
+    -Dstrip=true \
+    -Dwarning_level=everything \
+    -Dwerror=true \
+    -Db_lundef=true \
+    -Db_lto=true \
+    -Db_ndebug=false \
+    -Db_pgo=generate \
+    -Db_staticpic=true \
+    -Db_pie=false \
+    -Dc_std=gnu2x
diff --git a/rust/qmk-hid-com/src_c/meson.build b/rust/qmk-hid-com/src_c/meson.build
new file mode 100644
index 0000000..9e8357f
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/meson.build
@@ -0,0 +1,51 @@
+# This is heavily based on `wtype`
+
+project(
+    'qmk-unicode-type', 'c',
+    version: '0.1',
+    license: 'GPL-3.0-or-later',
+    default_options : [
+        'buildtype=release',
+        'default_library=static'
+    ],
+)
+
+git = find_program('git', native: true, required: false)
+
+if not git.found()
+    add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'c')
+else
+    git_commit_hash = run_command([git.full_path(), 'describe', '--always', '--tags'], check: true).stdout().strip()
+    git_branch = run_command([git.full_path(), 'rev-parse', '--abbrev-ref', 'HEAD'], check: true).stdout().strip()
+    version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch)
+    add_project_arguments('-DVERSION=@0@'.format(version), language: 'c')
+endif
+cc = meson.get_compiler('c')
+
+
+wayland_client = dependency('wayland-client')
+libhid = dependency('hidapi-hidraw')
+rt = cc.find_library('rt')
+
+
+
+src_files = files(
+  './src/error.c',
+  './src/hid/hid.c',
+  './src/keyboard/keyboard.c',
+  './src/main.c',
+)
+
+subdir('protocol')
+
+executable(
+    meson.project_name(),
+    src_files,
+    dependencies: [
+        client_protos,
+        wayland_client,
+        libhid,
+        rt,
+    ],
+)
+
diff --git a/rust/qmk-hid-com/src_c/protocol/meson.build b/rust/qmk-hid-com/src_c/protocol/meson.build
new file mode 100644
index 0000000..1bfe9e0
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/protocol/meson.build
@@ -0,0 +1,37 @@
+wayland_scanner = find_program('wayland-scanner')
+
+wayland_scanner_code = generator(
+	wayland_scanner,
+	output: '@BASENAME@-protocol.c',
+	arguments: ['private-code', '@INPUT@', '@OUTPUT@'],
+)
+
+wayland_scanner_client = generator(
+	wayland_scanner,
+	output: '@BASENAME@-client-protocol.h',
+	arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
+)
+
+
+client_protocols = [
+	['virtual-keyboard-unstable-v1.xml'],
+]
+client_protos_src = []
+client_protos_headers = []
+
+
+foreach p : client_protocols
+	xml = join_paths(p)
+	client_protos_src += wayland_scanner_code.process(xml)
+	client_protos_headers += wayland_scanner_client.process(xml)
+endforeach
+lib_client_protos = static_library(
+	'client_protos',
+	client_protos_src + client_protos_headers,
+	dependencies: [wayland_client]
+)
+
+client_protos = declare_dependency(
+	link_with: lib_client_protos,
+	sources: client_protos_headers,
+)
diff --git a/rust/qmk-hid-com/src_c/protocol/virtual-keyboard-unstable-v1.xml b/rust/qmk-hid-com/src_c/protocol/virtual-keyboard-unstable-v1.xml
new file mode 100644
index 0000000..5095c91
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/protocol/virtual-keyboard-unstable-v1.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="virtual_keyboard_unstable_v1">
+  <copyright>
+    Copyright © 2008-2011  Kristian Høgsberg
+    Copyright © 2010-2013  Intel Corporation
+    Copyright © 2012-2013  Collabora, Ltd.
+    Copyright © 2018       Purism SPC
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <interface name="zwp_virtual_keyboard_v1" version="1">
+    <description summary="virtual keyboard">
+      The virtual keyboard provides an application with requests which emulate
+      the behaviour of a physical keyboard.
+
+      This interface can be used by clients on its own to provide raw input
+      events, or it can accompany the input method protocol.
+    </description>
+
+    <request name="keymap">
+      <description summary="keyboard mapping">
+        Provide a file descriptor to the compositor which can be
+        memory-mapped to provide a keyboard mapping description.
+
+        Format carries a value from the keymap_format enumeration.
+      </description>
+      <arg name="format" type="uint" summary="keymap format"/>
+      <arg name="fd" type="fd" summary="keymap file descriptor"/>
+      <arg name="size" type="uint" summary="keymap size, in bytes"/>
+    </request>
+
+    <enum name="error">
+      <entry name="no_keymap" value="0" summary="No keymap was set"/>
+    </enum>
+
+    <request name="key">
+      <description summary="key event">
+        A key was pressed or released.
+        The time argument is a timestamp with millisecond granularity, with an
+        undefined base. All requests regarding a single object must share the
+        same clock.
+
+        Keymap must be set before issuing this request.
+
+        State carries a value from the key_state enumeration.
+      </description>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="key" type="uint" summary="key that produced the event"/>
+      <arg name="state" type="uint" summary="physical state of the key"/>
+    </request>
+
+    <request name="modifiers">
+      <description summary="modifier and group state">
+        Notifies the compositor that the modifier and/or group state has
+        changed, and it should update state.
+
+        The client should use wl_keyboard.modifiers event to synchronize its
+        internal state with seat state.
+
+        Keymap must be set before issuing this request.
+      </description>
+      <arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
+      <arg name="mods_latched" type="uint" summary="latched modifiers"/>
+      <arg name="mods_locked" type="uint" summary="locked modifiers"/>
+      <arg name="group" type="uint" summary="keyboard layout"/>
+    </request>
+
+    <request name="destroy" type="destructor" since="1">
+      <description summary="destroy the virtual keyboard keyboard object"/>
+    </request>
+  </interface>
+
+  <interface name="zwp_virtual_keyboard_manager_v1" version="1">
+    <description summary="virtual keyboard manager">
+      A virtual keyboard manager allows an application to provide keyboard
+      input events as if they came from a physical keyboard.
+    </description>
+
+    <enum name="error">
+      <entry name="unauthorized" value="0" summary="client not authorized to use the interface"/>
+    </enum>
+
+    <request name="create_virtual_keyboard">
+      <description summary="Create a new virtual keyboard">
+        Creates a new virtual keyboard associated to a seat.
+
+        If the compositor enables a keyboard to perform arbitrary actions, it
+        should present an error when an untrusted client requests a new
+        keyboard.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat"/>
+      <arg name="id" type="new_id" interface="zwp_virtual_keyboard_v1"/>
+    </request>
+  </interface>
+</protocol>
diff --git a/rust/qmk-hid-com/src_c/src/error.c b/rust/qmk-hid-com/src_c/src/error.c
new file mode 100644
index 0000000..f970cfc
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/error.c
@@ -0,0 +1,20 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wayland-client.h>
+
+#include "error.h"
+
+__attribute__((noreturn)) __attribute__((format(printf, 1, 2))) void
+fail(const char *format, ...) {
+  va_list vas;
+
+  va_start(vas, format);
+  fprintf(stderr, "Error: ");
+  vfprintf(stderr, format, vas);
+  va_end(vas);
+
+  fprintf(stderr, "\n");
+
+  exit(EXIT_FAILURE);
+}
diff --git a/rust/qmk-hid-com/src_c/src/error.h b/rust/qmk-hid-com/src_c/src/error.h
new file mode 100644
index 0000000..e0d6aaf
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/error.h
@@ -0,0 +1 @@
+void fail(const char *format, ...);
diff --git a/rust/qmk-hid-com/src_c/src/hid/hid.c b/rust/qmk-hid-com/src_c/src/hid/hid.c
new file mode 100644
index 0000000..d468ba8
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/hid/hid.c
@@ -0,0 +1,83 @@
+#include <assert.h>
+#include <hidapi/hidapi.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "../error.h"
+#include "hid.h"
+
+static hid_device *find_device_by_usage(uint32_t expected_usage,
+                                        uint32_t expected_usage_page) {
+  struct hid_device_info *devs;
+  devs = hid_enumerate(0x0, 0x0);
+
+  hid_device *found_device = NULL;
+  bool found = false;
+
+  struct hid_device_info *cur_dev = devs;
+  for (; cur_dev; cur_dev = cur_dev->next) {
+    // printf("Checking device: %ls, %d (%d:%d) at %s ..\n",
+    //        cur_dev->product_string, cur_dev->product_id, cur_dev->usage,
+    //        cur_dev->usage_page, cur_dev->path);
+
+    if (cur_dev->usage == expected_usage &&
+        cur_dev->usage_page == expected_usage_page) {
+      found_device = hid_open_path(cur_dev->path);
+      found = true;
+
+      goto output;
+    }
+  }
+
+output:
+  hid_free_enumeration(devs);
+  if (!found_device) {
+    if (found) {
+      fail("Failed to open device usage (%d) and usage page (%d): %ls",
+           expected_usage, expected_usage_page, hid_error(found_device));
+    } else {
+      fail("Can't find device with usage (%d) and usage page (%d): %ls",
+           expected_usage, expected_usage_page, hid_error(found_device));
+    }
+    hid_exit();
+
+    return NULL;
+  } else {
+    return found_device;
+  }
+}
+
+uint32_t read_next(uint32_t usage, uint32_t usage_page) {
+  hid_device *handle;
+
+  if (hid_init()) {
+    fail("Failed to init hid");
+  }
+
+  handle = find_device_by_usage(usage, usage_page);
+
+  uint8_t buf[4];
+  int res = 0;
+  res = hid_read(handle, buf, sizeof(buf));
+  if (res < 0) {
+    fail("Failed to read 4 bytes from hid device: %ls", hid_error(handle));
+  } else if (res == 0) {
+    fail("Device did not send anything!");
+  }
+
+  hid_close(handle);
+  if (hid_exit()) {
+    fail("Failed to exit hid");
+  }
+
+  // Buf should only contain positive values.
+  assert(buf[0] >= 0 && buf[1] >= 0 && buf[2] >= 0 && buf[3] >= 0);
+
+  // Only lower Endian supported, TODO add the other one
+  uint32_t key_code =
+      (uint32_t)(buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24));
+
+  return key_code;
+}
diff --git a/rust/qmk-hid-com/src_c/src/hid/hid.h b/rust/qmk-hid-com/src_c/src/hid/hid.h
new file mode 100644
index 0000000..9ffb9d3
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/hid/hid.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <stdint.h>
+
+uint32_t read_next(uint32_t usage, uint32_t usage_page);
diff --git a/rust/qmk-hid-com/src_c/src/keyboard/keyboard.c b/rust/qmk-hid-com/src_c/src/keyboard/keyboard.c
new file mode 100644
index 0000000..4949085
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/keyboard/keyboard.c
@@ -0,0 +1,135 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "virtual-keyboard-unstable-v1-client-protocol.h"
+
+#include "../error.h"
+#include "keyboard.h"
+
+
+static void handle_wl_event(void *data, struct wl_registry *registry,
+                            uint32_t name, const char *interface,
+                            uint32_t version);
+static void handle_wl_event_remove(void *data, struct wl_registry *registry,
+                                   uint32_t name);
+
+const struct wl_registry_listener registry_listener = {
+    .global = handle_wl_event,
+    .global_remove = handle_wl_event_remove,
+};
+
+struct qmk_unicode_type init_qmk_unicode() {
+  struct qmk_unicode_type qmk_unicode_type;
+
+  memset(&qmk_unicode_type, 0, sizeof(qmk_unicode_type));
+  qmk_unicode_type.display = wl_display_connect(NULL);
+
+  if (qmk_unicode_type.display == NULL) {
+    fail("Wayland connection failed");
+  }
+
+  qmk_unicode_type.registry = wl_display_get_registry(qmk_unicode_type.display);
+
+  wl_registry_add_listener(qmk_unicode_type.registry, &registry_listener,
+                           &qmk_unicode_type);
+  wl_display_dispatch(qmk_unicode_type.display);
+  wl_display_roundtrip(qmk_unicode_type.display);
+
+  if (qmk_unicode_type.manager == NULL) {
+    fail("Compositor does not support the virtual keyboard protocol");
+  }
+  if (qmk_unicode_type.seat == NULL) {
+    fail("No seat found");
+  }
+
+  qmk_unicode_type.keyboard =
+      zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
+          qmk_unicode_type.manager, qmk_unicode_type.seat);
+
+  return qmk_unicode_type;
+}
+void destroy_qmk_unicode(struct qmk_unicode_type qmk_unicode_type) {
+  zwp_virtual_keyboard_v1_destroy(qmk_unicode_type.keyboard);
+  zwp_virtual_keyboard_manager_v1_destroy(qmk_unicode_type.manager);
+  wl_registry_destroy(qmk_unicode_type.registry);
+  wl_display_disconnect(qmk_unicode_type.display);
+}
+
+static void upload_keymap(struct qmk_unicode_type *qmk_unicode_type,
+                          uint32_t key_code);
+
+void type_keycode(struct qmk_unicode_type *qmk_unicode_type,
+                  uint32_t key_code) {
+  upload_keymap(qmk_unicode_type, key_code);
+
+  zwp_virtual_keyboard_v1_key(qmk_unicode_type->keyboard, 0, 1,
+                              WL_KEYBOARD_KEY_STATE_PRESSED);
+  wl_display_roundtrip(qmk_unicode_type->display);
+  usleep(2000);
+  zwp_virtual_keyboard_v1_key(qmk_unicode_type->keyboard, 0, 1,
+                              WL_KEYBOARD_KEY_STATE_RELEASED);
+  wl_display_roundtrip(qmk_unicode_type->display);
+  usleep(2000);
+}
+
+static void handle_wl_event(void *data, struct wl_registry *registry,
+                            uint32_t name, const char *interface,
+                            uint32_t version) {
+  struct qmk_unicode_type *qmk_unicode_type = (struct qmk_unicode_type *)data;
+
+  if (!strcmp(interface, wl_seat_interface.name)) {
+    qmk_unicode_type->seat = (struct wl_seat *)wl_registry_bind(
+        registry, name, &wl_seat_interface, version <= 7 ? version : 7);
+  } else if (!strcmp(interface,
+                     zwp_virtual_keyboard_manager_v1_interface.name)) {
+    qmk_unicode_type->manager =
+        (struct zwp_virtual_keyboard_manager_v1 *)wl_registry_bind(
+            registry, name, &zwp_virtual_keyboard_manager_v1_interface, 1);
+  }
+}
+
+static void
+handle_wl_event_remove(__attribute__((unused)) void *data,
+                       __attribute__((unused)) struct wl_registry *_registry,
+                       __attribute__((unused)) uint32_t _name) {}
+
+static void upload_keymap(struct qmk_unicode_type *qmk_unicode_type,
+                          uint32_t key_code) {
+  char filename[] = "/tmp/qmk_unicode_type-XXXXXX";
+  int fd = mkstemp(filename);
+  if (fd < 0) {
+    fail("Failed to create the temporary keymap file");
+  }
+  unlink(filename);
+  FILE *f = fdopen(fd, "w");
+
+  fprintf(f,
+          "xkb_keymap {\n"
+          "  xkb_keycodes \"(unnamed)\" {\n"
+          "      minimum = 8;\n"
+          "      maximum = 10;\n"
+          "      <K1> = 9;\n"
+          "  };\n"
+          // TODO: Is including "complete" here really a good idea?
+          "  xkb_types \"(unnamed)\" { include \"complete\" };\n"
+          "  xkb_compatibility \"(unnamed)\" { include \"complete\" };\n"
+          "  xkb_symbols \"(unnamed)\" {\n"
+          "      key <K1> {[U%X]};\n"
+          "  };\n"
+          "};\n",
+          key_code);
+
+  fputc('\0', f);
+  fflush(f);
+
+  uint32_t keymap_size = (uint32_t)ftell(f);
+  zwp_virtual_keyboard_v1_keymap(qmk_unicode_type->keyboard,
+                                 WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, fileno(f),
+                                 keymap_size);
+
+  wl_display_roundtrip(qmk_unicode_type->display);
+  fclose(f);
+}
diff --git a/rust/qmk-hid-com/src_c/src/keyboard/keyboard.h b/rust/qmk-hid-com/src_c/src/keyboard/keyboard.h
new file mode 100644
index 0000000..ac5d7bd
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/keyboard/keyboard.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <stdint.h>
+
+struct qmk_unicode_type init_qmk_unicode();
+void destroy_qmk_unicode(struct qmk_unicode_type qmk_unicode_type);
+
+void type_keycode(struct qmk_unicode_type *qmk_unicode_type, uint32_t key_code);
+
+// Private
+struct qmk_unicode_type {
+  struct wl_display *display;
+  struct wl_registry *registry;
+  struct wl_seat *seat;
+  struct zwp_virtual_keyboard_manager_v1 *manager;
+  struct zwp_virtual_keyboard_v1 *keyboard;
+};
diff --git a/rust/qmk-hid-com/src_c/src/keyboard/virtual-keyboard-unstable-v1-client-protocol.h b/rust/qmk-hid-com/src_c/src/keyboard/virtual-keyboard-unstable-v1-client-protocol.h
new file mode 100644
index 0000000..81cac0b
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/keyboard/virtual-keyboard-unstable-v1-client-protocol.h
@@ -0,0 +1,280 @@
+/* Generated by wayland-scanner 1.23.1 */
+
+#ifndef VIRTUAL_KEYBOARD_UNSTABLE_V1_CLIENT_PROTOCOL_H
+#define VIRTUAL_KEYBOARD_UNSTABLE_V1_CLIENT_PROTOCOL_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include "wayland-client.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page page_virtual_keyboard_unstable_v1 The virtual_keyboard_unstable_v1 protocol
+ * @section page_ifaces_virtual_keyboard_unstable_v1 Interfaces
+ * - @subpage page_iface_zwp_virtual_keyboard_v1 - virtual keyboard
+ * - @subpage page_iface_zwp_virtual_keyboard_manager_v1 - virtual keyboard manager
+ * @section page_copyright_virtual_keyboard_unstable_v1 Copyright
+ * <pre>
+ *
+ * Copyright © 2008-2011  Kristian Høgsberg
+ * Copyright © 2010-2013  Intel Corporation
+ * Copyright © 2012-2013  Collabora, Ltd.
+ * Copyright © 2018       Purism SPC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * </pre>
+ */
+struct wl_seat;
+struct zwp_virtual_keyboard_manager_v1;
+struct zwp_virtual_keyboard_v1;
+
+#ifndef ZWP_VIRTUAL_KEYBOARD_V1_INTERFACE
+#define ZWP_VIRTUAL_KEYBOARD_V1_INTERFACE
+/**
+ * @page page_iface_zwp_virtual_keyboard_v1 zwp_virtual_keyboard_v1
+ * @section page_iface_zwp_virtual_keyboard_v1_desc Description
+ *
+ * The virtual keyboard provides an application with requests which emulate
+ * the behaviour of a physical keyboard.
+ *
+ * This interface can be used by clients on its own to provide raw input
+ * events, or it can accompany the input method protocol.
+ * @section page_iface_zwp_virtual_keyboard_v1_api API
+ * See @ref iface_zwp_virtual_keyboard_v1.
+ */
+/**
+ * @defgroup iface_zwp_virtual_keyboard_v1 The zwp_virtual_keyboard_v1 interface
+ *
+ * The virtual keyboard provides an application with requests which emulate
+ * the behaviour of a physical keyboard.
+ *
+ * This interface can be used by clients on its own to provide raw input
+ * events, or it can accompany the input method protocol.
+ */
+extern const struct wl_interface zwp_virtual_keyboard_v1_interface;
+#endif
+#ifndef ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_INTERFACE
+#define ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_INTERFACE
+/**
+ * @page page_iface_zwp_virtual_keyboard_manager_v1 zwp_virtual_keyboard_manager_v1
+ * @section page_iface_zwp_virtual_keyboard_manager_v1_desc Description
+ *
+ * A virtual keyboard manager allows an application to provide keyboard
+ * input events as if they came from a physical keyboard.
+ * @section page_iface_zwp_virtual_keyboard_manager_v1_api API
+ * See @ref iface_zwp_virtual_keyboard_manager_v1.
+ */
+/**
+ * @defgroup iface_zwp_virtual_keyboard_manager_v1 The zwp_virtual_keyboard_manager_v1 interface
+ *
+ * A virtual keyboard manager allows an application to provide keyboard
+ * input events as if they came from a physical keyboard.
+ */
+extern const struct wl_interface zwp_virtual_keyboard_manager_v1_interface;
+#endif
+
+#ifndef ZWP_VIRTUAL_KEYBOARD_V1_ERROR_ENUM
+#define ZWP_VIRTUAL_KEYBOARD_V1_ERROR_ENUM
+enum zwp_virtual_keyboard_v1_error {
+	/**
+	 * No keymap was set
+	 */
+	ZWP_VIRTUAL_KEYBOARD_V1_ERROR_NO_KEYMAP = 0,
+};
+#endif /* ZWP_VIRTUAL_KEYBOARD_V1_ERROR_ENUM */
+
+#define ZWP_VIRTUAL_KEYBOARD_V1_KEYMAP 0
+#define ZWP_VIRTUAL_KEYBOARD_V1_KEY 1
+#define ZWP_VIRTUAL_KEYBOARD_V1_MODIFIERS 2
+#define ZWP_VIRTUAL_KEYBOARD_V1_DESTROY 3
+
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ */
+#define ZWP_VIRTUAL_KEYBOARD_V1_KEYMAP_SINCE_VERSION 1
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ */
+#define ZWP_VIRTUAL_KEYBOARD_V1_KEY_SINCE_VERSION 1
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ */
+#define ZWP_VIRTUAL_KEYBOARD_V1_MODIFIERS_SINCE_VERSION 1
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ */
+#define ZWP_VIRTUAL_KEYBOARD_V1_DESTROY_SINCE_VERSION 1
+
+/** @ingroup iface_zwp_virtual_keyboard_v1 */
+static inline void
+zwp_virtual_keyboard_v1_set_user_data(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, void *user_data)
+{
+	wl_proxy_set_user_data((struct wl_proxy *) zwp_virtual_keyboard_v1, user_data);
+}
+
+/** @ingroup iface_zwp_virtual_keyboard_v1 */
+static inline void *
+zwp_virtual_keyboard_v1_get_user_data(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1)
+{
+	return wl_proxy_get_user_data((struct wl_proxy *) zwp_virtual_keyboard_v1);
+}
+
+static inline uint32_t
+zwp_virtual_keyboard_v1_get_version(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1)
+{
+	return wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_v1);
+}
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ *
+ * Provide a file descriptor to the compositor which can be
+ * memory-mapped to provide a keyboard mapping description.
+ *
+ * Format carries a value from the keymap_format enumeration.
+ */
+static inline void
+zwp_virtual_keyboard_v1_keymap(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t format, int32_t fd, uint32_t size)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) zwp_virtual_keyboard_v1,
+			 ZWP_VIRTUAL_KEYBOARD_V1_KEYMAP, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_v1), 0, format, fd, size);
+}
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ *
+ * A key was pressed or released.
+ * The time argument is a timestamp with millisecond granularity, with an
+ * undefined base. All requests regarding a single object must share the
+ * same clock.
+ *
+ * Keymap must be set before issuing this request.
+ *
+ * State carries a value from the key_state enumeration.
+ */
+static inline void
+zwp_virtual_keyboard_v1_key(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t time, uint32_t key, uint32_t state)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) zwp_virtual_keyboard_v1,
+			 ZWP_VIRTUAL_KEYBOARD_V1_KEY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_v1), 0, time, key, state);
+}
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ *
+ * Notifies the compositor that the modifier and/or group state has
+ * changed, and it should update state.
+ *
+ * The client should use wl_keyboard.modifiers event to synchronize its
+ * internal state with seat state.
+ *
+ * Keymap must be set before issuing this request.
+ */
+static inline void
+zwp_virtual_keyboard_v1_modifiers(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) zwp_virtual_keyboard_v1,
+			 ZWP_VIRTUAL_KEYBOARD_V1_MODIFIERS, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_v1), 0, mods_depressed, mods_latched, mods_locked, group);
+}
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ */
+static inline void
+zwp_virtual_keyboard_v1_destroy(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) zwp_virtual_keyboard_v1,
+			 ZWP_VIRTUAL_KEYBOARD_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_v1), WL_MARSHAL_FLAG_DESTROY);
+}
+
+#ifndef ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_ERROR_ENUM
+#define ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_ERROR_ENUM
+enum zwp_virtual_keyboard_manager_v1_error {
+	/**
+	 * client not authorized to use the interface
+	 */
+	ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_ERROR_UNAUTHORIZED = 0,
+};
+#endif /* ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_ERROR_ENUM */
+
+#define ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_CREATE_VIRTUAL_KEYBOARD 0
+
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_manager_v1
+ */
+#define ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_CREATE_VIRTUAL_KEYBOARD_SINCE_VERSION 1
+
+/** @ingroup iface_zwp_virtual_keyboard_manager_v1 */
+static inline void
+zwp_virtual_keyboard_manager_v1_set_user_data(struct zwp_virtual_keyboard_manager_v1 *zwp_virtual_keyboard_manager_v1, void *user_data)
+{
+	wl_proxy_set_user_data((struct wl_proxy *) zwp_virtual_keyboard_manager_v1, user_data);
+}
+
+/** @ingroup iface_zwp_virtual_keyboard_manager_v1 */
+static inline void *
+zwp_virtual_keyboard_manager_v1_get_user_data(struct zwp_virtual_keyboard_manager_v1 *zwp_virtual_keyboard_manager_v1)
+{
+	return wl_proxy_get_user_data((struct wl_proxy *) zwp_virtual_keyboard_manager_v1);
+}
+
+static inline uint32_t
+zwp_virtual_keyboard_manager_v1_get_version(struct zwp_virtual_keyboard_manager_v1 *zwp_virtual_keyboard_manager_v1)
+{
+	return wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_manager_v1);
+}
+
+/** @ingroup iface_zwp_virtual_keyboard_manager_v1 */
+static inline void
+zwp_virtual_keyboard_manager_v1_destroy(struct zwp_virtual_keyboard_manager_v1 *zwp_virtual_keyboard_manager_v1)
+{
+	wl_proxy_destroy((struct wl_proxy *) zwp_virtual_keyboard_manager_v1);
+}
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_manager_v1
+ *
+ * Creates a new virtual keyboard associated to a seat.
+ *
+ * If the compositor enables a keyboard to perform arbitrary actions, it
+ * should present an error when an untrusted client requests a new
+ * keyboard.
+ */
+static inline struct zwp_virtual_keyboard_v1 *
+zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(struct zwp_virtual_keyboard_manager_v1 *zwp_virtual_keyboard_manager_v1, struct wl_seat *seat)
+{
+	struct wl_proxy *id;
+
+	id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_virtual_keyboard_manager_v1,
+			 ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_CREATE_VIRTUAL_KEYBOARD, &zwp_virtual_keyboard_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_manager_v1), 0, seat, NULL);
+
+	return (struct zwp_virtual_keyboard_v1 *) id;
+}
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/rust/qmk-hid-com/src_c/src/main.c b/rust/qmk-hid-com/src_c/src/main.c
new file mode 100644
index 0000000..48d490e
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/main.c
@@ -0,0 +1,28 @@
+#include <stdlib.h>
+
+#include "error.h"
+#include "hid/hid.h"
+#include "keyboard/keyboard.h"
+
+int main(int argc, const char *argv[]) {
+  if (argc != 3) {
+    fail("Usage: %s <usage> <usage_page>", argv[0]);
+  }
+
+  char *ep;
+  uint32_t usage = (uint32_t)strtoul(argv[1], &ep, 0);
+  if (*ep != 0) {
+    fail("Invalid usage: %x", usage);
+  }
+
+  uint32_t usage_page = (uint32_t)strtoul(argv[2], &ep, 0);
+  if (*ep != 0) {
+    fail("Invalid usage: %x", usage_page);
+  }
+
+  uint32_t key_value = read_next(usage, usage_page);
+
+  struct qmk_unicode_type qmk_unicode_type = init_qmk_unicode();
+  type_keycode(&qmk_unicode_type, key_value);
+  destroy_qmk_unicode(qmk_unicode_type);
+}
diff --git a/src/keymaps/soispha/config.h b/src/keymaps/soispha/config.h
index 2b1d36a..9cc7cc5 100644
--- a/src/keymaps/soispha/config.h
+++ b/src/keymaps/soispha/config.h
@@ -23,7 +23,8 @@
 #define USB_SUSPEND_WAKEUP_DELAY 0
 #define LAYER_STATE_16BIT
 
-#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX
+#define RAW_USAGE_PAGE 0xFF61 // 65377
+#define RAW_USAGE_ID 0x6A // 106
 
 #define MOUSEKEY_DELAY 0
 #define MOUSEKEY_INTERVAL 20
diff --git a/src/keymaps/soispha/hid/hid.c b/src/keymaps/soispha/hid/hid.c
new file mode 100644
index 0000000..73b7370
--- /dev/null
+++ b/src/keymaps/soispha/hid/hid.c
@@ -0,0 +1,47 @@
+#include "hid.h"
+#include "raw_hid.h"
+#include <stdint.h>
+#include <string.h>
+
+void hid_send(uint32_t hex) {
+  uint8_t length = 32;
+
+  uint8_t response[length];
+  memset(response, 0, length);
+
+  uint8_t first, second, third, fourth;
+
+  uint8_t n = 1;
+  if (*(char *)&n == 1) {
+    // Little endian
+    first = (uint8_t)hex;
+    second = (uint8_t)(hex >> 8);
+    third = (uint8_t)(hex >> 16);
+    fourth = (uint8_t)(hex >> 24);
+  } else {
+    // Big endian
+    fourth = (uint8_t)hex;
+    third = (uint8_t)(hex << 8);
+    second = (uint8_t)(hex << 16);
+    first = (uint8_t)(hex << 24);
+  }
+
+  response[0] = first;
+  response[1] = second;
+  response[2] = third;
+  response[3] = fourth;
+
+  raw_hid_send(response, length);
+}
+
+// `data` is a pointer to the buffer containing the received HID report
+// `length` is the length of the report - always `RAW_EPSIZE`
+void raw_hid_receive(uint8_t *data, uint8_t length) {
+  uint8_t response[length];
+  memset(response, 0, length);
+  response[0] = 'B';
+
+  if (data[0] == 'A') {
+    raw_hid_send(response, length);
+  }
+}
diff --git a/src/keymaps/soispha/hid/hid.h b/src/keymaps/soispha/hid/hid.h
new file mode 100644
index 0000000..6ee0eda
--- /dev/null
+++ b/src/keymaps/soispha/hid/hid.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <stdint.h>
+
+#define UK(c) (UK_UNICODE | (c))
+
+
+
+void hid_send(uint32_t hex);
diff --git a/src/keymaps/soispha/keymap.c b/src/keymaps/soispha/keymap.c
index 9391212..2b6c16f 100644
--- a/src/keymaps/soispha/keymap.c
+++ b/src/keymaps/soispha/keymap.c
@@ -13,6 +13,9 @@
 #include "layout/layout.h"
 #include "macros.h"
 #include "version.h"
+#include <stdbool.h>
+#include <stdint.h>
+#include "hid/hid.h"
 
 bool process_record_user(uint16_t keycode, keyrecord_t *record) {
   switch (keycode) {
@@ -21,6 +24,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
       SEND_STRING(QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION
                                " (rev: " QMK_GIT_HASH ")");
     }
+    return false;
     break;
   case ST_MACRO_0:
     if (record->event.pressed) {
@@ -77,6 +81,11 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
     }
     return false;
   }
+
+  if (keycode >= UK_UNICODE && keycode <= UK_UNICODE_MAX) {
+    hid_send(QK_UNICODE_GET_CODE_POINT(keycode));
+  }
+
   return true;
 }
 
diff --git a/src/keymaps/soispha/layout/keymap.h b/src/keymaps/soispha/layout/keymap.h
index d4b183a..f61ac0f 100644
--- a/src/keymaps/soispha/layout/keymap.h
+++ b/src/keymaps/soispha/layout/keymap.h
@@ -83,7 +83,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
   [9] = LAYOUT_moonlander( // Special characters
     KK_TP,         KK_TP,              KK_TP,             KK_TP,               KK_TP,         KK_TP,        KK_TP,             KK_TP,         KK_TP,       KK_TP,        KK_TP,       KK_TP,       KK_TP,          KK_TP,
     KK_TP,         KK_TP,              KK_TP,             KK_TP,               KK_TP,         KK_TP,        KK_TP,             KK_TP,         KK_TP,       KK_TP,        KK_TP,       KK_TP,       KK_TP,          KK_TP,
-    KK_TP,         UC(0x201C),         KK_TP,             KK_TP,               UC(0x201D),    KK_TP,        KK_TP,             KK_TP,         KK_TP,       KK_TP,        KK_TP,       KK_TP,       KK_TP,          KK_TP,
+    KK_TP,         UK(0x201C),         KK_TP,             KK_TP,               UK(0x201D),    KK_TP,        KK_TP,             KK_TP,         KK_TP,       KK_TP,        KK_TP,       KK_TP,       KK_TP,          KK_TP,
     KK_TP,         KK_TP,              KK_TP,             KK_TP,               KK_TP,         KK_TP,                                          KK_TP,       KK_TP,        KK_TP,       KK_TP,       KK_TP,          KK_TP,
     KK_TP,         KK_TP,              KK_TP,             KK_TP,               KK_TP,         KK_TP,                                          KK_TP,       KK_TP,        KK_TP,       KK_TP,       KK_TP,          KK_TP,
                                                           KK_TP,               KK_TP,         KK_TP,                                          KK_TP,       KK_TP,        KK_TP
diff --git a/src/keymaps/soispha/layout/layout.h b/src/keymaps/soispha/layout/layout.h
index 4acb7f5..6b0c709 100644
--- a/src/keymaps/soispha/layout/layout.h
+++ b/src/keymaps/soispha/layout/layout.h
@@ -13,6 +13,7 @@
 #pragma once
 #include QMK_KEYBOARD_H
 #include "../macros.h"
+#include "../hid/hid.h"
 
 #define KK_TP KC_TRANSPARENT
 
diff --git a/src/keymaps/soispha/macros.h b/src/keymaps/soispha/macros.h
index e337830..ff03175 100644
--- a/src/keymaps/soispha/macros.h
+++ b/src/keymaps/soispha/macros.h
@@ -15,6 +15,8 @@ enum custom_keycodes {
   ST_MACRO_6,
   ST_MACRO_7,
   VERSION_NUMBER,
+  UK_UNICODE = 0x8000,
+  UK_UNICODE_MAX = 0xFFFF,
 };
 
 enum tap_dance_codes {
diff --git a/src/keymaps/soispha/rules.mk b/src/keymaps/soispha/rules.mk
index c04c2d7..27610b4 100644
--- a/src/keymaps/soispha/rules.mk
+++ b/src/keymaps/soispha/rules.mk
@@ -24,8 +24,10 @@ SEND_STRING_ENABLE = yes
 SPACE_CADET_ENABLE = no
 SWAP_HANDS_ENABLE = yes
 TAP_DANCE_ENABLE = yes
+UNICODE_ENABLE = no
 
-UNICODE_COMMON = yes
-UNICODE_ENABLE = yes
+RAW_ENABLE = yes
 
-SRC += rgb.c
+# LTO_ENABLE = yes
+
+SRC += rgb.c hid/hid.c