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
This commit is contained in:
Sergiusz Michalik 2026-04-18 11:58:34 +02:00
parent eb42d87994
commit a069b1d74c
2 changed files with 20 additions and 8 deletions

View file

@ -615,11 +615,22 @@ pub fn session_enter_or_leave(_session_id: SessionID, _enter: bool) -> SyncRetur
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if let Some(session) = sessions::get_session_by_session_id(&_session_id) {
let keyboard_mode = session.get_keyboard_mode();
// Use the per-window UUID (not lc.session_id which is per-connection)
// so that two windows viewing the same peer get distinct grab owners.
let window_id = _session_id.as_u128() as u64;
if _enter {
set_cur_session_id_(_session_id, &keyboard_mode);
session.enter(keyboard_mode);
crate::keyboard::client::change_grab_status(
crate::common::GrabState::Run,
&keyboard_mode,
window_id,
);
} else {
session.leave(keyboard_mode);
crate::keyboard::client::change_grab_status(
crate::common::GrabState::Wait,
&keyboard_mode,
window_id,
);
}
}
SyncReturn(())

View file

@ -89,6 +89,7 @@ pub mod client {
/// PointerEnter -> grab -> ... at ~10 Hz. `last_grab` lets us debounce
/// spurious `Wait` events that arrive shortly after a `Run` for the same
/// session - these are X11 focus feedback, not real user actions.
#[derive(Default)]
struct GrabOwnerState {
owner: Option<u64>,
last_grab: Option<std::time::Instant>,
@ -97,12 +98,6 @@ pub mod client {
deferred_pending: bool,
}
impl Default for GrabOwnerState {
fn default() -> Self {
Self { owner: None, last_grab: None, deferred_pending: false }
}
}
/// How long after a grab acquisition we suppress Wait from the same session.
/// Must exceed one full X11 feedback cycle (~100 ms: 50 ms enable + 50 ms disable).
const GRAB_DEBOUNCE_MS: u128 = 300;
@ -141,6 +136,9 @@ pub mod client {
// actively focused) and skip the actual grab call.
if gs.owner == Some(session_id) {
gs.last_grab = Some(std::time::Instant::now());
// Reset so the next Wait can spawn a fresh deferred-release
// timer with an up-to-date snapshot of last_grab.
gs.deferred_pending = false;
log::debug!("[grab] Run(0x{:x}): already owner, refresh debounce", session_id);
return;
}
@ -169,6 +167,9 @@ pub mod client {
}
gs.owner = Some(session_id);
gs.last_grab = Some(std::time::Instant::now());
// Invalidate any in-flight deferred release from the previous
// owner so it cannot suppress a fresh timer for the new owner.
gs.deferred_pending = false;
}
GrabState::Wait => {
// Drop stale `Wait` events that do not correspond to the