Bug: Simultaneous/"stacked" `press-vkey`s results in unexpected behavior (including crashes)
Requirements
- I've searched platform-specific issues, issues and discussions to see if this has been reported before.
- My issue does not involve multiple simultaneous key presses, OR it does but I've confirmed it is not key rollover or ghosting.
Describe the bug
Simultaneous/"stacked" press-vkeys where there is no release-vkey associated results in a lot of unexpected behavior.
Take this example:
(defvirtualkeys testkey (multi
(macro S-9 t S-0)
(macro-repeat-release-cancel 10 a 1000 b 1000 c 1000 spc 1000)
)
)
(defalias
hold (on-press press-vkey testkey)
release (on-press release-vkey testkey)
)The appropriate behavior if you tap your @hold alias is "abc " repeatedly until you @release it, which works. A second @hold results in a single additional concurrent macro that does not repeat, outputting "(t)abc abc a(t)abbcc abc abc(t)a bc abc abc abc". The additional macro runs simultaneously but has different behavior from the first press as it stops after one cycle.
Simultaneous presses where vkey emits a cmd action will emit the action properly until reaching approximately 70 presses. At this point, any key tapped before the release of another will be dropped (effectively 1-key rollover) despite the debug log showing all the appropriate IO events.
While simultaneous presses of a cmd action work until press ~70, the release fails much sooner. At 17 "stacked" vkeys, performing a release-vkey action succeeds and the key is released. At 18 "stacked" vkeys, performing a release-vkey action crashes.
For a press-vkey where the vkey outputs a layer-while-held, the crash happens on the 14th press (no release).
Relevant kanata config
(defcfg
danger-enable-cmd yes
process-unmapped-keys yes
)
(defsrc)
(defvirtualkeys
testmacro (multi
(macro S-9 t S-0)
(macro-repeat-release-cancel 10 a 1000 b 1000 c 1000 spc 1000))
testcmd (cmd echo "pressed")
testlayer (layer-while-held dummylayer)
)
(deflayermap base
[ (on-press press-vkey testmacro)
] (on-press release-vkey testmacro)
- (on-press press-vkey testcmd)
= (on-press release-vkey testcmd)
pgup (on-press press-vkey testlayer)
pgdn (on-press release-vkey testlayer)
)
(deflayermap dummylayer
pgup (on-press press-vkey testlayer)
pgdn (on-press release-vkey testlayer)
)To Reproduce
(With above config:) Press [ twice to see concurrent macro issue.
Press - ~70-80 times and then try typing a capital letter or very quickly for dropped input.
Press - 18 times, then = for crash.
Press pgup 14 times for crash.
Expected behavior
I know these are "virtual" but I would still expect similar behavior to a real keyboard where the state of a key is either off or on, and "changing" it from on to on is equivalent to a nop. A key having potential states of on, on on, on on on (etc) is surprising and difficult to reason about, especially if there are timers involved.
Is the best solution to align vkey behavior with a mechanical switch? If the switch is already pressed, do nothing. This shortcircuits all of these issues, of which I doubt anyone is relying upon because the behavior is pretty unpredictable and I could not find other open issues like it. If someone's intention is for multiple physical keys to set the same vkey "flag" for e.g. switching upon, this works fine as the flag remains unchanged. If someone's intention is for a vkey press to perform an action on every press regardless of its state, this is covered by multiing that action directly rather than using a vkey. I don't see a situation in which stacking the same held vkey is beneficial, especially enough to try solving these issues individually rather than in one fell swoop.
Kanata version
kanata 1.12.0
Debug logs
Performing a release-vkey on a cmd action after 18 stacks:
thread '<unnamed>' (2288758) panicked at keyberon/src/layout.rs:386:29:
overflowed custom action release queue
stack backtrace:
0: 0x55af0252a6a9 - <<std[52919eca6bce4da3]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[18c8dd30382e7099]::fmt::Display>::fmt
1: 0x55af02464e54 - core[18c8dd30382e7099]::fmt::write
2: 0x55af02529d41 - <std[52919eca6bce4da3]::sys::stdio::unix::Stderr as std[52919eca6bce4da3]::io::Write>::write_fmt
3: 0x55af02529a48 - std[52919eca6bce4da3]::panicking::panic_with_hook
4: 0x55af0254f598 - std[52919eca6bce4da3]::panicking::panic_handler::{closure#0}
5: 0x55af0254f519 - std[52919eca6bce4da3]::sys::backtrace::__rust_end_short_backtrace::<std[52919eca6bce4da3]::panicking::panic_handler::{closure#0}, !>
6: 0x55af0254fa0c - __rustc[8068f81614cfe5c]::rust_begin_unwind
7: 0x55af02464ceb - core[18c8dd30382e7099]::panicking::panic_fmt
8: 0x55af024d3b7b - kanata_keyberon::layout::State<T>::release::h8731106ab16e09ac
9: 0x55af024d9200 - heapless::vec::Vec<T,_>::retain::{{closure}}::ha9c70fe88dfab357
10: 0x55af024d6b79 - kanata_keyberon::layout::Layout<_,_,T>::dequeue::h5317011d83557e39
11: 0x55af024ecf2f - kanata_state_machine::kanata::Kanata::handle_keystate_changes::h94fa1530df3981be
12: 0x55af024e907c - kanata_state_machine::kanata::Kanata::tick_states::h27e4e3f1b66e80d0
13: 0x55af024e8884 - kanata_state_machine::kanata::Kanata::handle_time_ticks::h89e4e48c4ba8dd9a
14: 0x55af024e71cc - std::sys::backtrace::__rust_begin_short_backtrace::hef40d65a44e59a52
15: 0x55af024e6c33 - core::ops::function::FnOnce::call_once{{vtable.shim}}::hb0830d6121135088
16: 0x55af02550da8 - <std[52919eca6bce4da3]::sys::thread::unix::Thread>::new::thread_start
17: 0x7f3913460c19 - start_thread
18: 0x7f39134e45cc - __clone3
19: 0x0 - <unknown>
Pressing 14 of a vkey that emits layer-while-held:
thread '<unnamed>' (2332079) panicked at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/heapless-0.7.17/src/vec.rs:935:30:
Vec::from_iter overflow
stack backtrace:
0: 0x55b3460ad6a9 - <<std[52919eca6bce4da3]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[18c8dd30382e7099]::fmt::Display>::fmt
1: 0x55b345fe7e54 - core[18c8dd30382e7099]::fmt::write
2: 0x55b3460acd41 - <std[52919eca6bce4da3]::sys::stdio::unix::Stderr as std[52919eca6bce4da3]::io::Write>::write_fmt
3: 0x55b3460aca48 - std[52919eca6bce4da3]::panicking::panic_with_hook
4: 0x55b3460d2567 - std[52919eca6bce4da3]::panicking::panic_handler::{closure#0}
5: 0x55b3460d2519 - std[52919eca6bce4da3]::sys::backtrace::__rust_end_short_backtrace::<std[52919eca6bce4da3]::panicking::panic_handler::{closure#0}, !>
6: 0x55b3460d2a0c - __rustc[8068f81614cfe5c]::rust_begin_unwind
7: 0x55b345fe7ceb - core[18c8dd30382e7099]::panicking::panic_fmt
8: 0x55b345fec543 - core[18c8dd30382e7099]::option::expect_failed
9: 0x55b34605a7f5 - kanata_keyberon::layout::Layout<_,_,T>::trans_resolution_layer_order::h49da2a070b12b40a
10: 0x55b3460599c7 - kanata_keyberon::layout::Layout<_,_,T>::dequeue::h5317011d83557e39
11: 0x55b34606ff2f - kanata_state_machine::kanata::Kanata::handle_keystate_changes::h94fa1530df3981be
12: 0x55b34606c07c - kanata_state_machine::kanata::Kanata::tick_states::h27e4e3f1b66e80d0
13: 0x55b34606b884 - kanata_state_machine::kanata::Kanata::handle_time_ticks::h89e4e48c4ba8dd9apressed
14: 0x55b34606a8fa - std::sys::backtrace::__rust_begin_short_backtrace::hef40d65a44e59a52
15: 0x55b346069c33 - core::ops::function::FnOnce::call_once{{vtable.shim}}::hb0830d6121135088
16: 0x55b3460d3da8 - <std[52919eca6bce4da3]::sys::thread::unix::Thread>::new::thread_start
17: 0x7f898c144c19 - start_thread
18: 0x7f898c1c85cc - __clone3
19: 0x0 - <unknown>Operating system and I/O mechanism
Linux
Additional context
No response
Source: jtroo/kanata