Skip to content

Commit

Permalink
Make multi action work with non-keyberon actions
Browse files Browse the repository at this point in the history
Due to how the keyberon library works, only one Custom action will be
returned per tick. This means that for a multi action, kanata will only
ever get back one Custom action from the keyberon library. As a first
cut attempt to work around the issue, this commit adds Multi versions of
the cmd, unicode, and mouse actions which are handled differently.

An alternative solution could be to make the custom action used with
keyberon be a `&'static [&'static CustomAction]`. However this would
have required more changes and I felt that the functionality that this
enables wouldn't be of any use in practice.
  • Loading branch information
jtroo committed Jul 17, 2022
1 parent a656067 commit 1775d80
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 12 deletions.
42 changes: 41 additions & 1 deletion src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -802,15 +802,55 @@ fn parse_timeout(a: &SExpr) -> Result<u16> {
}
}

#[derive(Debug)]
enum MultiCustom {
Unset,
Cmd(Vec<&'static [String]>),
Unicode(Vec<char>),
Mouse(Vec<Btn>),
}

fn parse_multi(ac_params: &[SExpr], parsed_state: &ParsedState) -> Result<&'static KanataAction> {
if ac_params.is_empty() {
bail!("multi expects at least one atom after it")
}
let mut actions = Vec::new();
let mut multi_custom = MultiCustom::Unset;
for expr in ac_params {
let ac = parse_action(expr, parsed_state)?;
actions.push(*ac);
match ac {
Action::Custom(ac) => {
match (ac, &mut multi_custom) {
(CustomAction::LiveReload, _) => bail!("Live reload not supported in multi action"),
(_, MultiCustom::Unset) => match ac {
CustomAction::Cmd(ac) => multi_custom = MultiCustom::Cmd(vec![ac]),
CustomAction::Unicode(ac) => multi_custom = MultiCustom::Unicode(vec![*ac]),
CustomAction::Mouse(ac) => multi_custom = MultiCustom::Mouse(vec![*ac]),
_ => bail!("Unsupported action in multi: {:?}", ac),
}
(CustomAction::Cmd(ac), MultiCustom::Cmd(acs)) => acs.push(ac),
(CustomAction::Unicode(ac), MultiCustom::Unicode(acs)) => acs.push(*ac),
(CustomAction::Mouse(ac), MultiCustom::Mouse(acs)) => acs.push(*ac),
_ => bail!("For multi, only one type of the following actions is allowed: (mouse clicks),(unicode),(cmd)\nYou can have multiple mouse clicks, multiple unicode, or multiple cmd, but not e.g. one mouse click and one cmd."),
}
}
_ => actions.push(*ac),
}
}

match multi_custom {
MultiCustom::Cmd(v) => actions.push(Action::Custom(CustomAction::MultiCmd(Box::leak(
v.into_boxed_slice(),
)))),
MultiCustom::Unicode(v) => actions.push(Action::Custom(CustomAction::MultiUnicode(
Box::leak(v.into_boxed_slice()),
))),
MultiCustom::Mouse(v) => actions.push(Action::Custom(CustomAction::MultiMouse(Box::leak(
v.into_boxed_slice(),
)))),
MultiCustom::Unset => {}
}

Ok(sref(Action::MultipleActions(sref(actions))))
}

Expand Down
3 changes: 3 additions & 0 deletions src/custom_action.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CustomAction {
Cmd(&'static [String]),
MultiCmd(&'static [&'static [String]]),
Unicode(char),
MultiUnicode(&'static [char]),
Mouse(Btn),
MultiMouse(&'static [Btn]),
LiveReload,
}

Expand Down
63 changes: 52 additions & 11 deletions src/kanata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ impl Kanata {
// For unicode, only send on the press. No repeat action is supported for this for
// now.
CustomAction::Unicode(c) => self.kbd_out.send_unicode(*c)?,
CustomAction::MultiUnicode(chars) => {
for c in chars.iter() {
self.kbd_out.send_unicode(*c)?;
}
}
CustomAction::LiveReload => {
live_reload_requested = true;
log::info!("Requested live reload")
Expand All @@ -140,14 +145,36 @@ impl Kanata {
log::debug!("press {:?}", btn);
self.kbd_out.click_btn(*btn)?;
}
CustomAction::MultiMouse(btns) => {
assert!(!btns.is_empty());
for i in 0..btns.len() - 1 {
let btn = btns[i];
log::debug!("press {:?}", btn);
self.kbd_out.click_btn(btn)?;
log::debug!("release {:?}", btn);
self.kbd_out.release_btn(btn)?;
}
let btn = btns[btns.len() - 1];
log::debug!("press {:?}", btn);
self.kbd_out.click_btn(btn)?;
}
CustomAction::Cmd(cmd) => {
run_cmd(cmd);
}
CustomAction::MultiCmd(cmds) => {
run_multi_cmd(cmds);
}
},
CustomEvent::Release(CustomAction::Mouse(btn)) => {
log::debug!("release {:?}", btn);
self.kbd_out.release_btn(*btn)?;
}
CustomEvent::Release(CustomAction::MultiMouse(btns)) => {
assert!(!btns.is_empty());
let btn = btns[btns.len() - 1];
log::debug!("release {:?}", btn);
self.kbd_out.release_btn(btn)?;
}
_ => {}
};
Ok(live_reload_requested)
Expand Down Expand Up @@ -372,7 +399,7 @@ impl Kanata {
}
KeyValue::Press => {
let mut pressed_keys = PRESSED_KEYS.lock();
if pressed_keys.contains(&key_event.code) {
if pressed_keys.contains(&key_event.code) {
key_event.value = KeyValue::Repeat;
} else {
pressed_keys.insert(key_event.code);
Expand All @@ -399,17 +426,16 @@ impl Kanata {
}

#[cfg(feature = "cmd")]
fn run_cmd(cmd_and_args: &[String]) {
let mut args = cmd_and_args.iter().cloned();
let mut cmd = std::process::Command::new(
args.next()
.expect("Parsing should have forbidden empty cmd"),
);
for arg in args {
cmd.arg(arg);
}

fn run_cmd(cmd_and_args: &'static [String]) -> std::thread::JoinHandle<()> {
std::thread::spawn(move || {
let mut args = cmd_and_args.iter().cloned();
let mut cmd = std::process::Command::new(
args.next()
.expect("Parsing should have forbidden empty cmd"),
);
for arg in args {
cmd.arg(arg);
}
match cmd.output() {
Ok(output) => {
log::info!(
Expand All @@ -429,8 +455,23 @@ fn run_cmd(cmd_and_args: &[String]) {
}
Err(e) => log::error!("Failed to execute cmd: {}", e),
};
})
}

#[cfg(feature = "cmd")]
fn run_multi_cmd(cmds: &'static [&'static [String]]) {
let cmds = cmds.clone();
std::thread::spawn(move || {
for cmd in cmds {
if let Err(e) = run_cmd(cmd).join() {
log::error!("problem joining thread {:?}", e);
}
}
});
}

#[cfg(not(feature = "cmd"))]
fn run_cmd(_cmd_and_args: &[String]) {}

#[cfg(not(feature = "cmd"))]
fn run_multi_cmd(_cmds: &'static [&'static [String]]) {}

0 comments on commit 1775d80

Please sign in to comment.