diff --git a/compositor/src/main.rs b/compositor/src/main.rs index 15d4460..22cd6b3 100644 --- a/compositor/src/main.rs +++ b/compositor/src/main.rs @@ -90,12 +90,6 @@ fn main() -> Result<(), Box> { event_loop.handle().insert_source( Timer::from_duration(Duration::from_millis(150)), move |_, _, state: &mut Ferese| { - // Decoders finish on a worker even when an idle DRM output has no - // pending frame. Wake rendering once, never continuously for wallpaper. - if state.wallpaper.poll() { - state.backdrop_generation = state.backdrop_generation.wrapping_add(1); - backends::direct::render_all(state); - } if let Some(result) = monitor .as_mut() .and_then(|monitor| monitor.poll(std::time::Instant::now())) @@ -124,9 +118,15 @@ fn main() -> Result<(), Box> { }, )?; let result = event_loop.run(None, &mut state, |state| { + // The decoder wakes the loop after publishing its result, including + // when no output has a pending frame. + let wallpaper_changed = state.wallpaper.poll(); + if wallpaper_changed { + state.backdrop_generation = state.backdrop_generation.wrapping_add(1); + } // All input/Wayland callbacks have returned, releasing seat locks. // Coalesce cursor changes and redraw here, never inside cursor_image. - if std::mem::take(&mut state.cursor_redraw_pending) { + if std::mem::take(&mut state.cursor_redraw_pending) || wallpaper_changed { backends::direct::render_all(state); } // Registry/sync/configure replies must not depend on a submitted diff --git a/compositor/src/state.rs b/compositor/src/state.rs index b7a76f4..5f6b5a5 100644 --- a/compositor/src/state.rs +++ b/compositor/src/state.rs @@ -602,7 +602,10 @@ impl Ferese { resize_transactions: HashMap::new(), resize_snapshots: HashMap::new(), nested_backend: None, - wallpaper: crate::wallpaper::WallpaperState::new(config.wallpaper), + wallpaper: crate::wallpaper::WallpaperState::with_wakeup( + config.wallpaper, + Some(event_loop.get_signal()), + ), maximized_windows: HashSet::new(), maximized_column_widths: HashMap::new(), window_stack: crate::stacking::WindowStack::default(), diff --git a/compositor/src/wallpaper.rs b/compositor/src/wallpaper.rs index fabafd3..898d90d 100644 --- a/compositor/src/wallpaper.rs +++ b/compositor/src/wallpaper.rs @@ -67,15 +67,25 @@ pub(crate) struct WallpaperState { pixels: Option, textures: HashMap, upload_retries: HashMap, + wakeup: Option, } impl WallpaperState { + #[cfg(test)] pub fn new(config: WallpaperConfig) -> Self { + Self::with_wakeup(config, None) + } + + pub fn with_wakeup( + config: WallpaperConfig, + wakeup: Option, + ) -> Self { let retained = config.clone(); let (sender, receiver) = mpsc::channel(); let owned = config.path.as_ref().is_some_and(|path| path.is_file()); if owned { let path = config.path.unwrap(); + let wakeup = wakeup.clone(); std::thread::spawn(move || { let load = || -> Result { let (width, height) = @@ -88,6 +98,9 @@ impl WallpaperState { .map_err(|e| e.to_string()) }; let _ = sender.send(load()); + if let Some(wakeup) = wakeup { + wakeup.wakeup(); + } }); } Self { @@ -100,6 +113,7 @@ impl WallpaperState { pixels: None, textures: HashMap::new(), upload_retries: HashMap::new(), + wakeup, } } @@ -126,7 +140,7 @@ impl WallpaperState { tracing::warn!("new wallpaper is unavailable; retaining previous image"); return; } - let mut replacement = Self::new(config); + let mut replacement = Self::with_wakeup(config, self.wakeup.clone()); self.config = replacement.config; self.mode = replacement.mode; self.owned = replacement.owned; @@ -296,6 +310,29 @@ fn image_geometry( mod tests { use super::*; + #[test] + fn decoder_wakes_an_idle_event_loop() { + use smithay::reexports::calloop::{EventLoop, timer::Timer}; + let mut event_loop = EventLoop::::try_new().unwrap(); + event_loop + .handle() + .insert_source(Timer::from_duration(Duration::from_secs(5)), |_, _, _| { + panic!("wallpaper decoder failed to wake the idle event loop"); + }) + .unwrap(); + let signal = event_loop.get_signal(); + let mut state = + WallpaperState::with_wakeup(WallpaperConfig::default(), Some(signal.clone())); + event_loop + .run(None, &mut state, |state| { + if state.poll() { + assert!(state.pixels.is_some()); + signal.stop(); + } + }) + .unwrap(); + } + #[test] fn failed_uploads_back_off_per_context_and_reset_for_new_pixels() { use smithay::backend::renderer::ContextId; diff --git a/compositor/src/winit.rs b/compositor/src/winit.rs index 7d08af6..c5d17d7 100644 --- a/compositor/src/winit.rs +++ b/compositor/src/winit.rs @@ -1068,9 +1068,6 @@ fn output_elements( ); return elements; } - if state.wallpaper.poll() { - state.backdrop_generation = state.backdrop_generation.wrapping_add(1); - } let Some(output_geometry) = state.space.output_geometry(output) else { return Vec::new(); };