mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-05-07 06:38:11 +03:00
fix(client): serialize X11 keyboard grab and debounce focus feedback (#14836)
* fix(client): serialize X11 keyboard grab and debounce focus feedback When two RustDesk sessions run fullscreen on separate monitors on Linux/X11, keyboard input gets stuck on the wrong session or stops working entirely. This happens because each Flutter isolate calls change_grab_status concurrently, racing on KEYBOARD_HOOKED and the rdev grab channel. Additionally, XGrabKeyboard causes a focus-change feedback loop: grab shifts focus away from the Flutter window, triggering PointerExit, which releases the grab, restoring focus, triggering PointerEnter, which re-grabs -- cycling at ~10 Hz and blocking keyboard input. Fix by: - Serializing grab transitions with a mutex and tracking the owning session (by lc.session_id), so a stale Wait from session A cannot clobber session B's freshly acquired grab. - Debouncing Wait events (300 ms) from the same session that just acquired the grab, breaking the X11 focus feedback loop. - Refreshing the debounce timer on idempotent Run calls (enterView while already owner), keeping the grab stable during normal use. Signed-off-by: Sergiusz Michalik <github@latens.me> * fix(client): add deferred release and dedup for debounced Wait When a Wait is debounced (within 300ms of grab acquisition), schedule a deferred release thread that re-checks after the debounce window. If no new Run refreshed the grab, the deferred thread releases it, ensuring a genuine leave within the debounce window is not lost. Add a deferred_pending flag to GrabOwnerState to prevent spawning redundant threads during the X11 focus feedback loop. Signed-off-by: Sergiusz Michalik <github@latens.me> * fix(client): use window-scoped ID and fix deferred-release re-arming Address PR review feedback: - Use per-window UUID instead of connection-scoped lc.session_id so two windows viewing the same peer get distinct grab owners - Reset deferred_pending on both idempotent Run refresh and owner handoff, so a subsequent Wait can always spawn a fresh timer - Replace manual Default impl with derive * fix(client): recover from poisoned mutex instead of panicking * docs: clarify cross-platform rationale for GrabOwnerState * fix(client): only clear deferred_pending when timer snapshot matches * fix(client): use full u128 window ID, downgrade grab logs to debug - Widen GrabOwnerState.owner to u128 to avoid theoretical collision from truncating a 128-bit UUID to 64 bits - Downgrade all grab transition log::info! to log::debug! to reduce log noise during routine window switches - Clear deferred_pending on post-debounce release path to maintain the "deferred_pending => timer in flight" invariant * fix(client): gate GRAB_DEBOUNCE_MS with cfg(target_os = "linux") * fix(grab): release grabbed keys without clobbering new owner state Signed-off-by: fufesou <linlong1266@gmail.com> * fix(keyboard): Simple refactor Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: Sergiusz Michalik <github@latens.me> Signed-off-by: fufesou <linlong1266@gmail.com> Co-authored-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
committed by
GitHub
parent
c8ba99d1a1
commit
7308c448f1
@@ -870,12 +870,14 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
pub fn enter(&self, keyboard_mode: String) {
|
||||
keyboard::client::change_grab_status(GrabState::Run, &keyboard_mode);
|
||||
let session_id = self.lc.read().unwrap().session_id as u128;
|
||||
keyboard::client::change_grab_status(GrabState::Run, &keyboard_mode, session_id);
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
pub fn leave(&self, keyboard_mode: String) {
|
||||
keyboard::client::change_grab_status(GrabState::Wait, &keyboard_mode);
|
||||
let session_id = self.lc.read().unwrap().session_id as u128;
|
||||
keyboard::client::change_grab_status(GrabState::Wait, &keyboard_mode, session_id);
|
||||
}
|
||||
|
||||
// flutter only TODO new input
|
||||
|
||||
Reference in New Issue
Block a user