diff options
author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-12-30 18:22:41 +0100 |
---|---|---|
committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-12-30 18:22:41 +0100 |
commit | ba9f12810f7dc4969ac175f6e959d5fe6407747d (patch) | |
tree | f4039e46985d56bac471bd8568f96b159a92b2ae | |
parent | feat(src): Make usable (diff) | |
download | qmk_layout-ba9f12810f7dc4969ac175f6e959d5fe6407747d.zip |
feat(treewide): Migrate the Unicode handling to a custom c program, that works via rawhid
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, ®istry_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 |