Skip to content

Commit

Permalink
feat(fake-keys): add fake keys feature
Browse files Browse the repository at this point in the history
This commit adds full functionality of the fake keys feature. Examples
and descriptions of intended use cases are added to the kanata.kbd file.
  • Loading branch information
jtroo committed Aug 4, 2022
1 parent 044cf59 commit 0eac5ff
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 10 deletions.
63 changes: 62 additions & 1 deletion cfg_samples/kanata.kbd
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
(deflayer dvorak
@grl 1 2 3 4 5 6 7 8 9 0 [ ] bspc
tab ' , @.ms p y f g c r l / = \
@cap @anm @oar @ech @umc i d h t n s - ret
@cap @anm @oar @ech @umc @ifk d h t n s - ret
lsft ; q j k x b m w v z rsft
lctl lmet lalt spc @ralt rmet rctl
)
Expand All @@ -109,6 +109,7 @@
msc (layer-toggle misc)
lay (layer-toggle layers)
mse (layer-toggle mouse)
fks (layer-toggle fakekeys)

;; tap-hold aliases with tap for dvorak key, and hold for toggle layers
;;
Expand Down Expand Up @@ -149,6 +150,7 @@
umc (tap-hold 200 200 u @msc) ;; tap: u hold: misc layer
grl (tap-hold 200 200 grv @lay) ;; tap: grave hold: layers layer
.ms (tap-hold 200 200 . @mse) ;; tap: . hold: mouse layer
ifk (tap-hold 200 200 i @fks) ;; tap: i hold: fake keys layer

;; tap for capslk, hold for lctl
cap (tap-hold 200 200 caps lctl)
Expand Down Expand Up @@ -359,3 +361,62 @@
_ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _
)

;; Press and release fake keys.
;;
;; Fake keys can't be pressed by any physical keyboard buttons and can only be
;; acted upon by the actions on-press-fake-key-op and on-release-fake-key-op.
;; The purpose of fake keys is for a use case such as holding modifier keys for
;; any number of keypresses and then releasing the modifiers when desired.
;;
;; The actions associated with fake keys in deffakekeys are parsed before
;; aliases, so you can't use aliases within deffakekeys. Other than the lack
;; of alias support, fake keys can do any action that a normal key can,
;; including doing operations on previously defined fake keys.
;;
;; Operations on fake keys can occur either on press (on-press-fake-key-op) or
;; on release (on-release-fake-key-op). The use cases for the on-release variant
;; are left up to your own creativity.
(deflayer fakekeys
_ @fcp @fsp @fmp @pal _ _ _ _ _ _ _ _ _
_ @fcr @fsr @fap @ral _ _ _ _ _ _ _ _ _
_ @fct @fst @rma _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _
)

(deffakekeys
ctl lctl
sft lsft
met lmet
alt lalt
pal (multi
(on-press-fake-key-op ctl press)
(on-press-fake-key-op sft press)
(on-press-fake-key-op met press)
(on-press-fake-key-op alt press)
)
ral (multi
(on-press-fake-key-op ctl release)
(on-press-fake-key-op sft release)
(on-press-fake-key-op met release)
(on-press-fake-key-op alt release)
)
)

(defalias
fcp (on-press-fake-key-op ctl press)
fcr (on-press-fake-key-op ctl release)
fct (on-press-fake-key-op ctl tap)
fsp (on-release-fake-key-op sft press)
fsr (on-release-fake-key-op sft release)
fst (on-release-fake-key-op sft tap)
fmp (on-press-fake-key-op met press)
fap (on-press-fake-key-op alt press)
rma (multi
(on-press-fake-key-op met release)
(on-press-fake-key-op alt release)
)
pal (on-press-fake-key-op pal tap)
ral (on-press-fake-key-op ral tap)
)
114 changes: 111 additions & 3 deletions src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use kanata_keyberon::key_code::*;
use kanata_keyberon::layout::*;

pub type KanataAction = Action<&'static [&'static CustomAction]>;
pub type KanataLayout = Layout<256, 1, ACTUAL_NUM_LAYERS, &'static [&'static CustomAction]>;
pub type KanataLayout = Layout<256, 2, ACTUAL_NUM_LAYERS, &'static [&'static CustomAction]>;

pub struct Cfg {
pub mapped_keys: MappedKeys,
Expand Down Expand Up @@ -301,7 +301,6 @@ fn parse_cfg_raw(
let defsrc_layer = parse_defsrc_layer(src_expr, &mapping_order);
let mut parsed_state = ParsedState {
layer_exprs,
aliases: Default::default(),
layer_idxs,
mapping_order,
defsrc_layer,
Expand All @@ -323,7 +322,15 @@ fn parse_cfg_raw(
false
}
},
..Default::default()
};

let fake_keys_exprs = root_exprs
.iter()
.filter(gen_first_atom_filter("deffakekeys"))
.collect::<Vec<_>>();
parse_fake_keys(&fake_keys_exprs, &mut parsed_state)?;

parse_aliases(&alias_exprs, &mut parsed_state)?;

let klayers = parse_layers(&parsed_state)?;
Expand Down Expand Up @@ -627,6 +634,7 @@ struct ParsedState<'a> {
aliases: Aliases,
layer_idxs: LayerIndexes,
mapping_order: Vec<usize>,
fake_keys: HashMap<String, (usize, &'static KanataAction)>,
defsrc_layer: [KanataAction; 256],
is_cmd_enabled: bool,
}
Expand All @@ -639,6 +647,7 @@ impl<'a> Default for ParsedState<'a> {
layer_idxs: Default::default(),
mapping_order: Default::default(),
defsrc_layer: empty_layer!(),
fake_keys: Default::default(),
is_cmd_enabled: false,
}
}
Expand All @@ -657,7 +666,7 @@ fn parse_aliases(exprs: &[&Vec<SExpr>], parsed_state: &mut ParsedState) -> Resul
while let Some(alias) = subexprs.next() {
let action = match subexprs.next() {
Some(v) => v,
None => bail!("Incorrect number of elements found in defcfg; they should be pairs of aliases and actions."),
None => bail!("Incorrect number of elements found in defalias; they should be pairs of aliases and actions."),
};
let alias = match alias {
SExpr::Atom(a) => a,
Expand Down Expand Up @@ -800,6 +809,8 @@ fn parse_action_list(ac: &[SExpr], parsed_state: &ParsedState) -> Result<&'stati
"tap-dance" => parse_tap_dance(&ac[1..], parsed_state),
"release-key" => parse_release_key(&ac[1..], parsed_state),
"release-layer" => parse_release_layer(&ac[1..], parsed_state),
"on-press-fake-key-op" => parse_fake_key_op(&ac[1..], parsed_state),
"on-release-fake-key-op" => parse_on_release_fake_key_op(&ac[1..], parsed_state),
"cmd" => parse_cmd(&ac[1..], parsed_state.is_cmd_enabled),
_ => bail!(
"Unknown action type: {}. Valid types:\n\tlayer-switch\n\tlayer-toggle\n\ttap-hold\n\ttap-hold-press\n\ttap-hold-release\n\tmulti\n\tmacro\n\tunicode\n\tone-shot\n\ttap-dance\n\trelease-key\n\trelease-layer\n\tcmd",
Expand Down Expand Up @@ -1096,6 +1107,100 @@ fn parse_defsrc_layer(defsrc: &[SExpr], mapping_order: &[usize]) -> [KanataActio
layer
}

fn parse_fake_keys(exprs: &[&Vec<SExpr>], parsed_state: &mut ParsedState) -> Result<()> {
for expr in exprs {
let mut subexprs = match check_first_expr(expr.iter(), "deffakekeys") {
Ok(s) => s,
Err(e) => bail!(e),
};

// Read k-v pairs from the configuration
while let Some(key_name) = subexprs.next() {
let action = match subexprs.next() {
Some(v) => v,
None => bail!("Incorrect number of elements found in deffakekeys; they should be pairs of key-names and actions."),
};
let key_name = match key_name {
SExpr::Atom(a) => a,
_ => bail!(
"fake key names must be atoms. Invalid key name: {:?}",
key_name
),
};
let action = parse_action(action, parsed_state)?;
let idx = parsed_state.fake_keys.len();
if parsed_state
.fake_keys
.insert(key_name.into(), (idx, action))
.is_some()
{
bail!("Duplicate fake key: {}", key_name);
}
}
}
if parsed_state.fake_keys.len() > KEYS_IN_ROW {
bail!(
"Maximum number of fake keys is {KEYS_IN_ROW}, found {}",
parsed_state.fake_keys.len()
);
}
Ok(())
}

fn parse_fake_key_op(
ac_params: &[SExpr],
parsed_state: &ParsedState,
) -> Result<&'static KanataAction> {
let (coord, action) = parse_fake_key_op_coord_action(ac_params, parsed_state)?;
Ok(sref(Action::Custom(sref_slice(CustomAction::FakeKey {
coord,
action,
}))))
}

fn parse_on_release_fake_key_op(
ac_params: &[SExpr],
parsed_state: &ParsedState,
) -> Result<&'static KanataAction> {
let (coord, action) = parse_fake_key_op_coord_action(ac_params, parsed_state)?;
Ok(sref(Action::Custom(sref_slice(
CustomAction::FakeKeyOnRelease { coord, action },
))))
}

fn parse_fake_key_op_coord_action(
ac_params: &[SExpr],
parsed_state: &ParsedState,
) -> Result<(Coord, FakeKeyAction)> {
const ERR_MSG: &str = "fake-key-op expects two parameters: <fake key name> <operation>\n\tvalid operations: tap, press, release";
if ac_params.len() != 2 {
bail!("{ERR_MSG}");
}
let y = match parsed_state.fake_keys.get(match &ac_params[0] {
SExpr::Atom(fake_key_name) => fake_key_name,
_ => bail!(
"{ERR_MSG}\n\tinvalid first parameter (list): {:?}",
&ac_params[0]
),
}) {
Some((y, _)) => *y as u8, // cast should be safe; checked in `parse_fake_keys`
None => bail!("unknown fake key name {:?}", &ac_params[0]),
};
let action = match &ac_params[1] {
SExpr::Atom(op) => match op.as_str() {
"tap" => FakeKeyAction::Tap,
"press" => FakeKeyAction::Press,
"release" => FakeKeyAction::Release,
_ => bail!("{ERR_MSG}\n\tinvalid second parameter: {:?}", op),
},
_ => bail!(
"{ERR_MSG}\n\tinvalid second parameter (list): {:?}",
ac_params[1]
),
};
Ok((Coord { x: 1, y }, action))
}

/// Mutates `layers::LAYERS` using the inputs.
fn parse_layers(parsed_state: &ParsedState) -> Result<Box<KanataLayers>> {
let mut layers_cfg = new_layers();
Expand All @@ -1114,6 +1219,9 @@ fn parse_layers(parsed_state: &ParsedState) -> Result<Box<KanataLayers>> {
*layer_action = defsrc_action;
}
}
for (y, action) in parsed_state.fake_keys.values() {
layers_cfg[layer_level][1][*y] = **action;
}
}
Ok(layers_cfg)
}
Expand Down
15 changes: 15 additions & 0 deletions src/custom_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pub enum CustomAction {
Unicode(char),
Mouse(Btn),
MouseTap(Btn),
FakeKey { coord: Coord, action: FakeKeyAction },
FakeKeyOnRelease { coord: Coord, action: FakeKeyAction },
LiveReload,
}

Expand All @@ -13,3 +15,16 @@ pub enum Btn {
Right,
Mid,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Coord {
pub x: u8,
pub y: u8,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FakeKeyAction {
Press,
Release,
Tap,
}
26 changes: 26 additions & 0 deletions src/kanata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,19 @@ impl Kanata {
CustomAction::Cmd(cmd) => {
cmds.push(*cmd);
}
CustomAction::FakeKey { coord, action } => {
let (x, y) = (coord.x, coord.y);
log::debug!("fake key on press {action:?} {x:?},{y:?}");
match action {
FakeKeyAction::Press => self.layout.event(Event::Press(x, y)),
FakeKeyAction::Release => self.layout.event(Event::Release(x, y)),
FakeKeyAction::Tap => {
self.layout.event(Event::Press(x, y));
self.layout.event(Event::Release(x, y));
}
}
}
_ => {}
}
}
run_multi_cmd(cmds);
Expand All @@ -194,6 +207,19 @@ impl Kanata {
.iter()
.fold(None, |pbtn, ac| match ac {
CustomAction::Mouse(btn) => Some(btn),
CustomAction::FakeKeyOnRelease { coord, action } => {
let (x, y) = (coord.x, coord.y);
log::debug!("fake key on release {action:?} {x:?},{y:?}");
match action {
FakeKeyAction::Press => self.layout.event(Event::Press(x, y)),
FakeKeyAction::Release => self.layout.event(Event::Release(x, y)),
FakeKeyAction::Tap => {
self.layout.event(Event::Press(x, y));
self.layout.event(Event::Release(x, y));
}
}
pbtn
}
_ => pbtn,
})
.map(|btn| {
Expand Down
13 changes: 8 additions & 5 deletions src/layers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,18 +268,21 @@ macro_rules! empty_layer {

pub(crate) use empty_layer;

pub const KEYS_IN_ROW: usize = 256;
pub const LAYER_COLUMNS: usize = 2;
pub const MAX_LAYERS: usize = 25;
pub const ACTUAL_NUM_LAYERS: usize = MAX_LAYERS * 2;

pub type KanataLayers = Layers<256, 1, ACTUAL_NUM_LAYERS, &'static [&'static CustomAction]>;
pub type KanataLayers =
Layers<KEYS_IN_ROW, LAYER_COLUMNS, ACTUAL_NUM_LAYERS, &'static [&'static CustomAction]>;

type Row = [kanata_keyberon::action::Action<&'static [&'static CustomAction]>; KEYS_IN_ROW];

pub fn new_layers() -> Box<KanataLayers> {
let boxed_slice: Box<
[[kanata_keyberon::action::Action<&'static [&'static CustomAction]>; 256]],
> = {
let boxed_slice: Box<[[Row; LAYER_COLUMNS]]> = {
let mut layers = Vec::with_capacity(ACTUAL_NUM_LAYERS);
for _ in 0..ACTUAL_NUM_LAYERS {
layers.push(empty_layer!());
layers.push([empty_layer!(), empty_layer!()]);
}
layers
}
Expand Down
2 changes: 1 addition & 1 deletion src/oskbd/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl KbdIn {
pub fn read(&mut self) -> Result<Vec<InputEvent>, std::io::Error> {
let mut input_events = vec![];
loop {
log::debug!("polling");
log::trace!("polling");
if let Err(e) = self.poll.poll(&mut self.events, None) {
log::error!("failed poll: {:?}", e);
return Ok(vec![]);
Expand Down

0 comments on commit 0eac5ff

Please sign in to comment.