Skip to content

Commit

Permalink
feat(defcfg): (all-except ...) for process-unmapped-keys (#1370)
Browse files Browse the repository at this point in the history
  • Loading branch information
jtroo authored Nov 23, 2024
1 parent 4fe3508 commit 72d90e0
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 10 deletions.
5 changes: 4 additions & 1 deletion cfg_samples/kanata.kbd
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,10 @@ If you need help, please feel welcome to ask in the GitHub discussions.

;; We need to set it to yes in this kanata.kbd example config to allow the use of __ and ___
;; in the deflayer-custom-map example below in the file
process-unmapped-keys yes

;; This also accepts a list parameter (all-except key1 ... keyN)
;; which behaves like "yes" but excludes the keys within the list.
process-unmapped-keys (all-except f19)

;; Disable all keys not mapped in defsrc.
;; Only works if process-unmapped-keys is also yes.
Expand Down
10 changes: 5 additions & 5 deletions docs/config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -536,14 +536,13 @@ Some examples:
This option is disabled by default.
The reason this is not enabled by default
is because some keys may not work correctly if they are intercepted.
For example, see <<windows-only-windows-altgr>>.
A known issue being AltGr/ralt/Right Alt; see <<windows-only-windows-altgr>>.

.Example:
[source]
----
(defcfg
process-unmapped-keys yes
)
(defcfg process-unmapped-keys yes)
(defcfg process-unmapped-keys (all-except lctl ralt))
----

== Aliases and variables[[aliases-and-vars]]
Expand Down Expand Up @@ -3954,7 +3953,8 @@ You can use one of the listed values to change what kanata does with the key:
** This adds an `lctl` release when `ralt` is released

Without these workarounds,
you should instead ensure you set `process-unmapped-keys no`
you should use `process-unmapped-keys (all-except lctl ralt))`,
or use `process-unmapped-keys no`
**and** also omit both `ralt` and `lctl` from `defsrc`.

.Example:
Expand Down
34 changes: 33 additions & 1 deletion parser/src/cfg/defcfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use super::HashSet;
use super::{error::*, TrimAtomQuotes};
use crate::cfg::check_first_expr;
use crate::custom_action::*;
use crate::keys::*;
#[allow(unused)]
use crate::{anyhow_expr, anyhow_span, bail, bail_expr, bail_span};

Expand Down Expand Up @@ -117,6 +118,7 @@ impl Default for CfgOptionsGui {
#[derive(Debug)]
pub struct CfgOptions {
pub process_unmapped_keys: bool,
pub process_unmapped_keys_exceptions: Option<Vec<(OsCode, SExpr)>>,
pub block_unmapped_keys: bool,
pub allow_hardware_repeat: bool,
pub start_alias: Option<String>,
Expand Down Expand Up @@ -155,6 +157,7 @@ impl Default for CfgOptions {
fn default() -> Self {
Self {
process_unmapped_keys: false,
process_unmapped_keys_exceptions: None,
block_unmapped_keys: false,
allow_hardware_repeat: true,
start_alias: None,
Expand Down Expand Up @@ -663,8 +666,37 @@ pub fn parse_defcfg(expr: &[SExpr]) -> Result<CfgOptions> {

"process-unmapped-keys" => {
is_process_unmapped_keys_defined = true;
cfg.process_unmapped_keys = parse_defcfg_val_bool(val, label)?
if let Some(list) = val.list(None) {
let err = "Expected (all-except key1 ... keyN).";
if list.len() < 2 {
bail_expr!(val, "{err}");
}
match list[0].atom(None) {
Some("all-except") => {}
_ => {
bail_expr!(val, "{err}");
}
};
// Note: deflocalkeys should already be parsed when parsing defcfg,
// so can use safely use str_to_oscode here; it will include user
// configurations already.
let mut key_exceptions: Vec<(OsCode, SExpr)> = vec![];
for key_expr in list[1..].iter() {
let key = key_expr.atom(None).and_then(str_to_oscode).ok_or_else(
|| anyhow_expr!(key_expr, "Expected a known key name."),
)?;
if key_exceptions.iter().any(|k_exc| k_exc.0 == key) {
bail_expr!(key_expr, "Duplicate key name is not allowed.");
}
key_exceptions.push((key, key_expr.clone()));
}
cfg.process_unmapped_keys = true;
cfg.process_unmapped_keys_exceptions = Some(key_exceptions);
} else {
cfg.process_unmapped_keys = parse_defcfg_val_bool(val, label)?
}
}

"block-unmapped-keys" => {
cfg.block_unmapped_keys = parse_defcfg_val_bool(val, label)?
}
Expand Down
28 changes: 25 additions & 3 deletions parser/src/cfg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1138,14 +1138,33 @@ fn parse_defsrc(
ordered_codes.push(oscode.into());
}

let mapped_exceptions = match &defcfg.process_unmapped_keys_exceptions {
Some(excluded_keys) => {
for excluded_key in excluded_keys.iter() {
log::debug!("process unmapped keys exception: {:?}", excluded_key);
if mkeys.contains(&excluded_key.0) {
bail_expr!(&excluded_key.1, "Keys cannot be included in defsrc and also excepted in process-unmapped-keys.");
}
}

excluded_keys
.iter()
.map(|excluded_key| excluded_key.0)
.collect()
}
None => vec![],
};

log::info!("process unmapped keys: {}", defcfg.process_unmapped_keys);
if defcfg.process_unmapped_keys {
for osc in 0..KEYS_IN_ROW as u16 {
if let Some(osc) = OsCode::from_u16(osc) {
match KeyCode::from(osc) {
KeyCode::No => {}
_ => {
mkeys.insert(osc);
if !mapped_exceptions.contains(&osc) {
mkeys.insert(osc);
}
}
}
}
Expand Down Expand Up @@ -3827,11 +3846,14 @@ fn parse_unmod(
.ok_or_else(|| {
anyhow_expr!(
mod_key,
"{UNMOD} expects modifier key names within the modifier list"
"{UNMOD} expects modifier key names within the modifier list."
)
})?;
if !(mod_flags & flag).is_empty() {
bail_expr!(mod_key, "duplicate key name in modifier key list");
bail_expr!(
mod_key,
"Duplicate key name in modifier key list is not allowed."
);
}
Ok::<_, ParseError>(mod_flags | flag)
})?;
Expand Down
1 change: 1 addition & 0 deletions parser/src/cfg/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use kanata_keyberon::action::BooleanOperator::*;
use std::sync::{Mutex, MutexGuard};

mod ambiguous;
mod defcfg;
mod device_detect;
mod environment;
mod macros;
Expand Down
101 changes: 101 additions & 0 deletions parser/src/cfg/tests/defcfg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use super::*;

#[test]
fn disallow_same_key_in_defsrc_unmapped_except() {
let source = "
(defcfg process-unmapped-keys (all-except bspc))
(defsrc bspc)
(deflayermap (name) 0 0)
";
parse_cfg(source)
.map(|_| ())
//.map_err(|e| eprintln!("{:?}", miette::Error::from(e)))
.expect_err("fails");
}

#[test]
fn unmapped_except_keys_cannot_have_dupes() {
let source = "
(defcfg process-unmapped-keys (all-except bspc bspc))
(defsrc)
(deflayermap (name) 0 0)
";
parse_cfg(source)
.map(|_| ())
//.map_err(|e| eprintln!("{:?}", miette::Error::from(e)))
.expect_err("fails");
}

#[test]
fn unmapped_except_keys_must_be_known() {
let source = "
(defcfg process-unmapped-keys (all-except notakey))
(defsrc)
(deflayermap (name) 0 0)
";
parse_cfg(source)
.map(|_| ())
//.map_err(|e| eprintln!("{:?}", miette::Error::from(e)))
.expect_err("fails");
}

#[test]
fn unmapped_except_keys_respects_deflocalkeys() {
let source = "
(deflocalkeys-win lkey90 555)
(deflocalkeys-winiov2 lkey90 555)
(deflocalkeys-wintercept lkey90 555)
(deflocalkeys-linux lkey90 555)
(deflocalkeys-macos lkey90 555)
(defcfg process-unmapped-keys (all-except lkey90))
(defsrc)
(deflayermap (name) 0 0)
";
let cfg = parse_cfg(source)
.map_err(|e| eprintln!("{:?}", miette::Error::from(e)))
.expect("passes");
assert!(!cfg.mapped_keys.contains(&OsCode::from(555u16)));
assert!(cfg.mapped_keys.contains(&OsCode::KEY_ENTER));
for osc in 0..KEYS_IN_ROW as u16 {
if let Some(osc) = OsCode::from_u16(osc) {
match KeyCode::from(osc) {
KeyCode::No | KeyCode::K555 => {
assert!(!cfg.mapped_keys.contains(&osc));
}
_ => {
assert!(cfg.mapped_keys.contains(&osc));
}
}
}
}
}

#[test]
fn unmapped_except_keys_is_removed_from_mapping() {
let source = "
(defcfg process-unmapped-keys (all-except 1 2 3))
(defsrc)
(deflayermap (name) 0 0)
";
let cfg = parse_cfg(source)
.map_err(|e| eprintln!("{:?}", miette::Error::from(e)))
.expect("passes");
assert!(cfg.mapped_keys.contains(&OsCode::KEY_A));
assert!(cfg.mapped_keys.contains(&OsCode::KEY_0));
assert!(!cfg.mapped_keys.contains(&OsCode::KEY_1));
assert!(!cfg.mapped_keys.contains(&OsCode::KEY_2));
assert!(!cfg.mapped_keys.contains(&OsCode::KEY_3));
assert!(cfg.mapped_keys.contains(&OsCode::KEY_4));
for osc in 0..KEYS_IN_ROW as u16 {
if let Some(osc) = OsCode::from_u16(osc) {
match KeyCode::from(osc) {
KeyCode::No | KeyCode::Kb1 | KeyCode::Kb2 | KeyCode::Kb3 => {
assert!(!cfg.mapped_keys.contains(&osc));
}
_ => {
assert!(cfg.mapped_keys.contains(&osc));
}
}
}
}
}

0 comments on commit 72d90e0

Please sign in to comment.