Avoid unmatched ShowCursor/HideCursor requests

This commit is contained in:
Brian Tarricone 2022-05-24 20:32:15 -07:00
parent 63a176c26e
commit 344907f55a
2 changed files with 38 additions and 19 deletions

View File

@ -1,5 +1,5 @@
use log::{debug, warn}; use log::{debug, warn};
use std::cmp; use std::{cmp, cell::RefCell};
use xcb::{x, randr, xfixes, Xid}; use xcb::{x, randr, xfixes, Xid};
use bscreensaver_util::{BSCREENSAVER_WM_CLASS, create_atom}; use bscreensaver_util::{BSCREENSAVER_WM_CLASS, create_atom};
@ -7,7 +7,6 @@ use bscreensaver_util::{BSCREENSAVER_WM_CLASS, create_atom};
const BACKLIGHT_ATOM_NAME: &[u8] = b"Backlight"; const BACKLIGHT_ATOM_NAME: &[u8] = b"Backlight";
const BACKLIGHT_FALLBACK_ATOM_NAME: &[u8] = b"BACKLIGHT"; const BACKLIGHT_FALLBACK_ATOM_NAME: &[u8] = b"BACKLIGHT";
#[derive(Clone, Copy)]
struct BacklightControl { struct BacklightControl {
property: x::Atom, property: x::Atom,
min_level: i32, min_level: i32,
@ -15,7 +14,6 @@ struct BacklightControl {
step: u32, step: u32,
} }
#[derive(Clone, Copy)]
pub struct Monitor { pub struct Monitor {
pub root: x::Window, pub root: x::Window,
pub black_gc: x::Gcontext, pub black_gc: x::Gcontext,
@ -26,6 +24,8 @@ pub struct Monitor {
pub y: i16, pub y: i16,
pub width: u16, pub width: u16,
pub height: u16, pub height: u16,
// RefCell used here to avoid requiring '&mut self' on blank/unblank
cursor_hidden: RefCell<u32>,
backlight_control: Option<BacklightControl>, backlight_control: Option<BacklightControl>,
} }
@ -78,6 +78,7 @@ impl Monitor {
y: reply.y(), y: reply.y(),
width: reply.width(), width: reply.width(),
height: reply.height(), height: reply.height(),
cursor_hidden: RefCell::new(0),
backlight_control: backlight_control.ok().flatten(), backlight_control: backlight_control.ok().flatten(),
}); });
} }
@ -87,6 +88,15 @@ impl Monitor {
Ok(monitors) Ok(monitors)
} }
pub fn geometry(&self) -> x::Rectangle {
x::Rectangle {
x: self.x,
y: self.y,
width: self.width,
height: self.height,
}
}
pub fn blank(&self, conn: &xcb::Connection) -> anyhow::Result<()> { pub fn blank(&self, conn: &xcb::Connection) -> anyhow::Result<()> {
let mut cookies = Vec::new(); let mut cookies = Vec::new();
cookies.push(conn.send_request_checked(&x::ConfigureWindow { cookies.push(conn.send_request_checked(&x::ConfigureWindow {
@ -186,10 +196,15 @@ impl Monitor {
} }
pub fn show_cursor(&self, conn: &xcb::Connection) { pub fn show_cursor(&self, conn: &xcb::Connection) {
if let Err(err) = conn.send_and_check_request(&xfixes::ShowCursor { let ok = self.cursor_hidden.try_borrow().map(|ch| *ch > 0).unwrap_or(true);
window: self.blanker_window, if ok {
}) { if let Err(err) = conn.send_and_check_request(&xfixes::ShowCursor {
warn!("Failed to show cursor: {}", err); window: self.blanker_window,
}) {
warn!("Failed to show cursor: {}", err);
} else {
let _ = self.cursor_hidden.try_borrow_mut().map(|mut ch| *ch -= 1);
}
} }
} }
@ -198,6 +213,8 @@ impl Monitor {
window: self.blanker_window, window: self.blanker_window,
}) { }) {
warn!("Failed to hide cursor: {}", err); warn!("Failed to hide cursor: {}", err);
} else {
let _ = self.cursor_hidden.try_borrow_mut().map(|mut ch| *ch += 1);
} }
} }
@ -210,7 +227,7 @@ impl Monitor {
} }
fn brightness_change<F: FnOnce(&BacklightControl, u32) -> i32>(&self, conn: &xcb::Connection, updater: F) -> anyhow::Result<()> { fn brightness_change<F: FnOnce(&BacklightControl, u32) -> i32>(&self, conn: &xcb::Connection, updater: F) -> anyhow::Result<()> {
if let Some(backlight_control) = self.backlight_control { if let Some(backlight_control) = &self.backlight_control {
if let Ok(Some(cur_brightness)) = self.get_current_brightness(conn, &backlight_control) { if let Ok(Some(cur_brightness)) = self.get_current_brightness(conn, &backlight_control) {
let new_level = updater(&backlight_control, cur_brightness); let new_level = updater(&backlight_control, cur_brightness);
let new_level = cmp::min(backlight_control.max_level, new_level); let new_level = cmp::min(backlight_control.max_level, new_level);

View File

@ -24,7 +24,8 @@ pub enum BlankerState {
} }
struct UnlockDialog<'a> { struct UnlockDialog<'a> {
monitor: Monitor, monitor_geom: x::Rectangle,
blanker_window: x::Window,
embedder: Embedder<'a>, embedder: Embedder<'a>,
event_to_forward: Option<xcb::Event>, event_to_forward: Option<xcb::Event>,
child: Child, child: Child,
@ -204,9 +205,9 @@ impl<'a> Screensaver<'a> {
}, },
xcb::Event::X(x::Event::ConfigureNotify(ev)) if ev.window() == self.embedder_window() => { xcb::Event::X(x::Event::ConfigureNotify(ev)) if ev.window() == self.embedder_window() => {
if let Some(unlock_dialog) = &self.unlock_dialog { if let Some(unlock_dialog) = &self.unlock_dialog {
let monitor = &unlock_dialog.monitor; let monitor_geom = &unlock_dialog.monitor_geom;
let x = std::cmp::max(0, monitor.x as i32 + monitor.width as i32 / 2 - ev.width() as i32 / 2); let x = std::cmp::max(0, monitor_geom.x as i32 + monitor_geom.width as i32 / 2 - ev.width() as i32 / 2);
let y = std::cmp::max(0, monitor.y as i32 + monitor.height as i32 / 2 - ev.height() as i32 / 2); let y = std::cmp::max(0, monitor_geom.y as i32 + monitor_geom.height as i32 / 2 - ev.height() as i32 / 2);
if x != ev.x() as i32 || y != ev.y() as i32 { if x != ev.x() as i32 || y != ev.y() as i32 {
conn.send_and_check_request(&x::ConfigureWindow { conn.send_and_check_request(&x::ConfigureWindow {
window: unlock_dialog.embedder.embedder_window(), window: unlock_dialog.embedder.embedder_window(),
@ -274,7 +275,7 @@ impl<'a> Screensaver<'a> {
}, },
Some(unlock_dialog) => { Some(unlock_dialog) => {
let mut cookies = Vec::new(); let mut cookies = Vec::new();
for win in [unlock_dialog.monitor.blanker_window, unlock_dialog.embedder.embedder_window(), unlock_dialog.embedder.client_window()] { for win in [unlock_dialog.blanker_window, unlock_dialog.embedder.embedder_window(), unlock_dialog.embedder.client_window()] {
cookies.push(conn.send_request_checked(&x::ConfigureWindow { cookies.push(conn.send_request_checked(&x::ConfigureWindow {
window: win, window: win,
value_list: &[ value_list: &[
@ -324,7 +325,7 @@ impl<'a> Screensaver<'a> {
} }
fn start_unlock_dialog(&self, conn: &'a xcb::Connection, trigger_event: Option<xcb::Event>) -> anyhow::Result<UnlockDialog<'a>> { fn start_unlock_dialog(&self, conn: &'a xcb::Connection, trigger_event: Option<xcb::Event>) -> anyhow::Result<UnlockDialog<'a>> {
let mut pointer_monitor = None; let mut monitor_data = None;
for monitor in &self.monitors { for monitor in &self.monitors {
let cookie = conn.send_request(&x::QueryPointer { let cookie = conn.send_request(&x::QueryPointer {
window: monitor.root, window: monitor.root,
@ -336,13 +337,14 @@ impl<'a> Screensaver<'a> {
&& px >= monitor.x as i32 && px < monitor.x as i32 + monitor.width as i32 && px >= monitor.x as i32 && px < monitor.x as i32 + monitor.width as i32
&& py >= monitor.y as i32 && py < monitor.y as i32 + monitor.height as i32 && py >= monitor.y as i32 && py < monitor.y as i32 + monitor.height as i32
{ {
pointer_monitor = Some(monitor); monitor_data = Some((monitor.geometry(), monitor.blanker_window, monitor.unlock_window));
break; break;
} }
} }
let pointer_monitor = pointer_monitor.unwrap_or_else(|| { let (monitor_geom, blanker_window, unlock_window) = monitor_data.unwrap_or_else(|| {
warn!("Unable to determine which monitor pointer is on; using first one"); warn!("Unable to determine which monitor pointer is on; using first one");
self.monitors.iter().nth(0).unwrap() let monitor = self.monitors.iter().nth(0).unwrap();
(monitor.geometry(), monitor.blanker_window, monitor.unlock_window)
}); });
for monitor in &self.monitors { for monitor in &self.monitors {
@ -391,11 +393,11 @@ impl<'a> Screensaver<'a> {
})?; })?;
} }
let unlock_window = pointer_monitor.unlock_window;
let embedder = Embedder::start(conn, unlock_window, client_window)?; let embedder = Embedder::start(conn, unlock_window, client_window)?;
Ok(UnlockDialog { Ok(UnlockDialog {
monitor: *pointer_monitor, monitor_geom,
blanker_window,
embedder, embedder,
event_to_forward: trigger_event, event_to_forward: trigger_event,
child, child,