diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9341440c2..da716363e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -58,17 +58,26 @@ jobs: shared-key: "persist-cross-job" workspaces: ./ - run: rustup component add clippy + - name: Run tests no features run: cargo test --all --no-default-features - name: Run clippy no features run: cargo clippy --all --no-default-features -- -D warnings + - name: Run tests default features run: cargo test --all - name: Run clippy default features run: cargo clippy --all -- -D warnings + + - name: Run tests winIOv2 + run: cargo test --all --features=cmd,win_llhook_read_scancodes,win_sendinput_send_scancodes + - name: Run clippy all winIOv2 + run: cargo clippy --all --features=cmd,win_llhook_read_scancodes,win_sendinput_send_scancodes -- -D warnings + - name: Run tests all features run: cargo test --all --features=cmd,interception_driver,win_sendinput_send_scancodes - name: Run clippy all features run: cargo clippy --all --features=cmd,interception_driver,win_sendinput_send_scancodes -- -D warnings - - name: Run check simulated output + + - name: Run clippy simulated output run: cargo clippy --all --features=simulated_output,cmd -- -D warnings diff --git a/Cargo.lock b/Cargo.lock index bf6a8bc14..0b246a527 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1332,6 +1332,20 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +[[package]] +name = "windows_key_tester" +version = "0.3.0" +dependencies = [ + "anyhow", + "clap", + "kanata", + "kanata-interception", + "log", + "native-windows-gui", + "simplelog", + "winapi", +] + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" diff --git a/Cargo.toml b/Cargo.toml index 0db40d5d6..194d0f47d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "example_tcp_client", "tcp_protocol", "simulated_input", + "windows_key_tester", ] [package] diff --git a/cfg_samples/kanata.kbd b/cfg_samples/kanata.kbd index c3fe216d4..b6b644350 100644 --- a/cfg_samples/kanata.kbd +++ b/cfg_samples/kanata.kbd @@ -240,10 +240,12 @@ If you need help, please feel welcome to ask in the GitHub discussions. ;; deflocalkeys-* enables you to define and use key names that match your locale ;; by defining OS code number mappings for that character. ;; -;; There are three variants of deflocalkeys-*: +;; There are five variants of deflocalkeys-*: ;; - deflocalkeys-win +;; - deflocalkeys-winiov2 ;; - deflocalkeys-wintercept ;; - deflocalkeys-linux +;; - deflocalkeys-macos ;; ;; Only one of each deflocalkeys-* variant is allowed. The variants that are ;; not applicable will be ignored, e.g. deflocalkeys-linux and deflocalkeys-wintercept @@ -274,6 +276,10 @@ If you need help, please feel welcome to ask in the GitHub discussions. ì 187 ) +(deflocalkeys-winiov2 + ì 187 +) + (deflocalkeys-linux ì 13 ) diff --git a/docs/config.adoc b/docs/config.adoc index 5750380e3..557851d22 100644 --- a/docs/config.adoc +++ b/docs/config.adoc @@ -253,27 +253,22 @@ you may want to use `deflayermap` if remapping using these key names. WARNING: On Windows, you should use either `kanata_winIOv2.exe` or Interception when using key names according to the browser `event.code`. -The default `kanata.exe` does do remappings according to the browser `event.code` -keys. +The default `kanata.exe` does not do mappings according to the browser `event.code` +key names. === deflocalkeys You can use `deflocalkeys` to define additional key names that can be used in `defsrc`, `deflayer` and anywhere else in the configuration. -There are four variants of deflocalkeys: +There are five variants of deflocalkeys: - `deflocalkeys-win` +- `deflocalkeys-winiov2` - `deflocalkeys-wintercept` - `deflocalkeys-linux` - `deflocalkeys-macos` -WARNING: On Windows, -`kanata_winIOv2.exe` does not work using the values from `win-keycode-tester`. -In other words, `kanata.exe` and `kanata_winIOv2.exe` both use -`deflocalkeys-win` but do not agree. -The latest keycode tester program works correctly for the default `kanata.exe`. - Only one of each deflocalkeys-* variant is allowed. The variants that are not applicable will be ignored, e.g. `deflocalkeys-linux` and `deflocalkeys-wintercept` are both ignored when using the default Windows kanata binary. @@ -292,6 +287,10 @@ Please contribute to the document if you are able! ì 187 ) +(deflocalkeys-winiov2 + ì 187 +) + (deflocalkeys-wintercept ì 187 ) @@ -315,13 +314,20 @@ base 10. This differs between Windows-hooks, Windows-interception, and Linux. In Linux, `evtest` will give the correct number for the physical key you press. In Windows using the default hook mechanism, the non-interception version of the -keyboard tester in the kanata repository will give the correct number. +keyboard tester in the kanata repository will give the correct number +in the `code: ` section. (https://github.com/jtroo/kanata/releases/tag/win-keycode-tester-v0.2.0[prebuilt binary]) +In Windows uning `winIOv2`, the winIOv2 executable variant +will give the correct number in the `code: ` section. + In Windows using Interception, the interception version of the keyboard tester -will give the correct number. Between the hook and interception versions, some +will give the correct number i the `num: ` section. +Between the hook and interception versions, some keys may agree but others may not; do be aware that they are **not** compatible! +However, Interception and winIOv2 should generally agree with each other. + Ideas for improving the user-friendliness of this system are welcome! As mentioned before, please ask for help in an issue or discussion if needed, and help with https://github.com/jtroo/kanata/blob/main/docs/locales.adoc[this document] diff --git a/parser/src/cfg/mod.rs b/parser/src/cfg/mod.rs index 2cb262742..f0d36dfb1 100755 --- a/parser/src/cfg/mod.rs +++ b/parser/src/cfg/mod.rs @@ -289,8 +289,21 @@ fn parse_cfg(p: &Path) -> MResult { }) } -#[cfg(all(not(feature = "interception_driver"), target_os = "windows"))] +#[cfg(all( + not(feature = "interception_driver"), + any( + not(feature = "win_llhook_read_scancodes"), + not(feature = "win_sendinput_send_scancodes") + ), + target_os = "windows" +))] const DEF_LOCAL_KEYS: &str = "deflocalkeys-win"; +#[cfg(all( + feature = "win_llhook_read_scancodes", + feature = "win_sendinput_send_scancodes", + target_os = "windows" +))] +const DEF_LOCAL_KEYS: &str = "deflocalkeys-winiov2"; #[cfg(all(feature = "interception_driver", target_os = "windows"))] const DEF_LOCAL_KEYS: &str = "deflocalkeys-wintercept"; #[cfg(target_os = "macos")] @@ -430,6 +443,7 @@ pub fn parse_cfg_raw_string( clear_custom_str_oscode_mapping(); for def_local_keys_variant in [ "deflocalkeys-win", + "deflocalkeys-winiov2", "deflocalkeys-wintercept", "deflocalkeys-linux", "deflocalkeys-macos", @@ -709,6 +723,7 @@ fn error_on_unknown_top_level_atoms(exprs: &[Spanned>]) -> Result<()> | "deflocalkeys-macos" | "deflocalkeys-linux" | "deflocalkeys-win" + | "deflocalkeys-winiov2" | "deflocalkeys-wintercept" | "deffakekeys" | "defvirtualkeys" diff --git a/parser/src/cfg/tests.rs b/parser/src/cfg/tests.rs index a1069b5ca..43d92ce5c 100644 --- a/parser/src/cfg/tests.rs +++ b/parser/src/cfg/tests.rs @@ -1256,6 +1256,25 @@ yen 314 ¥ 315 new 316 ) +(deflocalkeys-winiov2 ++ 300 +[ 301 +] 302 +{ 303 +} 304 +/ 305 +; 306 +` 307 += 308 +- 309 +' 310 +, 311 +. 312 +\ 313 +yen 314 +¥ 315 +new 316 +) (deflocalkeys-wintercept + 300 [ 301 diff --git a/src/oskbd/windows/llhook.rs b/src/oskbd/windows/llhook.rs index 0dede6254..b3a0f7b70 100644 --- a/src/oskbd/windows/llhook.rs +++ b/src/oskbd/windows/llhook.rs @@ -108,7 +108,9 @@ impl InputEvent { } else { 0 }; - crate::oskbd::u16_to_osc((lparam.scanCode as u16) | extended).map(Into::into).unwrap_or(lparam.vkCode) + crate::oskbd::u16_to_osc((lparam.scanCode as u16) | extended) + .map(Into::into) + .unwrap_or(lparam.vkCode) } }; Self { diff --git a/src/oskbd/windows/mod.rs b/src/oskbd/windows/mod.rs index a398ef9bd..0cb94939b 100644 --- a/src/oskbd/windows/mod.rs +++ b/src/oskbd/windows/mod.rs @@ -18,7 +18,7 @@ pub use llhook::*; mod scancode_to_usvk; #[allow(unused)] -pub(crate) use scancode_to_usvk::*; +pub use scancode_to_usvk::*; #[cfg(feature = "interception_driver")] mod interception; diff --git a/src/oskbd/windows/scancode_to_usvk.rs b/src/oskbd/windows/scancode_to_usvk.rs index 2f482eeff..c7a11fff5 100644 --- a/src/oskbd/windows/scancode_to_usvk.rs +++ b/src/oskbd/windows/scancode_to_usvk.rs @@ -1,7 +1,7 @@ use kanata_parser::keys::OsCode; #[allow(unused)] -pub(crate) fn u16_to_osc(input: u16) -> Option { +pub fn u16_to_osc(input: u16) -> Option { Some(if input < 0xE000 { match input { 0x01 => OsCode::KEY_ESC, diff --git a/windows_key_tester/Cargo.toml b/windows_key_tester/Cargo.toml index 6e8909486..7c51e742e 100644 --- a/windows_key_tester/Cargo.toml +++ b/windows_key_tester/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windows_key_tester" -version = "0.2.0" +version = "0.3.0" authors = ["jtroo "] description = "Windows keycode tester" keywords = [] @@ -11,8 +11,8 @@ readme = "README.md" license = "LGPL-3.0" edition = "2021" -[dependencies] -clap = { version = "3", features = [ "derive" ] } +[target.'cfg(target_os = "windows")'.dependencies] +clap = { version = "4", features = [ "std", "derive", "help", "suggestions" ], default_features = false } log = "0.4.8" simplelog = "0.12.0" anyhow = "1" @@ -21,12 +21,13 @@ winapi = { version = "0.3.9", features = [ "timeapi", "mmsystem", ] } -native-windows-gui = "1.0.12" -interception = { version = "0.1.2", optional = true } +native-windows-gui = { version = "1.0.12", default_features = false } +kanata-interception = { version = "0.2.0", optional = true } +kanata = { path = "..", optional = true } [features] -cmd = [] -interception_driver = [ "interception" ] +interception_driver = [ "kanata-interception" ] +winiov2 = [ "kanata" ] [profile.release] opt-level = "z" diff --git a/windows_key_tester/src/main.rs b/windows_key_tester/src/main.rs index 0bb08bd85..24a5e0d85 100644 --- a/windows_key_tester/src/main.rs +++ b/windows_key_tester/src/main.rs @@ -2,72 +2,22 @@ //! events, print out the event info, then forward it the keyboard event as-is to the rest of the //! operating system handling. -use anyhow::Result; -use log::info; -use simplelog::*; +#[cfg(target_os = "windows")] +mod windows; +#[cfg(target_os = "windows")] +use windows::*; -use clap::Parser; - -#[cfg(not(feature = "interception_driver"))] -mod llhook; -#[cfg(not(feature = "interception_driver"))] -use llhook::*; - -#[cfg(feature = "interception_driver")] -mod interception; -#[cfg(feature = "interception_driver")] -use crate::interception::*; - -#[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] -struct Args { - /// Enable debug logging - #[clap(short, long)] - debug: bool, - - /// Enable trace logging (implies --debug as well) - #[clap(short, long)] - trace: bool, -} - -/// Parse CLI arguments and initialize logging. -fn cli_init() { - let args = Args::parse(); - - let log_lvl = match (args.debug, args.trace) { - (_, true) => LevelFilter::Trace, - (true, false) => LevelFilter::Debug, - (false, false) => LevelFilter::Info, - }; - - let mut log_cfg = ConfigBuilder::new(); - if let Err(e) = log_cfg.set_time_offset_to_local() { - eprintln!("WARNING: could not set log TZ to local: {:?}", e); - }; - CombinedLogger::init(vec![TermLogger::new( - log_lvl, - log_cfg.build(), - TerminalMode::Mixed, - ColorChoice::AlwaysAnsi, - )]) - .expect("logger can init"); - log::info!("windows_key_tester v{} starting", env!("CARGO_PKG_VERSION")); -} - -fn main_impl() -> Result<()> { - cli_init(); - info!("Sleeping for 2s. Please release all keys and don't press additional ones."); - std::thread::sleep(std::time::Duration::from_secs(2)); - start()?; - Ok(()) -} - -fn main() -> Result<()> { +#[cfg(target_os = "windows")] +fn main() { let ret = main_impl(); if let Err(ref e) = ret { log::error!("main got error {}", e); } eprintln!("\nPress any key to exit"); let _ = std::io::stdin().read_line(&mut String::new()); - ret +} + +#[cfg(not(target_os = "windows"))] +fn main() { + print!("Hello world! Wrong OS. Doing nothing."); } diff --git a/windows_key_tester/src/windows.rs b/windows_key_tester/src/windows.rs new file mode 100644 index 000000000..cd88826e1 --- /dev/null +++ b/windows_key_tester/src/windows.rs @@ -0,0 +1,58 @@ +use anyhow::Result; +use simplelog::*; + +use clap::Parser; +#[cfg(not(feature = "interception_driver"))] +mod llhook; +#[cfg(not(feature = "interception_driver"))] +use llhook::*; + +#[cfg(feature = "interception_driver")] +mod interception; +#[cfg(feature = "interception_driver")] +use interception::*; + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct Args { + /// Enable debug logging + #[clap(short, long)] + debug: bool, + + /// Enable trace logging (implies --debug as well) + #[clap(short, long)] + trace: bool, +} + +#[cfg(target_os = "windows")] +/// Parse CLI arguments and initialize logging. +fn cli_init() { + let args = Args::parse(); + + let log_lvl = match (args.debug, args.trace) { + (_, true) => LevelFilter::Trace, + (true, false) => LevelFilter::Debug, + (false, false) => LevelFilter::Info, + }; + + let mut log_cfg = ConfigBuilder::new(); + if let Err(e) = log_cfg.set_time_offset_to_local() { + eprintln!("WARNING: could not set log TZ to local: {:?}", e); + }; + CombinedLogger::init(vec![TermLogger::new( + log_lvl, + log_cfg.build(), + TerminalMode::Mixed, + ColorChoice::AlwaysAnsi, + )]) + .expect("logger can init"); + log::info!("windows_key_tester v{} starting", env!("CARGO_PKG_VERSION")); +} + +pub(crate) fn main_impl() -> Result<()> { + cli_init(); + log::info!("Sleeping for 2s. Please release all keys and don't press additional ones."); + std::thread::sleep(std::time::Duration::from_secs(2)); + start()?; + Ok(()) +} diff --git a/windows_key_tester/src/interception.rs b/windows_key_tester/src/windows/interception.rs similarity index 99% rename from windows_key_tester/src/interception.rs rename to windows_key_tester/src/windows/interception.rs index 0ed012930..ba9bef528 100644 --- a/windows_key_tester/src/interception.rs +++ b/windows_key_tester/src/windows/interception.rs @@ -1,6 +1,6 @@ use anyhow::Result; -use interception as ic; -use interception::{KeyState, ScanCode, Stroke}; +use kanata_interception as ic; +use kanata_interception::{KeyState, ScanCode, Stroke}; pub fn start() -> Result<()> { let intrcptn = ic::Interception::new().expect( diff --git a/windows_key_tester/src/llhook.rs b/windows_key_tester/src/windows/llhook.rs similarity index 85% rename from windows_key_tester/src/llhook.rs rename to windows_key_tester/src/windows/llhook.rs index 972e74669..1ad284110 100644 --- a/windows_key_tester/src/llhook.rs +++ b/windows_key_tester/src/windows/llhook.rs @@ -48,12 +48,25 @@ pub struct InputEvent { } impl InputEvent { + #[cfg(not(feature = "winiov2"))] fn from_hook_lparam(lparam: &KBDLLHOOKSTRUCT) -> Self { Self { code: lparam.vkCode, up: lparam.flags & LLKHF_UP != 0, } } + + #[cfg(feature = "winiov2")] + fn from_hook_lparam(lparam: &KBDLLHOOKSTRUCT) -> Self { + let extended = if lparam.flags & 0x1 == 0x1 { 0xE000 } else { 0 }; + let code = kanata_state_machine::oskbd::u16_to_osc((lparam.scanCode as u16) | extended) + .map(Into::into) + .unwrap_or(lparam.vkCode); + Self { + code, + up: lparam.flags & LLKHF_UP != 0, + } + } } /// The actual WinAPI compatible callback.