From bbd761d816347c24fb7273dff98accb4f2520565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sun, 19 Oct 2025 17:22:32 -0300 Subject: [PATCH 1/5] merge TransitionAnimator and ImageAnimator into one This merges the TransitionAnimator and ImageAnimator into one Animator enum. It has the advantage of using less memory than before, because we always allocated the same amount of TransitionAnimator and ImageAnimator anyway, and Animator has the same size as TransitionAnimator. It also cleans up a bit of the code, since we can remove some duplicated things. --- daemon/src/animations.rs | 133 +++++++++++++++++++++++---------------- daemon/src/main.rs | 87 +++++-------------------- 2 files changed, 93 insertions(+), 127 deletions(-) diff --git a/daemon/src/animations.rs b/daemon/src/animations.rs index 35848ef0..b031788a 100644 --- a/daemon/src/animations.rs +++ b/daemon/src/animations.rs @@ -9,7 +9,7 @@ use std::{ use common::{ compression::Decompressor, - ipc::{self, Animation, BgImg, ImgReq, PixelFormat}, + ipc::{self, BgImg, ImgReq, PixelFormat}, mmap::MmappedBytes, }; @@ -18,23 +18,24 @@ use crate::{WaylandObject, wallpaper::Wallpaper}; mod transitions; use transitions::Effect; -pub struct TransitionAnimator { +pub struct Animator { pub wallpapers: Vec>>, - fps: Duration, - effect: Effect, - img: MmappedBytes, - animation: Option, now: Instant, - over: bool, + animator: AnimatorKind, } -impl TransitionAnimator { +enum AnimatorKind { + Transition(Transition), + Animation(Animation), +} + +impl Animator { pub fn new( mut wallpapers: Vec>>, transition: &ipc::Transition, pixel_format: PixelFormat, img_req: ImgReq, - animation: Option, + animation: Option, ) -> Option { let ImgReq { img, path, dim, .. } = img_req; if wallpapers.is_empty() { @@ -54,17 +55,22 @@ impl TransitionAnimator { let effect = Effect::new(transition, pixel_format, dim); Some(Self { wallpapers, - effect, - fps, - img, - animation, now: Instant::now(), - over: false, + animator: AnimatorKind::Transition(Transition { + effect, + fps, + img, + animation, + over: false, + }), }) } pub fn time_to_draw(&self) -> std::time::Duration { - self.fps.saturating_sub(self.now.elapsed()) + match &self.animator { + AnimatorKind::Transition(transition) => transition.time_to_draw(&self.now), + AnimatorKind::Animation(animation) => animation.time_to_draw(&self.now), + } } pub fn updt_time(&mut self) { @@ -79,70 +85,89 @@ impl TransitionAnimator { ) -> bool { let Self { wallpapers, - effect, - img, - over, + animator, .. } = self; - if !*over { - *over = effect.execute(backend, objman, pixel_format, wallpapers, img.bytes()); - false - } else { - true + match animator { + AnimatorKind::Transition(transition) => { + if !transition.frame(backend, objman, wallpapers.as_mut_slice(), pixel_format) { + return false; + } + // Note: it needs to have more than a single frame, otherwise there is no point in + // animating it + if let Some(animation) = transition.animation.take() + && animation.animation.len() > 1 + { + *animator = AnimatorKind::Animation(Animation { + animation, + decompressor: Decompressor::new(), + i: 0, + }); + return false; + } + true + } + AnimatorKind::Animation(animation) => { + animation.frame(backend, objman, wallpapers, pixel_format); + false + } } } +} + +struct Transition { + fps: Duration, + effect: Effect, + img: MmappedBytes, + animation: Option, + over: bool, +} + +impl Transition { + fn time_to_draw(&self, now: &Instant) -> std::time::Duration { + self.fps.saturating_sub(now.elapsed()) + } - pub fn into_image_animator(self) -> Option { + fn frame( + &mut self, + backend: &mut Waybackend, + objman: &mut ObjectManager, + wallpapers: &mut [Rc>], + pixel_format: PixelFormat, + ) -> bool { let Self { - wallpapers, - animation, - .. + effect, img, over, .. } = self; - - if let Some(animation) = animation { - // it needs to have more than a single frame, otherwise there is no point in animating - // it - if animation.animation.len() > 1 { - return Some(ImageAnimator { - now: Instant::now(), - wallpapers, - animation, - decompressor: Decompressor::new(), - i: 0, - }); - } + if !*over { + *over = effect.execute(backend, objman, pixel_format, wallpapers, img.bytes()); + false + } else { + true } - None } } -pub struct ImageAnimator { - now: Instant, - pub wallpapers: Vec>>, - animation: Animation, +struct Animation { + animation: ipc::Animation, decompressor: Decompressor, i: usize, } -impl ImageAnimator { - pub fn time_to_draw(&self) -> std::time::Duration { +impl Animation { + fn time_to_draw(&self, now: &Instant) -> std::time::Duration { self.animation.animation[self.i % self.animation.animation.len()] .1 - .saturating_sub(self.now.elapsed()) + .saturating_sub(now.elapsed()) } - pub fn updt_time(&mut self) { - self.now = Instant::now(); - } - - pub fn frame( + fn frame( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, + wallpapers: &mut Vec>>, pixel_format: PixelFormat, ) { let Self { - wallpapers, animation, decompressor, i, diff --git a/daemon/src/main.rs b/daemon/src/main.rs index 27ca7539..44bb0040 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -28,7 +28,7 @@ use std::{ time::Duration, }; -use animations::{ImageAnimator, TransitionAnimator}; +use animations::Animator; use common::ipc::{ Answer, BgInfo, ImageReq, IpcSocket, PixelFormat, RequestRecv, RequestSend, Scale, Server, }; @@ -62,8 +62,7 @@ struct Daemon { layer: Layer, pixel_format: PixelFormat, wallpapers: Vec>>, - transition_animators: Vec, - image_animators: Vec, + animators: Vec, namespace: String, use_cache: bool, paused: bool, @@ -119,9 +118,8 @@ impl Daemon { layer_shell, layer: args.layer, pixel_format: args.format.unwrap_or(PixelFormat::Argb), - wallpapers: Vec::new(), - transition_animators: Vec::new(), - image_animators: Vec::new(), + wallpapers: Vec::with_capacity(1), + animators: Vec::with_capacity(1), namespace: args.namespace, use_cache: !args.no_cache, paused: false, @@ -203,15 +201,11 @@ impl Daemon { }; let wallpapers = self.find_wallpapers_by_names(&names); self.stop_animations(&wallpapers); - if let Some(mut transition) = TransitionAnimator::new( - wallpapers, - &transition, - self.pixel_format, - img, - animation, - ) { - transition.frame(&mut self.backend, &mut self.objman, self.pixel_format); - self.transition_animators.push(transition); + if let Some(mut animator) = + Animator::new(wallpapers, &transition, self.pixel_format, img, animation) + { + animator.frame(&mut self.backend, &mut self.objman, self.pixel_format); + self.animators.push(animator); } } self.set_poll_time(Timespec { @@ -249,8 +243,8 @@ impl Daemon { self.poll_time = None; let mut i = 0; - while i < self.transition_animators.len() { - let animator = &mut self.transition_animators[i]; + while i < self.animators.len() { + let animator = &mut self.animators[i]; if animator .wallpapers .iter() @@ -279,52 +273,9 @@ impl Daemon { wallpaper::commit_wallpapers(&mut self.backend, &animator.wallpapers); animator.updt_time(); if animator.frame(&mut self.backend, &mut self.objman, self.pixel_format) { - let animator = self.transition_animators.swap_remove(i); - if let Some(anim) = animator.into_image_animator() { - self.image_animators.push(anim); - } - continue; - } - } - let time = animator.time_to_draw(); - self.set_poll_time(Timespec { - tv_sec: time.as_secs() as Secs, - tv_nsec: time.subsec_nanos().saturating_sub(500_000) as Nsecs, - }); - i += 1; - } - - self.image_animators.retain(|a| !a.wallpapers.is_empty()); - let mut i = 0; - while i < self.image_animators.len() { - let animator = &mut self.image_animators[i]; - if animator - .wallpapers - .iter() - .all(|w| w.borrow().is_draw_ready()) - { - let time = animator.time_to_draw(); - if time > Duration::from_micros(1000) { - self.set_poll_time(Timespec { - tv_sec: time.as_secs() as Secs, - tv_nsec: time.subsec_nanos().saturating_sub(500_000) as Nsecs, - }); - i += 1; + self.animators.swap_remove(i); continue; } - - if !time.is_zero() { - spin_sleep(time); - } - - wallpaper::attach_buffers_and_damage_surfaces( - &mut self.backend, - &mut self.objman, - &animator.wallpapers, - ); - wallpaper::commit_wallpapers(&mut self.backend, &animator.wallpapers); - animator.updt_time(); - animator.frame(&mut self.backend, &mut self.objman, self.pixel_format); } let time = animator.time_to_draw(); self.set_poll_time(Timespec { @@ -351,22 +302,12 @@ impl Daemon { } fn stop_animations(&mut self, wallpapers: &[Rc>]) { - for transition in self.transition_animators.iter_mut() { - transition - .wallpapers - .retain(|w1| !wallpapers.iter().any(|w2| w1.as_ptr() == w2.as_ptr())); - } - - for animator in self.image_animators.iter_mut() { + for animator in self.animators.iter_mut() { animator .wallpapers .retain(|w1| !wallpapers.iter().any(|w2| w1.as_ptr() == w2.as_ptr())); } - - self.transition_animators - .retain(|t| !t.wallpapers.is_empty()); - - self.image_animators.retain(|a| !a.wallpapers.is_empty()); + self.animators.retain(|a| !a.wallpapers.is_empty()); } } From 86aa34cebdfbec7718d3c925db180971d7978898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sun, 19 Oct 2025 18:55:39 -0300 Subject: [PATCH 2/5] refactor to nuke Rc and RefCell from wallpapers We can use output_name as an unique id, so that we do not need to wrap everything around `Rc` and `RefCell`. In some places, this will entail some extra cost in filtering the `wallpaper` iterator for just those that are part of a group. However, because the vast majority of our users will probably not have anything beyong 4 outputs, the cost of this is pretty low. On the other hand, previously, every access to a wallpaper incurred one extra pointer load from the `Rc`, and one extra branch for the `RefCell`. On the other hand, this should improve memory usage and cache access patterns. This means overall this should also improve performance, or at the very least not negatively affect it very much. --- daemon/src/animations.rs | 91 +++++---- daemon/src/animations/transitions.rs | 278 +++++++++++++-------------- daemon/src/main.rs | 169 ++++++++-------- daemon/src/wallpaper.rs | 51 +++-- 4 files changed, 295 insertions(+), 294 deletions(-) diff --git a/daemon/src/animations.rs b/daemon/src/animations.rs index b031788a..ec967a68 100644 --- a/daemon/src/animations.rs +++ b/daemon/src/animations.rs @@ -1,11 +1,7 @@ use log::error; use waybackend::{Waybackend, objman::ObjectManager}; -use std::{ - cell::RefCell, - rc::Rc, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use common::{ compression::Decompressor, @@ -19,7 +15,8 @@ mod transitions; use transitions::Effect; pub struct Animator { - pub wallpapers: Vec>>, + /// Output names this Animator is responsible for + pub group: Vec, now: Instant, animator: AnimatorKind, } @@ -31,30 +28,40 @@ enum AnimatorKind { impl Animator { pub fn new( - mut wallpapers: Vec>>, + wallpapers: &mut [Wallpaper], + group: Vec, transition: &ipc::Transition, pixel_format: PixelFormat, img_req: ImgReq, animation: Option, ) -> Option { let ImgReq { img, path, dim, .. } = img_req; - if wallpapers.is_empty() { - return None; - } - for w in wallpapers.iter_mut() { - w.borrow_mut() - .set_img_info(BgImg::Img(path.str().to_string())); - } - let expect = wallpapers[0].borrow().get_dimensions(); + let expect = group.first().map(|i| { + wallpapers + .iter() + .find(|w| w.has_output_name(*i)) + .unwrap() + .get_dimensions() + })?; + if dim != expect { error!("image has wrong dimensions! Expect {expect:?}, actual {dim:?}"); return None; } + + for w in wallpapers + .iter_mut() + .filter(|w| group.contains(&w.output_name)) + { + w.set_animating(true); + w.set_img_info(BgImg::Img(path.str().to_string())); + } + let fps = Duration::from_nanos(1_000_000_000 / transition.fps as u64); let effect = Effect::new(transition, pixel_format, dim); Some(Self { - wallpapers, + group, now: Instant::now(), animator: AnimatorKind::Transition(Transition { effect, @@ -81,16 +88,18 @@ impl Animator { &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, + wallpapers: &mut [Wallpaper], pixel_format: PixelFormat, ) -> bool { let Self { - wallpapers, - animator, - .. + group, animator, .. } = self; match animator { AnimatorKind::Transition(transition) => { - if !transition.frame(backend, objman, wallpapers.as_mut_slice(), pixel_format) { + let wallpapers = wallpapers + .iter_mut() + .filter(|w| group.contains(&w.output_name)); + if !transition.frame(backend, objman, wallpapers, pixel_format) { return false; } // Note: it needs to have more than a single frame, otherwise there is no point in @@ -108,7 +117,7 @@ impl Animator { true } AnimatorKind::Animation(animation) => { - animation.frame(backend, objman, wallpapers, pixel_format); + animation.frame(backend, objman, group, wallpapers, pixel_format); false } } @@ -128,11 +137,11 @@ impl Transition { self.fps.saturating_sub(now.elapsed()) } - fn frame( + fn frame<'a, W: Iterator>( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, - wallpapers: &mut [Rc>], + wallpapers: W, pixel_format: PixelFormat, ) -> bool { let Self { @@ -164,7 +173,8 @@ impl Animation { &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, - wallpapers: &mut Vec>>, + group: &mut Vec, + wallpapers: &mut [Wallpaper], pixel_format: PixelFormat, ) { let Self { @@ -177,30 +187,29 @@ impl Animation { let frame = &animation.animation[*i % animation.animation.len()].0; if *i < animation.animation.len() { - wallpapers.retain(|w| { - let mut borrow = w.borrow_mut(); - let result = borrow.canvas_change(backend, objman, pixel_format, |canvas| { - decompressor.decompress(frame, canvas, pixel_format) - }); - match result { - Ok(()) => true, - Err(e) => { + for w in wallpapers { + if let Some(j) = group.iter().position(|g| w.has_output_name(*g)) { + let result = w.canvas_change(backend, objman, pixel_format, |canvas| { + decompressor.decompress(frame, canvas, pixel_format) + }); + if let Err(e) = result { error!("failed to unpack frame: {e}"); - false + group.remove(j); } } - }); + } } else { // if we already went through one loop, we can use the unsafe version, because // everything was already validated for w in wallpapers { - let mut borrow = w.borrow_mut(); - // SAFETY: we have already validated every frame and removed the ones that have - // errors in the previous loops. The only ones left should be those that can be - // decompressed correctly - borrow.canvas_change(backend, objman, pixel_format, |canvas| unsafe { - decompressor.decompress_unchecked(frame, canvas, pixel_format) - }); + if group.contains(&w.output_name) { + // SAFETY: we have already validated every frame and removed the ones that have + // errors in the previous loops. The only ones left should be those that can be + // decompressed correctly + w.canvas_change(backend, objman, pixel_format, |canvas| unsafe { + decompressor.decompress_unchecked(frame, canvas, pixel_format) + }); + } } } diff --git a/daemon/src/animations/transitions.rs b/daemon/src/animations/transitions.rs index 804dcdf7..9fc805ed 100644 --- a/daemon/src/animations/transitions.rs +++ b/daemon/src/animations/transitions.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, rc::Rc, time::Instant}; +use std::time::Instant; use crate::{WaylandObject, wallpaper::Wallpaper}; use common::ipc::{PixelFormat, Transition, TransitionType}; @@ -43,19 +43,18 @@ impl None { Self } - fn run( + fn run<'a, W: Iterator>( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, pixel_format: PixelFormat, - wallpapers: &mut [Rc>], + wallpapers: W, img: &[u8], ) -> bool { - wallpapers.iter().for_each(|w| { - w.borrow_mut() - .canvas_change(backend, objman, pixel_format, |canvas| { - canvas.copy_from_slice(img) - }) + wallpapers.for_each(|w| { + w.canvas_change(backend, objman, pixel_format, |canvas| { + canvas.copy_from_slice(img) + }) }); true } @@ -85,12 +84,12 @@ impl Effect { } } - pub fn execute( + pub fn execute<'a, W: Iterator>( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, pixel_format: PixelFormat, - wallpapers: &mut [Rc>], + wallpapers: W, img: &[u8], ) -> bool { let done = match self { @@ -126,25 +125,23 @@ impl Simple { fn new(step: u8) -> Self { Self { step } } - fn run( + fn run<'a, W: Iterator>( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, pixel_format: PixelFormat, - wallpapers: &mut [Rc>], + wallpapers: W, img: &[u8], ) -> bool { let step = self.step; let mut done = true; - for wallpaper in wallpapers.iter() { - wallpaper - .borrow_mut() - .canvas_change(backend, objman, pixel_format, |canvas| { - for (old, new) in canvas.iter_mut().zip(img) { - change_byte(step, old, new); - } - done = done && canvas == img; - }); + for wallpaper in wallpapers { + wallpaper.canvas_change(backend, objman, pixel_format, |canvas| { + for (old, new) in canvas.iter_mut().zip(img) { + change_byte(step, old, new); + } + done = done && canvas == img; + }); } done } @@ -162,24 +159,22 @@ impl Fade { let step = 0; Self { start, seq, step } } - fn run( + fn run<'a, W: Iterator>( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, pixel_format: PixelFormat, - wallpapers: &mut [Rc>], + wallpapers: W, img: &[u8], ) -> bool { - for wallpaper in wallpapers.iter() { - wallpaper - .borrow_mut() - .canvas_change(backend, objman, pixel_format, |canvas| { - for (old, new) in canvas.iter_mut().zip(img) { - let x = *old as u16 * (256 - self.step); - let y = *new as u16 * self.step; - *old = ((x + y) >> 8) as u8; - } - }); + for wallpaper in wallpapers { + wallpaper.canvas_change(backend, objman, pixel_format, |canvas| { + for (old, new) in canvas.iter_mut().zip(img) { + let x = *old as u16 * (256 - self.step); + let y = *new as u16 * self.step; + *old = ((x + y) >> 8) as u8; + } + }); } self.step = (256.0 * self.seq.now() as f64).trunc() as u16; self.seq.advance_to(self.start.elapsed().as_secs_f64()); @@ -245,12 +240,12 @@ impl Wave { step, } } - fn run( + fn run<'a, W: Iterator>( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, pixel_format: PixelFormat, - wallpapers: &mut [Rc>], + wallpapers: W, img: &[u8], ) -> bool { let Self { @@ -286,53 +281,50 @@ impl Wave { let offset = self.seq.now() as f64; self.seq.advance_to(self.start.elapsed().as_secs_f64()); - for wallpaper in wallpapers.iter() { - wallpaper - .borrow_mut() - .canvas_change(backend, objman, pixel_format, |canvas| { - // divide in 3 sections: the one we know will not be drawn to, the one we know - // WILL be drawn to, and the one we need to do a more expensive check on. - // We do this by creating 2 lines: the first tangential to the wave's peaks, - // the second to its valeys. In-between is where we have to do the more - // expensive checks - for line in 0..height { - let y = ((height - line) as f64 - center.1 as f64 - scale_y * sin) * b; - let x = (circle_radius.powi(2) - y - offset) / a - + center.0 as f64 - + scale_y * cos; - let x = x.min(width as f64); - let (col_begin, col_end) = if a.is_sign_negative() { - (0usize, x as usize * channels) - } else { - (x as usize * channels, stride) - }; - for col in col_begin..col_end { - let old = unsafe { canvas.get_unchecked_mut(line * stride + col) }; - let new = unsafe { img.get_unchecked(line * stride + col) }; - change_byte(step, old, new); - } - let old_x = x; - let y = ((height - line) as f64 - center.1 as f64 + scale_y * sin) * b; - let x = (circle_radius.powi(2) - y - offset) / a + center.0 as f64 - - scale_y * cos; - let x = x.min(width as f64); - let (col_begin, col_end) = if old_x < x { - (old_x as usize, x as usize) - } else { - (x as usize, old_x as usize) - }; - for col in col_begin..col_end { - if is_low(col as f64, line as f64, offset) { - let i = line * stride + col * channels; - for j in 0..channels { - let old = unsafe { canvas.get_unchecked_mut(i + j) }; - let new = unsafe { img.get_unchecked(i + j) }; - change_byte(step, old, new); - } + for wallpaper in wallpapers { + wallpaper.canvas_change(backend, objman, pixel_format, |canvas| { + // divide in 3 sections: the one we know will not be drawn to, the one we know + // WILL be drawn to, and the one we need to do a more expensive check on. + // We do this by creating 2 lines: the first tangential to the wave's peaks, + // the second to its valeys. In-between is where we have to do the more + // expensive checks + for line in 0..height { + let y = ((height - line) as f64 - center.1 as f64 - scale_y * sin) * b; + let x = + (circle_radius.powi(2) - y - offset) / a + center.0 as f64 + scale_y * cos; + let x = x.min(width as f64); + let (col_begin, col_end) = if a.is_sign_negative() { + (0usize, x as usize * channels) + } else { + (x as usize * channels, stride) + }; + for col in col_begin..col_end { + let old = unsafe { canvas.get_unchecked_mut(line * stride + col) }; + let new = unsafe { img.get_unchecked(line * stride + col) }; + change_byte(step, old, new); + } + let old_x = x; + let y = ((height - line) as f64 - center.1 as f64 + scale_y * sin) * b; + let x = + (circle_radius.powi(2) - y - offset) / a + center.0 as f64 - scale_y * cos; + let x = x.min(width as f64); + let (col_begin, col_end) = if old_x < x { + (old_x as usize, x as usize) + } else { + (x as usize, old_x as usize) + }; + for col in col_begin..col_end { + if is_low(col as f64, line as f64, offset) { + let i = line * stride + col * channels; + for j in 0..channels { + let old = unsafe { canvas.get_unchecked_mut(i + j) }; + let new = unsafe { img.get_unchecked(i + j) }; + change_byte(step, old, new); } } } - }); + } + }); } self.start.elapsed().as_secs_f64() > self.seq.duration() @@ -391,12 +383,12 @@ impl Wipe { step, } } - fn run( + fn run<'a, W: Iterator>( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, pixel_format: PixelFormat, - wallpapers: &mut [Rc>], + wallpapers: W, img: &[u8], ) -> bool { let Self { @@ -413,28 +405,26 @@ impl Wipe { let channels = pixel_format.channels() as usize; let offset = self.seq.now() as f64; self.seq.advance_to(self.start.elapsed().as_secs_f64()); - for wallpaper in wallpapers.iter() { - wallpaper - .borrow_mut() - .canvas_change(backend, objman, pixel_format, |canvas| { - // line formula: (x-h)*a + (y-k)*b + C = r^2 - // https://www.desmos.com/calculator/vpvzk12yar - for line in 0..height { - let y = ((height - line) as f64 - center.1 as f64) * b; - let x = (circle_radius.powi(2) - y - offset) / a + center.0 as f64; - let x = x.min(width as f64); - let (col_begin, col_end) = if a.is_sign_negative() { - (0usize, x as usize * channels) - } else { - (x as usize * channels, stride) - }; - for col in col_begin..col_end { - let old = unsafe { canvas.get_unchecked_mut(line * stride + col) }; - let new = unsafe { img.get_unchecked(line * stride + col) }; - change_byte(step, old, new); - } + for wallpaper in wallpapers { + wallpaper.canvas_change(backend, objman, pixel_format, |canvas| { + // line formula: (x-h)*a + (y-k)*b + C = r^2 + // https://www.desmos.com/calculator/vpvzk12yar + for line in 0..height { + let y = ((height - line) as f64 - center.1 as f64) * b; + let x = (circle_radius.powi(2) - y - offset) / a + center.0 as f64; + let x = x.min(width as f64); + let (col_begin, col_end) = if a.is_sign_negative() { + (0usize, x as usize * channels) + } else { + (x as usize * channels, stride) + }; + for col in col_begin..col_end { + let old = unsafe { canvas.get_unchecked_mut(line * stride + col) }; + let new = unsafe { img.get_unchecked(line * stride + col) }; + change_byte(step, old, new); } - }); + } + }); } self.start.elapsed().as_secs_f64() > self.seq.duration() } @@ -488,12 +478,12 @@ impl Grow { step, } } - fn run( + fn run<'a, W: Iterator>( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, pixel_format: PixelFormat, - wallpapers: &mut [Rc>], + wallpapers: W, img: &[u8], ) -> bool { let Self { @@ -508,26 +498,24 @@ impl Grow { } = *self; let channels = pixel_format.channels() as usize; - for wallpaper in wallpapers.iter() { - wallpaper - .borrow_mut() - .canvas_change(backend, objman, pixel_format, |canvas| { - let line_begin = center_y.saturating_sub(dist_center as usize); - let line_end = height.min(center_y + dist_center as usize); - - // to plot half a circle with radius r, we do sqrt(r^2 - x^2) - for line in line_begin..line_end { - let offset = (dist_center.powi(2) - (center_y as f32 - line as f32).powi(2)) - .sqrt() as usize; - let col_begin = center_x.saturating_sub(offset) * channels; - let col_end = width.min(center_x + offset) * channels; - for col in col_begin..col_end { - let old = unsafe { canvas.get_unchecked_mut(line * stride + col) }; - let new = unsafe { img.get_unchecked(line * stride + col) }; - change_byte(step, old, new); - } + for wallpaper in wallpapers { + wallpaper.canvas_change(backend, objman, pixel_format, |canvas| { + let line_begin = center_y.saturating_sub(dist_center as usize); + let line_end = height.min(center_y + dist_center as usize); + + // to plot half a circle with radius r, we do sqrt(r^2 - x^2) + for line in line_begin..line_end { + let offset = (dist_center.powi(2) - (center_y as f32 - line as f32).powi(2)) + .sqrt() as usize; + let col_begin = center_x.saturating_sub(offset) * channels; + let col_end = width.min(center_x + offset) * channels; + for col in col_begin..col_end { + let old = unsafe { canvas.get_unchecked_mut(line * stride + col) }; + let new = unsafe { img.get_unchecked(line * stride + col) }; + change_byte(step, old, new); } - }); + } + }); } self.dist_center = self.seq.now(); @@ -582,12 +570,12 @@ impl Outer { dist_center, } } - fn run( + fn run<'a, W: Iterator>( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, pixel_format: PixelFormat, - wallpapers: &mut [Rc>], + wallpapers: W, img: &[u8], ) -> bool { let Self { @@ -601,28 +589,26 @@ impl Outer { .. } = *self; let channels = pixel_format.channels() as usize; - for wallpaper in wallpapers.iter() { - wallpaper - .borrow_mut() - .canvas_change(backend, objman, pixel_format, |canvas| { - // to plot half a circle with radius r, we do sqrt(r^2 - x^2) - for line in 0..height { - let offset = (dist_center.powi(2) - (center_y as f32 - line as f32).powi(2)) - .sqrt() as usize; - let col_begin = center_x.saturating_sub(offset) * channels; - let col_end = width.min(center_x + offset) * channels; - for col in 0..col_begin { - let old = unsafe { canvas.get_unchecked_mut(line * stride + col) }; - let new = unsafe { img.get_unchecked(line * stride + col) }; - change_byte(step, old, new); - } - for col in col_end..stride { - let old = unsafe { canvas.get_unchecked_mut(line * stride + col) }; - let new = unsafe { img.get_unchecked(line * stride + col) }; - change_byte(step, old, new); - } + for wallpaper in wallpapers { + wallpaper.canvas_change(backend, objman, pixel_format, |canvas| { + // to plot half a circle with radius r, we do sqrt(r^2 - x^2) + for line in 0..height { + let offset = (dist_center.powi(2) - (center_y as f32 - line as f32).powi(2)) + .sqrt() as usize; + let col_begin = center_x.saturating_sub(offset) * channels; + let col_end = width.min(center_x + offset) * channels; + for col in 0..col_begin { + let old = unsafe { canvas.get_unchecked_mut(line * stride + col) }; + let new = unsafe { img.get_unchecked(line * stride + col) }; + change_byte(step, old, new); + } + for col in col_end..stride { + let old = unsafe { canvas.get_unchecked_mut(line * stride + col) }; + let new = unsafe { img.get_unchecked(line * stride + col) }; + change_byte(step, old, new); } - }); + } + }); } self.dist_center = self.seq.now(); self.seq.advance_to(self.start.elapsed().as_secs_f64()); diff --git a/daemon/src/main.rs b/daemon/src/main.rs index 44bb0040..b78ea6c9 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -19,11 +19,9 @@ use waybackend::{Global, objman, types::ObjectId}; use wayland::zwlr_layer_shell_v1::Layer; use std::{ - cell::RefCell, fs, io::{IsTerminal, Write}, num::NonZeroI32, - rc::Rc, sync::atomic::{AtomicBool, Ordering}, time::Duration, }; @@ -61,7 +59,7 @@ struct Daemon { layer_shell: ObjectId, layer: Layer, pixel_format: PixelFormat, - wallpapers: Vec>>, + wallpapers: Vec, animators: Vec, namespace: String, use_cache: bool, @@ -153,10 +151,13 @@ impl Daemon { let request = RequestRecv::receive(bytes); let answer = match request { RequestRecv::Clear(clear) => { - let wallpapers = self.find_wallpapers_by_names(&clear.outputs); - self.stop_animations(&wallpapers); - for wallpaper in &wallpapers { - let mut wallpaper = wallpaper.borrow_mut(); + let group = self.find_group_by_names(&clear.outputs); + self.stop_animations(&group); + for wallpaper in self + .wallpapers + .iter_mut() + .filter(|w| group.contains(&w.output_name)) + { wallpaper.set_img_info(common::ipc::BgImg::Color(clear.color)); wallpaper.clear( &mut self.backend, @@ -165,17 +166,16 @@ impl Daemon { clear.color, ); } - crate::wallpaper::attach_buffers_and_damage_surfaces( + crate::wallpaper::attach_damage_commit( &mut self.backend, &mut self.objman, - &wallpapers, + self.wallpapers + .iter_mut() + .filter(|w| group.contains(&w.output_name)), ); - crate::wallpaper::commit_wallpapers(&mut self.backend, &wallpapers); Answer::Ok } - RequestRecv::Ping => { - Answer::Ping(self.wallpapers.iter().all(|w| w.borrow().configured)) - } + RequestRecv::Ping => Answer::Ping(self.wallpapers.iter().all(|w| w.configured)), RequestRecv::Pause => { self.paused = !self.paused; Answer::Ok @@ -199,12 +199,22 @@ impl Daemon { } else { None }; - let wallpapers = self.find_wallpapers_by_names(&names); - self.stop_animations(&wallpapers); - if let Some(mut animator) = - Animator::new(wallpapers, &transition, self.pixel_format, img, animation) - { - animator.frame(&mut self.backend, &mut self.objman, self.pixel_format); + let group = self.find_group_by_names(&names); + self.stop_animations(&group); + if let Some(mut animator) = Animator::new( + &mut self.wallpapers, + group, + &transition, + self.pixel_format, + img, + animation, + ) { + animator.frame( + &mut self.backend, + &mut self.objman, + &mut self.wallpapers, + self.pixel_format, + ); self.animators.push(animator); } } @@ -223,18 +233,19 @@ impl Daemon { fn wallpapers_info(&self) -> Box<[BgInfo]> { self.wallpapers .iter() - .map(|wallpaper| wallpaper.borrow().get_bg_info(self.pixel_format)) + .map(|wallpaper| wallpaper.get_bg_info(self.pixel_format)) .collect() } - fn find_wallpapers_by_names(&self, names: &[MmappedStr]) -> Vec>> { + fn find_group_by_names(&self, names: &[MmappedStr]) -> Vec { self.wallpapers .iter() .filter_map(|wallpaper| { - if names.is_empty() || names.iter().any(|n| wallpaper.borrow().has_name(n.str())) { - return Some(Rc::clone(wallpaper)); + if names.is_empty() || names.iter().any(|n| wallpaper.has_name(n.str())) { + Some(wallpaper.output_name) + } else { + None } - None }) .collect() } @@ -245,11 +256,13 @@ impl Daemon { let mut i = 0; while i < self.animators.len() { let animator = &mut self.animators[i]; - if animator - .wallpapers - .iter() - .all(|w| w.borrow().is_draw_ready()) - { + if animator.group.iter().all(|j| { + self.wallpapers + .iter() + .find(|w| w.has_output_name(*j)) + .unwrap() + .is_draw_ready() + }) { let time = animator.time_to_draw(); if time > Duration::from_micros(1000) { self.set_poll_time(Timespec { @@ -264,15 +277,20 @@ impl Daemon { spin_sleep(time); } - wallpaper::attach_buffers_and_damage_surfaces( + crate::wallpaper::attach_damage_commit( &mut self.backend, &mut self.objman, - &animator.wallpapers, + self.wallpapers + .iter_mut() + .filter(|w| animator.group.contains(&w.output_name)), ); - - wallpaper::commit_wallpapers(&mut self.backend, &animator.wallpapers); animator.updt_time(); - if animator.frame(&mut self.backend, &mut self.objman, self.pixel_format) { + if animator.frame( + &mut self.backend, + &mut self.objman, + &mut self.wallpapers, + self.pixel_format, + ) { self.animators.swap_remove(i); continue; } @@ -288,26 +306,30 @@ impl Daemon { fn commit_pending_surface_changes(&mut self) { let mut to_stop = Vec::with_capacity(self.wallpapers.len()); - for wallpaper in self.wallpapers.iter() { - if wallpaper.borrow_mut().commit_surface_changes( + for wallpaper in &mut self.wallpapers { + if wallpaper.commit_surface_changes( &mut self.backend, &mut self.objman, &self.namespace, self.use_cache, ) { - to_stop.push(Rc::clone(wallpaper)); + to_stop.push(wallpaper.output_name); } } self.stop_animations(&to_stop); } - fn stop_animations(&mut self, wallpapers: &[Rc>]) { + fn stop_animations(&mut self, wallpapers: &[u32]) { + for w in &mut self.wallpapers { + if wallpapers.contains(&w.output_name) { + w.set_animating(false); + } + } + for animator in self.animators.iter_mut() { - animator - .wallpapers - .retain(|w1| !wallpapers.iter().any(|w2| w1.as_ptr() == w2.as_ptr())); + animator.group.retain(|x| !wallpapers.contains(x)); } - self.animators.retain(|a| !a.wallpapers.is_empty()); + self.animators.retain(|a| !a.group.is_empty()); } } @@ -340,14 +362,10 @@ impl wayland::wl_registry::EvHandler for Daemon { } fn global_remove(&mut self, _: ObjectId, name: u32) { - if let Some(i) = self - .wallpapers - .iter() - .position(|w| w.borrow().has_output_name(name)) - { + if let Some(i) = self.wallpapers.iter().position(|w| w.has_output_name(name)) { let w = self.wallpapers.remove(i); - w.borrow_mut().destroy(&mut self.backend); - self.stop_animations(std::slice::from_ref(&w)); + self.stop_animations(std::slice::from_ref(&w.output_name)); + w.destroy(&mut self.backend); } else if let Some(i) = self .pending_outputs .iter() @@ -424,7 +442,7 @@ impl wayland::wl_output::EvHandler for Daemon { .position(|o| o.output == sender_id) { let output_info = self.pending_outputs.swap_remove(i); - let wallpaper = Rc::new(RefCell::new(Wallpaper::new(self, output_info))); + let wallpaper = Wallpaper::new(self, output_info); self.wallpapers.push(wallpaper); } } @@ -445,8 +463,7 @@ impl wayland::wl_output::EvHandler for Daemon { } } - for wallpaper in self.wallpapers.iter() { - let mut wallpaper = wallpaper.borrow_mut(); + for wallpaper in &mut self.wallpapers { if wallpaper.has_output(sender_id) { wallpaper.set_scale(scale); return; @@ -462,8 +479,7 @@ impl wayland::wl_output::EvHandler for Daemon { } } - for wallpaper in self.wallpapers.iter() { - let mut wallpaper = wallpaper.borrow_mut(); + for wallpaper in &mut self.wallpapers { if wallpaper.has_output(sender_id) { wallpaper.set_name(name.to_string()); return; @@ -478,8 +494,7 @@ impl wayland::wl_output::EvHandler for Daemon { return; } } - for wallpaper in self.wallpapers.iter() { - let mut wallpaper = wallpaper.borrow_mut(); + for wallpaper in &mut self.wallpapers { if wallpaper.has_output(sender_id) { wallpaper.set_desc(description.to_string()); return; @@ -515,13 +530,8 @@ impl wayland::wl_region::EvHandler for Daemon {} impl wayland::wl_buffer::EvHandler for Daemon { fn release(&mut self, sender_id: ObjectId) { trace!("Releasing buffer {sender_id}"); - for wallpaper in self.wallpapers.iter() { - let strong_count = Rc::strong_count(wallpaper); - if wallpaper.borrow_mut().try_set_buffer_release_flag( - &mut self.backend, - sender_id, - strong_count, - ) { + for wallpaper in &mut self.wallpapers { + if wallpaper.set_buffer_release_flag(&mut self.backend, sender_id) { return; } } @@ -531,9 +541,9 @@ impl wayland::wl_buffer::EvHandler for Daemon { impl wayland::wl_callback::EvHandler for Daemon { fn done(&mut self, sender_id: ObjectId, _callback_data: u32) { - for wallpaper in self.wallpapers.iter() { - if wallpaper.borrow().has_callback(sender_id) { - wallpaper.borrow_mut().frame_callback_completed(); + for wallpaper in &mut self.wallpapers { + if wallpaper.has_callback(sender_id) { + wallpaper.frame_callback_completed(); break; } } @@ -546,12 +556,10 @@ impl wayland::wl_shm_pool::EvHandler for Daemon {} impl wayland::zwlr_layer_shell_v1::EvHandler for Daemon {} impl wayland::zwlr_layer_surface_v1::EvHandler for Daemon { fn configure(&mut self, sender_id: ObjectId, serial: u32, width: u32, height: u32) { - for wallpaper in self.wallpapers.iter_mut() { - if wallpaper.borrow().has_layer_surface(sender_id) { - wallpaper - .borrow_mut() - .set_dimensions(width as i32, height as i32); - wallpaper.borrow_mut().set_ack_serial(serial); + for wallpaper in &mut self.wallpapers { + if wallpaper.has_layer_surface(sender_id) { + wallpaper.set_dimensions(width as i32, height as i32); + wallpaper.set_ack_serial(serial); break; } } @@ -561,22 +569,22 @@ impl wayland::zwlr_layer_surface_v1::EvHandler for Daemon { if let Some(i) = self .wallpapers .iter() - .position(|w| w.borrow().has_layer_surface(sender_id)) + .position(|w| w.has_layer_surface(sender_id)) { let w = self.wallpapers.remove(i); - w.borrow_mut().destroy(&mut self.backend); - self.stop_animations(std::slice::from_ref(&w)); + self.stop_animations(std::slice::from_ref(&w.output_name)); + w.destroy(&mut self.backend); } } } impl wayland::wp_fractional_scale_v1::EvHandler for Daemon { fn preferred_scale(&mut self, sender_id: ObjectId, scale: u32) { - for wallpaper in self.wallpapers.iter() { - if wallpaper.borrow().has_fractional_scale(sender_id) { + for wallpaper in &mut self.wallpapers { + if wallpaper.has_fractional_scale(sender_id) { match NonZeroI32::new(scale as i32) { Some(factor) => { - wallpaper.borrow_mut().set_scale(Scale::Fractional(factor)); + wallpaper.set_scale(Scale::Fractional(factor)); } None => error!("received scale factor of 0 from compositor"), } @@ -592,9 +600,8 @@ impl wayland::wp_fractional_scale_manager_v1::EvHandler for Daemon {} impl Drop for Daemon { fn drop(&mut self) { - for wallpaper in self.wallpapers.iter() { - let mut w = wallpaper.borrow_mut(); - w.destroy(&mut self.backend); + for wallpaper in self.wallpapers.drain(..) { + wallpaper.destroy(&mut self.backend); } } } diff --git a/daemon/src/wallpaper.rs b/daemon/src/wallpaper.rs index 2865440a..e9c68cee 100644 --- a/daemon/src/wallpaper.rs +++ b/daemon/src/wallpaper.rs @@ -2,14 +2,14 @@ use common::ipc::{BgImg, BgInfo, PixelFormat, Scale}; use log::{debug, error, warn}; use waybackend::{Waybackend, objman::ObjectManager, types::ObjectId}; -use std::{cell::RefCell, num::NonZeroI32, rc::Rc}; +use std::num::NonZeroI32; use crate::{ WaylandObject, wayland::{ - bump_pool::BumpPool, wl_compositor, wl_region, wl_surface, wp_fractional_scale_manager_v1, - wp_fractional_scale_v1, wp_viewport, wp_viewporter, zwlr_layer_shell_v1, - zwlr_layer_surface_v1, + bump_pool::BumpPool, wl_compositor, wl_output, wl_region, wl_surface, + wp_fractional_scale_manager_v1, wp_fractional_scale_v1, wp_viewport, wp_viewporter, + zwlr_layer_shell_v1, zwlr_layer_surface_v1, }, }; @@ -85,7 +85,7 @@ pub struct Wallpaper { desc: Option, output: ObjectId, - output_name: u32, + pub output_name: u32, wl_surface: ObjectId, wp_viewport: ObjectId, wp_fractional: Option, @@ -98,6 +98,7 @@ pub struct Wallpaper { ack_serial: u32, needs_ack: bool, + pub animating: bool, pub configured: bool, dirty: bool, @@ -207,6 +208,7 @@ impl Wallpaper { needs_ack: false, configured: false, dirty: false, + animating: false, frame_callback_handler, img: BgImg::Color([0, 0, 0, 0]), pool, @@ -358,14 +360,9 @@ impl Wallpaper { self.layer_surface == layer_surface } - pub fn try_set_buffer_release_flag( - &mut self, - backend: &mut Waybackend, - buffer: ObjectId, - rc_strong_count: usize, - ) -> bool { + pub fn set_buffer_release_flag(&mut self, backend: &mut Waybackend, buffer: ObjectId) -> bool { self.pool - .set_buffer_release_flag(backend, buffer, rc_strong_count != 1) + .set_buffer_release_flag(backend, buffer, self.animating) } pub fn is_draw_ready(&self) -> bool { @@ -424,7 +421,7 @@ impl Wallpaper { self.img = img_info; } - pub fn destroy(&mut self, backend: &mut Waybackend) { + pub fn destroy(mut self, backend: &mut Waybackend) { // Careful not to panic here, since we call this on drop if let Err(e) = wp_viewport::req::destroy(backend, self.wp_viewport) { @@ -443,6 +440,14 @@ impl Wallpaper { self.pool.destroy(backend); + if let Err(e) = wl_surface::req::destroy(backend, self.wl_surface) { + error!("error destroying wl_surface: {e:?}"); + } + + if let Err(e) = wl_output::req::release(backend, self.output) { + error!("error releasing wl_output: {e:?}"); + } + debug!( "Destroyed output {} - {}", self.name.as_ref().unwrap_or(&"?".to_string()), @@ -464,25 +469,19 @@ impl Wallpaper { self.frame_callback_handler .request_frame_callback(backend, objman, surface); } + + pub fn set_animating(&mut self, animating: bool) { + self.animating = animating; + } } -/// attaches all pending buffers and damages all surfaces with one single request -pub fn attach_buffers_and_damage_surfaces( +pub fn attach_damage_commit<'a, W: Iterator>( backend: &mut Waybackend, objman: &mut ObjectManager, - wallpapers: &[Rc>], + wallpapers: W, ) { for wallpaper in wallpapers { - wallpaper - .borrow_mut() - .attach_buffer_and_damage_surface(backend, objman); - } -} - -/// commits multiple wallpapers at once with a single message through the socket -pub fn commit_wallpapers(backend: &mut Waybackend, wallpapers: &[Rc>]) { - for wallpaper in wallpapers { - let wallpaper = wallpaper.borrow(); + wallpaper.attach_buffer_and_damage_surface(backend, objman); wl_surface::req::commit(backend, wallpaper.wl_surface).unwrap(); } } From f4ca888458ee494ed7b5735eff812786c3e54d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sun, 19 Oct 2025 19:43:17 -0300 Subject: [PATCH 3/5] reformat the daemon's dependencies --- daemon/Cargo.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 59e1d2f0..f0208c77 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -11,17 +11,17 @@ workspace = true [dependencies] common = { workspace = true } - -waybackend = { version = "0.7" } -log = { version = "0.4", default-features = false, features = [ +log = { version = "0.4", default-features = false, features = [ "max_level_trace", "release_max_level_info", "std", ] } -rustix = { version = "1.1", default-features = false, features = ["event"] } -libc = "0.2" -keyframe = "1.1" -sd-notify = { version = "0.4" } + +waybackend = { version = "0.7" } +rustix = { version = "1.1", default-features = false, features = ["event"] } +libc = { version = "0.2" } +keyframe = { version = "1.1" } +sd-notify = { version = "0.4" } [build-dependencies] waybackend-scanner = { version = "0.7", features = ["build-script"] } From 3fff79582a1cf026a528de5ddbc39b2accf3b092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sun, 19 Oct 2025 20:02:15 -0300 Subject: [PATCH 4/5] remember to set_animating to false --- daemon/src/main.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/daemon/src/main.rs b/daemon/src/main.rs index b78ea6c9..1dbc95c7 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -291,7 +291,12 @@ impl Daemon { &mut self.wallpapers, self.pixel_format, ) { - self.animators.swap_remove(i); + let a = self.animators.swap_remove(i); + for w in &mut self.wallpapers { + if a.group.contains(&w.output_name) { + w.set_animating(false); + } + } continue; } } From 0ca07fcb80df4c388ae33b8dbf853a2ee7799c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sun, 19 Oct 2025 22:40:13 -0300 Subject: [PATCH 5/5] we do not need to store a vector representing the group Instead, the animation group is determined by its index in the animation vector. This reduces the amount of data we need to keep track of and simplifies a few things. --- daemon/src/animations.rs | 71 ++++++++++++++++------------------------ daemon/src/main.rs | 70 +++++++++++++++++++++++++++++---------- daemon/src/wallpaper.rs | 12 +++---- 3 files changed, 85 insertions(+), 68 deletions(-) diff --git a/daemon/src/animations.rs b/daemon/src/animations.rs index ec967a68..4801f04c 100644 --- a/daemon/src/animations.rs +++ b/daemon/src/animations.rs @@ -1,7 +1,10 @@ use log::error; use waybackend::{Waybackend, objman::ObjectManager}; -use std::time::{Duration, Instant}; +use std::{ + num::NonZeroU8, + time::{Duration, Instant}, +}; use common::{ compression::Decompressor, @@ -15,8 +18,6 @@ mod transitions; use transitions::Effect; pub struct Animator { - /// Output names this Animator is responsible for - pub group: Vec, now: Instant, animator: AnimatorKind, } @@ -29,7 +30,7 @@ enum AnimatorKind { impl Animator { pub fn new( wallpapers: &mut [Wallpaper], - group: Vec, + group: NonZeroU8, transition: &ipc::Transition, pixel_format: PixelFormat, img_req: ImgReq, @@ -37,13 +38,11 @@ impl Animator { ) -> Option { let ImgReq { img, path, dim, .. } = img_req; - let expect = group.first().map(|i| { - wallpapers - .iter() - .find(|w| w.has_output_name(*i)) - .unwrap() - .get_dimensions() - })?; + let expect = wallpapers + .iter() + .find(|w| Some(group) == w.animation_group) + .unwrap() + .get_dimensions(); if dim != expect { error!("image has wrong dimensions! Expect {expect:?}, actual {dim:?}"); @@ -52,16 +51,14 @@ impl Animator { for w in wallpapers .iter_mut() - .filter(|w| group.contains(&w.output_name)) + .filter(|w| Some(group) == w.animation_group) { - w.set_animating(true); w.set_img_info(BgImg::Img(path.str().to_string())); } let fps = Duration::from_nanos(1_000_000_000 / transition.fps as u64); let effect = Effect::new(transition, pixel_format, dim); Some(Self { - group, now: Instant::now(), animator: AnimatorKind::Transition(Transition { effect, @@ -84,21 +81,16 @@ impl Animator { self.now = Instant::now(); } - pub fn frame( + pub fn frame<'a, W: Iterator>( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, - wallpapers: &mut [Wallpaper], + wallpapers: W, pixel_format: PixelFormat, ) -> bool { - let Self { - group, animator, .. - } = self; + let Self { animator, .. } = self; match animator { AnimatorKind::Transition(transition) => { - let wallpapers = wallpapers - .iter_mut() - .filter(|w| group.contains(&w.output_name)); if !transition.frame(backend, objman, wallpapers, pixel_format) { return false; } @@ -117,7 +109,7 @@ impl Animator { true } AnimatorKind::Animation(animation) => { - animation.frame(backend, objman, group, wallpapers, pixel_format); + animation.frame(backend, objman, wallpapers, pixel_format); false } } @@ -169,12 +161,11 @@ impl Animation { .saturating_sub(now.elapsed()) } - fn frame( + fn frame<'a, W: Iterator>( &mut self, backend: &mut Waybackend, objman: &mut ObjectManager, - group: &mut Vec, - wallpapers: &mut [Wallpaper], + wallpapers: W, pixel_format: PixelFormat, ) { let Self { @@ -188,28 +179,24 @@ impl Animation { if *i < animation.animation.len() { for w in wallpapers { - if let Some(j) = group.iter().position(|g| w.has_output_name(*g)) { - let result = w.canvas_change(backend, objman, pixel_format, |canvas| { - decompressor.decompress(frame, canvas, pixel_format) - }); - if let Err(e) = result { - error!("failed to unpack frame: {e}"); - group.remove(j); - } + let result = w.canvas_change(backend, objman, pixel_format, |canvas| { + decompressor.decompress(frame, canvas, pixel_format) + }); + if let Err(e) = result { + error!("failed to unpack frame: {e}"); + w.animation_group = None; } } } else { // if we already went through one loop, we can use the unsafe version, because // everything was already validated for w in wallpapers { - if group.contains(&w.output_name) { - // SAFETY: we have already validated every frame and removed the ones that have - // errors in the previous loops. The only ones left should be those that can be - // decompressed correctly - w.canvas_change(backend, objman, pixel_format, |canvas| unsafe { - decompressor.decompress_unchecked(frame, canvas, pixel_format) - }); - } + // SAFETY: we have already validated every frame and removed the ones that have + // errors in the previous loops. The only ones left should be those that can be + // decompressed correctly + w.canvas_change(backend, objman, pixel_format, |canvas| unsafe { + decompressor.decompress_unchecked(frame, canvas, pixel_format) + }); } } diff --git a/daemon/src/main.rs b/daemon/src/main.rs index 1dbc95c7..21aa54ec 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -21,7 +21,7 @@ use wayland::zwlr_layer_shell_v1::Layer; use std::{ fs, io::{IsTerminal, Write}, - num::NonZeroI32, + num::{NonZeroI32, NonZeroU8}, sync::atomic::{AtomicBool, Ordering}, time::Duration, }; @@ -201,6 +201,7 @@ impl Daemon { }; let group = self.find_group_by_names(&names); self.stop_animations(&group); + let group = self.next_animation_group(&group); if let Some(mut animator) = Animator::new( &mut self.wallpapers, group, @@ -212,7 +213,9 @@ impl Daemon { animator.frame( &mut self.backend, &mut self.objman, - &mut self.wallpapers, + self.wallpapers + .iter_mut() + .filter(|w| w.animation_group == Some(group)), self.pixel_format, ); self.animators.push(animator); @@ -256,13 +259,13 @@ impl Daemon { let mut i = 0; while i < self.animators.len() { let animator = &mut self.animators[i]; - if animator.group.iter().all(|j| { - self.wallpapers - .iter() - .find(|w| w.has_output_name(*j)) - .unwrap() - .is_draw_ready() - }) { + let group = NonZeroU8::new(i as u8 + 1).unwrap(); + if self + .wallpapers + .iter() + .filter(|w| w.animation_group == Some(group)) + .all(|w| w.is_draw_ready()) + { let time = animator.time_to_draw(); if time > Duration::from_micros(1000) { self.set_poll_time(Timespec { @@ -282,19 +285,25 @@ impl Daemon { &mut self.objman, self.wallpapers .iter_mut() - .filter(|w| animator.group.contains(&w.output_name)), + .filter(|w| w.animation_group == Some(group)), ); animator.updt_time(); if animator.frame( &mut self.backend, &mut self.objman, - &mut self.wallpapers, + self.wallpapers + .iter_mut() + .filter(|w| w.animation_group == Some(group)), self.pixel_format, ) { - let a = self.animators.swap_remove(i); + self.animators.remove(i); for w in &mut self.wallpapers { - if a.group.contains(&w.output_name) { - w.set_animating(false); + if let Some(g) = w.animation_group { + if g == group { + w.animation_group = None; + } else if g > group { + w.animation_group = Some(NonZeroU8::new(g.get() - 1).unwrap()); + } } } continue; @@ -327,14 +336,39 @@ impl Daemon { fn stop_animations(&mut self, wallpapers: &[u32]) { for w in &mut self.wallpapers { if wallpapers.contains(&w.output_name) { - w.set_animating(false); + w.animation_group = None; } } - for animator in self.animators.iter_mut() { - animator.group.retain(|x| !wallpapers.contains(x)); + let mut i = 0; + while i < self.animators.len() { + let group = NonZeroU8::new(i as u8 + 1).unwrap(); + let mut delete = true; + for w in &mut self.wallpapers { + if w.animation_group.is_some() { + delete = false; + w.animation_group = Some(group); + } + } + + if delete { + self.animators.remove(i); + } else { + i += 1; + } } - self.animators.retain(|a| !a.group.is_empty()); + } + + fn next_animation_group(&mut self, wallpapers: &[u32]) -> NonZeroU8 { + let group = NonZeroU8::new(self.animators.len() as u8 + 1).unwrap(); + + for w in &mut self.wallpapers { + if wallpapers.contains(&w.output_name) { + w.animation_group = Some(group); + } + } + + group } } diff --git a/daemon/src/wallpaper.rs b/daemon/src/wallpaper.rs index e9c68cee..da27b699 100644 --- a/daemon/src/wallpaper.rs +++ b/daemon/src/wallpaper.rs @@ -2,7 +2,7 @@ use common::ipc::{BgImg, BgInfo, PixelFormat, Scale}; use log::{debug, error, warn}; use waybackend::{Waybackend, objman::ObjectManager, types::ObjectId}; -use std::num::NonZeroI32; +use std::num::{NonZeroI32, NonZeroU8}; use crate::{ WaylandObject, @@ -98,7 +98,7 @@ pub struct Wallpaper { ack_serial: u32, needs_ack: bool, - pub animating: bool, + pub animation_group: Option, pub configured: bool, dirty: bool, @@ -208,7 +208,7 @@ impl Wallpaper { needs_ack: false, configured: false, dirty: false, - animating: false, + animation_group: None, frame_callback_handler, img: BgImg::Color([0, 0, 0, 0]), pool, @@ -362,7 +362,7 @@ impl Wallpaper { pub fn set_buffer_release_flag(&mut self, backend: &mut Waybackend, buffer: ObjectId) -> bool { self.pool - .set_buffer_release_flag(backend, buffer, self.animating) + .set_buffer_release_flag(backend, buffer, self.animation_group.is_some()) } pub fn is_draw_ready(&self) -> bool { @@ -469,10 +469,6 @@ impl Wallpaper { self.frame_callback_handler .request_frame_callback(backend, objman, surface); } - - pub fn set_animating(&mut self, animating: bool) { - self.animating = animating; - } } pub fn attach_damage_commit<'a, W: Iterator>(