From cbdc47fc9ddd38f5a7962d84b677aee0ee367936 Mon Sep 17 00:00:00 2001 From: Marcelo Hernandez Date: Wed, 24 Dec 2025 01:02:07 -0500 Subject: [PATCH 1/4] new: fade animation on start --- src/buffer.rs | 17 ++++++++++------- src/dim.rs | 43 +++++++++++++++++++++++++++++++------------ src/surface.rs | 36 +++++++++++++++++++++++------------- 3 files changed, 64 insertions(+), 32 deletions(-) diff --git a/src/buffer.rs b/src/buffer.rs index b66a457..4379842 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -49,15 +49,18 @@ impl BufferManager { .create_buffer(1, 1, 4, wl_shm::Format::Argb8888) .expect("Failed to get buffer from slot pool!"); - // ARGB is actually backwards being little-endian, so we set BGR to 0 for black so - (0..3).for_each(|i| { - canvas[i] = 0; - }); - // then, we set pre-multiplied alpha - canvas[3] = (u8::MAX as f32 * alpha) as u8; - + BufferManager::paint(canvas, alpha); BufferType::Shared(buffer) } } } + + pub fn paint(canvas: &mut [u8], alpha: f32) { + // RGB + (0..3).for_each(|i| { + canvas[i] = 0; + }); + // ...A + canvas[3] = (u8::MAX as f32 * alpha) as u8; + } } diff --git a/src/dim.rs b/src/dim.rs index e110add..5f92a6f 100644 --- a/src/dim.rs +++ b/src/dim.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, time::Instant}; use log::{debug, error, warn}; use smithay_client_toolkit::{ @@ -59,6 +59,7 @@ pub struct DimData { alpha: f32, passthrough: bool, + start_time: Instant, surfaces: HashMap, keyboard: Option, @@ -99,6 +100,7 @@ impl DimData { alpha: opts.alpha(), passthrough: opts.passthrough, + start_time: Instant::now(), surfaces: HashMap::new(), keyboard: None, @@ -117,6 +119,7 @@ impl DimData { &self, qh: &QueueHandle, buffer: BufferType, + back_buffer: BufferType, output: &WlOutput, ) -> DimSurface { let layer = self.layer_shell.create_layer_surface( @@ -155,7 +158,7 @@ impl DimData { .expect("wp_viewporter failed") .get_viewport(layer.wl_surface(), qh, ()); - DimSurface::new(qh, buffer, viewport, layer) + DimSurface::new(qh, buffer, back_buffer, viewport, layer) } } @@ -187,12 +190,8 @@ impl LayerShellHandler for DimData { return; }; - if view.first_configure() { - let (width, height) = configure.new_size; - view.set_size(width, height); - view.viewport_mut().set_destination(width as _, height as _); - view.set_first_configure(false); - } + let (width, height) = configure.new_size; + view.set_size(width, height); view.draw(qh); } @@ -225,6 +224,24 @@ impl CompositorHandler for DimData { _time: u32, ) { for view in self.surfaces.values_mut() { + let elapsed_sec = self.start_time.elapsed().as_millis() as f32 / 1000.; + let fade_sec = 0.5; + + if elapsed_sec <= fade_sec { + let alpha = self.alpha * (elapsed_sec / fade_sec); + match &mut self.buffer_mgr { + BufferManager::SinglePixel(..) => { + view.set_back_buffer(self.buffer_mgr.get_buffer(qh, alpha)); + } + BufferManager::Shm(_, pool) => { + if let BufferType::Shared(buffer) = view.back_buffer_mut() { + let canvas = buffer.canvas(pool).expect("Canvas is not drawable."); + BufferManager::paint(canvas, alpha); + } + } + } + } + view.draw(qh); } } @@ -259,8 +276,9 @@ impl OutputHandler for DimData { qh: &QueueHandle, output: smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput, ) { - let buffer = self.buffer_mgr.get_buffer(qh, self.alpha); - let view = self.new_surface(qh, buffer, &output); + let buffer = self.buffer_mgr.get_buffer(qh, 0.); + let back_buffer = self.buffer_mgr.get_buffer(qh, 0.); + let view = self.new_surface(qh, buffer, back_buffer, &output); self.surfaces.insert(output, view); } @@ -270,8 +288,9 @@ impl OutputHandler for DimData { qh: &QueueHandle, output: smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput, ) { - let buffer = self.buffer_mgr.get_buffer(qh, self.alpha); - let new_view = self.new_surface(qh, buffer, &output); + let buffer = self.buffer_mgr.get_buffer(qh, 0.); + let back_buffer = self.buffer_mgr.get_buffer(qh, 0.); + let new_view = self.new_surface(qh, buffer, back_buffer, &output); if let Some(view) = self.surfaces.get_mut(&output) { *view = new_view; diff --git a/src/surface.rs b/src/surface.rs index 7dd05da..d0ac3b5 100644 --- a/src/surface.rs +++ b/src/surface.rs @@ -6,13 +6,14 @@ use smithay_client_toolkit::{ use crate::{buffer::BufferType, consts::INIT_SIZE, DimData}; pub struct DimSurface { - first_configure: bool, damaged: bool, width: u32, height: u32, buffer: BufferType, + back_buffer: BufferType, + viewport: WpViewport, layer: LayerSurface, } @@ -21,17 +22,18 @@ impl DimSurface { pub fn new( _qh: &QueueHandle, buffer: BufferType, + back_buffer: BufferType, viewport: WpViewport, layer: LayerSurface, ) -> Self { Self { - first_configure: true, damaged: true, width: INIT_SIZE, height: INIT_SIZE, buffer, + back_buffer, viewport, layer, } @@ -42,13 +44,17 @@ impl DimSurface { return; } - let wl_buffer = match &self.buffer { + let wl_buffer = match &self.back_buffer { BufferType::Wl(wl_buffer) => wl_buffer, BufferType::Shared(buffer) => buffer.wl_buffer(), }; self.layer.wl_surface().attach(Some(wl_buffer), 0, 0); + self.layer + .wl_surface() + .damage(0, 0, self.width as _, self.height as _); self.damaged = false; + std::mem::swap(&mut self.buffer, &mut self.back_buffer); // request next frame self.layer @@ -58,25 +64,29 @@ impl DimSurface { self.layer.commit(); } - pub fn first_configure(&self) -> bool { - self.first_configure - } - pub fn layer(&self) -> &LayerSurface { &self.layer } - pub fn set_first_configure(&mut self, value: bool) { - self.first_configure = value; - } - pub fn set_size(&mut self, width: u32, height: u32) { self.width = width; self.height = height; + self.viewport + .set_destination(self.width as _, self.height as _); + } + + pub fn set_damaged(&mut self, damaged: bool) { + self.damaged = damaged; + } + + pub fn set_back_buffer(&mut self, back_buffer: BufferType) { + self.back_buffer = back_buffer; + self.damaged = true; } - pub fn viewport_mut(&mut self) -> &mut WpViewport { - &mut self.viewport + pub fn back_buffer_mut(&mut self) -> &mut BufferType { + self.damaged = true; + &mut self.back_buffer } } From 4d844097e64c047ae108dad191d9d23edcbb65d1 Mon Sep 17 00:00:00 2001 From: Marcelo Hernandez Date: Wed, 24 Dec 2025 01:17:07 -0500 Subject: [PATCH 2/4] new: fade-in animation is customizable with -f,--fade + config option --- src/dim.rs | 7 ++++--- src/lib.rs | 1 + src/opts.rs | 23 ++++++++++++++++++++++- src/surface.rs | 6 +----- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/dim.rs b/src/dim.rs index 5f92a6f..d7e9ea1 100644 --- a/src/dim.rs +++ b/src/dim.rs @@ -60,6 +60,7 @@ pub struct DimData { alpha: f32, passthrough: bool, start_time: Instant, + fade_sec: f32, surfaces: HashMap, keyboard: Option, @@ -101,6 +102,7 @@ impl DimData { alpha: opts.alpha(), passthrough: opts.passthrough, start_time: Instant::now(), + fade_sec: opts.fade(), surfaces: HashMap::new(), keyboard: None, @@ -225,10 +227,9 @@ impl CompositorHandler for DimData { ) { for view in self.surfaces.values_mut() { let elapsed_sec = self.start_time.elapsed().as_millis() as f32 / 1000.; - let fade_sec = 0.5; - if elapsed_sec <= fade_sec { - let alpha = self.alpha * (elapsed_sec / fade_sec); + if elapsed_sec <= self.fade_sec { + let alpha = self.alpha * (elapsed_sec / self.fade_sec); match &mut self.buffer_mgr { BufferManager::SinglePixel(..) => { view.set_back_buffer(self.buffer_mgr.get_buffer(qh, alpha)); diff --git a/src/lib.rs b/src/lib.rs index 3770b61..87b807f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub use surface::DimSurface; pub mod consts { pub const DEFAULT_DURATION: u64 = 30; pub const DEFAULT_ALPHA: f32 = 0.5; + pub const DEFAULT_FADE: f32 = 0.5; pub const CONFIG_FILENAME: &str = "config.toml"; diff --git a/src/opts.rs b/src/opts.rs index 05a4656..cc72aeb 100644 --- a/src/opts.rs +++ b/src/opts.rs @@ -5,7 +5,7 @@ use clap::{CommandFactory, Parser, ValueEnum}; use clap_complete::{generate_to, Shell}; use serde::Deserialize; -use crate::{consts::DEFAULT_ALPHA, consts::DEFAULT_DURATION}; +use crate::consts::{DEFAULT_ALPHA, DEFAULT_DURATION, DEFAULT_FADE}; #[derive(Debug, Deserialize, Parser)] #[command(author, version, about)] @@ -24,6 +24,14 @@ pub struct DimOpts { )] alpha: Option, + #[arg( + short, + long, + help = format!("Fade-in animation duration in seconds. [default: {DEFAULT_FADE}]") + )] + #[serde(default)] + pub fade: Option, + #[arg( short, long, @@ -73,6 +81,14 @@ impl DimOpts { } } + if let Some(fade) = self.fade { + if !(0.0..=self.duration() as f32).contains(&fade) { + return Err(anyhow!( + "Fade must be at least 0 and as much as the duration option." + )); + } + } + Ok(()) } @@ -85,4 +101,9 @@ impl DimOpts { pub fn duration(&self) -> u64 { self.duration.unwrap_or(DEFAULT_DURATION) } + + /// Get user desired fade or the default value. + pub fn fade(&self) -> f32 { + self.fade.unwrap_or(DEFAULT_FADE) + } } diff --git a/src/surface.rs b/src/surface.rs index d0ac3b5..e94be62 100644 --- a/src/surface.rs +++ b/src/surface.rs @@ -75,17 +75,13 @@ impl DimSurface { .set_destination(self.width as _, self.height as _); } - pub fn set_damaged(&mut self, damaged: bool) { - self.damaged = damaged; - } - pub fn set_back_buffer(&mut self, back_buffer: BufferType) { self.back_buffer = back_buffer; self.damaged = true; } pub fn back_buffer_mut(&mut self) -> &mut BufferType { - self.damaged = true; + self.damaged = true; // we're most likely changing something. &mut self.back_buffer } } From c57f6dc51128e3b75ea7f7d49eb23a191e5410ef Mon Sep 17 00:00:00 2001 From: Marcelo Hernandez Date: Wed, 24 Dec 2025 01:19:30 -0500 Subject: [PATCH 3/4] doc: add fade option to manpage --- man/dim.1.scd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/dim.1.scd b/man/dim.1.scd index e08bf85..c989094 100644 --- a/man/dim.1.scd +++ b/man/dim.1.scd @@ -35,6 +35,10 @@ detected after its timeout, swaylock will be run, locking the screen. Set the *alpha* value of the overlay, 0.0 being transparent and 1.0 being solid black. When solid, cursor will be hidden. Default is 0.5. +\-f, --fade + Duration of fade-in animation in seconds. Must be at least 0, and at most + equal to the duration option above. Default is 0.5. + \-p, --passthrough Make dim ignore input, passing it to the surfaces behind it, making dim act as a way to lower your brightness artificially. You probably want to set the @@ -69,5 +73,6 @@ options are alpha, duration and passthrough as seen above, example config: # i am a comment! duration = 30 alpha = 0.5 +fade = 0.5 passthrough = false ``` From f6d437a3a7d1c26a1f74417c47be7d27d5ed248a Mon Sep 17 00:00:00 2001 From: Marcelo Hernandez Date: Wed, 24 Dec 2025 02:45:44 -0500 Subject: [PATCH 4/4] fix: fade check was racey --- src/dim.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/dim.rs b/src/dim.rs index d7e9ea1..c3b7892 100644 --- a/src/dim.rs +++ b/src/dim.rs @@ -61,6 +61,7 @@ pub struct DimData { passthrough: bool, start_time: Instant, fade_sec: f32, + fade_done: bool, surfaces: HashMap, keyboard: Option, @@ -103,6 +104,7 @@ impl DimData { passthrough: opts.passthrough, start_time: Instant::now(), fade_sec: opts.fade(), + fade_done: false, surfaces: HashMap::new(), keyboard: None, @@ -228,8 +230,8 @@ impl CompositorHandler for DimData { for view in self.surfaces.values_mut() { let elapsed_sec = self.start_time.elapsed().as_millis() as f32 / 1000.; - if elapsed_sec <= self.fade_sec { - let alpha = self.alpha * (elapsed_sec / self.fade_sec); + if !self.fade_done { + let alpha = (self.alpha * (elapsed_sec / self.fade_sec)).clamp(0., self.alpha); match &mut self.buffer_mgr { BufferManager::SinglePixel(..) => { view.set_back_buffer(self.buffer_mgr.get_buffer(qh, alpha)); @@ -241,6 +243,11 @@ impl CompositorHandler for DimData { } } } + + if elapsed_sec > self.fade_sec { + self.fade_done = true; + debug!("Fade done!") + } } view.draw(qh);