Skip to content

Commit

Permalink
Print layer as info log when layer changes
Browse files Browse the repository at this point in the history
The motivation behind this change is for getting accustomed to new
layout changes. The log may be of benefit so that the user can see what
layout they're currently on and what keys are associated with it.

The info-level logs are currently empty apart from initialization, so
may as well make it useful for something.

One current limitation is that any comments in the original layout file
won't exist in the printed text.

Additional changes:

- increment version
- refactor handle_time_ticks into multiple fns
- change to new kanata-keyberon version for pub fn current_layer
  • Loading branch information
jtroo committed Jun 5, 2022
1 parent 5d3f93a commit acb010b
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 49 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kanata"
version = "1.0.1"
version = "1.0.2"
authors = ["jtroo <[email protected]>"]
description = "Multi-layer keyboard customization"
keywords = ["cli", "linux", "windows", "keyboard", "layout"]
Expand All @@ -21,8 +21,11 @@ parking_lot = "0.12"
crossbeam-channel = "0.5"
once_cell = "1"

# Using my personal fork for tap_hold_interval and Sequence
kanata-keyberon = "0.2"
# Using my personal fork for:
# - tap_hold_interval
# - pub current_layer
# - Sequence
kanata-keyberon = "0.2.1"

[target.'cfg(target_os = "linux")'.dependencies]
evdev-rs = "0.4.0"
Expand Down
23 changes: 18 additions & 5 deletions src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,18 @@ pub type KanataLayout = Layout<256, 1, MAX_LAYERS, CustomAction>;
pub struct Cfg {
pub mapped_keys: MappedKeys,
pub key_outputs: KeyOutputs,
pub layer_strings: Vec<String>,
pub items: HashMap<String, String>,
pub layout: KanataLayout,
}

impl Cfg {
pub fn new_from_file(p: &std::path::Path) -> Result<Self> {
let (items, mapped_keys, key_outputs, layout) = parse_cfg(p)?;
let (items, mapped_keys, layer_strings, key_outputs, layout) = parse_cfg(p)?;
Ok(Self {
items,
mapped_keys,
layer_strings,
key_outputs,
layout,
})
Expand Down Expand Up @@ -119,15 +121,16 @@ fn parse_cfg(
) -> Result<(
HashMap<String, String>,
MappedKeys,
Vec<String>,
KeyOutputs,
KanataLayout,
)> {
let cfg = std::fs::read_to_string(p)?;

let root_expr_strs = get_root_exprs(&cfg)?;
let mut root_exprs = Vec::new();
for expr in root_expr_strs {
root_exprs.push(parse_expr(&expr)?);
for expr in root_expr_strs.iter() {
root_exprs.push(parse_expr(expr)?);
}

let cfg_expr = root_exprs
Expand Down Expand Up @@ -158,9 +161,10 @@ fn parse_cfg(
}
let (src, mapping_order) = parse_defsrc(src_expr)?;

let deflayer_filter = gen_first_atom_filter("deflayer");
let layer_exprs = root_exprs
.iter()
.filter(gen_first_atom_filter("deflayer"))
.filter(&deflayer_filter)
.collect::<Vec<_>>();
if layer_exprs.is_empty() {
bail!("No deflayer expressions exist. At least one layer must be defined.")
Expand All @@ -170,15 +174,24 @@ fn parse_cfg(
}
let layer_idxs = parse_layer_indexes(&layer_exprs, mapping_order.len())?;

let layer_strings = root_expr_strs
.into_iter()
.zip(root_exprs.iter())
.filter(|(_, expr)| deflayer_filter(expr))
.map(|(s, _)| s)
.collect::<Vec<_>>();

let alias_exprs = root_exprs
.iter()
.filter(gen_first_atom_filter("defalias"))
.collect::<Vec<_>>();
let aliases = parse_aliases(&alias_exprs, &layer_idxs)?;
let klayers = parse_layers(&layer_exprs, &aliases, &layer_idxs, &mapping_order)?;

Ok((
cfg,
src,
layer_strings,
create_key_outputs(&klayers),
create_layout(klayers),
))
Expand All @@ -187,7 +200,7 @@ fn parse_cfg(
/// Return a closure that filters a root expression by the content of the first element. The
/// closure returns true if the first element is an atom that matches the input `a` and false
/// otherwise.
fn gen_first_atom_filter(a: &str) -> impl FnMut(&&Vec<SExpr>) -> bool {
fn gen_first_atom_filter(a: &str) -> impl Fn(&&Vec<SExpr>) -> bool {
let a = a.to_owned();
move |expr: &&Vec<SExpr>| {
if expr.is_empty() {
Expand Down
117 changes: 79 additions & 38 deletions src/kanata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub struct Kanata {
pub key_outputs: cfg::KeyOutputs,
pub layout: cfg::KanataLayout,
pub prev_keys: Vec<KeyCode>,
pub layer_strings: Vec<String>,
pub prev_layer: usize,
last_tick: time::Instant,
}

Expand Down Expand Up @@ -67,7 +69,9 @@ impl Kanata {
mapped_keys: cfg.mapped_keys,
key_outputs: cfg.key_outputs,
layout: cfg.layout,
layer_strings: cfg.layer_strings,
prev_keys: Vec::new(),
prev_layer: 0,
last_tick: time::Instant::now(),
})
}
Expand All @@ -94,34 +98,62 @@ impl Kanata {
let now = time::Instant::now();
let ms_elapsed = now.duration_since(self.last_tick).as_millis();

let mut live_reload_requested = false;

for _ in 0..ms_elapsed {
live_reload_requested |= self.tick_get_custom_event()?;

let cur_keys = self.handle_keystate_changes()?;

if live_reload_requested && self.prev_keys.is_empty() && cur_keys.is_empty() {
live_reload_requested = false;
self.do_reload();
}

self.prev_keys = cur_keys;
}

if ms_elapsed > 0 {
self.last_tick = now;

// Handle layer change outside the loop. I don't see any practical scenario where it
// would make a difference, so may as well reduce the amount of processing.
self.check_handle_layer_change();
}
let mut live_reload_requested = false;

for _ in 0..ms_elapsed {
// Only send on the press. No repeat action is supported for this for the time being.
match self.layout.tick() {
CustomEvent::Press(custact) => match custact {
CustomAction::Unicode(c) => self.kbd_out.send_unicode(*c)?,
CustomAction::LiveReload => {
live_reload_requested = true;
log::info!("Requested live reload")
}
CustomAction::Mouse(btn) => {
log::debug!("press {:?}", btn);
self.kbd_out.click_btn(*btn)?;
}
},
CustomEvent::Release(CustomAction::Mouse(btn)) => {
log::debug!("release {:?}", btn);
self.kbd_out.release_btn(*btn)?;
Ok(())
}

/// Returns true if live reload is requested and false otherwise.
fn tick_get_custom_event(&mut self) -> Result<bool> {
let mut live_reload_requested = false;
match self.layout.tick() {
CustomEvent::Press(custact) => match custact {
// 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::LiveReload => {
live_reload_requested = true;
log::info!("Requested live reload")
}
_ => {}
CustomAction::Mouse(btn) => {
log::debug!("press {:?}", btn);
self.kbd_out.click_btn(*btn)?;
}
},
CustomEvent::Release(CustomAction::Mouse(btn)) => {
log::debug!("release {:?}", btn);
self.kbd_out.release_btn(*btn)?;
}
_ => {}
};
Ok(live_reload_requested)
}

/// Sends OS key events according to the change in key state between the current and the
/// previous keyberon keystate. Returns the current keys.
fn handle_keystate_changes(&mut self) -> Result<Vec<KeyCode>> {
let cur_keys: Vec<KeyCode> = self.layout.keycodes().collect();

// Release keys that are missing from the current state but exist in the previous
// state. It's important to iterate using a Vec because the order matters. This used to
// use HashSet force computing `difference` but that iteration order is random which is
Expand All @@ -146,26 +178,23 @@ impl Kanata {
bail!("failed to press key: {:?}", e);
}
}
Ok(cur_keys)
}

if live_reload_requested && self.prev_keys.is_empty() && cur_keys.is_empty() {
live_reload_requested = false;
match cfg::Cfg::new_from_file(&self.cfg_path) {
Err(e) => {
log::error!("Could not reload configuration:\n{}", e);
}
Ok(cfg) => {
self.layout = cfg.layout;
let mut mapped_keys = MAPPED_KEYS.lock();
*mapped_keys = cfg.mapped_keys;
self.key_outputs = cfg.key_outputs;
log::info!("Live reload successful")
}
};
fn do_reload(&mut self) {
match cfg::Cfg::new_from_file(&self.cfg_path) {
Err(e) => {
log::error!("Could not reload configuration:\n{}", e);
}

self.prev_keys = cur_keys;
}
Ok(())
Ok(cfg) => {
self.layout = cfg.layout;
let mut mapped_keys = MAPPED_KEYS.lock();
*mapped_keys = cfg.mapped_keys;
self.key_outputs = cfg.key_outputs;
self.layer_strings = cfg.layer_strings;
log::info!("Live reload successful")
}
};
}

/// This compares the active keys in the keyberon layout against the potential key outputs for
Expand Down Expand Up @@ -194,6 +223,18 @@ impl Kanata {
Ok(())
}

fn check_handle_layer_change(&mut self) {
let cur_layer = self.layout.current_layer();
if cur_layer != self.prev_layer {
self.prev_layer = cur_layer;
self.print_layer(cur_layer);
}
}

fn print_layer(&self, layer: usize) {
log::info!("Entered layer:\n{}", self.layer_strings[layer]);
}

/// Starts a new thread that processes OS key events and advances the keyberon layout's state.
pub fn start_processing_loop(kanata: Arc<Mutex<Self>>, rx: Receiver<KeyEvent>) {
info!("Kanata: entering the processing loop");
Expand Down

0 comments on commit acb010b

Please sign in to comment.