Be more aggressive about handling user activity

Previously we only trigger unblanking or showing the unlock dialog if
core key or motion events come through.  Now we also do this when xinput
events come in.  We also now reset the last-user-activity time for core
events.

This also changes xcb-xembed to explicitly only handle core events.
This commit is contained in:
Brian Tarricone 2022-06-08 00:30:11 -07:00
parent b0c2ffc74a
commit d3de9f7fad
2 changed files with 44 additions and 36 deletions

View File

@ -26,7 +26,7 @@ struct UnlockDialog<'a> {
monitor_geom: x::Rectangle, monitor_geom: x::Rectangle,
blanker_window: x::Window, blanker_window: x::Window,
embedder: Embedder<'a>, embedder: Embedder<'a>,
event_to_forward: Option<xcb::Event>, event_to_forward: Option<x::Event>,
child: Child, child: Child,
child_pidfd: PidFd, child_pidfd: PidFd,
} }
@ -109,16 +109,21 @@ impl<'a> Screensaver<'a> {
loop { loop {
if let Some(event) = self.conn.poll_for_event()? { if let Some(event) = self.conn.poll_for_event()? {
let embedder_handled = if let Some(mut unlock_dialog) = self.unlock_dialog.take() { let embedder_handled = if let Some(mut unlock_dialog) = self.unlock_dialog.take() {
match unlock_dialog.embedder.event(&event) { if let xcb::Event::X(xev) = &event {
Err(err) => { match unlock_dialog.embedder.event(xev) {
// XXX: should we assume unlock dialog is dead here? Err(err) => {
warn!("Error sending event to unlock dialog: {}", err); // XXX: should we assume unlock dialog is dead here?
false warn!("Error sending event to unlock dialog: {}", err);
}, false
Ok(handled) => { },
self.unlock_dialog = Some(unlock_dialog); Ok(handled) => {
handled self.unlock_dialog = Some(unlock_dialog);
}, handled
},
}
} else {
self.unlock_dialog = Some(unlock_dialog);
false
} }
} else { } else {
false false
@ -148,10 +153,7 @@ impl<'a> Screensaver<'a> {
}, },
} }
}, },
xcb::Event::Input(_) => { ev @ xcb::Event::Input(_) => self.handle_user_activity(ev)?,
// TODO: implement some sort of hysteresis/debouncing for mouse motion
self.last_user_activity = Instant::now();
},
xcb::Event::X(x::Event::ClientMessage(ev)) => { xcb::Event::X(x::Event::ClientMessage(ev)) => {
let res = match ev.r#type() { let res = match ev.r#type() {
b if b == self.command_atoms.blank => Some(self.blank_screen()), b if b == self.command_atoms.blank => Some(self.blank_screen()),
@ -259,18 +261,24 @@ impl<'a> Screensaver<'a> {
} }
fn handle_user_activity(&mut self, ev: xcb::Event) -> anyhow::Result<()> { fn handle_user_activity(&mut self, ev: xcb::Event) -> anyhow::Result<()> {
// TODO: implement some sort of hysteresis/debouncing for mouse motion
self.last_user_activity = Instant::now();
match self.blanker_state { match self.blanker_state {
BlankerState::Idle => Ok(()), BlankerState::Idle => Ok(()),
BlankerState::Blanked => self.unblank_screen(), BlankerState::Blanked => self.unblank_screen(),
BlankerState::Locked => match &self.unlock_dialog { BlankerState::Locked => match &self.unlock_dialog {
None => { None => {
self.unlock_dialog = match self.start_unlock_dialog(Some(ev)) { match ev {
Err(err) => { xcb::Event::X(xev) => self.unlock_dialog = match self.start_unlock_dialog(Some(xev)) {
error!("Unable to start unlock dialog: {}", err); Err(err) => {
None error!("Unable to start unlock dialog: {}", err);
None
},
Ok(unlock_dialog) => Some(unlock_dialog),
}, },
Ok(unlock_dialog) => Some(unlock_dialog), _ => (),
}; }
Ok(()) Ok(())
}, },
Some(unlock_dialog) => { Some(unlock_dialog) => {
@ -331,7 +339,7 @@ impl<'a> Screensaver<'a> {
Ok(()) Ok(())
} }
fn start_unlock_dialog(&self, trigger_event: Option<xcb::Event>) -> anyhow::Result<UnlockDialog<'a>> { fn start_unlock_dialog(&self, trigger_event: Option<x::Event>) -> anyhow::Result<UnlockDialog<'a>> {
let mut monitor_data = None; let mut monitor_data = None;
for monitor in &self.monitors { for monitor in &self.monitors {
let cookie = self.conn.send_request(&x::QueryPointer { let cookie = self.conn.send_request(&x::QueryPointer {
@ -359,15 +367,15 @@ impl<'a> Screensaver<'a> {
} }
let trigger_event = match trigger_event { let trigger_event = match trigger_event {
Some(xcb::Event::X(x::Event::KeyPress(ev))) => match keysym_for_keypress(self.conn, &ev) { Some(x::Event::KeyPress(ev)) => match keysym_for_keypress(self.conn, &ev) {
Err(err) => { Err(err) => {
warn!("Failed to get keysym for key press event: {}", err); warn!("Failed to get keysym for key press event: {}", err);
Some(xcb::Event::X(x::Event::KeyPress(ev))) Some(x::Event::KeyPress(ev))
}, },
Ok(Some(keysym)) if keysym == xkb::key::KP_Enter || keysym == xkb::key::ISO_Enter || keysym == xkb::key::Return || keysym == xkb::key::Escape => Ok(Some(keysym)) if keysym == xkb::key::KP_Enter || keysym == xkb::key::ISO_Enter || keysym == xkb::key::Return || keysym == xkb::key::Escape =>
// don't forward an <enter> or <esc> to the dialog, as that will make it activate/close immediately // don't forward an <enter> or <esc> to the dialog, as that will make it activate/close immediately
None, None,
_ => Some(xcb::Event::X(x::Event::KeyPress(ev))), _ => Some(x::Event::KeyPress(ev)),
}, },
te => te, te => te,
}; };

View File

@ -122,13 +122,13 @@ impl<'a> Embedder<'a> {
Ok(()) Ok(())
} }
pub fn event(&mut self, event: &xcb::Event) -> Result<bool, Error> { pub fn event(&mut self, event: &x::Event) -> Result<bool, Error> {
if self.client == x::WINDOW_NONE { if self.client == x::WINDOW_NONE {
return Err(Error::ClientDestroyed); return Err(Error::ClientDestroyed);
} }
match event { match event {
xcb::Event::X(x::Event::PropertyNotify(ev)) if ev.window() == self.client && ev.atom() == intern_atom(self.conn, XEMBED_INFO_ATOM_NAME)? => { x::Event::PropertyNotify(ev) if ev.window() == self.client && ev.atom() == intern_atom(self.conn, XEMBED_INFO_ATOM_NAME)? => {
let info = fetch_xembed_info(self.conn, self.client)?; let info = fetch_xembed_info(self.conn, self.client)?;
if (self.flags & XEmbedFlags::MAPPED) != (info.flags & XEmbedFlags::MAPPED) { if (self.flags & XEmbedFlags::MAPPED) != (info.flags & XEmbedFlags::MAPPED) {
if info.flags.contains(XEmbedFlags::MAPPED) { if info.flags.contains(XEmbedFlags::MAPPED) {
@ -146,7 +146,7 @@ impl<'a> Embedder<'a> {
} }
Ok(true) Ok(true)
}, },
xcb::Event::X(x::Event::ConfigureNotify(ev)) if ev.window() == self.client => { x::Event::ConfigureNotify(ev) if ev.window() == self.client => {
let mut cookies = Vec::new(); let mut cookies = Vec::new();
if ev.x() != 0 || ev.y() != 0 { if ev.x() != 0 || ev.y() != 0 {
cookies.push(self.conn.send_request_checked(&x::ConfigureWindow { cookies.push(self.conn.send_request_checked(&x::ConfigureWindow {
@ -169,7 +169,7 @@ impl<'a> Embedder<'a> {
} }
Ok(true) Ok(true)
}, },
xcb::Event::X(x::Event::ClientMessage(ev)) if ev.window() == self.embedder && ev.r#type() == intern_atom(self.conn, XEMBED_MESSAGE_ATOM_NAME)? => { x::Event::ClientMessage(ev) if ev.window() == self.embedder && ev.r#type() == intern_atom(self.conn, XEMBED_MESSAGE_ATOM_NAME)? => {
match ev.data() { match ev.data() {
x::ClientMessageData::Data32(data) if data[1] == XEmbedMessage::RequestFocus as u32 => { x::ClientMessageData::Data32(data) if data[1] == XEmbedMessage::RequestFocus as u32 => {
debug!("Client requests focus"); debug!("Client requests focus");
@ -188,7 +188,7 @@ impl<'a> Embedder<'a> {
_ => Ok(false), _ => Ok(false),
} }
}, },
xcb::Event::X(x::Event::KeyPress(ev)) if ev.event() == self.embedder && self.flags.contains(XEmbedFlags::MAPPED) => { x::Event::KeyPress(ev) if ev.event() == self.embedder && self.flags.contains(XEmbedFlags::MAPPED) => {
trace!("Forwarding key press to client ({:?} + {})", ev.state(), ev.detail()); trace!("Forwarding key press to client ({:?} + {})", ev.state(), ev.detail());
self.conn.send_and_check_request(&x::SendEvent { self.conn.send_and_check_request(&x::SendEvent {
propagate: false, propagate: false,
@ -199,7 +199,7 @@ impl<'a> Embedder<'a> {
Ok(true) Ok(true)
}, },
/* /*
xcb::Event::X(x::Event::KeyRelease(ev)) if ev.event() == self.embedder && self.flags.contains(XEmbedFlags::MAPPED) => { x::Event::KeyRelease(ev) if ev.event() == self.embedder && self.flags.contains(XEmbedFlags::MAPPED) => {
trace!("Forwarding key release to client ({:?} + {})", ev.state(), ev.detail()); trace!("Forwarding key release to client ({:?} + {})", ev.state(), ev.detail());
self.conn.send_and_check_request(&x::SendEvent { self.conn.send_and_check_request(&x::SendEvent {
propagate: false, propagate: false,
@ -210,7 +210,7 @@ impl<'a> Embedder<'a> {
Ok(true) Ok(true)
}, },
*/ */
xcb::Event::X(x::Event::MotionNotify(ev)) if ev.event() == self.embedder && self.flags.contains(XEmbedFlags::MAPPED) => { x::Event::MotionNotify(ev) if ev.event() == self.embedder && self.flags.contains(XEmbedFlags::MAPPED) => {
trace!("Forwarding pointer motion to client ({}, {})", ev.event_x(), ev.event_y()); trace!("Forwarding pointer motion to client ({}, {})", ev.event_x(), ev.event_y());
self.conn.send_and_check_request(&x::SendEvent { self.conn.send_and_check_request(&x::SendEvent {
propagate: false, propagate: false,
@ -220,7 +220,7 @@ impl<'a> Embedder<'a> {
})?; })?;
Ok(true) Ok(true)
}, },
xcb::Event::X(x::Event::ButtonPress(ev)) if ev.event() == self.embedder && self.flags.contains(XEmbedFlags::MAPPED) => { x::Event::ButtonPress(ev) if ev.event() == self.embedder && self.flags.contains(XEmbedFlags::MAPPED) => {
trace!("Forwarding button press to client ({:?} + {}: {}, {})", ev.state(), ev.detail(), ev.event_x(), ev.event_y()); trace!("Forwarding button press to client ({:?} + {}: {}, {})", ev.state(), ev.detail(), ev.event_x(), ev.event_y());
self.conn.send_and_check_request(&x::SendEvent { self.conn.send_and_check_request(&x::SendEvent {
propagate: false, propagate: false,
@ -230,7 +230,7 @@ impl<'a> Embedder<'a> {
})?; })?;
Ok(true) Ok(true)
}, },
xcb::Event::X(x::Event::ButtonRelease(ev)) if ev.event() == self.embedder && self.flags.contains(XEmbedFlags::MAPPED) => { x::Event::ButtonRelease(ev) if ev.event() == self.embedder && self.flags.contains(XEmbedFlags::MAPPED) => {
trace!("Forwarding button release to client ({:?} + {}: {}, {})", ev.state(), ev.detail(), ev.event_x(), ev.event_y()); trace!("Forwarding button release to client ({:?} + {}: {}, {})", ev.state(), ev.detail(), ev.event_x(), ev.event_y());
self.conn.send_and_check_request(&x::SendEvent { self.conn.send_and_check_request(&x::SendEvent {
propagate: false, propagate: false,
@ -240,12 +240,12 @@ impl<'a> Embedder<'a> {
})?; })?;
Ok(true) Ok(true)
}, },
xcb::Event::X(x::Event::UnmapNotify(ev)) if ev.window() == self.client => { x::Event::UnmapNotify(ev) if ev.window() == self.client => {
debug!("Client was unmapped"); debug!("Client was unmapped");
self.flags -= XEmbedFlags::MAPPED; self.flags -= XEmbedFlags::MAPPED;
Ok(true) Ok(true)
}, },
xcb::Event::X(x::Event::DestroyNotify(ev)) if ev.window() == self.client => { x::Event::DestroyNotify(ev) if ev.window() == self.client => {
debug!("Client was destroyed"); debug!("Client was destroyed");
self.flags -= XEmbedFlags::MAPPED; self.flags -= XEmbedFlags::MAPPED;
self.client = x::WINDOW_NONE; self.client = x::WINDOW_NONE;