From 60ce29a23c217fb31729945f850b505a7a9e0273 Mon Sep 17 00:00:00 2001 From: jtroo Date: Sat, 22 Jun 2024 21:49:25 -0700 Subject: [PATCH] feat(win-interception): add device exclusion to defcfg (#1116) --- parser/src/cfg/defcfg.rs | 312 ++++++++++++++++++----------- src/kanata/mod.rs | 74 +++++-- src/kanata/windows/interception.rs | 35 +++- 3 files changed, 280 insertions(+), 141 deletions(-) diff --git a/parser/src/cfg/defcfg.rs b/parser/src/cfg/defcfg.rs index 5c4a0df08..edf37962a 100644 --- a/parser/src/cfg/defcfg.rs +++ b/parser/src/cfg/defcfg.rs @@ -6,6 +6,56 @@ use crate::custom_action::*; #[allow(unused)] use crate::{anyhow_expr, anyhow_span, bail, bail_expr, bail_span}; +#[cfg(any(target_os = "linux", target_os = "unknown"))] +#[derive(Debug, Clone)] +pub struct CfgLinuxOptions { + #[cfg(any(target_os = "linux", target_os = "unknown"))] + pub linux_dev: Vec, + #[cfg(any(target_os = "linux", target_os = "unknown"))] + pub linux_dev_names_include: Option>, + #[cfg(any(target_os = "linux", target_os = "unknown"))] + pub linux_dev_names_exclude: Option>, + #[cfg(any(target_os = "linux", target_os = "unknown"))] + pub linux_continue_if_no_devs_found: bool, + #[cfg(any(target_os = "linux", target_os = "unknown"))] + pub linux_unicode_u_code: crate::keys::OsCode, + #[cfg(any(target_os = "linux", target_os = "unknown"))] + pub linux_unicode_termination: UnicodeTermination, + #[cfg(any(target_os = "linux", target_os = "unknown"))] + pub linux_x11_repeat_delay_rate: Option, + #[cfg(any(target_os = "linux", target_os = "unknown"))] + pub linux_use_trackpoint_property: bool, +} +#[cfg(any(target_os = "linux", target_os = "unknown"))] +impl Default for CfgLinuxOptions { + fn default() -> Self { + Self { + linux_dev: vec![], + linux_dev_names_include: None, + linux_dev_names_exclude: None, + linux_continue_if_no_devs_found: false, + // historically was the only option, so make KEY_U the default + linux_unicode_u_code: crate::keys::OsCode::KEY_U, + // historically was the only option, so make Enter the default + linux_unicode_termination: UnicodeTermination::Enter, + linux_x11_repeat_delay_rate: None, + linux_use_trackpoint_property: false, + } + } +} + +#[cfg(any( + all(feature = "interception_driver", target_os = "windows"), + target_os = "unknown" +))] +#[derive(Debug, Clone, Default)] +pub struct CfgWinterceptOptions { + pub windows_interception_mouse_hwids: Option>, + pub windows_interception_mouse_hwids_exclude: Option>, + pub windows_interception_keyboard_hwids: Option>, + pub windows_interception_keyboard_hwids_exclude: Option>, +} + #[cfg(all(any(target_os = "windows", target_os = "unknown"), feature = "gui"))] #[derive(Debug, Clone)] pub struct CfgOptionsGui { @@ -69,33 +119,14 @@ pub struct CfgOptions { pub trans_resolution_behavior_v2: bool, pub chords_v2_min_idle: u16, #[cfg(any(target_os = "linux", target_os = "unknown"))] - pub linux_dev: Vec, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - pub linux_dev_names_include: Option>, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - pub linux_dev_names_exclude: Option>, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - pub linux_continue_if_no_devs_found: bool, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - pub linux_unicode_u_code: crate::keys::OsCode, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - pub linux_unicode_termination: UnicodeTermination, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - pub linux_x11_repeat_delay_rate: Option, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - pub linux_use_trackpoint_property: bool, + pub linux_opts: CfgLinuxOptions, #[cfg(any(target_os = "windows", target_os = "unknown"))] pub windows_altgr: AltGrBehaviour, #[cfg(any( all(feature = "interception_driver", target_os = "windows"), target_os = "unknown" ))] - pub windows_interception_mouse_hwids: Option>, - #[cfg(any( - all(feature = "interception_driver", target_os = "windows"), - target_os = "unknown" - ))] - pub windows_interception_keyboard_hwids: Option>, + pub wintercept_opts: CfgWinterceptOptions, #[cfg(any(target_os = "macos", target_os = "unknown"))] pub macos_dev_names_include: Option>, #[cfg(all(any(target_os = "windows", target_os = "unknown"), feature = "gui"))] @@ -124,38 +155,17 @@ impl Default for CfgOptions { trans_resolution_behavior_v2: true, chords_v2_min_idle: 5, #[cfg(any(target_os = "linux", target_os = "unknown"))] - linux_dev: vec![], - #[cfg(any(target_os = "linux", target_os = "unknown"))] - linux_dev_names_include: None, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - linux_dev_names_exclude: None, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - linux_continue_if_no_devs_found: false, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - // historically was the only option, so make KEY_U the default - linux_unicode_u_code: crate::keys::OsCode::KEY_U, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - // historically was the only option, so make Enter the default - linux_unicode_termination: UnicodeTermination::Enter, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - linux_x11_repeat_delay_rate: None, - #[cfg(any(target_os = "linux", target_os = "unknown"))] - linux_use_trackpoint_property: false, + linux_opts: Default::default(), #[cfg(any(target_os = "windows", target_os = "unknown"))] windows_altgr: AltGrBehaviour::default(), #[cfg(any( all(feature = "interception_driver", target_os = "windows"), target_os = "unknown" ))] - windows_interception_mouse_hwids: None, - #[cfg(any( - all(feature = "interception_driver", target_os = "windows"), - target_os = "unknown" - ))] - windows_interception_keyboard_hwids: None, + wintercept_opts: Default::default(), #[cfg(any(target_os = "macos", target_os = "unknown"))] macos_dev_names_include: None, - #[cfg(all(any(target_os = "windows",target_os = "unknown"), feature = "gui"))] + #[cfg(all(any(target_os = "windows", target_os = "unknown"), feature = "gui"))] gui_opts: Default::default(), } } @@ -215,8 +225,8 @@ pub fn parse_defcfg(expr: &[SExpr]) -> Result { "linux-dev" => { #[cfg(any(target_os = "linux", target_os = "unknown"))] { - cfg.linux_dev = parse_dev(val)?; - if cfg.linux_dev.is_empty() { + cfg.linux_opts.linux_dev = parse_dev(val)?; + if cfg.linux_opts.linux_dev.is_empty() { bail_expr!( val, "device list is empty, no devices will be intercepted" @@ -231,21 +241,21 @@ pub fn parse_defcfg(expr: &[SExpr]) -> Result { if dev_names.is_empty() { log::warn!("linux-dev-names-include is empty"); } - cfg.linux_dev_names_include = Some(dev_names); + cfg.linux_opts.linux_dev_names_include = Some(dev_names); } } "linux-dev-names-exclude" => { #[cfg(any(target_os = "linux", target_os = "unknown"))] { - cfg.linux_dev_names_exclude = Some(parse_dev(val)?); + cfg.linux_opts.linux_dev_names_exclude = Some(parse_dev(val)?); } } "linux-unicode-u-code" => { #[cfg(any(target_os = "linux", target_os = "unknown"))] { let v = sexpr_to_str_or_err(val, label)?; - cfg.linux_unicode_u_code = - crate::keys::str_to_oscode(v).ok_or_else(|| { + cfg.linux_opts.linux_unicode_u_code = crate::keys::str_to_oscode(v) + .ok_or_else(|| { anyhow_expr!(val, "unknown code for {label}: {}", v) })?; } @@ -254,7 +264,7 @@ pub fn parse_defcfg(expr: &[SExpr]) -> Result { #[cfg(any(target_os = "linux", target_os = "unknown"))] { let v = sexpr_to_str_or_err(val, label)?; - cfg.linux_unicode_termination = match v { + cfg.linux_opts.linux_unicode_termination = match v { "enter" => UnicodeTermination::Enter, "space" => UnicodeTermination::Space, "enter-space" => UnicodeTermination::EnterSpace, @@ -276,7 +286,7 @@ pub fn parse_defcfg(expr: &[SExpr]) -> Result { if delay_rate.len() != 2 { bail_expr!(val, "{}", ERRMSG) } - cfg.linux_x11_repeat_delay_rate = Some(KeyRepeatSettings { + cfg.linux_opts.linux_x11_repeat_delay_rate = Some(KeyRepeatSettings { delay: match str::parse::(delay_rate[0]) { Ok(delay) => delay, Err(_) => bail_expr!(val, "{}", ERRMSG), @@ -291,7 +301,8 @@ pub fn parse_defcfg(expr: &[SExpr]) -> Result { "linux-use-trackpoint-property" => { #[cfg(any(target_os = "linux", target_os = "unknown"))] { - cfg.linux_use_trackpoint_property = parse_defcfg_val_bool(val, label)? + cfg.linux_opts.linux_use_trackpoint_property = + parse_defcfg_val_bool(val, label)? } } "windows-altgr" => { @@ -319,6 +330,13 @@ pub fn parse_defcfg(expr: &[SExpr]) -> Result { target_os = "unknown" ))] { + if cfg + .wintercept_opts + .windows_interception_mouse_hwids_exclude + .is_some() + { + bail_expr!(val, "{label} and windows-interception-mouse-hwid-exclude cannot both be included"); + } let v = sexpr_to_str_or_err(val, label)?; let hwid = v; log::trace!("win hwid: {hwid}"); @@ -339,15 +357,21 @@ pub fn parse_defcfg(expr: &[SExpr]) -> Result { hwid[i] = b; Ok(hwid) })?; - match cfg.windows_interception_mouse_hwids.as_mut() { + match cfg + .wintercept_opts + .windows_interception_mouse_hwids + .as_mut() + { Some(v) => { v.push(hwid_slice); } None => { - cfg.windows_interception_mouse_hwids = Some(vec![hwid_slice]); + cfg.wintercept_opts.windows_interception_mouse_hwids = + Some(vec![hwid_slice]); } } - cfg.windows_interception_mouse_hwids + cfg.wintercept_opts + .windows_interception_mouse_hwids .as_mut() .unwrap() .shrink_to_fit(); @@ -359,82 +383,102 @@ pub fn parse_defcfg(expr: &[SExpr]) -> Result { target_os = "unknown" ))] { - let hwids = sexpr_to_list_or_err(val, label)?; - let mut parsed_hwids = vec![]; - for hwid_expr in hwids.iter() { - let hwid = sexpr_to_str_or_err( - hwid_expr, - "entry in windows-interception-mouse-hwids", - )?; - log::trace!("win hwid: {hwid}"); - let hwid_vec = hwid - .split(',') - .try_fold(vec![], |mut hwid_bytes, hwid_byte| { - hwid_byte.trim_matches(' ').parse::().map(|b| { - hwid_bytes.push(b); - hwid_bytes - }) - }).map_err(|_| anyhow_expr!(hwid_expr, "Entry in {label} is invalid. Entries should be numbers [0,255] separated by commas"))?; - let hwid_slice = hwid_vec.iter().copied().enumerate() - .try_fold([0u8; HWID_ARR_SZ], |mut hwid, idx_byte| { - let (i, b) = idx_byte; - if i > HWID_ARR_SZ { - bail_expr!(hwid_expr, "entry in {label} is too long; it should be up to {HWID_ARR_SZ} 8-bit unsigned integers") - } - hwid[i] = b; - Ok(hwid) - }); - parsed_hwids.push(hwid_slice?); + if cfg + .wintercept_opts + .windows_interception_mouse_hwids_exclude + .is_some() + { + bail_expr!(val, "{label} and windows-interception-mouse-hwid-exclude cannot both be included"); } - match cfg.windows_interception_mouse_hwids.as_mut() { + let parsed_hwids = sexpr_to_hwids_vec( + val, + label, + "entry in windows-interception-mouse-hwids", + )?; + match cfg + .wintercept_opts + .windows_interception_mouse_hwids + .as_mut() + { Some(v) => { v.extend(parsed_hwids); } None => { - cfg.windows_interception_mouse_hwids = Some(parsed_hwids); + cfg.wintercept_opts.windows_interception_mouse_hwids = + Some(parsed_hwids); } } - cfg.windows_interception_mouse_hwids + cfg.wintercept_opts + .windows_interception_mouse_hwids .as_mut() .unwrap() .shrink_to_fit(); } } + "windows-interception-mouse-hwids-exclude" => { + #[cfg(any( + all(feature = "interception_driver", target_os = "windows"), + target_os = "unknown" + ))] + { + if cfg + .wintercept_opts + .windows_interception_mouse_hwids + .is_some() + { + bail_expr!(val, "{label} and windows-interception-mouse-hwid(s) cannot both be used"); + } + let parsed_hwids = sexpr_to_hwids_vec( + val, + label, + "entry in windows-interception-mouse-hwids-exclude", + )?; + cfg.wintercept_opts.windows_interception_mouse_hwids_exclude = + Some(parsed_hwids); + } + } "windows-interception-keyboard-hwids" => { #[cfg(any( all(feature = "interception_driver", target_os = "windows"), target_os = "unknown" ))] { - let hwids = sexpr_to_list_or_err(val, label)?; - let mut parsed_hwids = vec![]; - for hwid_expr in hwids.iter() { - let hwid = sexpr_to_str_or_err( - hwid_expr, - "entry in windows-interception-keyboard-hwids", - )?; - log::trace!("win hwid: {hwid}"); - let hwid_vec = hwid - .split(',') - .try_fold(vec![], |mut hwid_bytes, hwid_byte| { - hwid_byte.trim_matches(' ').parse::().map(|b| { - hwid_bytes.push(b); - hwid_bytes - }) - }).map_err(|_| anyhow_expr!(hwid_expr, "Entry in {label} is invalid. Entries should be numbers [0,255] separated by commas"))?; - let hwid_slice = hwid_vec.iter().copied().enumerate() - .try_fold([0u8; HWID_ARR_SZ], |mut hwid, idx_byte| { - let (i, b) = idx_byte; - if i > HWID_ARR_SZ { - bail_expr!(hwid_expr, "entry in {label} is too long; it should be up to {HWID_ARR_SZ} 8-bit unsigned integers") - } - hwid[i] = b; - Ok(hwid) - }); - parsed_hwids.push(hwid_slice?); + if cfg + .wintercept_opts + .windows_interception_keyboard_hwids_exclude + .is_some() + { + bail_expr!(val, "{label} and windows-interception-keyboard-hwid-exclude cannot both be used"); } - parsed_hwids.shrink_to_fit(); - cfg.windows_interception_keyboard_hwids = Some(parsed_hwids); + let parsed_hwids = sexpr_to_hwids_vec( + val, + label, + "entry in windows-interception-keyboard-hwids", + )?; + cfg.wintercept_opts.windows_interception_keyboard_hwids = + Some(parsed_hwids); + } + } + "windows-interception-keyboard-hwids-exclude" => { + #[cfg(any( + all(feature = "interception_driver", target_os = "windows"), + target_os = "unknown" + ))] + { + if cfg + .wintercept_opts + .windows_interception_keyboard_hwids + .is_some() + { + bail_expr!(val, "{label} and windows-interception-keyboard-hwid cannot both be used"); + } + let parsed_hwids = sexpr_to_hwids_vec( + val, + label, + "entry in windows-interception-keyboard-hwids-exclude", + )?; + cfg.wintercept_opts + .windows_interception_keyboard_hwids_exclude = Some(parsed_hwids); } } "macos-dev-names-include" => { @@ -580,7 +624,8 @@ pub fn parse_defcfg(expr: &[SExpr]) -> Result { "linux-continue-if-no-devs-found" => { #[cfg(any(target_os = "linux", target_os = "unknown"))] { - cfg.linux_continue_if_no_devs_found = parse_defcfg_val_bool(val, label)? + cfg.linux_opts.linux_continue_if_no_devs_found = + parse_defcfg_val_bool(val, label)? } } "movemouse-smooth-diagonals" => { @@ -751,6 +796,43 @@ fn sexpr_to_list_or_err<'a>(expr: &'a SExpr, label: &str) -> Result<&'a [SExpr]> } } +#[cfg(any( + all(feature = "interception_driver", target_os = "windows"), + target_os = "unknown" +))] +fn sexpr_to_hwids_vec( + val: &SExpr, + label: &str, + entry_label: &str, +) -> Result> { + let hwids = sexpr_to_list_or_err(val, label)?; + let mut parsed_hwids = vec![]; + for hwid_expr in hwids.iter() { + let hwid = sexpr_to_str_or_err(hwid_expr, entry_label)?; + log::trace!("win hwid: {hwid}"); + let hwid_vec = hwid + .split(',') + .try_fold(vec![], |mut hwid_bytes, hwid_byte| { + hwid_byte.trim_matches(' ').parse::().map(|b| { + hwid_bytes.push(b); + hwid_bytes + }) + }).map_err(|_| anyhow_expr!(hwid_expr, "Entry in {label} is invalid. Entries should be numbers [0,255] separated by commas"))?; + let hwid_slice = hwid_vec.iter().copied().enumerate() + .try_fold([0u8; HWID_ARR_SZ], |mut hwid, idx_byte| { + let (i, b) = idx_byte; + if i > HWID_ARR_SZ { + bail_expr!(hwid_expr, "entry in {label} is too long; it should be up to {HWID_ARR_SZ} 8-bit unsigned integers") + } + hwid[i] = b; + Ok(hwid) + }); + parsed_hwids.push(hwid_slice?); + } + parsed_hwids.shrink_to_fit(); + Ok(parsed_hwids) +} + #[cfg(any(target_os = "linux", target_os = "unknown"))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct KeyRepeatSettings { diff --git a/src/kanata/mod.rs b/src/kanata/mod.rs index 0e28eaf17..70b502317 100755 --- a/src/kanata/mod.rs +++ b/src/kanata/mod.rs @@ -163,9 +163,17 @@ pub struct Kanata { /// by kanata. intercept_mouse_hwids: Option>, #[cfg(all(feature = "interception_driver", target_os = "windows"))] - /// Used to know which input device to treat as a mouse for intercepting and processing inputs + /// Used to know which mouse input devices to exclude from processing inputs by kanata. This is + /// mutually exclusive from `intercept_mouse_hwids` and kanata will panic if both are included. + intercept_mouse_hwids_exclude: Option>, + #[cfg(all(feature = "interception_driver", target_os = "windows"))] + /// Used to know which input device to treat as a keyboard for intercepting and processing inputs /// by kanata. intercept_kb_hwids: Option>, + #[cfg(all(feature = "interception_driver", target_os = "windows"))] + /// Used to know which keyboard input devices to exclude from processing inputs by kanata. This + /// is mutually exclusive from `intercept_kb_hwids` and kanata will panic if both are included. + intercept_kb_hwids_exclude: Option>, /// User configuration to do logging of layer changes or not. log_layer_changes: bool, /// Tracks the caps-word state. Is Some(...) if caps-word is active and None otherwise. @@ -277,7 +285,7 @@ impl Kanata { #[cfg(target_os = "linux")] &args.symlink_path, #[cfg(target_os = "linux")] - cfg.options.linux_use_trackpoint_property, + cfg.options.linux_opts.linux_use_trackpoint_property, ) { Ok(kbd_out) => kbd_out, Err(err) => { @@ -339,17 +347,30 @@ impl Kanata { #[cfg(target_os = "macos")] include_names: cfg.options.macos_dev_names_include, #[cfg(target_os = "linux")] - kbd_in_paths: cfg.options.linux_dev, + kbd_in_paths: cfg.options.linux_opts.linux_dev, #[cfg(target_os = "linux")] - continue_if_no_devices: cfg.options.linux_continue_if_no_devs_found, + continue_if_no_devices: cfg.options.linux_opts.linux_continue_if_no_devs_found, #[cfg(target_os = "linux")] - include_names: cfg.options.linux_dev_names_include, + include_names: cfg.options.linux_opts.linux_dev_names_include, #[cfg(target_os = "linux")] - exclude_names: cfg.options.linux_dev_names_exclude, + exclude_names: cfg.options.linux_opts.linux_dev_names_exclude, + #[cfg(all(feature = "interception_driver", target_os = "windows"))] + intercept_mouse_hwids: cfg.options.wintercept_opts.windows_interception_mouse_hwids, #[cfg(all(feature = "interception_driver", target_os = "windows"))] - intercept_mouse_hwids: cfg.options.windows_interception_mouse_hwids, + intercept_mouse_hwids_exclude: cfg + .options + .wintercept_opts + .windows_interception_mouse_hwids_exclude, #[cfg(all(feature = "interception_driver", target_os = "windows"))] - intercept_kb_hwids: cfg.options.windows_interception_keyboard_hwids, + intercept_kb_hwids: cfg + .options + .wintercept_opts + .windows_interception_keyboard_hwids, + #[cfg(all(feature = "interception_driver", target_os = "windows"))] + intercept_kb_hwids_exclude: cfg + .options + .wintercept_opts + .windows_interception_keyboard_hwids_exclude, dynamic_macro_replay_state: None, dynamic_macro_record_state: None, dynamic_macros: Default::default(), @@ -364,7 +385,7 @@ impl Kanata { delay: cfg.options.dynamic_macro_replay_delay_behaviour, }, #[cfg(target_os = "linux")] - x11_repeat_rate: cfg.options.linux_x11_repeat_delay_rate, + x11_repeat_rate: cfg.options.linux_opts.linux_x11_repeat_delay_rate, waiting_for_idle: HashSet::default(), ticks_since_idle: 0, movemouse_buffer: None, @@ -399,7 +420,7 @@ impl Kanata { #[cfg(target_os = "linux")] &None, #[cfg(target_os = "linux")] - cfg.options.linux_use_trackpoint_property, + cfg.options.linux_opts.linux_use_trackpoint_property, ) { Ok(kbd_out) => kbd_out, Err(err) => { @@ -439,17 +460,30 @@ impl Kanata { #[cfg(target_os = "macos")] include_names: cfg.options.macos_dev_names_include, #[cfg(target_os = "linux")] - kbd_in_paths: cfg.options.linux_dev, + kbd_in_paths: cfg.options.linux_opts.linux_dev, #[cfg(target_os = "linux")] - continue_if_no_devices: cfg.options.linux_continue_if_no_devs_found, + continue_if_no_devices: cfg.options.linux_opts.linux_continue_if_no_devs_found, #[cfg(target_os = "linux")] - include_names: cfg.options.linux_dev_names_include, + include_names: cfg.options.linux_opts.linux_dev_names_include, #[cfg(target_os = "linux")] - exclude_names: cfg.options.linux_dev_names_exclude, + exclude_names: cfg.options.linux_opts.linux_dev_names_exclude, + #[cfg(all(feature = "interception_driver", target_os = "windows"))] + intercept_mouse_hwids: cfg.options.wintercept_opts.windows_interception_mouse_hwids, + #[cfg(all(feature = "interception_driver", target_os = "windows"))] + intercept_mouse_hwids_exclude: cfg + .options + .wintercept_opts + .windows_interception_mouse_hwids_exclude, #[cfg(all(feature = "interception_driver", target_os = "windows"))] - intercept_mouse_hwids: cfg.options.windows_interception_mouse_hwids, + intercept_kb_hwids: cfg + .options + .wintercept_opts + .windows_interception_keyboard_hwids, #[cfg(all(feature = "interception_driver", target_os = "windows"))] - intercept_kb_hwids: cfg.options.windows_interception_keyboard_hwids, + intercept_kb_hwids_exclude: cfg + .options + .wintercept_opts + .windows_interception_keyboard_hwids_exclude, dynamic_macro_replay_state: None, dynamic_macro_record_state: None, dynamic_macros: Default::default(), @@ -464,7 +498,7 @@ impl Kanata { delay: cfg.options.dynamic_macro_replay_delay_behaviour, }, #[cfg(target_os = "linux")] - x11_repeat_rate: cfg.options.linux_x11_repeat_delay_rate, + x11_repeat_rate: cfg.options.linux_opts.linux_x11_repeat_delay_rate, waiting_for_idle: HashSet::default(), ticks_since_idle: 0, movemouse_buffer: None, @@ -542,7 +576,7 @@ impl Kanata { *MAPPED_KEYS.lock() = cfg.mapped_keys; #[cfg(target_os = "linux")] - Kanata::set_repeat_rate(cfg.options.linux_x11_repeat_delay_rate)?; + Kanata::set_repeat_rate(cfg.options.linux_opts.linux_x11_repeat_delay_rate)?; log::info!("Live reload successful"); #[cfg(feature = "tcp_server")] if let Some(tx) = _tx { @@ -2085,8 +2119,8 @@ fn check_for_exit(_event: &KeyEvent) { fn update_kbd_out(_cfg: &CfgOptions, _kbd_out: &KbdOut) -> Result<()> { #[cfg(all(not(feature = "simulated_output"), target_os = "linux"))] { - _kbd_out.update_unicode_termination(_cfg.linux_unicode_termination); - _kbd_out.update_unicode_u_code(_cfg.linux_unicode_u_code); + _kbd_out.update_unicode_termination(_cfg.linux_opts.linux_unicode_termination); + _kbd_out.update_unicode_u_code(_cfg.linux_opts.linux_unicode_u_code); } Ok(()) } diff --git a/src/kanata/windows/interception.rs b/src/kanata/windows/interception.rs index e22638414..31cfe89a2 100644 --- a/src/kanata/windows/interception.rs +++ b/src/kanata/windows/interception.rs @@ -20,9 +20,12 @@ impl Kanata { }; 32]; let keyboards_to_intercept_hwids = kanata.lock().intercept_kb_hwids.clone(); + let keyboards_to_intercept_hwids_exclude = kanata.lock().intercept_kb_hwids_exclude.clone(); let mouse_to_intercept_hwids: Option> = kanata.lock().intercept_mouse_hwids.clone(); - if mouse_to_intercept_hwids.is_some() { + let mouse_to_intercept_excluded_hwids: Option> = + kanata.lock().intercept_mouse_hwids_exclude.clone(); + if mouse_to_intercept_hwids.is_some() || mouse_to_intercept_excluded_hwids.is_some() { intrcptn.set_filter( ic::is_mouse, ic::Filter::MouseFilter(ic::MouseState::all() & (!ic::MouseState::MOVE)), @@ -40,6 +43,7 @@ impl Kanata { dev, &intrcptn, &keyboards_to_intercept_hwids, + &keyboards_to_intercept_hwids_exclude, &mut is_dev_interceptable, ) { log::debug!("stroke {:?} is from undesired device", strokes[i]); @@ -62,11 +66,14 @@ impl Kanata { KeyEvent { code, value } } ic::Stroke::Mouse { state, rolling, .. } => { - if mouse_to_intercept_hwids.is_some() { + if mouse_to_intercept_hwids.is_some() + || mouse_to_intercept_excluded_hwids.is_some() + { log::trace!("checking mouse stroke {:?}", strokes[i]); if let Some(event) = mouse_state_to_event( dev, &mouse_to_intercept_hwids, + &mouse_to_intercept_excluded_hwids, state, rolling, &intrcptn, @@ -132,27 +139,42 @@ fn is_device_interceptable( input_dev: ic::Device, intrcptn: &ic::Interception, allowed_hwids: &Option>, + excluded_hwids: &Option>, cache: &mut HashMap, ) -> bool { - match allowed_hwids { - None => true, - Some(allowed) => match cache.get(&input_dev) { + match (allowed_hwids, excluded_hwids) { + (None, None) => true, + (Some(allowed), None) => match cache.get(&input_dev) { Some(v) => *v, None => { let mut hwid = [0u8; HWID_ARR_SZ]; log::trace!("getting hardware id for input dev: {input_dev}"); let res = intrcptn.get_hardware_id(input_dev, &mut hwid); let dev_is_interceptable = allowed.contains(&hwid); - log::info!("res {res}; device #{input_dev} hwid {hwid:?} matches allowed keyboard input: {dev_is_interceptable}"); + log::info!("include check - res {res}; device #{input_dev} is intercepted: {dev_is_interceptable}; hwid {hwid:?} "); cache.insert(input_dev, dev_is_interceptable); dev_is_interceptable } }, + (None, Some(excluded)) => match cache.get(&input_dev) { + Some(v) => *v, + None => { + let mut hwid = [0u8; HWID_ARR_SZ]; + log::trace!("getting hardware id for input dev: {input_dev}"); + let res = intrcptn.get_hardware_id(input_dev, &mut hwid); + let dev_is_interceptable = !excluded.contains(&hwid); + log::info!("exclude check - res {res}; device #{input_dev} is intercepted: {dev_is_interceptable}; hwid {hwid:?} "); + cache.insert(input_dev, dev_is_interceptable); + dev_is_interceptable + } + }, + _ => unreachable!("excluded and allowed should be mutually exclusive"), } } fn mouse_state_to_event( input_dev: ic::Device, allowed_hwids: &Option>, + excluded_hwids: &Option>, state: ic::MouseState, rolling: i16, intrcptn: &ic::Interception, @@ -162,6 +184,7 @@ fn mouse_state_to_event( input_dev, intrcptn, allowed_hwids, + excluded_hwids, device_interceptability_cache, ) { return None;