fix(osm): keep OSM/OSL timeout alive across non-consuming events - #866
fix(osm): keep OSM/OSL timeout alive across non-consuming events#866yekingyan wants to merge 3 commits into
Conversation
Size Report
|
7c1d906 to
c74a484
Compare
f48b908 to
2fd78c1
Compare
The OSM/OSL release path used a single select(timeout, next_event), so the first event arriving during the timeout cancelled the wait permanently, leaving the one-shot stuck in Single (never auto-released). A typical trigger is the release event of a layer key. Replace the single select with a deadline loop: non-consuming events are pushed to the queue and we keep waiting for the remaining time, while a consuming event (OSM: press in quick_release mode, release otherwise; OSL: press) breaks out. Additionally, check unprocessed_events before entering the await loop — if a consuming event was already dequeued from the channel, the subscriber would never see it, causing the OSM to hang indefinitely.
20a9693 to
055fd99
Compare
| match select(timeout, self.keyboard_event_subscriber.next_message_pure()).await { | ||
| Either::First(_) => { | ||
| // Timeout, deactivate layer | ||
| self.keymap.deactivate_layer(layer_num); |
There was a problem hiding this comment.
Should layer_num be l in OneShotState::Initial(l) | OneShotState::Single(l)?
There was a problem hiding this comment.
Should
layer_numbelinOneShotState::Initial(l) | OneShotState::Single(l)?
Good catch — should be l.
Just pushed a fix (0610cb4c) that uses the match-bound l instead of the outer layer_num parameter.
They're numerically equal on every current call path, so there's no behavior change today, but l is the semantically correct value and it's more robust if that invariant ever changes.
…aram In the OSL release path, the Initial(l) | Single(l) arm was calling self.keymap.deactivate_layer(layer_num) using the outer function parameter instead of the layer number l bound by the match arm. Currently these happen to be numerically equal on the only call path, but relying on that is fragile and semantically wrong: the deactivated layer should always be the one actually stored in osl_state, not the layer passed into this invocation of process_action_osl. Addresses review comment from @HaoboGu on PR rmk-rs#866.
Problem
The OSM/OSL release path waited with a single
select(timeout, next_event).The first event arriving during the timeout cancelled the wait permanently,
so the one-shot got stuck in
Singleand never auto-released. A typicaltrigger is the release event of a layer key (a non-consuming event).
Additionally, when a consuming event was already sitting in
unprocessed_events(dequeued from the channel before entering the wait), the subscriber would
never deliver it again, causing the OSM to hang indefinitely (e.g. fast
Ctrl+Shift+P sequences).
Fix
Replace the single
selectwith a deadline loop: non-consuming events arepushed to the queue and we keep waiting for the remaining time, while a
consuming event breaks out (OSM: press in quick_release mode, release
otherwise; OSL: press).
Before entering the loop, check whether
unprocessed_eventsalready containsa consuming event and skip the await entirely if so.
Test
Added
test_osm_timeout_not_cancelled_by_non_consuming_event: an OSL pressinterrupts the OSM timeout; after the timeout elapses the OSM must have
auto-released, so the later layer-1 key is sent without the modifier.
Red before the fix, green after.