diff --git a/daemon/src/animations/mod.rs b/daemon/src/animations/mod.rs index d0f48034..948befa4 100644 --- a/daemon/src/animations/mod.rs +++ b/daemon/src/animations/mod.rs @@ -22,12 +22,12 @@ use self::anim_barrier::ArcAnimBarrier; ///The default thread stack size of 2MiB is way too overkill for our purposes const STACK_SIZE: usize = 1 << 17; //128KiB -pub struct Animator { +pub(super) struct Animator { anim_barrier: ArcAnimBarrier, } impl Animator { - pub fn new() -> Self { + pub(super) fn new() -> Self { Self { anim_barrier: ArcAnimBarrier::new(), } @@ -69,7 +69,11 @@ impl Animator { } } - pub fn transition(&mut self, bytes: Vec, wallpapers: Vec>>) -> Answer { + pub(super) fn transition( + &mut self, + bytes: Vec, + wallpapers: Vec>>, + ) -> Answer { match thread::Builder::new() .stack_size(1 << 15) .name("transition spawner".to_string()) @@ -140,8 +144,7 @@ impl Animator { continue; } - let (success, buffer) = - wallpapers[i].canvas_change(|canvas| frame.unpack(canvas)); + let success = wallpapers[i].canvas_change(|canvas| frame.unpack(canvas)); if !success { error!("failed to unpack frame, canvas is smaller than expected"); @@ -150,7 +153,7 @@ impl Animator { continue; } - wallpapers[i].draw(&buffer); + wallpapers[i].draw(); i += 1; } @@ -169,7 +172,11 @@ impl Animator { } } - pub fn animate(&mut self, bytes: Vec, wallpapers: Vec>>) -> Answer { + pub(super) fn animate( + &mut self, + bytes: Vec, + wallpapers: Vec>>, + ) -> Answer { let barrier = self.anim_barrier.clone(); match thread::Builder::new() .stack_size(1 << 15) diff --git a/daemon/src/animations/transitions.rs b/daemon/src/animations/transitions.rs index c5c7f91a..ebb3ad22 100644 --- a/daemon/src/animations/transitions.rs +++ b/daemon/src/animations/transitions.rs @@ -42,7 +42,7 @@ macro_rules! change_cols { }; } -pub struct Transition { +pub(super) struct Transition { animation_tokens: Vec, wallpapers: Vec>, dimensions: (u32, u32), @@ -59,7 +59,7 @@ pub struct Transition { /// All transitions return whether or not they completed impl Transition { - pub fn new( + pub(super) fn new( wallpapers: Vec>, dimensions: (u32, u32), transition: utils::ipc::ArchivedTransition, @@ -92,7 +92,7 @@ impl Transition { } } - pub fn execute(mut self, new_img: &[u8]) { + pub(super) fn execute(mut self, new_img: &[u8]) { debug!("Starting transitions"); match self.transition_type { ArchivedTransitionType::Simple => self.simple(new_img), @@ -140,12 +140,12 @@ impl Transition { while !done { done = true; for wallpaper in self.wallpapers.iter_mut() { - let (_, buffer) = wallpaper.canvas_change(|canvas| { + wallpaper.canvas_change(|canvas| { for (old, new) in canvas.chunks_exact_mut(4).zip(new_img.chunks_exact(3)) { change_cols!(step, old, new, done); } }); - wallpaper.draw(&buffer); + wallpaper.draw(); } self.send_frame(&mut now); } @@ -158,7 +158,7 @@ impl Transition { let mut now = Instant::now(); while start.elapsed().as_secs_f64() < seq.duration() { for wallpaper in self.wallpapers.iter_mut() { - let (_, buffer) = wallpaper.canvas_change(|canvas| { + wallpaper.canvas_change(|canvas| { canvas .par_chunks_exact_mut(4) .zip(new_img.par_chunks_exact(3)) @@ -169,7 +169,7 @@ impl Transition { } }); }); - wallpaper.draw(&buffer); + wallpaper.draw(); } self.send_frame(&mut now); step = seq.now() as f64; @@ -227,7 +227,7 @@ impl Transition { while start.elapsed().as_secs_f64() < seq.duration() { for wallpaper in self.wallpapers.iter_mut() { - let (_, buffer) = wallpaper.canvas_change(|canvas| { + wallpaper.canvas_change(|canvas| { canvas .par_chunks_exact_mut(4) .zip(new_img.par_chunks_exact(3)) @@ -240,7 +240,7 @@ impl Transition { } }); }); - wallpaper.draw(&buffer); + wallpaper.draw(); } self.send_frame(&mut now); @@ -288,7 +288,7 @@ impl Transition { while start.elapsed().as_secs_f64() < seq.duration() { for wallpaper in self.wallpapers.iter_mut() { - let (_, buffer) = wallpaper.canvas_change(|canvas| { + wallpaper.canvas_change(|canvas| { canvas .par_chunks_exact_mut(4) .zip(new_img.par_chunks_exact(3)) @@ -301,7 +301,7 @@ impl Transition { } }); }); - wallpaper.draw(&buffer); + wallpaper.draw(); } self.send_frame(&mut now); @@ -335,7 +335,7 @@ impl Transition { let mut now = Instant::now(); while start.elapsed().as_secs_f64() < seq.duration() { for wallpaper in self.wallpapers.iter_mut() { - let (_, buffer) = wallpaper.canvas_change(|canvas| { + wallpaper.canvas_change(|canvas| { canvas .par_chunks_exact_mut(4) .zip(new_img.par_chunks_exact(3)) @@ -354,7 +354,7 @@ impl Transition { } }); }); - wallpaper.draw(&buffer); + wallpaper.draw(); } self.send_frame(&mut now); @@ -386,7 +386,7 @@ impl Transition { let mut now = Instant::now(); while start.elapsed().as_secs_f64() < seq.duration() { for wallpaper in self.wallpapers.iter_mut() { - let (_, buffer) = wallpaper.canvas_change(|canvas| { + wallpaper.canvas_change(|canvas| { canvas .par_chunks_exact_mut(4) .zip(new_img.par_chunks_exact(3)) @@ -405,7 +405,7 @@ impl Transition { } }); }); - wallpaper.draw(&buffer); + wallpaper.draw(); } self.send_frame(&mut now); diff --git a/daemon/src/bump_pool.rs b/daemon/src/bump_pool.rs new file mode 100644 index 00000000..09adb8f9 --- /dev/null +++ b/daemon/src/bump_pool.rs @@ -0,0 +1,175 @@ +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + +use smithay_client_toolkit::shm::{raw::RawPool, Shm}; +use wayland_client::{ + protocol::{wl_buffer::WlBuffer, wl_shm}, + QueueHandle, +}; + +use crate::Daemon; + +#[derive(Debug)] +struct Buffer { + inner: WlBuffer, + released: Arc, +} + +impl Buffer { + fn new(inner: WlBuffer, released: Arc) -> Self { + Self { inner, released } + } +} + +impl Drop for Buffer { + fn drop(&mut self) { + self.inner.destroy(); + } +} + +#[derive(Debug)] +/// A pool implementation that only gives buffers of a fixed size, creating new ones if none of +/// them are freed. It also takes care of copying the previous buffer's content over to the new one +/// for us +pub(crate) struct BumpPool { + pool: RawPool, + buffers: Vec, + width: i32, + height: i32, + last_used_buffer: Option, +} + +impl BumpPool { + /// We assume `width` and `height` have already been multiplied by their scale factor + pub(crate) fn new(width: i32, height: i32, shm: &Shm, qh: &QueueHandle) -> Self { + let len = width as usize * height as usize * 4; + let mut pool = RawPool::new(len, shm).expect("failed to create RawPool"); + let released = Arc::new(AtomicBool::new(true)); + let buffers = vec![Buffer::new( + pool.create_buffer( + 0, + width, + height, + width * 4, + wl_shm::Format::Xrgb8888, + released.clone(), + qh, + ), + released, + )]; + + Self { + pool, + buffers, + width, + height, + last_used_buffer: None, + } + } + + #[inline] + fn buffer_len(&self) -> usize { + self.width as usize * self.height as usize * 4 + } + + #[inline] + fn buffer_offset(&self, buffer_index: usize) -> usize { + self.buffer_len() * buffer_index + } + + #[inline] + fn occupied_bytes(&self) -> usize { + self.buffer_offset(self.buffers.len()) + } + + /// resizes the pool and creates a new WlBuffer at the next free offset + fn grow(&mut self, qh: &QueueHandle) { + //TODO: CHECK IF WE HAVE SIZE + let len = self.buffer_len(); + self.pool + .resize(self.occupied_bytes() + len) + .expect("failed to resize RawPool"); + let released = Arc::new(AtomicBool::new(true)); + let new_buffer_index = self.buffers.len(); + self.buffers.push(Buffer::new( + self.pool.create_buffer( + self.buffer_offset(new_buffer_index).try_into().unwrap(), + self.width, + self.height, + self.width * 4, + wl_shm::Format::Xrgb8888, + released.clone(), + qh, + ), + released, + )); + log::info!( + "BumpPool with: {} buffers. Size: {}Kb", + self.buffers.len(), + self.pool.len() / 1024 + ); + } + + /// Returns a drawable surface. If we can't find a free buffer, we request more memory + /// + /// This function automatically handles copying the previous buffer over onto the new one + pub(crate) fn get_drawable(&mut self, qh: &QueueHandle) -> &mut [u8] { + let (i, buf) = match self + .buffers + .iter() + .enumerate() + .find(|(_, b)| b.released.load(Ordering::Acquire)) + { + Some((i, buf)) => (i, buf), + None => { + self.grow(qh); + (self.buffers.len() - 1, self.buffers.last().unwrap()) + } + }; + + let len = self.buffer_len(); + let offset = self.buffer_offset(i); + buf.released.store(false, Ordering::Release); + + if let Some(i) = self.last_used_buffer { + let last_offset = self.buffer_offset(i); + self.pool + .mmap() + .copy_within(last_offset..last_offset + len, offset); + } + self.last_used_buffer = Some(i); + + &mut self.pool.mmap()[offset..offset + len] + } + + /// gets the last buffer we've drawn to + /// + /// This may return None if there was a resize request in-between the last call to get_drawable + #[inline] + pub(crate) fn get_commitable_buffer(&self) -> Option<&WlBuffer> { + self.last_used_buffer.map(|i| &self.buffers[i].inner) + } + + /// We assume `width` and `height` have already been multiplied by their scale factor + pub(crate) fn resize(&mut self, width: i32, height: i32, qh: &QueueHandle) { + self.width = width; + self.height = height; + self.last_used_buffer = None; + self.buffers.clear(); + let released = Arc::new(AtomicBool::new(true)); + self.buffers.push(Buffer::new( + self.pool.create_buffer( + 0, + width, + height, + width * 4, + wl_shm::Format::Xrgb8888, + released.clone(), + qh, + ), + released, + )); + } +} diff --git a/daemon/src/main.rs b/daemon/src/main.rs index ca0c2aac..f5672b01 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -3,6 +3,7 @@ //! of `expects`, **on purpose**, because we **want** to unwind and exit when they happen mod animations; +pub mod bump_pool; mod wallpaper; use log::{debug, error, info, warn, LevelFilter}; use nix::{ @@ -22,7 +23,7 @@ use std::{ }, sync::{ atomic::{AtomicBool, Ordering}, - Arc, Mutex, OnceLock, + Arc, OnceLock, }, }; @@ -36,13 +37,13 @@ use smithay_client_toolkit::{ wlr_layer::{Layer, LayerShell, LayerShellHandler, LayerSurface, LayerSurfaceConfigure}, WaylandSurface, }, - shm::{multi::MultiPool, Shm, ShmHandler}, + shm::{Shm, ShmHandler}, }; use wayland_client::{ globals::{registry_queue_init, GlobalList}, - protocol::{wl_output, wl_surface}, - Connection, QueueHandle, + protocol::{wl_buffer::WlBuffer, wl_output, wl_surface}, + Connection, Dispatch, QueueHandle, }; use utils::ipc::{get_socket_path, Answer, ArchivedRequest, BgInfo, Request}; @@ -233,7 +234,6 @@ struct Daemon { registry_state: RegistryState, output_state: OutputState, shm: Shm, - pool: wallpaper::MtShmPool, // swww stuff wallpapers: Vec>, @@ -251,7 +251,6 @@ impl Daemon { let layer_shell = LayerShell::bind(globals, qh).expect("layer shell is not available"); let shm = Shm::bind(globals, qh).expect("wl_shm is not available"); - let pool = MultiPool::new(&shm).expect("failed to create MultiPool"); Self { // Outputs may be hotplugged at runtime, therefore we need to setup a registry state to @@ -260,7 +259,6 @@ impl Daemon { output_state: OutputState::new(globals, qh), compositor_state, shm, - pool: Arc::new(Mutex::new(pool)), layer_shell, wallpapers: Vec::new(), @@ -300,8 +298,8 @@ impl Daemon { } for wallpaper in wallpapers { wallpaper.set_img_info(utils::ipc::BgImg::Color(color)); - let buffer = wallpaper.clear(color); - wallpaper.draw(&buffer); + wallpaper.clear(color); + wallpaper.draw(); } wake_poll(); }) { @@ -405,11 +403,11 @@ impl CompositorHandler for Daemon { _conn: &Connection, _qh: &QueueHandle, surface: &wl_surface::WlSurface, - _time: u32, + time: u32, ) { for wallpaper in self.wallpapers.iter_mut() { if wallpaper.has_surface(surface) { - wallpaper.draw(&wallpaper.canvas_change(|_| {}).1); + wallpaper.frame_callback_completed(time); return; } } @@ -478,7 +476,8 @@ impl OutputHandler for Daemon { self.wallpapers.push(Arc::new(Wallpaper::new( output_info, layer_surface, - Arc::clone(&self.pool), + &self.shm, + qh, ))); debug!("Output count: {}", self.wallpapers.len()); } @@ -559,6 +558,24 @@ impl LayerShellHandler for Daemon { } } +impl Dispatch> for Daemon { + fn event( + _state: &mut Self, + _proxy: &WlBuffer, + event: ::Event, + data: &Arc, + _conn: &Connection, + _qhandle: &QueueHandle, + ) { + match event { + wayland_client::protocol::wl_buffer::Event::Release => { + data.store(true, Ordering::Release); + } + _ => log::error!("There should be no buffer events other than Release"), + } + } +} + delegate_compositor!(Daemon); delegate_output!(Daemon); delegate_shm!(Daemon); diff --git a/daemon/src/wallpaper.rs b/daemon/src/wallpaper.rs index 47c85d6b..992b1da8 100644 --- a/daemon/src/wallpaper.rs +++ b/daemon/src/wallpaper.rs @@ -4,7 +4,7 @@ use std::{ num::NonZeroI32, sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, - Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, + Arc, Condvar, Mutex, RwLock, }, }; @@ -14,15 +14,12 @@ use smithay_client_toolkit::{ wlr_layer::{Anchor, KeyboardInteractivity, LayerSurface}, WaylandSurface, }, - shm, + shm::Shm, }; -use wayland_client::protocol::{wl_buffer::WlBuffer, wl_shm, wl_surface::WlSurface}; +use wayland_client::{protocol::wl_surface::WlSurface, QueueHandle}; -/// The memory pool wallpapers use -pub type ShmPool = shm::multi::MultiPool<(WlSurface, u32)>; -/// The memory pool, multithreaded -pub type MtShmPool = Arc>; +use crate::{bump_pool::BumpPool, Daemon}; #[derive(Debug)] struct AnimationState { @@ -31,23 +28,30 @@ struct AnimationState { } #[derive(Debug)] -pub struct AnimationToken { +pub(super) struct AnimationToken { id: usize, transition_done: Arc, } impl AnimationToken { - pub fn is_transition_done(&self) -> bool { + pub(super) fn is_transition_done(&self) -> bool { self.transition_done.load(Ordering::Acquire) } - pub fn set_transition_done(&self, wallpaper: &Wallpaper) { + pub(super) fn set_transition_done(&self, wallpaper: &Wallpaper) { if wallpaper.has_animation_id(self) { self.transition_done.store(true, Ordering::Release); } } } +struct FrameCallbackHandler { + cvar: Condvar, + /// This time doesn't really mean anything. We don't really use it for frame timing, but we + /// store it for the sake of signaling when the compositor emitted the last frame callback + time: Mutex>, +} + /// Owns all the necessary information for drawing. #[derive(Debug)] struct WallpaperInner { @@ -55,21 +59,28 @@ struct WallpaperInner { height: NonZeroI32, scale_factor: NonZeroI32, + pool: BumpPool, img: BgImg, } -pub struct Wallpaper { +pub(super) struct Wallpaper { output_id: u32, inner: RwLock, layer_surface: LayerSurface, animation_state: AnimationState, - pool: MtShmPool, pub configured: AtomicBool, + qh: QueueHandle, + frame_callback_handler: FrameCallbackHandler, } impl Wallpaper { - pub fn new(output_info: OutputInfo, layer_surface: LayerSurface, pool: MtShmPool) -> Self { + pub(crate) fn new( + output_info: OutputInfo, + layer_surface: LayerSurface, + shm: &Shm, + qh: &QueueHandle, + ) -> Self { let (width, height): (NonZeroI32, NonZeroI32) = if let Some(size) = output_info.logical_size { if size.0 == 0 || size.1 == 0 { @@ -83,6 +94,11 @@ impl Wallpaper { let scale_factor = NonZeroI32::new(output_info.scale_factor).unwrap(); + let frame_callback_handler = FrameCallbackHandler { + cvar: Condvar::new(), + time: Mutex::new(Some(0)), // we do not have to wait for the first frame + }; + // Configure the layer surface layer_surface.set_anchor(Anchor::all()); layer_surface.set_exclusive_zone(-1); @@ -94,32 +110,41 @@ impl Wallpaper { .unwrap(); // commit so that the compositor send the initial configuration layer_surface.commit(); + layer_surface + .wl_surface() + .frame(qh, layer_surface.wl_surface().clone()); + + let w = width.get() * scale_factor.get(); + let h = height.get() * scale_factor.get(); + let pool = BumpPool::new(w, h, shm, qh); Self { output_id: output_info.id, layer_surface, - pool, inner: RwLock::new(WallpaperInner { width, height, scale_factor, img: BgImg::Color([0, 0, 0]), + pool, }), animation_state: AnimationState { id: AtomicUsize::new(0), transition_finished: Arc::new(AtomicBool::new(false)), }, configured: AtomicBool::new(false), + qh: qh.clone(), + frame_callback_handler, } } #[inline] - pub fn has_id(&self, id: u32) -> bool { + pub(super) fn has_id(&self, id: u32) -> bool { self.output_id == id } #[inline] - pub fn has_animation_id(&self, token: &AnimationToken) -> bool { + pub(super) fn has_animation_id(&self, token: &AnimationToken) -> bool { self.animation_state .id .load(std::sync::atomic::Ordering::Acquire) @@ -127,75 +152,33 @@ impl Wallpaper { } #[inline] - pub fn has_surface(&self, surface: &WlSurface) -> bool { + pub(super) fn has_surface(&self, surface: &WlSurface) -> bool { self.layer_surface.wl_surface() == surface } - pub fn get_dimensions(&self) -> (u32, u32) { - let inner = self.lock_inner(); + pub(super) fn get_dimensions(&self) -> (u32, u32) { + let inner = self.inner.read().unwrap(); let width = inner.width.get() as u32; let height = inner.height.get() as u32; let scale_factor = inner.scale_factor.get() as u32; (width * scale_factor, height * scale_factor) } - #[inline] - fn lock(&self) -> (RwLockReadGuard, MutexGuard) { - (self.lock_inner(), self.pool.lock().unwrap()) - } - - #[inline] - fn lock_mut(&self) -> (RwLockWriteGuard, MutexGuard) { - (self.lock_inner_mut(), self.pool.lock().unwrap()) - } - - #[inline] - fn lock_inner(&self) -> RwLockReadGuard { - self.inner.read().unwrap() - } - - #[inline] - fn lock_inner_mut(&self) -> RwLockWriteGuard { - self.inner.write().unwrap() - } - - pub fn canvas_change(&self, f: F) -> (T, WlBuffer) + pub(super) fn canvas_change(&self, f: F) -> T where F: FnOnce(&mut [u8]) -> T, { - let (inner, mut pool) = self.lock(); - let width = inner.width.get() * inner.scale_factor.get(); - let stride = width * 4; - let height = inner.height.get() * inner.scale_factor.get(); - drop(inner); - let mut frame = 0u32; - loop { - match pool.create_buffer( - width, - stride, - height, - &(self.layer_surface.wl_surface().clone(), frame), - wl_shm::Format::Xrgb8888, - ) { - Ok((_offset, buffer, canvas)) => return (f(canvas), buffer.clone()), - Err(e) => match e { - smithay_client_toolkit::shm::multi::PoolError::InUse => frame += 1, - smithay_client_toolkit::shm::multi::PoolError::Overlap => { - pool.remove(&(self.layer_surface.wl_surface().clone(), frame)); - } - smithay_client_toolkit::shm::multi::PoolError::NotFound => unreachable!(), - }, - } - } + let mut inner = self.inner.write().unwrap(); + f(inner.pool.get_drawable(&self.qh)) } #[inline] - pub fn get_img_info(&self) -> BgImg { - self.lock_inner().img.clone() + pub(super) fn get_img_info(&self) -> BgImg { + self.inner.read().unwrap().img.clone() } #[inline] - pub fn create_animation_token(&self) -> AnimationToken { + pub(super) fn create_animation_token(&self) -> AnimationToken { let id = self.animation_state.id.load(Ordering::Acquire); AnimationToken { id, @@ -203,16 +186,22 @@ impl Wallpaper { } } + #[inline] + pub(super) fn frame_callback_completed(&self, time: u32) { + *self.frame_callback_handler.time.lock().unwrap() = Some(time); + self.frame_callback_handler.cvar.notify_all(); + } + /// Stops all animations with the current id, by increasing that id #[inline] - pub fn stop_animations(&self) { + pub(super) fn stop_animations(&self) { self.animation_state.id.fetch_add(1, Ordering::AcqRel); self.animation_state .transition_finished .store(false, Ordering::Release); } - pub fn clear(&self, color: [u8; 3]) -> WlBuffer { + pub(super) fn clear(&self, color: [u8; 3]) { self.canvas_change(|canvas| { for pixel in canvas.chunks_exact_mut(4) { pixel[2] = color[0]; @@ -220,27 +209,42 @@ impl Wallpaper { pixel[0] = color[2]; } }) - .1 } - pub fn set_img_info(&self, img_info: BgImg) { + pub(super) fn set_img_info(&self, img_info: BgImg) { log::debug!("output {} - drawing: {}", self.output_id, img_info); - self.lock_inner_mut().img = img_info; + self.inner.write().unwrap().img = img_info; } - pub fn draw(&self, buf: &WlBuffer) { - let inner = self.lock_inner(); - let width = inner.width.get() * inner.scale_factor.get(); - let height = inner.height.get() * inner.scale_factor.get(); - drop(inner); - - let surface = self.layer_surface.wl_surface(); - surface.attach(Some(buf), 0, 0); - surface.damage_buffer(0, 0, width, height); - surface.commit(); + pub(super) fn draw(&self) { + { + let mut time = self.frame_callback_handler.time.lock().unwrap(); + while time.is_none() { + log::debug!("waiting for condvar"); + time = self.frame_callback_handler.cvar.wait(time).unwrap(); + } + *time = None; + } + let inner = self.inner.read().unwrap(); + if let Some(buf) = inner.pool.get_commitable_buffer() { + let width = inner.width.get() * inner.scale_factor.get(); + let height = inner.height.get() * inner.scale_factor.get(); + let surface = self.layer_surface.wl_surface(); + surface.attach(Some(buf), 0, 0); + drop(inner); + surface.damage_buffer(0, 0, width, height); + surface.commit(); + surface.frame(&self.qh, surface.clone()); + } else { + drop(inner); + // commit and send another frame request, since we consumed the previous one + let surface = self.layer_surface.wl_surface(); + surface.commit(); + surface.frame(&self.qh, surface.clone()); + } } - pub fn resize( + pub(super) fn resize( &self, width: Option, height: Option, @@ -249,7 +253,7 @@ impl Wallpaper { if let Some(s) = scale_factor { self.layer_surface.set_buffer_scale(s.get() as u32).unwrap(); } - let (mut inner, mut pool) = self.lock_mut(); + let mut inner = self.inner.write().unwrap(); let width = width.unwrap_or(inner.width); let height = height.unwrap_or(inner.height); let scale_factor = scale_factor.unwrap_or(inner.scale_factor); @@ -258,25 +262,23 @@ impl Wallpaper { } self.stop_animations(); - // remove all buffers with the previous size - let mut frame = 0u32; - while pool - .remove(&(self.layer_surface.wl_surface().clone(), frame)) - .is_some() - { - frame += 1; - } - drop(pool); - inner.width = width; inner.height = height; inner.scale_factor = scale_factor; - - self.layer_surface - .set_size(inner.width.get() as u32, inner.height.get() as u32); inner.img = BgImg::Color([0, 0, 0]); + + let w = width.get() * scale_factor.get(); + let h = height.get() * scale_factor.get(); + inner.pool.resize(w, h, &self.qh); drop(inner); + + *self.frame_callback_handler.time.lock().unwrap() = Some(0); + self.layer_surface + .set_size(width.get() as u32, height.get() as u32); self.layer_surface.commit(); + self.layer_surface + .wl_surface() + .frame(&self.qh, self.layer_surface.wl_surface().clone()); self.configured.store(false, Ordering::Release); } }