Schedule redraws for pending wallpaper upload retries

This commit is contained in:
Samuel Onoja committed 2026-09-27 07:08:49 +01:00
1 parent 4da59f3906
commit 08a586612f
2 files changed
+95 -1

No files matched your search

+9 -1
View File
@@ -135,9 +135,17 @@ fn main() -> Result<(), Box<dyn Error>> {
}
// 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) || wallpaper_changed {
let wallpaper_retry = state.wallpaper.take_retry_wakeup();
if std::mem::take(&mut state.cursor_redraw_pending) || wallpaper_changed || wallpaper_retry
{
backends::direct::render_all(state);
}
if let Err(error) = state
.wallpaper
.arm_retry_timer(&reload_handle, std::time::Instant::now())
{
warn!(%error, "could not schedule wallpaper upload retry");
}
// Registry/sync/configure replies must not depend on a submitted
// frame: at startup clients need these before they can draw anything.
// This also keeps idle DRM sessions responsive when there is no damage.
+86
View File
@@ -16,8 +16,10 @@ use smithay::{
utils::{Buffer, Physical, Rectangle, Size},
};
use std::{
cell::Cell,
collections::HashMap,
path::PathBuf,
rc::Rc,
sync::mpsc,
time::{Duration, Instant},
};
@@ -68,6 +70,8 @@ pub(crate) struct WallpaperState {
textures: HashMap<ErasedContextId, WallpaperTexture>,
upload_retries: HashMap<ErasedContextId, Instant>,
wakeup: Option<smithay::reexports::calloop::LoopSignal>,
retry_timer_pending: Rc<Cell<bool>>,
retry_wakeup: Rc<Cell<bool>>,
}
impl WallpaperState {
@@ -114,6 +118,8 @@ impl WallpaperState {
textures: HashMap::new(),
upload_retries: HashMap::new(),
wakeup,
retry_timer_pending: Rc::default(),
retry_wakeup: Rc::default(),
}
}
@@ -163,6 +169,45 @@ impl WallpaperState {
.is_none_or(|retry| now >= *retry)
}
pub(crate) fn take_retry_wakeup(&self) -> bool {
self.retry_wakeup.replace(false) && !self.upload_retries.is_empty()
}
fn next_retry_deadline(&self, now: Instant) -> Option<Instant> {
self.upload_retries.values().copied().min().map(|deadline| {
// A suspended/hidden output may not have attempted an upload when
// the timer fired. Avoid immediately rescheduling an expired timer.
if deadline <= now {
now + UPLOAD_RETRY_DELAY
} else {
deadline
}
})
}
pub(crate) fn arm_retry_timer<Data: 'static>(
&self,
handle: &smithay::reexports::calloop::LoopHandle<'static, Data>,
now: Instant,
) -> Result<(), Box<dyn std::error::Error>> {
use smithay::reexports::calloop::timer::{TimeoutAction, Timer};
if self.retry_timer_pending.get() {
return Ok(());
}
let Some(deadline) = self.next_retry_deadline(now) else {
return Ok(());
};
let pending = self.retry_timer_pending.clone();
let wakeup = self.retry_wakeup.clone();
handle.insert_source(Timer::from_deadline(deadline), move |_, _, _| {
pending.set(false);
wakeup.set(true);
TimeoutAction::Drop
})?;
self.retry_timer_pending.set(true);
Ok(())
}
pub fn poll(&mut self) -> bool {
let Some(receiver) = &self.receiver else {
return false;
@@ -310,6 +355,47 @@ fn image_geometry(
mod tests {
use super::*;
#[test]
fn failed_upload_retry_wakes_idle_loop_and_does_not_spin_when_output_is_inactive() {
use smithay::backend::renderer::ContextId;
use smithay::reexports::calloop::EventLoop;
let mut event_loop = EventLoop::<()>::try_new().unwrap();
let mut state = WallpaperState::new(WallpaperConfig {
path: None,
mode: WallpaperMode::Fill,
});
let context = ContextId::<GlesTexture>::new().erased();
let now = Instant::now();
state
.upload_retries
.insert(context.clone(), now + Duration::from_millis(10));
state.arm_retry_timer(&event_loop.handle(), now).unwrap();
state.arm_retry_timer(&event_loop.handle(), now).unwrap();
assert!(state.retry_timer_pending.get());
assert!(!state.take_retry_wakeup());
event_loop
.dispatch(Some(Duration::from_secs(1)), &mut ())
.unwrap();
assert!(!state.retry_timer_pending.get());
assert!(
state.take_retry_wakeup(),
"retry must request redraw without input or client activity"
);
assert!(!state.take_retry_wakeup());
let after = Instant::now();
assert_eq!(
state.next_retry_deadline(after),
Some(after + UPLOAD_RETRY_DELAY)
);
state.forget_context(&context);
assert_eq!(state.next_retry_deadline(after), None);
state.retry_wakeup.set(true);
assert!(
!state.take_retry_wakeup(),
"cleared failures must not trigger stale redraws"
);
}
#[test]
fn decoder_wakes_an_idle_event_loop() {
use smithay::reexports::calloop::{EventLoop, timer::Timer};