From 7dfb1e8f30250a92198a21ef82c2e672f4af558e Mon Sep 17 00:00:00 2001 From: tittu Date: Fri, 19 Jun 2026 17:29:10 +0530 Subject: [PATCH 1/2] minor bug fixes --- shaders/blur.comp | 29 +++++++++++++---------------- src/main.rs | 13 ++++++++++++- src/wallbash.rs | 9 ++++----- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/shaders/blur.comp b/shaders/blur.comp index e3ea30f..df4affa 100644 --- a/shaders/blur.comp +++ b/shaders/blur.comp @@ -1,25 +1,22 @@ #version 450 + layout(local_size_x = 16, local_size_y = 16) in; -layout(binding = 0) uniform sampler2D srcImage; -layout(rgba8, binding = 1) uniform image2D dstImage; +layout(set = 0, binding = 0) uniform sampler2D inputTex; +layout(set = 0, binding = 1, rgba8) uniform writeonly image2D outputTex; void main() { ivec2 gid = ivec2(gl_GlobalInvocationID.xy); - ivec2 size = imageSize(dstImage); - if (gid.x >= size.x || gid.y >= size.y) return; - - vec2 texelSize = 1.0 / vec2(size); + vec2 size = vec2(imageSize(outputTex)); + vec2 texelSize = 1.0 / size; vec4 color = vec4(0.0); - int radius = 5; - int samples = 0; - - for (int dx = -radius; dx <= radius; dx++) { - for (int dy = -radius; dy <= radius; dy++) { - vec2 offset = vec2(dx, dy) * texelSize; - color += texture(srcImage, (vec2(gid) + 0.5) / vec2(size) + offset); - samples++; + int count = 0; + for (int dx = -7; dx <= 7; dx++) { + for (int dy = -7; dy <= 7; dy++) { + vec2 offset = vec2(float(dx), float(dy)) * texelSize; + color += texture(inputTex, (vec2(gid) + 0.5) / size + offset); + count++; } } - imageStore(dstImage, gid, color / float(samples)); + color /= float(count); + imageStore(outputTex, gid, color); } - diff --git a/src/main.rs b/src/main.rs index 0802e20..090f471 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,7 +65,18 @@ fn parse_args(args: &[String]) -> (String, String, f32, f32) { let wall = args.iter().position(|a| a == "--wall" || a == "-w") .and_then(|i| args.get(i + 1).cloned()) .or_else(|| { - args.iter().skip(2).find(|a| !a.starts_with('-')).cloned() + args.iter().skip(2).scan(false, |skip, a| { + if *skip { + *skip = false; + Some(None) + } else if a.starts_with('-') { + *skip = true; + Some(None) + } else { + Some(Some(a.clone())) + } + }) + .flatten().last() }) .unwrap_or_else(|| { eprintln!("Missing wallpaper (use --wall or bare path)"); diff --git a/src/wallbash.rs b/src/wallbash.rs index d792870..cd789b7 100644 --- a/src/wallbash.rs +++ b/src/wallbash.rs @@ -83,11 +83,11 @@ fn set_wallpaper( // load the wallpaper let (img, pixel_bytes) = timer("load+decode", || { - let img = image::open(path).expect("failed to open image"); + let img = image::open(path)?; let rgba = img.to_rgba8(); let bytes = rgba.into_raw(); - (img, bytes) - }); + Ok::<_, Box>((img, bytes)) + })?; // call the vulkan pipeline let texture = timer("upload", || { @@ -202,7 +202,6 @@ pub fn run(socket_path: &str) -> Result<(), Box> { // check for commands from the IPC if let Ok(raw) = rx.try_recv() { let raw = raw.trim().to_string(); - println!("[wallbash] received '{:?}'", raw); if raw == "stop" { println!("[wallbash] stopping daemon."); @@ -216,7 +215,7 @@ pub fn run(socket_path: &str) -> Result<(), Box> { let resolved = std::fs::canonicalize(&path) .map(|p| p.to_string_lossy().to_string()) .unwrap_or(path); - println!("[wallbash] loading '{}' ({} | ax:{:?} | ay:{:?})", resolved, mode, anchor_x, anchor_y); + println!("[wallbash] loading '{}' ({}|ax:{:?}|ay:{:?})", resolved, mode, anchor_x, anchor_y); // apply blur for non cover mode let effect = |tex: &vulkan::VulkanTexture| { From 6471cfd7c77013a2abba5f140bfd619002cb5d18 Mon Sep 17 00:00:00 2001 From: tittu Date: Fri, 19 Jun 2026 19:41:13 +0530 Subject: [PATCH 2/2] minor refactor --- src/main.rs | 18 +-- src/wallbash.rs | 310 +++++++++++++++++++++++++++++------------------- 2 files changed, 194 insertions(+), 134 deletions(-) diff --git a/src/main.rs b/src/main.rs index 090f471..92b2217 100644 --- a/src/main.rs +++ b/src/main.rs @@ -66,15 +66,15 @@ fn parse_args(args: &[String]) -> (String, String, f32, f32) { .and_then(|i| args.get(i + 1).cloned()) .or_else(|| { args.iter().skip(2).scan(false, |skip, a| { - if *skip { - *skip = false; - Some(None) - } else if a.starts_with('-') { - *skip = true; - Some(None) - } else { - Some(Some(a.clone())) - } + if *skip { + *skip = false; + Some(None) + } else if a.starts_with('-') { + *skip = true; + Some(None) + } else { + Some(Some(a.clone())) + } }) .flatten().last() }) diff --git a/src/wallbash.rs b/src/wallbash.rs index cd789b7..d00e01b 100644 --- a/src/wallbash.rs +++ b/src/wallbash.rs @@ -7,13 +7,72 @@ // --------------------------------------------------------------------- / imports use crate::{vulkan, wayland, filters}; +use ash::vk; use std::{ os::unix::net::{UnixListener, UnixStream}, io::{BufRead, BufReader}, - sync::mpsc,time::Instant, + sync::mpsc, time::Instant, }; +// --------------------------------------------------------------------- / datatypes + +enum Command { + Stop, + Status, + Set { mode: String, anchor_x: f32, anchor_y: f32, path: String }, +} + +struct DaemonState { + vk_core: vulkan::VulkanCore, + wl_core: wayland::WaylandCore, + vk_surfchain: Option, + wallpaper: Option, + blur_module: vk::ShaderModule, + blur_pipeline: vk::Pipeline, + blur_desc_layout: vk::DescriptorSetLayout, +} + + +// --------------------------------------------------------------------- / implementations + +impl Command { + fn parse_raw(raw: &str) -> Self { + let raw = raw.trim(); + if raw == "stop" { return Command::Stop; } + if raw == "status" { return Command::Status; } + if raw.starts_with("set") { + let payload = &raw[3..]; + let mut parts = payload.splitn(4, '\x01'); + let mode = parts.next().unwrap().to_string(); + let anchor_x = parts.next().unwrap().parse().unwrap(); + let anchor_y = parts.next().unwrap().parse().unwrap(); + let path = parts.next().unwrap().to_string(); + return Command::Set { mode, anchor_x, anchor_y, path }; + } + panic!("unknown internal command: {}", raw); + } +} + +impl DaemonState { + fn new() -> Result> { + let wl_core = wayland::wayland_core()?; + let vk_core = vulkan::vulkan_core()?; + let (blur_module, blur_pipeline, blur_desc_layout) = filters::filter_pipeline(&vk_core.device, "blur")?; + let vk_surfchain = Some(set_surfchain(&vk_core, &wl_core, None)?); + Ok(Self { + vk_core, + wl_core, + vk_surfchain, + wallpaper: None, + blur_module, + blur_pipeline, + blur_desc_layout, + }) + } +} + + // --------------------------------------------------------------------- / listener fn start_ipc(socket_path: &str) -> Result, Box> { @@ -171,153 +230,154 @@ fn set_surfchain( } +// --------------------------------------------------------------------- / command + +impl DaemonState { + fn set_command( + &mut self, + mode: String, + anchor_x: f32, + anchor_y: f32, + path: String, + ) -> Result<(), ()> { + let resolved = std::fs::canonicalize(&path) + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or(path); + println!("[wallbash] loading '{}' ({}|ax:{:?}|ay:{:?})", resolved, mode, anchor_x, anchor_y); + + let effect = |tex: &vulkan::VulkanTexture| { + if mode != "cover" { + filters::blur_texture( + &self.vk_core, + tex, + tex.width, + tex.height, + self.blur_pipeline, + self.blur_desc_layout, + ) + .ok() + } else { None } + }; + + match set_wallpaper( + &resolved, + &self.vk_core, + self.vk_surfchain.as_ref().unwrap(), + self.wl_core.state.layer_width, + self.wl_core.state.layer_height, + &mut self.wallpaper, + anchor_x, + anchor_y, + &mode, + effect, + ) { + Ok(()) => { + println!("[wallbash] wallpaper set."); + Ok(()) + } + Err(e) if e.to_string().contains("out of date") => { + println!("[wallbash] swapchain out of date, recreating..."); + + match vulkan::vulkan_surfchain( + &self.vk_core.entry, + &self.vk_core.instance, + self.vk_core.physical_device, + self.vk_core.graphics_family_index, + &self.vk_core.device, + &self.wl_core.display, + &self.wl_core.surface, + self.wl_core.state.layer_width, + self.wl_core.state.layer_height, + ) { + Ok(new_sc) => { + let old = self.vk_surfchain.take().unwrap(); + vulkan::destroy_wallbash( + &self.vk_core, + vulkan::VulkanCleanup { + surfchain: Some(old), + filter_module: None, + filter_pipeline: None, + filter_desc_layout: None, + wallpaper_texture: None, + }, + 1, + ); + self.vk_surfchain = Some(new_sc); + } + Err(e2) => { + eprintln!("[wallbash] failed to recreate swapchain {}", e2); + return Err(()); + } + } + + if let Err(e3) = set_wallpaper( + &resolved, + &self.vk_core, + self.vk_surfchain.as_ref().unwrap(), + self.wl_core.state.layer_width, + self.wl_core.state.layer_height, + &mut self.wallpaper, + anchor_x, + anchor_y, + &mode, + effect, + ) { + eprintln!("[wallbash] error after swapchain recreation {}", e3); + } + Ok(()) + } + Err(e) => { + eprintln!("[wallbash] error {}", e); + Ok(()) + } + } + } +} + + // --------------------------------------------------------------------- / daemon pub fn run(socket_path: &str) -> Result<(), Box> { - - // init listener if UnixStream::connect(socket_path).is_ok() { return Err("Daemon is already running.".into()); } let _ = std::fs::remove_file(socket_path); let rx = start_ipc(socket_path)?; - // init wayland - let mut wl_core = wayland::wayland_core()?; - - // init vulkan - let vk_core = vulkan::vulkan_core()?; - let (blur_module, blur_pipeline, blur_desc_layout) = filters::filter_pipeline(&vk_core.device, "blur")?; - let mut vk_surfchain = Some(set_surfchain(&vk_core, &wl_core, None)?); - - // blank surface until a command arrives - let mut wallpaper: Option = None; + let mut state = DaemonState::new()?; println!("[wallbash] ready, press Ctrl+C to quit."); - // main event loop let mut running = true; while running { - wl_core.event.dispatch_pending(&mut wl_core.state)?; - - // check for commands from the IPC + state.wl_core.event.dispatch_pending(&mut state.wl_core.state)?; if let Ok(raw) = rx.try_recv() { - let raw = raw.trim().to_string(); - - if raw == "stop" { - println!("[wallbash] stopping daemon."); - running = false; - } else if raw.starts_with("set") { - let args: Vec<&str> = raw[3..].split('\x01').collect(); - let mode = args[0].to_string(); - let anchor_x: f32 = args[1].parse().unwrap_or(0.5); - let anchor_y: f32 = args[2].parse().unwrap_or(0.5); - let path = args[3..].join("\x01"); - let resolved = std::fs::canonicalize(&path) - .map(|p| p.to_string_lossy().to_string()) - .unwrap_or(path); - println!("[wallbash] loading '{}' ({}|ax:{:?}|ay:{:?})", resolved, mode, anchor_x, anchor_y); - - // apply blur for non cover mode - let effect = |tex: &vulkan::VulkanTexture| { - if mode != "cover" { - filters::blur_texture( - &vk_core, - tex, - tex.width, - tex.height, - blur_pipeline, - blur_desc_layout, - ) - .ok() - } else { - None - } - }; - - match set_wallpaper( - &resolved, - &vk_core, - vk_surfchain.as_ref().unwrap(), - wl_core.state.layer_width, - wl_core.state.layer_height, - &mut wallpaper, - anchor_x, - anchor_y, - &mode, - effect, - ) { - Ok(()) => println!("[wallbash] wallpaper set."), - Err(e) if e.to_string().contains("out of date") => { - println!("[wallbash] swapchain out of date, recreating..."); - - // destroy old swapchain and surface - match vulkan::vulkan_surfchain( - &vk_core.entry, - &vk_core.instance, - vk_core.physical_device, - vk_core.graphics_family_index, - &vk_core.device, - &wl_core.display, - &wl_core.surface, - wl_core.state.layer_width, - wl_core.state.layer_height, - ) { - Ok(new_sc) => { - // Destroy the old swapchain (now that we have a working new one) - let old = vk_surfchain.take().unwrap(); - vulkan::destroy_wallbash( - &vk_core, - vulkan::VulkanCleanup { - surfchain: Some(old), - filter_module: None, - filter_pipeline: None, - filter_desc_layout: None, - wallpaper_texture: None, - }, - 1, - ); - vk_surfchain = Some(new_sc); - } - Err(e2) => { - eprintln!("[wallbash] failed to recreate swapchain {}", e2); - continue; - } - } - - // retry setting the wallpaper once - if let Err(e3) = set_wallpaper( - &resolved, - &vk_core, - vk_surfchain.as_ref().unwrap(), - wl_core.state.layer_width, - wl_core.state.layer_height, - &mut wallpaper, - anchor_x, - anchor_y, - &mode, - effect, - ) { - eprintln!("[wallbash] error after swapchain recreation {}", e3); - } + match Command::parse_raw(&raw) { + Command::Stop => { + println!("[wallbash] stopping daemon."); + running = false; + } + Command::Status => { + println!("[wallbash] daemon is running."); + } + Command::Set { mode, anchor_x, anchor_y, path } => { + if state.set_command(mode, anchor_x, anchor_y, path).is_err() + { + continue; } - Err(e) => eprintln!("[wallbash] error {}", e), } - } else { - eprintln!("[wallbash] unknown {}", raw); } } std::thread::sleep(std::time::Duration::from_millis(16)); } - // clean shutdowon vulkan::destroy_wallbash( - &vk_core, + &state.vk_core, vulkan::VulkanCleanup { - surfchain: vk_surfchain.take(), - filter_module: Some(blur_module), - filter_pipeline: Some(blur_pipeline), - filter_desc_layout: Some(blur_desc_layout), - wallpaper_texture: wallpaper.take(), + surfchain: state.vk_surfchain.take(), + filter_module: Some(state.blur_module), + filter_pipeline: Some(state.blur_pipeline), + filter_desc_layout: Some(state.blur_desc_layout), + wallpaper_texture: state.wallpaper.take(), }, 2, );