Wake the event loop when wallpaper decoding completes

This commit is contained in:
Samuel Onoja committed 2026-09-27 07:06:15 +01:00
1 parent 6dd132a4a5
commit b469929847
4 files changed
+49 -12

No files matched your search

+7 -7
View File
@@ -90,12 +90,6 @@ fn main() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
},
)?;
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
+4 -1
View File
@@ -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(),
+38 -1
View File
@@ -67,15 +67,25 @@ pub(crate) struct WallpaperState {
pixels: Option<image::RgbaImage>,
textures: HashMap<ErasedContextId, WallpaperTexture>,
upload_retries: HashMap<ErasedContextId, Instant>,
wakeup: Option<smithay::reexports::calloop::LoopSignal>,
}
impl WallpaperState {
#[cfg(test)]
pub fn new(config: WallpaperConfig) -> Self {
Self::with_wakeup(config, None)
}
pub fn with_wakeup(
config: WallpaperConfig,
wakeup: Option<smithay::reexports::calloop::LoopSignal>,
) -> 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<image::RgbaImage, String> {
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::<WallpaperState>::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;
-3
View File
@@ -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();
};