srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/udev/render.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2025-10-03 22:59:00 +0200
committersrdusr <[email protected]>2025-10-03 22:59:00 +0200
commit4eb36873d4ca4b0faf0e54dbeab8f5f6450dd704 (patch)
tree657c89f7398f98b0305b9cd69889c232608f3027 /crates/wayland/src/udev/render.rs
parentd7031dd060ec12e6de334518aa75122c65794047 (diff)
downloadsrdwm-4eb36873d4ca4b0faf0e54dbeab8f5f6450dd704.tar.gz
srdwm-4eb36873d4ca4b0faf0e54dbeab8f5f6450dd704.zip
Make the secondary-cursor sprite opt-in and expire stale entries
Live report: a second cursor appeared uninvited and unusably (frozen, uncontrollable) on screen. Multi-cursor Phase 1 rendered one sprite per physical libinput pointer device that had ever reported a position, with no way to turn it off and no expiry - so a phantom device (a real mouse's side-button/scroll cluster enumerating as its own HID path is a common case) that reports once and never moves again left a frozen ghost sprite with nothing to control or dismiss it. Adds general.multi_cursor (default false, live-settable via `srd set multi_cursor <bool>`) and keys secondary_cursors to (Point, Instant) so both the recording side (udev/session.rs) and the render side (udev/render.rs) drop any entry older than SECONDARY_CURSOR_TIMEOUT (1.5s). The "agent controls a window without interrupting the user" use case this report also raised was never gated on this flag - that's Multi-cursor Phase 2's pinned virtual-pointer delivery, which never shows a visible cursor at all.
Diffstat (limited to 'crates/wayland/src/udev/render.rs')
-rw-r--r--crates/wayland/src/udev/render.rs27
1 files changed, 21 insertions, 6 deletions
diff --git a/crates/wayland/src/udev/render.rs b/crates/wayland/src/udev/render.rs
index 4bfe04d..dc84a2e 100644
--- a/crates/wayland/src/udev/render.rs
+++ b/crates/wayland/src/udev/render.rs
@@ -321,13 +321,28 @@ impl CompState {
// (`cursor_status`/`cursor_buffers`) rather than each
// device getting its own - a real visual distinction
// between devices is a later-phase refinement, not needed
- // to prove multiple live positions render at all.
- let active_device = udev.secondary_cursors.iter().find(|&(_, &p)| p == pointer_pos).map(|(d, _)| d.clone());
- for (device, &pos) in &udev.secondary_cursors {
- if Some(device) == active_device.as_ref() {
- continue;
+ // to prove multiple live positions render at all. Gated
+ // on `general.multi_cursor` (off by default) and on each
+ // entry's own recency: a device that reported a position
+ // once and then never moved again - the real, reported
+ // live bug - stops rendering after `SECONDARY_CURSOR_
+ // TIMEOUT` instead of sitting frozen on screen forever.
+ if self.wm.borrow().multi_cursor_enabled {
+ let now = std::time::Instant::now();
+ let active_device = udev
+ .secondary_cursors
+ .iter()
+ .find(|&(_, &(p, _))| p == pointer_pos)
+ .map(|(d, _)| d.clone());
+ for (device, &(pos, seen)) in &udev.secondary_cursors {
+ if Some(device) == active_device.as_ref() {
+ continue;
+ }
+ if now.duration_since(seen) >= super::SECONDARY_CURSOR_TIMEOUT {
+ continue;
+ }
+ custom_elements.extend(crate::cursor::render_elements(&cursor_status, &cursor_buffers, &mut udev.renderer, pos, origin, hsize));
}
- custom_elements.extend(crate::cursor::render_elements(&cursor_status, &cursor_buffers, &mut udev.renderer, pos, origin, hsize));
}
// Night light/reading mode - a translucent full-output
// overlay, pushed right after the cursor so it colours