diff --git a/src/background.rs b/src/background.rs index a0bb337fc8c3a01e298ebac3a993fdf44f3b93de..73061570c44410e1f71ddae67859c66d98cb6c1e 100644 --- a/src/background.rs +++ b/src/background.rs @@ -1,3 +1,4 @@ +use std::io::Read; use std::process::Command; use dbus::blocking::Connection; @@ -5,6 +6,7 @@ use dbus::Message; use crate::img_processors::process_image; use crate::result::MBGResult; +use crate::TestingPic; pub fn reset_background_handler(_: (), _: &Connection, message: &Message) -> bool { let member_name = message.read1::<String>(); @@ -34,6 +36,18 @@ pub fn process_image_url(image_url: String) -> MBGResult<()> { Ok(()) } +pub fn test_processors(test_data: TestingPic) -> MBGResult<()> { + let mut img_file = std::fs::File::open(test_data.input.as_str())?; + let mut image_bytes = Vec::new(); + img_file.read_to_end(&mut image_bytes)?; + let res = process_image(image_bytes)?; + res.save(test_data.output.as_str())?; + if test_data.wallpaper { + set_wallpaper(test_data.output.as_str()); + } + Ok(()) +} + pub fn set_wallpaper(path: &str) { debug!("Setting background"); let out = Command::new("feh").arg("--bg-fill").arg(path).output(); diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000000000000000000000000000000000000..13933cf4a8be668e1a6908ef3a128b76b63099d7 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,109 @@ +#[derive(Debug, StructOpt)] +#[structopt( +name = "music_bg", +about = "Listens to your mpris interface and sets you a cool background" +)] +pub struct Opt { + #[structopt(subcommand)] + pub cmd: Option<RunMode>, +} + +#[derive(StructOpt, Debug)] +pub enum RunMode { + #[structopt(name = "run", about = "Run d-bus listener")] + Run, + #[structopt( + name = "config", + about = "Create or replace default configuration file" + )] + GenConf, + #[structopt( + name = "test", + about = "Process image file according to your ~/.mbg.toml" + )] + TestPic(TestingPic), + #[structopt( + name = "completions", + about = "Generate completion for your shell" + )] + GenComp(ShellType), +} + +#[derive(StructOpt, Debug)] +pub struct TestingPic { + #[structopt( + name = "input", + about = "Image file to process" + )] + pub input: String, + #[structopt( + long, + about = "Processed image output file", + name = "out", + default_value = "/dev/shm/test.png" + )] + pub output: String, + #[structopt( + short, + about = "Set processed image as wallpaper", + name = "wall" + )] + pub wallpaper: bool, +} + +#[derive(StructOpt, Debug)] +pub enum ShellType { + #[structopt( + about = "Generates a .bash completion file for the Bourne Again SHell (BASH)", + name = "bash" + )] + Bash, + #[structopt( + about = "Generates a .fish completion file for the Friendly Interactive SHell (fish)", + name = "fish" + )] + Fish, + #[structopt( + about = "Generates a completion file for the Z SHell (ZSH)", + name = "zsh" + )] + Zsh, + #[structopt( + about = "Generates a completion file for PowerShell", + name = "ps" + )] + PowerShell, + #[structopt( + about = "Generates a completion file for Elvish", + name = "elvish" + )] + Elvish, +} + +impl From<ShellType> for Shell { + fn from(shell: ShellType) -> Self { + match shell { + ShellType::Bash => { Shell::Bash } + ShellType::Fish => { Shell::Fish } + ShellType::Zsh => { Shell::Zsh } + ShellType::PowerShell => { Shell::PowerShell } + ShellType::Elvish => { Shell::Elvish } + } + } +} + +pub fn generate_completions(shell: ShellType) -> MBGResult<()> { + Opt::clap().gen_completions(env!("CARGO_PKG_NAME"), shell.into(), "."); + info!("Generated completion file in _music_bg"); + Ok(()) +} + +pub fn run(opts: Opt) -> MBGResult<()> { + let mode = opts.cmd.unwrap_or_else(|| RunMode::Run); + match mode { + RunMode::Run => start_listen(), + RunMode::GenConf => Config::generate_config(), + RunMode::TestPic(data) => test_processors(data), + RunMode::GenComp(shell) => generate_completions(shell) + } +} \ No newline at end of file diff --git a/src/img_processors/border.rs b/src/img_processors/border.rs index b9b41c925bfc11ea5512503f478170aaf6a0d657..190fcbe9e896feb51fc733ecded4e159d92bc0ca 100644 --- a/src/img_processors/border.rs +++ b/src/img_processors/border.rs @@ -1,8 +1,8 @@ use std::cmp::min; +use image::imageops::{overlay, FilterType}; use image::{DynamicImage, GenericImageView, Rgba, RgbaImage}; -use image::imageops::{FilterType, overlay}; -use imageproc::drawing::{Blend, Canvas, draw_filled_circle_mut}; +use imageproc::drawing::{draw_filled_circle_mut, Blend, Canvas}; use crate::config::image_processors::ProcessorParams; use crate::img_processors::ImageProcessor; @@ -15,18 +15,22 @@ pub struct Border { pub width: u32, } - impl Border { fn crop_circle(&self, img: &DynamicImage) -> MBGResult<DynamicImage> { let img_height = image::GenericImageView::height(img); let img_width = image::GenericImageView::width(img); let transparent_pixel = Rgba([0u8, 0u8, 0u8, 0u8]); - let mut mask = Blend(RgbaImage::from_pixel(img_width * 3, img_height * 3, transparent_pixel)); + let mut mask = Blend(RgbaImage::from_pixel( + img_width, + img_height, + transparent_pixel, + )); let center = ((mask.width() / 2) as i32, (mask.height() / 2) as i32); let radius = min(center.0, center.1); let black_pixel = Rgba([0u8, 0u8, 0u8, 255u8]); draw_filled_circle_mut(&mut mask, center, radius, black_pixel); let mut mask = DynamicImage::ImageRgba8(mask.0); + debug!("{}x{}", img_width, img_height); mask = mask.resize(img_width, img_height, FilterType::Nearest); let mut res = mask.clone(); for (x, y, pix) in mask.pixels() { @@ -39,17 +43,20 @@ impl Border { fn draw_border(&self, img: &DynamicImage) -> MBGResult<DynamicImage> { let img_width = image::GenericImageView::width(img); let img_height = image::GenericImageView::height(img); - let center = (((img_width / 2) + self.width), ((img_height / 2) + self.width)); + let center = ( + ((img_width / 2) + self.width), + ((img_height / 2) + self.width), + ); let target_color = read_color::rgb_maybe_a(&mut self.color.chars()); if target_color.is_none() { - return Err( - MBGError::ConfigErr(String::from("Can't parse border color. Please use hex value.")) - ); + return Err(MBGError::ConfigErr(String::from( + "Can't parse border color. Please use hex value.", + ))); } let target_color = target_color.unwrap(); let target_color = match target_color { - ([r, g, b, ], Some(a)) => Rgba([r, g, b, a]), - ([r, g, b, ], None) => Rgba([r, g, b, 255u8]), + ([r, g, b], Some(a)) => Rgba([r, g, b, a]), + ([r, g, b], None) => Rgba([r, g, b, 255u8]), }; if self.circle { let mut mask = DynamicImage::ImageRgba8(RgbaImage::from_pixel( @@ -76,6 +83,7 @@ impl Border { impl ImageProcessor for Border { fn process(&self, img: &DynamicImage) -> MBGResult<DynamicImage> { + debug!("Setting border"); let mut res = img.clone(); if self.circle { res = self.crop_circle(&res)?; diff --git a/src/img_processors/mod.rs b/src/img_processors/mod.rs index d9d13e38de41a5a7d41e30c39120a11cb1e84d7d..adf94250722c93c2b0b0d829a265a9e11ccef942 100644 --- a/src/img_processors/mod.rs +++ b/src/img_processors/mod.rs @@ -142,4 +142,4 @@ fn mapper( width, }), } -} \ No newline at end of file +} diff --git a/src/img_processors/scale.rs b/src/img_processors/scale.rs index e1c20e3d6748ec21593d9d098110689e79c557ae..8592aefa8cacf358d0c16627ccab973c66c21a1b 100644 --- a/src/img_processors/scale.rs +++ b/src/img_processors/scale.rs @@ -1,5 +1,5 @@ -use image::imageops::FilterType; use image::{DynamicImage, GenericImageView}; +use image::imageops::FilterType; use crate::config::image_processors::ProcessorParams; use crate::result::MBGResult; @@ -13,12 +13,11 @@ pub struct ScaleProcessor { impl super::ImageProcessor for ScaleProcessor { fn process(&self, img: &DynamicImage) -> MBGResult<DynamicImage> { let abs_width = self.screen_width.abs() as u32; - let scale_factor = ((abs_width / img.width()) as f32) * self.params.strength; - let scale_factor = scale_factor.ceil() as u32; + let scale_factor = (abs_width as f32 / img.width() as f32) * self.params.strength; debug!("Scaling image by {} times", scale_factor); Ok(img.resize( - img.width() * scale_factor, - img.height() * scale_factor, + (img.width() as f32 * scale_factor) as u32, + (img.height() as f32 * scale_factor) as u32, FilterType::Nearest, )) } diff --git a/src/main.rs b/src/main.rs index af98e4d430e4141194fe76588a6e77980a462d54..cbabcac7f22bd628ef5506eb0c1b96620f63319f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,10 +9,12 @@ extern crate serde_derive; use structopt::StructOpt; +use crate::background::test_processors; use crate::config::Config; use crate::logging::setup_logger; use crate::player_dbus::start_listen; use crate::result::{MBGError, MBGResult}; +use structopt::clap::Shell; pub mod background; pub mod config; @@ -23,30 +25,10 @@ pub mod logging; pub mod player_dbus; pub mod result; -#[derive(Debug, StructOpt)] -#[structopt( - name = "music_bg", - about = "Listens to your mpris interface and sets you a cool background." -)] -struct Opt { - #[structopt(subcommand)] - cmd: Option<RunMode>, -} - -#[derive(StructOpt, Debug)] -enum RunMode { - #[structopt(name = "run", about = "Run d-bus listener")] - Run, - #[structopt( - name = "config", - about = "Create or replace default configuration file." - )] - GenConf, -} +include!("cli.rs"); fn main() -> MBGResult<()> { let opt: Opt = Opt::from_args(); - let mode = opt.cmd.unwrap_or_else(|| RunMode::Run); let logger = setup_logger(); if let Err(error) = logger { error!("Failed to create pretty logger"); @@ -57,9 +39,5 @@ fn main() -> MBGResult<()> { if res.is_err() { return Err(MBGError::X11Error(String::from("Could not connect to GTK"))); } - - match mode { - RunMode::Run => start_listen(), - RunMode::GenConf => Config::generate_config(), - } + run(opt) }