From d7a7d57ccdcd8a16290d6fecec3450e7d8e228bd Mon Sep 17 00:00:00 2001 From: "Brian J. Tarricone" Date: Sun, 14 Aug 2022 22:54:24 -0700 Subject: [PATCH] Hackily retry keyboard/pointer grabs if they fail When using a global key combo to lock the screen, often the WM (or whatever) will still have an active grab on the keyboard by the time we try to get our own grab. --- locker/src/monitor.rs | 83 ++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/locker/src/monitor.rs b/locker/src/monitor.rs index 9acb72c..7c9d56d 100644 --- a/locker/src/monitor.rs +++ b/locker/src/monitor.rs @@ -1,5 +1,5 @@ -use log::{debug, warn}; -use std::cmp; +use log::{debug, error, warn}; +use std::{cmp, thread, time::Duration}; use xcb::{x, randr, Xid}; use bscreensaver_util::{BSCREENSAVER_WM_CLASS, create_atom, destroy_cursor, destroy_pixmap, destroy_gc, destroy_window}; @@ -238,38 +238,65 @@ impl<'a> Monitor<'a> { self.conn.check_request(cookie)?; } - let cookie = self.conn.send_request(&x::GrabKeyboard { - owner_events: true, - grab_window: self.unlock_window, - time: x::CURRENT_TIME, - pointer_mode: x::GrabMode::Async, - keyboard_mode: x::GrabMode::Async, - }); - let reply = self.conn.wait_for_reply(cookie)?; - if reply.status() != x::GrabStatus::Success { - // FIXME: try to grab later? - warn!("Failed to grab keyboard on window {:?}: {:?}", self.blanker_window, reply.status()); + if let Err(err) = self.grab_keyboard() { + warn!("Failed to grab keyboard on window {:?}: {:?}", self.blanker_window, err); } - - let cookie = self.conn.send_request(&x::GrabPointer { - owner_events: true, - grab_window: self.unlock_window, - event_mask: x::EventMask::BUTTON_PRESS | x::EventMask::BUTTON_RELEASE | x::EventMask::POINTER_MOTION | x::EventMask::POINTER_MOTION_HINT, - pointer_mode: x::GrabMode::Async, - keyboard_mode: x::GrabMode::Async, - confine_to: self.blanker_window, - cursor: x::CURSOR_NONE, - time: x::CURRENT_TIME, - }); - let reply = self.conn.wait_for_reply(cookie)?; - if reply.status() != x::GrabStatus::Success { - // FIXME: try to grab later? - warn!("Failed to grab pointer on window {:?}: {:?}", self.blanker_window, reply.status()); + if let Err(err) = self.grab_pointer() { + error!("Failed to grab pointer on window {:?}: {:?}", self.blanker_window, err); } Ok(()) } + fn grab_keyboard(&self) -> anyhow::Result<()> { + let mut attempts_remaining = 10; + loop { + let cookie = self.conn.send_request(&x::GrabKeyboard { + owner_events: true, + grab_window: self.unlock_window, + time: x::CURRENT_TIME, + pointer_mode: x::GrabMode::Async, + keyboard_mode: x::GrabMode::Async, + }); + let reply = self.conn.wait_for_reply(cookie)?; + if reply.status() == x::GrabStatus::Success { + break Ok(()); + } else if attempts_remaining > 0 { + attempts_remaining -= 1; + warn!("Failed to grab keyboard ({:?}); retrying", reply.status()); + thread::sleep(Duration::from_millis(25)); + } else { + break Err(anyhow::anyhow!("{:?}", reply.status())); + } + } + } + + fn grab_pointer(&self) -> anyhow::Result<()> { + let mut attempts_remaining = 10; + loop { + let cookie = self.conn.send_request(&x::GrabPointer { + owner_events: true, + grab_window: self.unlock_window, + event_mask: x::EventMask::BUTTON_PRESS | x::EventMask::BUTTON_RELEASE | x::EventMask::POINTER_MOTION | x::EventMask::POINTER_MOTION_HINT, + pointer_mode: x::GrabMode::Async, + keyboard_mode: x::GrabMode::Async, + confine_to: self.blanker_window, + cursor: x::CURSOR_NONE, + time: x::CURRENT_TIME, + }); + let reply = self.conn.wait_for_reply(cookie)?; + if reply.status() == x::GrabStatus::Success { + break Ok(()); + } else if attempts_remaining > 0 { + attempts_remaining -= 1; + warn!("Failed to grab pointer ({:?}); retrying", reply.status()); + thread::sleep(Duration::from_millis(25)); + } else { + break Err(anyhow::anyhow!("{:?}", reply.status())); + } + } + } + pub fn unlock(&self) -> anyhow::Result<()> { let mut cookies = Vec::new();