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 std::cmp;
use std::{cmp, cell::RefCell};
use xcb::{x, randr, xfixes, Xid};
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_FALLBACK_ATOM_NAME: &[u8] = b"BACKLIGHT";
#[derive(Clone, Copy)]
struct BacklightControl {
property: x::Atom,
min_level: i32,
@ -15,7 +14,6 @@ struct BacklightControl {
step: u32,
}
#[derive(Clone, Copy)]
pub struct Monitor {
pub root: x::Window,
pub black_gc: x::Gcontext,
@ -26,6 +24,8 @@ pub struct Monitor {
pub y: i16,
pub width: u16,
pub height: u16,
// RefCell used here to avoid requiring '&mut self' on blank/unblank
cursor_hidden: RefCell<u32>,
backlight_control: Option<BacklightControl>,
}
@ -78,6 +78,7 @@ impl Monitor {
y: reply.y(),
width: reply.width(),
height: reply.height(),
cursor_hidden: RefCell::new(0),
backlight_control: backlight_control.ok().flatten(),
});
}
@ -87,6 +88,15 @@ impl Monitor {
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<()> {
let mut cookies = Vec::new();
cookies.push(conn.send_request_checked(&x::ConfigureWindow {
@ -186,10 +196,15 @@ impl Monitor {
}
pub fn show_cursor(&self, conn: &xcb::Connection) {
if let Err(err) = conn.send_and_check_request(&xfixes::ShowCursor {
window: self.blanker_window,
}) {
warn!("Failed to show cursor: {}", err);
let ok = self.cursor_hidden.try_borrow().map(|ch| *ch > 0).unwrap_or(true);
if ok {
if let Err(err) = conn.send_and_check_request(&xfixes::ShowCursor {
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,
}) {
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<()> {
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) {
let new_level = updater(&backlight_control, cur_brightness);
let new_level = cmp::min(backlight_control.max_level, new_level);

View File

@ -24,7 +24,8 @@ pub enum BlankerState {
}
struct UnlockDialog<'a> {
monitor: Monitor,
monitor_geom: x::Rectangle,
blanker_window: x::Window,
embedder: Embedder<'a>,
event_to_forward: Option<xcb::Event>,
child: Child,
@ -204,9 +205,9 @@ impl<'a> Screensaver<'a> {
},
xcb::Event::X(x::Event::ConfigureNotify(ev)) if ev.window() == self.embedder_window() => {
if let Some(unlock_dialog) = &self.unlock_dialog {
let monitor = &unlock_dialog.monitor;
let x = std::cmp::max(0, monitor.x as i32 + monitor.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 monitor_geom = &unlock_dialog.monitor_geom;
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_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 {
conn.send_and_check_request(&x::ConfigureWindow {
window: unlock_dialog.embedder.embedder_window(),
@ -274,7 +275,7 @@ impl<'a> Screensaver<'a> {
},
Some(unlock_dialog) => {
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 {
window: win,
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>> {
let mut pointer_monitor = None;
let mut monitor_data = None;
for monitor in &self.monitors {
let cookie = conn.send_request(&x::QueryPointer {
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
&& 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;
}
}
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");
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 {
@ -391,11 +393,11 @@ impl<'a> Screensaver<'a> {
})?;
}
let unlock_window = pointer_monitor.unlock_window;
let embedder = Embedder::start(conn, unlock_window, client_window)?;
Ok(UnlockDialog {
monitor: *pointer_monitor,
monitor_geom,
blanker_window,
embedder,
event_to_forward: trigger_event,
child,