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.
This commit is contained in:
fufesou
2026-09-12 16:56:48 +08:00
parent 944f93ba7b
commit 1d98d1a647
2 changed files with 35 additions and 1 deletions

View File

@@ -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<Arc<Displays>> {
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<Displays> {
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.

View File

@@ -995,7 +995,8 @@ pub fn drm_cursor_snapshot<T>(
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()