diff --git a/src/main.rs b/src/main.rs index e0e56a8..a2f3caa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,6 +50,15 @@ fn send_command(cmd: &str) -> Result<(), Box> { Ok(()) } +fn parse_mode(args: &[String]) -> &str { + let i = args.iter().position(|a| a == "--mode" || a == "-m"); + match i.and_then(|i| args.get(i + 1)).map(|s| s.as_str()) { + Some("fit") => "fit", + Some("original") => "original", + _ => "cover", + } +} + fn parse_anchor(args: &[String]) -> (f32, f32) { let i = args.iter().position(|a| a == "--anchor" || a == "-a"); let num = i @@ -109,8 +118,9 @@ fn main() { return; } } + let mode = parse_mode(&args); let (anchor_h, anchor_v) = parse_anchor(&args); - let cmd = format!("set {} {} {}", args[2], anchor_h, anchor_v); + let cmd = format!("set{}\x01{}\x01{}\x01{}", mode, anchor_h, anchor_v, args[2]); if let Err(e) = send_command(&cmd) { eprintln!("Failed to set wallpaper {}. Is the daemon running?", e); } diff --git a/src/vulkan.rs b/src/vulkan.rs index 5ee3d10..ac96b5e 100644 --- a/src/vulkan.rs +++ b/src/vulkan.rs @@ -508,6 +508,50 @@ pub fn vulkan_pipeline( } +// --------------------------------------------------------------------- / set mode + +fn mode_set( + img_w: u32, + img_h: u32, + scr_w: u32, + scr_h: u32, + anchor_x: f32, + anchor_y: f32, + mode: &str, +) -> (u32, u32, u32, u32, i32, i32, u32, u32, bool) { + + if mode == "fit" { + let scale = (scr_w as f64 / img_w as f64).min(scr_h as f64 / img_h as f64); + let sw = (img_w as f64 * scale) as u32; + let sh = (img_h as f64 * scale) as u32; + let dx = ((scr_w - sw) as f32 * anchor_x) as i32; + let dy = ((scr_h - sh) as f32 * anchor_y) as i32; + return (0, 0, img_w, img_h, dx, dy, sw, sh, true); + } + if mode == "original" { + if img_w <= scr_w && img_h <= scr_h { + let dx = ((scr_w - img_w) as f32 * anchor_x) as i32; + let dy = ((scr_h - img_h) as f32 * anchor_y) as i32; + return (0, 0, img_w, img_h, dx, dy, img_w, img_h, true); + } + } + let src_aspect = img_w as f64 / img_h as f64; + let dst_aspect = scr_w as f64 / scr_h as f64; + let (sx, sy, sw, sh) = if src_aspect > dst_aspect { + let new_w = (img_h as f64 * dst_aspect) as u32; + let max_x = (img_w - new_w) as f32; + let x = (max_x * anchor_x) as u32; + (x, 0, new_w, img_h) + } else { + let new_h = (img_w as f64 / dst_aspect) as u32; + let max_y = (img_h - new_h) as f32; + let y = (max_y * anchor_y) as u32; + (0, y, img_w, new_h) + }; + (sx, sy, sw, sh, 0, 0, scr_w, scr_h, false) +} + + // --------------------------------------------------------------------- / draw wallpaper pub fn draw_wallpaper( @@ -525,6 +569,7 @@ pub fn draw_wallpaper( swapchain_extent_height: u32, anchor_x: f32, anchor_y: f32, + mode: &str, ) -> Result<(), Box> { // acquire swapchain image @@ -596,22 +641,32 @@ pub fn draw_wallpaper( ); } - // preserve aspect ratios and fill screen (anchored crop) - let src_aspect = texture_width as f64 / texture_height as f64; - let dst_aspect = swapchain_extent_width as f64 / swapchain_extent_height as f64; - let (src_x, src_y, src_w, src_h) = if src_aspect > dst_aspect { - // Image is wider → crop left/right, horizontal anchor controls which side is kept - let new_width = (texture_height as f64 * dst_aspect) as u32; - let max_x = (texture_width - new_width) as f32; - let x = (max_x * anchor_x) as u32; - (x, 0, new_width, texture_height) - } else { - // Image is taller → crop top/bottom, vertical anchor controls which part is kept - let new_height = (texture_width as f64 / dst_aspect) as u32; - let max_y = (texture_height - new_height) as f32; - let y = (max_y * anchor_y) as u32; - (0, y, texture_width, new_height) - }; + // compute source and destination rectangles based on mode + let (src_x, src_y, src_w, src_h, dst_x, dst_y, dst_w, dst_h, needs_clear) = mode_set( + texture_width, texture_height, + swapchain_extent_width, swapchain_extent_height, + anchor_x, anchor_y, + mode, + ); + + // if the mode requires black bars, clear the image first + if needs_clear { + let clear_color = vk::ClearColorValue { float32: [0.0, 0.0, 0.0, 1.0] }; + let clear_range = vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, level_count: 1, + base_array_layer: 0, layer_count: 1, + }; + unsafe { + device.cmd_clear_color_image( + command_buffer, + target_image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &clear_color, + &[clear_range], + ); + } + } // record the blit command let blit_region = vk::ImageBlit::default() @@ -640,12 +695,8 @@ pub fn draw_wallpaper( layer_count: 1, }) .dst_offsets([ - vk::Offset3D { x: 0, y: 0, z: 0 }, - vk::Offset3D { - x: swapchain_extent_width as i32, - y: swapchain_extent_height as i32, - z: 1, - }, + vk::Offset3D { x: dst_x, y: dst_y, z: 0 }, + vk::Offset3D { x: dst_x + dst_w as i32, y: dst_y + dst_h as i32, z: 1 }, ]); unsafe { device.cmd_blit_image( diff --git a/src/wallbashed.rs b/src/wallbashed.rs index 84b4fff..ec30901 100644 --- a/src/wallbashed.rs +++ b/src/wallbashed.rs @@ -64,6 +64,7 @@ fn set_wallpaper( wallpaper: &mut Option, anchor_x: f32, anchor_y: f32, + mode: &str, ) -> Result<(), Box> { // load the wallpaper @@ -109,6 +110,7 @@ fn set_wallpaper( layer_height, anchor_x, anchor_y, + mode, )?; Ok(()) @@ -160,16 +162,17 @@ pub fn run(socket_path: &str) -> Result<(), Box> { if raw == "stop" { println!("[wallbash] stopping daemon."); running = false; - } else if raw.starts_with("set ") { - let args: Vec<&str> = raw[4..].trim().split_whitespace().collect(); - let path = args[..args.len()-2].join(" "); - let anchor_x: f32 = args[args.len()-2].parse().unwrap_or(0.5); - let anchor_y: f32 = args[args.len()-1].parse().unwrap_or(0.5); + } 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 {} (anchor {}/{})", resolved, anchor_x, anchor_y); + println!("[wallbash] loading '{}' ({}::{}x{})", resolved, mode, anchor_x, anchor_y); match set_wallpaper( &resolved, &vk_core, @@ -179,6 +182,7 @@ pub fn run(socket_path: &str) -> Result<(), Box> { &mut wallpaper, anchor_x, anchor_y, + &mode, ) { Ok(()) => println!("[wallbash] wallpaper set."), Err(e) if e.to_string().contains("out of date") => { @@ -221,6 +225,7 @@ pub fn run(socket_path: &str) -> Result<(), Box> { &mut wallpaper, anchor_x, anchor_y, + &mode, ) { eprintln!("[wallbash] error after swapchain recreation {}", e3); }