From 1d98d1a64770827ce0bc862b298482641fdfb567 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 12 Sep 2026 16:56:48 +0800 Subject: [PATCH] fix(cursor): keep DRM density lookup off the polling path Read optional Wayland metadata from the cache without starting discovery or waiting on a refresh. Keep delivering cursor shapes with unknown density when metadata is unavailable, and cover lock contention and repeated empty-cache reads. --- libs/scrap/src/wayland/display.rs | 33 +++++++++++++++++++++++++++++++ src/server/drm_capturer.rs | 3 ++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/libs/scrap/src/wayland/display.rs b/libs/scrap/src/wayland/display.rs index 6ff4177cc..1e19bb415 100644 --- a/libs/scrap/src/wayland/display.rs +++ b/libs/scrap/src/wayland/display.rs @@ -248,6 +248,19 @@ pub fn wayland_failure_stamped() -> bool { LAST_FAILED_LOOKUP.lock().unwrap().is_some() } +/// Cursor polls must neither start discovery nor wait for an in-progress display refresh. +#[cfg(feature = "drm")] +pub fn get_cached_displays() -> Option> { + match DISPLAYS.try_lock() { + Ok(cache) => cache.clone(), + Err(std::sync::TryLockError::WouldBlock) => None, + Err(err) => { + warn!("Failed to read cached Wayland displays: {}", err); + None + } + } +} + pub fn get_displays() -> Arc { let mut lock = DISPLAYS.lock().unwrap(); match lock.as_ref() { @@ -516,6 +529,26 @@ fn map_axis(v: i32, base_origin: i32, base_extent: i32, live_origin: i32, live_e mod tests { use super::*; + #[cfg(feature = "drm")] + #[test] + fn cursor_metadata_does_not_wait_for_display_discovery() { + let cache = DISPLAYS.lock().unwrap(); + let (tx, rx) = std::sync::mpsc::channel(); + let reader = std::thread::spawn(move || { + tx.send(get_cached_displays().is_none()).unwrap(); + }); + // Discovery owns this lock until it completes or times out. + let result = rx.recv_timeout(Duration::from_secs(1)); + drop(cache); + reader.join().unwrap(); + assert!(result.unwrap()); + + clear_wayland_displays_cache(); + for _ in 0..100 { + assert!(get_cached_displays().is_none()); + } + } + #[test] fn test_lookup_backoff_boundaries() { // Future `now`s sidestep Instant subtraction, which can panic near boot. diff --git a/src/server/drm_capturer.rs b/src/server/drm_capturer.rs index 76c2a3d09..ed1826ce9 100644 --- a/src/server/drm_capturer.rs +++ b/src/server/drm_capturer.rs @@ -995,7 +995,8 @@ pub fn drm_cursor_snapshot( None } else { display_info_of(display).and_then(|display| { - let wayland = scrap::wayland::display::get_displays(); + // Display discovery runs outside the cursor service; missing metadata means unknown DPI. + let wayland = scrap::wayland::display::get_cached_displays()?; let index = identity_matches(&[display], &wayland.displays) .into_iter() .next()