fix(capture): release peer keys on release-bind

When the user pressed the release-bind chord (typically all four
modifiers) the down events for the chord were forwarded to the peer
while capture was active, but the matching up events arrived after
the local tap flipped to passthrough and were never forwarded. The
peer was left with phantom held modifiers until either its watchdog
ran (1+ s) or the Leave message was processed — and Leave is sent
over UDP/DTLS and can be lost.

Drain the capture's pressed_keys set in release_capture and emit a
KeyboardEvent::Key{state: 0} for every still-held key, plus a
zeroed KeyboardEvent::Modifiers, before sending Leave. The receiver
already maintains pressed_keys per handle and processes these
key-up events through its normal path, so no protocol change is
required and an unmodified peer picks up the fix automatically.

The receiver-side release_keys safety net stays in place for the
genuine packet-loss / disconnect-without-Leave cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jon Kinney
2026-04-27 12:59:33 -05:00
committed by Ferdinand Schober
parent 10fd728804
commit 373e382152
2 changed files with 49 additions and 1 deletions

View File

@@ -171,6 +171,19 @@ impl InputCapture {
self.capture.release().await
}
/// Drain and return every key the capture has forwarded as
/// down-but-not-up. The caller is expected to synthesize key-up
/// events to the remote peer for each — otherwise the peer
/// retains phantom-held keys after capture is released. The
/// canonical case is the release-bind chord
/// (Ctrl+Shift+Alt+Meta): the down events were sent while
/// capture was active, but the matching up events arrive after
/// the local tap has flipped to passthrough and never reach
/// the peer.
pub fn take_pressed_keys(&mut self) -> HashSet<scancode::Linux> {
std::mem::take(&mut self.pressed_keys)
}
/// destroy the input capture
pub async fn terminate(&mut self) -> Result<(), CaptureError> {
self.capture.terminate().await