RustDesk/src
Sergiusz Michalik 7308c448f1
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>
2026-04-26 22:46:41 +08:00
..
client fix: file transfer, path traversal (#14678) 2026-04-10 18:00:11 +08:00
hbbs_http tcp proxy (#14633) 2026-04-03 23:13:05 +08:00
lang feat(i18n): Complete and fix french translations (#14890) 2026-04-24 18:38:34 +08:00
platform fix(win): exe icon path (#14686) 2026-04-04 22:54:13 +08:00
plugin refact: remove unnecessary printing (#12000) 2025-06-06 14:52:01 +08:00
privacy_mode feat(macos): initial privacy mode support [a simple try] (#14102) 2026-01-27 16:38:37 +08:00
server add brute-force protection for one-time password (#14682) 2026-04-09 17:14:21 +08:00
ui fix(sicter): control side, privacy mode (#14880) 2026-04-24 14:35:58 +08:00
whiteboard feat: cursor, linux, Xwayland (#12859) 2025-09-06 20:35:51 +08:00
auth_2fa.rs trust this device to skip 2fa (#9012) 2024-08-12 18:08:33 +08:00
cli.rs Fix: replace unwrap() with proper error handling in CLI password prompt (#14910) 2026-04-26 21:28:05 +08:00
client.rs fix(client): stop retrying on restricted mobile access errors (#14797) 2026-04-15 21:40:03 +08:00
clipboard.rs fix: correct typos and improve code clarity (#14341) 2026-02-17 14:29:50 +08:00
clipboard_file.rs feat: clipboard files, audit (#12730) 2025-08-25 22:29:53 +08:00
common.rs tcp proxy (#14633) 2026-04-03 23:13:05 +08:00
core_main.rs fix https://github.com/rustdesk/rustdesk/issues/14520 2026-03-13 10:42:13 +08:00
custom_server.rs A=b, A case insensitive (#9976) 2024-11-20 19:44:24 +08:00
flutter.rs fix: wayland controlled side, cursor misalignment (#13537) 2025-11-18 00:37:15 +08:00
flutter_ffi.rs fix(client): serialize X11 keyboard grab and debounce focus feedback (#14836) 2026-04-26 22:46:41 +08:00
hbbs_http.rs tcp proxy (#14633) 2026-04-03 23:13:05 +08:00
ipc.rs refact(password): Store permanent password as hashed verifier (#14619) 2026-03-26 14:49:54 +08:00
kcp_stream.rs fix kcp_stream 2025-06-13 00:30:21 +08:00
keyboard.rs fix(client): serialize X11 keyboard grab and debounce focus feedback (#14836) 2026-04-26 22:46:41 +08:00
lan.rs refact: suppress warns on macos (#12449) 2025-08-18 15:09:11 +08:00
lang.rs Add Malayalam language support (#14753) 2026-04-22 18:06:10 +08:00
lib.rs feat: Add relative mouse mode (#13928) 2026-01-09 10:03:14 +08:00
main.rs refact: rust backtrace logs (#13467) 2025-11-10 15:43:46 +08:00
naming.rs fix: http/https proxy (#7821) 2024-04-25 11:46:21 +08:00
port_forward.rs change port forward listen to localhost 2026-02-24 21:57:55 +08:00
privacy_mode.rs feat(macos): initial privacy mode support [a simple try] (#14102) 2026-01-27 16:38:37 +08:00
rendezvous_mediator.rs feat: windows, custom client, update (#13687) 2026-02-27 21:50:20 +08:00
server.rs Fix some single device multiple ids scenarios on MacOS (#14196) 2026-02-15 16:12:26 +08:00
service.rs missed file 2025-01-01 23:11:38 +08:00
tray.rs add option to hide stop-service when service is running (#14563) 2026-03-19 20:04:10 +08:00
ui.rs refact(password): Store permanent password as hashed verifier (#14619) 2026-03-26 14:49:54 +08:00
ui_cm_interface.rs fix: file transfer, path traversal (#14678) 2026-04-10 18:00:11 +08:00
ui_interface.rs refact(password): Store permanent password as hashed verifier (#14619) 2026-03-26 14:49:54 +08:00
ui_session_interface.rs fix(client): serialize X11 keyboard grab and debounce focus feedback (#14836) 2026-04-26 22:46:41 +08:00
updater.rs feat: windows, custom client, update (#13687) 2026-02-27 21:50:20 +08:00
virtual_display_manager.rs fix: uninstall, idd (#13142) 2025-10-12 09:14:21 +08:00